"""Role binding models for the Admin API."""
from __future__ import annotations
from collections.abc import Iterator
from enum import Enum
from typing import Any
from msgspec import Struct
from pinecone._internal.validation import require_one_of
from pinecone.errors.exceptions import ValidationError
from pinecone.models._mixin import StructDictMixin, _struct_to_dict_recursive
from pinecone.models.admin.pagination import PaginationResponse
[docs]
class PrincipalType(str, Enum):
"""The kind of principal that receives permissions from a role binding.
Possible values: ``user``, ``service_account``, ``api_key``, ``invite``.
Examples:
>>> from pinecone.models.admin.role_binding import PrincipalType
>>> PrincipalType.SERVICE_ACCOUNT == "service_account"
True
"""
USER = "user"
SERVICE_ACCOUNT = "service_account"
API_KEY = "api_key"
INVITE = "invite"
[docs]
class ResourceType(str, Enum):
"""The kind of resource scope a role binding applies to.
Possible values: ``organization``, ``project``.
Examples:
>>> from pinecone.models.admin.role_binding import ResourceType
>>> ResourceType.PROJECT == "project"
True
"""
ORGANIZATION = "organization"
PROJECT = "project"
[docs]
class RoleName(str, Enum):
"""A role that can be assigned to a principal at a resource scope.
Organization-scoped roles: ``OrgOwner``, ``OrgManager``, ``OrgMember``,
``OrgBillingAdmin``. Project-scoped roles: ``ProjectOwner``,
``ProjectManager``, ``ProjectMember``, ``ProjectEditor``,
``ProjectViewer``, ``ControlPlaneEditor``, ``ControlPlaneViewer``,
``DataPlaneEditor``, ``DataPlaneViewer``.
Membership in this enum only means the SDK will forward the value. Which of
these roles may be bound to which scope and principal type, and which the
organization's plan includes, is the server's decision and is reported as
:exc:`~pinecone.errors.exceptions.ForbiddenError` at bind time.
Examples:
>>> from pinecone.models.admin.role_binding import RoleName
>>> RoleName.DATA_PLANE_EDITOR == "DataPlaneEditor"
True
"""
ORG_OWNER = "OrgOwner"
ORG_MANAGER = "OrgManager"
ORG_MEMBER = "OrgMember"
ORG_BILLING_ADMIN = "OrgBillingAdmin"
PROJECT_OWNER = "ProjectOwner"
PROJECT_MANAGER = "ProjectManager"
PROJECT_MEMBER = "ProjectMember"
PROJECT_EDITOR = "ProjectEditor"
PROJECT_VIEWER = "ProjectViewer"
CONTROL_PLANE_EDITOR = "ControlPlaneEditor"
CONTROL_PLANE_VIEWER = "ControlPlaneViewer"
DATA_PLANE_EDITOR = "DataPlaneEditor"
DATA_PLANE_VIEWER = "DataPlaneViewer"
_VALID_RESOURCE_TYPES = [r.value for r in ResourceType]
_VALID_ROLE_NAMES = [r.value for r in RoleName]
def _as_str(value: str) -> str:
return value.value if isinstance(value, Enum) else value
[docs]
class RoleBindingModel(StructDictMixin, Struct, kw_only=True):
"""Response model for a role binding: a ``role`` granted to a principal at a scope.
``principal_type``, ``resource_type``, and ``role`` are typed as
:class:`str` rather than as enums so values the server adds after this SDK
release surface as their raw strings instead of raising. Compare against
:class:`PrincipalType`, :class:`ResourceType`, and :class:`RoleName`
directly — they are ``str`` values.
Attributes:
id (str): Unique identifier (UUID) for the role binding. This is what
:meth:`RoleBindings.delete()
<pinecone.admin.role_bindings.RoleBindings.delete>` takes — revoking a role is
addressed by the binding, never by the principal/scope/role triple.
principal_type (str): One of the :class:`PrincipalType` values.
principal_id (str): The principal's UUID.
resource_type (str): One of the :class:`ResourceType` values.
resource_id (str): The organization or project the binding is scoped to.
Always populated, including on organization-scoped bindings whose
create request omitted it.
role (str): One of the :class:`RoleName` values.
created_at (str): RFC 3339 timestamp for when the binding was created.
There is no updated timestamp: bindings are immutable, so a role
change is a create plus a delete rather than an edit.
Examples:
>>> from pinecone.models.admin.role_binding import RoleBindingModel, RoleName
>>> binding = RoleBindingModel(
... id="9a8e3528-b9c0-4358-84ce-84c28e91b566",
... principal_type="service_account",
... principal_id="f8a3b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
... resource_type="project",
... resource_id="a2f7dddb-1597-4eff-9f71-535fde243f58",
... role="DataPlaneEditor",
... created_at="2026-04-10T15:23:00Z",
... )
>>> binding.role == RoleName.DATA_PLANE_EDITOR
True
"""
id: str
principal_type: str
principal_id: str
resource_type: str
resource_id: str
role: str
created_at: str
[docs]
class RoleBindingList(Struct, kw_only=True):
"""A page of role bindings, plus the cursor for the next page.
One raw page of a role-binding listing. Callers who reach bindings through
:meth:`RoleBindings.list() <pinecone.admin.role_bindings.RoleBindings.list>` get a
:class:`~pinecone.models.pagination.Paginator` instead, which follows these
cursors for them.
Attributes:
data (list[RoleBindingModel]): The role bindings on this page.
pagination (PaginationResponse | None): Cursor envelope for the next
page, or ``None`` on the final page.
Examples:
>>> from pinecone.models.admin.role_binding import RoleBindingList, RoleBindingModel
>>> bindings = RoleBindingList(
... data=[
... RoleBindingModel(
... id="9a8e3528-b9c0-4358-84ce-84c28e91b566",
... principal_type="user",
... principal_id="e2e92523-85dc-4142-b8c2-e681be8b78df",
... resource_type="organization",
... resource_id="4f6a1e0c-8f2b-4c1a-9d3e-1b2c3d4e5f60",
... role="OrgMember",
... created_at="2026-04-10T15:23:00Z",
... )
... ]
... )
>>> bindings.roles()
['OrgMember']
"""
data: list[RoleBindingModel] = []
pagination: PaginationResponse | None = None
def __iter__(self) -> Iterator[RoleBindingModel]:
return iter(self.data)
def __len__(self) -> int:
return len(self.data)
def __getitem__(self, index: int) -> RoleBindingModel:
return self.data[index]
@property
def pagination_token(self) -> str | None:
"""Opaque cursor for the next page, or ``None`` if this is the last page."""
return self.pagination.next if self.pagination is not None else None
@property
def has_more(self) -> bool:
"""``True`` when the server supplied a cursor for a further page."""
return self.pagination_token is not None
[docs]
def roles(self) -> list[str]:
"""Return the role names on this page, in order."""
return [binding.role for binding in self.data]
[docs]
def to_dict(self) -> dict[str, Any]:
"""Return this page as a serializable dict with ``data`` and ``pagination`` keys."""
return {
"data": [_struct_to_dict_recursive(binding) for binding in self.data],
"pagination": _struct_to_dict_recursive(self.pagination),
}
def __repr__(self) -> str:
summaries = ", ".join(
f"<principal_type={b.principal_type!r}, principal_id={b.principal_id!r}, "
f"role={b.role!r}>"
for b in self.data
)
return f"RoleBindingList([{summaries}], has_more={self.has_more!r})"