diff --git a/.alembic/versions/2026-06-15_group_name_unique_per_org.py b/.alembic/versions/2026-06-15_group_name_unique_per_org.py deleted file mode 100644 index 9a8e7f9..0000000 --- a/.alembic/versions/2026-06-15_group_name_unique_per_org.py +++ /dev/null @@ -1,34 +0,0 @@ -"""group name unique per org - -Revision ID: 98e20aae555c -Revises: b6c8614ef799 -Create Date: 2026-06-15 11:05:16.673658 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '98e20aae555c' -down_revision: Union[str, Sequence[str], None] = 'b6c8614ef799' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(op.f('group_name_key'), 'group', type_='unique') - op.create_unique_constraint('uniq_group_name_org_id', 'group', ['name', 'org_id']) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('uniq_group_name_org_id', 'group', type_='unique') - op.create_unique_constraint(op.f('group_name_key'), 'group', ['name'], postgresql_nulls_not_distinct=False) - # ### end Alembic commands ### diff --git a/src/iam/models.py b/src/iam/models.py index 5e9b821..0087abc 100644 --- a/src/iam/models.py +++ b/src/iam/models.py @@ -56,18 +56,10 @@ class Permission(Base): class Group(Base): __tablename__ = "group" id = Column(Integer, primary_key=True) - name = Column(String, nullable=False) + name = Column(String, nullable=False, unique=True) org_id = Column(Integer, ForeignKey("organisation.id", ondelete="CASCADE")) - __table_args__ = ( - UniqueConstraint( - "name", - "org_id", - name="uniq_group_name_org_id", - ), - ) - user_rel = relationship("User", secondary="user_groups", back_populates="group_rel") org_rel = relationship("Organisation", back_populates="group_rel") diff --git a/src/iam/service.py b/src/iam/service.py index 056b39c..e7d6699 100644 --- a/src/iam/service.py +++ b/src/iam/service.py @@ -8,14 +8,12 @@ Exports: from typing import Annotated from datetime import datetime, timedelta, timezone +from src.iam.schemas import IAMCAoRRequest from src.service.models import Service from src.database import db_dependency from src.exceptions import UnauthorizedException from src.utils import send_email, generate_jwt -from src.iam.schemas import IAMCAoRRequest -from src.iam.models import Group - from fastapi import Request, Depends @@ -66,49 +64,3 @@ async def send_user_group_invitation( subject=subject, body=body, ) - - -async def create_default_user_group(db: db_dependency, org_model): - new_group = Group(name="Default Users", org_id=org_model.id) - db.add(new_group) - db.flush() - # Grant default permissions here - db.flush() - return new_group - - -async def assign_default_user_group(db: db_dependency, org_model, user_model): - group_model = None - for group in org_model.group_rel: - if group.name == "Default Users": - group_model = group - break - - if group_model is None: - group_model = await create_default_user_group(db=db, org_model=org_model) - - user_model.group_rel.append(group_model) - db.flush() - - -async def create_default_root_group(db: db_dependency, org_model): - new_group = Group(name="Root User", org_id=org_model.id) - db.add(new_group) - db.flush() - # Grant default permissions here - db.flush() - return new_group - - -async def assign_default_root_group(db: db_dependency, org_model, user_model): - group_model = None - for group in org_model.group_rel: - if group.name == "Root User": - group_model = group - break - - if group_model is None: - group_model = await create_default_root_group(db=db, org_model=org_model) - - user_model.group_rel.append(group_model) - db.flush() diff --git a/src/organisation/router.py b/src/organisation/router.py index f0f212a..3be0a2b 100644 --- a/src/organisation/router.py +++ b/src/organisation/router.py @@ -33,7 +33,6 @@ from src.contact.models import Contact from src.contact.schemas import ContactAddress from src.contact.exceptions import ContactNotFoundException from src.database import db_dependency -from src.iam.service import assign_default_user_group, assign_default_root_group from src.organisation.schemas_questionnaires import QuestionnaireQuestionsVersion0 from src.user.dependencies import ( user_model_body_dependency, @@ -184,10 +183,6 @@ async def create_org( # Adds currently logged-in user to org users list and sets them as root_user org_model.user_rel.append(user_model) org_model.root_user_rel = user_model - - # Creates default user and default root IAM groups and assigns them - await assign_default_user_group(db, org_model, user_model) - await assign_default_root_group(db, org_model, user_model) for contact_type in [ "billing_contact_id", "security_contact_id", @@ -352,7 +347,6 @@ async def add_user_to_org( raise ConflictException(message="User already a part of this organisation") org_model.user_rel.append(user_model) db.flush() - await assign_default_user_group(db=db, org_model=org_model, user_model=user_model) response = { "organisation": org_model, "users": [{"id": user.id, "email": user.email} for user in org_model.user_rel], diff --git a/src/user/router.py b/src/user/router.py index c4b5379..a561be4 100644 --- a/src/user/router.py +++ b/src/user/router.py @@ -10,7 +10,6 @@ Endpoints: from fastapi import APIRouter, status, BackgroundTasks -from src.iam.service import assign_default_user_group from src.organisation.exceptions import OrgNotFoundException from src.user.schemas import ( UserResponse, @@ -200,7 +199,6 @@ async def accept_invitation( org_model.user_rel.append(user_model) db.flush() - await assign_default_user_group(db=db, org_model=org_model, user_model=user_model) response = { "organisation": org_model, diff --git a/test/test_iam.py b/test/test_iam.py index b9e597c..3980eea 100644 --- a/test/test_iam.py +++ b/test/test_iam.py @@ -283,15 +283,6 @@ async def test_post_group_conflict(default_client: AsyncClient): assert resp.status_code == 409 -@pytest.mark.anyio -async def test_post_group_non_conflict(default_client: AsyncClient): - resp = await default_client.post( - "/iam/group", json={"organisation_id": 2, "name": "Org One Group"} - ) - - assert resp.status_code == 201 - - @pytest.mark.anyio async def test_put_group_perm_success(default_client: AsyncClient): resp = await default_client.put(