Initial commit
[ZITADOPER-1]
This commit is contained in:
166
src/pkg/zitadel/zitadel.go
Normal file
166
src/pkg/zitadel/zitadel.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package zitadel
|
||||
|
||||
import (
|
||||
zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1"
|
||||
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/deployment"
|
||||
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/machinekey"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/schema"
|
||||
"google.golang.org/grpc"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/client"
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/management"
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/zitadel"
|
||||
"golang.org/x/oauth2"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
type MachineKey struct {
|
||||
Type string `json:"type"`
|
||||
KeyID string `json:"keyId"`
|
||||
Key string `json:"key"`
|
||||
UserID string `json:"userId"`
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, zitadelCluster *zitadelv1alpha1.ZitadelCluster, refresolver zitadelv1alpha1.RefResolver) (*management.Client, error) {
|
||||
machineKeyData, err := refresolver.SecretKeyRef(ctx, corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: machinekey.MachineKeySecretName(zitadelCluster)}, Key: machinekey.Key}, zitadelCluster.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
api, err := management.NewClient(GetIssuer(zitadelCluster), fmt.Sprintf("%s", GetAPI(zitadelCluster)), []string{oidc.ScopeOpenID, zitadel.ScopeZitadelAPI()}, zitadel.WithInsecure(), zitadel.WithJWTProfileTokenSource(Discover([]byte(machineKeyData), GetAPIUrl(zitadelCluster), GetAuthority(zitadelCluster), GetAPI(zitadelCluster))),
|
||||
zitadel.WithDialOptions(grpc.WithAuthority(GetAuthority(zitadelCluster))),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ERROR CREATING CLIENT: %v", err)
|
||||
}
|
||||
return api, nil
|
||||
}
|
||||
|
||||
func GetAuthority(zitadel *zitadelv1alpha1.ZitadelCluster) string {
|
||||
return fmt.Sprintf("%s:%d", zitadel.Spec.Host, zitadel.Spec.ExternalPort)
|
||||
}
|
||||
|
||||
func GetIssuer(zitadel *zitadelv1alpha1.ZitadelCluster) string {
|
||||
return fmt.Sprintf("http://%s:%d", zitadel.Spec.Host, zitadel.Spec.ExternalPort)
|
||||
}
|
||||
|
||||
func GetAPI(zitadel *zitadelv1alpha1.ZitadelCluster) string {
|
||||
return fmt.Sprintf("%s:%d", deployment.ServiceFQDN(zitadel.ObjectMeta), deployment.ZitadelPort)
|
||||
}
|
||||
|
||||
func GetAPIUrl(zitadel *zitadelv1alpha1.ZitadelCluster) string {
|
||||
return fmt.Sprintf("http://%s:%d", deployment.ServiceFQDN(zitadel.ObjectMeta), deployment.ZitadelPort)
|
||||
}
|
||||
|
||||
type jwtProfileTokenSource struct {
|
||||
clientID string
|
||||
audience []string
|
||||
signer jose.Signer
|
||||
scopes []string
|
||||
httpClient *http.Client
|
||||
tokenEndpoint string
|
||||
host string
|
||||
}
|
||||
|
||||
func Discover(key []byte, discoverUrl string, host string, api string) func(issuer string, scopes []string) (oauth2.TokenSource, error) {
|
||||
return func(issuer string, scopes []string) (oauth2.TokenSource, error) {
|
||||
var machineKeyData MachineKey
|
||||
if err := json.Unmarshal(key, &machineKeyData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signer, err := client.NewSignerFromPrivateKeyByte([]byte(machineKeyData.Key), machineKeyData.KeyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source := &jwtProfileTokenSource{
|
||||
host: host,
|
||||
clientID: machineKeyData.UserID,
|
||||
audience: []string{issuer},
|
||||
signer: signer,
|
||||
scopes: scopes,
|
||||
httpClient: http.DefaultClient,
|
||||
}
|
||||
config, err := GetDiscoveryConfig(discoverUrl, http.DefaultClient, host, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source.tokenEndpoint = config.TokenEndpoint
|
||||
return source, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetDiscoveryConfig(issuer string, httpClient *http.Client, host string, api string, wellKnownUrl ...string) (*oidc.DiscoveryConfiguration, error) {
|
||||
wellKnown := strings.TrimSuffix(issuer, "/") + oidc.DiscoveryEndpoint
|
||||
if len(wellKnownUrl) == 1 && wellKnownUrl[0] != "" {
|
||||
wellKnown = wellKnownUrl[0]
|
||||
}
|
||||
req, err := http.NewRequest("GET", wellKnown, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Host = host
|
||||
discoveryConfig := new(oidc.DiscoveryConfiguration)
|
||||
err = httphelper.HttpRequest(httpClient, req, &discoveryConfig)
|
||||
discoveryConfig.TokenEndpoint = strings.ReplaceAll(discoveryConfig.TokenEndpoint, host, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return discoveryConfig, nil
|
||||
}
|
||||
|
||||
func (j *jwtProfileTokenSource) TokenEndpoint() string {
|
||||
return j.tokenEndpoint
|
||||
}
|
||||
|
||||
func (j *jwtProfileTokenSource) HttpClient() *http.Client {
|
||||
return j.httpClient
|
||||
}
|
||||
|
||||
func (j *jwtProfileTokenSource) Token() (*oauth2.Token, error) {
|
||||
assertion, err := client.SignedJWTProfileAssertion(j.clientID, j.audience, time.Hour, j.signer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token, err := callTokenEndpoint(oidc.NewJWTProfileGrantRequest(assertion, j.scopes...), nil, j, j.host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return token, err
|
||||
}
|
||||
|
||||
var Encoder = func() httphelper.Encoder {
|
||||
e := schema.NewEncoder()
|
||||
e.RegisterEncoder(oidc.SpaceDelimitedArray{}, func(value reflect.Value) string {
|
||||
return value.Interface().(oidc.SpaceDelimitedArray).Encode()
|
||||
})
|
||||
return e
|
||||
}()
|
||||
|
||||
func callTokenEndpoint(request interface{}, authFn interface{}, caller client.TokenEndpointCaller, host string) (newToken *oauth2.Token, err error) {
|
||||
req, err := httphelper.FormRequest(caller.TokenEndpoint(), request, Encoder, authFn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenRes := new(oidc.AccessTokenResponse)
|
||||
|
||||
req.Host = host
|
||||
if err := httphelper.HttpRequest(caller.HttpClient(), req, &tokenRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &oauth2.Token{
|
||||
AccessToken: tokenRes.AccessToken,
|
||||
TokenType: tokenRes.TokenType,
|
||||
RefreshToken: tokenRes.RefreshToken,
|
||||
Expiry: time.Now().UTC().Add(time.Duration(tokenRes.ExpiresIn) * time.Second),
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user