115 lines
3 KiB
Go
115 lines
3 KiB
Go
package supplier
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/Nerzal/gocloak/v11"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type KeycloakSupplier struct {
|
|
host string
|
|
realm string
|
|
username string
|
|
password string
|
|
}
|
|
|
|
func NewKeycloakSupplier(u *url.URL) (KeycloakSupplier, error) {
|
|
if err := validateUrl(u); err != nil {
|
|
return KeycloakSupplier{}, nil
|
|
}
|
|
if u.Path == "" {
|
|
return KeycloakSupplier{}, fmt.Errorf("a realm must be provider")
|
|
}
|
|
username := u.User.Username()
|
|
password, _ := u.User.Password()
|
|
realm := strings.ReplaceAll(u.Path, "/", "")
|
|
u.Path = ""
|
|
u.User = nil
|
|
return KeycloakSupplier{
|
|
username: username,
|
|
password: password,
|
|
realm: realm,
|
|
host: u.String(),
|
|
}, nil
|
|
}
|
|
|
|
type ScimClient struct {
|
|
Name string
|
|
Endpoint string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func (r *KeycloakSupplier) login(ctx context.Context) (gocloak.GoCloak, *gocloak.JWT, error) {
|
|
client := gocloak.NewClient(r.host)
|
|
token, err := client.LoginAdmin(ctx, r.username, r.password, r.realm)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return client, token, nil
|
|
}
|
|
|
|
func (r *KeycloakSupplier) Reconcile(ctx context.Context, scim ScimClient) error {
|
|
client, token, err := r.login(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var component gocloak.Component
|
|
var exists bool
|
|
components, err := client.GetComponents(ctx, token.AccessToken, r.realm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, v := range components {
|
|
if *v.ProviderType == "org.keycloak.storage.UserStorageProvider" && *v.ProviderID == "scim" && *v.Name == scim.Name {
|
|
component = *v
|
|
exists = true
|
|
break
|
|
}
|
|
}
|
|
componentConfig := map[string][]string{
|
|
"endpoint": {scim.Endpoint},
|
|
"auth-mode": {"BASIC_AUTH"},
|
|
"auth-user": {scim.Username},
|
|
"auth-pass": {scim.Password},
|
|
}
|
|
if !exists {
|
|
realm, err := client.GetRealm(ctx, token.AccessToken, r.realm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pId := "scim"
|
|
pType := "org.keycloak.storage.UserStorageProvider"
|
|
providerId := uuid.NewString()
|
|
component = gocloak.Component{
|
|
ID: &providerId,
|
|
ParentID: realm.ID,
|
|
Name: &scim.Name,
|
|
ProviderID: &pId,
|
|
ProviderType: &pType,
|
|
}
|
|
componentConfig["priority"] = []string{"0"}
|
|
componentConfig["content-type"] = []string{"application/json"}
|
|
componentConfig["sync-import"] = []string{"false"}
|
|
componentConfig["sync-import-action"] = []string{"CREATE_LOCAL"}
|
|
componentConfig["propagation-user"] = []string{"true"}
|
|
componentConfig["propagation-group"] = []string{"true"}
|
|
}
|
|
component.ComponentConfig = &componentConfig
|
|
// (*component.ComponentConfig)["endpoint"] = []string{scim.Endpoint}
|
|
// (*component.ComponentConfig)["auth-mode"] = []string{"BASIC_AUTH"}
|
|
// (*component.ComponentConfig)["auth-username"] = []string{scim.Username}
|
|
// (*component.ComponentConfig)["auth-pass"] = []string{scim.Password}
|
|
|
|
if !exists {
|
|
_, err = client.CreateComponent(ctx, token.AccessToken, r.realm, component)
|
|
} else {
|
|
_, err = client.UpdateComponent(ctx, token.AccessToken, r.realm, component)
|
|
}
|
|
return err
|
|
}
|