From 90f0701818f891778b60bf94330e08e6ef5431b5 Mon Sep 17 00:00:00 2001 From: clime Date: Apr 09 2019 11:36:17 +0000 Subject: [frontend] private tables for user and copr --- diff --git a/frontend/coprs_frontend/alembic/schema/versions/29c352bde564_separate_private_stuff_into_private_.py b/frontend/coprs_frontend/alembic/schema/versions/29c352bde564_separate_private_stuff_into_private_.py new file mode 100644 index 0000000..b0132fc --- /dev/null +++ b/frontend/coprs_frontend/alembic/schema/versions/29c352bde564_separate_private_stuff_into_private_.py @@ -0,0 +1,47 @@ +"""separate private stuff into private tables by schema + +Revision ID: 29c352bde564 +Revises: 6fed8655d074 +Create Date: 2018-12-03 12:55:34.810037 + +""" + +# revision identifiers, used by Alembic. +revision = '29c352bde564' +down_revision = '6fed8655d074' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.create_table('user_private', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('mail', sa.String(length=150), nullable=False), + sa.Column('timezone', sa.String(length=50), nullable=True), + sa.Column('api_login', sa.String(length=40), nullable=False), + sa.Column('api_token', sa.String(length=40), nullable=False), + sa.Column('api_token_expiration', sa.Date(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_user_private_user_id'), 'user_private', ['user_id'], unique=True) + op.create_table('copr_private', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('webhook_secret', sa.String(length=100), nullable=True), + sa.Column('scm_api_auth_json', sa.Text(), nullable=True), + sa.Column('copr_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['copr_id'], ['copr.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index('copr_private_webhook_secret', 'copr_private', ['webhook_secret'], unique=False) + op.create_index(op.f('ix_copr_private_copr_id'), 'copr_private', ['copr_id'], unique=True) + + +def downgrade(): + op.drop_index(op.f('ix_copr_private_copr_id'), table_name='copr_private') + op.drop_index('copr_private_webhook_secret', table_name='copr_private') + op.drop_table('copr_private') + op.drop_index(op.f('ix_user_private_user_id'), table_name='user_private') + op.drop_table('user_private') diff --git a/frontend/coprs_frontend/alembic/schema/versions/7723d580c625_copy_private_data_to_private_tables.py b/frontend/coprs_frontend/alembic/schema/versions/7723d580c625_copy_private_data_to_private_tables.py new file mode 100644 index 0000000..5c266eb --- /dev/null +++ b/frontend/coprs_frontend/alembic/schema/versions/7723d580c625_copy_private_data_to_private_tables.py @@ -0,0 +1,26 @@ +"""copy private data to private tables + +Revision ID: 7723d580c625 +Revises: 29c352bde564 +Create Date: 2018-12-03 13:49:15.501999 + +""" + +# revision identifiers, used by Alembic. +revision = '7723d580c625' +down_revision = '29c352bde564' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + session = sa.orm.sessionmaker(bind=op.get_bind())() + session.execute("""INSERT INTO copr_private(webhook_secret, scm_api_auth_json, copr_id) + (select webhook_secret, scm_api_auth_json, id from copr) ON CONFLICT DO NOTHING;""") + session.execute("""INSERT INTO user_private(mail, timezone, api_login, api_token, api_token_expiration, user_id) + (select mail, timezone, api_login, api_token, api_token_expiration, id from \"user\") ON CONFLICT DO NOTHING;""") + +def downgrade(): + # no downgrade + pass diff --git a/frontend/coprs_frontend/coprs/logic/packages_logic.py b/frontend/coprs_frontend/coprs/logic/packages_logic.py index 5b3ddcc..a0176ee 100644 --- a/frontend/coprs_frontend/coprs/logic/packages_logic.py +++ b/frontend/coprs_frontend/coprs/logic/packages_logic.py @@ -147,7 +147,7 @@ WHERE package.copr_dir_id = :copr_dir_id; clone_url_stripped = re.sub(r'(\.git)?/*$', '', clone_url) packages = (models.Package.query.join(models.Copr) - .filter(models.Copr.webhook_secret == webhook_secret) + .filter(models.CoprPrivate.webhook_secret == webhook_secret) .filter(models.Package.source_type == helpers.BuildSourceEnum("scm")) .filter(models.Package.copr_id == copr_id) .filter(models.Package.webhook_rebuild == true()) diff --git a/frontend/coprs_frontend/coprs/logic/users_logic.py b/frontend/coprs_frontend/coprs/logic/users_logic.py index 6fc898d..05b6698 100644 --- a/frontend/coprs_frontend/coprs/logic/users_logic.py +++ b/frontend/coprs_frontend/coprs/logic/users_logic.py @@ -3,7 +3,7 @@ from coprs import exceptions from flask import url_for from coprs import app, db -from coprs.models import User, Group +from coprs.models import User, Group, UserPrivate from coprs.helpers import copr_url from sqlalchemy import update @@ -16,7 +16,7 @@ class UsersLogic(object): @classmethod def get_by_api_login(cls, login): - return User.query.filter(User.api_login == login) + return User.query.join(UserPrivate).filter(UserPrivate.api_login == login) @classmethod def raise_if_cant_update_copr(cls, user, copr, message): diff --git a/frontend/coprs_frontend/coprs/models.py b/frontend/coprs_frontend/coprs/models.py index d8439cd..fd9597a 100644 --- a/frontend/coprs_frontend/coprs/models.py +++ b/frontend/coprs_frontend/coprs/models.py @@ -31,22 +31,38 @@ class CoprSearchRelatedData(object): raise "Not Implemented" -class User(db.Model, helpers.Serializer): +class UserPrivate(db.Model, helpers.Serializer): """ - Represents user of the copr frontend + Records all the private information for a user. """ - id = db.Column(db.Integer, primary_key=True) - # unique username - username = db.Column(db.String(100), nullable=False, unique=True) - # email mail = db.Column(db.String(150), nullable=False) # optional timezone timezone = db.Column(db.String(50), nullable=True) + # stuff for the cli interface + api_login = db.Column(db.String(40), nullable=False, default="abc") + api_token = db.Column(db.String(40), nullable=False, default="abc") + api_token_expiration = db.Column( + db.Date, nullable=False, default=datetime.date(2000, 1, 1)) + + user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True, nullable=False) + user = db.relationship("User", back_populates="private") + + +class User(db.Model, helpers.Serializer): + """ + Represents user of the copr frontend + """ + + id = db.Column(db.Integer, primary_key=True) + + # unique username + username = db.Column(db.String(100), nullable=False, unique=True) + # is this user proven? proven users can modify builder memory and # timeout for single builds proven = db.Column(db.Boolean, default=False) @@ -57,21 +73,72 @@ class User(db.Model, helpers.Serializer): # can this user behave as someone else? proxy = db.Column(db.Boolean, default=False) - # stuff for the cli interface - api_login = db.Column(db.String(40), nullable=False, default="abc") - api_token = db.Column(db.String(40), nullable=False, default="abc") - api_token_expiration = db.Column( - db.Date, nullable=False, default=datetime.date(2000, 1, 1)) - # list of groups as retrieved from openid openid_groups = db.Column(JSONEncodedDict) + # private info + private = db.relationship("UserPrivate", uselist=False, back_populates="user") + + def set_private(self, attr, value): + if not self.private: + self.private = UserPrivate() + setattr(self.private, attr, value) + + @property + def mail(self): + if self.private: + return self.private.mail + return None + + @property + def timezone(self): + if self.private: + return self.private.timezone + return None + + @property + def api_login(self): + if self.private: + return self.private.api_login + return None + + @property + def api_token(self): + if self.private: + return self.private.api_token + return None + + @property + def api_token_expiration(self): + if self.private: + return self.private.api_token_expiration + return None + + @mail.setter + def mail(self, value): + self.set_private("mail", value) + + @timezone.setter + def timezone(self, value): + self.set_private("timezone", value) + + @api_login.setter + def api_login(self, value): + self.set_private("api_login", value) + + @api_token.setter + def api_token(self, value): + self.set_private("api_token", value) + + @api_token_expiration.setter + def api_token_expiration(self, value): + self.set_private("api_token_expiration", value) + @property def name(self): """ Return the short username of the user, e.g. bkabrda """ - return self.username def permissions_for_copr(self, copr): @@ -179,9 +246,31 @@ class User(db.Model, helpers.Serializer): return "" +class CoprPrivate(db.Model, helpers.Serializer): + """ + Records all the private information for a copr. + """ + + __table_args__ = ( + db.Index('copr_private_webhook_secret', 'webhook_secret'), + ) + + id = db.Column(db.Integer, primary_key=True) + + # a secret to be used for webhooks authentication + webhook_secret = db.Column(db.String(100)) + + # remote Git sites auth info + scm_api_auth_json = db.Column(db.Text) + + # copr relation + copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True, nullable=False) + copr = db.relationship("Copr", back_populates="private") + + class Copr(db.Model, helpers.Serializer, CoprSearchRelatedData): """ - Represents a single copr (private repo with builds, mock chroots, etc.). + Represents a single copr (personal repo with builds, mock chroots, etc.). """ __table_args__ = ( @@ -217,9 +306,6 @@ class Copr(db.Model, helpers.Serializer, CoprSearchRelatedData): forked_from_id = db.Column(db.Integer, db.ForeignKey("copr.id")) forked_from = db.relationship("Copr", remote_side=id, backref=db.backref("forks")) - # a secret to be used for webhooks authentication - webhook_secret = db.Column(db.String(100)) - # enable networking for the builds by default build_enable_net = db.Column(db.Boolean, default=True, server_default="1", nullable=False) @@ -244,12 +330,39 @@ class Copr(db.Model, helpers.Serializer, CoprSearchRelatedData): # scm integration properties scm_repo_url = db.Column(db.Text) scm_api_type = db.Column(db.Text) - scm_api_auth_json = db.Column(db.Text) + + # private info + private = db.relationship("CoprPrivate", uselist=False, back_populates="copr") __mapper_args__ = { "order_by": created_on.desc() } + def set_private(self, attr, value): + if not self.private: + self.private = CoprPrivate() + setattr(self.private, attr, value) + + @property + def webhook_secret(self): + if self.private: + return self.private.webhook_secret + return None + + @property + def scm_api_auth_json(self): + if self.private: + return self.private.scm_api_auth_json + return None + + @webhook_secret.setter + def webhook_secret(self, value): + self.set_private("webhook_secret", value) + + @scm_api_auth_json.setter + def scm_api_auth_json(self, value): + self.set_private("scm_api_auth_json", value) + @property def main_dir(self): """