- 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.
94 lines
2.6 KiB
Go
94 lines
2.6 KiB
Go
package migration
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
)
|
|
|
|
// OnboardingHints guides the user-facing migration setup flow.
|
|
type OnboardingHints struct {
|
|
NeedsUserOAuth bool `json:"needs_user_oauth"`
|
|
OAuthProvider string `json:"oauth_provider,omitempty"`
|
|
WaitingForAdmin bool `json:"waiting_for_admin"`
|
|
WaitingReason string `json:"waiting_reason,omitempty"`
|
|
HasMigrationCredentials bool `json:"has_migration_credentials"`
|
|
NeedsMicrosoftAdminConsent bool `json:"needs_microsoft_admin_consent,omitempty"`
|
|
}
|
|
|
|
func (s *Service) BuildOnboardingHints(ctx context.Context, userID string, proj Project, invite Invite) OnboardingHints {
|
|
h := OnboardingHints{
|
|
OAuthProvider: proj.SourceProvider,
|
|
}
|
|
|
|
switch proj.Status {
|
|
case "active", "cutover":
|
|
// worker eligible
|
|
default:
|
|
h.WaitingForAdmin = true
|
|
if proj.Status == "draft" {
|
|
h.WaitingReason = "project_not_activated"
|
|
} else {
|
|
h.WaitingReason = "project_status_" + proj.Status
|
|
}
|
|
}
|
|
|
|
if UsesUserOAuth(proj.SourceProvider, proj.AuthMode) {
|
|
h.NeedsUserOAuth = true
|
|
if userID != "" {
|
|
hasCred, err := s.hasMigrationCredential(ctx, userID, proj.ID, proj.SourceProvider)
|
|
if err == nil {
|
|
h.HasMigrationCredentials = hasCred
|
|
}
|
|
}
|
|
}
|
|
|
|
if proj.SourceProvider == "microsoft" && proj.MicrosoftAdminConsentAt == nil {
|
|
h.NeedsMicrosoftAdminConsent = true
|
|
}
|
|
|
|
if invite.Status == "claimed" && h.WaitingForAdmin {
|
|
h.WaitingReason = waitingReasonMessage(h.WaitingReason)
|
|
}
|
|
|
|
return h
|
|
}
|
|
|
|
func waitingReasonMessage(code string) string {
|
|
switch code {
|
|
case "project_not_activated":
|
|
return "project_not_activated"
|
|
default:
|
|
return code
|
|
}
|
|
}
|
|
|
|
func (s *Service) hasMigrationCredential(ctx context.Context, userID, projectID, provider string) (bool, error) {
|
|
provider = strings.ToLower(strings.TrimSpace(provider))
|
|
if provider == "" {
|
|
return false, nil
|
|
}
|
|
var exists bool
|
|
err := s.db.QueryRow(ctx, `
|
|
SELECT EXISTS(
|
|
SELECT 1 FROM migration_credentials
|
|
WHERE user_id = $1::uuid AND project_id = $2::uuid AND provider = $3 AND revoked_at IS NULL
|
|
)
|
|
`, userID, projectID, provider).Scan(&exists)
|
|
return exists, err
|
|
}
|
|
|
|
func (s *Service) BuildInviteOnboardingHints(proj Project, invite Invite) OnboardingHints {
|
|
h := OnboardingHints{OAuthProvider: proj.SourceProvider}
|
|
if invite.Status == "claimed" {
|
|
h.NeedsUserOAuth = UsesUserOAuth(proj.SourceProvider, proj.AuthMode)
|
|
return h
|
|
}
|
|
if UsesUserOAuth(proj.SourceProvider, proj.AuthMode) {
|
|
h.NeedsUserOAuth = true
|
|
}
|
|
if proj.SourceProvider == "microsoft" && proj.MicrosoftAdminConsentAt == nil {
|
|
h.NeedsMicrosoftAdminConsent = true
|
|
}
|
|
return h
|
|
}
|