2022-01-11 18:49:06 +00:00
|
|
|
import datetime
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
from authlib.oauth2.rfc6749 import AuthorizationCodeMixin
|
|
|
|
from authlib.oauth2.rfc6749 import ClientMixin
|
|
|
|
from authlib.oauth2.rfc6749 import TokenMixin
|
|
|
|
from authlib.oauth2.rfc6749 import util
|
2022-01-11 16:57:58 +00:00
|
|
|
from canaille.ldap_backend.ldapobject import LDAPObject
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Client(LDAPObject, ClientMixin):
|
|
|
|
object_class = ["oauthClient"]
|
|
|
|
base = "ou=clients,ou=oauth"
|
|
|
|
id = "oauthClientID"
|
2022-01-11 16:57:58 +00:00
|
|
|
attribute_table = {
|
|
|
|
"description": "description",
|
|
|
|
"client_id": "oauthClientID",
|
|
|
|
"name": "oauthClientName",
|
|
|
|
"contact": "oauthClientContact",
|
|
|
|
"uri": "oauthClientURI",
|
|
|
|
"redirect_uris": "oauthRedirectURIs",
|
|
|
|
"logo_uri": "oauthLogoURI",
|
|
|
|
"issue_date": "oauthIssueDate",
|
|
|
|
"secret": "oauthClientSecret",
|
|
|
|
"secret_expires_date": "oauthClientSecretExpDate",
|
|
|
|
"grant_type": "oauthGrantType",
|
|
|
|
"response_type": "oauthResponseType",
|
|
|
|
"scope": "oauthScope",
|
|
|
|
"tos_uri": "oauthTermsOfServiceURI",
|
|
|
|
"policy_uri": "oauthPolicyURI",
|
|
|
|
"jwk_uri": "oauthJWKURI",
|
|
|
|
"jwk": "oauthJWK",
|
|
|
|
"token_endpoint_auth_method": "oauthTokenEndpointAuthMethod",
|
|
|
|
"software_id": "oauthSoftwareID",
|
|
|
|
"software_version": "oauthSoftwareVersion",
|
|
|
|
"audience": "oauthAudience",
|
|
|
|
"preconsent": "oauthPreconsent",
|
|
|
|
}
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_client_id(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return self.id
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_default_redirect_uri(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return self.redirect_uris[0]
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_allowed_scope(self, scope):
|
2022-01-11 16:57:58 +00:00
|
|
|
return util.list_to_scope(self.scope)
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def check_redirect_uri(self, redirect_uri):
|
2022-01-11 16:57:58 +00:00
|
|
|
return redirect_uri in self.redirect_uris
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def has_client_secret(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return bool(self.secret)
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def check_client_secret(self, client_secret):
|
2022-01-11 16:57:58 +00:00
|
|
|
return client_secret == self.secret
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def check_token_endpoint_auth_method(self, method):
|
2022-01-11 16:57:58 +00:00
|
|
|
return method == self.token_endpoint_auth_method
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def check_response_type(self, response_type):
|
2022-01-11 16:57:58 +00:00
|
|
|
return all(r in self.response_type for r in response_type.split(" "))
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def check_grant_type(self, grant_type):
|
2022-01-11 16:57:58 +00:00
|
|
|
return grant_type in self.grant_type
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def client_info(self):
|
|
|
|
return dict(
|
|
|
|
client_id=self.client_id,
|
|
|
|
client_secret=self.client_secret,
|
|
|
|
client_id_issued_at=self.client_id_issued_at,
|
2022-01-11 16:57:58 +00:00
|
|
|
client_secret_expires_at=self.client_secret_expires_date,
|
2022-01-11 18:49:06 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class AuthorizationCode(LDAPObject, AuthorizationCodeMixin):
|
|
|
|
object_class = ["oauthAuthorizationCode"]
|
|
|
|
base = "ou=authorizations,ou=oauth"
|
2022-02-16 17:00:30 +00:00
|
|
|
id = "oauthAuthorizationCodeID"
|
2022-01-11 16:57:58 +00:00
|
|
|
attribute_table = {
|
2022-02-16 17:00:30 +00:00
|
|
|
"authorization_code_id": "oauthAuthorizationCodeID",
|
2022-01-11 16:57:58 +00:00
|
|
|
"description": "description",
|
|
|
|
"code": "oauthCode",
|
|
|
|
"client": "oauthClient",
|
|
|
|
"subject": "oauthSubject",
|
|
|
|
"redirect_uri": "oauthRedirectURI",
|
|
|
|
"response_type": "oauthResponseType",
|
|
|
|
"scope": "oauthScope",
|
|
|
|
"nonce": "oauthNonce",
|
|
|
|
"issue_date": "oauthAuthorizationDate",
|
|
|
|
"lifetime": "oauthAuthorizationLifetime",
|
|
|
|
"challenge": "oauthCodeChallenge",
|
|
|
|
"challenge_method": "oauthCodeChallengeMethod",
|
|
|
|
"revokation_date": "oauthRevokationDate",
|
|
|
|
}
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_redirect_uri(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return self.redirect_uri
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_scope(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return self.scope
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_nonce(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return self.nonce
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def is_expired(self):
|
|
|
|
return (
|
2022-01-11 16:57:58 +00:00
|
|
|
self.issue_date + datetime.timedelta(seconds=int(self.lifetime))
|
2022-01-11 18:49:06 +00:00
|
|
|
< datetime.datetime.now()
|
|
|
|
)
|
|
|
|
|
|
|
|
def get_auth_time(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return int((self.issue_date - datetime.datetime(1970, 1, 1)).total_seconds())
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Token(LDAPObject, TokenMixin):
|
|
|
|
object_class = ["oauthToken"]
|
|
|
|
base = "ou=tokens,ou=oauth"
|
2022-02-16 17:00:30 +00:00
|
|
|
id = "oauthTokenID"
|
2022-01-11 16:57:58 +00:00
|
|
|
attribute_table = {
|
2022-02-16 17:00:30 +00:00
|
|
|
"token_id": "oauthTokenID",
|
2022-01-11 16:57:58 +00:00
|
|
|
"access_token": "oauthAccessToken",
|
|
|
|
"description": "description",
|
|
|
|
"client": "oauthClient",
|
|
|
|
"subject": "oauthSubject",
|
|
|
|
"type": "oauthTokenType",
|
|
|
|
"refresh_token": "oauthRefreshToken",
|
|
|
|
"scope": "oauthScope",
|
|
|
|
"issue_date": "oauthIssueDate",
|
|
|
|
"lifetime": "oauthTokenLifetime",
|
|
|
|
"revokation_date": "oauthRevokationDate",
|
|
|
|
"audience": "oauthAudience",
|
|
|
|
}
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def expire_date(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return self.issue_date + datetime.timedelta(seconds=int(self.lifetime))
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def revoked(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return bool(self.revokation_date)
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_client_id(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return Client.get(self.client).id
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_scope(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return " ".join(self.scope)
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_expires_in(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return int(self.lifetime)
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_issued_at(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
return int((self.issue_date - datetime.datetime(1970, 1, 1)).total_seconds())
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def get_expires_at(self):
|
|
|
|
issue_timestamp = (
|
2022-01-11 16:57:58 +00:00
|
|
|
self.issue_date - datetime.datetime(1970, 1, 1)
|
2022-01-11 18:49:06 +00:00
|
|
|
).total_seconds()
|
2022-01-11 16:57:58 +00:00
|
|
|
return int(issue_timestamp) + int(self.lifetime)
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def is_refresh_token_active(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
if self.revokation_date:
|
2022-01-11 18:49:06 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
return self.expire_date >= datetime.datetime.now()
|
|
|
|
|
|
|
|
def is_expired(self):
|
|
|
|
return (
|
2022-01-11 16:57:58 +00:00
|
|
|
self.issue_date + datetime.timedelta(seconds=int(self.lifetime))
|
2022-01-11 18:49:06 +00:00
|
|
|
< datetime.datetime.now()
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class Consent(LDAPObject):
|
|
|
|
object_class = ["oauthConsent"]
|
|
|
|
base = "ou=consents,ou=oauth"
|
|
|
|
id = "cn"
|
2022-01-11 16:57:58 +00:00
|
|
|
attribute_table = {
|
|
|
|
"cn": "cn",
|
|
|
|
"subject": "oauthSubject",
|
|
|
|
"client": "oauthClient",
|
|
|
|
"scope": "oauthScope",
|
|
|
|
"issue_date": "oauthIssueDate",
|
|
|
|
"revokation_date": "oauthRevokationDate",
|
|
|
|
}
|
2022-01-11 18:49:06 +00:00
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
if "cn" not in kwargs:
|
|
|
|
kwargs["cn"] = str(uuid.uuid4())
|
|
|
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
def revoke(self):
|
2022-01-11 16:57:58 +00:00
|
|
|
self.revokation_date = datetime.datetime.now()
|
2022-01-11 18:49:06 +00:00
|
|
|
self.save()
|
|
|
|
|
|
|
|
tokens = Token.filter(
|
2022-01-11 16:57:58 +00:00
|
|
|
oauthClient=self.client,
|
|
|
|
oauthSubject=self.subject,
|
2022-01-11 18:49:06 +00:00
|
|
|
)
|
|
|
|
for t in tokens:
|
2022-01-11 16:57:58 +00:00
|
|
|
if t.revoked or any(scope not in t.scope[0] for scope in self.scope):
|
2022-01-11 18:49:06 +00:00
|
|
|
continue
|
|
|
|
|
2022-01-11 16:57:58 +00:00
|
|
|
t.revokation_date = self.revokation_date
|
2022-01-11 18:49:06 +00:00
|
|
|
t.save()
|