Fix Microsoft 365 SCIM bridge connection and scope

Add the missing MICROSOFT_365 value to the connector_provider enum
so the connector loader stops failing with SQLSTATE 22P02.

Scope each Identity Provider card to its own SCIMBridge type so
connecting Microsoft 365 no longer marks Google Workspace as
connected (and vice versa).

Filter Microsoft Graph /users to userType eq 'Member' so the bridge
only syncs home-tenant members and skips B2B guest accounts that
were polluting the synced People list.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-05-07 15:11:23 +02:00
committed by Émile Ré
parent d1f34add6f
commit cf53ca3a0d
4 changed files with 49 additions and 12 deletions

View File

@@ -23,6 +23,8 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
scimclient "go.probo.inc/probo/pkg/iam/scim/bridge/client"
@@ -47,6 +49,11 @@ const graphPageSize = 999
// API misbehaves.
const graphMaxPages = 1000
// graphMemberFilter restricts /users to home-tenant members and
// excludes B2B guest users invited from external tenants — those guests
// should not be provisioned into the connected organization.
const graphMemberFilter = "userType eq 'Member'"
var _ provider.Provider = (*Provider)(nil)
type Provider struct {
@@ -102,16 +109,14 @@ type graphUsersResponse struct {
}
func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
url := fmt.Sprintf(
"%s/users?$select=%s&$top=%d",
graphBaseURL,
graphUserSelect,
graphPageSize,
)
endpoint, err := buildListUsersURL()
if err != nil {
return nil, fmt.Errorf("cannot build list users URL: %w", err)
}
var allUsers scimclient.Users
for range graphMaxPages {
users, next, err := p.fetchPage(ctx, url)
users, next, err := p.fetchPage(ctx, endpoint)
if err != nil {
return nil, err
}
@@ -146,14 +151,29 @@ func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
if next == "" {
return allUsers, nil
}
url = next
endpoint = next
}
return nil, fmt.Errorf("microsoft 365: pagination limit of %d pages reached", graphMaxPages)
}
func (p *Provider) fetchPage(ctx context.Context, url string) ([]graphUser, string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
func buildListUsersURL() (string, error) {
u, err := url.Parse(graphBaseURL + "/users")
if err != nil {
return "", fmt.Errorf("cannot parse graph base URL: %w", err)
}
q := u.Query()
q.Set("$select", graphUserSelect)
q.Set("$top", strconv.Itoa(graphPageSize))
q.Set("$filter", graphMemberFilter)
u.RawQuery = q.Encode()
return u.String(), nil
}
func (p *Provider) fetchPage(ctx context.Context, endpoint string) ([]graphUser, string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, "", fmt.Errorf("cannot create graph users request: %w", err)
}