You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

158 lines
6.0 KiB

7 years ago
7 years ago
7 years ago
7 years ago
  1. """
  2. """
  3. from keycloak.exceptions import raise_error_from_response, KeycloakGetError
  4. from .urls_patterns import URL_AUTH, URL_TOKEN, URL_USERINFO, URL_WELL_KNOWN, URL_LOGOUT, \
  5. URL_CERTS, URL_ENTITLEMENT, URL_INTROSPECT
  6. from .connection import ConnectionManager
  7. class Keycloak:
  8. def __init__(self, server_url, client_id, realm_name, client_secret_key=None):
  9. self.__client_id = client_id
  10. self.__client_secret_key = client_secret_key
  11. self.__realm_name = realm_name
  12. self.__connection = ConnectionManager(base_url=server_url,
  13. headers={},
  14. timeout=60)
  15. def well_know(self):
  16. """ The most important endpoint to understand is the well-known configuration
  17. endpoint. It lists endpoints and other configuration options relevant to
  18. the OpenID Connect implementation in Keycloak.
  19. :return It lists endpoints and other configuration options relevant.
  20. """
  21. params_path = {"realm-name": self.__realm_name}
  22. data_raw = self.__connection.raw_get(URL_WELL_KNOWN.format(**params_path))
  23. return raise_error_from_response(data_raw, KeycloakGetError)
  24. def auth_url(self, redirect_uri):
  25. """
  26. http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
  27. :return:
  28. """
  29. return NotImplemented
  30. def token(self, username, password, grant_type=["password",]):
  31. """
  32. The token endpoint is used to obtain tokens. Tokens can either be obtained by
  33. exchanging an authorization code or by supplying credentials directly depending on
  34. what flow is used. The token endpoint is also used to obtain new access tokens
  35. when they expire.
  36. http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
  37. :param username:
  38. :param password:
  39. :param grant_type:
  40. :return:
  41. """
  42. params_path = {"realm-name": self.__realm_name}
  43. payload = {"username": username, "password": password,
  44. "client_id": self.__client_id, "grant_type": grant_type}
  45. if self.__client_secret_key:
  46. payload.update({"client_secret": self.__client_secret_key})
  47. data_raw = self.__connection.raw_post(URL_TOKEN.format(**params_path),
  48. data=payload)
  49. return raise_error_from_response(data_raw, KeycloakGetError)
  50. def userinfo(self, token):
  51. """
  52. The userinfo endpoint returns standard claims about the authenticated user,
  53. and is protected by a bearer token.
  54. http://openid.net/specs/openid-connect-core-1_0.html#UserInfo
  55. :param token:
  56. :return:
  57. """
  58. self.__connection.add_param_headers("Authorization", "Bearer " + token)
  59. params_path = {"realm-name": self.__realm_name}
  60. data_raw = self.__connection.raw_get(URL_USERINFO.format(**params_path))
  61. return raise_error_from_response(data_raw, KeycloakGetError)
  62. def logout(self, refresh_token):
  63. """
  64. The logout endpoint logs out the authenticated user.
  65. :param refresh_token:
  66. :return:
  67. """
  68. params_path = {"realm-name": self.__realm_name}
  69. payload = {"client_id": self.__client_id, "refresh_token": refresh_token}
  70. if self.__client_secret_key:
  71. payload.update({"client_secret": self.__client_secret_key})
  72. data_raw = self.__connection.raw_post(URL_LOGOUT.format(**params_path),
  73. data=payload)
  74. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  75. def certs(self):
  76. """
  77. The certificate endpoint returns the public keys enabled by the realm, encoded as a
  78. JSON Web Key (JWK). Depending on the realm settings there can be one or more keys enabled
  79. for verifying tokens.
  80. https://tools.ietf.org/html/rfc7517
  81. :return:
  82. """
  83. params_path = {"realm-name": self.__realm_name}
  84. data_raw = self.__connection.raw_get(URL_CERTS.format(**params_path))
  85. return raise_error_from_response(data_raw, KeycloakGetError)
  86. def entitlement(self, token, resource_server_id):
  87. """
  88. Client applications can use a specific endpoint to obtain a special security token
  89. called a requesting party token (RPT). This token consists of all the entitlements
  90. (or permissions) for a user as a result of the evaluation of the permissions and authorization
  91. policies associated with the resources being requested. With an RPT, client applications can
  92. gain access to protected resources at the resource server.
  93. :return:
  94. """
  95. self.__connection.add_param_headers("Authorization", "Bearer " + token)
  96. params_path = {"realm-name": self.__realm_name, "resource-server-id": resource_server_id}
  97. data_raw = self.__connection.raw_get(URL_ENTITLEMENT.format(**params_path))
  98. return raise_error_from_response(data_raw, KeycloakGetError)
  99. def instropect(self, token, rpt, token_type_hint="requesting_party_token"):
  100. """
  101. The introspection endpoint is used to retrieve the active state of a token. It is can only be
  102. invoked by confidential clients.
  103. https://tools.ietf.org/html/rfc7662
  104. :param token:
  105. :param rpt:
  106. :param token_type_hint:
  107. :return:
  108. """
  109. params_path = {"realm-name": self.__realm_name}
  110. payload = {"client_id": self.__client_id, "token": rpt,
  111. 'token_type_hint': token_type_hint}
  112. if self.__client_secret_key:
  113. payload.update({"client_secret": self.__client_secret_key})
  114. self.__connection.add_param_headers("Authorization", "Bearer " + token)
  115. data_raw = self.__connection.raw_post(URL_INTROSPECT.format(**params_path),
  116. data=payload)
  117. return raise_error_from_response(data_raw, KeycloakGetError)