2022-05-27 13:28:26 +02:00
|
|
|
import time
|
2022-05-28 15:36:18 +02:00
|
|
|
from typing import Any, Dict, Optional
|
2022-05-27 13:28:26 +02:00
|
|
|
|
|
|
|
from authlib.integrations.sqla_oauth2 import (
|
|
|
|
OAuth2AuthorizationCodeMixin,
|
|
|
|
OAuth2ClientMixin,
|
|
|
|
OAuth2TokenMixin,
|
|
|
|
)
|
2022-05-28 15:36:18 +02:00
|
|
|
from sqlalchemy.engine.base import Connection
|
|
|
|
from sqlalchemy.event import listens_for
|
2023-10-04 15:25:26 +02:00
|
|
|
from sqlalchemy.ext.declarative import DeclarativeMeta
|
2022-05-28 15:36:18 +02:00
|
|
|
from sqlalchemy.orm.mapper import Mapper
|
|
|
|
from sqlalchemy.orm.session import Session
|
2022-05-27 13:28:26 +02:00
|
|
|
|
|
|
|
from fittrackee import db
|
|
|
|
|
2023-10-04 15:25:26 +02:00
|
|
|
BaseModel: DeclarativeMeta = db.Model
|
2022-05-27 13:28:26 +02:00
|
|
|
|
2023-10-04 15:25:26 +02:00
|
|
|
|
|
|
|
class OAuth2Client(BaseModel, OAuth2ClientMixin):
|
2022-05-27 13:28:26 +02:00
|
|
|
__tablename__ = 'oauth2_client'
|
|
|
|
|
2023-10-04 15:25:26 +02:00
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
|
|
user_id = db.Column(
|
2022-05-28 15:36:18 +02:00
|
|
|
db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), index=True
|
2022-05-27 13:28:26 +02:00
|
|
|
)
|
2023-10-04 15:25:26 +02:00
|
|
|
user = db.relationship('User')
|
2022-05-27 13:28:26 +02:00
|
|
|
|
2022-05-28 15:36:18 +02:00
|
|
|
def serialize(self, with_secret: bool = False) -> Dict:
|
|
|
|
client = {
|
2022-05-27 13:28:26 +02:00
|
|
|
'client_id': self.client_id,
|
2022-05-28 15:36:18 +02:00
|
|
|
'client_description': self.client_description,
|
2022-05-27 13:28:26 +02:00
|
|
|
'id': self.id,
|
2022-05-28 15:36:18 +02:00
|
|
|
'issued_at': time.strftime(
|
|
|
|
'%a, %d %B %Y %H:%M:%S GMT',
|
|
|
|
time.gmtime(self.client_id_issued_at),
|
|
|
|
),
|
2022-05-27 13:28:26 +02:00
|
|
|
'name': self.client_name,
|
|
|
|
'redirect_uris': self.redirect_uris,
|
2022-05-28 15:36:18 +02:00
|
|
|
'scope': self.scope,
|
2022-05-27 13:28:26 +02:00
|
|
|
'website': self.client_uri,
|
|
|
|
}
|
2022-05-28 15:36:18 +02:00
|
|
|
if with_secret:
|
|
|
|
client['client_secret'] = self.client_secret
|
|
|
|
return client
|
|
|
|
|
|
|
|
@property
|
|
|
|
def client_description(self) -> Optional[str]:
|
|
|
|
return self.client_metadata.get('client_description')
|
|
|
|
|
|
|
|
|
|
|
|
@listens_for(OAuth2Client, 'after_delete')
|
|
|
|
def on_old_oauth2_delete(
|
|
|
|
mapper: Mapper, connection: Connection, old_oauth2_client: OAuth2Client
|
|
|
|
) -> None:
|
|
|
|
@listens_for(db.Session, 'after_flush', once=True)
|
|
|
|
def receive_after_flush(session: Session, context: Any) -> None:
|
|
|
|
session.query(OAuth2AuthorizationCode).filter(
|
|
|
|
OAuth2AuthorizationCode.client_id == old_oauth2_client.client_id
|
|
|
|
).delete(synchronize_session=False)
|
|
|
|
session.query(OAuth2Token).filter(
|
|
|
|
OAuth2Token.client_id == old_oauth2_client.client_id
|
|
|
|
).delete(synchronize_session=False)
|
2022-05-27 13:28:26 +02:00
|
|
|
|
|
|
|
|
2023-10-04 15:25:26 +02:00
|
|
|
class OAuth2AuthorizationCode(BaseModel, OAuth2AuthorizationCodeMixin):
|
2022-05-27 13:28:26 +02:00
|
|
|
__tablename__ = 'oauth2_code'
|
2022-05-28 15:36:18 +02:00
|
|
|
__table_args__ = (
|
|
|
|
db.Index(
|
|
|
|
'ix_oauth2_code_client_id',
|
|
|
|
'client_id',
|
|
|
|
),
|
|
|
|
)
|
2022-05-27 13:28:26 +02:00
|
|
|
|
2023-10-04 15:25:26 +02:00
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
|
|
user_id = db.Column(
|
2022-05-28 15:36:18 +02:00
|
|
|
db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), index=True
|
2022-05-27 13:28:26 +02:00
|
|
|
)
|
2023-10-04 15:25:26 +02:00
|
|
|
user = db.relationship('User')
|
2022-05-27 13:28:26 +02:00
|
|
|
|
|
|
|
|
2023-10-04 15:25:26 +02:00
|
|
|
class OAuth2Token(BaseModel, OAuth2TokenMixin):
|
2022-05-27 13:28:26 +02:00
|
|
|
__tablename__ = 'oauth2_token'
|
|
|
|
|
2023-10-04 15:25:26 +02:00
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
|
|
user_id = db.Column(
|
2022-05-28 15:36:18 +02:00
|
|
|
db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), index=True
|
2022-05-27 13:28:26 +02:00
|
|
|
)
|
2023-10-04 15:25:26 +02:00
|
|
|
user = db.relationship('User')
|
2022-05-27 13:28:26 +02:00
|
|
|
|
|
|
|
def is_refresh_token_active(self) -> bool:
|
2022-06-08 11:19:14 +02:00
|
|
|
if self.is_revoked():
|
2022-05-27 13:28:26 +02:00
|
|
|
return False
|
|
|
|
expires_at = self.issued_at + self.expires_in * 2
|
|
|
|
return expires_at >= time.time()
|
2022-06-12 17:15:18 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def revoke_client_tokens(cls, client_id: str) -> None:
|
|
|
|
sql = """
|
|
|
|
UPDATE oauth2_token
|
2023-10-04 15:25:26 +02:00
|
|
|
SET access_token_revoked_at = %(revoked_at)s
|
|
|
|
WHERE client_id = %(client_id)s;
|
2022-06-12 17:15:18 +02:00
|
|
|
"""
|
2023-10-04 15:25:26 +02:00
|
|
|
db.engine.execute(
|
|
|
|
sql, {'client_id': client_id, 'revoked_at': int(time.time())}
|
2022-06-12 17:15:18 +02:00
|
|
|
)
|
|
|
|
db.session.commit()
|