Weiwei | 074778b | 2015-08-07 21:30:44 -0700 | [diff] [blame] | 1 | # -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ |
| 2 | # |
| 3 | # Copyright (C) 2014 Regents of the University of California. |
| 4 | # Author: Teng Liang <philoliang2011@gmail.com> |
| 5 | # This program is free software: you can redistribute it and/or modify |
| 6 | # it under the terms of the GNU Lesser General Public License as published by |
| 7 | # the Free Software Foundation, either version 3 of the License, or |
| 8 | # (at your option) any later version. |
| 9 | # |
| 10 | # This program is distributed in the hope that it will be useful, |
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | # GNU General Public License for more details. |
| 14 | # |
| 15 | # You should have received a copy of the GNU General Public License |
| 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 | # A copy of the GNU General Public License is in the file COPYING. |
| 18 | """ |
| 19 | UserAccessStorage implements a basic storage of users and access |
| 20 | """ |
| 21 | |
| 22 | import os |
| 23 | import sqlite3 |
| 24 | from pyndn import Name |
| 25 | from device_profile import DeviceProfile |
| 26 | from hmac_key import HMACKey |
| 27 | |
| 28 | INIT_USER_TABLE = [""" |
| 29 | CREATE TABLE IF NOT EXISTS |
| 30 | User( |
| 31 | id INTEGER, |
| 32 | prefix BLOB NOT NULL UNIQUE, |
| 33 | username BLOB NOT NULL UNIQUE, |
| 34 | hash BLOB NOT NULL, |
| 35 | salt BLOB NOT NULL, |
| 36 | type BLOB NOT NULL, |
| 37 | |
| 38 | PRIMARY KEY (id) |
| 39 | ); |
| 40 | """] |
| 41 | |
| 42 | INIT_ACCESS_TABLE = [""" |
| 43 | CREATE TABLE IF NOT EXISTS |
| 44 | Access( |
| 45 | id INTEGER, |
| 46 | command_id INTEGER, |
| 47 | user_id INTEGER, |
| 48 | user_device BLOB NOT NULL, |
| 49 | access_token_name BLOB, |
| 50 | access_token_sequence INTEGER NOT NULL, |
| 51 | access_token BLOB NOT NULL, |
| 52 | |
| 53 | PRIMARY KEY (id) |
| 54 | FOREIGN KEY(command_id) REFERENCES Command(id) |
| 55 | FOREIGN KEY(user_id) REFERENCES User(id) |
| 56 | ); |
| 57 | """] |
| 58 | |
| 59 | class UserAccessStorage(object): |
| 60 | """ |
| 61 | Create a new UserAccessStorage to work with an SQLite file. |
| 62 | :param str databaseFilePath: (optional) The path of the SQLite file. If ommitted, use the default path. |
| 63 | """ |
| 64 | def __init__(self, databaseFilePath = None): |
| 65 | if databaseFilePath == None or databaseFilePath == "": |
| 66 | if not "HOME" in os.environ: |
| 67 | home = '.' |
| 68 | else: |
| 69 | home = os.environ["HOME"] |
| 70 | |
| 71 | dbDirectory = os.path.join(home, '.ndn') |
| 72 | if not os.path.exists(dbDirectory): |
| 73 | os.makedirs(dbDirectory) |
| 74 | |
| 75 | databaseFilePath = os.path.join(dbDirectory, 'ndnhome-controller.db') |
| 76 | |
| 77 | self._database = sqlite3.connect(databaseFilePath) |
| 78 | |
| 79 | #Check if the User table exists |
| 80 | cursor = self._database.cursor() |
| 81 | cursor.execute("SELECT name FROM sqlite_master WHERE TYPE ='table' and NAME = 'User'") |
| 82 | if cursor.fetchone() == None: |
| 83 | #no device table exists, create one |
| 84 | for command in INIT_USER_TABLE: |
| 85 | self._database.execute(command) |
| 86 | cursor.close() |
| 87 | |
| 88 | #Check if the Access table exists |
| 89 | cursor = self._database.cursor() |
| 90 | cursor.execute("SELECT name FROM sqlite_master WHERE TYPE ='table' and NAME = 'Access'") |
| 91 | if cursor.fetchone() == None: |
| 92 | #no device table exists, create one |
| 93 | for command in INIT_ACCESS_TABLE: |
| 94 | self._database.execute(command) |
| 95 | cursor.close() |
| 96 | |
| 97 | self._database.commit() |
| 98 | |
| 99 | def doesTableExist(self, tableName): |
| 100 | """ |
| 101 | check if table exists |
| 102 | :param str tableName |
| 103 | :return True if exists, otherwise False |
| 104 | """ |
| 105 | selectOperation = "SELECT name FROM sqlite_master WHERE TYPE='table' and NAME='" + tableName +"'" |
| 106 | #print selectOperation |
| 107 | cursor = self._database.cursor() |
| 108 | cursor.execute(selectOperation) |
| 109 | if cursor.fetchone() == None: |
| 110 | result = False |
| 111 | else: |
| 112 | result = True |
| 113 | cursor.close() |
| 114 | return result |
| 115 | |
| 116 | def doesUserExistByPrefix(self, prefix): |
| 117 | """ |
| 118 | Check if the specified user already exists. |
| 119 | |
| 120 | :param Name prefix: the prefix of user |
| 121 | :return True if the user exists, otherwise False |
| 122 | :rtype: bool |
| 123 | """ |
| 124 | result = False |
| 125 | |
| 126 | cursor = self._database.cursor() |
| 127 | cursor.execute("SELECT count(*) FROM User WHERE prefix =?", (prefix.toUri(),)) |
| 128 | (count,) = cursor.fetchone() |
| 129 | if count > 0: |
| 130 | result = True |
| 131 | #print 'device with %s is founnd, count %d' %(prefix, count) |
| 132 | |
| 133 | cursor.close() |
| 134 | return result |
| 135 | |
| 136 | def doesUserExistByUsername(self, username): |
| 137 | """ |
| 138 | Check if the specified user already exists. |
| 139 | |
| 140 | :param str username: the username of user |
| 141 | :return True if the user exists, otherwise False |
| 142 | :rtype: bool |
| 143 | """ |
| 144 | result = False |
| 145 | |
| 146 | cursor = self._database.cursor() |
| 147 | cursor.execute("SELECT count(*) FROM User WHERE username =?", (username,)) |
| 148 | (count,) = cursor.fetchone() |
| 149 | if count > 0: |
| 150 | result = True |
| 151 | #print 'device with %s is founnd, count %d' %(prefix, count) |
| 152 | |
| 153 | cursor.close() |
| 154 | return result |
| 155 | |
| 156 | |
| 157 | def addUser(self, prefix, username, hash_, salt, type_): |
| 158 | """ |
| 159 | Add a new user to User table, do nothing if the user already exists |
| 160 | |
| 161 | :param Name prefix: the prefix of the user |
| 162 | :param str username: username |
| 163 | :param hash_: |
| 164 | :param salt: |
| 165 | :param str type: the type of user, either guest or user |
| 166 | :return the user id if it's added successfully, 0 if prefix conflict exists, -1 if username conflict exists |
| 167 | :rtype: INTEGER |
| 168 | """ |
| 169 | data = ( prefix.toUri(), username, hash_, salt, type_) |
| 170 | |
| 171 | #check if there already exists a user with the same prefix, if yes return 0 |
| 172 | if self.doesUserExistByPrefix(prefix): |
| 173 | return 0 |
| 174 | |
| 175 | #check if there already exists a user with the same username, if yes return -1 |
| 176 | if self.doesUserExistByUsername(username): |
| 177 | return -1 |
| 178 | |
| 179 | #add user to dababase |
| 180 | insertUser = ( |
| 181 | "INSERT INTO User(prefix, username, hash, salt, type)" |
| 182 | "VALUES(?,?,?,?,?)" |
| 183 | ) |
| 184 | cursor = self._database.cursor() |
| 185 | cursor.execute(insertUser, data) |
| 186 | self._database.commit() |
| 187 | result = cursor.lastrowid |
| 188 | cursor.close() |
| 189 | return result |
| 190 | |
| 191 | def getUserId(self, prefix): |
| 192 | """ |
| 193 | get the user id of a specified user |
| 194 | :param Name prefix: the prefix of the user |
| 195 | :return id of the user, 0 if the user doesn't exist |
| 196 | :rtype: INTEGER |
| 197 | """ |
| 198 | if not self.doesUserExistByPrefix(prefix): |
| 199 | return 0 |
| 200 | cursor = self._database.cursor() |
| 201 | operation = "SELECT id FROM User WHERE prefix=?" |
| 202 | cursor.execute(operation, (prefix.toUri(),)) |
| 203 | result = cursor.fetchone() |
| 204 | userId = result[0] |
| 205 | self._database.commit() |
| 206 | cursor.close() |
| 207 | return userId |
| 208 | |
| 209 | def updateUser(self, prefix, hash_, salt, type_): |
| 210 | """ |
| 211 | update specifided user |
| 212 | :param Name prefix |
| 213 | :param hash_ |
| 214 | :param salt |
| 215 | :param str type_: the type of the user |
| 216 | :return id of the user if successful, 0 if user not found |
| 217 | :rtype int |
| 218 | """ |
| 219 | if not self.doesUserExistByPrefix(prefix): |
| 220 | return 0 |
| 221 | |
| 222 | updateUser = "UPDATE User Set hash=?, salt =?, type=? WHERE prefix=?" |
| 223 | cursor = self._database.cursor() |
| 224 | cursor.execute(updateUser, (hash_, salt, type_, prefix.toUri())) |
| 225 | self._database.commit() |
| 226 | result = cursor.lastrowid |
| 227 | cursor.close() |
| 228 | |
| 229 | def getUserEntry(self, prefix): |
| 230 | """ |
| 231 | get the specified User entry |
| 232 | :param Name prefix: the user prefix |
| 233 | :return the corresponding row of the device |
| 234 | :rtype: str |
| 235 | """ |
| 236 | cursor = self._database.cursor() |
| 237 | cursor.execute("SELECT * FROM User WHERE prefix =?", (prefix.toUri(),)) |
| 238 | row = cursor.fetchone() |
| 239 | cursor.close() |
| 240 | return row |
| 241 | |
| 242 | def deleteUser(self, prefix): |
| 243 | """ |
| 244 | delete specified user. |
| 245 | :param Name prefix: The user prefix |
| 246 | :return: 1 if successful, 0 if no device to delete, otherwise -1. |
| 247 | :rtype: INTEGER |
| 248 | """ |
| 249 | result = -1 |
| 250 | if not self.doesUserExistByPrefix(prefix): |
| 251 | return 0 |
| 252 | cursor = self._database.cursor() |
| 253 | deleteUser = "DELETE FROM User WHERE prefix=?" |
| 254 | cursor.execute(deleteUser, (prefix.toUri(),)) |
| 255 | self._database.commit() |
| 256 | cursor.close() |
| 257 | return 1 |
| 258 | |
| 259 | def doesAccessExist(self, commandId, userId, userDevice=None): |
| 260 | """ |
| 261 | check if the specified access already exists. |
| 262 | |
| 263 | :param int userId: user id |
| 264 | :param int commandId: command id |
| 265 | :param str userdevice: user device name |
| 266 | :return True if the access exists, otherwise False |
| 267 | :rtype: bool |
| 268 | """ |
| 269 | result = False |
| 270 | |
| 271 | cursor = self._database.cursor() |
| 272 | if userDevice == None: |
| 273 | operation = "SELECT count(*) FROM Access WHERE user_id =? AND command_id=? " |
| 274 | data = (userId, commandId) |
| 275 | else: |
| 276 | operation = "SELECT count(*) FROM Access WHERE user_id =? AND command_id=? AND user_device=?" |
| 277 | data = (userId, commandId, userDevice) |
| 278 | cursor.execute(operation, data) |
| 279 | |
| 280 | (count,) = cursor.fetchone() |
| 281 | if count > 0: |
| 282 | result = True |
| 283 | cursor.close() |
| 284 | return result |
| 285 | |
| 286 | def addAccess(self, commandId, userId, userDevice, accessToken): |
| 287 | """ |
| 288 | Add a new access to the Access table, do nothing if the access already exists |
| 289 | |
| 290 | :param int commandId: the command id |
| 291 | :param int userId: the user id |
| 292 | :param int userDevice: the user device |
| 293 | :param HMACKey accessToken: the access token |
| 294 | :return the access id if it's added successfully, 0 if the access already exists, otherwise -1 |
| 295 | :rtype: int |
| 296 | """ |
| 297 | result = -1 |
| 298 | data = (commandId, |
| 299 | userId, |
| 300 | userDevice, |
| 301 | accessToken.getName(), |
| 302 | accessToken.getSequence(), |
| 303 | accessToken.getKey() |
| 304 | ) |
| 305 | |
| 306 | #check if the access already exists, if yes return 0 |
| 307 | if self.doesAccessExist(commandId, userId, userDevice): |
| 308 | return 0 |
| 309 | |
| 310 | insertAccess = ( |
| 311 | "INSERT INTO Access(command_id, user_id, user_device, access_token_name, access_token_sequence, access_token)" |
| 312 | "VALUES(?,?,?,?,?,?)" |
| 313 | ) |
| 314 | cursor = self._database.cursor() |
| 315 | cursor.execute(insertAccess, data) |
| 316 | self._database.commit() |
| 317 | result = cursor.lastrowid |
| 318 | cursor.close() |
| 319 | return result |
| 320 | |
| 321 | def deleteAccess(self, commandId, userId): |
| 322 | """ |
| 323 | delete specified accesses. |
| 324 | :param int commandId: The command id |
| 325 | :param int userId: the user id |
| 326 | :return: 1 if successful, 0 if no access to delete. |
| 327 | :rtype: int |
| 328 | """ |
| 329 | if not self.doesAccessExist(userId, userId): |
| 330 | return 0 |
| 331 | cursor = self._database.cursor() |
| 332 | deleteAccess = "DELETE FROM Access WHERE command_id=? AND user_id=?" |
| 333 | cursor.execute(deleteAccess, (commandId, userId)) |
| 334 | self._database.commit() |
| 335 | cursor.close() |
| 336 | return 1 |
| 337 | |
| 338 | def getCommandsOfUser(self, userId): |
| 339 | """ |
| 340 | get all the commands to which the specified user has access. |
| 341 | :param int userId: user id |
| 342 | :return command id list if any commands exist, otherwise None |
| 343 | """ |
| 344 | cursor = self._database.cursor() |
| 345 | operation = "SELECT DISTINCT command_id FROM Access WHERE user_id=?" |
| 346 | cursor.execute(operation,(userId,)) |
| 347 | result = cursor.fetchall() |
| 348 | cursor.close() |
| 349 | |
| 350 | commandIdList = [] |
| 351 | if result == None: |
| 352 | return commandIdList |
| 353 | else: |
| 354 | for row in result: |
| 355 | commandIdList.append(row[0]) |
| 356 | |
| 357 | return commandIdList |
| 358 | |
| 359 | def getAccessToken(self,commandId, userId, userDevice): |
| 360 | """ |
| 361 | get access token of a specified access |
| 362 | :param int commandId: command id |
| 363 | :param int userId: user id |
| 364 | :param str userDevice: user device |
| 365 | :return access token if access exists, otherwise None |
| 366 | :rtype HMACKey |
| 367 | """ |
| 368 | operation = "SELECT access_token_sequence, access_token, access_token_name FROM Access WHERE command_id = ? AND user_id = ? AND user_device=?" |
| 369 | cursor = self._database.cursor() |
| 370 | cursor.execute(operation, (commandId, userId, userDevice)) |
| 371 | result = cursor.fetchone() |
| 372 | cursor.close() |
| 373 | |
| 374 | if result == None: |
| 375 | return None |
| 376 | else: |
| 377 | accessToken = HMACKey(sequence = result[0], key = result[1], name = result[2]) |
| 378 | return accessToken |
| 379 | |
| 380 | def getUserDevices(self, commandId, userId): |
| 381 | """ |
| 382 | get user devices given commandi id and user id |
| 383 | :param int commandId: command id |
| 384 | :param int userId: user id |
| 385 | :return a list of user device |
| 386 | :rtype list |
| 387 | """ |
| 388 | operation = "SELECT user_device from Access WHERE command_id=? AND user_id=?" |
| 389 | cursor = self._database.cursor() |
| 390 | cursor.execute(operation,(commandId, userId)) |
| 391 | result = cursor.fetchall() |
| 392 | cursor.close() |
| 393 | |
| 394 | userDeviceList = [] |
| 395 | if result == None: |
| 396 | return userDeviceList |
| 397 | |
| 398 | for row in result: |
| 399 | userDeviceList.append(row[0]) |
| 400 | |
| 401 | return userDeviceList |
| 402 | |