Skip to content

Commit

Permalink
feat: external connectors
Browse files Browse the repository at this point in the history
Signed-off-by: m.nabokikh <[email protected]>
  • Loading branch information
nabokihms committed Dec 28, 2021
1 parent c8d55ce commit 56bd6bc
Show file tree
Hide file tree
Showing 18 changed files with 2,306 additions and 16 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ARG GOPROXY

COPY go.mod go.sum ./
COPY api/v2/go.mod api/v2/go.sum ./api/v2/
COPY connector/external/sdk/go.mod connector/external/sdk/go.sum ./connector/external/sdk/
RUN go mod download

COPY . .
Expand Down
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ bin/dex:
@mkdir -p bin/
@go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex

examples: bin/grpc-client bin/example-app
examples: bin/grpc-client bin/example-app bin/external-connector bin/external-gitlab

bin/grpc-client:
@mkdir -p bin/
Expand All @@ -45,6 +45,14 @@ bin/example-app:
@mkdir -p bin/
@cd examples/ && go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/examples/example-app

bin/external-connector:
@mkdir -p bin/
@cd examples/ && go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/examples/external-connector

bin/external-gitlab:
@mkdir -p bin/
@cd examples/ && go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/examples/external-gitlab

.PHONY: release-binary
release-binary: generate
@go build -o /go/bin/dex -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex
Expand Down Expand Up @@ -108,6 +116,7 @@ FORCE:
proto:
@protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. api/v2/*.proto
@protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. api/*.proto
@protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. connector/external/sdk/*.proto
#@cp api/v2/*.proto api/

.PHONY: proto-internal
Expand Down
87 changes: 87 additions & 0 deletions connector/external/callback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package external

import (
"context"
"fmt"
"io"
"net/http"

"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/connector/external/sdk"
"github.com/dexidp/dex/pkg/log"
)

type CallbackConnectorConfig struct {
gRPCConnectorConfig

id string
logger log.Logger

client sdk.CallbackConnectorClient
}

func (c *CallbackConnectorConfig) Open(id string, logger log.Logger) (connector.Connector, error) {
conn, err := grpcConn(c.Port, c.CAPath, c.ClientCrt, c.ClientKey)
if err != nil {
return connector.Identity{}, err
}

c.id = id
c.client = sdk.NewCallbackConnectorClient(conn)
c.logger = logger
return c, nil
}

func (c *CallbackConnectorConfig) LoginURL(scopes connector.Scopes, callbackURL, state string) (string, error) {
response, err := c.client.LoginURL(context.Background(), &sdk.LoginURLReq{
Scopes: toSDKScopes(scopes), CallbackUrl: callbackURL, State: state,
})
if err != nil {
return "", err
}

return response.Url, nil
}

func (c *CallbackConnectorConfig) HandleCallback(scopes connector.Scopes, r *http.Request) (connector.Identity, error) {
body, err := io.ReadAll(r.Body)
if err != nil {
return connector.Identity{}, fmt.Errorf("external connector %q: read body: %v", c.id, err)
}

defer r.Body.Close()

headers := map[string][]string(r.Header)

response, err := c.client.HandleCallback(r.Context(), &sdk.CallbackReq{
Scopes: toSDKScopes(scopes),
Body: body,
Headers: convertToListOfStrings(headers),
RawQuery: r.URL.RawQuery,
})
if err != nil {
return connector.Identity{}, err
}

return toConnectorIdentity(response.Identity), nil
}

func (c *CallbackConnectorConfig) Refresh(ctx context.Context, scopes connector.Scopes, identity connector.Identity) (connector.Identity, error) {
response, err := c.client.Refresh(ctx, &sdk.RefreshReq{
Scopes: toSDKScopes(scopes), Identity: toSDKIdentity(identity),
})
if err != nil {
return connector.Identity{}, err
}

return toConnectorIdentity(response.GetIdentity()), nil
}

func convertToListOfStrings(oldMap map[string][]string) map[string]*sdk.ListOfStrings {
newMap := make(map[string]*sdk.ListOfStrings, len(oldMap))
for k, v := range oldMap {
newMap[k] = &sdk.ListOfStrings{Value: v}
}

return newMap
}
37 changes: 37 additions & 0 deletions connector/external/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package external

import (
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/connector/external/sdk"
)

func toSDKScopes(s connector.Scopes) *sdk.Scopes {
return &sdk.Scopes{
OfflineAccess: s.OfflineAccess,
Groups: s.Groups,
}
}

func toSDKIdentity(id connector.Identity) *sdk.Identity {
return &sdk.Identity{
UserId: id.UserID,
Username: id.Username,
PreferredUsername: id.PreferredUsername,
Email: id.Email,
EmailVerified: id.EmailVerified,
Groups: id.Groups,
ConnectorData: id.ConnectorData,
}
}

func toConnectorIdentity(id *sdk.Identity) connector.Identity {
return connector.Identity{
UserID: id.UserId,
Username: id.Username,
PreferredUsername: id.PreferredUsername,
Email: id.Email,
EmailVerified: id.EmailVerified,
Groups: id.Groups,
ConnectorData: id.ConnectorData,
}
}
50 changes: 50 additions & 0 deletions connector/external/grpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package external

import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"

"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)

type gRPCConnectorConfig struct {
Port int `json:"port"`
CAPath string `json:"tlsClientCA"`
ClientCrt string `json:"tlsCert"`
ClientKey string `json:"tlsKey"`
}

func grpcConn(port int, ca, crt, key string) (*grpc.ClientConn, error) {
cPool := x509.NewCertPool()
caCert, err := os.ReadFile(ca)
if err != nil {
return nil, fmt.Errorf("invalid CA crt file: %s", ca)
}

if !cPool.AppendCertsFromPEM(caCert) {
return nil, fmt.Errorf("failed to parse CA crt")
}

clientCert, err := tls.LoadX509KeyPair(crt, key)
if err != nil {
return nil, fmt.Errorf("invalid client crt file: %s", crt)
}

clientTLSConfig := &tls.Config{
RootCAs: cPool,
Certificates: []tls.Certificate{clientCert},
}
creds := credentials.NewTLS(clientTLSConfig)

hostAndPort := fmt.Sprintf("127.0.0.1:%d", port)

conn, err := grpc.Dial(hostAndPort, grpc.WithTransportCredentials(creds))
if err != nil {
return nil, fmt.Errorf("dial: %v", err)
}

return conn, nil
}
68 changes: 68 additions & 0 deletions connector/external/password.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package external

import (
"context"

"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/connector/external/sdk"
"github.com/dexidp/dex/pkg/log"
)

type PasswordConnectorConfig struct {
gRPCConnectorConfig

id string
logger log.Logger

client sdk.PasswordConnectorClient
}

func (c *PasswordConnectorConfig) Open(id string, logger log.Logger) (connector.Connector, error) {
conn, err := grpcConn(c.Port, c.CAPath, c.ClientCrt, c.ClientKey)
if err != nil {
return connector.Identity{}, err
}

c.id = id
c.client = sdk.NewPasswordConnectorClient(conn)
c.logger = logger
return c, nil
}

func (c *PasswordConnectorConfig) Prompt() string {
prompt := "username"

response, err := c.client.Prompt(context.Background(), &sdk.PromptReq{})
if err != nil {
c.logger.Errorf(err.Error())
return prompt
}

if response.Prompt != "" {
prompt = response.Prompt
}

return prompt
}

func (c *PasswordConnectorConfig) Login(ctx context.Context, scopes connector.Scopes, username, password string) (identity connector.Identity, validPassword bool, err error) {
response, err := c.client.Login(ctx, &sdk.LoginReq{
Scopes: toSDKScopes(scopes), Username: username, Password: password,
})
if err != nil {
return connector.Identity{}, false, err
}

return toConnectorIdentity(response.GetIdentity()), response.GetValidPassword(), nil
}

func (c *PasswordConnectorConfig) Refresh(ctx context.Context, scopes connector.Scopes, identity connector.Identity) (connector.Identity, error) {
response, err := c.client.Refresh(ctx, &sdk.RefreshReq{
Scopes: toSDKScopes(scopes), Identity: toSDKIdentity(identity),
})
if err != nil {
return connector.Identity{}, err
}

return toConnectorIdentity(response.GetIdentity()), nil
}
16 changes: 16 additions & 0 deletions connector/external/sdk/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module github.com/dexidp/dex/connector/external/sdk

go 1.17

require (
google.golang.org/grpc v1.43.0
google.golang.org/protobuf v1.27.1
)

require (
github.com/golang/protobuf v1.5.0 // indirect
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect
golang.org/x/text v0.3.0 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
)
Loading

0 comments on commit 56bd6bc

Please sign in to comment.