|
13 | 13 | # This file handles all flask-restful resources for /v3/credentials
|
14 | 14 |
|
15 | 15 | import hashlib
|
| 16 | +import six |
16 | 17 |
|
17 | 18 | import flask
|
18 | 19 | from oslo_serialization import jsonutils
|
@@ -60,30 +61,41 @@ def _blob_to_json(ref):
|
60 | 61 | ref['blob'] = jsonutils.dumps(blob)
|
61 | 62 | return ref
|
62 | 63 |
|
63 |
| - def _assign_unique_id(self, ref, trust_id=None): |
| 64 | + def _validate_blob_json(self, ref): |
| 65 | + try: |
| 66 | + blob = jsonutils.loads(ref.get('blob')) |
| 67 | + except (ValueError, TabError): |
| 68 | + raise exception.ValidationError( |
| 69 | + message=_('Invalid blob in credential')) |
| 70 | + if not blob or not isinstance(blob, dict): |
| 71 | + raise exception.ValidationError(attribute='blob', |
| 72 | + target='credential') |
| 73 | + if blob.get('access') is None: |
| 74 | + raise exception.ValidationError(attribute='access', |
| 75 | + target='credential') |
| 76 | + return blob |
| 77 | + |
| 78 | + def _assign_unique_id( |
| 79 | + self, ref, trust_id=None, app_cred_id=None, access_token_id=None): |
64 | 80 | # Generates an assigns a unique identifier to a credential reference.
|
65 | 81 | if ref.get('type', '').lower() == 'ec2':
|
66 |
| - try: |
67 |
| - blob = jsonutils.loads(ref.get('blob')) |
68 |
| - except (ValueError, TabError): |
69 |
| - raise exception.ValidationError( |
70 |
| - message=_('Invalid blob in credential')) |
71 |
| - if not blob or not isinstance(blob, dict): |
72 |
| - raise exception.ValidationError(attribute='blob', |
73 |
| - target='credential') |
74 |
| - if blob.get('access') is None: |
75 |
| - raise exception.ValidationError(attribute='access', |
76 |
| - target='credential') |
77 |
| - |
| 82 | + blob = self._validate_blob_json(ref) |
78 | 83 | ref = ref.copy()
|
79 | 84 | ref['id'] = hashlib.sha256(
|
80 | 85 | blob['access'].encode('utf8')).hexdigest()
|
81 |
| - # update the blob with the trust_id, so credentials created with |
82 |
| - # a trust scoped token will result in trust scoped tokens when |
83 |
| - # authentication via ec2tokens happens |
| 86 | + # update the blob with the trust_id or app_cred_id, so credentials |
| 87 | + # created with a trust- or app cred-scoped token will result in |
| 88 | + # trust- or app cred-scoped tokens when authentication via |
| 89 | + # ec2tokens happens |
84 | 90 | if trust_id is not None:
|
85 | 91 | blob['trust_id'] = trust_id
|
86 | 92 | ref['blob'] = jsonutils.dumps(blob)
|
| 93 | + if app_cred_id is not None: |
| 94 | + blob['app_cred_id'] = app_cred_id |
| 95 | + ref['blob'] = jsonutils.dumps(blob) |
| 96 | + if access_token_id is not None: |
| 97 | + blob['access_token_id'] = access_token_id |
| 98 | + ref['blob'] = jsonutils.dumps(blob) |
87 | 99 | return ref
|
88 | 100 | else:
|
89 | 101 | return super(CredentialResource, self)._assign_unique_id(ref)
|
@@ -146,23 +158,47 @@ def post(self):
|
146 | 158 | )
|
147 | 159 | validation.lazy_validate(schema.credential_create, credential)
|
148 | 160 | trust_id = getattr(self.oslo_context, 'trust_id', None)
|
| 161 | + app_cred_id = getattr( |
| 162 | + self.auth_context['token'], 'application_credential_id', None) |
| 163 | + access_token_id = getattr( |
| 164 | + self.auth_context['token'], 'access_token_id', None) |
149 | 165 | ref = self._assign_unique_id(
|
150 |
| - self._normalize_dict(credential), trust_id=trust_id) |
151 |
| - ref = PROVIDERS.credential_api.create_credential(ref['id'], ref, |
152 |
| - initiator=self.audit_initiator) |
| 166 | + self._normalize_dict(credential), |
| 167 | + trust_id=trust_id, app_cred_id=app_cred_id, |
| 168 | + access_token_id=access_token_id) |
| 169 | + ref = PROVIDERS.credential_api.create_credential( |
| 170 | + ref['id'], ref, initiator=self.audit_initiator) |
153 | 171 | return self.wrap_member(ref), http_client.CREATED
|
154 | 172 |
|
| 173 | + def _validate_blob_update_keys(self, credential, ref): |
| 174 | + if credential.get('type', '').lower() == 'ec2': |
| 175 | + new_blob = self._validate_blob_json(ref) |
| 176 | + old_blob = credential.get('blob') |
| 177 | + if isinstance(old_blob, six.string_types): |
| 178 | + old_blob = jsonutils.loads(old_blob) |
| 179 | + # if there was a scope set, prevent changing it or unsetting it |
| 180 | + for key in ['trust_id', 'app_cred_id', 'access_token_id']: |
| 181 | + if old_blob.get(key) != new_blob.get(key): |
| 182 | + message = _('%s can not be updated for credential') % key |
| 183 | + raise exception.ValidationError(message=message) |
| 184 | + |
155 | 185 | def patch(self, credential_id):
|
156 | 186 | # Update Credential
|
157 | 187 | ENFORCER.enforce_call(
|
158 | 188 | action='identity:update_credential',
|
159 | 189 | build_target=_build_target_enforcement
|
160 | 190 | )
|
161 |
| - PROVIDERS.credential_api.get_credential(credential_id) |
| 191 | + current = PROVIDERS.credential_api.get_credential(credential_id) |
162 | 192 |
|
163 | 193 | credential = self.request_body_json.get('credential', {})
|
164 | 194 | validation.lazy_validate(schema.credential_update, credential)
|
| 195 | + self._validate_blob_update_keys(current.copy(), credential.copy()) |
165 | 196 | self._require_matching_id(credential)
|
| 197 | + # Check that the user hasn't illegally modified the owner or scope |
| 198 | + target = {'credential': dict(current, **credential)} |
| 199 | + ENFORCER.enforce_call( |
| 200 | + action='identity:update_credential', target_attr=target |
| 201 | + ) |
166 | 202 | ref = PROVIDERS.credential_api.update_credential(
|
167 | 203 | credential_id, credential)
|
168 | 204 | return self.wrap_member(ref)
|
|
0 commit comments