Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions app/alembic/versions/01f3f05a5b11_add_primary_group_id.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
"""Add primaryGroupId attribute and domain computers group.

Revision ID: 01f3f05a5b11
Revises: 8164b4a9e1f1
Revises: c007129b7973
Create Date: 2025-09-26 12:36:05.974255

"""

import sqlalchemy as sa
from alembic import op
from dishka import AsyncContainer, Scope
from sqlalchemy import delete, exists, select
from sqlalchemy.exc import DBAPIError, IntegrityError
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncSession
from sqlalchemy.orm import Session, selectinload

from constants import DOMAIN_COMPUTERS_GROUP_NAME
from entities import Attribute, Directory, EntityType, Group
from enums import EntityTypeNames
from extra.alembic_utils import temporary_stub_column
from ldap_protocol.ldap_schema.attribute_value_validator import (
AttributeValueValidator,
)
Expand All @@ -35,6 +38,7 @@
depends_on: None = None


@temporary_stub_column("is_system", sa.Boolean())
def upgrade(container: AsyncContainer) -> None:
"""Upgrade."""

Expand All @@ -51,15 +55,15 @@ async def _add_domain_computers_group(connection: AsyncConnection) -> None: # n
try:
group_dir_query = select(
exists(Directory)
.where(qa(Directory.name) == "domain computers"),
.where(qa(Directory.name) == DOMAIN_COMPUTERS_GROUP_NAME),
) # fmt: skip
group_dir = (await session.scalars(group_dir_query)).one()

if group_dir:
return

dir_, group_ = await create_group(
name="domain computers",
name=DOMAIN_COMPUTERS_GROUP_NAME,
sid=515,
attribute_value_validator=AttributeValueValidator(),
session=session,
Expand Down Expand Up @@ -165,6 +169,7 @@ async def _add_primary_group_id(connection: AsyncConnection) -> None: # noqa: A
op.run_async(_add_primary_group_id)


@temporary_stub_column("is_system", sa.Boolean())
def downgrade(container: AsyncContainer) -> None:
"""Downgrade."""
bind = op.get_bind()
Expand Down
2 changes: 2 additions & 0 deletions app/alembic/versions/05ddc0bd562a_add_roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncSession

from entities import Directory, Group
from extra.alembic_utils import temporary_stub_column
from ldap_protocol.roles.role_use_case import RoleUseCase
from ldap_protocol.utils.queries import get_base_directories
from repo.pg.tables import queryable_attr as qa
Expand All @@ -23,6 +24,7 @@
depends_on: None = None


@temporary_stub_column("is_system", sa.Boolean())
def upgrade(container: AsyncContainer) -> None:
"""Upgrade."""
op.create_table(
Expand Down
31 changes: 14 additions & 17 deletions app/alembic/versions/16a9fa2c1f1e_rename_readonly_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@

"""

import sqlalchemy as sa
from alembic import op
from dishka import AsyncContainer
from sqlalchemy import select, update
from sqlalchemy.exc import DBAPIError, IntegrityError
from sqlalchemy.orm import Session, selectinload

from constants import READ_ONLY_GROUP_NAME
from entities import Attribute, Directory
from extra.alembic_utils import temporary_stub_column
from repo.pg.tables import queryable_attr as qa

# revision identifiers, used by Alembic.
Expand All @@ -22,6 +25,7 @@
depends_on: None | list[str] = None


@temporary_stub_column("is_system", sa.Boolean())
def upgrade(container: AsyncContainer) -> None: # noqa: ARG001
"""Upgrade."""
bind = op.get_bind()
Expand All @@ -30,19 +34,15 @@ def upgrade(container: AsyncContainer) -> None: # noqa: ARG001
try:
ro_dir_query = (
select(Directory)
.options(
selectinload(qa(Directory.parent)),
)
.where(
qa(Directory.name) == "readonly domain controllers",
)
) # fmt: skip
.options(selectinload(qa(Directory.parent)))
.where(qa(Directory.name) == "readonly domain controllers")
)
ro_dir = session.scalar(ro_dir_query)

if not ro_dir:
return

ro_dir.name = "read-only"
ro_dir.name = READ_ONLY_GROUP_NAME

ro_dir.create_path(ro_dir.parent, ro_dir.get_dn_prefix())

Expand Down Expand Up @@ -73,6 +73,7 @@ def upgrade(container: AsyncContainer) -> None: # noqa: ARG001
session.close()


@temporary_stub_column("is_system", sa.Boolean())
def downgrade(container: AsyncContainer) -> None: # noqa: ARG001
"""Downgrade."""
bind = op.get_bind()
Expand All @@ -81,13 +82,9 @@ def downgrade(container: AsyncContainer) -> None: # noqa: ARG001
try:
ro_dir_query = (
select(Directory)
.options(
selectinload(qa(Directory.parent)),
)
.where(
qa(Directory.name) == "read-only",
)
) # fmt: skip
.options(selectinload(qa(Directory.parent)))
.where(qa(Directory.name) == READ_ONLY_GROUP_NAME)
)
ro_dir = session.scalar(ro_dir_query)

if not ro_dir:
Expand All @@ -102,7 +99,7 @@ def downgrade(container: AsyncContainer) -> None: # noqa: ARG001
.filter_by(
name="sAMAccountName",
directory=ro_dir,
value="read-only",
value=READ_ONLY_GROUP_NAME,
)
.values({"value": ro_dir.name}),
)
Expand All @@ -112,7 +109,7 @@ def downgrade(container: AsyncContainer) -> None: # noqa: ARG001
.filter_by(
name="cn",
directory=ro_dir,
value="read-only",
value=READ_ONLY_GROUP_NAME,
)
.values({"value": ro_dir.name}),
)
Expand Down
4 changes: 2 additions & 2 deletions app/alembic/versions/275222846605_initial_ldap_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from sqlalchemy.orm import Session, selectinload

from entities import Attribute, AttributeType, ObjectClass
from extra.alembic_utils import temporary_stub_entity_type_name
from extra.alembic_utils import temporary_stub_column
from ldap_protocol.ldap_schema.attribute_type_dao import AttributeTypeDAO
from ldap_protocol.ldap_schema.dto import AttributeTypeDTO
from ldap_protocol.utils.raw_definition_parser import (
Expand All @@ -35,7 +35,7 @@
ad_2012_r2_schema_json = json.loads(ad_2012_r2_schema)


@temporary_stub_entity_type_name
@temporary_stub_column("entity_type_id", sa.Integer())
def upgrade(container: AsyncContainer) -> None:
"""Upgrade."""
bind = op.get_bind()
Expand Down
5 changes: 3 additions & 2 deletions app/alembic/versions/4442d1d982a4_remove_krb_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@

"""

import sqlalchemy as sa
from alembic import op
from dishka import AsyncContainer
from sqlalchemy import delete
from sqlalchemy.orm import Session

from entities import Attribute, Directory
from extra.alembic_utils import temporary_stub_entity_type_name
from extra.alembic_utils import temporary_stub_column

# revision identifiers, used by Alembic.
revision = "4442d1d982a4"
Expand All @@ -21,7 +22,7 @@
depends_on: None | str = None


@temporary_stub_entity_type_name
@temporary_stub_column("entity_type_id", sa.Integer())
def upgrade(container: AsyncContainer) -> None: # noqa: ARG001
"""Upgrade."""
bind = op.get_bind()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@

"""

import sqlalchemy as sa
from alembic import op
from dishka import AsyncContainer, Scope
from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncSession
from sqlalchemy.orm import joinedload

from entities import Attribute, Directory
from extra.alembic_utils import temporary_stub_column
from ldap_protocol.objects import UserAccountControlFlag
from ldap_protocol.utils.helpers import create_integer_hash
from repo.pg.tables import queryable_attr as qa
Expand All @@ -24,6 +26,7 @@
depends_on: None | list[str] = None


@temporary_stub_column("is_system", sa.Boolean())
def upgrade(container: AsyncContainer) -> None:
"""Upgrade."""

Expand Down Expand Up @@ -90,6 +93,7 @@ async def _change_uid_admin(connection: AsyncConnection) -> None: # noqa: ARG00
op.run_async(_change_uid_admin)


@temporary_stub_column("is_system", sa.Boolean())
def downgrade(container: AsyncContainer) -> None:
"""Downgrade."""

Expand Down
2 changes: 2 additions & 0 deletions app/alembic/versions/6c858cc05da7_add_default_admin_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from sqlalchemy.orm import Session

from entities import Attribute, User
from extra.alembic_utils import temporary_stub_column
from repo.pg.tables import queryable_attr as qa

# revision identifiers, used by Alembic.
Expand All @@ -21,6 +22,7 @@
depends_on: None | list[str] = None


@temporary_stub_column("is_system", sa.Boolean())
def upgrade(container: AsyncContainer) -> None: # noqa: ARG001
"""Upgrade."""
bind = op.get_bind()
Expand Down
9 changes: 6 additions & 3 deletions app/alembic/versions/6f8fe2548893_fix_read_only.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@

"""

import sqlalchemy as sa
from alembic import op
from dishka import AsyncContainer
from sqlalchemy import delete, select, update
from sqlalchemy.orm import Session

from constants import DOMAIN_USERS_GROUP_NAME
from entities import Attribute, Directory
from extra.alembic_utils import temporary_stub_entity_type_name
from extra.alembic_utils import temporary_stub_column
from ldap_protocol.utils.helpers import create_integer_hash

# revision identifiers, used by Alembic.
Expand All @@ -22,7 +24,8 @@
depends_on: None = None


@temporary_stub_entity_type_name
@temporary_stub_column("entity_type_id", sa.Integer())
@temporary_stub_column("is_system", sa.Boolean())
def upgrade(container: AsyncContainer) -> None: # noqa: ARG001
"""Upgrade."""
bind = op.get_bind()
Expand All @@ -43,7 +46,7 @@ def upgrade(container: AsyncContainer) -> None: # noqa: ARG001
.filter_by(
name="sAMAccountName",
directory=ro_dir,
value="domain users",
value=DOMAIN_USERS_GROUP_NAME,
)
.values({"value": ro_dir.name}),
)
Expand Down
104 changes: 104 additions & 0 deletions app/alembic/versions/71e642808369_add_directory_is_system.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""Add directory is_system column.

Revision ID: 71e642808369
Revises: a99f866a7e3a
Create Date: 2026-01-15 09:08:12.866533

"""

import sqlalchemy as sa
from alembic import op
from dishka import AsyncContainer, Scope
from sqlalchemy import update
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncSession
from sqlalchemy.orm import Session

from constants import (
COMPUTERS_CONTAINER_NAME,
DOMAIN_ADMIN_GROUP_NAME,
DOMAIN_COMPUTERS_GROUP_NAME,
DOMAIN_USERS_GROUP_NAME,
GROUPS_CONTAINER_NAME,
READ_ONLY_GROUP_NAME,
USERS_CONTAINER_NAME,
)
from entities import Directory
from ldap_protocol.utils.queries import get_base_directories
from repo.pg.tables import queryable_attr as qa

# revision identifiers, used by Alembic.
revision: None | str = "71e642808369"
down_revision: None | str = "a99f866a7e3a"
branch_labels: None | list[str] = None
depends_on: None | list[str] = None


def upgrade(container: AsyncContainer) -> None:
"""Upgrade."""
bind = op.get_bind()
session = Session(bind=bind)

op.add_column(
"Directory",
sa.Column("is_system", sa.Boolean(), nullable=True),
)
# NOTE: If instances of Directories exists, set default value
session.execute(update(Directory).values({"is_system": False}))
op.alter_column("Directory", "is_system", nullable=False)

async def _indicate_system_directories(
connection: AsyncConnection, # noqa: ARG001
) -> None:
async with container(scope=Scope.REQUEST) as cnt:
session = await cnt.get(AsyncSession)

base_dn_list = await get_base_directories(session)
if not base_dn_list:
return

for base_dn in base_dn_list:
base_dn.is_system = True

await session.flush()

await session.execute(
update(Directory)
.where(
qa(Directory.is_system).is_(False),
qa(Directory.name).in_(
(
GROUPS_CONTAINER_NAME,
DOMAIN_ADMIN_GROUP_NAME,
DOMAIN_USERS_GROUP_NAME,
READ_ONLY_GROUP_NAME,
DOMAIN_COMPUTERS_GROUP_NAME,
COMPUTERS_CONTAINER_NAME,
USERS_CONTAINER_NAME,
"services",
"krbadmin",
"kerberos",
),
),
)
.values(is_system=True),
)
await session.flush()

# NOTE: It's required to mark only administrator users as system.
# Because only main administrator has object_class=='user'.
await session.execute(
update(Directory)
.where(
qa(Directory.is_system).is_(False),
qa(Directory.object_class) == "user",
)
.values(is_system=True),
)
await session.flush()

op.run_async(_indicate_system_directories)


def downgrade(container: AsyncContainer) -> None: # noqa: ARG001
"""Downgrade."""
op.drop_column("Directory", "is_system")
Loading