Skip to content
Open
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
142 changes: 142 additions & 0 deletions app/alembic/versions/379fce54fb08_rename_base_cn_to_cc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"""Rename base containers.

users -> Users, groups -> Groups, computers -> Computers.

Revision ID: 379fce54fb08
Revises: ec45e3e8aa0f
Create Date: 2026-01-23 12:26:10.758698

"""

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

from entities import Attribute, Directory
from repo.pg.tables import queryable_attr as qa

# revision identifiers, used by Alembic.
revision: None | str = "379fce54fb08"
down_revision: None | str = "ec45e3e8aa0f"
branch_labels: None | list[str] = None
depends_on: None | list[str] = None


CONTAINER_RENAMES = {
"users": "Users",
"groups": "Groups",
"computers": "Computers",
}


async def _update_descendants(
session: AsyncSession,
parent_id: int,
cn_from: str,
cn_to: str,
) -> None:
"""Recursively update paths of all descendants."""
child_dirs = await session.scalars(
select(Directory).where(qa(Directory.parent_id) == parent_id),
)

for child_dir in child_dirs:
child_dir.path = [cn_to if p == cn_from else p for p in child_dir.path]
await session.flush()
await _update_descendants(
session,
child_dir.id,
cn_from=cn_from,
cn_to=cn_to,
)


async def _update_attributes(
session: AsyncSession,
old_value: str,
new_value: str,
) -> None:
"""Update attribute values containing old DN references."""
result = await session.execute(
select(Attribute).where(
qa(Attribute.value).ilike(f"%{old_value}%"),
),
)
attributes = result.scalars().all()

for attr in attributes:
if attr.value and old_value in attr.value:
attr.value = attr.value.replace(old_value, new_value)

await session.flush()


async def _rename_container(
session: AsyncSession,
old_name: str,
new_name: str,
) -> None:
"""Rename a single container and update all references."""
container_dir = await session.scalar(
select(Directory).where(
qa(Directory.name) == old_name,
qa(Directory.is_system).is_(True),
),
)

if not container_dir:
return

cn_from = f"cn={old_name}"
cn_to = f"cn={new_name}"

container_dir.name = new_name
container_dir.path = [
cn_to if p == cn_from else p for p in container_dir.path
]

await session.flush()

await _update_descendants(
session,
container_dir.id,
cn_from=cn_from,
cn_to=cn_to,
)

await _update_attributes(session, cn_from, cn_to)


def upgrade(container: AsyncContainer) -> None:
"""Upgrade: Rename containers to capitalized versions."""

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

for old_name, new_name in CONTAINER_RENAMES.items():
await _rename_container(session, old_name, new_name)

await session.commit()

op.run_async(_rename_containers)


def downgrade(container: AsyncContainer) -> None:
"""Downgrade: Rename containers back to lowercase."""

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

for old_name, new_name in CONTAINER_RENAMES.items():
await _rename_container(session, new_name, old_name)

await session.commit()

op.run_async(_rename_containers_back)
9 changes: 3 additions & 6 deletions app/alembic/versions/71e642808369_add_directory_is_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,10 @@
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
Expand Down Expand Up @@ -72,13 +69,13 @@ async def _indicate_system_directories(
qa(Directory.is_system).is_(False),
qa(Directory.name).in_(
(
GROUPS_CONTAINER_NAME,
"groups",
DOMAIN_ADMIN_GROUP_NAME,
DOMAIN_USERS_GROUP_NAME,
READ_ONLY_GROUP_NAME,
DOMAIN_COMPUTERS_GROUP_NAME,
COMPUTERS_CONTAINER_NAME,
USERS_CONTAINER_NAME,
"computers",
"users",
"services",
"krbadmin",
"kerberos",
Expand Down
2 changes: 1 addition & 1 deletion app/alembic/versions/8164b4a9e1f1_add_ou_computers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from sqlalchemy import delete, exists, select
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncSession

from constants import COMPUTERS_CONTAINER_NAME
from entities import Directory
from extra.alembic_utils import temporary_stub_column
from ldap_protocol.roles.role_use_case import RoleUseCase
Expand All @@ -26,6 +25,7 @@
depends_on: None = None


COMPUTERS_CONTAINER_NAME = "computers"
_OU_COMPUTERS_DATA = {
"name": COMPUTERS_CONTAINER_NAME,
"object_class": "organizationalUnit",
Expand Down
6 changes: 3 additions & 3 deletions app/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

from enums import EntityTypeNames

GROUPS_CONTAINER_NAME = "groups"
COMPUTERS_CONTAINER_NAME = "computers"
USERS_CONTAINER_NAME = "users"
GROUPS_CONTAINER_NAME = "Groups"
COMPUTERS_CONTAINER_NAME = "Computers"
USERS_CONTAINER_NAME = "Users"

READ_ONLY_GROUP_NAME = "read-only"

Expand Down
6 changes: 3 additions & 3 deletions app/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ class RoleConstants(StrEnum):
READ_ONLY_ROLE_NAME = "Read Only Role"
KERBEROS_ROLE_NAME = "Kerberos Role"

DOMAIN_ADMINS_GROUP_CN = "cn=domain admins,cn=groups,"
READONLY_GROUP_CN = "cn=read-only,cn=groups,"
KERBEROS_GROUP_CN = "cn=krbadmin,cn=groups,"
DOMAIN_ADMINS_GROUP_CN = "cn=domain admins,cn=Groups,"
READONLY_GROUP_CN = "cn=read-only,cn=Groups,"
KERBEROS_GROUP_CN = "cn=krbadmin,cn=Groups,"


@verify(UNIQUE)
Expand Down
4 changes: 2 additions & 2 deletions app/ldap_protocol/kerberos/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,9 @@ def _build_kerberos_admin_dns(self, base_dn: str) -> KerberosAdminDnGroup:
:return KerberosAdminDnGroup:
dataclass with DN for krbadmin, services_container, krbadmin_group.
"""
krbadmin = f"cn=krbadmin,cn=users,{base_dn}"
krbadmin = f"cn=krbadmin,cn=Users,{base_dn}"
services_container = get_system_container_dn(base_dn)
krbgroup = f"cn=krbadmin,cn=groups,{base_dn}"
krbgroup = f"cn=krbadmin,cn=Groups,{base_dn}"
return KerberosAdminDnGroup(
krbadmin_dn=krbadmin,
services_container_dn=services_container,
Expand Down
4 changes: 2 additions & 2 deletions app/ldap_protocol/ldap_requests/modify_dn.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ class ModifyDNRequest(BaseRequest):
entry='cn=main,dc=multifactor,dc=dev'
newrdn='cn=main2'
deleteoldrdn=true
new_superior='cn=users,dc=multifactor,dc=dev'
new_superior='cn=Users,dc=multifactor,dc=dev'

>>> cn = main2, cn = users, dc = multifactor, dc = dev
>>> cn = main2, cn = Users, dc = multifactor, dc = dev
"""

PROTOCOL_OP: ClassVar[int] = ProtocolRequests.MODIFY_DN
Expand Down
4 changes: 2 additions & 2 deletions app/ldap_protocol/utils/cte.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def find_members_recursive_cte(
FROM "Directory"
JOIN "Groups" ON "Directory".id = "Groups"."directoryId"
WHERE "Directory"."path" =
'{dc=test,dc=md,cn=groups,"cn=domain admins"}'
'{dc=test,dc=md,cn=Groups,"cn=domain admins"}'

UNION ALL

Expand Down Expand Up @@ -129,7 +129,7 @@ def find_root_group_recursive_cte(dn_list: list) -> CTE:
FROM "Directory"
LEFT OUTER JOIN "Groups" ON "Directory".id = "Groups"."directoryId"
WHERE "Directory"."path" =
'{dc=test,dc=md,cn=groups,"cn=domain admins"}'
'{dc=test,dc=md,cn=Groups,"cn=domain admins"}'

UNION ALL

Expand Down
6 changes: 3 additions & 3 deletions app/ldap_protocol/utils/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ async def get_dn_by_id(id_: int, session: AsyncSession) -> str:
"""Get dn by id.

>>> await get_dn_by_id(0, session)
>>> "cn=groups,dc=example,dc=com"
>>> "cn=Groups,dc=example,dc=com"
"""
query = select(Directory).filter_by(id=id_)
retval = (await session.scalars(query)).one()
Expand All @@ -353,7 +353,7 @@ async def create_group(
) -> tuple[Directory, Group]:
"""Create group in default groups path.

cn=name,cn=groups,dc=domain,dc=com
cn=name,cn=Groups,dc=domain,dc=com

:param str name: group name
:param int sid: objectSid
Expand All @@ -362,7 +362,7 @@ async def create_group(
base_dn_list = await get_base_directories(session)

query = select(Directory).filter(
get_filter_from_path("cn=groups," + base_dn_list[0].path_dn),
get_filter_from_path("cn=Groups," + base_dn_list[0].path_dn),
)

parent = (await session.scalars(query)).one()
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ async def setup_session(
name="TEST ONLY LOGIN ROLE",
creator_upn=None,
is_system=True,
groups=["cn=admin login only,cn=groups,dc=md,dc=test"],
groups=["cn=admin login only,cn=Groups,dc=md,dc=test"],
permissions=AuthorizationRules.AUTH_LOGIN,
),
)
Expand Down
Loading