diff options
author | Joshua Watt <JPEWhacker@gmail.com> | 2023-11-03 08:26:31 -0600 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-11-09 17:33:03 +0000 |
commit | 1af725b2eca63fa113cedb6d77eb5c5f1de6e2f0 (patch) | |
tree | adf200a0b03b8ee1f1a56c55e2ec657dcc7ed04a /bitbake/lib/hashserv/sqlite.py | |
parent | 6e67b000efb89c4e3121fd907a47dc7042c07bed (diff) | |
download | poky-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.py | 105 |
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 @@ | |||
7 | import sqlite3 | 7 | import sqlite3 |
8 | import logging | 8 | import logging |
9 | from contextlib import closing | 9 | from contextlib import closing |
10 | from . import User | ||
10 | 11 | ||
11 | logger = logging.getLogger("hashserv.sqlite") | 12 | logger = logging.getLogger("hashserv.sqlite") |
12 | 13 | ||
@@ -34,6 +35,14 @@ OUTHASH_TABLE_DEFINITION = ( | |||
34 | 35 | ||
35 | OUTHASH_TABLE_COLUMNS = tuple(name for name, _, _ in OUTHASH_TABLE_DEFINITION) | 36 | OUTHASH_TABLE_COLUMNS = tuple(name for name, _, _ in OUTHASH_TABLE_DEFINITION) |
36 | 37 | ||
38 | USERS_TABLE_DEFINITION = ( | ||
39 | ("username", "TEXT NOT NULL", "UNIQUE"), | ||
40 | ("token", "TEXT NOT NULL", ""), | ||
41 | ("permissions", "TEXT NOT NULL", ""), | ||
42 | ) | ||
43 | |||
44 | USERS_TABLE_COLUMNS = tuple(name for name, _, _ in USERS_TABLE_DEFINITION) | ||
45 | |||
37 | 46 | ||
38 | def _make_table(cursor, name, definition): | 47 | def _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 | ||
65 | def 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 | |||
56 | class DatabaseEngine(object): | 74 | class 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 | ||