From 14b75099f7176544df4bfc4ad2886c3aef80630c Mon Sep 17 00:00:00 2001 From: Timur Enikeev Date: Thu, 28 Nov 2024 01:48:06 -0500 Subject: [PATCH 1/2] Migration for Verified users --- .../5d71a2a2405d_verified_user_group.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 migrations/versions/5d71a2a2405d_verified_user_group.py diff --git a/migrations/versions/5d71a2a2405d_verified_user_group.py b/migrations/versions/5d71a2a2405d_verified_user_group.py new file mode 100644 index 00000000..f80b69ab --- /dev/null +++ b/migrations/versions/5d71a2a2405d_verified_user_group.py @@ -0,0 +1,53 @@ +"""verified user group + +Revision ID: 5d71a2a2405d +Revises: 2d29fc132e89 +Create Date: 2024-11-28 00:01:19.608684 + +""" + +import sqlalchemy as sa +from alembic import op + + +# revision identifiers, used by Alembic. +revision = '5d71a2a2405d' +down_revision = '2d29fc132e89' +branch_labels = None +depends_on = None + + +def upgrade(): + conn = op.get_bind() + users_group_id = conn.execute( + sa.text('SELECT "value_integer" FROM "dynamic_option" WHERE name=\'users_group_id\'') + ).scalar() + + query = 'INSERT INTO "group" (name, parent_id, create_ts, is_deleted, update_ts) VALUES (:name, :parent_id, CURRENT_TIMESTAMP, false, CURRENT_TIMESTAMP)' + conn.execute(sa.text(query).bindparams(name="verified", parent_id=users_group_id)) + + verified_group_id = conn.execute(sa.text('SELECT "id" FROM "group" WHERE name=\'verified\'')).scalar() + query = 'INSERT INTO "dynamic_option" (name, value_integer, create_ts, update_ts) VALUES (:name, :value_integer, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)' + conn.execute(sa.text(query).bindparams(name="verified_group_id", value_integer=verified_group_id)) + + verified_user_ids = conn.execute( + sa.text('SELECT "user_id" FROM "auth_method" WHERE auth_method=\'lkmsu_auth\' AND is_deleted=\'false\'') + ).scalars() + query = 'INSERT INTO "user_group" VALUES (:user_id, :group_id, false)' + for user_id in verified_user_ids: + conn.execute(sa.text(query).bindparams(user_id=user_id, group_id=verified_group_id)) + + +def downgrade(): + conn = op.get_bind() + try: + verified_group_id, option_id = conn.execute( + sa.text('SELECT "value_integer", "id" FROM "dynamic_option" WHERE "name" = \'verified_group_id\'') + ).one() + conn.execute( + sa.text('DELETE FROM "user_group" WHERE "group_id" = :group_id').bindparams(group_id=verified_group_id) + ) + conn.execute(sa.text('DELETE FROM "group" WHERE "id" = :id').bindparams(id=verified_group_id)) + conn.execute(sa.text('DELETE FROM "dynamic_option" WHERE "id" = :id').bindparams(id=option_id)) + except Exception as e: + pass From f9fb9a20695d612d882feb9e6d1e0f4947bb8da4 Mon Sep 17 00:00:00 2001 From: Timur Enikeev Date: Thu, 28 Nov 2024 02:48:59 -0500 Subject: [PATCH 2/2] Verification with lkmsu --- auth_backend/auth_plugins/lkmsu.py | 46 +++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/auth_backend/auth_plugins/lkmsu.py b/auth_backend/auth_plugins/lkmsu.py index 82c14266..339fcc94 100644 --- a/auth_backend/auth_plugins/lkmsu.py +++ b/auth_backend/auth_plugins/lkmsu.py @@ -13,7 +13,8 @@ from auth_backend.auth_method import AuthPluginMeta, OauthMeta, Session from auth_backend.exceptions import AlreadyExists, OauthAuthFailed from auth_backend.kafka.kafka import get_kafka_producer -from auth_backend.models.db import User, UserSession +from auth_backend.models.db import Group, User, UserGroup, UserSession +from auth_backend.models.dynamic_settings import DynamicOption from auth_backend.schemas.types.scopes import Scope from auth_backend.settings import Settings from auth_backend.utils.security import UnionAuth @@ -99,6 +100,7 @@ async def _register( old_user = {'user_id': user.id} new_user["user_id"] = user.id lk_id = cls.create_auth_method_param('user_id', lk_user_id, user.id, db_session=db.session) + cls.assign_verified_user(user) new_user = {cls.get_name(): {"user_id": lk_id.value}} userdata = await LkmsuAuth._convert_data_to_userdata_format(userinfo) background_tasks.add_task( @@ -153,6 +155,7 @@ async def _login( raise OauthAuthFailed( 'No users found for lk msu account', 'Не найдено пользователей с таким аккаунтом LK MSU', id_token ) + cls.assign_verified_user(user) userdata = await LkmsuAuth._convert_data_to_userdata_format(userinfo) background_tasks.add_task( get_kafka_producer().produce, @@ -164,6 +167,33 @@ async def _login( user, user_inp.scopes, db_session=db.session, session_name=user_inp.session_name ) + @classmethod + async def _unregister(cls, user_session: UserSession = Depends(UnionAuth(scopes=[], auto_error=True))): + """Отключает для пользователя метод входа""" + user: User = user_session.user + verified_group_id = DynamicOption.get("verified_group_id", session=db.session).value + if verified_group_id: + verified_group = Group.query(with_deleted=True, session=db.session).get(verified_group_id) + if verified_group: + user_group: UserGroup = ( + UserGroup.query(session=db.session) + .filter(UserGroup.user_id == user.id, UserGroup.group_id == verified_group.id) + .one_or_none() + ) + if user_group: + UserGroup.delete(user_group.id, session=db.session) + else: + logger.error("Verified group not found") + else: + logger.error("Fail to obtain verified group id") + + old_user = {"user_id": user_session.user.id} + new_user = {"user_id": user_session.user.id} + old_user_params = await cls._delete_auth_methods(user_session.user, db_session=db.session) + old_user[cls.get_name()] = old_user_params + await AuthPluginMeta.user_updated(new_user, old_user) + return None + @classmethod async def _redirect_url(cls): """URL на который происходит редирект после завершения входа на стороне провайдера""" @@ -176,6 +206,20 @@ async def _auth_url(cls): url=f'https://lk.msu.ru/oauth/authorize?response_type=code&client_id={cls.settings.LKMSU_CLIENT_ID}&redirect_uri={quote(cls.settings.LKMSU_REDIRECT_URL)}&scope=scope.profile.view' ) + @classmethod + def assign_verified_user(cls, user: User): + verified_group_id = DynamicOption.get("verified_group_id", session=db.session).value + if verified_group_id: + verified_group = Group.query(with_deleted=True, session=db.session).get(verified_group_id) + if verified_group: + if verified_group not in user.groups: + user.groups.append(verified_group) + else: + logger.error("Verified group not found") + else: + logger.error("Fail to obtain verified group id") + return None + @classmethod def get_student(cls, data: dict[str, Any]) -> list[dict[str | Any]]: student: dict[str, Any] = data.get("student", {})