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

130 lines
4.3 KiB
Go

//go:build integration
package migration_test
import (
"context"
"testing"
"github.com/google/uuid"
"github.com/ultisuite/ulti-backend/internal/integrationtest"
"github.com/ultisuite/ulti-backend/internal/users"
)
func TestClaimInviteFlexibleEmailMatch(t *testing.T) {
h := integrationtest.RequireHarness(t)
ctx := context.Background()
adminClient, adminClaims := integrationtest.RequireAdminClient(t, h)
if _, err := users.EnsureUser(ctx, h.Pool, adminClaims); err != nil {
t.Fatalf("ensure admin: %v", err)
}
if err := users.GrantPlatformAdmin(ctx, h.Pool, adminClaims.Sub); err != nil {
t.Fatalf("grant admin: %v", err)
}
createResp, err := adminClient.Post("/api/v1/admin/migration/projects", map[string]any{
"name": "Flexible email match",
"source_provider": "microsoft",
})
integrationtest.FailIf(err, t, "create project")
integrationtest.FailUnlessStatus(t, createResp, 201)
var created struct {
ID string `json:"id"`
}
integrationtest.DecodeJSON(t, createResp, &created)
actResp, err := adminClient.Post("/api/v1/admin/migration/projects/"+created.ID+"/activate", nil)
integrationtest.FailIf(err, t, "activate project")
integrationtest.FailUnlessStatus(t, actResp, 200)
inviteEmail := "alice-" + uuid.NewString()[:8] + "@example.com"
ssoEmail := "alice.sso-" + uuid.NewString()[:8] + "@example.com"
inviteResp, err := adminClient.Post("/api/v1/admin/migration/projects/"+created.ID+"/invites", map[string]any{
"email": inviteEmail,
"alternate_emails": []string{ssoEmail},
})
integrationtest.FailIf(err, t, "create invite")
integrationtest.FailUnlessStatus(t, inviteResp, 201)
var invite struct {
Token string `json:"token"`
}
integrationtest.DecodeJSON(t, inviteResp, &invite)
migrateeClaims := integrationtest.RegularUser(integrationtest.NewExternalID("flex-claim"))
migrateeClaims.Email = ssoEmail
migrateeClaims.PreferredUsername = inviteEmail
migrateeClient, err := h.Client(migrateeClaims)
integrationtest.FailIf(err, t, "migratee client")
if _, err := users.EnsureUser(ctx, h.Pool, migrateeClaims); err != nil {
t.Fatalf("ensure migratee: %v", err)
}
claimResp, err := migrateeClient.Post("/api/v1/migration/claim", map[string]string{
"token": invite.Token,
"password": "test-password-123",
})
integrationtest.FailIf(err, t, "claim invite")
integrationtest.FailUnlessStatus(t, claimResp, 200)
}
func TestClaimInviteRejectsEmailMismatch(t *testing.T) {
h := integrationtest.RequireHarness(t)
ctx := context.Background()
adminClient, adminClaims := integrationtest.RequireAdminClient(t, h)
if _, err := users.EnsureUser(ctx, h.Pool, adminClaims); err != nil {
t.Fatalf("ensure admin: %v", err)
}
if err := users.GrantPlatformAdmin(ctx, h.Pool, adminClaims.Sub); err != nil {
t.Fatalf("grant admin: %v", err)
}
createResp, err := adminClient.Post("/api/v1/admin/migration/projects", map[string]any{
"name": "Reject mismatch",
"source_provider": "google",
})
integrationtest.FailIf(err, t, "create project")
integrationtest.FailUnlessStatus(t, createResp, 201)
var created struct {
ID string `json:"id"`
}
integrationtest.DecodeJSON(t, createResp, &created)
actResp, err := adminClient.Post("/api/v1/admin/migration/projects/"+created.ID+"/activate", nil)
integrationtest.FailIf(err, t, "activate project")
integrationtest.FailUnlessStatus(t, actResp, 200)
inviteEmail := "victim-" + uuid.NewString() + "@example.com"
inviteResp, err := adminClient.Post("/api/v1/admin/migration/projects/"+created.ID+"/invites", map[string]string{
"email": inviteEmail,
})
integrationtest.FailIf(err, t, "create invite")
integrationtest.FailUnlessStatus(t, inviteResp, 201)
var invite struct {
Token string `json:"token"`
}
integrationtest.DecodeJSON(t, inviteResp, &invite)
attackerClaims := integrationtest.RegularUser(integrationtest.NewExternalID("flex-claim-bad"))
attackerClaims.Email = "attacker-" + uuid.NewString() + "@example.com"
attackerClient, err := h.Client(attackerClaims)
integrationtest.FailIf(err, t, "attacker client")
if _, err := users.EnsureUser(ctx, h.Pool, attackerClaims); err != nil {
t.Fatalf("ensure attacker: %v", err)
}
claimResp, err := attackerClient.Post("/api/v1/migration/claim", map[string]string{
"token": invite.Token,
})
integrationtest.FailIf(err, t, "claim invite")
integrationtest.AssertErrorCode(t, claimResp, 400, "email_mismatch")
}