Skip to content

Identity Module

Experimental

The Identity service lives under unique_toolkit.experimental.identity. Its public API, method names, argument shapes, and return types may change without notice and are not covered by the toolkit's normal stability guarantees. Pin your toolkit version if you depend on a specific shape.

Import it via:

from unique_toolkit.experimental.identity import Identity
# or, with sub-services:
from unique_toolkit.experimental.identity import Identity, Users, Groups

Identity is a thin facade — it wires two CRUD-style sub-services, Users and Groups, behind identity.users and identity.groups respectively. Constructors are keyword-only, so callers always write Identity(user_id=..., company_id=...).

Service

unique_toolkit.experimental.resources.facades.identity.service

The :class:Identity facade — users + groups in one place.

.. warning::

**Experimental.** Lives under :mod:`unique_toolkit.experimental`. The
public API, method names, and return shapes may change without notice
and are not covered by the toolkit's normal stability guarantees.

:class:Identity is a client: it owns no CRUD logic of its own and simply composes two resource services:

  • :attr:Identity.users — an instance of :class:~unique_toolkit.experimental.resources.user.Users.
  • :attr:Identity.groups — an instance of :class:~unique_toolkit.experimental.resources.group.Groups.

Both sub-services share the same (user_id, company_id) pair, so instantiating :class:Identity is equivalent to building both sub-services manually.

Acting user — every API call is made on behalf of user_id. That user needs the usual directory permissions; most reads are open to any authenticated user, group mutations require admin-equivalent rights.

Identity

Unified users + groups facade for a (user_id, company_id) context.

.. warning::

**Experimental.** Import path is
:mod:`unique_toolkit.experimental.resources.facades.identity`. The API may
change without notice.

:class:Identity owns two sub-services:

  • :attr:users — an instance of :class:~unique_toolkit.experimental.resources.user.Users.
  • :attr:groups — an instance of :class:~unique_toolkit.experimental.resources.group.Groups.

Identity itself is stateless beyond the credentials it holds; the actual CRUD surface lives on the sub-services so callers write identity.users.list() or identity.groups.add_members(...).

Source code in unique_toolkit/unique_toolkit/experimental/resources/facades/identity/service.py
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
class Identity:
    """Unified users + groups facade for a ``(user_id, company_id)`` context.

    .. warning::

        **Experimental.** Import path is
        :mod:`unique_toolkit.experimental.resources.facades.identity`. The API may
        change without notice.

    :class:`Identity` owns two sub-services:

    - :attr:`users` — an instance of
      :class:`~unique_toolkit.experimental.resources.user.Users`.
    - :attr:`groups` — an instance of
      :class:`~unique_toolkit.experimental.resources.group.Groups`.

    ``Identity`` itself is stateless beyond the credentials it holds; the
    actual CRUD surface lives on the sub-services so callers write
    ``identity.users.list()`` or ``identity.groups.add_members(...)``.
    """

    def __init__(self, *, user_id: str, company_id: str) -> None:
        [user_id, company_id] = validate_required_values([user_id, company_id])
        self._user_id = user_id
        self._company_id = company_id
        self._users = Users(user_id=user_id, company_id=company_id)
        self._groups = Groups(user_id=user_id, company_id=company_id)

    @classmethod
    def from_context(cls, context: UniqueContext) -> Self:
        """Create from a :class:`UniqueContext` (preferred constructor)."""
        return cls(
            user_id=context.auth.get_confidential_user_id(),
            company_id=context.auth.get_confidential_company_id(),
        )

    @classmethod
    def from_settings(
        cls,
        settings: UniqueSettings | str | None = None,
    ) -> Self:
        """Create from :class:`UniqueSettings` — standalone convenience only.

        Mirrors :meth:`KnowledgeBaseService.from_settings` so callers can write
        ``Identity.from_settings()`` in standalone scripts:

        - ``settings=None`` auto-loads from ``unique.env`` via
          :meth:`UniqueSettings.from_env_auto_with_sdk_init`.
        - ``settings="my.env"`` loads from the given env file name.
        - ``settings=<UniqueSettings>`` uses the provided instance as-is.

        .. note::

            :class:`Identity` is **not** registered with
            :class:`UniqueServiceFactory`; experimental services are
            constructed directly so the experimental dependency stays visible
            at every call site.
        """
        if settings is None:
            settings = UniqueSettings.from_env_auto_with_sdk_init()
        elif isinstance(settings, str):
            settings = UniqueSettings.from_env_auto_with_sdk_init(filename=settings)

        return cls(
            user_id=settings.authcontext.get_confidential_user_id(),
            company_id=settings.authcontext.get_confidential_company_id(),
        )

    @property
    def users(self) -> Users:
        return self._users

    @property
    def groups(self) -> Groups:
        return self._groups

from_context(context) classmethod

Create from a :class:UniqueContext (preferred constructor).

Source code in unique_toolkit/unique_toolkit/experimental/resources/facades/identity/service.py
67
68
69
70
71
72
73
@classmethod
def from_context(cls, context: UniqueContext) -> Self:
    """Create from a :class:`UniqueContext` (preferred constructor)."""
    return cls(
        user_id=context.auth.get_confidential_user_id(),
        company_id=context.auth.get_confidential_company_id(),
    )

from_settings(settings=None) classmethod

Create from :class:UniqueSettings — standalone convenience only.

Mirrors :meth:KnowledgeBaseService.from_settings so callers can write Identity.from_settings() in standalone scripts:

  • settings=None auto-loads from unique.env via :meth:UniqueSettings.from_env_auto_with_sdk_init.
  • settings="my.env" loads from the given env file name.
  • settings=<UniqueSettings> uses the provided instance as-is.

.. note::

:class:`Identity` is **not** registered with
:class:`UniqueServiceFactory`; experimental services are
constructed directly so the experimental dependency stays visible
at every call site.
Source code in unique_toolkit/unique_toolkit/experimental/resources/facades/identity/service.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
@classmethod
def from_settings(
    cls,
    settings: UniqueSettings | str | None = None,
) -> Self:
    """Create from :class:`UniqueSettings` — standalone convenience only.

    Mirrors :meth:`KnowledgeBaseService.from_settings` so callers can write
    ``Identity.from_settings()`` in standalone scripts:

    - ``settings=None`` auto-loads from ``unique.env`` via
      :meth:`UniqueSettings.from_env_auto_with_sdk_init`.
    - ``settings="my.env"`` loads from the given env file name.
    - ``settings=<UniqueSettings>`` uses the provided instance as-is.

    .. note::

        :class:`Identity` is **not** registered with
        :class:`UniqueServiceFactory`; experimental services are
        constructed directly so the experimental dependency stays visible
        at every call site.
    """
    if settings is None:
        settings = UniqueSettings.from_env_auto_with_sdk_init()
    elif isinstance(settings, str):
        settings = UniqueSettings.from_env_auto_with_sdk_init(filename=settings)

    return cls(
        user_id=settings.authcontext.get_confidential_user_id(),
        company_id=settings.authcontext.get_confidential_company_id(),
    )

Schemas

unique_toolkit.experimental.resources.user.schemas

Pydantic schemas for the :mod:unique_toolkit.experimental.resources.user resource.

Mirrors :mod:unique_sdk.User responses with field-level documentation and camelCase aliases so SDK wire payloads validate directly via :func:BaseModel.model_validate. :class:UserGroupMembership is the response shape of GET /users/{id}/groups and lives here — not in :mod:..group.schemas — because the endpoint is rooted on the user.

UserInfo

Bases: BaseModel

Directory record for a single user (GET /users/{id}).

The id field is the canonical user id used everywhere in the platform (equivalent to uid on a Linux box). The external_id is the upstream identity-provider id (SSO / SCIM), present on provisioned users, absent on users created via the admin UI.

Source code in unique_toolkit/unique_toolkit/experimental/resources/user/schemas.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class UserInfo(BaseModel):
    """Directory record for a single user (``GET /users/{id}``).

    The ``id`` field is the canonical user id used everywhere in the platform
    (equivalent to ``uid`` on a Linux box). The ``external_id`` is the upstream
    identity-provider id (SSO / SCIM), present on provisioned users, absent on
    users created via the admin UI.
    """

    model_config = _model_config

    id: str = Field(
        description=(
            "Internal user id. Use this when calling any toolkit/SDK method that "
            "takes ``user_id``. Stable for the lifetime of the account."
        ),
    )
    external_id: str | None = Field(
        default=None,
        description=(
            "Upstream identity-provider id (SCIM/SSO). ``None`` for users who were "
            "not provisioned through the directory sync."
        ),
    )
    first_name: str = Field(description="Given name as stored in the directory.")
    last_name: str = Field(description="Family name as stored in the directory.")
    display_name: str = Field(
        description=(
            'Human-readable label. Typically ``f"{first_name} {last_name}"`` but '
            "may be overridden by the directory admin."
        ),
    )
    user_name: str = Field(
        description=(
            "Login handle / username (the ``pw_name`` equivalent). Unique within a "
            "company."
        ),
    )
    email: str = Field(description="Primary contact email. Unique within a company.")
    active: bool = Field(
        description=(
            "``True`` when the user can log in. Set to ``False`` on deprovisioning; "
            "memberships and ACLs are preserved but the user cannot authenticate."
        ),
    )
    created_at: datetime = Field(
        description="When the user record was created (ISO-8601 from the API).",
    )
    updated_at: datetime = Field(
        description="When the user record was last mutated (ISO-8601).",
    )

UserWithConfiguration

Bases: UserInfo

A :class:UserInfo plus the free-form user_configuration blob.

The configuration blob is an opaque JSON object used by apps and the frontend to store per-user preferences. The platform does not interpret it; the contract between writer and reader is entirely application-defined.

Source code in unique_toolkit/unique_toolkit/experimental/resources/user/schemas.py
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class UserWithConfiguration(UserInfo):
    """A :class:`UserInfo` plus the free-form ``user_configuration`` blob.

    The configuration blob is an opaque JSON object used by apps and the frontend
    to store per-user preferences. The platform does not interpret it; the
    contract between writer and reader is entirely application-defined.
    """

    model_config = _model_config

    user_configuration: dict[str, Any] = Field(
        description=(
            "Free-form per-user configuration blob. The schema is entirely "
            "application-defined; the platform treats it as opaque JSON."
        ),
    )

UserGroupMembership

Bases: BaseModel

One of the groups a user is a member of (GET /users/{id}/groups).

This is the response shape of a user endpoint — a subset of :class:~unique_toolkit.experimental.resources.group.schemas.GroupInfo with no members or configuration field — and therefore lives with the users resource, not the groups resource.

Source code in unique_toolkit/unique_toolkit/experimental/resources/user/schemas.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
class UserGroupMembership(BaseModel):
    """One of the groups a user is a member of (``GET /users/{id}/groups``).

    This is the response shape of a *user* endpoint — a subset of
    :class:`~unique_toolkit.experimental.resources.group.schemas.GroupInfo`
    with no ``members`` or ``configuration`` field — and therefore lives with
    the users resource, not the groups resource.
    """

    model_config = _model_config

    id: str = Field(description="Group id (``gid`` equivalent).")
    name: str = Field(description="Group display name; unique within the company.")
    external_id: str | None = Field(
        default=None,
        description="Upstream group id (SCIM/SSO) or ``None`` if locally managed.",
    )
    parent_id: str | None = Field(
        default=None,
        description=(
            "Scope id of the parent group when the group hierarchy is nested, "
            "``None`` for top-level groups."
        ),
    )
    created_at: datetime = Field(description="When the group was created.")
    updated_at: datetime = Field(description="When the group was last mutated.")

unique_toolkit.experimental.resources.group.schemas

Pydantic schemas for the :mod:unique_toolkit.experimental.resources.group resource.

Mirrors :mod:unique_sdk.Group responses with field-level documentation and camelCase aliases so SDK wire payloads validate directly via :func:BaseModel.model_validate.

GroupMember

Bases: BaseModel

A single member entry as returned on :class:GroupInfo.members.

Source code in unique_toolkit/unique_toolkit/experimental/resources/group/schemas.py
22
23
24
25
26
27
28
29
30
31
32
class GroupMember(BaseModel):
    """A single member entry as returned on :class:`GroupInfo.members`."""

    model_config = _model_config

    entity_id: str = Field(
        description=(
            "The user id of this member. Named ``entity_id`` in the API because the "
            "member model allows non-user principals in future revisions."
        ),
    )

GroupInfo

Bases: BaseModel

Directory record for a group (GET /groups).

A group is the Unique equivalent of a POSIX group: an addressable set of users that can be granted permissions collectively (see the folder ACL model). Groups may be nested via :attr:parent_id.

Source code in unique_toolkit/unique_toolkit/experimental/resources/group/schemas.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class GroupInfo(BaseModel):
    """Directory record for a group (``GET /groups``).

    A group is the Unique equivalent of a POSIX group: an addressable set of
    users that can be granted permissions collectively (see the folder ACL
    model). Groups may be nested via :attr:`parent_id`.
    """

    model_config = _model_config

    id: str = Field(description="Group id; primary key.")
    name: str = Field(description="Display name. Unique within the company.")
    external_id: str | None = Field(
        default=None,
        description="Upstream directory id (SCIM/SSO) or ``None`` for local groups.",
    )
    parent_id: str | None = Field(
        default=None,
        description=(
            "Parent group id when the group is nested; ``None`` for top-level groups."
        ),
    )
    members: list[GroupMember] | None = Field(
        default=None,
        description=(
            "Group members. The list endpoint (``list_groups``) returns ``None`` "
            "to keep the response small; membership is only materialised on the "
            "single-group detail endpoints."
        ),
    )
    created_at: datetime = Field(description="When the group was created.")
    updated_at: datetime = Field(description="When the group was last mutated.")

GroupWithConfiguration

Bases: GroupInfo

A :class:GroupInfo plus the free-form configuration blob.

Source code in unique_toolkit/unique_toolkit/experimental/resources/group/schemas.py
69
70
71
72
73
74
75
76
77
78
79
class GroupWithConfiguration(GroupInfo):
    """A :class:`GroupInfo` plus the free-form ``configuration`` blob."""

    model_config = _model_config

    configuration: dict[str, Any] = Field(
        description=(
            "Free-form per-group configuration blob. The schema is application-"
            "defined; the platform treats it as opaque JSON."
        ),
    )

GroupMembership

Bases: BaseModel

Relationship row between a user (entity_id) and a group (group_id).

Source code in unique_toolkit/unique_toolkit/experimental/resources/group/schemas.py
82
83
84
85
86
87
88
89
class GroupMembership(BaseModel):
    """Relationship row between a user (``entity_id``) and a group (``group_id``)."""

    model_config = _model_config

    entity_id: str = Field(description="User id on the user side of the relationship.")
    group_id: str = Field(description="Group id on the group side of the relationship.")
    created_at: datetime = Field(description="When the membership was created.")

GroupDeleted

Bases: BaseModel

Response from delete_group — echoes the deleted group's id.

Source code in unique_toolkit/unique_toolkit/experimental/resources/group/schemas.py
92
93
94
95
96
97
class GroupDeleted(BaseModel):
    """Response from ``delete_group`` — echoes the deleted group's id."""

    model_config = _model_config

    id: str = Field(description="Id of the group that was deleted.")