waldur-site-agent

Offering Users and Async User Creation

The Waldur Site Agent provides robust support for managing offering users with asynchronous username generation and state management. This system enables non-blocking user processing and supports complex username generation scenarios through a pluggable backend architecture.

Overview

Offering users represent the relationship between Waldur users and marketplace offerings. The agent handles username generation, state transitions, and integration with backend systems to ensure users can access provisioned resources.

Async User Creation Workflow

State Machine

The async user creation follows a state-based workflow that prevents blocking operations:

stateDiagram-v2
    [*] --> REQUESTED : User requests access
    REQUESTED --> CREATING : begin_creating
    CREATING --> OK : Username set (auto-transition)
    CREATING --> PENDING_ACCOUNT_LINKING : Linking required
    CREATING --> PENDING_ADDITIONAL_VALIDATION : Validation needed
    CREATING --> ERROR_CREATING : BackendError
    ERROR_CREATING --> CREATING : begin_creating (retry)
    ERROR_CREATING --> PENDING_ACCOUNT_LINKING : Linking required
    ERROR_CREATING --> PENDING_ADDITIONAL_VALIDATION : Validation needed
    PENDING_ACCOUNT_LINKING --> OK : set_validation_complete
    PENDING_ADDITIONAL_VALIDATION --> OK : set_validation_complete
    PENDING_ACCOUNT_LINKING --> PENDING_ADDITIONAL_VALIDATION : Cross-transition
    PENDING_ADDITIONAL_VALIDATION --> PENDING_ACCOUNT_LINKING : Cross-transition
    OK --> [*] : User ready for resource access

State Descriptions

Core Components

Main Functions

sync_offering_users()

Entry point function that processes all offering users across configured offerings.

Usage:

uv run waldur_sync_offering_users -c config.yaml

Behavior:

update_offering_users()

Core processing function that handles username generation and state transitions.

Process:

  1. Early validation checks (empty users list, username generation policy)
  2. Username management backend validation (skips if UnknownUsernameManagementBackend)
  3. Efficient user grouping by state (single pass through users)
  4. Processes users in REQUESTED state via _process_requested_users()
  5. Handles users in pending states via _process_pending_users()
  6. Manages state transitions and centralized error handling

New Architecture: The function has been refactored into focused sub-functions:

Username Management Backend System

The agent uses a pluggable backend architecture for username generation, allowing custom implementations for different identity providers and naming conventions.

Backend Validation

The system now includes early validation to skip processing when no valid username management backend is available:

Base Abstract Class

from waldur_site_agent.backend.backends import AbstractUsernameManagementBackend

class CustomUsernameBackend(AbstractUsernameManagementBackend):
    def generate_username(self, offering_user: OfferingUser) -> str:
        """Generate new username based on offering user details."""
        # Custom logic here
        return generated_username

    def get_username(self, offering_user: OfferingUser) -> Optional[str]:
        """Retrieve existing username from local identity provider."""
        # Custom lookup logic here
        return existing_username

    def get_or_create_username(self, offering_user: OfferingUser) -> Optional[str]:
        """Get existing username or create new one if not found."""
        username = self.get_username(offering_user)
        if not username:
            username = self.generate_username(offering_user)
        return username

When Waldur owns the username

A backend does not have to mint usernames. If the identity is Waldur’s — the offering uses a username_generation_policy other than service_provider, and the backend’s job is to write Waldur’s values into an external system — set is_username_authoritative = False:

class MirroringBackend(AbstractUsernameManagementBackend):
    is_username_authoritative = False

Core then skips username generation for that offering entirely, and in particular never PATCHes a username back over the authoritative value. Backends that leave the default (True) are unaffected.

Such a backend does its work in sync_user_profiles(offering_users) instead of generate_username. That hook is called with the full offering-user list on every membership cycle, including accounts already in OK — which the username-generation path never sees, since it only handles accounts still in REQUESTED or a pending state. It is therefore the right place for a reconcile loop: create what is missing, update what has drifted, leave the rest alone.

The POSIX identity Waldur holds for an account is available on the offering user as uidnumber, primarygroup, login_shell and home_directory. These are always requested by the membership processor, and are not gated by the offering’s OfferingUserAttributeConfig — they are account attributes, not personal data.

waldur-site-agent-ldap is the reference implementation; see its account_source: waldur mode.

Plugin Registration

Register your backend via entry points in pyproject.toml:

[project.entry-points."waldur_site_agent.username_management_backends"]
custom_backend = "my_package.backend:CustomUsernameBackend"

Built-in Backends

Configuration

Offering Configuration

Configure username management per offering in your agent configuration:

offerings:
  - name: "SLURM Cluster"
    waldur_api_url: "https://waldur.example.com/api/"
    waldur_api_token: "your-token"
    waldur_offering_uuid: "offering-uuid"
    backend_type: "slurm"
    username_management_backend: "custom_backend"  # References entry point name
    backend_settings:
      # ... other settings

Prerequisites

  1. Service Provider Username Generation: The offering must be configured with username_generation_policy = SERVICE_PROVIDER in Waldur
  2. Backend Plugin: Appropriate username management backend must be installed and configured
  3. Permissions: API token user must have OFFERING.MANAGER role on the offering (grants permissions to manage offering users, orders, and agent identities)

Integration with Order Processing

The async user creation system is seamlessly integrated with the agent’s order processing workflows:

Automatic Processing

Username generation is automatically triggered during:

Implementation in Processors

The OfferingBaseProcessor class provides _update_offering_users() method that:

  1. Calls username generation for users with blank usernames
  2. Refreshes offering user data after processing
  3. Filters users to only include those with valid usernames for resource operations

Example usage in order processing:

# Optimized processing with conditional refresh
offering_users = user_context["offering_users"]

# Only refresh if username generation actually occurred
if self._update_offering_users(offering_users):
    # Refresh local user_context cache
    user_context_new = self._fetch_user_context_for_resource(waldur_resource.uuid.hex)
    user_context.update(user_context_new)

# Use only users with valid usernames
valid_usernames = {
    user.username for user in user_context["offering_users"]
    if user.state == OfferingUserState.OK and user.username
}

Performance Improvements:

Error Handling

Exception Types

The system defines specific exceptions for different error scenarios:

Both linking/validation exceptions support an optional comment_url parameter to provide links to forms, documentation, or other resources needed for error resolution.

Error Recovery

When exceptions occur during username generation:

  1. User state transitions to appropriate pending or error state
  2. Error details are logged with context
  3. Comment field is updated with error message and comment_url field with any provided URL
  4. Processing continues for other users
  5. Pending and error users are retried in subsequent runs

State transition handling by current user state:

Username Reconciliation in Event Processing Mode

When the agent runs in event_process mode, offering user username synchronization is primarily driven by real-time STOMP events. However, transient STOMP disconnections or message loss can cause missed updates. To address this, the main event loop includes a periodic reconciliation timer.

How it works

Reconciliation interval setting

# Environment variable (default: 60 minutes)
WALDUR_SITE_AGENT_RECONCILIATION_PERIOD_MINUTES=60

User Attribute Forwarding

During membership synchronization, the processor can forward user profile attributes to backends that need them (e.g., the Waldur federation backend sends attributes to the Identity Bridge API when resolving remote users).

Attribute resolution flow

Which attributes are forwarded is driven by the offering’s OfferingUserAttributeConfig. Providers configure which user fields are exposed via the Waldur admin UI (e.g., expose_email, expose_organization, expose_gender). The agent:

  1. Fetches the attribute config from the API (cached for 5 minutes).
  2. Requests only the exposed fields when listing offering users.
  3. During user sync, extracts exposed attribute values from each OfferingUser and passes them to the backend via user_attributes.

Supported attributes

All 20+ attributes from OfferingUserAttributeConfig are supported: username, full_name (includes first_name, last_name), email, phone_number, organization, job_title, affiliations, gender, personal_title, place_of_birth, country_of_residence, nationality, nationalities, organization_country, organization_type, organization_registry_code, eduperson_assurance, civil_number, birth_date, identity_source, active_isds.

Fallback behavior

When the attribute config API is unavailable, the agent defaults to exposing username, full_name, and email.

Best Practices

Username Backend Implementation

  1. Idempotent Operations: Ensure get_or_create_username() can be called multiple times safely
  2. Error Handling: Raise appropriate exceptions for recoverable errors
  3. Logging: Include detailed logging for troubleshooting
  4. Validation: Validate generated usernames meet backend system requirements
  5. Performance Considerations: Implement efficient lookup mechanisms to avoid blocking operations
  6. Backend Validation: Return empty strings when username generation is not supported

Deployment Considerations

  1. Regular Sync: Run waldur_sync_offering_users regularly via cron or systemd timer
  2. Monitoring: Monitor pending user states for manual intervention needs
  3. Backup Strategy: Consider username mapping backup for disaster recovery
  4. Testing: Test username generation logic thoroughly before production deployment
  5. Backend Configuration: Ensure proper username_management_backend configuration to avoid UnknownUsernameManagementBackend fallback
  6. Performance Tuning: Monitor processing times and adjust batch sizes if needed
  7. Error Recovery: Set up alerting for persistent pending states that may require manual intervention

Troubleshooting

Diagnostic Commands

# Check system health
uv run waldur_site_diagnostics -c config.yaml

# Manual user sync
uv run waldur_sync_offering_users -c config.yaml

# Check offering user states via API
curl -H "Authorization: Token YOUR_TOKEN" \
  "https://waldur.example.com/api/marketplace-offering-users/?offering_uuid=OFFERING_UUID"