summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/hashserv/sqlite.py
diff options
context:
space:
mode:
authorJoshua Watt <JPEWhacker@gmail.com>2023-11-03 08:26:31 -0600
committerRichard Purdie <richard.purdie@linuxfoundation.org>2023-11-09 17:33:03 +0000
commit1af725b2eca63fa113cedb6d77eb5c5f1de6e2f0 (patch)
treeadf200a0b03b8ee1f1a56c55e2ec657dcc7ed04a /bitbake/lib/hashserv/sqlite.py
parent6e67b000efb89c4e3121fd907a47dc7042c07bed (diff)
downloadpoky-1af725b2eca63fa113cedb6d77eb5c5f1de6e2f0.tar.gz
bitbake: hashserv: Add user permissions
Adds support for the hashserver to have per-user permissions. User management is done via a new "auth" RPC API where a client can authenticate itself with the server using a randomly generated token. The user can then be given permissions to read, report, manage the database, or manage other users. In addition to explicit user logins, the server supports anonymous users which is what all users start as before they make the "auth" RPC call. Anonymous users can be assigned a set of permissions by the server, making it unnecessary for users to authenticate to use the server. The set of Anonymous permissions defines the default behavior of the server, for example if set to "@read", Anonymous users are unable to report equivalent hashes with authenticating. Similarly, setting the Anonymous permissions to "@none" would require authentication for users to perform any action. User creation and management is entirely manual (although bitbake-hashclient is very useful as a front end). There are many different mechanisms that could be implemented to allow user self-registration (e.g. OAuth, LDAP, etc.), and implementing these is outside the scope of the server. Instead, it is recommended to implement a registration service that validates users against the necessary service, then adds them as a user in the hash equivalence server. (Bitbake rev: 69e5417413ee2414fffaa7dd38057573bac56e35) Signed-off-by: Joshua Watt <JPEWhacker@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/hashserv/sqlite.py')
-rw-r--r--bitbake/lib/hashserv/sqlite.py105
1 files changed, 105 insertions, 0 deletions
diff --git a/bitbake/lib/hashserv/sqlite.py b/bitbake/lib/hashserv/sqlite.py
index 6809c53706..414ee8ffb8 100644
--- a/bitbake/lib/hashserv/sqlite.py
+++ b/bitbake/lib/hashserv/sqlite.py
@@ -7,6 +7,7 @@
7import sqlite3 7import sqlite3
8import logging 8import logging
9from contextlib import closing 9from contextlib import closing
10from . import User
10 11
11logger = logging.getLogger("hashserv.sqlite") 12logger = logging.getLogger("hashserv.sqlite")
12 13
@@ -34,6 +35,14 @@ OUTHASH_TABLE_DEFINITION = (
34 35
35OUTHASH_TABLE_COLUMNS = tuple(name for name, _, _ in OUTHASH_TABLE_DEFINITION) 36OUTHASH_TABLE_COLUMNS = tuple(name for name, _, _ in OUTHASH_TABLE_DEFINITION)
36 37
38USERS_TABLE_DEFINITION = (
39 ("username", "TEXT NOT NULL", "UNIQUE"),
40 ("token", "TEXT NOT NULL", ""),
41 ("permissions", "TEXT NOT NULL", ""),
42)
43
44USERS_TABLE_COLUMNS = tuple(name for name, _, _ in USERS_TABLE_DEFINITION)
45
37 46
38def _make_table(cursor, name, definition): 47def _make_table(cursor, name, definition):
39 cursor.execute( 48 cursor.execute(
@@ -53,6 +62,15 @@ def _make_table(cursor, name, definition):
53 ) 62 )
54 63
55 64
65def map_user(row):
66 if row is None:
67 return None
68 return User(
69 username=row["username"],
70 permissions=set(row["permissions"].split()),
71 )
72
73
56class DatabaseEngine(object): 74class DatabaseEngine(object):
57 def __init__(self, dbname, sync): 75 def __init__(self, dbname, sync):
58 self.dbname = dbname 76 self.dbname = dbname
@@ -66,6 +84,7 @@ class DatabaseEngine(object):
66 with closing(db.cursor()) as cursor: 84 with closing(db.cursor()) as cursor:
67 _make_table(cursor, "unihashes_v2", UNIHASH_TABLE_DEFINITION) 85 _make_table(cursor, "unihashes_v2", UNIHASH_TABLE_DEFINITION)
68 _make_table(cursor, "outhashes_v2", OUTHASH_TABLE_DEFINITION) 86 _make_table(cursor, "outhashes_v2", OUTHASH_TABLE_DEFINITION)
87 _make_table(cursor, "users", USERS_TABLE_DEFINITION)
69 88
70 cursor.execute("PRAGMA journal_mode = WAL") 89 cursor.execute("PRAGMA journal_mode = WAL")
71 cursor.execute( 90 cursor.execute(
@@ -227,6 +246,7 @@ class Database(object):
227 "oldest": oldest, 246 "oldest": oldest,
228 }, 247 },
229 ) 248 )
249 self.db.commit()
230 return cursor.rowcount 250 return cursor.rowcount
231 251
232 async def insert_unihash(self, method, taskhash, unihash): 252 async def insert_unihash(self, method, taskhash, unihash):
@@ -257,3 +277,88 @@ class Database(object):
257 cursor.execute(query, data) 277 cursor.execute(query, data)
258 self.db.commit() 278 self.db.commit()
259 return cursor.lastrowid != prevrowid 279 return cursor.lastrowid != prevrowid
280
281 def _get_user(self, username):
282 with closing(self.db.cursor()) as cursor:
283 cursor.execute(
284 """
285 SELECT username, permissions, token FROM users WHERE username=:username
286 """,
287 {
288 "username": username,
289 },
290 )
291 return cursor.fetchone()
292
293 async def lookup_user_token(self, username):
294 row = self._get_user(username)
295 if row is None:
296 return None, None
297 return map_user(row), row["token"]
298
299 async def lookup_user(self, username):
300 return map_user(self._get_user(username))
301
302 async def set_user_token(self, username, token):
303 with closing(self.db.cursor()) as cursor:
304 cursor.execute(
305 """
306 UPDATE users SET token=:token WHERE username=:username
307 """,
308 {
309 "username": username,
310 "token": token,
311 },
312 )
313 self.db.commit()
314 return cursor.rowcount != 0
315
316 async def set_user_perms(self, username, permissions):
317 with closing(self.db.cursor()) as cursor:
318 cursor.execute(
319 """
320 UPDATE users SET permissions=:permissions WHERE username=:username
321 """,
322 {
323 "username": username,
324 "permissions": " ".join(permissions),
325 },
326 )
327 self.db.commit()
328 return cursor.rowcount != 0
329
330 async def get_all_users(self):
331 with closing(self.db.cursor()) as cursor:
332 cursor.execute("SELECT username, permissions FROM users")
333 return [map_user(r) for r in cursor.fetchall()]
334
335 async def new_user(self, username, permissions, token):
336 with closing(self.db.cursor()) as cursor:
337 try:
338 cursor.execute(
339 """
340 INSERT INTO users (username, token, permissions) VALUES (:username, :token, :permissions)
341 """,
342 {
343 "username": username,
344 "token": token,
345 "permissions": " ".join(permissions),
346 },
347 )
348 self.db.commit()
349 return True
350 except sqlite3.IntegrityError:
351 return False
352
353 async def delete_user(self, username):
354 with closing(self.db.cursor()) as cursor:
355 cursor.execute(
356 """
357 DELETE FROM users WHERE username=:username
358 """,
359 {
360 "username": username,
361 },
362 )
363 self.db.commit()
364 return cursor.rowcount != 0