OfferingUser States and Management
OfferingUser represents a user account created for a specific marketplace offering. It tracks two independent state dimensions:
- Lifecycle state (
state): A finite state machine (FSM) tracking where the account is in the provisioning/deletion workflow. - Runtime state (
runtime_state): An operational flag the service provider can set freely to signal whether the user currently has access to the service (e.g. TOU accepted, account linked). This is independent of lifecycle and can be updated at any time except when the account isDELETED.
Lifecycle States
OfferingUser has the following lifecycle states:
| State | Description |
|---|---|
CREATION_REQUESTED |
Initial state when user account creation is requested |
CREATING |
Account is being created by the service provider |
PENDING_ACCOUNT_LINKING |
Waiting for user to link their existing account |
PENDING_ADDITIONAL_VALIDATION |
Requires additional validation from service provider |
OK |
Account is active and ready to use |
DELETION_REQUESTED |
Account deletion has been requested |
DELETING |
Account is being deleted |
DELETED |
Account has been successfully deleted |
ERROR_CREATING |
An error occurred during account creation |
ERROR_DELETING |
An error occurred during account deletion |
The deletion-flow states (DELETION_REQUESTED, DELETING, ERROR_DELETING, DELETED) are not terminal for the person: a member who regains project access while their account is in any of them gets it back through the restore() transition. Under a Waldur-named username policy (waldur_username, anonymized, full_name, freeipa, identity_claim) the account returns straight to OK under its existing username, because the name is a stable function of the person and the provider keeps (or parks) the entry behind it. Under the service_provider policy a DELETED account is asked for again as CREATION_REQUESTED, since the provider chose the name and has torn the account down; one whose deletion was only requested or begun returns to OK. See Departure and return.
State Transitions
stateDiagram-v2
[*] --> CREATION_REQUESTED : Account requested
CREATION_REQUESTED --> CREATING : begin_creating()
CREATION_REQUESTED --> OK : set_ok()
CREATING --> PENDING_ACCOUNT_LINKING : set_pending_account_linking()
CREATING --> PENDING_ADDITIONAL_VALIDATION : set_pending_additional_validation()
CREATING --> OK : set_ok()
PENDING_ACCOUNT_LINKING --> OK : set_validation_complete()
PENDING_ACCOUNT_LINKING --> PENDING_ADDITIONAL_VALIDATION : set_pending_additional_validation()
PENDING_ADDITIONAL_VALIDATION --> OK : set_validation_complete()
PENDING_ADDITIONAL_VALIDATION --> PENDING_ACCOUNT_LINKING : set_pending_account_linking()
OK --> DELETION_REQUESTED : request_deletion()
DELETION_REQUESTED --> DELETING : set_deleting()
DELETING --> DELETED : set_deleted()
%% Error state transitions during creation flow
CREATION_REQUESTED --> ERROR_CREATING : set_error_creating()
CREATING --> ERROR_CREATING : set_error_creating()
PENDING_ACCOUNT_LINKING --> ERROR_CREATING : set_error_creating()
PENDING_ADDITIONAL_VALIDATION --> ERROR_CREATING : set_error_creating()
%% Error state transitions during deletion flow
DELETION_REQUESTED --> ERROR_DELETING : set_error_deleting()
DELETING --> ERROR_DELETING : set_error_deleting()
%% Recovery from error states
ERROR_CREATING --> CREATING : begin_creating()
ERROR_CREATING --> OK : set_ok()
ERROR_CREATING --> PENDING_ACCOUNT_LINKING : set_pending_account_linking()
ERROR_CREATING --> PENDING_ADDITIONAL_VALIDATION : set_pending_additional_validation()
ERROR_DELETING --> DELETING : set_deleting()
ERROR_DELETING --> OK : set_ok()
%% Return of a departed member (Waldur-named username policies)
DELETION_REQUESTED --> OK : restore()
DELETING --> OK : restore()
ERROR_DELETING --> OK : restore()
DELETED --> OK : restore()
%% Return of a departed member (service_provider username policy)
DELETED --> CREATION_REQUESTED : account requested again
%% Legacy error transitions (backward compatibility)
CREATION_REQUESTED --> ERROR_CREATING : set_error() [legacy]
CREATING --> ERROR_CREATING : set_error() [legacy]
PENDING_ACCOUNT_LINKING --> ERROR_CREATING : set_error() [legacy]
PENDING_ADDITIONAL_VALIDATION --> ERROR_CREATING : set_error() [legacy]
OK --> ERROR_CREATING : set_error() [legacy]
DELETION_REQUESTED --> ERROR_CREATING : set_error() [legacy]
DELETING --> ERROR_CREATING : set_error() [legacy]
REST API Endpoints
State Transition Actions
All state transition endpoints require UPDATE_OFFERING_USER permission and are accessed via POST to the offering user detail endpoint with the action suffix.
Base URL: /api/marketplace-offering-users/{uuid}/
Set Pending Additional Validation
1 2 3 4 5 6 7 | |
Valid transitions from: CREATING, ERROR_CREATING, PENDING_ACCOUNT_LINKING
Set Pending Account Linking
1 2 3 4 5 6 7 | |
Valid transitions from: CREATING, ERROR_CREATING, PENDING_ADDITIONAL_VALIDATION
Set Validation Complete
1 | |
Valid transitions from: PENDING_ADDITIONAL_VALIDATION, PENDING_ACCOUNT_LINKING
Note: This action clears both the service_provider_comment and service_provider_comment_url fields.
Set OK
1 | |
Valid transitions from: CREATION_REQUESTED, CREATING, PENDING_ADDITIONAL_VALIDATION, PENDING_ACCOUNT_LINKING, ERROR_CREATING, ERROR_DELETING
Manually sets the account to OK, for example to recover from an error state or to finish a manual creation. Like set_validation_complete, it clears both the service_provider_comment and service_provider_comment_url fields.
Set Error Creating
1 | |
Valid transitions from: CREATION_REQUESTED, CREATING, PENDING_ACCOUNT_LINKING, PENDING_ADDITIONAL_VALIDATION
Sets the user account to error state during the creation process. Used when creation operations fail.
Set Error Deleting
1 | |
Valid transitions from: DELETION_REQUESTED, DELETING
Sets the user account to error state during the deletion process. Used when deletion operations fail.
409 Conflict: returned when the account is OK again because the member regained access after the deletion was requested (see Departure and return). The deletion no longer stands; re-read the account and keep it enabled.
Begin Creating
1 | |
Valid transitions from: CREATION_REQUESTED, ERROR_CREATING
Initiates the account creation process. Can be used to retry creation after an error.
Request Deletion
1 | |
Valid transitions from: OK
Initiates the account deletion process. Moves the user from active status to deletion requested.
Set Deleting
1 | |
Valid transitions from: DELETION_REQUESTED, ERROR_DELETING
Begins the account deletion process. Can be used to retry deletion after an error.
409 Conflict: returned when the account is OK again because the member regained access after the deletion was requested (see Departure and return). The deletion no longer stands; re-read the account and keep it enabled.
Set Deleted
1 | |
Valid transitions from: DELETING
Marks the user account as successfully deleted. This is the final state for successful account deletion. When the account reads through a provider-level account (account_scope: provider) and this was the last offering account still reading through it, the provider account completes its own deletion in the same request (see Departure and return).
409 Conflict: returned when the account is OK again because the member regained access after the deletion was requested. The deletion no longer stands; re-read the account and keep it enabled.
Service Provider Comment Management
The comment and its URL explain a pending state to the user, so they are cleared whenever the account moves to OK: through set_validation_complete, set_ok, username assignment, or restore(). To show a comment on an account that is already OK, set it with update_comments after the transition.
Update Comments
Service providers can directly update comment fields without changing the user's state:
1 2 3 4 5 6 7 | |
Permissions: Requires UPDATE_OFFERING_USER permission on the offering's customer.
Valid states: All states except DELETED
Both fields are optional - you can update just the comment, just the URL, or both.
Update Runtime State
1 2 3 4 5 6 7 8 | |
service_provider_comment and service_provider_comment_url are optional. Omit them to leave existing comments unchanged, or pass empty strings to clear them.
Where runtime_state is one of:
| Value | Meaning |
|---|---|
Active |
User can access the service normally |
Pending account linking |
User must link an external account (e.g. MyAccessID) before access is granted |
Pending additional validation |
User must complete additional validation (e.g. accept new Terms of Use) |
Valid transitions: Any → Any (no FSM). Can be set regardless of lifecycle state, except when lifecycle is DELETED.
Permissions: Requires UPDATE_OFFERING_USER permission on the offering's customer.
Key use case — backfill sync: When a service provider syncs an external system (e.g. Puhuri), they can update runtime_state on users already in lifecycle OK without touching the provisioning FSM.
OfferingUser Fields
When retrieving or updating OfferingUser objects, the following state-related fields are available:
state(string, read-only): Current lifecycle state of the user account (provisioning/deletion)runtime_state(string, read-only): Current operational/access state of the user accountservice_provider_comment(string, read-only): Comment from service provider for pending states; cleared when the account moves toOKservice_provider_comment_url(string, read-only): Optional URL link for additional information or actions related to the service provider comment
Runtime States
| State | Description |
|---|---|
Active |
User has full access to the service |
Pending account linking |
Access blocked; user must link an external account |
Pending additional validation |
Access blocked; user must complete additional validation (e.g. TOU) |
Lifecycle vs Runtime State
These two fields are independent:
state (lifecycle) |
runtime_state |
Meaning |
|---|---|---|
OK |
Active |
Account provisioned and fully accessible |
OK |
Pending account linking |
Provisioned in Waldur, but blocked in backend (e.g. MyAccessID not linked yet) |
OK |
Pending additional validation |
Provisioned in Waldur, but blocked pending TOU acceptance |
Creating |
Active |
Account being created; default runtime state |
The lifecycle FSM (state) tracks Waldur-side provisioning. The runtime_state tracks operational access status as reported by the service provider. Service providers should update runtime_state via update_runtime_state, and upstream consumers should read both fields from STOMP messages.
Backward Compatibility
The system maintains backward compatibility with existing integrations:
Automatic State Transitions
- Username Assignment: When a username is assigned to an OfferingUser (via API or
set_offerings_username), the state automatically transitions toOKand the service provider comment fields are cleared - Creation with Username: Creating an OfferingUser with a username immediately sets the state to
OK
Legacy Endpoints
POST /api/marketplace-service-providers/{uuid}/set_offerings_username/- Bulk username assignment that automatically transitions users toOKstate
Legacy Error State Support
For backward compatibility with existing integrations:
set_error()method: The legacyset_error()method still exists and defaults toERROR_CREATINGstate
New integrations should use the specific error states (ERROR_CREATING, ERROR_DELETING) for better error context.
Usage Examples
Service Provider Workflow
Standard Creation Flow
- Initial Creation: OfferingUser is created with state
CREATION_REQUESTED - Begin Processing: Transition to
CREATINGstate - Require Validation: If additional validation needed, transition to
PENDING_ADDITIONAL_VALIDATIONwith explanatory comment and optional URL - Complete Validation: Once validated, transition to
OKstate - Account Ready: User can now access the service
Enhanced Workflow with Comment URLs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Error Handling and Recovery
1 2 3 4 5 6 7 8 9 10 11 | |
Account Deletion Workflow
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Departure and return
A member who leaves a project and later comes back gets the same account back: same username, same POSIX uid. The flow, end to end:
- Last project role revoked. For offerings with
offering_user_auto_deletionenabled, every account of the user in an offering where they no longer hold a live resource movesOK→DELETION_REQUESTED(request_deletion). An account that was never provisioned (CREATION_REQUESTED) goes straight toDELETED. - Provider tears down (or parks) the entry. The service provider or site agent claims the request with
set_deletingand acknowledges it withset_deleted. A provider that runs a shared directory may park the entry instead of removing it -- disable it while keeping its uid and username -- so that it can be re-enabled on return; the site agent'son_departuresetting controls this (see Waldur-authoritative accounts in OpenLDAP in the user guide). - Provider account completes. Under
account_scope: providerthe offering accounts read through oneServiceProviderAccount. When the last account still reading through it leaves the live states, the provider account moves toDELETION_REQUESTED; once every account reading through it isDELETED, the provider account completes toDELETEDon its own, in the request that acknowledged the last one. There is no separate acknowledgement for the provider account: it is a projection of its offering accounts and has no backend of its own. ThePosixIdentitybehind it is not released, so a parked entry keeps its uid. - Return. When the user is granted a project role again (or a new resource brings the project back into the offering), every existing account of theirs in an offering with a live resource in that project is restored, whether or not the offering allows minting new accounts (
service_provider_can_create_offering_usergates creation only). The provider account, if any, returns toOKfirst; the offering account then comes back under its existing username -- or, for a Waldur-named policy, a name derived on the way back if it never had one -- and lands inOK. Under theservice_providerpolicy aDELETEDaccount is asked for again asCREATION_REQUESTEDinstead. AnOFFERING_USERevent withaction: updateis published, so a directory writer subscribed to it re-enables the parked entry.
A provider that still holds a pending deletion when the member returns may acknowledge it late. set_deleting, set_deleted and set_error_deleting refuse such an acknowledgement with 409 Conflict when the account is already OK again, so the account ends live rather than deleted; the provider should re-read the account and keep the entry enabled.
Permissions
State transition endpoints use the permission_factory pattern with:
- Permission:
UPDATE_OFFERING_USER - Scope:
["offering.customer"]- User must have permission on the offering's customer
This means users need the UPDATE_OFFERING_USER permission on the customer that owns the offering associated with the OfferingUser.
Filtering OfferingUsers
The OfferingUser list endpoint supports filtering by state to help manage users across different lifecycle stages.
State Filtering
Filter OfferingUsers by their current state using the state query parameter:
1 2 | |
Available State Filter Values
| Filter Value | State Constant | Description |
|---|---|---|
Requested |
CREATION_REQUESTED |
Users with account creation requested |
Creating |
CREATING |
Users whose accounts are being created |
Pending account linking |
PENDING_ACCOUNT_LINKING |
Users waiting to link existing accounts |
Pending additional validation |
PENDING_ADDITIONAL_VALIDATION |
Users requiring additional validation |
OK |
OK |
Users with active, ready-to-use accounts |
Requested deletion |
DELETION_REQUESTED |
Users with deletion requested |
Deleting |
DELETING |
Users whose accounts are being deleted |
Deleted |
DELETED |
Users with successfully deleted accounts |
Error creating |
ERROR_CREATING |
Users with errors during account creation |
Error deleting |
ERROR_DELETING |
Users with errors during account deletion |
Multiple State Filtering
Filter by multiple states simultaneously:
1 2 | |
Combining with Other Filters
State filtering can be combined with other available filters:
1 2 3 4 5 6 7 8 | |
Error Handling
Invalid state values return HTTP 400 Bad Request:
1 2 | |
Other Available Filters
The OfferingUser list endpoint also supports these filters:
offering_uuid- Filter by offering UUIDuser_uuid- Filter by user UUIDuser_username- Filter by user's username (case-insensitive)provider_uuid- Filter by service provider UUIDis_restricted- Filter by restriction status (boolean)created_before/created_after- Filter by creation datemodified_before/modified_after- Filter by modification datequery- General search across offering name, username, and user names
Practical Filtering Examples
Here are common filtering scenarios for managing OfferingUsers:
Find Users Requiring Attention
1 2 3 4 5 6 7 8 9 10 11 | |
Monitor Service Provider Operations
1 2 3 4 5 | |
Audit and Reporting
1 2 3 4 5 | |
Events and Logging
State transitions generate:
- Event logs: Recorded in the system event log for audit purposes
- Application logs: Logged with user attribution for debugging and monitoring
- STOMP messages: Published to the
offering_userqueue for external systems (see Event-Based Order Processing).OfferingUserAttributeConfigalso gates which user profile attributes are included in STOMP event payloads.
User Attribute Exposure Configuration
Waldur supports GDPR-compliant per-offering configuration of which user attributes are exposed to service providers. This allows organizations to declare and control what personal data is shared with each offering.
Overview
The OfferingUserAttributeConfig model allows service provider administrators to configure exactly which user profile attributes are exposed when retrieving OfferingUser data via the API.
flowchart LR
subgraph User Profile
UP[User]
UP --> |has| A1[username]
UP --> |has| A2[full_name]
UP --> |has| A3[email]
UP --> |has| A4[phone_number]
UP --> |has| A5[organization]
UP --> |has| A6[nationality]
UP --> |has| A7[...]
end
subgraph Offering Config
OC[OfferingUserAttributeConfig]
OC --> |expose_username| E1[true]
OC --> |expose_full_name| E2[true]
OC --> |expose_email| E3[true]
OC --> |expose_phone_number| E4[false]
OC --> |expose_nationality| E5[true]
end
subgraph API Response
AR[OfferingUser API]
AR --> |returns| R1[username ✓]
AR --> |returns| R2[full_name ✓]
AR --> |returns| R3[email ✓]
AR --> |filters| R4[phone_number ✗]
AR --> |returns| R5[nationality ✓]
end
UP --> OC
OC --> AR
API Endpoints
Get/Update Attribute Configuration
Endpoint: /api/marketplace-offering-user-attribute-configs/
1 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Update Existing Configuration
1 2 3 4 5 6 7 | |
Available Attributes
| Attribute | Default | Description |
|---|---|---|
expose_username |
true |
User's username |
expose_full_name |
true |
User's full name |
expose_email |
true |
User's email address |
expose_phone_number |
false |
User's phone number |
expose_organization |
false |
User's organization |
expose_job_title |
false |
User's job title |
expose_affiliations |
false |
User's affiliations |
expose_gender |
false |
User's gender (ISO 5218) |
expose_personal_title |
false |
Honorific title |
expose_place_of_birth |
false |
Place of birth |
expose_country_of_residence |
false |
Country of residence |
expose_nationality |
false |
Primary nationality |
expose_nationalities |
false |
All citizenships |
expose_organization_country |
false |
Organization's country |
expose_organization_type |
false |
Organization type (SCHAC URN) |
expose_eduperson_assurance |
false |
REFEDS assurance level |
expose_civil_number |
false |
Civil/national ID number |
expose_birth_date |
false |
Date of birth |
expose_identity_source |
false |
Identity provider source |
Default Behavior
When no OfferingUserAttributeConfig exists for an offering, the system uses the DEFAULT_OFFERING_USER_ATTRIBUTES Constance setting, which defaults to:
1 | |
Staff can configure system-wide defaults via /api-auth/override-db-settings/:
1 2 3 4 5 6 | |
Permissions
- View: Users with
VIEW_OFFERINGpermission on the offering - Create/Update: Offering owner or customer owner
GDPR Compliance
This feature supports GDPR Article 13/14 compliance by:
- Data minimization: Only expose attributes necessary for the service
- Transparency: Configuration is accessible via API for audit
- Purpose limitation: Each offering declares its data processing needs
- Consent integration: Can be linked to
OfferingTermsOfServiceto show users what data is collected