Soft-fail Microsoft 365 MFA when registration fetch fails

AuditLog.Read.All is not available on every tenant. Keep importing
accounts with MFA unknown and log the error instead of failing the
whole source fetch.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
This commit is contained in:
Sacha Al Himdani
2026-07-31 15:32:49 +02:00
parent e8e5e9bf9d
commit d8c284399b
3 changed files with 125 additions and 5 deletions

View File

@@ -22,11 +22,15 @@ package drivers
import (
"context"
"io"
"net/http"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
)
@@ -36,7 +40,7 @@ func TestMicrosoft365Driver(t *testing.T) {
rec := newRecorder(t, "testdata/microsoft_365", "MICROSOFT_365_TOKEN")
client := newVCRClient(rec, bearerAuth(os.Getenv("MICROSOFT_365_TOKEN")))
driver := NewMicrosoft365Driver(client)
driver := NewMicrosoft365Driver(client, log.NewLogger(log.WithName("test")))
records, err := driver.ListAccounts(context.Background())
require.NoError(t, err)
require.Len(t, records, 4)
@@ -83,3 +87,105 @@ func TestMicrosoft365Driver(t *testing.T) {
require.NotNil(t, dana.Active)
assert.False(t, *dana.Active)
}
func TestMicrosoft365Driver_MFAFetchFailureLeavesUnknown(t *testing.T) {
t.Parallel()
client := &http.Client{
Transport: roundTripperFunc(func(r *http.Request) (*http.Response, error) {
header := http.Header{"Content-Type": []string{"application/json"}}
switch {
case strings.HasSuffix(r.URL.Path, "/directoryRoles"):
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(strings.NewReader(`{"value":[]}`)),
}, nil
case strings.HasSuffix(r.URL.Path, "/users"):
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(strings.NewReader(`{
"value":[{
"id":"user-1",
"userPrincipalName":"alice@example.com",
"mail":"alice@example.com",
"displayName":"Alice",
"accountEnabled":true
}]
}`)),
}, nil
case strings.Contains(r.URL.Path, "userRegistrationDetails"):
return &http.Response{
StatusCode: http.StatusForbidden,
Header: header,
Body: io.NopCloser(strings.NewReader(`{
"error":{
"code":"Authentication_RequestFromNonPremiumTenantOrB2CTenant",
"message":"Tenant is not a B2C tenant and doesn't have premium license"
}
}`)),
}, nil
default:
return &http.Response{
StatusCode: http.StatusNotFound,
Header: header,
Body: io.NopCloser(strings.NewReader(`{}`)),
}, nil
}
}),
}
driver := NewMicrosoft365Driver(client, log.NewLogger(log.WithName("test")))
records, err := driver.ListAccounts(context.Background())
require.NoError(t, err)
require.Len(t, records, 1)
assert.Equal(t, "alice@example.com", records[0].Email)
assert.Equal(t, coredata.MFAStatusUnknown, records[0].MFAStatus)
}
func TestMicrosoft365Driver_MFAFetchCanceledPropagates(t *testing.T) {
t.Parallel()
client := &http.Client{
Transport: roundTripperFunc(func(r *http.Request) (*http.Response, error) {
header := http.Header{"Content-Type": []string{"application/json"}}
switch {
case strings.HasSuffix(r.URL.Path, "/directoryRoles"):
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(strings.NewReader(`{"value":[]}`)),
}, nil
case strings.HasSuffix(r.URL.Path, "/users"):
return &http.Response{
StatusCode: http.StatusOK,
Header: header,
Body: io.NopCloser(strings.NewReader(`{
"value":[{
"id":"user-1",
"userPrincipalName":"alice@example.com",
"mail":"alice@example.com",
"displayName":"Alice",
"accountEnabled":true
}]
}`)),
}, nil
case strings.Contains(r.URL.Path, "userRegistrationDetails"):
return nil, context.Canceled
default:
return &http.Response{
StatusCode: http.StatusNotFound,
Header: header,
Body: io.NopCloser(strings.NewReader(`{}`)),
}, nil
}
}),
}
driver := NewMicrosoft365Driver(client, log.NewLogger(log.WithName("test")))
_, err := driver.ListAccounts(context.Background())
require.ErrorIs(t, err, context.Canceled)
}