Add bridge backend for sync

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-02-01 21:22:31 +01:00
parent bc5bbdae81
commit 3d4b215b8f
19 changed files with 1038 additions and 145 deletions

View File

@@ -0,0 +1,87 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// Package googleworkspace provides a Google Workspace identity provider
// for SCIM synchronization using OAuth2.
package googleworkspace
import (
"context"
"fmt"
"net/http"
admin "google.golang.org/api/admin/directory/v1"
"google.golang.org/api/option"
scimclient "go.probo.inc/probo/pkg/iam/scim/bridge/client"
"go.probo.inc/probo/pkg/iam/scim/bridge/provider"
)
var _ provider.Provider = (*Provider)(nil)
type Provider struct {
httpClient *http.Client
}
func New(httpClient *http.Client) *Provider {
return &Provider{
httpClient: httpClient,
}
}
func (p *Provider) Name() string {
return "google-workspace"
}
func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) {
adminService, err := admin.NewService(ctx, option.WithHTTPClient(p.httpClient))
if err != nil {
return nil, fmt.Errorf("cannot create admin service: %w", err)
}
var allUsers scimclient.Users
pageToken := ""
for {
call := adminService.Users.List().Customer("my_customer").MaxResults(500).Context(ctx)
if pageToken != "" {
call = call.PageToken(pageToken)
}
resp, err := call.Do()
if err != nil {
return nil, fmt.Errorf("cannot list users: %w", err)
}
for _, u := range resp.Users {
allUsers = append(
allUsers,
scimclient.User{
UserName: u.PrimaryEmail,
DisplayName: u.Name.FullName,
GivenName: u.Name.GivenName,
FamilyName: u.Name.FamilyName,
Active: !u.Suspended && !u.Archived,
},
)
}
pageToken = resp.NextPageToken
if pageToken == "" {
break
}
}
return allUsers, nil
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// Package provider defines the interface for identity providers that can be
// used as a source of truth for SCIM synchronization.
package provider
import (
"context"
scimclient "go.probo.inc/probo/pkg/iam/scim/bridge/client"
)
type Provider interface {
Name() string
ListUsers(ctx context.Context) (scimclient.Users, error)
}