From f29e43cd1a36ce889e829d76c87ba1400ca26be2 Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Tue, 10 Dec 2024 12:21:41 -0600 Subject: [PATCH 01/10] Add extended reservation checks in the server --- server/src/api/v1.py | 36 ++++++++++++++++- server/tests/conftest.py | 2 + server/tests/test_v1_authorization.py | 58 +++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/server/src/api/v1.py b/server/src/api/v1.py index cad09bab..3991ec60 100644 --- a/server/src/api/v1.py +++ b/server/src/api/v1.py @@ -157,8 +157,29 @@ def check_token_queue(auth_token: str, secret_key: str, queue: str) -> bool: return queue in allowed_queues +def check_token_reservation_timeout( + auth_token: str, secret_key: str, reservation_timeout: int, queue: str +) -> bool: + """ + Checks if the requested reservation is either less than the max + or that their token gives them the permission to use a higher one + """ + # Max reservation time defaults to 6 hours + max_reservation_time = 6 * 60 * 60 + if reservation_timeout <= max_reservation_time: + return True + decoded_jwt = decode_jwt_token(auth_token, secret_key) + max_reservation_time_dict = decoded_jwt.get("max_reservation_time", {}) + max_reservation_time = max_reservation_time_dict.get(queue, 0) + return reservation_timeout <= max_reservation_time + + def check_token_permissions( - auth_token: str, secret_key: str, priority: int, queue: str + auth_token: str, + secret_key: str, + priority: int, + queue: str, + reservation_timeout: int, ) -> bool: """ Validates token received from client and checks if it can @@ -168,7 +189,10 @@ def check_token_permissions( auth_token, secret_key, queue, priority ) queue_allowed = check_token_queue(auth_token, secret_key, queue) - return priority_allowed and queue_allowed + reservation_time_allowed = check_token_reservation_timeout( + auth_token, secret_key, reservation_timeout, queue + ) + return priority_allowed and queue_allowed and reservation_time_allowed def job_builder(data: dict, auth_token: str): @@ -196,11 +220,15 @@ def job_builder(data: dict, auth_token: str): priority_level = data.get("job_priority", 0) job_queue = data["job_queue"] + reserve_data = data.get("reserve_data", {}) + # default reservation timeout is 1 hour + reservation_timeout = reserve_data.get("timeout", 3600) allowed = check_token_permissions( auth_token, os.environ.get("JWT_SIGNING_KEY"), priority_level, job_queue, + reservation_timeout, ) if not allowed: abort( @@ -766,6 +794,10 @@ def generate_token(allowed_resources, secret_key): token_payload["max_priority"] = allowed_resources["max_priority"] if "allowed_queues" in allowed_resources: token_payload["allowed_queues"] = allowed_resources["allowed_queues"] + if "max_reservation_time" in allowed_resources: + token_payload["max_reservation_time"] = allowed_resources[ + "max_reservation_time" + ] token = jwt.encode(token_payload, secret_key, algorithm="HS256") return token diff --git a/server/tests/conftest.py b/server/tests/conftest.py index 849ec9a3..0a2f5898 100644 --- a/server/tests/conftest.py +++ b/server/tests/conftest.py @@ -84,12 +84,14 @@ def mongo_app_with_permissions(mongo_app): "myqueue2": 200, } allowed_queues = ["rqueue1", "rqueue2"] + max_reservation_time = {"myqueue": 30000} mongo.client_permissions.insert_one( { "client_id": client_id, "client_secret_hash": client_key_hash, "max_priority": max_priority, "allowed_queues": allowed_queues, + "max_reservation_time": max_reservation_time, } ) restricted_queues = [ diff --git a/server/tests/test_v1_authorization.py b/server/tests/test_v1_authorization.py index b05ebd08..7dddd349 100644 --- a/server/tests/test_v1_authorization.py +++ b/server/tests/test_v1_authorization.py @@ -316,3 +316,61 @@ def test_restricted_queue_reject_no_token(mongo_app_with_permissions): job = {"job_queue": "rqueue1"} job_response = app.post("/v1/job", json=job) assert 401 == job_response.status_code + + +def test_extended_reservation_allowed(mongo_app_with_permissions): + """ + Tests that jobs that include extended reservation are accepted when + the token gives them permission + """ + app, _, client_id, client_key, _ = mongo_app_with_permissions + authenticate_output = app.post( + "/v1/oauth2/token", + headers=create_auth_header(client_id, client_key), + ) + token = authenticate_output.data.decode("utf-8") + job = {"job_queue": "myqueue", "reserve_data": {"timeout": 30000}} + job_response = app.post( + "/v1/job", json=job, headers={"Authorization": token} + ) + assert 200 == job_response.status_code + + +def test_extended_reservation_rejected(mongo_app_with_permissions): + """ + Tests that jobs that include extended reservation are rejected when + the token does not give them permission + """ + app, _, client_id, client_key, _ = mongo_app_with_permissions + authenticate_output = app.post( + "/v1/oauth2/token", + headers=create_auth_header(client_id, client_key), + ) + token = authenticate_output.data.decode("utf-8") + job = {"job_queue": "myqueue2", "reserve_data": {"timeout": 21601}} + job_response = app.post( + "/v1/job", json=job, headers={"Authorization": token} + ) + assert 403 == job_response.status_code + + +def test_extended_reservation_reject_no_token(mongo_app_with_permissions): + """ + Tests that jobs that included extended reservation are rejected + when no token is included + """ + app, _, _, _, _ = mongo_app_with_permissions + job = {"job_queue": "myqueue", "reserve_data": {"timeout": 21601}} + job_response = app.post("/v1/job", json=job) + assert 401 == job_response.status_code + + +def test_normal_reservation_no_token(mongo_app): + """ + Tests that jobs that include reservation times less than the maximum + are accepted when no token is included + """ + app, _ = mongo_app + job = {"job_queue": "myqueue", "reserve_data": {"timeout": 21600}} + job_response = app.post("/v1/job", json=job) + assert 200 == job_response.status_code From acaa7296fe77492cc6bbb77b1d6ca6ec9a4a54d5 Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Thu, 12 Dec 2024 16:04:28 -0600 Subject: [PATCH 02/10] Added support for star permissions for extended reservations --- server/src/api/v1.py | 4 +++- server/tests/test_v1_authorization.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/server/src/api/v1.py b/server/src/api/v1.py index 3991ec60..e19f32fe 100644 --- a/server/src/api/v1.py +++ b/server/src/api/v1.py @@ -170,7 +170,9 @@ def check_token_reservation_timeout( return True decoded_jwt = decode_jwt_token(auth_token, secret_key) max_reservation_time_dict = decoded_jwt.get("max_reservation_time", {}) - max_reservation_time = max_reservation_time_dict.get(queue, 0) + queue_reservation_time = max_reservation_time_dict.get(queue, 0) + star_reservation_time = max_reservation_time_dict.get("*", 0) + max_reservation_time = max(queue_reservation_time, star_reservation_time) return reservation_timeout <= max_reservation_time diff --git a/server/tests/test_v1_authorization.py b/server/tests/test_v1_authorization.py index 7dddd349..d157bcda 100644 --- a/server/tests/test_v1_authorization.py +++ b/server/tests/test_v1_authorization.py @@ -374,3 +374,25 @@ def test_normal_reservation_no_token(mongo_app): job = {"job_queue": "myqueue", "reserve_data": {"timeout": 21600}} job_response = app.post("/v1/job", json=job) assert 200 == job_response.status_code + + +def test_star_extended_reservation(mongo_app_with_permissions): + """ + Tests submission to generic queue with extended reservation + when client has star permissions + """ + app, mongo, client_id, client_key, _ = mongo_app_with_permissions + mongo.client_permissions.find_one_and_update( + {"client_id": client_id}, + {"$set": {"max_reservation_time": {"*": 30000}}}, + ) + authenticate_output = app.post( + "/v1/oauth2/token", + headers=create_auth_header(client_id, client_key), + ) + token = authenticate_output.data.decode("utf-8") + job = {"job_queue": "myrandomqueue", "reserve_data": {"timeout": 30000}} + job_response = app.post( + "/v1/job", json=job, headers={"Authorization": token} + ) + assert 200 == job_response.status_code From 45353efc77eab8a08755f8a018efb006230e8596 Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Thu, 12 Dec 2024 16:27:29 -0600 Subject: [PATCH 03/10] Add extended reservation documentation --- docs/explanation/extended-reservation.rst | 10 ++++++++++ docs/explanation/index.rst | 1 + docs/reference/test-phases.rst | 1 + 3 files changed, 12 insertions(+) create mode 100644 docs/explanation/extended-reservation.rst diff --git a/docs/explanation/extended-reservation.rst b/docs/explanation/extended-reservation.rst new file mode 100644 index 00000000..d4116ad0 --- /dev/null +++ b/docs/explanation/extended-reservation.rst @@ -0,0 +1,10 @@ +Extended Reservation +============ + +Normal users can reserve a machine with Testflinger for a maximum of 6 hours. +However, for certain use cases this is limiting. Testflinger has the ability +to allow authorised clients to request longer reservation times. See the Reserve +section of the :doc:`Test Phase <../reference/test-phases>` documentation for more +information on how to set reservation times. +Using this feature requires :doc:`authenticating <./authentication>` with +Testflinger server. diff --git a/docs/explanation/index.rst b/docs/explanation/index.rst index 9a1e2d57..54189392 100644 --- a/docs/explanation/index.rst +++ b/docs/explanation/index.rst @@ -10,4 +10,5 @@ This section covers conceptual questions about Testflinger. queues job-priority restricted-queues + extended-reservation authentication diff --git a/docs/reference/test-phases.rst b/docs/reference/test-phases.rst index 3ef962fe..07c8ac85 100644 --- a/docs/reference/test-phases.rst +++ b/docs/reference/test-phases.rst @@ -178,6 +178,7 @@ Variables in ``reserve_data``: * ``ssh_keys``: The list of public SSH keys to use for reserving the device. Each line includes an identity provider name and your username on the provider's system. Testflinger uses the ``ssh-import-id`` command to import public SSH keys from trusted, online identity. Supported identities are Launchpad (``lp``) and GitHub (``gh``). * ``timeout``: Reservation time in seconds. The default is one hour (3600), and you can request a reservation for up to 6 hours (21600). + Authenticated clients can request longer :doc:`reservation times <../explanation/extended-reservation>` with prior authorisation. If either ``reserve_command`` is missing from the agent configuration, or the the ``reserve_data`` section is missing from the job, this phase will be skipped. From c730abddc721c117ba11ac6a62b490103814a6e6 Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Wed, 18 Dec 2024 00:25:35 -0600 Subject: [PATCH 04/10] Remove max reservation time from device connector --- .../src/testflinger_device_connectors/devices/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/device-connectors/src/testflinger_device_connectors/devices/__init__.py b/device-connectors/src/testflinger_device_connectors/devices/__init__.py index f8d897b6..1fb7f940 100644 --- a/device-connectors/src/testflinger_device_connectors/devices/__init__.py +++ b/device-connectors/src/testflinger_device_connectors/devices/__init__.py @@ -276,12 +276,6 @@ def reserve(self, args): logger.error("Failed to copy ssh key: %s", key) # default reservation timeout is 1 hour timeout = int(reserve_data.get("timeout", "3600")) - # If max_reserve_timeout isn't specified, default to 6 hours - max_reserve_timeout = int( - config.get("max_reserve_timeout", 6 * 60 * 60) - ) - if timeout > max_reserve_timeout: - timeout = max_reserve_timeout serial_host = config.get("serial_host") serial_port = config.get("serial_port") print("*** TESTFLINGER SYSTEM RESERVED ***") From edb3431180ccf8522db83401495880f303886173 Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Thu, 19 Dec 2024 17:16:12 -0600 Subject: [PATCH 05/10] Minor changes --- server/src/api/v1.py | 47 ++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/server/src/api/v1.py b/server/src/api/v1.py index e19f32fe..4c624a80 100644 --- a/server/src/api/v1.py +++ b/server/src/api/v1.py @@ -179,20 +179,24 @@ def check_token_reservation_timeout( def check_token_permissions( auth_token: str, secret_key: str, - priority: int, - queue: str, - reservation_timeout: int, + job_data: dict, ) -> bool: """ Validates token received from client and checks if it can push a job to the queue with the requested priority """ + priority_level = job_data.get("job_priority", 0) + job_queue = job_data["job_queue"] priority_allowed = check_token_priority( - auth_token, secret_key, queue, priority + auth_token, secret_key, job_queue, priority_level ) - queue_allowed = check_token_queue(auth_token, secret_key, queue) + queue_allowed = check_token_queue(auth_token, secret_key, job_queue) + + reserve_data = job_data.get("reserve_data", {}) + # default reservation timeout is 1 hour + reservation_timeout = reserve_data.get("timeout", 3600) reservation_time_allowed = check_token_reservation_timeout( - auth_token, secret_key, reservation_timeout, queue + auth_token, secret_key, reservation_timeout, job_queue ) return priority_allowed and queue_allowed and reservation_time_allowed @@ -222,15 +226,10 @@ def job_builder(data: dict, auth_token: str): priority_level = data.get("job_priority", 0) job_queue = data["job_queue"] - reserve_data = data.get("reserve_data", {}) - # default reservation timeout is 1 hour - reservation_timeout = reserve_data.get("timeout", 3600) allowed = check_token_permissions( auth_token, os.environ.get("JWT_SIGNING_KEY"), - priority_level, - job_queue, - reservation_timeout, + data, ) if not allowed: abort( @@ -792,14 +791,7 @@ def generate_token(allowed_resources, secret_key): "iat": datetime.now(timezone.utc), # Issued at time "sub": "access_token", } - if "max_priority" in allowed_resources: - token_payload["max_priority"] = allowed_resources["max_priority"] - if "allowed_queues" in allowed_resources: - token_payload["allowed_queues"] = allowed_resources["allowed_queues"] - if "max_reservation_time" in allowed_resources: - token_payload["max_reservation_time"] = allowed_resources[ - "max_reservation_time" - ] + token_payload.update(allowed_resources) token = jwt.encode(token_payload, secret_key, algorithm="HS256") return token @@ -822,12 +814,25 @@ def validate_client_key_pair(client_id: str, client_key: str): client_permissions_entry["client_secret_hash"].encode("utf8"), ): return None + client_permissions_entry.pop("_id", None) return client_permissions_entry @v1.post("/oauth2/token") def retrieve_token(): - """Get JWT with priority and queue permissions""" + """ + Get JWT with priority and queue permissions + + Before being encrypted, the JWT can contain fields like: + { + exp: , + iat: , + sub: , + max_priority: , + allowed_queues: , + max_reservation_time: , + } + """ auth_header = request.authorization if auth_header is None: return "No authorization header specified", 401 From afd5a684d87330fb1cc83e1e8c86d43cb670aa67 Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Fri, 20 Dec 2024 08:57:53 -0600 Subject: [PATCH 06/10] Minor changes to docstring --- server/src/api/v1.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/api/v1.py b/server/src/api/v1.py index 4c624a80..e6d95146 100644 --- a/server/src/api/v1.py +++ b/server/src/api/v1.py @@ -784,7 +784,11 @@ def get_agents_on_queue(queue_name): def generate_token(allowed_resources, secret_key): - """Generates JWT token with queue permission given a secret key""" + """ + Generates JWT token with queue permission given a secret key + See retrieve_token for more information on the contents of + the token payload + """ expiration_time = datetime.utcnow() + timedelta(seconds=2) token_payload = { "exp": expiration_time, From 8ee07d8a3faef39575109577dd19268fee188d47 Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Thu, 9 Jan 2025 17:22:06 -0600 Subject: [PATCH 07/10] Refactor token checking --- server/src/api/v1.py | 59 +++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/server/src/api/v1.py b/server/src/api/v1.py index e6d95146..56f27bb0 100644 --- a/server/src/api/v1.py +++ b/server/src/api/v1.py @@ -135,31 +135,45 @@ def check_token_priority( specified in the authorization token if it exists """ if priority == 0: - return True + return decoded_jwt = decode_jwt_token(auth_token, secret_key) max_priority_dict = decoded_jwt.get("max_priority", {}) star_priority = max_priority_dict.get("*", 0) queue_priority = max_priority_dict.get(queue, 0) max_priority = max(star_priority, queue_priority) - return priority <= max_priority + if priority > max_priority: + abort( + 403, + ( + f"Not enough permissions to push to {queue}", + f"with priority {priority}", + ), + ) -def check_token_queue(auth_token: str, secret_key: str, queue: str) -> bool: +def check_token_queue(auth_token: str, secret_key: str, queue: str): """ Checks if the queue is in the restricted list. If it is, then it checks the authorization token for restricted queues the user is allowed to use. """ if not database.check_queue_restricted(queue): - return True + return decoded_jwt = decode_jwt_token(auth_token, secret_key) allowed_queues = decoded_jwt.get("allowed_queues", []) - return queue in allowed_queues + if queue not in allowed_queues: + abort( + 403, + ( + "Not enough permissions to push to the ", + f"restricted queue: {queue}", + ), + ) def check_token_reservation_timeout( auth_token: str, secret_key: str, reservation_timeout: int, queue: str -) -> bool: +): """ Checks if the requested reservation is either less than the max or that their token gives them the permission to use a higher one @@ -167,38 +181,42 @@ def check_token_reservation_timeout( # Max reservation time defaults to 6 hours max_reservation_time = 6 * 60 * 60 if reservation_timeout <= max_reservation_time: - return True + return decoded_jwt = decode_jwt_token(auth_token, secret_key) max_reservation_time_dict = decoded_jwt.get("max_reservation_time", {}) queue_reservation_time = max_reservation_time_dict.get(queue, 0) star_reservation_time = max_reservation_time_dict.get("*", 0) max_reservation_time = max(queue_reservation_time, star_reservation_time) - return reservation_timeout <= max_reservation_time + if reservation_timeout > max_reservation_time: + abort( + 403, + ( + f"Not enough permissions to push to {queue}", + f"with reservation timeout {reservation_timeout}", + ), + ) def check_token_permissions( auth_token: str, secret_key: str, job_data: dict, -) -> bool: +): """ Validates token received from client and checks if it can push a job to the queue with the requested priority """ priority_level = job_data.get("job_priority", 0) job_queue = job_data["job_queue"] - priority_allowed = check_token_priority( - auth_token, secret_key, job_queue, priority_level - ) - queue_allowed = check_token_queue(auth_token, secret_key, job_queue) + check_token_priority(auth_token, secret_key, job_queue, priority_level) + check_token_queue(auth_token, secret_key, job_queue) reserve_data = job_data.get("reserve_data", {}) # default reservation timeout is 1 hour reservation_timeout = reserve_data.get("timeout", 3600) - reservation_time_allowed = check_token_reservation_timeout( + check_token_reservation_timeout( auth_token, secret_key, reservation_timeout, job_queue ) - return priority_allowed and queue_allowed and reservation_time_allowed def job_builder(data: dict, auth_token: str): @@ -225,20 +243,11 @@ def job_builder(data: dict, auth_token: str): data["attachments_status"] = "waiting" priority_level = data.get("job_priority", 0) - job_queue = data["job_queue"] - allowed = check_token_permissions( + check_token_permissions( auth_token, os.environ.get("JWT_SIGNING_KEY"), data, ) - if not allowed: - abort( - 403, - ( - f"Not enough permissions to push to {job_queue}", - f"with priority {priority_level}", - ), - ) job["job_priority"] = priority_level job["job_id"] = job_id From cde4be527b73a09240e6572f4790afeee615c8bc Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Thu, 9 Jan 2025 17:48:04 -0600 Subject: [PATCH 08/10] Add permissions field to jwt token --- server/src/api/v1.py | 20 +++++++++++++------- server/tests/test_v1_authorization.py | 12 ++++++++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/server/src/api/v1.py b/server/src/api/v1.py index 56f27bb0..b881c8ed 100644 --- a/server/src/api/v1.py +++ b/server/src/api/v1.py @@ -137,7 +137,8 @@ def check_token_priority( if priority == 0: return decoded_jwt = decode_jwt_token(auth_token, secret_key) - max_priority_dict = decoded_jwt.get("max_priority", {}) + permissions = decoded_jwt.get("permissions", {}) + max_priority_dict = permissions.get("max_priority", {}) star_priority = max_priority_dict.get("*", 0) queue_priority = max_priority_dict.get(queue, 0) max_priority = max(star_priority, queue_priority) @@ -160,7 +161,8 @@ def check_token_queue(auth_token: str, secret_key: str, queue: str): if not database.check_queue_restricted(queue): return decoded_jwt = decode_jwt_token(auth_token, secret_key) - allowed_queues = decoded_jwt.get("allowed_queues", []) + permissions = decoded_jwt.get("permissions", {}) + allowed_queues = permissions.get("allowed_queues", []) if queue not in allowed_queues: abort( 403, @@ -183,7 +185,8 @@ def check_token_reservation_timeout( if reservation_timeout <= max_reservation_time: return decoded_jwt = decode_jwt_token(auth_token, secret_key) - max_reservation_time_dict = decoded_jwt.get("max_reservation_time", {}) + permissions = decoded_jwt.get("permissions", {}) + max_reservation_time_dict = permissions.get("max_reservation_time", {}) queue_reservation_time = max_reservation_time_dict.get(queue, 0) star_reservation_time = max_reservation_time_dict.get("*", 0) max_reservation_time = max(queue_reservation_time, star_reservation_time) @@ -803,8 +806,9 @@ def generate_token(allowed_resources, secret_key): "exp": expiration_time, "iat": datetime.now(timezone.utc), # Issued at time "sub": "access_token", + "permissions": {}, } - token_payload.update(allowed_resources) + token_payload.get("permissions").update(allowed_resources) token = jwt.encode(token_payload, secret_key, algorithm="HS256") return token @@ -841,9 +845,11 @@ def retrieve_token(): exp: , iat: , sub: , - max_priority: , - allowed_queues: , - max_reservation_time: , + permissions: { + max_priority: , + allowed_queues: , + max_reservation_time: , + } } """ auth_header = request.authorization diff --git a/server/tests/test_v1_authorization.py b/server/tests/test_v1_authorization.py index d157bcda..bdf6b9a3 100644 --- a/server/tests/test_v1_authorization.py +++ b/server/tests/test_v1_authorization.py @@ -50,9 +50,9 @@ def test_retrieve_token(mongo_app_with_permissions): token, os.environ.get("JWT_SIGNING_KEY"), algorithms="HS256", - options={"require": ["exp", "iat", "sub", "max_priority"]}, + options={"require": ["exp", "iat", "sub", "permissions"]}, ) - assert decoded_token["max_priority"] == max_priority + assert decoded_token["permissions"]["max_priority"] == max_priority def test_retrieve_token_invalid_client_id(mongo_app_with_permissions): @@ -147,7 +147,9 @@ def test_priority_expired_token(mongo_app_with_permissions): "exp": datetime.utcnow() - timedelta(seconds=2), "iat": datetime.utcnow() - timedelta(seconds=4), "sub": "access_token", - "max_priority": {}, + "permissions": { + "max_priority": {}, + }, } token = jwt.encode(expired_token_payload, secret_key, algorithm="HS256") job = {"job_queue": "myqueue", "job_priority": 100} @@ -163,7 +165,9 @@ def test_missing_fields_in_token(mongo_app_with_permissions): app, _, _, _, _ = mongo_app_with_permissions secret_key = os.environ.get("JWT_SIGNING_KEY") incomplete_token_payload = { - "max_priority": {}, + "permissions": { + "max_priority": {}, + } } token = jwt.encode(incomplete_token_payload, secret_key, algorithm="HS256") job = {"job_queue": "myqueue", "job_priority": 100} From 8462860781d5e574d9ea621ca2cc9144b85a2e98 Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Thu, 9 Jan 2025 17:53:13 -0600 Subject: [PATCH 09/10] Remove secret hash from returned client permissions --- server/src/api/v1.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/api/v1.py b/server/src/api/v1.py index b881c8ed..56744e39 100644 --- a/server/src/api/v1.py +++ b/server/src/api/v1.py @@ -832,6 +832,7 @@ def validate_client_key_pair(client_id: str, client_key: str): ): return None client_permissions_entry.pop("_id", None) + client_permissions_entry.pop("client_secret_hash", None) return client_permissions_entry From 81435a8e5d4da52b977195e22f7f7010e080c7f4 Mon Sep 17 00:00:00 2001 From: Varun Valada Date: Thu, 23 Jan 2025 16:02:38 -0600 Subject: [PATCH 10/10] Minor fix --- server/src/api/v1.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/api/v1.py b/server/src/api/v1.py index 56744e39..e5735d96 100644 --- a/server/src/api/v1.py +++ b/server/src/api/v1.py @@ -806,9 +806,8 @@ def generate_token(allowed_resources, secret_key): "exp": expiration_time, "iat": datetime.now(timezone.utc), # Issued at time "sub": "access_token", - "permissions": {}, + "permissions": allowed_resources, } - token_payload.get("permissions").update(allowed_resources) token = jwt.encode(token_payload, secret_key, algorithm="HS256") return token