- 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.
142 lines
3.7 KiB
Go
142 lines
3.7 KiB
Go
package migration
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestNormalizeAuthModeMicrosoftApp(t *testing.T) {
|
|
if got := NormalizeAuthMode("microsoft", "microsoft_app"); got != AuthModeMicrosoftApp {
|
|
t.Fatalf("got %q", got)
|
|
}
|
|
if got := NormalizeAuthMode("google", "microsoft_app"); got != AuthModeOAuth {
|
|
t.Fatalf("google ignores ms app: got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestUsesUserOAuth(t *testing.T) {
|
|
if UsesUserOAuth("google", AuthModeGoogleDWD) {
|
|
t.Fatal("google dwd should skip user oauth")
|
|
}
|
|
if UsesUserOAuth("microsoft", AuthModeMicrosoftApp) {
|
|
t.Fatal("microsoft app should skip user oauth")
|
|
}
|
|
if !UsesUserOAuth("microsoft", "oauth") {
|
|
t.Fatal("microsoft oauth needs user oauth")
|
|
}
|
|
}
|
|
|
|
func TestGraphUserBase(t *testing.T) {
|
|
if got := graphUserBase(""); got != "/v1.0/me" {
|
|
t.Fatalf("empty upn: %q", got)
|
|
}
|
|
if got := graphUserBase("alice@contoso.com"); got != "/v1.0/users/alice@contoso.com" {
|
|
t.Fatalf("encoded upn: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestNewMicrosoftAppEmpty(t *testing.T) {
|
|
app, err := NewMicrosoftApp(MicrosoftAppConfig{})
|
|
if err != nil || app != nil {
|
|
t.Fatalf("empty config: app=%v err=%v", app, err)
|
|
}
|
|
}
|
|
|
|
func TestMicrosoftAppAccessToken(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
t.Fatalf("method %s", r.Method)
|
|
}
|
|
if err := r.ParseForm(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if r.Form.Get("grant_type") != "client_credentials" {
|
|
t.Fatalf("grant_type=%q", r.Form.Get("grant_type"))
|
|
}
|
|
auth := r.Header.Get("Authorization")
|
|
if !strings.HasPrefix(auth, "Basic ") {
|
|
t.Fatalf("expected basic auth, got %q", auth)
|
|
}
|
|
if !strings.Contains(r.Form.Get("scope"), "graph.microsoft.com/.default") {
|
|
t.Fatalf("scope=%q", r.Form.Get("scope"))
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = io.WriteString(w, `{"access_token":"app-token","token_type":"Bearer","expires_in":3600}`)
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
app, err := NewMicrosoftApp(MicrosoftAppConfig{
|
|
ClientID: "client-id",
|
|
ClientSecret: "client-secret",
|
|
TokenURL: srv.URL,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
token, err := app.AccessToken(t.Context(), "tenant-123")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if token != "app-token" {
|
|
t.Fatalf("token=%q", token)
|
|
}
|
|
}
|
|
|
|
func TestMicrosoftAppRequiresTenant(t *testing.T) {
|
|
app, err := NewMicrosoftApp(MicrosoftAppConfig{
|
|
ClientID: "client-id",
|
|
ClientSecret: "client-secret",
|
|
TokenURL: "http://example.invalid/token",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := app.AccessToken(t.Context(), ""); err == nil {
|
|
t.Fatal("expected tenant required error")
|
|
}
|
|
}
|
|
|
|
func TestWorkerAuthPathSelection(t *testing.T) {
|
|
cases := []struct {
|
|
provider string
|
|
authMode string
|
|
userOAuth bool
|
|
}{
|
|
{"google", AuthModeOAuth, true},
|
|
{"google", AuthModeGoogleDWD, false},
|
|
{"microsoft", AuthModeOAuth, true},
|
|
{"microsoft", AuthModeMicrosoftApp, false},
|
|
{"microsoft", "google_dwd", true},
|
|
}
|
|
for _, tc := range cases {
|
|
got := UsesUserOAuth(tc.provider, tc.authMode)
|
|
if got != tc.userOAuth {
|
|
t.Fatalf("%s/%s: got userOAuth=%v want %v", tc.provider, tc.authMode, got, tc.userOAuth)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMicrosoftAppTokenError(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
_ = json.NewEncoder(w).Encode(map[string]string{"error": "invalid_client"})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
app, err := NewMicrosoftApp(MicrosoftAppConfig{
|
|
ClientID: "bad",
|
|
ClientSecret: "bad",
|
|
TokenURL: srv.URL,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := app.AccessToken(t.Context(), "tenant"); err == nil {
|
|
t.Fatal("expected token error")
|
|
}
|
|
}
|