Browse Source

Add untested yet endpoint

pull/100/head
squeaky otter 2 years ago
parent
commit
c53075cc44
Signed by: dashie GPG Key ID: C2D57B325840B755
  1. 1
      .coveragerc
  2. 7
      app.py
  3. 12
      app_oauth.py
  4. 110
      controllers/api/v1/accounts.py
  5. 36
      controllers/api/v1/auth.py
  6. 29
      migrations/versions/36_588930f4335e_.py
  7. 123
      migrations/versions/37_9569dcac2399_.py
  8. 32
      migrations/versions/38_d3745d45e223_.py
  9. 28
      migrations/versions/39_463da36c5bd5_.py
  10. 34
      models.py
  11. 1
      pyproject.toml

1
.coveragerc

@ -6,3 +6,4 @@ omit =
configtest.py
**/*__init__.py
venv/*
front/*

7
app.py

@ -17,7 +17,7 @@ from app_oauth import config_oauth
from flask_cors import CORS
from forms import ExtendedRegisterForm
from models import db, Config, user_datastore, Role, create_actor, OAuth2Client, OAuth2Token
from models import db, Config, user_datastore, Role, create_actor
from utils import InvalidUsage, is_admin, duration_elapsed_human, duration_song_human, add_user_log
import texttable
@ -96,7 +96,6 @@ def create_app(config_filename="config.py", app_name=None, register_blueprints=T
print(" * Sentry Flask/Celery support activated")
print(" * Sentry DSN: %s" % app.config["SENTRY_DSN"])
if app.config["DEBUG"] is True:
app.jinja_env.auto_reload = True
logging.basicConfig(level=logging.DEBUG)
@ -108,11 +107,11 @@ def create_app(config_filename="config.py", app_name=None, register_blueprints=T
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
app.logger.addHandler(file_handler)
CORS(app, origins=["*"])
if app.config["DEBUG"] is True:
logging.getLogger('flask_cors.extension').level = logging.DEBUG
logging.getLogger("flask_cors.extension").level = logging.DEBUG
mail.init_app(app)
migrate = Migrate(app, db) # noqa: F841

12
app_oauth.py

@ -31,8 +31,7 @@ class AuthorizationCodeGrant(grants.AuthorizationCodeGrant):
def parse_authorization_code(self, code, client):
current_app.logger.debug("parse auth code")
item = OAuth2AuthorizationCode.query.filter_by(
code=code, client_id=client.client_id).first()
item = OAuth2AuthorizationCode.query.filter_by(code=code, client_id=client.client_id).first()
if item and not item.is_expired():
return item
@ -70,17 +69,12 @@ class RefreshTokenGrant(grants.RefreshTokenGrant):
class ClientCredentialsGrant(grants.ClientCredentialsGrant):
TOKEN_ENDPOINT_AUTH_METHODS = [
'client_secret_basic', 'client_secret_post'
]
TOKEN_ENDPOINT_AUTH_METHODS = ["client_secret_basic", "client_secret_post"]
query_client = create_query_client_func(db.session, OAuth2Client)
save_token = create_save_token_func(db.session, OAuth2Token)
authorization = AuthorizationServer(
query_client=query_client,
save_token=save_token,
)
authorization = AuthorizationServer(query_client=query_client, save_token=save_token)
require_oauth = ResourceProtector()

110
controllers/api/v1/accounts.py

@ -2,8 +2,8 @@ from flask import Blueprint, request, jsonify, abort, current_app
from models import db, User, user_datastore, Role, create_actor, OAuth2Token, OAuth2Client
from flask_security.utils import encrypt_password
from flask_security import confirmable as FSConfirmable
from app_oauth import authorization
import datetime
from app_oauth import authorization, require_oauth
from authlib.flask.oauth2 import current_token
bp_api_v1_accounts = Blueprint("bp_api_v1_accounts", __name__)
@ -22,8 +22,8 @@ def accounts():
# Get the bearer token
bearer = None
if 'Authorization' in request.headers:
b = request.headers.get('Authorization')
if "Authorization" in request.headers:
b = request.headers.get("Authorization")
b.strip().split(" ")
if len(b) == 2:
bearer = b[1]
@ -35,56 +35,56 @@ def accounts():
if not request.json:
abort(400)
if 'nickname' not in request.json:
if "nickname" not in request.json:
errors["nickname"] = ["nickname is missing"]
if 'email' not in request.json:
if "email" not in request.json:
errors["email"] = ["email is missing"]
if 'fullname' not in request.json:
errors['fullname'] = ['fullname is missing']
if 'password' not in request.json:
errors['password'] = ['password is missing']
if 'confirm' not in request.json:
errors['confirm'] = ['password confirm is missing']
if 'agreement' not in request.json:
errors['agreement'] = ['agreement is missing']
if "fullname" not in request.json:
errors["fullname"] = ["fullname is missing"]
if "password" not in request.json:
errors["password"] = ["password is missing"]
if "confirm" not in request.json:
errors["confirm"] = ["password confirm is missing"]
if "agreement" not in request.json:
errors["agreement"] = ["agreement is missing"]
if len(errors) > 0:
return jsonify({"error": errors}), 400
if request.json['password'] != request.json['confirm']:
if request.json["password"] != request.json["confirm"]:
return jsonify({"error": {"confirm": ["passwords doesn't match"]}}), 400
if 'agreement' not in request.json:
if "agreement" not in request.json:
return jsonify({"error": {"agreement": ["you need to accept the terms and conditions"]}}), 400
# Check if user already exists by username
user = User.query.filter(User.name == request.json['username']).first()
user = User.query.filter(User.name == request.json["username"]).first()
if user:
return jsonify({"error": {"ap_id": ["has already been taken"]}}), 400
# Check if user already exists by email
user = User.query.filter(User.email == request.json['email']).first()
user = User.query.filter(User.email == request.json["email"]).first()
if user:
return jsonify({"error": {"email": ["has already been taken"]}}), 400
# Proceed to register the user
role = Role.query.filter(Role.name == "user").first()
if not role:
return jsonify({'error': 'server error'}), 500
return jsonify({"error": "server error"}), 500
u = user_datastore.create_user(
name=request.json['username'],
email=request.json['email'],
display_name=request.json['fullname'],
password=encrypt_password(request.json['password']),
roles=[role]
name=request.json["username"],
email=request.json["email"],
display_name=request.json["fullname"],
password=encrypt_password(request.json["password"]),
roles=[role],
)
actor = create_actor(u)
actor.user = u
actor.user_id = u.id
if 'bio' in request.json:
actor.summary = request.json['bio']
if "bio" in request.json:
actor.summary = request.json["bio"]
db.session.add(actor)
db.session.commit()
@ -101,8 +101,9 @@ def accounts():
abort(400)
# https://github.com/lepture/authlib/blob/master/authlib/oauth2/rfc6749/grants/base.py#L51
token = authorization.generate_token(client_item.client_id, "client_credentials", user=u, scope=client_item.scope,
expires_in=None)
token = authorization.generate_token(
client_item.client_id, "client_credentials", user=u, scope=client_item.scope, expires_in=None
)
tok = OAuth2Token()
tok.user_id = u.id
@ -111,16 +112,53 @@ def accounts():
# and this app should allow delivering a somewhat non usuable Token
# token which gets sent to this endpoint and gets used to get back the right client_id
# to associate in the database...
tok.token_type = token['token_type']
tok.access_token = token['access_token']
tok.token_type = token["token_type"]
tok.access_token = token["access_token"]
tok.refresh_token = None
tok.scope = token['scope']
tok.scope = token["scope"]
tok.revoked = False
tok.expires_in = token['expires_in']
tok.expires_in = token["expires_in"]
db.session.add(tok)
db.session.commit()
return jsonify({
**token,
'created_at': tok.issued_at
}), 200
return jsonify({**token, "created_at": tok.issued_at}), 200
@bp_api_v1_accounts.route("/api/v1/accounts/verify_credentials", methods=["POST"])
@require_oauth("read")
def accounts_verify_credentials():
"""
Eats nothing
:return: Account object with extra source attribute
"""
user = current_token.user
return jsonify(
account={
"id": user.id,
"username": user.name,
"acct": user.name,
"display_name": user.display_name,
"locked": False,
"created_at": user.created_at,
"followers_count": user.followers.count,
"following_count": user.followings.count,
"statuses_count": user.sounds.count,
"note": user.actor[0].summary,
"url": user.actor[0].url,
"avatar": "",
"avatar_static": "",
"header": "",
"header_static": "",
"emojis": [],
"moved": None,
"fields": [],
"bot": False,
},
source={
"privacy": "unlisted",
"sensitive": False,
"language": user.locale,
"note": user.actor[0].summary,
"fields": [],
},
)

36
controllers/api/v1/auth.py

@ -13,11 +13,11 @@ def create_client():
:return:
"""
err = False
if not 'client_name' in request.form:
if "client_name" not in request.form:
err = True
elif not 'redirect_uris' in request.form:
elif "redirect_uris" not in request.form:
err = True
elif not 'scopes' in request.form:
elif "scopes" not in request.form:
err = True
if err:
@ -28,19 +28,19 @@ def create_client():
return response
client = OAuth2Client()
client.client_name = request.form.get('client_name')
client.client_uri = request.form.get('website', None)
client.redirect_uri = request.form.get('redirect_uris')
client.scope = request.form.get('scopes')
client.client_name = request.form.get("client_name")
client.client_uri = request.form.get("website", None)
client.redirect_uri = request.form.get("redirect_uris")
client.scope = request.form.get("scopes")
client.client_id = gen_salt(24)
if client.token_endpoint_auth_method == 'none':
client.client_secret = ''
if client.token_endpoint_auth_method == "none":
client.client_secret = ""
else:
client.client_secret = gen_salt(48)
# this needs to be hardcoded for whatever reason
client.response_type = 'code'
client.grant_type = 'authorization_code\r\nclient_credentials\r\npassword'
client.token_endpoint_auth_method = 'client_secret_post'
client.response_type = "code"
client.grant_type = "authorization_code\r\nclient_credentials\r\npassword"
client.token_endpoint_auth_method = "client_secret_post"
db.session.add(client)
db.session.commit()
@ -52,7 +52,7 @@ def create_client():
"name": client.client_name,
"redirect_uri": client.redirect_uri,
"website": client.client_uri,
"vapid_key": None # FIXME to implement this
"vapid_key": None, # FIXME to implement this
}
response = jsonify(resp)
response.mimetype = "application/json; charset=utf-8"
@ -65,15 +65,15 @@ def create_client():
def oauth_authorize():
# input: client_id, client_secret, redirect_uri, scope
# should authorize the user, and then return auth code if urn:ietf:wg:oauth:2.0:oob or redirect
if request.method == 'GET':
if request.method == "GET":
abort(404)
else:
grant_user = None
if 'username' and 'password' in request.form:
username = request.form.get('username')
if "username" and "password" in request.form:
username = request.form.get("username")
user = User.query.filter_by(name=username).first()
if user and user.check_password(request.form.get('password')):
if user and user.check_password(request.form.get("password")):
grant_user = user
return authorization.create_authorization_response(grant_user=grant_user)
@ -87,4 +87,4 @@ def oauth_token():
@bp_api_v1_auth.route("/oauth/revoke", methods=["POST"])
def oauth_revoke():
# input: client_id, client_secret
return authorization.create_endpoint_response('revocation')
return authorization.create_endpoint_response("revocation")

29
migrations/versions/36_588930f4335e_.py

@ -7,8 +7,8 @@ Create Date: 2019-06-21 16:59:35.554167
"""
# revision identifiers, used by Alembic.
revision = '588930f4335e'
down_revision = 'da6070b2ce4e'
revision = "588930f4335e"
down_revision = "da6070b2ce4e"
from alembic import op # noqa: E402
import sqlalchemy as sa # noqa: E402
@ -16,22 +16,23 @@ import sqlalchemy as sa # noqa: E402
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('app',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('client_name', sa.String(length=255), nullable=True),
sa.Column('redirect_uris', sa.String(length=255), nullable=True),
sa.Column('scopes', sa.ARRAY(sa.String(length=255)), nullable=False),
sa.Column('website', sa.String(length=255), nullable=True),
sa.Column('client_id', sa.String(length=255), nullable=True),
sa.Column('client_secret', sa.String(length=255), nullable=True),
sa.Column('inserted_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id')
op.create_table(
"app",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("client_name", sa.String(length=255), nullable=True),
sa.Column("redirect_uris", sa.String(length=255), nullable=True),
sa.Column("scopes", sa.ARRAY(sa.String(length=255)), nullable=False),
sa.Column("website", sa.String(length=255), nullable=True),
sa.Column("client_id", sa.String(length=255), nullable=True),
sa.Column("client_secret", sa.String(length=255), nullable=True),
sa.Column("inserted_at", sa.DateTime(), nullable=True),
sa.Column("updated_at", sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('app')
op.drop_table("app")
# ### end Alembic commands ###

123
migrations/versions/37_9569dcac2399_.py

@ -7,8 +7,8 @@ Create Date: 2019-06-21 17:20:46.229819
"""
# revision identifiers, used by Alembic.
revision = '9569dcac2399'
down_revision = '588930f4335e'
revision = "9569dcac2399"
down_revision = "588930f4335e"
from alembic import op # noqa: E402
import sqlalchemy as sa # noqa: E402
@ -16,70 +16,73 @@ import sqlalchemy as sa # noqa: E402
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('oauth2_client',
sa.Column('client_id', sa.String(length=48), nullable=True),
sa.Column('client_secret', sa.String(length=120), nullable=True),
sa.Column('issued_at', sa.Integer(), nullable=False),
sa.Column('expires_at', sa.Integer(), nullable=False),
sa.Column('redirect_uri', sa.Text(), nullable=True),
sa.Column('token_endpoint_auth_method', sa.String(length=48), nullable=True),
sa.Column('grant_type', sa.Text(), nullable=False),
sa.Column('response_type', sa.Text(), nullable=False),
sa.Column('scope', sa.Text(), nullable=False),
sa.Column('client_name', sa.String(length=100), nullable=True),
sa.Column('client_uri', sa.Text(), nullable=True),
sa.Column('logo_uri', sa.Text(), nullable=True),
sa.Column('contact', sa.Text(), nullable=True),
sa.Column('tos_uri', sa.Text(), nullable=True),
sa.Column('policy_uri', sa.Text(), nullable=True),
sa.Column('jwks_uri', sa.Text(), nullable=True),
sa.Column('jwks_text', sa.Text(), nullable=True),
sa.Column('i18n_metadata', sa.Text(), nullable=True),
sa.Column('software_id', sa.String(length=36), nullable=True),
sa.Column('software_version', sa.String(length=48), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
op.create_table(
"oauth2_client",
sa.Column("client_id", sa.String(length=48), nullable=True),
sa.Column("client_secret", sa.String(length=120), nullable=True),
sa.Column("issued_at", sa.Integer(), nullable=False),
sa.Column("expires_at", sa.Integer(), nullable=False),
sa.Column("redirect_uri", sa.Text(), nullable=True),
sa.Column("token_endpoint_auth_method", sa.String(length=48), nullable=True),
sa.Column("grant_type", sa.Text(), nullable=False),
sa.Column("response_type", sa.Text(), nullable=False),
sa.Column("scope", sa.Text(), nullable=False),
sa.Column("client_name", sa.String(length=100), nullable=True),
sa.Column("client_uri", sa.Text(), nullable=True),
sa.Column("logo_uri", sa.Text(), nullable=True),
sa.Column("contact", sa.Text(), nullable=True),
sa.Column("tos_uri", sa.Text(), nullable=True),
sa.Column("policy_uri", sa.Text(), nullable=True),
sa.Column("jwks_uri", sa.Text(), nullable=True),
sa.Column("jwks_text", sa.Text(), nullable=True),
sa.Column("i18n_metadata", sa.Text(), nullable=True),
sa.Column("software_id", sa.String(length=36), nullable=True),
sa.Column("software_version", sa.String(length=48), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["user_id"], ["user.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f('ix_oauth2_client_client_id'), 'oauth2_client', ['client_id'], unique=False)
op.create_table('oauth2_code',
sa.Column('code', sa.String(length=120), nullable=False),
sa.Column('client_id', sa.String(length=48), nullable=True),
sa.Column('redirect_uri', sa.Text(), nullable=True),
sa.Column('response_type', sa.Text(), nullable=True),
sa.Column('scope', sa.Text(), nullable=True),
sa.Column('auth_time', sa.Integer(), nullable=False),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('code')
op.create_index(op.f("ix_oauth2_client_client_id"), "oauth2_client", ["client_id"], unique=False)
op.create_table(
"oauth2_code",
sa.Column("code", sa.String(length=120), nullable=False),
sa.Column("client_id", sa.String(length=48), nullable=True),
sa.Column("redirect_uri", sa.Text(), nullable=True),
sa.Column("response_type", sa.Text(), nullable=True),
sa.Column("scope", sa.Text(), nullable=True),
sa.Column("auth_time", sa.Integer(), nullable=False),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["user_id"], ["user.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("code"),
)
op.create_table('oauth2_token',
sa.Column('client_id', sa.String(length=48), nullable=True),
sa.Column('token_type', sa.String(length=40), nullable=True),
sa.Column('access_token', sa.String(length=255), nullable=False),
sa.Column('refresh_token', sa.String(length=255), nullable=True),
sa.Column('scope', sa.Text(), nullable=True),
sa.Column('revoked', sa.Boolean(), nullable=True),
sa.Column('issued_at', sa.Integer(), nullable=False),
sa.Column('expires_in', sa.Integer(), nullable=False),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('access_token')
op.create_table(
"oauth2_token",
sa.Column("client_id", sa.String(length=48), nullable=True),
sa.Column("token_type", sa.String(length=40), nullable=True),
sa.Column("access_token", sa.String(length=255), nullable=False),
sa.Column("refresh_token", sa.String(length=255), nullable=True),
sa.Column("scope", sa.Text(), nullable=True),
sa.Column("revoked", sa.Boolean(), nullable=True),
sa.Column("issued_at", sa.Integer(), nullable=False),
sa.Column("expires_in", sa.Integer(), nullable=False),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["user_id"], ["user.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("access_token"),
)
op.create_index(op.f('ix_oauth2_token_refresh_token'), 'oauth2_token', ['refresh_token'], unique=False)
op.create_index(op.f("ix_oauth2_token_refresh_token"), "oauth2_token", ["refresh_token"], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_oauth2_token_refresh_token'), table_name='oauth2_token')
op.drop_table('oauth2_token')
op.drop_table('oauth2_code')
op.drop_index(op.f('ix_oauth2_client_client_id'), table_name='oauth2_client')
op.drop_table('oauth2_client')
op.drop_index(op.f("ix_oauth2_token_refresh_token"), table_name="oauth2_token")
op.drop_table("oauth2_token")
op.drop_table("oauth2_code")
op.drop_index(op.f("ix_oauth2_client_client_id"), table_name="oauth2_client")
op.drop_table("oauth2_client")
# ### end Alembic commands ###

32
migrations/versions/38_d3745d45e223_.py

@ -7,31 +7,33 @@ Create Date: 2019-06-21 19:58:34.817643
"""
# revision identifiers, used by Alembic.
revision = 'd3745d45e223'
down_revision = '9569dcac2399'
revision = "d3745d45e223"
down_revision = "9569dcac2399"
from alembic import op # noqa: E402
import sqlalchemy as sa # noqa: E402
from sqlalchemy.dialects import postgresql
from sqlalchemy.dialects import postgresql # noqa: E402
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('app')
op.drop_table("app")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('app',
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column('client_name', sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column('redirect_uris', sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column('scopes', postgresql.ARRAY(sa.VARCHAR(length=255)), autoincrement=False, nullable=False),
sa.Column('website', sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column('client_id', sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column('client_secret', sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column('inserted_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
sa.Column('updated_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
sa.PrimaryKeyConstraint('id', name='app_pkey')
op.create_table(
"app",
sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column("client_name", sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column("redirect_uris", sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column("scopes", postgresql.ARRAY(sa.VARCHAR(length=255)), autoincrement=False, nullable=False),
sa.Column("website", sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column("client_id", sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column("client_secret", sa.VARCHAR(length=255), autoincrement=False, nullable=True),
sa.Column("inserted_at", postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
sa.Column("updated_at", postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
sa.PrimaryKeyConstraint("id", name="app_pkey"),
)
# ### end Alembic commands ###

28
migrations/versions/39_463da36c5bd5_.py

@ -0,0 +1,28 @@
"""Add user created_at and updated_at
Revision ID: 463da36c5bd5
Revises: d3745d45e223
Create Date: 2019-06-24 10:33:50.764691
"""
# revision identifiers, used by Alembic.
revision = "463da36c5bd5"
down_revision = "d3745d45e223"
from alembic import op # noqa: E402
import sqlalchemy as sa # noqa: E402
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("user", sa.Column("created_at", sa.DateTime(), nullable=True))
op.add_column("user", sa.Column("updated_at", sa.DateTime(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("user", "updated_at")
op.drop_column("user", "created_at")
# ### end Alembic commands ###

34
models.py

@ -15,18 +15,13 @@ from sqlalchemy_searchable import make_searchable
from sqlalchemy_utils.types.choice import ChoiceType
from sqlalchemy_utils.types.url import URLType
from sqlalchemy_utils.types.json import JSONType
from sqlalchemy.types import ARRAY
from little_boxes.key import Key as LittleBoxesKey
from activitypub.utils import ap_url
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import text as sa_text
from little_boxes import activitypub as ap
from urllib.parse import urlparse
from authlib.flask.oauth2.sqla import (
OAuth2ClientMixin,
OAuth2AuthorizationCodeMixin,
OAuth2TokenMixin,
)
from authlib.flask.oauth2.sqla import OAuth2ClientMixin, OAuth2AuthorizationCodeMixin, OAuth2TokenMixin
import time
db = SQLAlchemy()
@ -74,6 +69,11 @@ class User(db.Model, UserMixin):
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
created_at = db.Column(db.DateTime(timezone=False), default=datetime.datetime.utcnow)
updated_at = db.Column(
db.DateTime(timezone=False), default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow
)
display_name = db.Column(db.String(30), nullable=True, info={"label": "Display name"})
locale = db.Column(db.String(5), default="en")
@ -127,7 +127,6 @@ class User(db.Model, UserMixin):
return verify_password(password, self.password)
event.listen(User.name, "set", User.generate_slug, retval=False)
@ -142,30 +141,27 @@ user_datastore = SQLAlchemyUserDatastore(db, User, Role)
class OAuth2Client(db.Model, OAuth2ClientMixin):
__tablename__ = 'oauth2_client'
__tablename__ = "oauth2_client"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(
db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'))
user = db.relationship('User')
user_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="CASCADE"))
user = db.relationship("User")
class OAuth2AuthorizationCode(db.Model, OAuth2AuthorizationCodeMixin):
__tablename__ = 'oauth2_code'
__tablename__ = "oauth2_code"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(
db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'))
user = db.relationship('User')
user_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="CASCADE"))
user = db.relationship("User")
class OAuth2Token(db.Model, OAuth2TokenMixin):
__tablename__ = 'oauth2_token'
__tablename__ = "oauth2_token"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(
db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'))
user = db.relationship('User')
user_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="CASCADE"))
user = db.relationship("User")
def is_refresh_token_expired(self):
expires_at = self.issued_at + self.expires_in * 2

1
pyproject.toml

@ -8,6 +8,7 @@ exclude = '''
| \.idea
| \__pycache__
| venv
| front
)/
'''
skip-numeric-underscore-normalization = true
Loading…
Cancel
Save