Browse Source

Add untested yet endpoint

pull/100/head
Dashie der otter 1 year ago
parent
commit
c53075cc44
Signed by: dashie <rhaamo@leloop.org> GPG Key ID: C2D57B325840B755
11 changed files with 238 additions and 175 deletions
  1. +1
    -0
      .coveragerc
  2. +3
    -4
      app.py
  3. +3
    -9
      app_oauth.py
  4. +74
    -36
      controllers/api/v1/accounts.py
  5. +18
    -18
      controllers/api/v1/auth.py
  6. +15
    -14
      migrations/versions/36_588930f4335e_.py
  7. +63
    -60
      migrations/versions/37_9569dcac2399_.py
  8. +17
    -15
      migrations/versions/38_d3745d45e223_.py
  9. +28
    -0
      migrations/versions/39_463da36c5bd5_.py
  10. +15
    -19
      models.py
  11. +1
    -0
      pyproject.toml

+ 1
- 0
.coveragerc View File

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

+ 3
- 4
app.py View File

@@ -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


+ 3
- 9
app_oauth.py View File

@@ -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()




+ 74
- 36
controllers/api/v1/accounts.py View File

@@ -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": [],
},
)

+ 18
- 18
controllers/api/v1/auth.py View File

@@ -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")

+ 15
- 14
migrations/versions/36_588930f4335e_.py View File

@@ -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 ###

+ 63
- 60
migrations/versions/37_9569dcac2399_.py View File

@@ -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 ###

+ 17
- 15
migrations/versions/38_d3745d45e223_.py View File

@@ -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
- 0
migrations/versions/39_463da36c5bd5_.py View File

@@ -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 ###

+ 15
- 19
models.py View File

@@ -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
- 0
pyproject.toml View File

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

Loading…
Cancel
Save