Service Accounts in Waldur
Overview
Service accounts provide automated, programmatic access to Waldur resources at various organizational levels. This feature is optional and can be enabled to support integration with external authentication systems and automation workflows.
Architecture
Core Components
1. ServiceAccountMixin
Located in src/waldur_core/structure/models.py, this mixin provides the foundational capability for service account management:
1 2 3 4 5 6 7 8 9 10 | |
This mixin is applied to both Customer and Project models, enabling service account limits at organizational and project levels.
2. Service Account Hierarchy
1 2 3 4 5 | |
- BaseServiceAccount: Abstract base providing common fields (username, description, state)
- ScopedServiceAccount: Extends BaseServiceAccount with email and preferred_identifier
- ProjectServiceAccount: Service accounts scoped to specific projects
- CustomerServiceAccount: Service accounts scoped to customer organizations
- RobotAccount: Automated accounts for resource-level access
State Management
Service accounts use a finite state machine with three states:
- OK (0): Account is active and operational
- CLOSED (1): Account has been closed/deactivated
- ERRED (2): Account is in error state
State transitions:
set_state_ok(): ERRED → OKset_state_closed(): OK/ERRED → CLOSEDset_state_erred(): * → ERRED
Backend Integration
Configuration Settings
Service account functionality requires the following settings in WALDUR_CORE:
1 2 3 4 5 6 7 8 | |
Mock Mode
For development and testing, a mock backend can be enabled:
1 | |
This simulates service account operations without requiring external backend connections.
Backend API Requirements
When SERVICE_ACCOUNT_USE_API is enabled, the backend must implement the following endpoints:
1. Authentication Endpoint
POST {SERVICE_ACCOUNT_TOKEN_URL}
Request:
1 2 3 4 5 | |
Response:
1 2 3 | |
2. Create Service Account
POST {SERVICE_ACCOUNT_URL}
Headers:
1 2 | |
Request Body:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Waldur refuses a preferred_identifier that couldn't be a username: only
letters, digits, ., _ and - are allowed, and not . or .. alone.
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
3. Update Service Account
PUT {SERVICE_ACCOUNT_URL}/{username}
Headers:
1 2 | |
Request Body:
1 2 3 4 | |
4. Close Service Account
PUT {SERVICE_ACCOUNT_URL}/{username}/close
Headers:
1 | |
Response:
1 2 3 4 5 6 | |
5. Rotate API Key
PUT {SERVICE_ACCOUNT_URL}/{username}/rotate-api-key
Headers:
1 | |
Response:
1 2 3 4 5 6 7 8 | |
6. Get Service Account
GET {SERVICE_ACCOUNT_URL}/{username}
Headers:
1 | |
Response: Same as create response structure
API Endpoints (Frontend)
Project Service Accounts
- List:
GET /api/marketplace-project-service-accounts/ - Create:
POST /api/marketplace-project-service-accounts/ - Retrieve:
GET /api/marketplace-project-service-accounts/{uuid}/ - Update:
PATCH /api/marketplace-project-service-accounts/{uuid}/ - Delete:
DELETE /api/marketplace-project-service-accounts/{uuid}/ - Rotate API Key:
POST /api/marketplace-project-service-accounts/{uuid}/rotate_api_key/
Customer Service Accounts
- List:
GET /api/marketplace-customer-service-accounts/ - Create:
POST /api/marketplace-customer-service-accounts/ - Retrieve:
GET /api/marketplace-customer-service-accounts/{uuid}/ - Update:
PATCH /api/marketplace-customer-service-accounts/{uuid}/ - Delete:
DELETE /api/marketplace-customer-service-accounts/{uuid}/ - Rotate API Key:
POST /api/marketplace-customer-service-accounts/{uuid}/rotate_api_key/
Permissions
Service account operations require the MANAGE_SERVICE_ACCOUNT permission at the appropriate scope:
- Project Service Accounts: Permission required at project or customer level
- Customer Service Accounts: Permission required at customer level
Lifecycle Management
Automatic Cleanup
Service accounts are automatically closed when their parent scope is deleted:
- Project Deletion: All associated ProjectServiceAccounts are closed
- Customer Deletion: All associated CustomerServiceAccounts are closed
This is handled by Django signal handlers:
close_service_accounts_on_project_deletionclose_customer_service_accounts_on_customer_deletion
The handlers only record the accounts. Closing happens in one task per project
or customer (close_service_accounts_task), scheduled once the deletion has
committed, so a rolled-back deletion closes nothing. A closed account's row is
deleted; one that fails is kept as ERRED with its error message. One closed in
Waldur only, because no backend call could carry its username, is kept too, with
the reason: its backend account may still be open, and
waldur check_service_account_usernames lists it for staff. After a hard
delete has removed the rows, the task closes the accounts by username. It
refuses a username that an open service account still claims, because that
account belongs to the other account's scope, and only logs the refusal. An
account under a removed project claims nothing here, so accounts of removed
projects that share a username don't block each other's close.
A daily task, reconcile_removed_project_accounts (03:10), retries what that
attempt left open: every ProjectServiceAccount still OK or ERRED under a
removed project goes through the same close again. This also covers a close
that was never queued, for example because the broker was down when the
deletion committed. The task queues one
reconcile_removed_project_accounts_for_project per project, each with its own
token. It leaves a project removed less than an hour ago to its own deletion
close, and a project that is no longer removed keeps its accounts, also when it
is recovered while its close is under way. Project members can no longer see the
accounts of a removed project, so between runs only staff can close an ERRED
one through the API.
Not covered: CustomerServiceAccount, whose rows are deleted together with the
customer, and a failed close by username after a hard delete. Neither leaves a
row to retry, only a log entry.
An account the backend no longer has (404), or reports as closed, is closed
locally without calling close: that is the requested outcome, not an error. An
account with no username was never created at the backend and is closed
locally as well.
Every call to the service account backend has a 5-second timeout
(ACCOUNT_API_REQUEST_TIMEOUT).
Account Limits
Organizations can enforce service account limits:
- Project Level: Set
max_service_accountson the Project model - Customer Level: Set
max_service_accountson the Customer model
When limits are set, attempts to create accounts beyond the limit will be rejected with a validation error.
Error Handling
Service accounts track errors through:
- state: Transitions to ERRED state on failures
- error_message: Human-readable error description
- error_traceback: Full error traceback for debugging
Failed operations automatically transition accounts to ERRED state, which can be recovered using set_state_ok() after resolving issues.
Integration with GLAuth
Service accounts can be exported for GLAuth synchronization through the offering endpoint:
GET /api/marketplace-offerings/{uuid}/glauth_users_config/
This generates configuration records for:
- Offering users
- Robot accounts (including service accounts)
Implementation Checklist
When implementing a service account backend, ensure:
- OAuth2 token endpoint is available and returns bearer tokens
- Service account creation endpoint generates unique usernames
- API keys are returned with expiration information
- Update operations modify only allowed fields (email, description)
- Close operation marks accounts as disabled
- Get operation returns current account status
- Rotate operation generates new API keys
- Error responses use standard HTTP status codes
- All endpoints validate bearer token authentication
- Account status transitions are logged appropriately
Security Considerations
- API Keys: Generated keys are returned only once during creation. Store securely.
- Token Expiration: API keys should have reasonable TTL (default: 30 days)
- Permission Checks: All operations validate user permissions at appropriate scope
- Audit Logging: All service account operations are logged for audit trails
- Cleanup: Accounts are automatically closed when parent resources are deleted
Testing
Mock mode can be enabled for testing without external dependencies:
1 2 | |
This simulates all backend operations locally, useful for:
- Unit tests
- Development environments
- Demo installations