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
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ launch: ## run standalone app without tty container
docker compose down
docker compose run sh -c "python multidirectory.py --migrate && python ."

rerun_last_migration: ## re-run migration
rerun_last_migration:
docker exec -it multidirectory_api sh -c "alembic downgrade -1; python multidirectory.py --migrate;"

rerun_all_migrations:
docker exec -it multidirectory_api sh -c "alembic downgrade base; python multidirectory.py --migrate;"

down: ## shutdown services
docker compose -f docker-compose.test.yml down --remove-orphans
docker compose down --remove-orphans
Expand Down
7 changes: 7 additions & 0 deletions app/alembic/versions/19d86e660cf2_fix_krbadmin_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

"""

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

from enums import RoleConstants
from extra.alembic_utils import temporary_stub_column
from ldap_protocol.roles.exceptions import RoleNotFoundError
from ldap_protocol.roles.role_dao import RoleDAO
from ldap_protocol.roles.role_use_case import RoleUseCase
Expand All @@ -23,6 +25,11 @@
depends_on: None | list[str] = None


@temporary_stub_column(
"AccessControlEntries",
"attribute_type_name",
sa.String(),
)
def upgrade(container: AsyncContainer) -> None:
"""Upgrade."""

Expand Down
101 changes: 101 additions & 0 deletions app/alembic/versions/1b71cafba681_replace_attributeTypeId.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""Delete attributeTypeId add attribute_type_name from AccessControlEntries.

Revision ID: 1b71cafba681
Revises: 708b01eaf025
Create Date: 2026-03-24 16:28:49.116712

"""

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

from ldap_protocol.roles.migrations_ace_dao import (
AccessControlEntryDirectoryMappingDAO,
)
from ldap_protocol.utils.queries import get_base_directories

# revision identifiers, used by Alembic.
revision: None | str = "1b71cafba681"
down_revision: None | str = "708b01eaf025"
branch_labels: None | list[str] = None
depends_on: None | list[str] = None


def upgrade(container: AsyncContainer) -> None:
"""Upgrade."""

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

if not await get_base_directories(session):
return

await ace_dao.upgrade()
await session.commit()

op.add_column(
"AccessControlEntries",
sa.Column("attribute_type_name", sa.String(), nullable=True),
)

op.run_async(_map_ace_to_directory_name)

op.drop_index(
op.f("idx_ace_attribute_type_id"),
table_name="AccessControlEntries",
postgresql_using="hash",
)
op.drop_constraint(
op.f("AccessControlEntries_directoryAttributeTypeId_fkey"),
"AccessControlEntries",
type_="foreignkey",
)
op.drop_column("AccessControlEntries", "attributeTypeId")


def downgrade(container: AsyncContainer) -> None:
"""Downgrade."""

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

if not await get_base_directories(session):
return

await ace_dao.downgrade()
await session.commit()

op.add_column(
"AccessControlEntries",
sa.Column(
"attributeTypeId",
sa.INTEGER(),
autoincrement=False,
nullable=True,
),
)

op.run_async(_map_ace_to_directory_id)

op.create_foreign_key(
op.f("AccessControlEntries_directoryAttributeTypeId_fkey"),
"AccessControlEntries",
"Directory",
["attributeTypeId"],
["id"],
ondelete="CASCADE",
)
op.create_index(
op.f("idx_ace_attribute_type_id"),
"AccessControlEntries",
["attributeTypeId"],
unique=False,
postgresql_using="hash",
)
op.drop_column("AccessControlEntries", "attribute_type_name")
1 change: 1 addition & 0 deletions app/alembic/versions/275222846605_initial_ldap_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ async def _create_attribute_types(connection: AsyncConnection) -> None: # noqa:
AttributeTypeDTO(
oid=oid,
name=name,
ldap_display_name=name,
syntax="1.3.6.1.4.1.1466.115.121.1.15",
single_value=True,
no_user_modification=False,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ async def _change_uid_admin(connection: AsyncConnection) -> None: # noqa: ARG00


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

Expand Down
94 changes: 79 additions & 15 deletions app/alembic/versions/708b01eaf025_convert_schema_to_ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@

"""

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

from constants import ENTITY_TYPE_DTOS_V2
from constants import CONFIGURATION_DIR_NAME, ENTITY_TYPE_DTOS_V2
from enums import EntityTypeNames
from extra.alembic_utils import temporary_stub_column
from ldap_protocol.ldap_schema._legacy.attribute_type.attribute_type_use_case import ( # noqa: E501
AttributeTypeUseCaseLegacy,
)
Expand All @@ -20,6 +23,10 @@
from ldap_protocol.ldap_schema.attribute_type.attribute_type_use_case import (
AttributeTypeUseCase,
)
from ldap_protocol.ldap_schema.directory_create_use_case import (
DirectoryCreateUseCase,
)
from ldap_protocol.ldap_schema.dto import AttributeDTO, CreateDirDTO
from ldap_protocol.ldap_schema.entity_type.entity_type_use_case import (
EntityTypeUseCase,
)
Expand All @@ -38,22 +45,13 @@
depends_on: None | list[str] = None


@temporary_stub_column(
"AccessControlEntries",
"attribute_type_name",
sa.String(),
)
def upgrade(container: AsyncContainer) -> None:
"""Upgrade."""
op.drop_constraint(
op.f("AccessControlEntries_attributeTypeId_fkey"),
"AccessControlEntries",
type_="foreignkey",
)

op.create_foreign_key(
op.f("AccessControlEntries_directoryAttributeTypeId_fkey"),
"AccessControlEntries",
"Directory",
["attributeTypeId"],
["id"],
ondelete="CASCADE",
)

async def _update_entity_types(connection: AsyncConnection) -> None: # noqa: ARG001
async with container(scope=Scope.REQUEST) as cnt:
Expand All @@ -68,6 +66,35 @@ async def _update_entity_types(connection: AsyncConnection) -> None: # noqa: AR

await session.commit()

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

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

_dto = CreateDirDTO(
name=CONFIGURATION_DIR_NAME,
entity_type_name=EntityTypeNames.CONFIGURATION,
attributes=(
AttributeDTO(
name="objectClass",
values=["top", "container", "configuration"],
),
),
is_system=True,
)

await directory_create_use_case.create_dir(
dto=_dto,
parent_dir=base_dirs[0],
)
await session.commit()

async def _create_ldap_attributes(connection: AsyncConnection) -> None: # noqa: ARG001
async with container(scope=Scope.REQUEST) as cnt:
session = await cnt.get(AsyncSession)
Expand Down Expand Up @@ -117,13 +144,35 @@ async def _rebind_ace_attribute_types_to_directories(
return

await ace_dao.upgrade()
await session.commit()

op.drop_constraint(
op.f("AccessControlEntries_attributeTypeId_fkey"),
"AccessControlEntries",
type_="foreignkey",
)

op.run_async(_update_entity_types)
op.run_async(_create_ldap_configuration_directory)
op.run_async(_create_ldap_attributes)
op.run_async(_create_ldap_object_classes)
op.run_async(_rebind_ace_attribute_types_to_directories)

op.create_foreign_key(
op.f("AccessControlEntries_directoryAttributeTypeId_fkey"),
"AccessControlEntries",
"Directory",
["attributeTypeId"],
["id"],
ondelete="CASCADE",
)


@temporary_stub_column(
"AccessControlEntries",
"attribute_type_name",
sa.String(),
)
def downgrade(container: AsyncContainer) -> None:
"""Downgrade."""

Expand All @@ -138,6 +187,7 @@ async def _rebind_ace_attribute_types_to_legacy(
return

await ace_dao.downgrade()
await session.commit()

async def _delete_ldap_attributes(connection: AsyncConnection) -> None: # noqa: ARG001
async with container(scope=Scope.REQUEST) as cnt:
Expand All @@ -161,6 +211,19 @@ async def _delete_ldap_object_classes(connection: AsyncConnection) -> None: # n
await obj_cls_use_case_legacy.delete_all_dirs()
await session.commit()

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

if not await get_base_directories(session):
return

await directory_create_use_case.delete_configuration_dir()
await session.commit()

async def _delete_entity_types(connection: AsyncConnection) -> None: # noqa: ARG001
async with container(scope=Scope.REQUEST) as cnt:
session = await cnt.get(AsyncSession)
Expand All @@ -185,6 +248,7 @@ async def _delete_entity_types(connection: AsyncConnection) -> None: # noqa: AR
op.run_async(_rebind_ace_attribute_types_to_legacy)
op.run_async(_delete_ldap_attributes)
op.run_async(_delete_ldap_object_classes)
op.run_async(_delete_ldap_configuration_directory)
op.run_async(_delete_entity_types)

op.create_foreign_key(
Expand Down
2 changes: 2 additions & 0 deletions app/alembic/versions/8164b4a9e1f1_add_ou_computers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncSession

from entities import Directory
from enums import EntityTypeNames
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
Expand All @@ -28,6 +29,7 @@
COMPUTERS = "computers"
_OU_COMPUTERS_DATA = {
"name": COMPUTERS,
"entity_type_name": EntityTypeNames.ORGANIZATIONAL_UNIT,
"object_class": "organizationalUnit",
"attributes": {"objectClass": ["top", "container"]},
"children": [],
Expand Down
2 changes: 1 addition & 1 deletion app/alembic/versions/ba78cef9700a_initial_entity_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ async def _create_entity_types(connection: AsyncConnection) -> None: # noqa: AR
return

for entity_type_dto in ENTITY_TYPE_DTOS_V1:
await entity_type_use_case.create(entity_type_dto)
await entity_type_use_case.create_not_safe(entity_type_dto)

await session.commit()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async def _create_entity_type(
if not await get_base_directories(session):
return

await entity_type_use_case.create(
await entity_type_use_case.create_not_safe(
EntityTypeDTO(
name=EntityTypeNames.CONTACT,
object_class_names=[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from config import Settings
from constants import DOMAIN_CONTROLLERS_OU_NAME
from entities import Directory
from enums import SamAccountTypeCodes
from enums import EntityTypeNames, SamAccountTypeCodes
from ldap_protocol.auth.setup_gateway import SetupGateway
from ldap_protocol.objects import UserAccountControlFlag
from ldap_protocol.roles.role_use_case import RoleUseCase
Expand All @@ -32,6 +32,7 @@

_OU_DOMAIN_CONTROLLERS_DATA: dict[str, Any] = {
"name": DOMAIN_CONTROLLERS_OU_NAME,
"entity_type_name": EntityTypeNames.ORGANIZATIONAL_UNIT,
"object_class": "organizationalUnit",
"attributes": {"objectClass": ["top", "container"]},
}
Expand Down Expand Up @@ -66,6 +67,7 @@ async def _create_domain_controllers_ou(
domain_controller_data = [
{
"name": settings.HOST_MACHINE_SHORT_NAME,
"entity_type_name": EntityTypeNames.COMPUTER,
"object_class": "computer",
"attributes": {
"objectClass": ["top"],
Expand Down
5 changes: 5 additions & 0 deletions app/api/ldap_schema/adapters/attribute_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def _convert_update_uschema_to_dto(
return AttributeTypeDTO[None](
oid="",
name="",
ldap_display_name="",
syntax=request.syntax,
single_value=request.single_value,
no_user_modification=request.no_user_modification,
Expand All @@ -54,6 +55,10 @@ def _convert_update_uschema_to_dto(
AttributeTypeDTO[None],
recipe=[
allow_unlinked_optional(P[AttributeTypeDTO].id),
link_function(
lambda _: _.ldap_display_name or "",
P[AttributeTypeDTO].ldap_display_name,
),
link_function(
lambda _: DEFAULT_ATTRIBUTE_TYPE_SYNTAX,
P[AttributeTypeDTO].syntax,
Expand Down
Loading
Loading