ultisuite-backend/internal/migration/microsoft_admin_consent.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

144 lines
3.9 KiB
Go

package migration
import (
"context"
"fmt"
"strings"
)
const adminConsentStatePrefix = "project:"
// MicrosoftAdminConsentRecord stores the outcome of a Microsoft admin consent redirect.
type MicrosoftAdminConsentRecord struct {
TenantID string
ClientID string
ProjectID string
Granted bool
ErrorCode string
ErrorDescription string
}
// MicrosoftAdminConsent is a persisted tenant-level admin consent row.
type MicrosoftAdminConsent struct {
TenantID string `json:"tenant_id"`
ClientID string `json:"client_id"`
ProjectID string `json:"project_id,omitempty"`
Granted bool `json:"granted"`
ErrorCode string `json:"error_code,omitempty"`
ErrorDescription string `json:"error_description,omitempty"`
ConsentedAt string `json:"consented_at"`
UpdatedAt string `json:"updated_at"`
}
func EncodeAdminConsentState(projectID string) string {
projectID = strings.TrimSpace(projectID)
if projectID == "" {
return ""
}
return adminConsentStatePrefix + projectID
}
func ParseAdminConsentProjectID(state string) string {
state = strings.TrimSpace(state)
if !strings.HasPrefix(state, adminConsentStatePrefix) {
return ""
}
return strings.TrimSpace(strings.TrimPrefix(state, adminConsentStatePrefix))
}
func (s *Service) RecordMicrosoftAdminConsent(ctx context.Context, in MicrosoftAdminConsentRecord) error {
if s.db == nil {
return fmt.Errorf("database not configured")
}
tenantID := strings.TrimSpace(in.TenantID)
clientID := strings.TrimSpace(in.ClientID)
projectID := strings.TrimSpace(in.ProjectID)
if tenantID == "" {
return fmt.Errorf("tenant id required")
}
if clientID == "" {
return fmt.Errorf("client id required")
}
tx, err := s.db.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
_, err = tx.Exec(ctx, `
INSERT INTO migration_microsoft_admin_consents (
tenant_id, client_id, project_id, granted, error_code, error_description
) VALUES ($1, $2, NULLIF($3, '')::uuid, $4, $5, $6)
ON CONFLICT (tenant_id, client_id) DO UPDATE SET
project_id = COALESCE(EXCLUDED.project_id, migration_microsoft_admin_consents.project_id),
granted = EXCLUDED.granted,
error_code = EXCLUDED.error_code,
error_description = EXCLUDED.error_description,
consented_at = NOW(),
updated_at = NOW()
`, tenantID, clientID, projectID, in.Granted, in.ErrorCode, in.ErrorDescription)
if err != nil {
return err
}
if projectID != "" {
if in.Granted {
_, err = tx.Exec(ctx, `
UPDATE migration_projects SET
microsoft_tenant_id = $2,
microsoft_admin_consent_at = NOW(),
microsoft_admin_consent_error = '',
updated_at = NOW()
WHERE id = $1::uuid
`, projectID, tenantID)
} else {
errMsg := strings.TrimSpace(in.ErrorDescription)
if errMsg == "" {
errMsg = strings.TrimSpace(in.ErrorCode)
}
_, err = tx.Exec(ctx, `
UPDATE migration_projects SET
microsoft_tenant_id = CASE
WHEN microsoft_tenant_id = '' THEN $2
ELSE microsoft_tenant_id
END,
microsoft_admin_consent_error = $3,
updated_at = NOW()
WHERE id = $1::uuid
`, projectID, tenantID, errMsg)
}
if err != nil {
return err
}
}
return tx.Commit(ctx)
}
func (s *Service) ListMicrosoftAdminConsents(ctx context.Context) ([]MicrosoftAdminConsent, error) {
rows, err := s.db.Query(ctx, `
SELECT tenant_id, client_id, COALESCE(project_id::text, ''), granted,
error_code, error_description, consented_at::text, updated_at::text
FROM migration_microsoft_admin_consents
ORDER BY updated_at DESC
`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []MicrosoftAdminConsent
for rows.Next() {
var row MicrosoftAdminConsent
if err := rows.Scan(
&row.TenantID, &row.ClientID, &row.ProjectID, &row.Granted,
&row.ErrorCode, &row.ErrorDescription, &row.ConsentedAt, &row.UpdatedAt,
); err != nil {
return nil, err
}
out = append(out, row)
}
return out, rows.Err()
}