Skip to content

Commit

Permalink
feat: add system api (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
livio-a authored Sep 9, 2022
1 parent 840c3df commit 424ddd5
Show file tree
Hide file tree
Showing 7 changed files with 3,992 additions and 1 deletion.
3 changes: 2 additions & 1 deletion build/zitadel/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ RUN ./build/zitadel/generate-grpc-client.sh ${PROJECT_PATH} \
&& find /go/src/${PROJECT_PATH}/zitadel -iname '*.pb.go' -exec cp --parents \{\} /zitadel-api \; \
&& mv /go/src/github.com/zitadel/zitadel/pkg/grpc/admin/admin_grpc.pb.go /zitadel-api/go/src/${PROJECT_PATH}/zitadel/admin/ \
&& mv /go/src/github.com/zitadel/zitadel/pkg/grpc/auth/auth_grpc.pb.go /zitadel-api/go/src/${PROJECT_PATH}/zitadel/auth/ \
&& mv /go/src/github.com/zitadel/zitadel/pkg/grpc/management/management_grpc.pb.go /zitadel-api/go/src/${PROJECT_PATH}/zitadel/management/
&& mv /go/src/github.com/zitadel/zitadel/pkg/grpc/management/management_grpc.pb.go /zitadel-api/go/src/${PROJECT_PATH}/zitadel/management/ \
&& mv /go/src/github.com/zitadel/zitadel/pkg/grpc/system/system_grpc.pb.go /zitadel-api/go/src/${PROJECT_PATH}/zitadel/system/
#######################
## prepare generated files for output
#######################
Expand Down
1 change: 1 addition & 0 deletions build/zitadel/generate-grpc-client.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protoc \
--go_opt=Mzitadel/policy.proto=${ZITADEL_IMPORT}/policy \
--go_opt=Mzitadel/project.proto=${ZITADEL_IMPORT}/project \
--go_opt=Mzitadel/settings.proto=${ZITADEL_IMPORT}/settings \
--go_opt=Mzitadel/system.proto=${ZITADEL_IMPORT}/system \
--go_opt=Mzitadel/text.proto=${ZITADEL_IMPORT}/text \
--go_opt=Mzitadel/user.proto=${ZITADEL_IMPORT}/user \
--go_opt=Mzitadel/v1.proto=${ZITADEL_IMPORT}/v1 \
Expand Down
50 changes: 50 additions & 0 deletions example/system/system.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"context"
"flag"
"log"

"github.com/zitadel/zitadel-go/v2/pkg/client/system"
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/system"
)

var (
issuer = flag.String("issuer", "", "issuer of your ZITADEL instance (in the form: https://<instance>.zitadel.cloud or https://<yourdomain>)")
api = flag.String("api", "", "gRPC endpoint of your ZITADEL instance (in the form: <instance>.zitadel.cloud:443 or <yourdomain>:443)")
)

func main() {
flag.Parse()

//create a client for the system api providing:
//- issuer (e.g. https://acme-dtfhdg.zitadel.cloud)
//- api (e.g. acme-dtfhdg.zitadel.cloud:443)
//- a JWT Profile source token (e.g. path to your key.pem) and the corresponding userID from the `SystemAPIUsers` config in ZITADEL
client, err := system.NewClient(
*issuer,
*api,
system.JWTProfileFromPath("system_user_1.pem", "system_user_1"),
)
if err != nil {
log.Fatalln("could not create client", err)
}
defer func() {
err := client.Connection.Close()
if err != nil {
log.Println("could not close grpc connection", err)
}
}()

ctx := context.Background()
//call ZITADEL and print the name and creation date of all instances
//the call was successful if no error occurred
resp, err := client.ListInstances(ctx, &pb.ListInstancesRequest{})
if err != nil {
log.Fatalln("call failed: ", err)
}
//print all instances
for _, instance := range resp.GetResult() {
log.Printf("%s was created on %s", instance.Name, instance.Details.CreationDate.AsTime())
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ require (
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
gopkg.in/square/go-jose.v2 v2.6.0
)
106 changes: 106 additions & 0 deletions pkg/client/system/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package system

import (
"io/ioutil"
"net/http"
"time"

"github.com/zitadel/oidc/pkg/client"
"github.com/zitadel/oidc/pkg/oidc"
"golang.org/x/oauth2"
"gopkg.in/square/go-jose.v2"

"github.com/zitadel/zitadel-go/v2/pkg/client/middleware"
"github.com/zitadel/zitadel-go/v2/pkg/client/zitadel"
"github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/system"
)

type Client struct {
Connection *zitadel.Connection
system.SystemServiceClient
}

func NewClient(issuer, api string, source JWTAuthenticationSource, opts ...Option) (*Client, error) {
options := make([]zitadel.Option, len(opts)+1)
options[0] = zitadel.WithJWTProfileTokenSource(source())
for i, opt := range opts {
options[i+1] = opt()
}
conn, err := zitadel.NewConnection(issuer, api, nil, options...)
if err != nil {
return nil, err
}
return &Client{
Connection: conn,
SystemServiceClient: system.NewSystemServiceClient(conn.ClientConn),
}, nil
}

type Option func() zitadel.Option

// WithInsecure disables transport security for the client connection
// use only when absolutely necessary (e.g. local development)
func WithInsecure() func() zitadel.Option {
return func() zitadel.Option {
return zitadel.WithInsecure()
}
}

type JWTAuthenticationSource func() middleware.JWTProfileTokenSource

// JWTProfileFromPath reads the key at the provided path and creates a JWTAuthenticationSource (oauth2.TokensSource)
// to authenticate against the SystemAPI
func JWTProfileFromPath(keyPath, userID string) JWTAuthenticationSource {
return func() middleware.JWTProfileTokenSource {
return func(issuer string, _ []string) (oauth2.TokenSource, error) {
key, err := ioutil.ReadFile(keyPath)
if err != nil {
return nil, err
}
return jwtAuthenticationTokenSource(issuer, userID, key)
}
}
}

// JWTProfileFromKey creates a JWTAuthenticationSource (oauth2.TokensSource) from the provided key (and userID)
// to authenticate against the SystemAPI
func JWTProfileFromKey(key []byte, userID string) JWTAuthenticationSource {
return func() middleware.JWTProfileTokenSource {
return func(issuer string, _ []string) (oauth2.TokenSource, error) {
return jwtAuthenticationTokenSource(issuer, userID, key)
}
}
}

type jwtAuthentication struct {
userID string
audience []string
signer jose.Signer
httpClient *http.Client
}

func jwtAuthenticationTokenSource(issuer, userID string, key []byte) (oauth2.TokenSource, error) {
signer, err := client.NewSignerFromPrivateKeyByte(key, "")
if err != nil {
return nil, err
}
source := &jwtAuthentication{
userID: userID,
audience: []string{issuer},
signer: signer,
httpClient: http.DefaultClient,
}
return source, nil
}

func (j *jwtAuthentication) Token() (*oauth2.Token, error) {
token, err := client.SignedJWTProfileAssertion(j.userID, j.audience, time.Hour, j.signer)
if err != nil {
return nil, err
}
return &oauth2.Token{
AccessToken: token,
TokenType: oidc.BearerToken,
Expiry: time.Now().Add(time.Hour - 5*time.Second),
}, nil
}
Loading

0 comments on commit 424ddd5

Please sign in to comment.