keystone_admin_api

本文详细剖析了Keystone Admin API的核心组件与流程,包括业务流程分析、过滤器功能及认证授权机制,全面展示了如何通过配置文件实现API的权限控制与资源管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<paste_deploy function="业务流程分析">
   <keystone-paste.ini path="/etc/keystone/">
      # 我们分析admin
      [composite: admin]
      use = egg:Pate#urlmap
      /v2.0 = admin_api
      /v3 = api_v3
      / = public_version_api
      [pipeline:admin_api]
      pipeline = sizelimit url_normalize request_id build_auth_context token_auth
      admin_token_auth json_body ec2_extension s3_extension crud_extension admin_service
      # 分析/v2.0 入口处理业务
      1-[filter:sizelimit]
      paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory
      <RequestBodySizeLimiter path="oslo_middle.sizelimit">
         _opts = [
            cfg.IntOpt('max_request_body_size',
                     default=114688, # 默认为112k
                     help='...',
                     deprecated_opts=_oldopts)
               ]
         class RequestBodySizeLimiter(base.Middleware):
            # 限制进入的多个请求body大小
            def __init__(self, application, conf=None):

               super(RequestBodySizeLimiter, self).__init__(application, conf)
               <base.Middleware values="" path="oslo_middleware.base">
                  # 最后会调用其factory方法
                  class Middleware(object):
                     # factory会调用子类的__call__方法,在调用前需要初始化
                     @classmethod
                     def factory(cls, global_conf, **local_conf):
                        conf = global_conf.copy() if global_conf else {}
                        conf.update(local_conf)
                        def middleware_filter(app):
                           return cls(app, conf)
                        return middleware_filter

                     def __init__(self, application, conf=None):
                        if isinstance(conf, cfg.COnfigOPts):
                           self.conf = []
                           self.oslo_conf = conf
                        else:
                           self.conf = conf or []
                           if 'oslo_config_project' in self.conf:
                              if 'oslo_config_file' in self.conf:
                                 default_config_files = [self.conf['oslo_config_fil']]
                              else:
                                 default_config_files = None
                              self.oslo_conf = cfg.ConfigOpts()
                              self.oslo_conf([], project=self.conf['oslo_config_project'],
                                          default_config_files=default_config_files,
                                          validate_default_values=True)
                              # 上面的调用 ConfigOpts实例的__call__
                           else:
                              # 直接调用cfg.CONF
                              self.oslo_conf = cfg.CONF
                     def _conf_get(self, key, group='oslo_middleware'):
                        if key in self.conf:
                           self.oslo_conf.set_override(key, self.conf[key], group=group,
                                                enfore_type=True)
                           # 是配置有效
                        return getattr(getattr(self.oslo_conf, group), key)

                  @staticmethod
                  def process_request(req):
                     # 对每个请求进行处理
                     return None

                  @staticmethod
                  def process_response(response, request=None):
                     return response

                  @webob.dec.wsgif
                  def __call__(self, req):
                     response = self.process_request(req)
                     if response:
                        return response
                     response = req.get_response(self.application)
                     # 这个get_response(self.application)不知道是哪里的
                     (args, varargs, varkw, defaults) = getargspec(self.process_response)
                     # getargsepc 是inspec库的函数,用来检测函数的参数
                     # ArgSpec(args=['response', 'request'], varargs=None, keywords=None, defaults=(None,))
                     if 'request' in args:
                        return self.process_response(response, request=req)
                     return self.process_response(response)






               </base.Middleware>
               # 继承的是base.Middleware
               self.oslo_conf.register_opts(_opts, group='oslo_middleware')
               # 将上面的选项注册进ConfigOpts实例中

            @webob.dec.wsgif
            # 这个装饰器会将func转变为app
            def __call__(self, req):
               max_size = self._conf_get('max_request_body_size')
               if (req.content_length is not None and req.content_length > max_size):
                  msg = _('Request is too large')
                  raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
               if req.content_length is None and req.is_body_readable:

                  limiter = LimitingRead(req.body_file, max_size)
                  <LimitingRead values="(req.body_file, max_size)" path="." function="对单个请求大小进行限制">
                     class LimitingReader(object):
                        def __init__(self, data, limit):
                           self.data = data
                           self.limit = limit
                           self.bytes_read = 0
                        def __iter__(self):
                           for chunk in self.data:
                              self.bytes_read += len(chunk)
                              if self.bytes_read > self.limit:
                                 msg = _('Request too large')
                                 raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
                              else:
                                 yield chunk
                        def read(self, i=None):
                           # mod_wsgi 与 eventlet不同,所以不能简单的提高一个read
                           if i is None:
                              result = self.data.read()
                           else:
                              result = self.data.read(i)
                           if self.bytes_read > self.limit:
                              # read 加上limit判断
                              msg = _('Request too large')
                                 raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
                           return result


                  </LimitingRead>

                  req.body_file = limiter
               return self.application



      1.1流程->RequestBodySizeLimiter初始化后调用父类Middleware的factory函数继续调用RequestBodySizeLimter的__call__
      </RequestBodySizeLimiter>








      2-[filter:url_normalize]
      paste.filter_factory = keystone.middleware:NormalizingFilter.factory
      <NormalizingFIlter.factory values="" path="keystone.middleware.core">
         class NormalizingFilter(wsgi.Middleware):
            <wsgi.Middleware path="keystone.common.wsgi.core">
               class Middleware(Application):
                  <Application path=".">

                  </Application>
                  # 基础WSGI 中间件
                  # 初始化需要app,然后才调用factory,只会调用__call__
                  @classmethod
                  def factory(cls, global_config, **local_config):
                     #  配置文件如下

                        # [filter:analytics]
                        #  redis_host = 127.0.0.1
                           # paste.filter_factory = keystone.analytics:Analytics.factory

                       # 相当于如下,后返回call调用

                        #  import keystone.analytics
                        #  keystone.analytics.Analytics(app, redis_host='127.0.0.1')
                     def _factory(app):
                        conf = global_config.copy()
                        conf.update(local)
                        return cls(app, **local_config)
                     return _factory

                  def __init__(self, application)
                     super(Middleware, self).__init__()
                     self.application = application
                  def process_requires(self, request):
                     return None
                  def process_response(self, request, response):
                     return response
                  @webob.dec.wsgify()
                  # 初始化后调用wsgi().__call__
                  def __call__(self, request):
                     try:
                        response = self.process_request(self.application)
                        if response:
                           return response
                        response = request.get_response(self.application)
                        # 通过get_response 可以返回一个webob response object
                        return self.process_response(request, response)
                     except
                        ....
                     # 这个__call__调用下面的process_request













            </wsgi.Middleware>
            # 中间件过滤处理url正常化
            def process_request(request):
               # 正常化 url
               # 移除多余反斜杠
               if (len(request.environ['PATH_INFO'])>1 and
                  request.environ['PATH_INFO'][-1] == '/'):
                  request.environ['PATH_INFO'] = request.environ['PATH_INFO'][:-1]
               # 重写path,将其置为root
               elif not request.environ['PATH_INFO']:
                  request.environ['PATH_INFO'] = '/'




      </NormalizingFIlter.factory>



      3-[filter:request_id]
      paste.filter_factory = oslo_middleware:RequestId.factory
      <RequestId.factory path="oslo_middleware.request_id">
         ENV_REQUEST_ID = 'openstack.request_id'
         HTTP_RESP_HEADER_ID = 'x-openstack-request-id'

         class RequestId(base.Middleware):
            # 中间件确保request ID
            # 确保分配ID对应每个API请求,将其置为request env,request ID同样被添加到API response
            @webob.dec.wsgify
            def __call__(self, req):

               req_id = context.generate_request_id()
               <get_response_id path="oslo_context.context.generate_request_id">
                  def generate_request_id():
                      return b'req-' + str(uuid.uuid4()).encode('ascii')

               </get_response_id>
               req.environ[ENV_REQUEST_ID] = req.id
               response = req.get_response(self.application)

               if HTTP_RESP_HEADER_REQUEST_ID not in response.headers:
                  response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, req_id)
               return response

      </RequestId.factory>


      4-[filter:build_auth_context]
      paste.filter_factory = keystone.middleware:AuthContextMiddleware.factory
      <AuthContextMiddleware.factory path="keystone.middleware">
         class AuthContextMiddleware(wsgi.Middleware):
            # 从 request auth token 中构建 authentication context
            def _build_auth_context(self, request):
               token_id = request.headers.get(AUTH_TOKEN_HEADER).strip()

               if token_id == CONF.admin_token:
                  return {}
               context = {'token_id': token_id}
               context['environment'] = request.environ
               try:
                  token_ref = token_model.KeystoneToken(
                        token_id=token_id,
                        token_data=self.token_provider_api.validate_token(token_id) # Application 有token_provider_api
                  )
                  <token_model.KeystoneToken values="(token_id, token_data)" path="keystone.model.token_model">
                     class KeystoneToken(dict):
                        def __init__(self, token_id, token_data):
                           self.token_data = token_data
                           if 'access' in token_access:
                              super(KeystoneToken, self).__init__(**token_data['access'])
                           elif 'token' in token_data and 'methods' in token_data['token']:
                              super(KeystoneToken, self).__init__(**token_data['token'])
                           else:
                              raise exception.UnsupportedTokenVersionException()
                           self.token_id = token_id
                           self.short_id = cms.cms_has_token(token_id, mode=CONF.token.has_algorithm) # mode = 'md5' return hash code
                           if self.project_scoped and self.domain_scoped:
                              raise exception.UnexpectedError(_('Found invalid token: scoped to '
                                                      'both project and domain'))


                  </token_model.KeystoneToken>

                  wsgi.validate_token_bind(context, token_ref)
                  <wsgi.validate_token_bind values="(context, token_ref)" path="keystone.common.wsgi">
                     CONTEXT_ENV = 'openstack.context'
                     PARAMS_ENV = 'openstack.params'
                     JSON_ENCODE_CONTENT_TYPES = set(['application/json', 'application/json-home'])
                     def validate_token_bind(context, token_ref):
                        bind_mode = CONF.token.enforce_token_bind # permissive
                        if not isinstance(token_ref, token_model.KeystoneToken):
                           raise exception.UnexpectedError(_('token reference must be a KeystoneToken type, got: %s') % type(token_ref))
                        bind = token_ref.bind

                        permissive = bind_mode in ('permissive', 'strict')
                        name = None if permissive or bind_name == 'required' else bind_mode
                        if not bind:
                           if permissive:
                              # 需要绑定而且permissive不为空
                              return
                           else:
                              LOG(_LI('No bind information present in token'))
                              raise exception.Unauthorized()
                        if name and name not in bind:
                           raise exception.Unauthorized()

                        for bind_type, identifier in bind.items():
                           if bind_type == 'kerberos':
                              if not (context['environment'].get('AUTH_TYPE', '').lower()
                                      == 'negotiate'):
                                     LOG.info(_LI("Kerberos credentials required and not present"))
                                     raise exception.Unauthorized()

                                 if not context['environment'].get('REMOTE_USER') == identifier:
                                     LOG.info(_LI("Kerberos credentials do not match "
                                              "those in bind"))
                                     raise exception.Unauthorized()

                                 LOG.info(_LI("Kerberos bind authentication successful"))

                                elif bind_mode == 'permissive':
                                    LOG.debug(("Ignoring unknown bind for permissive mode: "
                                          "{%(bind_type)s: %(identifier)s}"),
                                         {'bind_type': bind_type, 'identifier': identifier})
                                else:
                                    LOG.info(_LI("Couldn't verify unknown bind: "
                                             "{%(bind_type)s: %(identifier)s}"),
                                            {'bind_type': bind_type, 'identifier': identifier})
                                    raise exception.Unauthorized()




                  </wsgi.validate_token_bind>

                  return authorization.token_to_auth_context(token_ref)
                  <authorization.token_to_auth_context values="token_ref" path="keystone.common.authorization">
                     AUTH_CONTEXT_ENV = 'KEYSTONE_AUTH_CONTEXT'
                     def token_to_auth_context(token):
                        if not isinstance(token, token_model.KeystoneToken):
                           raise exception.UnexpectedError(_('token refernce must be a KeystoneToken type, got: %s') % type(token))

                        auth_context = {'token': token, 'is_delegated_auth': False}
                        try:
                           auth_context['user_id'] = token.user_id
                        except KeyError:
                           LOG.warning(_LW('RBAC:Invalid user data in token'))
                           raise exception.Unauthorized()


                        if token.project_scoped:
                           auth_context['project_id'] = token.project_id
                        elif token.domain_scoped:
                           auth_context['domain_id'] = token.domain_id
                           auth_context['domain_name'] = token.domain_name
                        else:
                           LOG.debug('RBAC: proceeding without project or domain scope')
                        if token.trust_scoped:
                           auth_context['is_delegated_auth'] = True
                           auth_context['trust_id'] = token.trust_id
                           auth_context['trustor_id'] = token.trustor_user_id
                           auth_conetxt['trustee_id'] = token.trustee_user_id
                        else:
                           auth_context['trust_id'] = None
                           auth_context['trustor_id'] = None
                           auth_context['trustee_id'] = None

                        roles = token.role_names

                        if roles:
                           auth_context['roles'] = roles
                        if token.oauth_scoped:
                           auth_context['is_delegated_auth'] = True
                        auth_context['consumer_id'] = token.oauth_consumer_id
                        auth_context['access_token_id'] = token.oauth_access_token_id

                        if token.is_federated_user:
                           auth_context['group_ids'] = token.federation_group_ids
                        return auth_context





                  </authorization.token_to_auth_context>
               except exception.TokenNotFound:
                  LOG.warning(_LW('RBAC:Invalid token'))
                  raise exception.Unauthorized

            def process_request(self, request):
               if AUTH_TOKEN_HEADER not in request.headers:
                  LOG.debug(('Auth token not in the request header.'
                           'will not build auth context'))
                     return
               if authorization.AUTH_CONTEXT_ENV in request.environ:
                  # 已经存在,返回
                  return
               auth_context = self._build_auth_context(request)
               request.environ[authorization.AUTH_CONTEXT_ENV] = auth_context

               # request.environ['KEYSTONE_AUTH_CONTEXT'] = auth_context








      </AuthContextMiddleware.factory>





      5-[filter:token_auth]
      paste.filter_factory = keystone.middleware:TokenAuthMiddleware.factory
      <TokenAuthMiddleware.factory path="keystone.middleware.core">
         AUTH_TOKEN_HEADER = 'X-Auth-Token'
         CONTEXT_ENV = 'openstack.context'
         PARAMS_ENV = 'openstack.params'
         SUBJECT_TOKEN_HEADER = 'X-Subject-Token'
         class TokenAuthMIddleware(wsgi.Middleware):
            def process_request(self, request):
               token = request.headers.get(AUTH_TOKEN_HEADER)
               context = request.environ.get(CONTEXT_ENV, {})
               context['token_id'] = token
               if SUBJECT_TOKEN_HEADER in request.headers:
                  context['subject_token_id'] = request.headers[SUBJECT_TOKEN_HEADER]
               request.environ[CONTEXT_ENV] = context




      </TokenAuthMiddleware.factory>


      6-[filter:admin_token_auth]
      paste.filter_factory = keystone.middleware:AdminTokenAuthMiddleware.factory
      <AdminTokenAuthMIddleware.factory path="keystone.middleware.core">
         class AdminAuthMiddleware(wsgi.Middleware):
            def process_request(self, request):
               token = request.headers.get(AUTH_TOKEN_HEADER)
               context = request.environ.get(CONTEXT_ENV, {})
               context['is_admin'] = (token == CONF.admin_token)
               request.environ[CONTEXT_ENV] = context
               # 验证token == CONF.admin_token,置is_admin的布尔值





      </AdminTokenAuthMIddleware.factory>



      7-[filter:json_body]
      paste.filter_factory = keystone.middleware:JsonBodyMiddleware.factory
      <JsonBodyMiddleware.factory path="keystone.middleware.core">
         class JsonBodyMiddleware(wsgi.Middleware):
            # 允许方法参数以序列化json形式通过
            def process_request(self, request):
               params_json = request.body:
               # 如果为空,早早结束
               if not params_json:
                  return
               # 识别context_type是否为json
               if request.context_type not in ('application/json', ''):
                  e = exception.ValidationError(attribute='application/json',
                                          target='Content-Type header')
                  return wsgi.render_exception(e, request=request)
               params_parsed = {}
               try:
                  params_parsed = jsonutils.loads(params_json)
               except ValueError:
                  e = exception.ValidationError(attribute='valid JSON',
                                          target='request body')
                  return wsgi.render_exception(e, request=request)
               finally:
                  if not params_parsed:
                     params_parsed = {}
               if not isinstance(params_parsed, dict):
                  e = exception.ValidationError(attribute='valid JSON object',
                                          target='request body')
                  return wsgi.render_exception(e, request=request)
               params = {}
               for k, v in params_parsed.items():
                  if k in (self, 'context'):
                     continue
                  if k.startswith('_'):
                     continue
                  params[k] = v
               request.environ[PARAMS_ENV] = params
         # 将request.body json化 ,然后以字典形式 放到 request.environ['openstack.params']






      </JsonBodyMiddleware.factory>


      8-[filter:ec2_extension]
      paste.filter_factory = keystone.contrib.ec2:Ec2Extension.factory
      <EC2Extension.factory path="keystone.contrib/ec2/routers">
         build_resource_relation = functools.partial(
            json_home.build_v3_extension_resource_relation, extension_name='OS-EC2',
            extension_version='1.0')

         build_parameter_relation = functools.partial(
            json_home.build_v3_extension_parameter_relation.extension_name='OS-EC2',
            extension_version='1.0')

         class EC2Extension(wsgi.ExtensionRouter):
            <wsgi.ExtensionRouter values="" path="keystone.common.wsgi">
               class ExtensionRouter(Router):
                  <Router path="keystone.common.wsgi">
                     class Router(object):
                        def __init__(self, mapper):
                           # 为给定的routes.Mapper创建一个router
                           # 每个在Mapper里的router都需要指定一个controller,这就是要被调用的WSGI app
                           # action 就是controller里面的方法
                           self.map = mapper
                           self._router = routes.middleware.RouterMiddleware(self._dispatch, self.map)

                        @webob.dec.wsgify() # 下面就是调用的wsgfiy().__call__
                        def __call__(self, req):

                        @staticmethod
                        @webob.dec.wsgify()
                        def _dispatch(req):
                           # 将请求转发到匹配的controller
                           # self._router调用它, 匹配request,并将信息装入req.environ
                           match  = req.environ['wsgiorg.routing_args'][1]
                           if not match:
                              msg = _('The resource could not be found.')
                              return render_exception(exception.NotFound(msg),
                                                request=req,
                                                user_local=best_match_language(req))
                           app = match['controller']
                           return app

                           return self._router
                  </Router>

                  def __init__(self, application, mapper=None):
                     if mapper is None:
                        mapper = routes.Mapper()
                     self.application = application
                     self.add_routes(mapper)
                     mapper.connect('/{path_info:.*}', controller=self.application)
                     super(ExtensionRouter, self).__init__(mapper)
                  def add_router(self, mapper)# 需要覆写
                     return pass
                  @classmethod
                  def factory(cls, global_config, **local_config):
                     def _factory(app):
                        conf = global_config.copy()
                        conf.update(local_config)
                        return cls(app, **local_config) # 调用类初始化
                     return _factory



            </wsgi.ExtensionRouter>

            def add_routes(self, mapper):

               ec2_controller = controllers.EC2Controller()
               <EC2Controller values="" path="keystone.contrib.ec2.controller">
                  @dependency.requires('policy_api', 'token_provider_api')
                  class EC2Controller(Ec2ControllerCommon, controller.V2Controller):
                     @controller.v2_deprecated
                     def authenticate(self, context, credentials=None, ec2Credentials=None):
                        (user_ref, tenant_ref, metadata_ref, role_ref, catalog_ref) =
                        self._authenticate(credentials=credentials,
                                       ec2credentials=ec2credentials)
                        # 继承EC2ControllerCommon._authenticate
                        <Ec2ControllerCommon._authenticate path="">
                           def _authenticate(self, credentials=None, ec2credentials=None):
                              # credentials: ec2 签名字典
                              # ec2credentials : 丢弃的ec2 签名字典
                              if not credentials and ec2credentials: 只要ec2credentials
                                 credentials = ec2credentials
                              if 'access' not in credentials:
                                 raise exception.Unauthorized(message='EC2 signature not supplied.')
                              creds_ref = self._get_credentials(credentials['access']) # credentials['access'] 是ID
                              <EC2ControllerCommon._get_credentials values="(self, credential_id)" path="keystone.contrib.ec2.controller">
                                 def _get_credentials(self, credentials):
                                    # return: credentials ec2 credentials 字典
                                    # utils 在 keystone.common
                                    ec2_credentials_id = utils.hash_access_key(credential_id) # 对credential_id进行hash
                                    creds = self.credential_api.get_credential(ec2_credential_id)
                                    <credential_api.get_credential values="ec2_credential_id" path="keystone.credential.Credential">

                                       self.credential_api 就是下面的类的实例
                                       # Manager->Driver的过程
                                       class Credential(credential.Driver):
                                          def get_credential(self, credential_id):
                                             session = sql.get_session()
                                             return self._get_credential(session, credential_id).to_dict()
                                             # self._get_credential(session, credential_id)返回的是CredentialModel

                                          def _get_credential(self, session, credential_id):
                                             ref = session.query(CredentialModel).get(credential_id)
                                             # 查询表CredentialModel
                                             if ref is None:
                                                raise exception.CredentialNotFound(credential_id=credential_id)
                                             return ref





                                    </credential_api.get_credential>
                                    if not creds:
                                       raise exception.Unauthorized(message='EC2 access key not found.')
                                    return self._convert_v3_to_ec2_credential(creds)
                                    <self._convert_v3_to_ec2_credential values="creds" path="keystone.contrib.ec2.controllers.EC2ControllerCommon">
                                       @staticmethod
                                       def _convert_v3_to_ec2_credential(credential):
                                          try:
                                             blob = jsonutils.loads(credential['blob'])
                                          except TYpeError:
                                             blob = credential['blob']
                                          return {'user_id': credential.get('user_id'),
                                                'tenant_id': credential.get('project_id'),
                                                'access': blob.get('access'),
                                                'secret': blob.get('secret'),
                                                'trust_id': blob.get('trust_id')}


                                    </self._convert_v3_to_ec2_credential>

                              </EC2ControllerCommon._get_credentials>

                              self.check_signature(creds_ref, credentials)
                              <self.check_signature values="(creds_ref, credentials)" path="keystone.contrib.ec2.controllers.EC2ControllerCommon">
                                 signer = ec2_utils.Ec2Signer(creds_ref['secret'])
                                 ...
                              </self.check_signature>

                              tenant_ref = self.resource_api.get_project(creds_ref['tenant_id'])
                              <self.resource_api.get_project values="(creds_ref['tenant_id'])" path="keystone.resource.core.Manager">
                                 @dependency.provider('resource_api')
                                 @dependency.requires('assignment_api', 'credential_api', 'domain_config_qpi', 'identity_api', 'revoke_api')
                                 class Manager(manager.Manager):
                                    # main entry point into the resource service
                                    _DOMAIN = 'domain'
                                    _PROJECT = 'project'
                                    def __init__(self):
                                       # 没特别指定就用assignment的驱动
                                       resource_driver = CONF.resource.driver
                                       if resource_driver is None:
                                          assignment_manager = dependency.get_provider('assignment_api')
                                          resource_driver = assignment_manager.default_resource_driver()
                                          # 返回的是'keystone.resource.backends.sql.Resource.'

                                       super(Manger, self).__init__(resource_driver)
                                       # 初始化驱动 传递的是entry_point

                                    @MEMOIZE
                                    def get_project(self, project_id):
                                       return self.driver.get_project(project)
                                    <self.driver.get_project values="project" path="keystone.resource.backends.sql.Resource">
                                       # from keystone import resource as keystone_resource
                                       class Resource(keystone_resource.Driver):
                                          def get_project(self, tenant_id):
                                             with sql.transaction() as session:

                                                <sql.transaction values="" path="keystone.common.sql.core">
                                                   @contextlib.contextmanager
                                                   # contextlib.contextmanager 是装饰函数级别的with,保存上下文
                                                   def transaction(expire_on_commit=False):
                                                      # 返回SQLAlchemy session
                                                      session = get_session(expire_on_commit=expire_on_commit):
                                                      with session.begin():
                                                         yield session

                                                </sql.transaction>
                                                return self._get_project(session, tenant_id).to_dict()
                                                # _get_project 见下面
                                                # 每次sql返回值都会进行一下to_dict()操作
                                          def default_assignment_driver(self):
                                             # 上面找驱动的返回 就是这里返回的字符串
                                             return 'keystone.assignment.backends.sql.Assignment'
                                          def _get_project(self, session, project):
                                             project_ref = session.query(Project).get(project_id)
                                             # 表Project
                                             if project_ref is None:
                                                raise exception.ProjectNotFound(project_id=project_id)
                                             return project_ref



                                    </self.driver.get_project>





                              </self.resource_api.get_project>
                              user_ref = self.identity_api.get_user(creds_ref['user_id'])
                              <self.identity_api.get_user values="(creds_ref['user_id'])" path="keystone.identity.core.Manager">
                                 MEMOIZE = cache.get_memoization_decorator(section='identity')
                                 DOMAIN_CONF = 'keystone.'
                                 DOMAIN_CONF_FIAIL = '.conf'
                                 @dependency.provider('identity_api')
                                 @dependency.requires('assignment_api', 'credential_api', 'id_mapping_api', 'resource_api', 'revoke_api')
                                 class Manager(manager.Mnager):
                                    _USER = 'user'
                                    _GROUP = 'group'
                                    def __init__(self):
                                       super(Manager, self).__init__(CONF.identity.driver)
                                       self.domain_configs = DomainConfigs()
                                       self.event_callbacks = {
                                          notification.ACTIONS.deleted: {
                                             'domain': [self._domain_deleted]
                                          }

                                       }

                                    @domain_configured
                                    <domain_configured path="keystone.identity.core">
                                       # 这边会启动domain的配置加载
                                       def domains_configured(f):
                                          @functools.wraps(f):
                                          def wrapper(self, *args, **kwargs):
                                             if (not self.domain_configs.configured and CONF.identity.domain_specific_drivers_enabled):
                                                self.domain_configs.setup_domain_drivers(self.driver, self.resource_api)
                                             return f(self, *args, **kwargs)
                                          return wrapper
                                    </domain_configured>
                                    @exception_translated('user')
                                    @MEMOIZE
                                    def get_user(self, user_id):

                                       domain_id, driver, entity_id = (self._get_domain_driver_and_entity_id(user_id))
                                       <self._get_domain_driver_and_entity_id values="user_id" path="">
                                          def _get_domain_driver_and_entity_id(self, public_id):
                                             # 查询public_id详情
                                             # 返回: domain_id ,可为空,支持多domain, driver根据domain获取,entity_id根据driver

                                             # 使用映射表查找domain,driver以及local entity
                                             # 单一driver无需映射表
                                             conf = CONF.identiy
                                             # 因为不知道entity,所以需要做映射
                                             if conf.domain_specific_drivers_enabled:
                                                local_id_ref = self.id_mapping_api.get_id_mapping(public_id)
                                                return (local_id_ref['domain_id'], self._select_identity_driver(local_id_ref['domain_id']),
                                                      local_id_ref['local_id'])
                                             driver = self.driver
                                             if driver.generates_uuids():
                                                if driver.is_domain_aware:
                                                   return (None, driver, public_id)
                                                else:
                                                   return (conf.default_domain_id, driver, public_id)
                                             if not CONF.identity_mapping.backward_compatible_ids:
                                                local_id_ref = self.id_mapping_api.get_id_mapping(public)
                                                if local_id_ref:
                                                   return (
                                                      local_id_ref['domain_id'],
                                                      driver,
                                                      local_id_ref['local_id'],
                                                      )

                                                else:
                                                   raise exception.PublicDNotFound(id=public_id)


                                             return (conf.default_domain_id, driver, public_id)




                                       </self._get_domain_driver_and_entity_id>

                                       ref = driver.get_user(entity_id)
                                       return self._set_domain_id_and_mapping(ref, domain_id, driver, mapping.EntityType.USER)


                              </self.identity_api.get_user>

                              metadata_ref = {}
                              metadata_ref['roles'] = (
                                 self.assignment_api.get_roles_for_user_and_project(user_ref['id'], tenant_ref['id'])
                              <self.assignment_api.get_roles_for_user_and_project values="(user_ref['id'], tenant_ref['id'])" path="keystone.assignment.core">
                                 @dependency.provider('assignment_api')
                                 @dependency.requires('credential_api', 'identity_api', 'resource_api', 'revoke_api', 'role_api')
                                 class Manager(manager.Manager):
                                    _PROJECT = 'project'
                                    _ROLE_REMOVED_FROM_USER = 'role_removed_from_user'
                                    _INVALIDATION_USER_PROJECT_TOKENS = 'invalidate_user_project_tokens'

                                    def __init__(self):
                                       assignemnt = CONF.assignment.driver
                                       # 以后identity将包括 identity、resource、assignment
                                       super(Manager, self).__init__(assignment_driver)

                                    def get_roles_for_user_and_project(self, user_id, tenant_id):
                                       # 如果OS-INHERIT拓展可用,那么domain将包含role inherited





                                       project_ref = self.resource_api.get_project(tenant_id)
                                       user_role_list = _get_user_project_roles(user_id, project_ref)
                                       group_role_list = _get_group_project_roles(user_id, project_ref)

                                       return list(set(user_role_list + group_role_list))






                              </self.assignment_api.get_roles_for_user_and_project>



                              trust_id = creds_ref.get('trust_id')
                              if trust_id:
                                 metadata_trf['trust_id'] = trust_id
                                 metadata_ref['trustee_user_id'] = user_ref['id']

                              try:
                                 self.identity_api.assert_user_enable(
                                    user_id=user_ref['id'], user=user_ref)
                                 <self.identity_api.assert_user_enable values="(user_id=user_ref['id'], user=user_ref)" path="keystone.identity.core">
                                    def assert_user_enabled(self, user_id, user=None):
                                       if user is None:
                                          user = self.get_user(user_id)
                                       self.resource_api.assert_domain_enabled(user['domain_id'])
                                          <self.reouce_api.assert_domain_enbaled values="(user['domain_id'])" path="keystone.resource.core.Manager">
                                             def assert_domain_enabled(self, domain_id, domain=None):
                                                if domain is None:
                                                   domain = self.get_domain(domain_id)
                                                   <self.get_domain values="domain_id" path=".">
                                                      @MEMOIZE
                                                      def get_domain(self, domain_id):
                                                         return self.driver.get_domain(domain_id)
                                                      # 调用sql等后端驱动处理
                                                   </self.get_domain>
                                                if not domain.get('enabled', True):
                                                   raise AssertionError(_('Domain is disabled: %s') % domain_id)

                                          </self.reouce_api.assert_domain_enbaled>
                                       if not user.get('enabled', True):
                                          raise AssertionError(_('User is disabled: %s') % user_id)


                                    # identity_api.assert_user_enabled(user_id, user)--->resource_api.assert_domain_enabled(user['domain_id'])
                                 </self.identity_api.assert_user_enable>
                                 self.resource_api.assert_domain_enabled(
                                    domain_id=user_ref['domain_id'])
                                    <self.resource_api.assert_domain_enabled values="(domain_id=user_ref['domain_id'])" path="keystone.resource.core">
                                       # 就是上面的
                                    </self.resource_api.assert_domain_enabled>
                                 self.resource_api.assert_project_enabled(
                                    project_id=tenant_ref['id'], project=tenant_ref)
                                    # 跟assert_project_enabled 类似

                              except AssertionError as e:
                                 six.reraise(exception.Unauthorized, exception.Unauthorized(e),
                                                sys.exc_info()[2])

                              roles = metadata_ref.get('roles', [])
                              if not roles:
                                 raise exception.Unauthorized(message='User not valid for tenant.')
                              roles_ref = [self.role_api.get_role(role_id) for role_id in roles]
                              <self.role_api.get_role values="(role_id)" path="keystone.assignment.core.RoleManager">
                                 @dependency.provider('role_api')
                                 @dependency.requires('assignment_api')
                                 class RoleManager(manager.Manager):
                                    _ROLE = 'role'
                                    def __init__(self):
                                       role_driver = CONF.role.driver
                                       if rle_driver is None:
                                          assignment_manager = dependency.get_provider('assignment_api')
                                          role_driver = assignment_manager.default_role_driver()

                                       super(RoleManager, self).__init__(role_driver)

                                    @MEMOIZE
                                    def get_role(self, role_id):
                                       return self.driver.get_role(role_id)





                              </self.role_api.get_role>

                              catalog_ref = self.catalog_api.get_catalog(user_ref['id'], tenant_ref['id'])

                              return user_ref, tenant_ref, metadata_ref, roles_ref, catalog_ref



                        </Ec2ControllerCommon._authenticate>

                        user_ref = self.v3_to_v2_user(user_ref)
                        <self.v3_to_v2_user values="user_ref" path="keystone.common.controller.v2Controller">
                           # 上面EC2Controller类的继承
                           @staticmethod
                           def v3_to_v2_user(ref):
                              # v3 转变为v2
                              # v2 users 没有domain
                              # v2 user 需要tenantId 而不是 default_project_id
                              # v2 user 有username 属性
                              def _format_default_project_id(ref):
                                 # 将default_project_id 转变为 tenantId
                                 default_project_id = ref.pop('default_project_id', None)
                                 if default_project_id is not None:
                                    ref['tenantId'] = default_project_id
                                 elif 'tenantId' in ref:
                                    del ref['tenantId']

                              def _normalize_and_filter_user_properties(ref):
                                 _format_default_project_ids(ref)
                                 V2Controller.filter_domain(ref)
                                 <V2Controller.filter_domain values="ref" path="keystone.contrib.ec2.core.V2Controller">
                                    @staticmethod
                                    def filter_domain(ref):
                                       # 移除domain,这个方法确保v3 user创建时候属于默认domain
                                       if 'domain' in ref:
                                          if ref['domain'].get('id') != CONF.identity.default_domain_id:
                                          # CONF.identity.default_domain_id = default 应该是008
                                             raise exception.Unauthorized(
                                                     _('Non-default domain is not supported'))
                                          del ref['domain']
                                       return ref
                                 </V2Controller.filter_domain>

                                 V2Controller.filter_domain_id(ref)
                                 <V2Controller.filter_domain_id values="ref" path=".">
                                    @staticmethod
                                    def filter_domain_id(ref):
                                       # 因为V2没有domain_id 所以移除
                                       if 'domain_id' in ref:
                                          if ref['domain_id'] != CONF.identity.default_domain_id:
                                             raise exception.Unauthorized(
                                                  _('Non-default domain is not supported'))
                                          def ref['domain_id']
                                       return ref

                                 </V2Controller.filter_domain_id>

                                 V2Controller.normalize_username_in_response(ref)
                                 <V2Controller.normalize_username_in_response values="ref" path="">
                                    @staticmethod
                                    def normalize_username_in_response(ref):
                                       # 在输出的ref中添加username
                                       if 'username' not in ref nad 'name' in ref:
                                          ref['username'] = ref['name']
                                       return ref
                                 </V2Controller.normalize_username_in_response>

                                 return ref

                              if isinstance(ref, dict):
                                 return _normalize_and_filter_user_properties(ref)
                              elif isinstance(ref, list):
                                 [_normalize_and_filter_user_properties(x) for x in ref]
                              else:
                                    raise ValueError(_('Expected dict or list: %s') % type(ref))





                        </self.v3_to_v2_user>
                        auth_token_data = dict(user=user_ref, tenant_ref, metadata=metadata_ref, id='placeholder')

                        (token_id, token_data) = self.token_provider_api.issue_v2_token(auth_token_data, roles_ref, catalog_ref)
                        <self.token_provider_api.issue_v2_token values="(auth_token_data, roles_ref, catalog_ref)" path="keystone.token.provider.Manger">
                           # token 总共会用到token_api , token_provider_api, 前者依赖于后者
                           MEMOIZE = cache.get_memoization_decorator(section='token')
                           UnsupportedTokenVersionException = exception.UnsupportedTokenVersionException
                           # 防止某些依赖于旧版本的UnsupportedTokenVersionException,增强兼容
                           V2 = token_model.V2
                           V3 = token_model.V3
                           VERSIONS = token_model.VERSIONS
                           @dependency.provider(‘token_provider_api’)
                           @dependency.requires('assignment_api', 'revoke_api')
                           class Manager(manager.Manager):
                              V2 = V2
                              V3 = V3
                              VERSIONS = VERSIONS
                              INVALIDATE_PROJECT_TOKEN_PERSISTENCE = 'invalidate_project_tokens'
                              INVALIDATE_USER_TOKEN_PERSISTENCE = 'invalidate_user_tokens'
                              _persistence_manager = None

                              def __init__(self):
                                 super(Manager, self).__init__(CONF.token.provider)
                                 self._register_callback_listeners()

                              def issue_v2_token(self, token_ref, roles_ref=None, catalog_ref=None):
                                 token_id, token_data = self.driver.issue_v2_token(token_ref, roles_ref, catalog_ref)
                                 <self.driver.issue_v2_token values="(token_ref, roles_ref, catalog_ref)" path="keystone.token.providers.uuid">
                                    # 看keystone.conf可知 provider使用uuid
                                    # persistent使用sql
                                    # uuid 继承common.BaseProvider
                                    class Provider(common.BaseProvider):
                                       def __init__(self, *args, **kwargs):
                                          super(Provider, self).__init__(*args, **kwargs)

                                       def _get_token_id(self, token_data):
                                          return uuid.uuid4().hex

                                       def needs_persistence(self):
                                          return True
                                    @dependency.requires('catalog_api', 'identity_api', 'oauth_api', 'resource_api', 'role_api', 'trust_api')

                                    class BaseProvider(provider.Provider):
                                       def __init__(self, *args, **kwargs):
                                          super(BaeProvider, self).__init__(*args, **kwargs)
                                          self.v3_token_data_helper = V3TokenDataHelper()
                                          self.v2_token_data_helper = V2TokenDataHelper()

                                       def issue_v2_token(self, token_ref, reolse_ref=None, catalog_ref=None):
                                          metadata_ref = token_ref['metadata']
                                          trust_ref = None
                                          if CONF.trust.enabled and metadata_ref and 'trust_id' in metadata_ref:
                                             trust_ref = self.trust_api.get_trust(metadata_ref['trust_id'])

                                          token_data = self.v2_token_data_helper.format_token(token_ref, roles_ref, catalog_ref, trust_ref)
                                          <self.v2_token_data_helper.format_token values="(token_ref, roles_ref, catalog_ref, trust_ref)" path=".">
                                             @dependency.requires('catalog_api', 'resource_api')
                                             class V2TokenDataHelper(object):
                                                @classmethod
                                                def format_token(cls, token_ref, roles_ref=None, catalog_ref=None):
                                                   audit_info =None
                                                   user_ref = token_ref['user']
                                                   metadata_ref = token_ref['metadata']
                                                   if roles_ref is None:
                                                      roles_ref = []
                                                   expires = token_ref.get('expires', provider.default_expire_time())
                                                   # provider.default_expire_time() 是调用的keystone.token.provider
                                                   <provider.default_expire_time values="" path="keystone.token.provider">
                                                      def default_expire_time():
                                                         expire_delta = datetime.timedelta(seconds=CONF.token.expiration)
                                                         return timeutils.utcnow() + expire_delta
                                                   </provider.default_expire_time>
                                                   if expires is not None:
                                                      if not isinstance(expires, six.text_type):
                                                         expires = timeutils.isotime(expires)

                                                   token_data = token_ref.get('token_data')
                                                   if token_data:
                                                      token_audit = token_data.get('access', token_data).get('token', {}).get('audit_ids')
                                                      audit_info = token_audit
                                                   if audit_info is None:
                                                      audit_info = provider.audit_info(token_ref.get('parent_audit_id'))
                                                      <provider.audit_info values="token_ref.get('parent_audit_id')" path="keystone.token.provider">
                                                         def random_urlsafe_str():
                                                            return base64.urlsafe_b64encode(uuid.uuid4().bytes)[:-2]
                                                            # 生成随机的url

                                                         def audit_info(parent_audit_id):
                                                            audit_id = random_urlsafe_str()
                                                            if parent_audit_id is not None:
                                                               return [audit_id, parent_audit_id]
                                                            return [audit_id]

                                                      </provider.audit_info>
                                                   o = {'access': {'id': token_ref['id'],
                                                               'expires': expires,
                                                               'issued_at': timeutils.strtime(),
                                                               'audit_ids': audit_info
                                                               },
                                                         'user': {'id': user_ref['id'],
                                                               'name': user_ref['name'],
                                                               'username': user_ref['name'],
                                                               'roles': roles_ref,
                                                               'roles_links': metadata_ref.get('roles_links', [])
                                                               }
                                                         }

                                                   if 'bind' in token_ref:
                                                      o['access']['token']['bind'] = token_ref['bind']
                                                   if 'tenant' in token_ref and token_ref['tenant']:
                                                      token_ref['tenant']['enabled'] = True
                                                      o['access']['token']['tenant'] = token_ref['tenant']
                                                   if catalog_ref is not None:
                                                      o['access']['serviceCatalog'] = V2TokenDataHelper.format_catalog(catalog_ref)
                                                   if metadata_ref:
                                                      if 'is_admin' in metadata_ref:
                                                         o['access']['metadata'] = {'is_admin': metadata_ref['is_admin']}
                                                      else:
                                                         o['access']['metadata'] = {'is_admin': 0}
                                                   if 'roles' in metadata_ref:
                                                      o['access']['metadata']['roles'] = metadata_ref['roles']

                                                   if CONF.trust.enabled and trust_ref:
                                                      o['access']['trust'] = {'trustee_user_id':
                                                                        trust_ref['trustee_user_id'],
                                                                        'id':trust_ref['id'],
                                                                        'trustor_user_id':
                                                                        trust_ref['trustor_user_id'],
                                                                        'impersonation':
                                                                        trust_ref['impersonation']
                                                                        }
                                                   return o

                                                @classmethod
                                                def format_catalog(cls, catalog_ref):
                                                   <!--# 输出形式:
                                                   #  {$REGION: {
                                                      #     {$SERVICE: {
                                                       #        $key1: $value1,
                                                       #
                                                       #        }
                                                   #     }
                                                     #  }
                                                   # 输入形式:
                                                   #  [{'name': $SERVICE[name],
                                                   #'type': $SERVICE,
                                                      #     'endpoints': [{
                                                       #     'tenantId': $tenant_id,
                                                       #     ...
                                                       #     'region': $REGION,
                                                       #     }],
                                                   #     'endpoints_links': [],
                                                         #     }]
                                                   -->
                                                   if not catalog_ref:
                                                      services = {}
                                                   for region, region_ref in six.iteritems(catalog_ref):
                                                      for service, service_ref in six.iteritems(region_ref):
                                                         new_service_ref = services.get(service, {})
                                                         new_service_ref['name'] = service_ref.pop('name')
                                                         new_service_ref['type'] = service
                                                         new_service_ref['endpoints_links'] = []
                                                         service_ref['region'] = region

                                                         endpoints_ref = new_service_ref.get('endpoints', [])
                                                         endpoints_ref.append(service_ref)
                                                         services[service] = new_service_ref
                                                   return services.values()







                                          </self.v2_token_data_helper.format_token>
                                          token_id = self._get_token_id(token_data)
                                          token_data['access']['token']['id'] = token_id
                                          return token_id, token_data


                                 </self.driver.issue_v2_token>
                                 if self._needs_persistence:
                                 <self._needs_persistence path=".">
                                    @property
                                    def _needs_persistence:
                                       return self.driver.needs_persistence()
                                       <self.driver.needs_persistence values="" path="keystone.token.provider.uuid.Provider">
                                          class Provider(common.BaseProvider):
                                             def __init__(self, *args, **kwargs):
                                                super(Provider, self).__init__(*args, **kwargs)
                                             def _get_token_id(self, token_data):
                                                return uuid.uuid4().hex
                                             def needs_persistence(self):
                                                return True
                                             # 这就是说token需要被后端存储
                                       </self.driver.needs_persistence>
                                 </self._needs_persistence>
                                    data = dict(

                                             id=token_id,
                                             expires=token_data['access']['token']['expires'],
                                             user=token_ref['user']
                                             tenant=token_ref['tenant']
                                             metadata_ref=token_ref['metadata'],
                                             token_data=token_data,
                                             bind=token_ref.get('bind'),
                                             trust_id=token_ref['metadata'].get('trust_id'),
                                             token_version=self.V2)

                                    self._create_token(token_id, data)
                                    <self._create_token values="token_id, data" path="keystone.token.provider">
                                       def _create_token(self, token_id, token_data):
                                          try:
                                             if isinstance(token_data, six.string_types):
                                                token_data['expires'] = timeutils.normalize_time(timeutils.pare_isotime(token_data['expires']))
                                                timeutils.parse_isotime(token_data, token_data)

                                             self._persistence.create_token(token_id, token_data)
                                             <self._persistence values="self" path=".">
                                                @property
                                                def _persistence(self):
                                                   if self._persistence_manager is None:
                                                      self._persistence_manager = persistence.PersistenceManager()
                                                   return self._persistence_manager

                                                # persistence.PersistenceManager() path="keystone.token.persistence.core"
                                                @dependency.requires('assignment_api', 'identity_api', 'resource_api', 'token_provider_api', 'trust_api')
                                                class PersistenceManager(manager.Manager):
                                                   driver_namespace = 'keystone.token.persistence'
                                                   def __init__(self):
                                                      super(PersistenceManager, self).__init__(CONF.token.driver)

                                                   def get_token(self, token_id):
                                                      if not token_id:
                                                         raise exception.TokenNotFound(unique_id='')
                                                      unique_id = utils.generate_unique_id(token_id)
                                                      <utils.generate_unique_id values="(token_id='')" path="keystone.token.utils">
                                                         def generate_unique_id(token_id):
                                                            return cms.cms_hash_token(token_id, mode=cfg.CONF.token.hash_algorithm)
                                                      </utils.generate_unique_id>
                                                      token_ref = self._get_token(unique_id)
                                                      <self._get_token values="unique_id"  path="">
                                                         @MEMOIZE
                                                         def _get_token(self, token_id):
                                                            return self.driver.get_token(token_id)
                                                      </self._get_token>

                                                      self._assert_valid(token_id, token_ref)
                                                      <self._assert_valid values="(token_id, token_ref)" path="">
                                                         def _assert_valid(self, token_id, token_ref):
                                                            # token如果过期,抛出TokenNotFound
                                                            current_time = timeutils.normalize_time(timeutils.utcnow())
                                                            expires = token_ref.get('expires')
                                                            if not expires or current_time > timeutils.normalize_time(expires):
                                                               raise exception.TokenNotFound(token_id=token_id)
                                                      </self._assert_valid>
                                                      return token_ref


                                                   def create_token(self, token_id, data):
                                                      unique_id = utils.generate_unique_id(token_id)
                                                      data_copy = copy.deepcopy(data)
                                                      data_copy['id'] = unique_id

                                                      ret = self.driver.create_token(unique_id, data_copy)
                                                      <self.drier.create_token values="(unique_id, data_copy)" path="keystone.token.persistence.backends.sql.Token">
                                                         def create_token(self, token_id, data):
                                                            data_copy = copy.deepcopy(data)
                                                            if not data_copy.get('expires'):
                                                               data_copy['expires'] = provider.default_expire_time()
                                                            if not data_copy.get('user_id'):
                                                               data_copy['user_id'] = data_copy['user']['id']
                                                            token_ref = TokenModel.from_dict(data_copy)
                                                            token_ref.valid = True
                                                            session = sql.get_session()
                                                            with session.begin():
                                                               session.add(token_ref)
                                                               # 跟sqlchemy一样 可以查看sqlchemy相关文档
                                                            return token_ref.to_dict()


                                                      </self.drier.create_token>''

                                                      if MEMOIZE.should_cache(ret):
                                                         self._get_token.set(ret, self, unique_id)
                                                         # 这边的set到底是哪里来的
                                                         # 这个是继承的sqlalchemy.models.ModelBase 父类的父类

                                                      return ret


                                             </self._persistence>
                                          exception Exception:
                                             exce_info = sys,exc_info()
                                             try:
                                                self._persistence.get_token(token_id)
                                             except exception.TokenNotFound:
                                                six.reraise(*exc_info)
                                    </self._create_token>

                                 return token_id, token_data









                        </self.token_provider_api.issue_v2_token>
                        return token_data
               </EC2Controller>


               # 生效
               mapper.connect(
                  '/ec2tokens',
                  # /v2.0/ec2tokens url是组装还是就直接 /ec2tokens ?
                  controller=ec2_controller,
                  action='authenticate',
                  conditions=dict(method=['POST'])

               )
               # crub
               mapper.connect(
                  '/users/{user_id}/credentials/OS-EC2',
                  controllers=ec2_controller,
                  action='create_credential',
                  conditions=dict(method=['POST']))
               <create_credential values="(self, context, user_id, tenant_id)" path="keystone.contrib.ec2.controller.EC2Extension">
                  @controller.v2_deprecated
                  def create_credential(self, context, user_id):
                     if not self._is_admin(context):
                        <self._is_admin values="context" path=".">
                           def _is_admin(self, context):
                              try:
                                 self.assert_admin(context)
                                 <self.assert_admin values="context" path="keystone.common.wsgi.Application">
                                    def assert_admin(self, context):
                                       if not context['is_admin']:

                                          user_token_ref = utils.get_token_ref(context)
                                          <utils.get_token_ref values="context" path="keystone.common.utils">
                                             def get_token_ref(context):
                                                try:
                                                   auth_context = (context['environment'][authorization.AUTH_CONTEXT_ENV])
                                                   # authorization.AUTH_CONTEXT_ENV = 'KEYSTONE_AUTH_CONTEXT'
                                                   # path: keystone.common.authorization
                                                   return auth_context['token']
                                                except KeyError:
                                                   LOG.warning(_LW("Couldn't find the auth context."))
                                                   raise exception.Unauthorized()
                                          </utils.get_token_ref>

                                          validate_token_bind(context, user_token_ref)
                                          <validate_token_bind values="(context, user_token_ref)" path="keystone.common.wsgi">
                                             def validate_token_bind(context, token_ref):
                                                # 确认token是否绑定
                                                bind_mode = CONF.token.enforce_token_bind
                                                # bind = 'permissive'
                                                if bind_mode == 'disabled':
                                                   return
                                                if not isinstance(token_ref, token_model.KeystoneToken):
                                                   raise except.UnexpectedError(_('token reference must be a KeystoneToken type, got: %s') % type(token_ref))
                                                bind = token_ref.bind

                                                permissive = bind_mode in ('permissive', 'strict')

                                                name = None if permissive or bind_mode == 'required' else bind_mode

                                                if not bind:
                                                   if permissive:
                                                      return
                                                   else:
                                                      LOG.info(_LI("No bind information present in token"))
                                                      raise exception.Unauthorized()

                                                for bind_type, identifier in bind.items():
                                                   # kerberos: 判断绑定类型在判断绑定remote-user是否相符合
                                                   # permissive: pass
                                                   # 其他: 报错
                                                   if bind_type == 'kerberos':
                                                      if not (context['environment']).get('AUTH_TYPE', '').lower() == 'negotiate'):
                                                         LOG.info(_LI("Kerberos credentials required and not present"))
                                                         raise exception.Unauthorized()
                                                      if not context['environment'].get('REMOTE_USER') == identifier:
                                                         LOG.info(_LI("Kerberos credentials do not match "
                                                                "those in bind"))
                                                         raise exception.Unauthorized()
                                                      LOG.info(_LI("Kerberos bind authentication successful"))
                                                   elif bind_mode == 'permissive':
                                                      LOG.debug(("Ignoring unknown bind for permissive mode: "
                       "                                             {%(bind_type)s: %(identifier)s}"),
                                                              {'bind_type': bind_type, 'identifier': identifier})

                                                   else:
                                                      LOG.infoLOG.info(_LI("Couldn't verify unknown bind: "
                                                                     "{%(bind_type)s: %(identifier)s}"),
                                                              {'bind_type': bind_type, 'identifier': identifier})
                                                      raise exception.Unauthorized()



                                          </validate_token_bind>
                                          creds = copy.deepcopy(user_token_ref.metadata)
                                          try:
                                             creds['user_id'] = user_token_ref.user_id
                                             # user_token_ref 是KeystoneToken类
                                          except exception.UnexpectedError:
                                             LOG.debug('Invalid user')
                                             raise exception.Unauthorized()
                                          if user_token_ref.project_scoped:
                                             <user_token_ref.project_scoped values="" path="keystone.modele.token_model">
                                                @property
                                                def project_scoped(self):
                                                   if self.version is V3:
                                                      return 'project' in self
                                                   else:
                                                      return 'tenant' in self['token']
                                             </user_token_ref.project_scoped>

                                             creds['tenant_id'] = user_token_ref.project_id
                                             <user_token_ref.project_id values="" path=".">
                                                @property
                                                def project_id(self):
                                                   try:
                                                      if self.version is V3:
                                                         return self['project']['id']
                                                      else:
                                                         return self['token']['tenant']['id']
                                                   except KeyError:
                                                      raise exception.UnexpectedError()
                                             </user_token_ref.project_id>
                                          else:
                                             LOG.debug('Invalid tenant')
                                             raise exception.Unauthorized()
                                          creds['roles'] = user_token_ref.role_names
                                          <user_token_ref.role_names values="" path="">
                                             @property
                                             def role_names(self):
                                                # 返回role name列表
                                                # r为one role
                                                if self.version is V3:
                                                   return [r['name'] for r in self.get('roles', [])]
                                                else:
                                                   return [r['name'] for r in self['user'].get('roles', [])]

                                          </user_token_ref.role_names>

                                          self.policy_api.enforce(creds, 'admin_required', {})
                                          <self.policy_api.enforce values="(creds, 'admin_required', {})" path="keystone.policy.core">
                                             @dependency.provider('policy_api')
                                             class Manager(manager.Manager):
                                                driver_namespace = 'keystone.policy'
                                                _POLICY = 'policy'
                                                def __init__(self):
                                                   super(Manager, self).__init__(CONF.policy.driver)
                                             # keystone.policy.backends.rules
                                             class Policy(policy.PolicyDriverV8):
                                                def enforce(self, credentials, action, target):
                                                   LOG.debug('enfore %(action)s: %(credentials)s', {'action': action, 'credentials': credentials})
                                                   enforce(credentials, action, target)
                                                   <enforce values="credentials, action, target, do_raise=True" path="keystone.policy.backends.rules">
                                                      from oslo_policy import policy as common_policy
                                                      _ENFORCER = None
                                                      def reset():
                                                         global _ENFORCER
                                                         _ENFORCER = None



                                                      def init():
                                                         global _ENFORCE
                                                         if not _ENFORCER:
                                                            _ENFORCER = common_policy.Enforcer(CONF)

                                                      def enforce(credentials, action, target, do_raise=True):
                                                         # 确认action在目标上的有效性
                                                         init()
                                                         extra = {}
                                                         # 如果需要做一个raise,所以需要添加字典参数
                                                         if do_raise:
                                                            extra.update(exc=exception.ForbiddenAction, action=action, do_raise=do_raise)
                                                         return _ENFORCER.enforce(action, target, credentials, **extra)
                                                         # _ENFORCER.enforce 是调用Enforce下的enforce
                                                         <_ENFORCER.enforce values="(action, target, credentials, **extra)" path="oslo_policy.policy">
                                                            class Enforcer(object):
                                                               def enforce(self, rule, target, creds, do_raise=False, exc=None, *agrs, **kwargs):
                                                                  self.load_rules()

                                                                  if instance(rule, _checks.BaseCheck):
                                                                     result = rule(target, creds, self)
                                                                  elif not self.rules:
                                                                     result = False
                                                                  else:
                                                                     try:
                                                                        result = self.rules[rule](target, creds, self)
                                                                     except KeyError:
                                                                        LOG.debug('Rule [%s] does not exist', rule)
                                                                        result = False
                                                                  if do_raise and not reslut:
                                                                     if exc:
                                                                        raise exc(*args, **kwargs)
                                                                     raise PolicyNotAuthorized(rule, target, creds)
                                                                  return result


                                                         </_ENFORCER.enforce>




                                                   @重点部分,重点分析

                                                   </enforce>





                                          </self.policy_api.enforce>




                                 </self.assert_admin>
                                 # 判断是否有admin权限
                                 return True
                              except (exception.Forbidden, exception.Unauthorized):
                                 return False


                        </self._is_admin>
                        self._assert_identity(context, user_id)
                     return super(EC2Controller, self).create_credential(context, user_id, tenant_id)
               </create_credential>


               mapper.connect(
                  '/users/{user_id}/credentials/OS-EC2',
                  controllers=ec2_controller,
                  action='get_credentials',
                  conditions=dict(method=['GET']))
               mapper.connect(
                  '/users/{user_id}/credentials/OS-EC2/{credential_id}',
                  controllers=ec2_controller,
                  action='get_credential',
                  conditions=dict(method=['GET']))
               mapper.connect(
                  '/users/{user_id}/credentials/OS-EC2/{credential_id}',
                  controllers=ec2_controller,
                  action='delete_credential',
                  conditions=dict(method=['DELETE']))




      </EC2Extension.factory>




      9-[filter:s3_extension]
      paste.filter_factory = keystone.contrib.s3:S3Extension.factory
      <S3Extension.factory values="" path="keystone.contrib.s3:S3Extension.factory">
         # 是向其他应用开放的认证
      </S3Extension.factory>




      10-[filter:crud_extension]
      paste.filter_factory = keystone.contrib.admin_crud:CrudExtension.factory
      <CrudExtension.factory path="keystone.contrib.admin_crud:CrudExtension.factory">
         class CrudExtension(wsgi.ExtensionRouter):
            def add_router(self, mapper):
               tenant_controller = resource.controllers.Tenant()
               assignment_tenant_controller = (assignment.controllers.TenantAssignment())
               user_controller = identity.controllers.User()
               role_controller = assignment.controllers.Role()
               assignment_role_controller = assignment.controller.RoleAssignmentV2()
               service_controller = catalog.controllers.Service()
               endpoint_controller = catalog.controllers.Endpoint()
               # 上面全是提供的API

               # 租户/project操作
               mapper.connect(
                     '/tenants',
                     controller=tenant_controller,
                     action='create_project',
                     conditions=dict(method=['POST']))
               mapper.connect(
                     '/tenant/{tenant_id}',
                     controller=tenant_controller,
                     action='update_project',
                     conditions=dict(method=['PUT','POST'])

               # 重点,后面在分析
      </CrudExtension.factory>



      11-[filter:admin_service]
      paste.app_factory = keystone.service:admin_app_factory
      <keystone.service.admin_app_factory>
         @fail_gracefully
         <fail_garcefully values="" path="." function="执行装饰函数,若出错,报错:严重">
            def fail_gracefully(f):
               @functools.wraps(f):
               def wrapper(*args, **kw):
                  try:
                     return f(*args, **kw)
                  except Exception as e:
                     LOG.debug(e, exc_info=True)
                     LOG.critical(e)
                     sys.exit(1)

            return wrapper
         </fail_garcefully>
         def admin_app_factory(global_conf, **local_conf):
            controllers.register_version('v2.0'):
            return wsgi.ComposingRouter(routes.Mapper(), [identity.routes.Admin(), assignment.routes.Admin(), token.routes.Router(),
                                 resource.routers.Admin(), routers.VersionV2('admin'), routers.Extension()])

      </keystone.service.admin_app_factory>



   </keystone-paste.ini>





</paste_deploy>

查看原文:http://www.zoues.com/index.php/2015/09/26/keystone_admin_api/

carbon@carbon-R5300-G4X:/$ openstack --debug token issue # 启用调试模式查看详细过程 START with options: ['--debug', 'token', 'issue'] options: Namespace(access_key='', access_secret='***', access_token='***', access_token_endpoint='', access_token_type='', auth_type='', auth_url='', authorization_code='', cacert=None, cert='', client_id='', client_secret='***', cloud='', consumer_key='', consumer_secret='***', debug=True, default_domain='default', default_domain_id='', default_domain_name='', deferred_help=False, discovery_endpoint='', domain_id='', domain_name='', endpoint='', identity_provider='', identity_provider_url='', insecure=None, interface='', key='', log_file=None, openid_scope='', os_beta_command=False, os_compute_api_version='', os_identity_api_version='3', os_image_api_version='', os_network_api_version='', os_object_api_version='', os_project_id=None, os_project_name=None, os_volume_api_version='', passcode='', password='***', project_domain_id='', project_domain_name='Default', project_id='', project_name='admin', protocol='', redirect_uri='', region_name='', timing=False, token='***', trust_id='', url='http://controller:35357/v3', user_domain_id='', user_domain_name='Default', user_id='', username='admin', verbose_level=3, verify=None) Auth plugin token_endpoint selected auth_config_hook(): {'dns_api_version': '2', 'api_timeout': None, 'secgroup_source': 'neutron', 'interface': None, 'default_domain': 'default', 'url': 'http://controller:35357/v3', 'deferred_help': False, 'object_store_api_version': '1', 'identity_api_version': '3', 'network_api_version': '2', 'networks': [], 'region_name': '', 'database_api_version': '1.0', 'volume_api_version': '2', 'container_api_version': '1', 'verbose_level': 3, 'disable_vendor_agent': {}, 'image_format': 'qcow2', 'image_api_version': '2', 'floating_ip_source': 'neutron', 'verify': True, 'compute_api_version': '2', 'username': 'admin', 'password': 'Hbis@123', 'auth_type': 'token_endpoint', 'baremetal_api_version': '1', 'beta_command': False, 'cert': None, 'cacert': None, 'debug': True, 'auth': {'project_domain_name': 'Default', 'project_name': 'admin', 'user_domain_name': 'Default', 'token': '4dfca0e2d881e685109d'}, 'orchestration_api_version': '1', 'image_api_use_tasks': False, 'metering_api_version': '2', 'key': None, 'key_manager_api_version': 'v1', 'timing': False} defaults: {'dns_api_version': '2', 'api_timeout': None, 'object_store_api_version': '1', 'interface': None, 'verify': True, 'container_api_version': '1', 'identity_api_version': '2.0', 'network_api_version': '2', 'volume_api_version': '2', 'secgroup_source': 'neutron', 'database_api_version': '1.0', 'disable_vendor_agent': {}, 'image_format': 'qcow2', 'image_api_version': '2', 'compute_api_version': '2', 'cert': None, 'auth_type': 'token_endpoint', 'baremetal_api_version': '1', 'cacert': None, 'orchestration_api_version': '1', 'image_api_use_tasks': False, 'metering_api_version': '2', 'key': None, 'key_manager_api_version': 'v1', 'floating_ip_source': 'neutron'} cloud cfg: {'dns_api_version': '2', 'api_timeout': None, 'secgroup_source': 'neutron', 'interface': None, 'default_domain': 'default', 'object_store_api_version': '1', 'url': 'http://controller:35357/v3', 'deferred_help': False, 'container_api_version': '1', 'identity_api_version': '3', 'network_api_version': '2', 'networks': [], 'region_name': '', 'database_api_version': '1.0', 'volume_api_version': '2', 'verbose_level': 3, 'disable_vendor_agent': {}, 'image_format': 'qcow2', 'image_api_version': '2', 'verify': True, 'compute_api_version': '2', 'cert': None, 'username': 'admin', 'password': '***', 'auth_type': 'token_endpoint', 'timing': False, 'beta_command': False, 'baremetal_api_version': '1', 'floating_ip_source': 'neutron', 'debug': True, 'auth': {'url': 'http://controller:35357/v3', 'project_name': 'admin', 'user_domain_name': 'Default', 'project_domain_name': 'Default', 'token': '***'}, 'orchestration_api_version': '1', 'image_api_use_tasks': False, 'metering_api_version': '2', 'key': None, 'key_manager_api_version': 'v1', 'cacert': None} object_store API version 1, cmd group openstack.object_store.v1 identity API version 3, cmd group openstack.identity.v3 network API version 2, cmd group openstack.network.v2 volume API version 2, cmd group openstack.volume.v2 compute API version 2, cmd group openstack.compute.v2 image API version 2, cmd group openstack.image.v2 neutronclient API version 2, cmd group openstack.neutronclient.v2 Auth plugin token_endpoint selected auth_config_hook(): {'dns_api_version': '2', 'api_timeout': None, 'secgroup_source': 'neutron', 'interface': None, 'default_domain': 'default', 'url': 'http://controller:35357/v3', 'deferred_help': False, 'object_store_api_version': '1', 'identity_api_version': '3', 'network_api_version': '2', 'networks': [], 'region_name': '', 'database_api_version': '1.0', 'volume_api_version': '2', 'container_api_version': '1', 'verbose_level': 3, 'disable_vendor_agent': {}, 'image_format': 'qcow2', 'image_api_version': '2', 'floating_ip_source': 'neutron', 'verify': True, 'compute_api_version': '2', 'username': 'admin', 'password': 'Hbis@123', 'auth_type': 'token_endpoint', 'baremetal_api_version': '1', 'beta_command': False, 'cert': None, 'cacert': None, 'debug': True, 'auth': {'project_domain_name': 'Default', 'project_name': 'admin', 'user_domain_name': 'Default', 'token': '4dfca0e2d881e685109d'}, 'orchestration_api_version': '1', 'image_api_use_tasks': False, 'metering_api_version': '2', 'key': None, 'key_manager_api_version': 'v1', 'timing': False} command: token issue -> openstackclient.identity.v3.token.IssueToken Using auth plugin: token_endpoint Using parameters {'url': 'http://controller:35357/v3', 'project_name': 'admin', 'user_domain_name': 'Default', 'project_domain_name': 'Default', 'token': '***'} Get auth_ref run(Namespace(columns=[], formatter='table', max_width=0, noindent=False, prefix='', variables=[])) Get auth_ref Only an authorized user may issue a new token. Traceback (most recent call last): File "/usr/lib/python3/dist-packages/cliff/app.py", line 387, in run_subcommand result = cmd.run(parsed_args) File "/usr/lib/python3/dist-packages/osc_lib/command/command.py", line 41, in run return super(Command, self).run(parsed_args) File "/usr/lib/python3/dist-packages/cliff/display.py", line 100, in run column_names, data = self.take_action(parsed_args) File "/usr/lib/python3/dist-packages/openstackclient/identity/v3/token.py", line 180, in take_action _("Only an authorized user may issue a new token.")) osc_lib.exceptions.AuthorizationFailure: Only an authorized user may issue a new token. clean_up IssueToken: Only an authorized user may issue a new token. Traceback (most recent call last): File "/usr/lib/python3/dist-packages/osc_lib/shell.py", line 135, in run ret_val = super(OpenStackShell, self).run(argv) File "/usr/lib/python3/dist-packages/cliff/app.py", line 267, in run result = self.run_subcommand(remainder) File "/usr/lib/python3/dist-packages/osc_lib/shell.py", line 180, in run_subcommand ret_value = super(OpenStackShell, self).run_subcommand(argv) File "/usr/lib/python3/dist-packages/cliff/app.py", line 387, in run_subcommand result = cmd.run(parsed_args) File "/usr/lib/python3/dist-packages/osc_lib/command/command.py", line 41, in run return super(Command, self).run(parsed_args) File "/usr/lib/python3/dist-packages/cliff/display.py", line 100, in run column_names, data = self.take_action(parsed_args) File "/usr/lib/python3/dist-packages/openstackclient/identity/v3/token.py", line 180, in take_action _("Only an authorized user may issue a new token.")) osc_lib.exceptions.AuthorizationFailure: Only an authorized user may issue a new token. END return value: 1
最新发布
06-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值