secure.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. """
  2. This module defines a SecureOperation class, which implements the security handler for an operation.
  3. """
  4. import functools
  5. import logging
  6. from ..decorators.decorator import RequestResponseDecorator
  7. logger = logging.getLogger("connexion.operations.secure")
  8. DEFAULT_MIMETYPE = 'application/json'
  9. class SecureOperation:
  10. def __init__(self, api, security, security_schemes):
  11. """
  12. :param security: list of security rules the application uses by default
  13. :type security: list
  14. :param security_definitions: `Security Definitions Object
  15. <https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#security-definitions-object>`_
  16. :type security_definitions: dict
  17. """
  18. self._api = api
  19. self._security = security
  20. self._security_schemes = security_schemes
  21. @property
  22. def api(self):
  23. return self._api
  24. @property
  25. def security(self):
  26. return self._security
  27. @property
  28. def security_schemes(self):
  29. return self._security_schemes
  30. @property
  31. def security_decorator(self):
  32. """
  33. Gets the security decorator for operation
  34. From Swagger Specification:
  35. **Security Definitions Object**
  36. A declaration of the security schemes available to be used in the specification.
  37. This does not enforce the security schemes on the operations and only serves to provide the relevant details
  38. for each scheme.
  39. **Operation Object -> security**
  40. A declaration of which security schemes are applied for this operation. The list of values describes alternative
  41. security schemes that can be used (that is, there is a logical OR between the security requirements).
  42. This definition overrides any declared top-level security. To remove a top-level security declaration,
  43. an empty array can be used.
  44. **Security Requirement Object**
  45. Lists the required security schemes to execute this operation. The object can have multiple security schemes
  46. declared in it which are all required (that is, there is a logical AND between the schemes).
  47. The name used for each property **MUST** correspond to a security scheme declared in the Security Definitions.
  48. :rtype: types.FunctionType
  49. """
  50. logger.debug('... Security: %s', self.security, extra=vars(self))
  51. if not self.security:
  52. return self._api.security_handler_factory.security_passthrough
  53. auth_funcs = []
  54. for security_req in self.security:
  55. if not security_req:
  56. auth_funcs.append(self._api.security_handler_factory.verify_none())
  57. continue
  58. sec_req_funcs = {}
  59. oauth = False
  60. for scheme_name, required_scopes in security_req.items():
  61. security_scheme = self.security_schemes[scheme_name]
  62. if security_scheme['type'] == 'oauth2':
  63. if oauth:
  64. logger.warning("... multiple OAuth2 security schemes in AND fashion not supported", extra=vars(self))
  65. break
  66. oauth = True
  67. token_info_func = self._api.security_handler_factory.get_tokeninfo_func(security_scheme)
  68. scope_validate_func = self._api.security_handler_factory.get_scope_validate_func(security_scheme)
  69. if not token_info_func:
  70. logger.warning("... x-tokenInfoFunc missing", extra=vars(self))
  71. break
  72. sec_req_funcs[scheme_name] = self._api.security_handler_factory.verify_oauth(
  73. token_info_func, scope_validate_func, required_scopes)
  74. # Swagger 2.0
  75. elif security_scheme['type'] == 'basic':
  76. basic_info_func = self._api.security_handler_factory.get_basicinfo_func(security_scheme)
  77. if not basic_info_func:
  78. logger.warning("... x-basicInfoFunc missing", extra=vars(self))
  79. break
  80. sec_req_funcs[scheme_name] = self._api.security_handler_factory.verify_basic(basic_info_func)
  81. # OpenAPI 3.0.0
  82. elif security_scheme['type'] == 'http':
  83. scheme = security_scheme['scheme'].lower()
  84. if scheme == 'basic':
  85. basic_info_func = self._api.security_handler_factory.get_basicinfo_func(security_scheme)
  86. if not basic_info_func:
  87. logger.warning("... x-basicInfoFunc missing", extra=vars(self))
  88. break
  89. sec_req_funcs[scheme_name] = self._api.security_handler_factory.verify_basic(basic_info_func)
  90. elif scheme == 'bearer':
  91. bearer_info_func = self._api.security_handler_factory.get_bearerinfo_func(security_scheme)
  92. if not bearer_info_func:
  93. logger.warning("... x-bearerInfoFunc missing", extra=vars(self))
  94. break
  95. sec_req_funcs[scheme_name] = self._api.security_handler_factory.verify_bearer(bearer_info_func)
  96. else:
  97. logger.warning("... Unsupported http authorization scheme %s" % scheme, extra=vars(self))
  98. break
  99. elif security_scheme['type'] == 'apiKey':
  100. scheme = security_scheme.get('x-authentication-scheme', '').lower()
  101. if scheme == 'bearer':
  102. bearer_info_func = self._api.security_handler_factory.get_bearerinfo_func(security_scheme)
  103. if not bearer_info_func:
  104. logger.warning("... x-bearerInfoFunc missing", extra=vars(self))
  105. break
  106. sec_req_funcs[scheme_name] = self._api.security_handler_factory.verify_bearer(bearer_info_func)
  107. else:
  108. apikey_info_func = self._api.security_handler_factory.get_apikeyinfo_func(security_scheme)
  109. if not apikey_info_func:
  110. logger.warning("... x-apikeyInfoFunc missing", extra=vars(self))
  111. break
  112. sec_req_funcs[scheme_name] = self._api.security_handler_factory.verify_api_key(
  113. apikey_info_func, security_scheme['in'], security_scheme['name']
  114. )
  115. else:
  116. logger.warning("... Unsupported security scheme type %s" % security_scheme['type'], extra=vars(self))
  117. break
  118. else:
  119. # No break encountered: no missing funcs
  120. if len(sec_req_funcs) == 1:
  121. (func,) = sec_req_funcs.values()
  122. auth_funcs.append(func)
  123. else:
  124. auth_funcs.append(self._api.security_handler_factory.verify_multiple_schemes(sec_req_funcs))
  125. return functools.partial(self._api.security_handler_factory.verify_security, auth_funcs)
  126. def get_mimetype(self):
  127. return DEFAULT_MIMETYPE
  128. @property
  129. def _request_response_decorator(self):
  130. """
  131. Guarantees that instead of the internal representation of the
  132. operation handler response
  133. (connexion.lifecycle.ConnexionRequest) a framework specific
  134. object is returned.
  135. :rtype: types.FunctionType
  136. """
  137. return RequestResponseDecorator(self.api, self.get_mimetype())