Coverage for webapp/endpoints/signing_keys.py: 97%
78 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-05 22:06 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-05 22:06 +0000
1# Packages
2import flask
3from flask import make_response
4from canonicalwebteam.exceptions import (
5 StoreApiResponseErrorList,
6)
7from canonicalwebteam.store_api.publishergw import PublisherGW
9# Local
10from webapp.decorators import login_required, exchange_required
11from webapp.helpers import api_publisher_session, get_brand_id
12from webapp.endpoints.models import get_models, get_policies
15publisher_gateway = PublisherGW("snap", api_publisher_session)
17signing_keys = flask.Blueprint(
18 "signing_keys",
19 __name__,
20)
23@signing_keys.route("/api/store/<store_id>/signing-keys")
24@login_required
25@exchange_required
26def get_signing_keys(store_id: str):
27 brand_id = get_brand_id(flask.session, store_id)
28 res = {}
29 try:
30 signing_keys = publisher_gateway.get_store_signing_keys(
31 flask.session, brand_id
32 )
33 res["data"] = signing_keys
34 res["success"] = True
35 response = make_response(res, 200)
36 response.cache_control.max_age = 3600
37 return response
38 except StoreApiResponseErrorList as error_list:
39 res["success"] = False
40 res["success"] = False
41 res["message"] = " ".join(
42 [
43 f"{error.get('message', 'An error occurred')}"
44 for error in error_list.errors
45 ]
46 )
47 res["data"] = []
48 return make_response(res, 500)
51@signing_keys.route("/api/store/<store_id>/signing-keys", methods=["POST"])
52@login_required
53@exchange_required
54def create_signing_key(store_id: str):
55 name = flask.request.form.get("name")
56 res = {}
58 try:
59 if name and len(name) <= 128:
60 publisher_gateway.create_store_signing_key(
61 flask.session, store_id, name
62 )
63 res["success"] = True
64 return make_response(res, 200)
65 else:
66 res["message"] = "Invalid signing key. Limit 128 characters"
67 res["success"] = False
68 make_response(res, 500)
69 except StoreApiResponseErrorList as error_list:
70 res["success"] = False
71 res["message"] = error_list.errors[0]["message"]
73 return make_response(res, 500)
76@signing_keys.route(
77 "/api/store/<store_id>/signing-keys/<signing_key_sha3_384>",
78 methods=["DELETE"],
79)
80@login_required
81@exchange_required
82def delete_signing_key(store_id: str, signing_key_sha3_384: str):
83 """
84 Deletes a signing key from the store.
86 Args:
87 store_id (str): The ID of the store.
88 signing_key_sha3_384 (str): The signing key to delete.
90 Returns:
91 Response: A response object with the following fields:
92 - success (bool): True if the signing key was deleted successfully,
93 False otherwise.
94 - message (str): A message describing the result of the deletion.
95 - data (dict): A dictionary containing models where the signing
96 key is used.
97 """
98 res = {}
100 try:
101 response = publisher_gateway.delete_store_signing_key(
102 flask.session, store_id, signing_key_sha3_384
103 )
105 if response.status_code == 204:
106 res["success"] = True
107 return make_response(res, 200)
108 elif response.status_code == 404:
109 res["success"] = False
110 res["message"] = "Signing key not found"
111 return make_response(res, 404)
112 except StoreApiResponseErrorList as error_list:
113 message = error_list.errors[0]["message"]
114 if (
115 error_list.status_code == 409
116 and "used to sign at least one serial policy" in message
117 ):
118 matching_models = []
119 models_response = get_models(store_id).json
120 models = models_response.get("data", [])
122 for model in models:
123 policies_resp = get_policies(store_id, model["name"]).json
124 policies = policies_resp.get("data", [])
125 matching_policies = [
126 {"revision": policy["revision"]}
127 for policy in policies
128 if policy["signing-key-sha3-384"] == signing_key_sha3_384
129 ]
130 if matching_policies:
131 matching_models.append(
132 {
133 "name": model["name"],
134 "policies": matching_policies,
135 }
136 )
137 res["data"] = {"models": matching_models}
138 res["message"] = "Signing key is used in at least one policy"
139 res["success"] = False
140 else:
141 res["success"] = False
142 res["message"] = error_list.errors[0]["message"]
144 return make_response(res, 500)