Developer Guide
This guide covers the technical implementation of Access Control for CX Assistants, including data models, the access check logic, and API reference.
Architecture Overviewβ
Access Control restricts which users can access specific Assistants through a group-based permission system:
Data Modelβ
AssistantUserGroupβ
Groups that can be assigned to both users and Assistants to control access.
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | Auto | Primary key |
name | string | Yes | Unique group name (max 255 characters) |
users | ManyToMany | No | Users who belong to this group |
created_at | datetime | Auto | Timestamp when the group was created |
updated_at | datetime | Auto | Timestamp when the group was last updated |
Django Model Definition:
class AssistantUserGroup(TimestampedModel):
name = models.CharField(
max_length=255,
unique=True,
error_messages={"unique": "Group with this name already exists."}
)
class Meta:
verbose_name = "User Group"
verbose_name_plural = "User Groups"
Assistant (Access Control Fields)β
The Assistant model includes group-based authorization:
| Field | Type | Description |
|---|---|---|
authorized_groups | ManyToMany | Groups that have access to this Assistant |
Django Model Definition (relevant excerpt):
class Assistant(TimestampedModel):
# ... other fields ...
authorized_groups = models.ManyToManyField(
"assistants.AssistantUserGroup",
blank=True,
related_name="authorized_assistants"
)
CustomUser (Group Membership)β
Users are assigned to groups through the assistant_user_groups field:
| Field | Type | Description |
|---|---|---|
assistant_user_groups | ManyToMany | Groups the user belongs to |
Django Model Definition (relevant excerpt):
class CustomUser(AbstractBaseUser, PermissionsMixin):
# ... other fields ...
assistant_user_groups = models.ManyToManyField(
"assistants.AssistantUserGroup",
blank=True,
related_name="users"
)
User (Domain Model)β
The internal domain representation used during evaluation:
class User(BaseModel):
user_id: int
assistant_groups: list[int] = []
Access Control Logicβ
user_has_access Methodβ
The user_has_access method on the Assistant domain model determines whether a user can access the Assistant:
def user_has_access(self, user: User) -> bool:
if not self.authorized_groups:
return True
return any(group in user.assistant_groups for group in self.authorized_groups)
Logic:
- If the Assistant has no
authorized_groupsconfigured, all users have access - If
authorized_groupsare configured, the user must belong to at least one of those groups
Evaluation Checkβ
The access check is performed in the AssistantEvaluator._evaluate_assistant method:
if user is not None and not assistant.user_has_access(user):
logger.info(
"Assistant %s: user cannot access (evaluation trace ID: %s)",
assistant.code,
evaluation_trace_id
)
return USER_HAS_NO_ACCESS_MESSAGE, evaluation_id, EvaluationStatus.NO_ACCESS_TO_ASSISTANT
Constants:
USER_HAS_NO_ACCESS_MESSAGE = "User has no access to this assistant."EvaluationStatus.NO_ACCESS_TO_ASSISTANT- Returned when access is denied
User Group Resolutionβ
When evaluating an Assistant, the user's groups are retrieved from the database:
class UserRepository:
async def get_user(self, user_uuid: str) -> User:
orm_user = await orm.CustomUser.objects.filter(uuid=user_uuid).afirst()
if not orm_user:
raise ValueError(f"User with UUID {user_uuid} not found")
return User(
user_id=orm_user.id,
assistant_groups=[group.id async for group in orm_user.assistant_user_groups.all()],
)
API Referenceβ
Assistant User Groups APIβ
CRUD operations for managing user groups.
List Groupsβ
GET /api/v1/assistants/user-groups/
Response:
[
{
"id": 1,
"name": "Pilot Group",
"users": [
{
"id": 42,
"email": "agent@example.com",
"first_name": "John",
"last_name": "Doe"
}
],
"assistants": [
{
"id": 10,
"code": "support-assistant",
"name": "Support Assistant"
}
]
}
]
Create Groupβ
POST /api/v1/assistants/user-groups/
Request Body:
{
"name": "Technical Support",
"users": [42, 43, 44]
}
Update Groupβ
PUT /api/v1/assistants/user-groups/{id}/
Request Body:
{
"name": "Technical Support Team",
"users": [42, 43, 44, 45]
}
Delete Groupβ
DELETE /api/v1/assistants/user-groups/{id}/
Deleting a group immediately removes access for all users in that group to any Assistants that only authorized that group.
Evaluation Responseβ
When a user lacks access to an Assistant, the evaluation endpoint returns:
{
"output": "User has no access to this assistant.",
"evaluation_id": "abc-123",
"evaluation_status": "no_access_to_assistant"
}
Integration with On-Demand Assistantsβ
For on-demand Assistants displayed in the UI, the profile serializer filters Assistants based on user group membership:
if user and user.assistant_user_groups.exists():
assistant_user_group_ids = list(user.assistant_user_groups.values_list("id", flat=True))
assistant_id_list += list(
Assistant.authorized_groups.through.objects.filter(
assistantusergroup_id__in=assistant_user_group_ids
).values_list("assistant_id", flat=True)
)
This ensures users only see Assistants they have access to in the frontend.
Access Control Matrixβ
| Assistant Config | User Groups | Result |
|---|---|---|
| No authorized groups | Any | Access granted |
| Groups: [A, B] | User in [A] | Access granted |
| Groups: [A, B] | User in [B, C] | Access granted |
| Groups: [A, B] | User in [C, D] | Access denied |
| Groups: [A] | User in [] | Access denied |
Source Code Referencesβ
| Component | Location |
|---|---|
| AssistantUserGroup model | assistants/models.py:291-301 |
| Assistant.authorized_groups | assistants/models.py:221-222 |
| CustomUser.assistant_user_groups | dashboard/models/custom_user.py:53 |
| User domain model | backend/domains/assistants/models/user.py |
| user_has_access method | backend/domains/assistants/models/assistant.py:150-154 |
| Access check in evaluator | backend/domains/assistants/services/evaluate.py:188-192 |
| UserRepository | backend/domains/assistants/repositories/user.py |
| AssistantUserGroupViewSet | assistants/views.py:385-390 |
Related Documentationβ
- Overview: Conceptual overview of Access Control
- User Guide: Configuration instructions
- Assistants Developer Guide: Full Assistant evaluation flow