Exclude users from google workspace bridge

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-02-09 17:13:32 +01:00
parent 96f1ca8621
commit 1494707cb5
19 changed files with 826 additions and 43 deletions

View File

@@ -28,10 +28,11 @@ import (
type (
Bridge struct {
provider provider.Provider
scimClient *scimclient.Client
forceUpdate bool
dryRun bool
provider provider.Provider
scimClient *scimclient.Client
excludedUserNames []string
forceUpdate bool
dryRun bool
}
Option func(*Bridge)
@@ -49,6 +50,12 @@ func WithForceUpdate(forceUpdate bool) Option {
}
}
func WithExcludedUserNames(excludedUserNames []string) Option {
return func(s *Bridge) {
s.excludedUserNames = excludedUserNames
}
}
func NewBridge(provider provider.Provider, scimClient *scimclient.Client, opts ...Option) *Bridge {
s := &Bridge{
provider: provider,
@@ -62,15 +69,15 @@ func NewBridge(provider provider.Provider, scimClient *scimclient.Client, opts .
return s
}
func (s *Bridge) Run(ctx context.Context) (created, updated, deactivated, skipped int, err error) {
func (s *Bridge) Run(ctx context.Context) (created, updated, deleted, deactivated, skipped int, err error) {
providerUsers, err := s.provider.ListUsers(ctx)
if err != nil {
return 0, 0, 0, 0, fmt.Errorf("cannot list provider users: %w", err)
return 0, 0, 0, 0, 0, fmt.Errorf("cannot list provider users: %w", err)
}
scimUsers, err := s.scimClient.ListUsers(ctx)
if err != nil {
return 0, 0, 0, 0, fmt.Errorf("cannot list scim users: %w", err)
return 0, 0, 0, 0, 0, fmt.Errorf("cannot list scim users: %w", err)
}
scimUsersByEmail := make(map[string]*scimclient.User)
@@ -125,6 +132,17 @@ func (s *Bridge) Run(ctx context.Context) (created, updated, deactivated, skippe
continue
}
if s.isExcluded(email) {
if !s.dryRun {
if err := s.scimClient.DeleteUser(ctx, scimUser.ID); err != nil {
errs = append(errs, fmt.Errorf("cannot delete user %q: %w", email, err))
continue
}
}
deleted++
continue
}
if !scimUser.Active {
continue
}
@@ -138,5 +156,14 @@ func (s *Bridge) Run(ctx context.Context) (created, updated, deactivated, skippe
deactivated++
}
return created, updated, deactivated, skipped, errors.Join(errs...)
return created, updated, deleted, deactivated, skipped, errors.Join(errs...)
}
func (s *Bridge) isExcluded(email string) bool {
for _, excluded := range s.excludedUserNames {
if strings.EqualFold(excluded, email) {
return true
}
}
return false
}

View File

@@ -248,6 +248,29 @@ func (c *Client) DeactivateUser(ctx context.Context, userID string) error {
return nil
}
func (c *Client) DeleteUser(ctx context.Context, userID string) error {
reqURL := fmt.Sprintf("%s/Users/%s", c.endpoint, url.PathEscape(userID))
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, reqURL, nil)
if err != nil {
return fmt.Errorf("cannot create request: %w", err)
}
c.setHeaders(req)
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("cannot delete user: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusNotFound {
respBody, _ := io.ReadAll(resp.Body)
return fmt.Errorf("SCIM API error: status %d, body: %s", resp.StatusCode, string(respBody))
}
return nil
}
func (c *Client) setHeaders(req *http.Request) {
req.Header.Set("Authorization", "Bearer "+c.token)
req.Header.Set("Accept", "application/scim+json")

View File

@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"net/http"
"strings"
admin "google.golang.org/api/admin/directory/v1"
"google.golang.org/api/option"
@@ -31,12 +32,14 @@ import (
var _ provider.Provider = (*Provider)(nil)
type Provider struct {
httpClient *http.Client
httpClient *http.Client
excludedUserNames []string
}
func New(httpClient *http.Client) *Provider {
func New(httpClient *http.Client, excludedUserNames []string) *Provider {
return &Provider{
httpClient: httpClient,
httpClient: httpClient,
excludedUserNames: excludedUserNames,
}
}
@@ -44,6 +47,16 @@ func (p *Provider) Name() string {
return "google-workspace"
}
func (p *Provider) isExcluded(email string) bool {
emailLower := strings.ToLower(email)
for _, excluded := range p.excludedUserNames {
if strings.ToLower(excluded) == emailLower {
return true
}
}
return false
}
func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
adminService, err := admin.NewService(ctx, option.WithHTTPClient(p.httpClient))
if err != nil {
@@ -65,6 +78,10 @@ func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
}
for _, u := range resp.Users {
if p.isExcluded(u.PrimaryEmail) {
continue
}
allUsers = append(
allUsers,
scimclient.User{