keycloak-operator/controllers/keycloak/keycloak.go

94 lines
2.3 KiB
Go

package keycloak
import (
"context"
"log"
"sync"
"time"
"github.com/Nerzal/gocloak/v7"
)
// Keycloak contains all information to administrate keycloak
// and stay logged in
type Keycloak struct {
mutex sync.Mutex
client gocloak.GoCloak
accessToken string
validUntil time.Time
user, pass, realm string
}
func (kc *Keycloak) CreateRealm(ctx context.Context, realm gocloak.RealmRepresentation) error {
_, err := kc.client.CreateRealm(ctx, kc.getToken(), realm)
return err
}
func (kc *Keycloak) UpdateRealm(ctx context.Context, realm gocloak.RealmRepresentation) error {
return kc.client.UpdateRealm(ctx, kc.getToken(), realm)
}
func (kc *Keycloak) CreateProvider(ctx context.Context, realm string, provider gocloak.IdentityProviderRepresentation) error {
_, err := kc.client.CreateIdentityProvider(ctx, kc.getToken(), realm, provider)
return err
}
func (kc *Keycloak) CreateClient(ctx context.Context, realm string, c gocloak.Client) error {
_, err := kc.client.CreateClient(ctx, kc.getToken(), realm, c)
return err
}
func (kc *Keycloak) UpdateClient(ctx context.Context, realm string, c gocloak.Client) error {
return kc.client.UpdateClient(ctx, kc.getToken(), realm, c)
}
func New(url, user, pass, realm string) (*Keycloak, error) {
kc := &Keycloak{}
kc.client = gocloak.NewClient(url)
kc.user = user
kc.pass = pass
kc.realm = realm
jwt, err := kc.createToken()
kc.accessToken = jwt.AccessToken
kc.validUntil = time.Now().Add(time.Duration(jwt.ExpiresIn) * time.Second)
return kc, err
}
func (kc *Keycloak) getToken() string {
kc.mutex.Lock()
defer kc.mutex.Unlock()
// If token is not valid 30 seconds in the future,
// we need to create a new one
if time.Now().Add(30 * time.Second).After(kc.validUntil) {
jwt, err := kc.createToken()
if err != nil {
log.Fatalf("Valid credentials became invalid: %s", err)
}
kc.accessToken = jwt.AccessToken
kc.validUntil = time.Now().Add(time.Duration(jwt.ExpiresIn) * time.Second)
}
return kc.accessToken
}
func (kc *Keycloak) createToken() (*gocloak.JWT, error) {
token, err := kc.client.LoginAdmin(context.Background(),
kc.user, kc.pass, kc.realm)
if err != nil {
return nil, err
}
return token, nil
}
func isConflict(err error) bool {
if e, ok := err.(*gocloak.APIError); ok {
return (e.Code == 409)
}
return false
}