-
Notifications
You must be signed in to change notification settings - Fork 0
Сached get_base_directories #889
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
c70a8a2
6e41433
97a9824
ded6d9d
097e5f9
1ded88e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| """Module for dtos.""" | ||
|
|
||
| import uuid | ||
| from dataclasses import dataclass | ||
| from datetime import datetime | ||
| from typing import ClassVar | ||
|
|
||
| from adaptix.conversion import get_converter | ||
|
|
||
| from entities import Directory, DistinguishedNamePrefix | ||
|
|
||
|
|
||
| @dataclass | ||
| class DirectoryDTO: | ||
| id: int | ||
| name: str | ||
| is_system: bool | ||
| object_sid: str | ||
| object_guid: uuid.UUID | ||
| parent_id: int | None | ||
| entity_type_id: int | None | ||
| object_class: str | ||
| rdname: str | ||
| created_at: datetime | None | ||
| updated_at: datetime | None | ||
| depth: int | ||
| password_policy_id: int | None | ||
| path: list[str] | ||
|
|
||
| search_fields: ClassVar[dict[str, str]] = { | ||
| "name": "name", | ||
| "objectguid": "objectGUID", | ||
| "objectsid": "objectSid", | ||
| } | ||
| ro_fields: ClassVar[set[str]] = { | ||
| "uid", | ||
| "whencreated", | ||
| "lastlogon", | ||
| "authtimestamp", | ||
| "objectguid", | ||
| "objectsid", | ||
| "entitytypename", | ||
| } | ||
|
|
||
| def get_dn_prefix(self) -> DistinguishedNamePrefix: | ||
| return { | ||
| "organizationalUnit": "ou", | ||
| "domain": "dc", | ||
| "container": "cn", | ||
| }.get( | ||
| self.object_class, | ||
| "cn", | ||
| ) # type: ignore | ||
|
|
||
| def get_dn(self, dn: str = "cn") -> str: | ||
| return f"{dn}={self.name}" | ||
|
|
||
| @property | ||
| def is_domain(self) -> bool: | ||
| return not self.parent_id and self.object_class == "domain" | ||
|
|
||
| @property | ||
| def host_principal(self) -> str: | ||
| return f"host/{self.name}" | ||
|
|
||
| @property | ||
| def path_dn(self) -> str: | ||
| return ",".join(reversed(self.path)) | ||
|
|
||
| @property | ||
| def relative_id(self) -> str: | ||
| """Get RID from objectSid. | ||
|
|
||
| Relative Identifier (RID) is the last sub-authority value of a SID. | ||
| """ | ||
| if "-" in self.object_sid: | ||
| return self.object_sid.split("-")[-1] | ||
| return "" | ||
|
|
||
|
|
||
| _directory_sqla_obj_to_dto = get_converter(Directory, DirectoryDTO) |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,11 +11,13 @@ | |||||||||||||
| from sqlalchemy import exists, select | ||||||||||||||
| from sqlalchemy.ext.asyncio import AsyncSession | ||||||||||||||
|
|
||||||||||||||
| from dtos import DirectoryDTO | ||||||||||||||
TheMihMih marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
| from entities import Attribute, Directory, Group, NetworkPolicy, User | ||||||||||||||
| from ldap_protocol.ldap_schema.attribute_value_validator import ( | ||||||||||||||
| AttributeValueValidator, | ||||||||||||||
| ) | ||||||||||||||
| from ldap_protocol.ldap_schema.entity_type_dao import EntityTypeDAO | ||||||||||||||
| from ldap_protocol.utils.async_cache import base_directories_cache | ||||||||||||||
| from ldap_protocol.utils.helpers import create_object_sid, generate_domain_sid | ||||||||||||||
| from ldap_protocol.utils.queries import get_domain_object_class | ||||||||||||||
| from password_utils import PasswordUtils | ||||||||||||||
|
|
@@ -113,6 +115,7 @@ async def setup_enviroment( | |||||||||||||
| domain=domain, | ||||||||||||||
| parent=domain, | ||||||||||||||
| ) | ||||||||||||||
| base_directories_cache.clear() | ||||||||||||||
|
|
||||||||||||||
| except Exception: | ||||||||||||||
| import traceback | ||||||||||||||
|
|
@@ -124,21 +127,22 @@ async def create_dir( | |||||||||||||
| self, | ||||||||||||||
| data: dict, | ||||||||||||||
| is_system: bool, | ||||||||||||||
| domain: Directory, | ||||||||||||||
| parent: Directory | None = None, | ||||||||||||||
| domain: Directory | DirectoryDTO, | ||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Зачем здесь оставлять Directory ?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тут может приходить Directory (app/ldap_protocol/auth/setup_gateway.py::72) |
||||||||||||||
| parent: Directory | DirectoryDTO | None = None, | ||||||||||||||
| ) -> None: | ||||||||||||||
| """Create data recursively.""" | ||||||||||||||
| dir_ = Directory( | ||||||||||||||
| is_system=is_system, | ||||||||||||||
| object_class=data["object_class"], | ||||||||||||||
| name=data["name"], | ||||||||||||||
| parent=parent, | ||||||||||||||
| ) | ||||||||||||||
| dir_.groups = [] | ||||||||||||||
| dir_.create_path(parent, dir_.get_dn_prefix()) | ||||||||||||||
| path = parent.path if parent else [] | ||||||||||||||
| dir_.create_path(path, dir_.get_dn_prefix()) | ||||||||||||||
|
|
||||||||||||||
| self._session.add(dir_) | ||||||||||||||
| await self._session.flush() | ||||||||||||||
| dir_.parent_id = parent.id if parent else None | ||||||||||||||
|
Comment on lines
140
to
+145
|
||||||||||||||
| self._session.add(dir_) | |
| await self._session.flush() | |
| dir_.parent_id = parent.id if parent else None | |
| dir_.parent_id = parent.id if parent else None | |
| self._session.add(dir_) | |
| await self._session.flush() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Почему то в таком случае parent_id не сохраняется
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| """Async cache implementation.""" | ||
| import time | ||
| from functools import wraps | ||
| from typing import Callable, Generic, TypeVar | ||
|
|
||
| from dtos import DirectoryDTO | ||
|
|
||
| T = TypeVar("T") | ||
| DEFAULT_CACHE_TIME = 5 * 60 # 5 minutes | ||
|
|
||
|
|
||
| class AsyncTTLCache(Generic[T]): | ||
| def __init__(self, ttl: int | None = DEFAULT_CACHE_TIME) -> None: | ||
| self._ttl = ttl | ||
| self._value: T | None = None | ||
| self._expires_at: float | None = None | ||
|
|
||
| def clear(self) -> None: | ||
| self._value = None | ||
| self._expires_at = None | ||
|
|
||
| def __call__(self, func: Callable) -> Callable: | ||
| @wraps(func) | ||
| async def wrapper(*args: tuple, **kwargs: dict) -> T: | ||
| if self._value is not None: | ||
| if not self._expires_at or self._expires_at > time.monotonic(): | ||
| return self._value | ||
| self.clear() | ||
|
|
||
| result = await func(*args, **kwargs) | ||
|
|
||
| self._value = result | ||
| self._expires_at = ( | ||
| time.monotonic() + self._ttl if self._ttl else None | ||
| ) | ||
|
|
||
| return result | ||
|
|
||
| return wrapper | ||
|
|
||
|
|
||
| base_directories_cache = AsyncTTLCache[list[DirectoryDTO]]() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -130,6 +130,7 @@ | |
| License: https://github.com/MultiDirectoryLab/MultiDirectory/blob/main/LICENSE | ||
| """ | ||
|
|
||
| import asyncio | ||
| import functools | ||
| import hashlib | ||
| import random | ||
|
|
@@ -138,9 +139,10 @@ | |
| import time | ||
| from calendar import timegm | ||
| from datetime import datetime | ||
| from functools import wraps | ||
| from hashlib import blake2b | ||
| from operator import attrgetter | ||
| from typing import Callable | ||
| from typing import Any, Callable, Generic, TypeVar | ||
| from zoneinfo import ZoneInfo | ||
|
|
||
| from loguru import logger | ||
|
|
@@ -149,6 +151,7 @@ | |
| from sqlalchemy.sql.compiler import DDLCompiler | ||
| from sqlalchemy.sql.expression import ClauseElement, Executable, Visitable | ||
|
|
||
| from dtos import DirectoryDTO | ||
| from entities import Directory | ||
TheMihMih marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
|
|
@@ -192,12 +195,18 @@ def validate_attribute(attribute: str) -> bool: | |
| ) | ||
|
|
||
|
|
||
| def is_dn_in_base_directory(base_directory: Directory, entry: str) -> bool: | ||
| def is_dn_in_base_directory( | ||
| base_directory: DirectoryDTO, | ||
| entry: str, | ||
| ) -> bool: | ||
| """Check if an entry in a base dn.""" | ||
| return entry.lower().endswith(base_directory.path_dn.lower()) | ||
|
|
||
|
|
||
| def dn_is_base_directory(base_directory: Directory, entry: str) -> bool: | ||
| def dn_is_base_directory( | ||
| base_directory: DirectoryDTO, | ||
| entry: str, | ||
| ) -> bool: | ||
| """Check if an entry is a base dn.""" | ||
| return base_directory.path_dn.lower() == entry.lower() | ||
|
|
||
|
|
@@ -302,7 +311,7 @@ def string_to_sid(sid_string: str) -> bytes: | |
|
|
||
|
|
||
| def create_object_sid( | ||
| domain: Directory, | ||
| domain: Directory | DirectoryDTO, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Здесь вроде тоже можно оставить только dto
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Вызывается внутри create_dir, там могут быть оба варианта |
||
| rid: int, | ||
| reserved: bool = False, | ||
| ) -> str: | ||
|
|
@@ -402,3 +411,28 @@ async def explain_query( | |
| for row in await session.execute(explain(query, analyze=True)) | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| # def async_cache(ttl: int | None = DEFAULT_CACHE_TIME) -> Callable: | ||
| # """Cache for get_base_directories""" | ||
| # cache: list[tuple[list[DirectoryDTO], float | None]] = [] | ||
|
|
||
| # def decorator(func: Callable) -> Callable: | ||
| # @wraps(func) | ||
| # async def wrapper(*args: tuple, **kwargs: dict) -> list[DirectoryDTO]: | ||
| # if cache: | ||
| # value, expires_at = cache[0] | ||
| # if not expires_at or expires_at > time.monotonic(): | ||
| # return value | ||
| # else: | ||
| # cache.clear() | ||
|
|
||
| # result = await func(*args, **kwargs) | ||
| # expires_at = time.monotonic() + ttl if ttl else None | ||
| # cache.append((result, expires_at)) | ||
|
|
||
| # return result | ||
|
|
||
| # return wrapper | ||
|
|
||
| # return decorator | ||
Uh oh!
There was an error while loading. Please reload this page.