2021-01-10 23:49:22 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-01-11 06:32:28 +01:00
|
|
|
func (kc *Keycloak) CreateRealm(ctx context.Context, realm gocloak.RealmRepresentation) error {
|
2021-01-10 23:49:22 +01:00
|
|
|
_, err := kc.client.CreateRealm(ctx, kc.getToken(), realm)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-11 06:32:28 +01:00
|
|
|
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 {
|
2021-01-10 23:49:22 +01:00
|
|
|
_, err := kc.client.CreateIdentityProvider(ctx, kc.getToken(), realm, provider)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-11 06:32:28 +01:00
|
|
|
func (kc *Keycloak) CreateClient(ctx context.Context, realm string, c gocloak.Client) error {
|
2021-01-10 23:49:22 +01:00
|
|
|
_, err := kc.client.CreateClient(ctx, kc.getToken(), realm, c)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-11 06:32:28 +01:00
|
|
|
func (kc *Keycloak) UpdateClient(ctx context.Context, realm string, c gocloak.Client) error {
|
|
|
|
return kc.client.UpdateClient(ctx, kc.getToken(), realm, c)
|
|
|
|
}
|
|
|
|
|
2021-01-10 23:49:22 +01:00
|
|
|
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
|
|
|
|
}
|