diff --git a/modules/odr_core/odr_core/models/user.py b/modules/odr_core/odr_core/models/user.py index fe9e458..e52c0bc 100644 --- a/modules/odr_core/odr_core/models/user.py +++ b/modules/odr_core/odr_core/models/user.py @@ -25,9 +25,9 @@ class User(Base): image = Column(String) identity_provider = Column(String, index=True, default="omi") hashed_password = Column(String) - is_active = Column(Boolean, default=True) - is_superuser = Column(Boolean, default=False) - dco_accepted = Column(Boolean, default=False) + is_active = Column(Boolean, nullable=False, default=True) + is_superuser = Column(Boolean, nullable=False, default=False) + dco_accepted = Column(Boolean, nullable=False, default=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() diff --git a/modules/odr_datamodel/alembic/versions/e06f931c3c84_update_users_table_to_have_non_nullable_.py b/modules/odr_datamodel/alembic/versions/e06f931c3c84_update_users_table_to_have_non_nullable_.py new file mode 100644 index 0000000..88b0e83 --- /dev/null +++ b/modules/odr_datamodel/alembic/versions/e06f931c3c84_update_users_table_to_have_non_nullable_.py @@ -0,0 +1,52 @@ +"""'Update users table to have non nullable and default values for flags' + +Revision ID: e06f931c3c84 +Revises: 6917b71d9140 +Create Date: 2024-10-03 18:14:20.255813 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'e06f931c3c84' +down_revision: Union[str, None] = '6917b71d9140' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('users', 'is_active', + existing_type=sa.BOOLEAN(), + nullable=False, + server_default=sa.text('true')) + op.alter_column('users', 'is_superuser', + existing_type=sa.BOOLEAN(), + nullable=False, + server_default=sa.text('false')) + op.alter_column('users', 'dco_accepted', + existing_type=sa.BOOLEAN(), + nullable=False, + server_default=sa.text('false')) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('users', 'dco_accepted', + existing_type=sa.BOOLEAN(), + nullable=True, + server_default=sa.text('false')) + op.alter_column('users', 'is_superuser', + existing_type=sa.BOOLEAN(), + nullable=True, + server_default=sa.text('false')) + op.alter_column('users', 'is_active', + existing_type=sa.BOOLEAN(), + nullable=True, + server_default=sa.text('true')) + # ### end Alembic commands ### diff --git a/modules/odr_frontend/src/hooks.server.ts b/modules/odr_frontend/src/hooks.server.ts index 7f12da6..e35a09b 100644 --- a/modules/odr_frontend/src/hooks.server.ts +++ b/modules/odr_frontend/src/hooks.server.ts @@ -11,6 +11,8 @@ const authorizationHandle:Handle = async ({ event, resolve }) => { throw redirect(303, '/auth'); } else if (!session.user) { throw redirect(303, '/auth'); + } else if (!event.url.pathname.startsWith('/inactive') && (!session.user.is_active)) { + throw redirect(303, '/inactive'); } else if (!event.url.pathname.startsWith('/dco') && (!session.user.dco_accepted)) { throw redirect(303, '/dco'); } else if (event.url.pathname.startsWith('/admin') && (!session.user.is_superuser)) { diff --git a/modules/odr_frontend/src/lib/admin/ActiveToggle.svelte b/modules/odr_frontend/src/lib/admin/ActiveToggle.svelte new file mode 100644 index 0000000..4084a00 --- /dev/null +++ b/modules/odr_frontend/src/lib/admin/ActiveToggle.svelte @@ -0,0 +1,14 @@ + + + diff --git a/modules/odr_frontend/src/lib/admin/utils.ts b/modules/odr_frontend/src/lib/admin/utils.ts index 49d7e06..17aa189 100644 --- a/modules/odr_frontend/src/lib/admin/utils.ts +++ b/modules/odr_frontend/src/lib/admin/utils.ts @@ -1,7 +1,21 @@ import type { IDBUser } from '$lib/server/pg'; export async function toggleSuperUser(user: IDBUser) { - const req = await fetch('/admin/users/api', { + const req = await fetch('/admin/users/api/toggleSuperUser', { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ user }) + }); + const res = await req.json(); + if (!res.success) { + console.error(res.error); //TODO: Need to add a toast here + } +} + +export async function toggleActive(user: IDBUser) { + const req = await fetch('/admin/users/api/toggleActive', { method: 'PUT', headers: { 'Content-Type': 'application/json' diff --git a/modules/odr_frontend/src/lib/server/pg.ts b/modules/odr_frontend/src/lib/server/pg.ts index 712d8a0..cada4cb 100644 --- a/modules/odr_frontend/src/lib/server/pg.ts +++ b/modules/odr_frontend/src/lib/server/pg.ts @@ -4,7 +4,7 @@ import pg from 'pg'; export interface IDBUser { id: number; email: string; - is_active: boolean | null; + is_active: boolean; is_superuser: boolean; created_at: Date; updated_at: Date | null; diff --git a/modules/odr_frontend/src/routes/admin/users/+page.svelte b/modules/odr_frontend/src/routes/admin/users/+page.svelte index 48a4da1..e4564db 100644 --- a/modules/odr_frontend/src/routes/admin/users/+page.svelte +++ b/modules/odr_frontend/src/routes/admin/users/+page.svelte @@ -15,6 +15,7 @@ id name isSuperUser? + isActive? email diff --git a/modules/odr_frontend/src/routes/admin/users/UserRow.svelte b/modules/odr_frontend/src/routes/admin/users/UserRow.svelte index 14b309a..cb1dd51 100644 --- a/modules/odr_frontend/src/routes/admin/users/UserRow.svelte +++ b/modules/odr_frontend/src/routes/admin/users/UserRow.svelte @@ -1,5 +1,6 @@ @@ -8,6 +9,7 @@ {user.id} {user.name} + {user.email} diff --git a/modules/odr_frontend/src/routes/admin/users/api/toggleActive/+server.ts b/modules/odr_frontend/src/routes/admin/users/api/toggleActive/+server.ts new file mode 100644 index 0000000..a0aeb18 --- /dev/null +++ b/modules/odr_frontend/src/routes/admin/users/api/toggleActive/+server.ts @@ -0,0 +1,22 @@ +import { pgClient, type IDBUser } from '$lib/server/pg'; +import type { RequestHandler } from '@sveltejs/kit'; +export const PUT: RequestHandler = async ({ request }) => { + const body = await request.json(); + if (!body.user) { + return new Response(JSON.stringify({ success: false, error: 'No user provided' })); + } + const user: IDBUser = body.user; + try { + const result = await pgClient.query('UPDATE users SET is_active=$1 WHERE id=$2', [ + user.is_active, + user.id + ]); + console.info(`Set user ${user.id} as ${user.is_active ? 'active' : 'not active'}`); + return new Response(JSON.stringify({ success: true, result: result })); + } catch (e) { + console.error( + `Failed to set user ${user.id} as ${user.is_active ? 'active' : 'not active'}` + ); + return new Response(JSON.stringify({ success: false, error: e })); + } +}; diff --git a/modules/odr_frontend/src/routes/admin/users/api/+server.ts b/modules/odr_frontend/src/routes/admin/users/api/toggleSuperUser/+server.ts similarity index 100% rename from modules/odr_frontend/src/routes/admin/users/api/+server.ts rename to modules/odr_frontend/src/routes/admin/users/api/toggleSuperUser/+server.ts diff --git a/modules/odr_frontend/src/routes/inactive/+page.svelte b/modules/odr_frontend/src/routes/inactive/+page.svelte new file mode 100644 index 0000000..958c512 --- /dev/null +++ b/modules/odr_frontend/src/routes/inactive/+page.svelte @@ -0,0 +1,29 @@ + + Inactive User | OMI Data Pipeline + + +
+
+ +

Inactive User

+ +

+ Your user account is inactive. If you believe this is in error, please reach out in our + + Discord + + for support. +

+ +
+
+ +