blob: 3e108ef907d1dac5fb43a17f56df07ded4c31c49 [file] [log] [blame]
Weiwei074778b2015-08-07 21:30:44 -07001# -*- 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"""
19UserAccessStorage implements a basic storage of users and access
20"""
21
22import os
23import sqlite3
24from pyndn import Name
25from device_profile import DeviceProfile
26from hmac_key import HMACKey
27
28INIT_USER_TABLE = ["""
29CREATE 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
42INIT_ACCESS_TABLE = ["""
43CREATE 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
59class 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