ultisuite-backend/internal/users/provision.go
R3D347HR4Y 7143a36c19
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(mail): integrate Stalwart hosted mail and migration features
- Added configuration options for Stalwart hosted mail in .env.example.
- Updated Docker Compose to include Stalwart service with health checks.
- Introduced new API endpoints for managing mail domains and migration projects.
- Enhanced Authentik blueprints for user enrollment and post-migration security.
- Updated OAuth handling for Google and Microsoft migration processes.
- Improved error handling and response structures in the mail API.
- Added integration tests for email claiming and migration workflows.
2026-06-13 12:47:08 +02:00

65 lines
1.7 KiB
Go

package users
import (
"context"
"fmt"
"strings"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/ultisuite/ulti-backend/internal/auth"
"github.com/ultisuite/ulti-backend/internal/migration"
)
// ProvisionEmail returns the email stored for a newly provisioned user.
func ProvisionEmail(claims *auth.Claims) string {
if claims == nil {
return ""
}
email := strings.TrimSpace(claims.Email)
if email != "" {
return email
}
return claims.Sub + "@unknown.ultimail.local"
}
// EnsureUser inserts or updates the Ultimail user row for OIDC claims and returns the internal UUID.
func EnsureUser(ctx context.Context, db *pgxpool.Pool, claims *auth.Claims) (string, error) {
if db == nil {
return "", fmt.Errorf("database not configured")
}
if claims == nil || strings.TrimSpace(claims.Sub) == "" {
return "", fmt.Errorf("missing subject claim")
}
email := ProvisionEmail(claims)
name := strings.TrimSpace(claims.Name)
var userCount int64
if err := db.QueryRow(ctx, `SELECT COUNT(*) FROM users`).Scan(&userCount); err != nil {
return "", fmt.Errorf("count users: %w", err)
}
isFirstUser := userCount == 0
var userID string
err := db.QueryRow(ctx, `
INSERT INTO users (external_id, email, name, platform_admin)
VALUES ($1, $2, $3, $4)
ON CONFLICT (external_id) DO UPDATE SET
email = EXCLUDED.email,
name = EXCLUDED.name,
updated_at = NOW()
RETURNING id
`, claims.Sub, email, name, isFirstUser).Scan(&userID)
if err != nil {
return "", fmt.Errorf("provision user: %w", err)
}
if isFirstUser {
if err := bootstrapFirstUserAdmin(ctx, db, userID); err != nil {
return "", fmt.Errorf("bootstrap platform admin: %w", err)
}
}
_ = migration.LinkHostedMailboxByEmail(ctx, db, userID, email)
return userID, nil
}