forked from Github-Mirrors/canaille
Merge branch 'issue-98-authlib-v1' into 'master'
Authlib 1 Closes #98 See merge request yaal/canaille!48
This commit is contained in:
commit
fc4130a38b
11 changed files with 39 additions and 25 deletions
|
@ -11,6 +11,7 @@ Added
|
||||||
|
|
||||||
- ``DISABLE_PASSWORD_RESET`` configuration option to disable password recovery. :pr:`46`
|
- ``DISABLE_PASSWORD_RESET`` configuration option to disable password recovery. :pr:`46`
|
||||||
- ``edit_self`` ACL permission to control user self edition. :pr:`47`
|
- ``edit_self`` ACL permission to control user self edition. :pr:`47`
|
||||||
|
- Bumped to authlib 1 :pr:`48`
|
||||||
|
|
||||||
Fixed
|
Fixed
|
||||||
*****
|
*****
|
||||||
|
|
|
@ -146,6 +146,8 @@ WRITE = ["groups"]
|
||||||
PRIVATE_KEY = "canaille/conf/private.pem"
|
PRIVATE_KEY = "canaille/conf/private.pem"
|
||||||
# The path to the public key.
|
# The path to the public key.
|
||||||
PUBLIC_KEY = "canaille/conf/public.pem"
|
PUBLIC_KEY = "canaille/conf/public.pem"
|
||||||
|
# The URI of the identity provider
|
||||||
|
ISS = "https://auth.mydomain.tld"
|
||||||
# The key type parameter
|
# The key type parameter
|
||||||
# KTY = "RSA"
|
# KTY = "RSA"
|
||||||
# The key algorithm
|
# The key algorithm
|
||||||
|
|
|
@ -37,9 +37,6 @@ class Client(LDAPObject, ClientMixin):
|
||||||
"preconsent": "oauthPreconsent",
|
"preconsent": "oauthPreconsent",
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_client_id(self):
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
def get_default_redirect_uri(self):
|
def get_default_redirect_uri(self):
|
||||||
return self.redirect_uris[0]
|
return self.redirect_uris[0]
|
||||||
|
|
||||||
|
@ -55,8 +52,10 @@ class Client(LDAPObject, ClientMixin):
|
||||||
def check_client_secret(self, client_secret):
|
def check_client_secret(self, client_secret):
|
||||||
return client_secret == self.secret
|
return client_secret == self.secret
|
||||||
|
|
||||||
def check_token_endpoint_auth_method(self, method):
|
def check_endpoint_auth_method(self, method, endpoint):
|
||||||
return method == self.token_endpoint_auth_method
|
if endpoint == "token":
|
||||||
|
return method == self.token_endpoint_auth_method
|
||||||
|
return True
|
||||||
|
|
||||||
def check_response_type(self, response_type):
|
def check_response_type(self, response_type):
|
||||||
return all(r in self.response_type for r in response_type.split(" "))
|
return all(r in self.response_type for r in response_type.split(" "))
|
||||||
|
@ -141,9 +140,6 @@ class Token(LDAPObject, TokenMixin):
|
||||||
def revoked(self):
|
def revoked(self):
|
||||||
return bool(self.revokation_date)
|
return bool(self.revokation_date)
|
||||||
|
|
||||||
def get_client_id(self):
|
|
||||||
return Client.get(self.client).id
|
|
||||||
|
|
||||||
def get_scope(self):
|
def get_scope(self):
|
||||||
return " ".join(self.scope)
|
return " ".join(self.scope)
|
||||||
|
|
||||||
|
@ -171,6 +167,12 @@ class Token(LDAPObject, TokenMixin):
|
||||||
< datetime.datetime.now()
|
< 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):
|
class Consent(LDAPObject):
|
||||||
object_class = ["oauthConsent"]
|
object_class = ["oauthConsent"]
|
||||||
|
|
|
@ -103,7 +103,7 @@ def authorize():
|
||||||
return jsonify(response)
|
return jsonify(response)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
grant = authorization.validate_consent_request(end_user=user)
|
grant = authorization.get_consent_grant(end_user=user)
|
||||||
except OAuth2Error as error:
|
except OAuth2Error as error:
|
||||||
response = dict(error.get_body())
|
response = dict(error.get_body())
|
||||||
current_app.logger.debug("authorization endpoint response: %s", response)
|
current_app.logger.debug("authorization endpoint response: %s", response)
|
||||||
|
|
|
@ -45,7 +45,7 @@ def get_jwt_config(grant):
|
||||||
return {
|
return {
|
||||||
"key": pk.read(),
|
"key": pk.read(),
|
||||||
"alg": current_app.config["JWT"].get("ALG", DEFAULT_JWT_ALG),
|
"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),
|
"exp": current_app.config["JWT"].get("EXP", DEFAULT_JWT_EXP),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,29 +250,30 @@ class BearerTokenValidator(_BearerTokenValidator):
|
||||||
|
|
||||||
|
|
||||||
class RevocationEndpoint(_RevocationEndpoint):
|
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":
|
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":
|
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:
|
if item:
|
||||||
return item[0]
|
return item[0]
|
||||||
|
|
||||||
item = Token.filter(client=client.dn, refresh_token=token)
|
item = Token.filter(refresh_token=token)
|
||||||
if item:
|
if item:
|
||||||
return item[0]
|
return item[0]
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def revoke_token(self, token):
|
def revoke_token(self, token, request):
|
||||||
token.revokation_date = datetime.datetime.now()
|
token.revokation_date = datetime.datetime.now()
|
||||||
token.save()
|
token.save()
|
||||||
|
|
||||||
|
|
||||||
class IntrospectionEndpoint(_IntrospectionEndpoint):
|
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":
|
if token_type_hint == "access_token":
|
||||||
tok = Token.filter(access_token=token)
|
tok = Token.filter(access_token=token)
|
||||||
elif token_type_hint == "refresh_token":
|
elif token_type_hint == "refresh_token":
|
||||||
|
@ -281,10 +282,10 @@ class IntrospectionEndpoint(_IntrospectionEndpoint):
|
||||||
tok = Token.filter(access_token=token)
|
tok = Token.filter(access_token=token)
|
||||||
if not tok:
|
if not tok:
|
||||||
tok = Token.filter(refresh_token=token)
|
tok = Token.filter(refresh_token=token)
|
||||||
if tok:
|
return tok[0] if tok else None
|
||||||
tok = tok[0]
|
|
||||||
if client.dn in tok.audience:
|
def check_permission(self, token, client, request):
|
||||||
return tok
|
return client.dn in token.audience
|
||||||
|
|
||||||
def introspect_token(self, token):
|
def introspect_token(self, token):
|
||||||
client_id = Client.get(token.client).client_id
|
client_id = Client.get(token.client).client_id
|
||||||
|
@ -298,7 +299,7 @@ class IntrospectionEndpoint(_IntrospectionEndpoint):
|
||||||
"scope": token.get_scope(),
|
"scope": token.get_scope(),
|
||||||
"sub": user.uid[0],
|
"sub": user.uid[0],
|
||||||
"aud": audience,
|
"aud": audience,
|
||||||
"iss": authorization.metadata["issuer"],
|
"iss": current_app.config["JWT"]["ISS"],
|
||||||
"exp": token.get_expires_at(),
|
"exp": token.get_expires_at(),
|
||||||
"iat": token.get_issued_at(),
|
"iat": token.get_issued_at(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,7 @@ def create_app():
|
||||||
@app.route("/authorize")
|
@app.route("/authorize")
|
||||||
def authorize():
|
def authorize():
|
||||||
token = oauth.yaal.authorize_access_token()
|
token = oauth.yaal.authorize_access_token()
|
||||||
userinfo = oauth.yaal.parse_id_token(token)
|
session["user"] = token.get("userinfo")
|
||||||
session["user"] = userinfo
|
|
||||||
flash("You have been successfully logged in.", "success")
|
flash("You have been successfully logged in.", "success")
|
||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,8 @@ WRITE = ["groups"]
|
||||||
PRIVATE_KEY = "conf/private.pem"
|
PRIVATE_KEY = "conf/private.pem"
|
||||||
# The path to the public key.
|
# The path to the public key.
|
||||||
PUBLIC_KEY = "conf/public.pem"
|
PUBLIC_KEY = "conf/public.pem"
|
||||||
|
# The URI of the identity provider
|
||||||
|
ISS = "http://localhost:5000"
|
||||||
# The key type parameter
|
# The key type parameter
|
||||||
# KTY = "RSA"
|
# KTY = "RSA"
|
||||||
# The key algorithm
|
# The key algorithm
|
||||||
|
|
|
@ -152,6 +152,8 @@ WRITE = ["groups"]
|
||||||
PRIVATE_KEY = "conf/private.pem"
|
PRIVATE_KEY = "conf/private.pem"
|
||||||
# The path to the public key.
|
# The path to the public key.
|
||||||
PUBLIC_KEY = "conf/public.pem"
|
PUBLIC_KEY = "conf/public.pem"
|
||||||
|
# The URI of the identity provider
|
||||||
|
ISS = "http://localhost:5000"
|
||||||
# The key type parameter
|
# The key type parameter
|
||||||
# KTY = "RSA"
|
# KTY = "RSA"
|
||||||
# The key algorithm
|
# The key algorithm
|
||||||
|
|
|
@ -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.
|
**Required.** The path to the public key.
|
||||||
e.g. ``/path/to/canaille/conf/private.pem``
|
e.g. ``/path/to/canaille/conf/private.pem``
|
||||||
|
|
||||||
|
:ISS:
|
||||||
|
**Required.** The URI of the identity provider.
|
||||||
|
e.g. ``https://auth.mydomain.tld``
|
||||||
|
|
||||||
:KTY:
|
:KTY:
|
||||||
*Optional.* The key type parameter.
|
*Optional.* The key type parameter.
|
||||||
Defaults to ``RSA``.
|
Defaults to ``RSA``.
|
||||||
|
|
|
@ -25,7 +25,7 @@ packages = find:
|
||||||
include_package_data = true
|
include_package_data = true
|
||||||
python_requires = >= 3.7
|
python_requires = >= 3.7
|
||||||
install_requires =
|
install_requires =
|
||||||
authlib<1
|
authlib>1,<2
|
||||||
click<9
|
click<9
|
||||||
email_validator<2
|
email_validator<2
|
||||||
flask<3
|
flask<3
|
||||||
|
|
|
@ -184,6 +184,7 @@ def configuration(slapd_server, smtpd, keypair_path):
|
||||||
"JWT": {
|
"JWT": {
|
||||||
"PUBLIC_KEY": public_key_path,
|
"PUBLIC_KEY": public_key_path,
|
||||||
"PRIVATE_KEY": private_key_path,
|
"PRIVATE_KEY": private_key_path,
|
||||||
|
"ISS": "https://mydomain.tld",
|
||||||
"MAPPING": {
|
"MAPPING": {
|
||||||
"SUB": "{{ user.uid[0] }}",
|
"SUB": "{{ user.uid[0] }}",
|
||||||
"NAME": "{{ user.cn[0] }}",
|
"NAME": "{{ user.cn[0] }}",
|
||||||
|
|
Loading…
Reference in a new issue