From 9b0f84bac5986e952aaa7f3f60d8b9dc2e853194 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 7 Nov 2023 23:06:22 +0000 Subject: [PATCH] OAuth: Add app type (is_clientside) --- app/blueprints/oauth/__init__.py | 7 +++++- app/models/collections.py | 1 + app/models/users.py | 9 ++++++++ app/templates/oauth/create_edit.html | 5 ++++- migrations/versions/7828535fe339_.py | 32 ++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 migrations/versions/7828535fe339_.py diff --git a/app/blueprints/oauth/__init__.py b/app/blueprints/oauth/__init__.py index 9b71e249..1241b590 100644 --- a/app/blueprints/oauth/__init__.py +++ b/app/blueprints/oauth/__init__.py @@ -22,7 +22,7 @@ from flask_babel import lazy_gettext, gettext from flask_login import current_user, login_required from flask_wtf import FlaskForm -from wtforms import StringField, SubmitField, URLField +from wtforms import StringField, SubmitField, URLField, SelectField from wtforms.validators import InputRequired, Length, Optional from app import csrf @@ -167,6 +167,10 @@ class OAuthClientForm(FlaskForm): title = StringField(lazy_gettext("Title"), [InputRequired(), Length(5, 30)]) description = StringField(lazy_gettext("Description"), [Optional()]) redirect_url = URLField(lazy_gettext("Redirect URL"), [InputRequired(), Length(5, 123)]) + app_type = SelectField(lazy_gettext("App Type"), [InputRequired()], choices=[ + ("server", "Server-side (client_secret is kept safe)"), + ("client", "Client-side (client_secret is visible to all users)"), + ], coerce=lambda x: x) submit = SubmitField(lazy_gettext("Save")) @@ -186,6 +190,7 @@ def create_edit_client(username, id_=None): abort(404) form = OAuthClientForm(formdata=request.form, obj=client) + if form.validate_on_submit(): if is_new: client = OAuthClient() diff --git a/app/models/collections.py b/app/models/collections.py index c0c1882c..d1e1f5ac 100644 --- a/app/models/collections.py +++ b/app/models/collections.py @@ -55,6 +55,7 @@ class Collection(db.Model): long_description = db.Column(db.UnicodeText, nullable=True) created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) private = db.Column(db.Boolean, nullable=False, default=False) + pinned = db.Column(db.Boolean, nullable=False, default=False) packages = db.relationship("Package", secondary=CollectionPackage.__table__, backref="collections") items = db.relationship("CollectionPackage", back_populates="collection", order_by=db.asc("order"), diff --git a/app/models/users.py b/app/models/users.py index 8458d17d..5591d2bd 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -560,6 +560,7 @@ class OAuthClient(db.Model): redirect_url = db.Column(db.String(128), nullable=False) approved = db.Column(db.Boolean, nullable=False, default=False) verified = db.Column(db.Boolean, nullable=False, default=False) + is_clientside = db.Column(db.Boolean, nullable=False, default=False) owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) owner = db.relationship("User", foreign_keys=[owner_id], back_populates="clients") @@ -567,3 +568,11 @@ class OAuthClient(db.Model): tokens = db.relationship("APIToken", back_populates="client", lazy="dynamic", cascade="all, delete, delete-orphan") created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) + + def get_app_type(self): + return "client" if self.is_clientside else "server" + + def set_app_type(self, value): + self.is_clientside = value == "client" + + app_type = property(get_app_type, set_app_type) diff --git a/app/templates/oauth/create_edit.html b/app/templates/oauth/create_edit.html index 2f8bcfc8..862a4711 100644 --- a/app/templates/oauth/create_edit.html +++ b/app/templates/oauth/create_edit.html @@ -47,7 +47,9 @@

{{ _("Application isn't approved yet") }}

- {{ _("Keep the secret safe") }} + {% if not client.is_clientside %} + {{ _("You must keep the secret safe. If you are unable, set the app type to 'client-side'.") }} + {% endif %}

@@ -65,6 +67,7 @@

{{ _("Application isn't approved yet") }}

{{ render_field(form.title) }} {{ render_field(form.description, hint=_("Shown to users when you request access to their account")) }} {{ render_field(form.redirect_url) }} + {{ render_field(form.app_type, hint=_("Where will you store your client_secret?")) }} {{ render_submit_field(form.submit) }} diff --git a/migrations/versions/7828535fe339_.py b/migrations/versions/7828535fe339_.py new file mode 100644 index 00000000..4136fc0f --- /dev/null +++ b/migrations/versions/7828535fe339_.py @@ -0,0 +1,32 @@ +"""empty message + +Revision ID: 7828535fe339 +Revises: 52cf6746f255 +Create Date: 2023-11-07 22:51:39.450652 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '7828535fe339' +down_revision = '52cf6746f255' +branch_labels = None +depends_on = None + + +def upgrade(): + with op.batch_alter_table('collection', schema=None) as batch_op: + batch_op.add_column(sa.Column('pinned', sa.Boolean(), nullable=False, server_default="false")) + + with op.batch_alter_table('oauth_client', schema=None) as batch_op: + batch_op.add_column(sa.Column('is_clientside', sa.Boolean(), nullable=False, server_default="false")) + + +def downgrade(): + with op.batch_alter_table('oauth_client', schema=None) as batch_op: + batch_op.drop_column('is_clientside') + + with op.batch_alter_table('collection', schema=None) as batch_op: + batch_op.drop_column('pinned')