From a1c4f7a2786333cbb8bcfc5e7267edfa13fd82f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Sun, 10 Apr 2022 16:00:51 +0200 Subject: [PATCH] Bumped to authlib 1 --- CHANGES.rst | 1 + canaille/conf/config.sample.toml | 2 ++ canaille/oidc/models.py | 18 ++++++++++-------- canaille/oidc/oauth.py | 2 +- canaille/oidc/oauth2utils.py | 27 ++++++++++++++------------- demo/client/__init__.py | 3 +-- demo/conf-docker/canaille.toml | 2 ++ demo/conf/canaille.toml | 2 ++ doc/configuration.rst | 4 ++++ setup.cfg | 2 +- tests/conftest.py | 1 + 11 files changed, 39 insertions(+), 25 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 184e1abd..7c516c03 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,7 @@ Added - ``DISABLE_PASSWORD_RESET`` configuration option to disable password recovery. :pr:`46` - ``edit_self`` ACL permission to control user self edition. :pr:`47` +- Bumped to authlib 1 :pr:`48` Fixed ***** diff --git a/canaille/conf/config.sample.toml b/canaille/conf/config.sample.toml index bafef63a..89811d5c 100644 --- a/canaille/conf/config.sample.toml +++ b/canaille/conf/config.sample.toml @@ -146,6 +146,8 @@ WRITE = ["groups"] PRIVATE_KEY = "canaille/conf/private.pem" # The path to the public key. PUBLIC_KEY = "canaille/conf/public.pem" +# The URI of the identity provider +ISS = "https://auth.mydomain.tld" # The key type parameter # KTY = "RSA" # The key algorithm diff --git a/canaille/oidc/models.py b/canaille/oidc/models.py index afb27268..bbca9552 100644 --- a/canaille/oidc/models.py +++ b/canaille/oidc/models.py @@ -37,9 +37,6 @@ class Client(LDAPObject, ClientMixin): "preconsent": "oauthPreconsent", } - def get_client_id(self): - return self.id - def get_default_redirect_uri(self): return self.redirect_uris[0] @@ -55,8 +52,10 @@ class Client(LDAPObject, ClientMixin): def check_client_secret(self, client_secret): return client_secret == self.secret - def check_token_endpoint_auth_method(self, method): - return method == self.token_endpoint_auth_method + def check_endpoint_auth_method(self, method, endpoint): + if endpoint == "token": + return method == self.token_endpoint_auth_method + return True def check_response_type(self, response_type): return all(r in self.response_type for r in response_type.split(" ")) @@ -141,9 +140,6 @@ class Token(LDAPObject, TokenMixin): def revoked(self): return bool(self.revokation_date) - def get_client_id(self): - return Client.get(self.client).id - def get_scope(self): return " ".join(self.scope) @@ -171,6 +167,12 @@ class Token(LDAPObject, TokenMixin): < datetime.datetime.now() ) + def is_revoked(self): + return bool(self.revokation_date) + + def check_client(self, client): + return client.client_id == Client.get(self.client).client_id + class Consent(LDAPObject): object_class = ["oauthConsent"] diff --git a/canaille/oidc/oauth.py b/canaille/oidc/oauth.py index 63642ec4..bd6a208a 100644 --- a/canaille/oidc/oauth.py +++ b/canaille/oidc/oauth.py @@ -103,7 +103,7 @@ def authorize(): return jsonify(response) try: - grant = authorization.validate_consent_request(end_user=user) + grant = authorization.get_consent_grant(end_user=user) except OAuth2Error as error: response = dict(error.get_body()) current_app.logger.debug("authorization endpoint response: %s", response) diff --git a/canaille/oidc/oauth2utils.py b/canaille/oidc/oauth2utils.py index 30ba969a..c03d2e7a 100644 --- a/canaille/oidc/oauth2utils.py +++ b/canaille/oidc/oauth2utils.py @@ -45,7 +45,7 @@ def get_jwt_config(grant): return { "key": pk.read(), "alg": current_app.config["JWT"].get("ALG", DEFAULT_JWT_ALG), - "iss": authorization.metadata["issuer"], + "iss": current_app.config["JWT"]["ISS"], "exp": current_app.config["JWT"].get("EXP", DEFAULT_JWT_EXP), } @@ -250,29 +250,30 @@ class BearerTokenValidator(_BearerTokenValidator): class RevocationEndpoint(_RevocationEndpoint): - def query_token(self, token, token_type_hint, client): + def query_token(self, token, token_type_hint): + print("query_token") if token_type_hint == "access_token": - return Token.filter(client=client.dn, access_token=token) + return Token.filter(access_token=token) elif token_type_hint == "refresh_token": - return Token.filter(client=client.dn, refresh_token=token) + return Token.filter(refresh_token=token) - item = Token.filter(client=client.dn, access_token=token) + item = Token.filter(access_token=token) if item: return item[0] - item = Token.filter(client=client.dn, refresh_token=token) + item = Token.filter(refresh_token=token) if item: return item[0] return None - def revoke_token(self, token): + def revoke_token(self, token, request): token.revokation_date = datetime.datetime.now() token.save() class IntrospectionEndpoint(_IntrospectionEndpoint): - def query_token(self, token, token_type_hint, client): + def query_token(self, token, token_type_hint): if token_type_hint == "access_token": tok = Token.filter(access_token=token) elif token_type_hint == "refresh_token": @@ -281,10 +282,10 @@ class IntrospectionEndpoint(_IntrospectionEndpoint): tok = Token.filter(access_token=token) if not tok: tok = Token.filter(refresh_token=token) - if tok: - tok = tok[0] - if client.dn in tok.audience: - return tok + return tok[0] if tok else None + + def check_permission(self, token, client, request): + return client.dn in token.audience def introspect_token(self, token): client_id = Client.get(token.client).client_id @@ -298,7 +299,7 @@ class IntrospectionEndpoint(_IntrospectionEndpoint): "scope": token.get_scope(), "sub": user.uid[0], "aud": audience, - "iss": authorization.metadata["issuer"], + "iss": current_app.config["JWT"]["ISS"], "exp": token.get_expires_at(), "iat": token.get_issued_at(), } diff --git a/demo/client/__init__.py b/demo/client/__init__.py index 6c2c3fb6..388b8fb6 100644 --- a/demo/client/__init__.py +++ b/demo/client/__init__.py @@ -38,8 +38,7 @@ def create_app(): @app.route("/authorize") def authorize(): token = oauth.yaal.authorize_access_token() - userinfo = oauth.yaal.parse_id_token(token) - session["user"] = userinfo + session["user"] = token.get("userinfo") flash("You have been successfully logged in.", "success") return redirect(url_for("index")) diff --git a/demo/conf-docker/canaille.toml b/demo/conf-docker/canaille.toml index 81146643..31f6f81e 100644 --- a/demo/conf-docker/canaille.toml +++ b/demo/conf-docker/canaille.toml @@ -152,6 +152,8 @@ WRITE = ["groups"] PRIVATE_KEY = "conf/private.pem" # The path to the public key. PUBLIC_KEY = "conf/public.pem" +# The URI of the identity provider +ISS = "http://localhost:5000" # The key type parameter # KTY = "RSA" # The key algorithm diff --git a/demo/conf/canaille.toml b/demo/conf/canaille.toml index dfb6a7d1..c34dd935 100644 --- a/demo/conf/canaille.toml +++ b/demo/conf/canaille.toml @@ -152,6 +152,8 @@ WRITE = ["groups"] PRIVATE_KEY = "conf/private.pem" # The path to the public key. PUBLIC_KEY = "conf/public.pem" +# The URI of the identity provider +ISS = "http://localhost:5000" # The key type parameter # KTY = "RSA" # The key algorithm diff --git a/doc/configuration.rst b/doc/configuration.rst index 3037cccb..552154e0 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -178,6 +178,10 @@ Canaille needs a key pair to sign the JWT. The installation command will generat **Required.** The path to the public key. e.g. ``/path/to/canaille/conf/private.pem`` +:ISS: + **Required.** The URI of the identity provider. + e.g. ``https://auth.mydomain.tld`` + :KTY: *Optional.* The key type parameter. Defaults to ``RSA``. diff --git a/setup.cfg b/setup.cfg index c44d9e8d..bfb5aa89 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,7 +25,7 @@ packages = find: include_package_data = true python_requires = >= 3.7 install_requires = - authlib<1 + authlib>1,<2 click<9 email_validator<2 flask<3 diff --git a/tests/conftest.py b/tests/conftest.py index 6de41385..1301e5e9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -184,6 +184,7 @@ def configuration(slapd_server, smtpd, keypair_path): "JWT": { "PUBLIC_KEY": public_key_path, "PRIVATE_KEY": private_key_path, + "ISS": "https://mydomain.tld", "MAPPING": { "SUB": "{{ user.uid[0] }}", "NAME": "{{ user.cn[0] }}",