keycloak-operator/controllers/keycloak/keycloak.go
2021-01-10 23:49:22 +01:00

99 lines
2.4 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) CreateRealmIfNotExists(ctx context.Context, realm gocloak.RealmRepresentation) error {
_, err := kc.client.CreateRealm(ctx, kc.getToken(), realm)
if isConflict(err) {
log.Printf("Realm '%s' already exists, not updated", *realm.Realm)
return nil
}
return err
}
func (kc *Keycloak) CreateProviderIfNotExists(ctx context.Context, realm string, provider gocloak.IdentityProviderRepresentation) error {
_, err := kc.client.CreateIdentityProvider(ctx, kc.getToken(), realm, provider)
if isConflict(err) {
log.Printf("Provider '%s/%s' already exists, not updated", realm, *provider.Alias)
return nil
}
return err
}
func (kc *Keycloak) CreateClientIfNotExists(ctx context.Context, realm string, c gocloak.Client) error {
_, err := kc.client.CreateClient(ctx, kc.getToken(), realm, c)
if isConflict(err) {
log.Printf("Client '%s/%s' already exists, not updated", realm, *c.ClientID)
return nil
}
return err
}
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
}