Skip to content

Commit

Permalink
Move account-related plugins to new-style API
Browse files Browse the repository at this point in the history
  • Loading branch information
themylogin committed Sep 8, 2024
1 parent d156c5f commit 5a53593
Show file tree
Hide file tree
Showing 11 changed files with 424 additions and 270 deletions.
19 changes: 18 additions & 1 deletion src/middlewared/middlewared/api/base/types/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
from pydantic.functional_validators import AfterValidator
from typing_extensions import Annotated

__all__ = ["LocalUsername", "RemoteUsername", "LocalUID"]
from middlewared.utils.sid import sid_is_valid

__all__ = ["LocalUsername", "RemoteUsername", "LocalUID", "LocalGID", "SID"]

TRUENAS_IDMAP_DEFAULT_LOW = 90000001

Expand Down Expand Up @@ -45,6 +47,21 @@ def validate_remote_username(val: str) -> str:
return validate_username(val, DEFAULT_VALID_CHARS + '\\', None, None)


def validate_sid(value: str) -> str:
value = value.strip()
value = value.upper()

assert sid_is_valid(value), ('SID is malformed. See MS-DTYP Section 2.4 for SID type specifications. Typically '
'SIDs refer to existing objects on the local or remote server and so an appropriate '
'value should be queried prior to submitting to API endpoints.')

return value


LocalUsername = Annotated[str, AfterValidator(validate_local_username)]
RemoteUsername = Annotated[str, AfterValidator(validate_remote_username)]
LocalUID = Annotated[int, Ge(0), Le(TRUENAS_IDMAP_DEFAULT_LOW - 1)]

LocalGID = Annotated[int, Ge(0), Le(TRUENAS_IDMAP_DEFAULT_LOW - 1)]

SID = Annotated[str, AfterValidator(validate_sid)]
3 changes: 3 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from .api_key import * # noqa
from .auth import * # noqa
from .cloud_sync import * # noqa
from .common import * # noqa
from .core import * # noqa
from .group import * # noqa
from .privilege import * # noqa
from .user import * # noqa
from .vendor import * # noqa
14 changes: 14 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from middlewared.api.base import BaseModel, single_argument_result
from .user import UserGetUserObjResult


class AuthMeArgs(BaseModel):
pass


@single_argument_result
class AuthMeResult(UserGetUserObjResult.model_fields["result"].annotation):
attributes: dict
two_factor_config: dict
privilege: dict
account_attributes: list[str]
129 changes: 129 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
from typing import Literal

from annotated_types import Ge, Le
from pydantic import Field
from typing_extensions import Annotated

from middlewared.api.base import (BaseModel, Excluded, excluded_field, ForUpdateMetaclass, LocalUsername, RemoteUsername,
LocalUID, LongString, NonEmptyString, Private, single_argument_args, single_argument_result)

__all__ = ["GroupEntry",
"GroupCreateArgs", "GroupCreateResult",
"GroupUpdateArgs", "GroupUpdateResult",
"GroupDeleteArgs", "GroupDeleteResult",
"GroupGetNextGidArgs", "GroupGetNextGidResult",
"GroupGetGroupObjArgs", "GroupGetGroupObjResult",
"GroupHasPasswordEnabledUserArgs", "GroupHasPasswordEnabledUserResult"]


class GroupEntry(BaseModel):
id: int
gid: int
name: NonEmptyString
builtin: bool
sudo_commands: list[NonEmptyString] = []
sudo_commands_nopasswd: list[NonEmptyString] = []
smb: bool = True
"Specifies whether the group should be mapped into an NT group."
group: NonEmptyString
id_type_both: bool
local: bool
sid: str | None
roles: list[str]
users: list[int] = []
"A list of user ids (`id` attribute from `user.query`)."


class GroupCreate(GroupEntry):
id: Excluded = excluded_field()
builtin: Excluded = excluded_field()
group: Excluded = excluded_field()
id_type_both: Excluded = excluded_field()
local: Excluded = excluded_field()
sid: Excluded = excluded_field()
roles: Excluded = excluded_field()

gid: LocalUID | None = None
"If `null`, it is automatically filled with the next one available."
allow_duplicate_gid: bool = False
"Allows distinct group names to share the same gid."


class GroupCreateArgs(BaseModel):
group_create: GroupCreate


class GroupCreateResult(BaseModel):
result: int


class GroupUpdate(GroupCreate, metaclass=ForUpdateMetaclass):
pass


class GroupUpdateArgs(BaseModel):
id: int
group_update: GroupUpdate


class GroupUpdateResult(BaseModel):
result: int


class GroupDeleteOptions(BaseModel):
delete_users: bool = False
"Deletes all users that have this group as their primary group."


class GroupDeleteArgs(BaseModel):
id: int
options: GroupDeleteOptions = Field(default=GroupDeleteOptions())


class GroupDeleteResult(BaseModel):
result: int


class GroupGetNextGidArgs(BaseModel):
pass


class GroupGetNextGidResult(BaseModel):
result: int


@single_argument_args("get_group_obj")
class GroupGetGroupObjArgs(BaseModel):
groupname: str | None = None
gid: int | None = None
sid_info: bool = False


@single_argument_result
class GroupGetGroupObjResult(BaseModel):
gr_name: str
"name of the group"
gr_gid: int
"group id of the group"
gr_mem: list[int]
"list of gids that are members of the group"
sid: str | None = None
"optional SID value for the account that is present if `sid_info` is specified in payload."
source: Literal['LOCAL', 'ACTIVEDIRECTORY', 'LDAP']
"""
the name server switch module that provided the user. Options are:
FILES - local user in passwd file of server,
WINBIND - user provided by winbindd,
SSS - user provided by SSSD.
"""
local: bool
"boolean indicating whether this group is local to the NAS or provided by a directory service."


class GroupHasPasswordEnabledUserArgs(BaseModel):
gids: list[int]
exclude_user_ids: list[int] = []


class GroupHasPasswordEnabledUserResult(BaseModel):
result: bool
55 changes: 55 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/privilege.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from middlewared.api.base import BaseModel, Excluded, excluded_field, ForUpdateMetaclass, NonEmptyString, SID
from .api_key import AllowListItem
from .group import GroupEntry

__all__ = ["PrivilegeEntry",
"PrivilegeCreateArgs", "PrivilegeCreateResult",
"PrivilegeUpdateArgs", "PrivilegeUpdateResult",
"PrivilegeDeleteArgs", "PrivilegeDeleteResult"]


class PrivilegeEntry(BaseModel):
id: int
builtin_name: str | None
name: NonEmptyString
local_groups: list[GroupEntry]
ds_groups: list[GroupEntry]
allowlist: list[AllowListItem] = []
roles: list[str] = []
web_shell: bool


class PrivilegeCreate(PrivilegeEntry):
id: Excluded = excluded_field()
builtin_name: Excluded = excluded_field()
local_groups: list[int] = []
ds_groups: list[int | SID] = []


class PrivilegeCreateArgs(BaseModel):
privilege_create: PrivilegeCreate


class PrivilegeCreateResult(BaseModel):
result: PrivilegeEntry


class PrivilegeUpdate(PrivilegeCreate, metaclass=ForUpdateMetaclass):
pass


class PrivilegeUpdateArgs(BaseModel):
id: int
privilege_update: PrivilegeUpdate


class PrivilegeUpdateResult(BaseModel):
result: PrivilegeEntry


class PrivilegeDeleteArgs(BaseModel):
id: int


class PrivilegeDeleteResult(BaseModel):
result: bool
Loading

0 comments on commit 5a53593

Please sign in to comment.