"Fossies" - the Fresh Open Source Software Archive

Member "keystone-18.0.0/keystone/api/credentials.py" (14 Oct 2020, 9096 Bytes) of package /linux/misc/openstack/keystone-18.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "credentials.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 17.0.0_vs_18.0.0.

    1 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    2 #    not use this file except in compliance with the License. You may obtain
    3 #    a copy of the License at
    4 #
    5 #         http://www.apache.org/licenses/LICENSE-2.0
    6 #
    7 #    Unless required by applicable law or agreed to in writing, software
    8 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    9 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   10 #    License for the specific language governing permissions and limitations
   11 #    under the License.
   12 
   13 # This file handles all flask-restful resources for /v3/credentials
   14 
   15 import hashlib
   16 
   17 import flask
   18 import http.client
   19 from oslo_serialization import jsonutils
   20 
   21 from keystone.common import provider_api
   22 from keystone.common import rbac_enforcer
   23 from keystone.common import validation
   24 import keystone.conf
   25 from keystone.credential import schema
   26 from keystone import exception
   27 from keystone.i18n import _
   28 from keystone.server import flask as ks_flask
   29 
   30 CONF = keystone.conf.CONF
   31 PROVIDERS = provider_api.ProviderAPIs
   32 ENFORCER = rbac_enforcer.RBACEnforcer
   33 
   34 
   35 def _build_target_enforcement():
   36     target = {}
   37     try:
   38         target['credential'] = PROVIDERS.credential_api.get_credential(
   39             flask.request.view_args.get('credential_id')
   40         )
   41     except exception.NotFound:  # nosec
   42         # Defer existance in the event the credential doesn't exist, we'll
   43         # check this later anyway.
   44         pass
   45 
   46     return target
   47 
   48 
   49 class CredentialResource(ks_flask.ResourceBase):
   50     collection_key = 'credentials'
   51     member_key = 'credential'
   52 
   53     @staticmethod
   54     def _blob_to_json(ref):
   55         # credentials stored via ec2tokens before the fix for #1259584
   56         # need json_serailzing, as that's the documented API format
   57         blob = ref.get('blob')
   58         if isinstance(blob, dict):
   59             ref = ref.copy()
   60             ref['blob'] = jsonutils.dumps(blob)
   61         return ref
   62 
   63     def _validate_blob_json(self, ref):
   64         try:
   65             blob = jsonutils.loads(ref.get('blob'))
   66         except (ValueError, TabError):
   67             raise exception.ValidationError(
   68                 message=_('Invalid blob in credential'))
   69         if not blob or not isinstance(blob, dict):
   70             raise exception.ValidationError(attribute='blob',
   71                                             target='credential')
   72         if blob.get('access') is None:
   73             raise exception.ValidationError(attribute='access',
   74                                             target='credential')
   75         return blob
   76 
   77     def _assign_unique_id(
   78             self, ref, trust_id=None, app_cred_id=None, access_token_id=None):
   79         # Generates an assigns a unique identifier to a credential reference.
   80         if ref.get('type', '').lower() == 'ec2':
   81             blob = self._validate_blob_json(ref)
   82             ref = ref.copy()
   83             ref['id'] = hashlib.sha256(
   84                 blob['access'].encode('utf8')).hexdigest()
   85             # update the blob with the trust_id or app_cred_id, so credentials
   86             # created with a trust- or app cred-scoped token will result in
   87             # trust- or app cred-scoped tokens when authentication via
   88             # ec2tokens happens
   89             if trust_id is not None:
   90                 blob['trust_id'] = trust_id
   91                 ref['blob'] = jsonutils.dumps(blob)
   92             if app_cred_id is not None:
   93                 blob['app_cred_id'] = app_cred_id
   94                 ref['blob'] = jsonutils.dumps(blob)
   95             if access_token_id is not None:
   96                 blob['access_token_id'] = access_token_id
   97                 ref['blob'] = jsonutils.dumps(blob)
   98             return ref
   99         else:
  100             return super(CredentialResource, self)._assign_unique_id(ref)
  101 
  102     def _list_credentials(self):
  103         filters = ['user_id', 'type']
  104         if not self.oslo_context.system_scope:
  105             target = {'credential': {'user_id': self.oslo_context.user_id}}
  106         else:
  107             target = None
  108         ENFORCER.enforce_call(action='identity:list_credentials',
  109                               filters=filters, target_attr=target)
  110         hints = self.build_driver_hints(filters)
  111         refs = PROVIDERS.credential_api.list_credentials(hints)
  112         # If the request was filtered, make sure to return only the
  113         # credentials specific to that user. This makes it so that users with
  114         # roles on projects can't see credentials that aren't theirs.
  115         filtered_refs = []
  116         for ref in refs:
  117             # Check each credential again to make sure the user has access to
  118             # it, either by owning it, being a project admin with
  119             # enforce_scope=false, being a system user, or having some other
  120             # custom policy that allows access.
  121             try:
  122                 cred = PROVIDERS.credential_api.get_credential(ref['id'])
  123                 ENFORCER.enforce_call(
  124                     action='identity:get_credential',
  125                     target_attr={'credential': cred}
  126                 )
  127                 filtered_refs.append(ref)
  128             except exception.Forbidden:
  129                 pass
  130         refs = filtered_refs
  131         refs = [self._blob_to_json(r) for r in refs]
  132         return self.wrap_collection(refs, hints=hints)
  133 
  134     def _get_credential(self, credential_id):
  135         ENFORCER.enforce_call(
  136             action='identity:get_credential',
  137             build_target=_build_target_enforcement
  138         )
  139         credential = PROVIDERS.credential_api.get_credential(credential_id)
  140         return self.wrap_member(self._blob_to_json(credential))
  141 
  142     def get(self, credential_id=None):
  143         # Get Credential or List of credentials.
  144         if credential_id is None:
  145             # No Parameter passed means that we're doing a LIST action.
  146             return self._list_credentials()
  147         else:
  148             return self._get_credential(credential_id)
  149 
  150     def post(self):
  151         # Create a new credential
  152         credential = self.request_body_json.get('credential', {})
  153         target = {}
  154         target['credential'] = credential
  155         ENFORCER.enforce_call(
  156             action='identity:create_credential', target_attr=target
  157         )
  158         validation.lazy_validate(schema.credential_create, credential)
  159         trust_id = getattr(self.oslo_context, 'trust_id', None)
  160         app_cred_id = getattr(
  161             self.auth_context['token'], 'application_credential_id', None)
  162         access_token_id = getattr(
  163             self.auth_context['token'], 'access_token_id', None)
  164         ref = self._assign_unique_id(
  165             self._normalize_dict(credential),
  166             trust_id=trust_id, app_cred_id=app_cred_id,
  167             access_token_id=access_token_id)
  168         ref = PROVIDERS.credential_api.create_credential(
  169             ref['id'], ref, initiator=self.audit_initiator)
  170         return self.wrap_member(ref), http.client.CREATED
  171 
  172     def _validate_blob_update_keys(self, credential, ref):
  173         if credential.get('type', '').lower() == 'ec2':
  174             new_blob = self._validate_blob_json(ref)
  175             old_blob = credential.get('blob')
  176             if isinstance(old_blob, str):
  177                 old_blob = jsonutils.loads(old_blob)
  178             # if there was a scope set, prevent changing it or unsetting it
  179             for key in ['trust_id', 'app_cred_id', 'access_token_id',
  180                         'access_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 
  185     def patch(self, credential_id):
  186         # Update Credential
  187         ENFORCER.enforce_call(
  188             action='identity:update_credential',
  189             build_target=_build_target_enforcement
  190         )
  191         current = PROVIDERS.credential_api.get_credential(credential_id)
  192 
  193         credential = self.request_body_json.get('credential', {})
  194         validation.lazy_validate(schema.credential_update, credential)
  195         self._validate_blob_update_keys(current.copy(), credential.copy())
  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         )
  202         ref = PROVIDERS.credential_api.update_credential(
  203             credential_id, credential)
  204         return self.wrap_member(ref)
  205 
  206     def delete(self, credential_id):
  207         # Delete credentials
  208         ENFORCER.enforce_call(
  209             action='identity:delete_credential',
  210             build_target=_build_target_enforcement
  211         )
  212 
  213         return (PROVIDERS.credential_api.delete_credential(
  214             credential_id, initiator=self.audit_initiator),
  215             http.client.NO_CONTENT)
  216 
  217 
  218 class CredentialAPI(ks_flask.APIBase):
  219 
  220     _name = 'credentials'
  221     _import_name = __name__
  222     resource_mapping = []
  223     resources = [CredentialResource]
  224 
  225 
  226 APIs = (CredentialAPI,)