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
9 changes: 9 additions & 0 deletions app/ldap_protocol/filter_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
User,
)
from ldap_protocol.utils.helpers import ft_to_dt
from ldap_protocol.utils.queries import get_path_filter, get_search_path
from repo.pg.tables import groups_table, queryable_attr as qa, users_table

from .asn1parser import ASN1Row, TagNumbers
Expand Down Expand Up @@ -398,6 +399,14 @@ def _cast_item(self, item: ASN1Row) -> UnaryExpression | ColumnElement: # noqa:

is_substring = item.tag_id == TagNumbers.SUBSTRING

if attr == "distinguishedname" and not is_substring:
try:
dn_search_path = get_search_path(right.value)
except Exception: # noqa: S110
pass
Comment on lines +405 to +406
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The broad exception handling with except Exception: and pass silently swallows all exceptions without any logging. If get_search_path() fails due to malformed DN syntax or other issues, this will silently fall through to the original filter logic. Consider logging the exception or narrowing the exception type to catch only expected parsing errors.

Copilot uses AI. Check for mistakes.
else:
return get_path_filter(dn_search_path)

if attr == "anr":
if is_substring:
expr = right.value[0]
Expand Down
24 changes: 12 additions & 12 deletions app/ldap_protocol/kerberos/ldap_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,20 @@ async def create_kerberos_structure(
:return None.
"""
async with self._session.begin_nested():
results = (
await anext(services.handle(ctx)),
await anext(group.handle(ctx)),
await anext(krb_user.handle(ctx)),
)
await self._session.flush()
service_result = await anext(services.handle(ctx))
if service_result.result_code != 0:
raise KerberosConflictError("Service error")

if not all(result.result_code == 0 for result in results):
await self._session.rollback()
raise KerberosConflictError(
"Error creating Kerberos structure in directory",
)
async with self._session.begin_nested():
group_result = await anext(group.handle(ctx))
if group_result.result_code != 0:
raise KerberosConflictError("Group error")

async with self._session.begin_nested():
await self._role_use_case.create_kerberos_system_role()
await self._session.commit()
user_result = await anext(krb_user.handle(ctx))
if user_result.result_code != 0:
raise KerberosConflictError("User error")
Comment on lines +62 to +73
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error messages "Service error", "Group error", and "User error" are not descriptive enough. They don't provide information about what went wrong or which specific operation failed. Consider including the result_code from the failed operation in the error message to aid debugging.

Copilot uses AI. Check for mistakes.
Comment on lines 59 to +73
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactoring changes transaction semantics. Previously, all three operations (services, group, krb_user) were in a single nested transaction, and all would be rolled back together if any failed. Now they're in separate nested transactions, meaning if the user creation fails, the service and group will already be committed. Additionally, the role creation is now between group and user creation, which changes the order. Ensure this new transaction boundary behavior is intentional and doesn't leave the system in an inconsistent state if user creation fails.

Copilot uses AI. Check for mistakes.

async def rollback_kerberos_structure(
self,
Expand Down
2 changes: 1 addition & 1 deletion app/ldap_protocol/ldap_requests/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ async def handle( # noqa: C901
parent_directory=parent,
directory=new_dir,
)
await ctx.session.flush()
await ctx.session.commit()
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing from flush() to commit() here fundamentally alters transaction behavior. This now commits the transaction immediately instead of just flushing changes to the database. This means if there's an error in subsequent operations (like the KRB principal additions), the directory entry will have already been committed and won't be rolled back. Consider whether this is the intended behavior or if the original flush was correct.

Copilot uses AI. Check for mistakes.
except IntegrityError:
await ctx.session.rollback()
yield AddResponse(result_code=LDAPCodes.ENTRY_ALREADY_EXISTS)
Expand Down
22 changes: 15 additions & 7 deletions app/ldap_protocol/ldap_requests/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ def is_sid_requested(self) -> bool:
def is_guid_requested(self) -> bool:
return self.all_attrs or "objectguid" in self.requested_attrs

@property
def is_objectclass_requested(self) -> bool:
return self.all_attrs or "objectclass" in self.requested_attrs

@cached_property
def all_attrs(self) -> bool:
return "*" in self.requested_attrs or not self.requested_attrs
Expand Down Expand Up @@ -417,11 +421,16 @@ def _mutate_query_with_attributes_to_load(
if attr not in _ATTRS_TO_CLEAN
}

cond = or_(
func.lower(Attribute.name).in_(attrs),
func.lower(Attribute.name) == "objectclass",
)

return query.options(
selectinload(qa(Directory.attributes)),
with_loader_criteria(
Attribute,
func.lower(Attribute.name).in_(attrs),
cond,
),
)

Expand Down Expand Up @@ -534,7 +543,7 @@ async def _fill_attrs(
attrs: dict[str, list[str]],
session: AsyncSession,
) -> None:
if "distinguishedname" not in self.requested_attrs or self.all_attrs:
if "distinguishedname" in self.requested_attrs or self.all_attrs:
attrs["distinguishedName"].append(distinguished_name)

if "whenCreated" in self.requested_attrs or self.all_attrs:
Expand Down Expand Up @@ -572,10 +581,6 @@ async def _fill_attrs(
attrs["memberOf"].append(group.directory.path_dn)

if self.token_groups and "user" in obj_classes:
attrs["tokenGroups"].append(
str(string_to_sid(directory.object_sid)),
)

group_directories = await get_all_parent_group_directories(
directory.groups,
session,
Expand All @@ -584,7 +589,7 @@ async def _fill_attrs(
if group_directories is not None:
async for directory_ in group_directories:
attrs["tokenGroups"].append(
str(string_to_sid(directory_.object_sid)),
string_to_sid(directory_.object_sid), # type: ignore
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the type: ignore comment suppresses the type error, the root cause is that the type annotation for the attrs parameter at line 543 is incorrect. It's declared as dict[str, list[str]] but the function actually appends bytes values in multiple places (tokenGroups, objectGuid, objectSid). Consider updating the type annotation to dict[str, list[str | bytes]] to accurately reflect the actual usage and remove the need for type: ignore comments.

Copilot uses AI. Check for mistakes.
)

if self.member and "group" in obj_classes and directory.group:
Expand Down Expand Up @@ -638,6 +643,9 @@ async def tree_view( # noqa: C901

if attr.name.lower() == "objectclass":
obj_classes.append(value)
if self.is_objectclass_requested:
attrs[attr.name].append(value)
continue

attrs[attr.name].append(value)

Expand Down
Loading