summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/hashserv/client.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/client.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/client.py')
-rw-r--r--bitbake/lib/hashserv/client.py62
1 files changed, 59 insertions, 3 deletions
diff --git a/bitbake/lib/hashserv/client.py b/bitbake/lib/hashserv/client.py
index 9542d72f6c..82400fe5aa 100644
--- a/bitbake/lib/hashserv/client.py
+++ b/bitbake/lib/hashserv/client.py
@@ -6,6 +6,7 @@
6import logging 6import logging
7import socket 7import socket
8import bb.asyncrpc 8import bb.asyncrpc
9import json
9from . import create_async_client 10from . import create_async_client
10 11
11 12
@@ -16,15 +17,19 @@ class AsyncClient(bb.asyncrpc.AsyncClient):
16 MODE_NORMAL = 0 17 MODE_NORMAL = 0
17 MODE_GET_STREAM = 1 18 MODE_GET_STREAM = 1
18 19
19 def __init__(self): 20 def __init__(self, username=None, password=None):
20 super().__init__('OEHASHEQUIV', '1.1', logger) 21 super().__init__('OEHASHEQUIV', '1.1', logger)
21 self.mode = self.MODE_NORMAL 22 self.mode = self.MODE_NORMAL
23 self.username = username
24 self.password = password
22 25
23 async def setup_connection(self): 26 async def setup_connection(self):
24 await super().setup_connection() 27 await super().setup_connection()
25 cur_mode = self.mode 28 cur_mode = self.mode
26 self.mode = self.MODE_NORMAL 29 self.mode = self.MODE_NORMAL
27 await self._set_mode(cur_mode) 30 await self._set_mode(cur_mode)
31 if self.username:
32 await self.auth(self.username, self.password)
28 33
29 async def send_stream(self, msg): 34 async def send_stream(self, msg):
30 async def proc(): 35 async def proc():
@@ -41,6 +46,7 @@ class AsyncClient(bb.asyncrpc.AsyncClient):
41 if new_mode == self.MODE_NORMAL and self.mode == self.MODE_GET_STREAM: 46 if new_mode == self.MODE_NORMAL and self.mode == self.MODE_GET_STREAM:
42 r = await self._send_wrapper(stream_to_normal) 47 r = await self._send_wrapper(stream_to_normal)
43 if r != "ok": 48 if r != "ok":
49 self.check_invoke_error(r)
44 raise ConnectionError("Unable to transition to normal mode: Bad response from server %r" % r) 50 raise ConnectionError("Unable to transition to normal mode: Bad response from server %r" % r)
45 elif new_mode == self.MODE_GET_STREAM and self.mode == self.MODE_NORMAL: 51 elif new_mode == self.MODE_GET_STREAM and self.mode == self.MODE_NORMAL:
46 r = await self.invoke({"get-stream": None}) 52 r = await self.invoke({"get-stream": None})
@@ -109,9 +115,52 @@ class AsyncClient(bb.asyncrpc.AsyncClient):
109 await self._set_mode(self.MODE_NORMAL) 115 await self._set_mode(self.MODE_NORMAL)
110 return await self.invoke({"clean-unused": {"max_age_seconds": max_age}}) 116 return await self.invoke({"clean-unused": {"max_age_seconds": max_age}})
111 117
118 async def auth(self, username, token):
119 await self._set_mode(self.MODE_NORMAL)
120 result = await self.invoke({"auth": {"username": username, "token": token}})
121 self.username = username
122 self.password = token
123 return result
124
125 async def refresh_token(self, username=None):
126 await self._set_mode(self.MODE_NORMAL)
127 m = {}
128 if username:
129 m["username"] = username
130 result = await self.invoke({"refresh-token": m})
131 if self.username and result["username"] == self.username:
132 self.password = result["token"]
133 return result
134
135 async def set_user_perms(self, username, permissions):
136 await self._set_mode(self.MODE_NORMAL)
137 return await self.invoke({"set-user-perms": {"username": username, "permissions": permissions}})
138
139 async def get_user(self, username=None):
140 await self._set_mode(self.MODE_NORMAL)
141 m = {}
142 if username:
143 m["username"] = username
144 return await self.invoke({"get-user": m})
145
146 async def get_all_users(self):
147 await self._set_mode(self.MODE_NORMAL)
148 return (await self.invoke({"get-all-users": {}}))["users"]
149
150 async def new_user(self, username, permissions):
151 await self._set_mode(self.MODE_NORMAL)
152 return await self.invoke({"new-user": {"username": username, "permissions": permissions}})
153
154 async def delete_user(self, username):
155 await self._set_mode(self.MODE_NORMAL)
156 return await self.invoke({"delete-user": {"username": username}})
157
112 158
113class Client(bb.asyncrpc.Client): 159class Client(bb.asyncrpc.Client):
114 def __init__(self): 160 def __init__(self, username=None, password=None):
161 self.username = username
162 self.password = password
163
115 super().__init__() 164 super().__init__()
116 self._add_methods( 165 self._add_methods(
117 "connect_tcp", 166 "connect_tcp",
@@ -126,7 +175,14 @@ class Client(bb.asyncrpc.Client):
126 "backfill_wait", 175 "backfill_wait",
127 "remove", 176 "remove",
128 "clean_unused", 177 "clean_unused",
178 "auth",
179 "refresh_token",
180 "set_user_perms",
181 "get_user",
182 "get_all_users",
183 "new_user",
184 "delete_user",
129 ) 185 )
130 186
131 def _get_async_client(self): 187 def _get_async_client(self):
132 return AsyncClient() 188 return AsyncClient(self.username, self.password)