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

1# Packages 

2import flask 

3from flask import make_response 

4from canonicalwebteam.exceptions import ( 

5 StoreApiResponseErrorList, 

6) 

7from canonicalwebteam.store_api.publishergw import PublisherGW 

8 

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 

13 

14 

15publisher_gateway = PublisherGW("snap", api_publisher_session) 

16 

17signing_keys = flask.Blueprint( 

18 "signing_keys", 

19 __name__, 

20) 

21 

22 

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) 

49 

50 

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 = {} 

57 

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"] 

72 

73 return make_response(res, 500) 

74 

75 

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. 

85 

86 Args: 

87 store_id (str): The ID of the store. 

88 signing_key_sha3_384 (str): The signing key to delete. 

89 

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 = {} 

99 

100 try: 

101 response = publisher_gateway.delete_store_signing_key( 

102 flask.session, store_id, signing_key_sha3_384 

103 ) 

104 

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", []) 

121 

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"] 

143 

144 return make_response(res, 500)