Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8faed6e
new proto accessors for idp well-known info on SDK
jakedoublev Aug 15, 2024
709d197
remove condition no longer true since tokenEndpoint can be fetched fr…
jakedoublev Aug 16, 2024
63ae9c0
add public client to keycloak_data.yaml
jakedoublev Aug 16, 2024
4e71ac0
clean up keycloak provisioning
jakedoublev Aug 16, 2024
7cb6ec9
WIP
jakedoublev Aug 16, 2024
1f0fec1
set public_client_id from config to idp well-known configuration and …
jakedoublev Aug 16, 2024
c8b7549
handle access token source in separate PR
jakedoublev Aug 16, 2024
3f29f20
Merge branch 'main' into feat/cli-auth
jakedoublev Aug 16, 2024
2d18f54
lint
jakedoublev Aug 16, 2024
ace5c4f
lint
jakedoublev Aug 16, 2024
1ffe6d4
Merge branch 'main' into feat/cli-auth
jakedoublev Aug 16, 2024
4201591
Merge branch 'main' into feat/cli-auth
jakedoublev Aug 16, 2024
debcdbc
put back platform_issuer to avoid breaking java sdk
jakedoublev Aug 16, 2024
012cf09
expose idp accessors of well-known off platform config rather than to…
jakedoublev Aug 16, 2024
505671d
put back token_endpoint fetching if not provided
jakedoublev Aug 16, 2024
1d6ebb1
add comment
jakedoublev Aug 16, 2024
0d841be
use underscores for public_client_id in config
jakedoublev Aug 19, 2024
e667fbd
Merge branch 'main' into feat/cli-auth
jakedoublev Aug 19, 2024
a6a0c7c
use deprecated variable name
jakedoublev Aug 19, 2024
e66964f
comment for redirect uri usage
jakedoublev Aug 19, 2024
3371f23
cleanup
jakedoublev Aug 19, 2024
45130df
put back lib/fixtures
jakedoublev Aug 19, 2024
a58a2c0
Merge branch 'main' into feat/cli-auth
jakedoublev Aug 19, 2024
b729e00
yaml fmt
jakedoublev Aug 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions lib/fixtures/keycloak.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e
testingOnlyRoleName := "opentdf-testing-role"
opentdfERSClientID := "tdf-entity-resolution"
opentdfAuthorizationClientID := "tdf-authorization-svc"
opentdfPublicClientID := "opentdf-public"
realmMangementClientName := "realm-management"

protocolMappers := []gocloak.ProtocolMapperRepresentation{
Expand Down Expand Up @@ -305,6 +306,21 @@ func SetupKeycloak(ctx context.Context, kcConnectParams KeycloakConnectParams) e
return err
}

// Create OpenTDF Public Client
_, err = createClient(ctx, client, token, &kcConnectParams, gocloak.Client{
ClientID: gocloak.StringP(opentdfPublicClientID),
Enabled: gocloak.BoolP(true),
Name: gocloak.StringP(opentdfPublicClientID),
PublicClient: gocloak.BoolP(true),
ProtocolMappers: &protocolMappers,
// TODO: take via CLI arguments?
// Note: otdfctl CLI auth code flow runs from localhost:9000)
RedirectURIs: &[]string{"http://localhost:9000/*"},
}, nil, nil)
if err != nil {
return err
}
Comment thread
jakedoublev marked this conversation as resolved.
Outdated

// opentdfSdkClientNumericId, err := getIDOfClient(ctx, client, token, &kcConnectParams, &opentdfClientId)
// if err != nil {
// slog.Error(fmt.Sprintf("Error getting the SDK id: %s", err))
Expand Down
2 changes: 1 addition & 1 deletion sdk/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func WithTokenEndpoint(tokenEndpoint string) Option {
}
}

Comment thread
jakedoublev marked this conversation as resolved.
func withCustomAccessTokenSource(a auth.AccessTokenSource) Option {
func WithCustomAccessTokenSource(a auth.AccessTokenSource) Option {
return func(c *config) {
c.customAccessTokenSource = a
}
Expand Down
41 changes: 37 additions & 4 deletions sdk/platformconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,43 @@ package sdk

import "log/slog"

func (s SDK) PlatformIssuer() string {
value, ok := s.config.platformConfiguration["platform_issuer"].(string)


func (s SDK) getIdpConfig() map[string]interface{} {
idpCfg, err := s.config.platformConfiguration["idp"].(map[string]interface{})
if !err {
slog.Warn("idp configuration not found in well-known configuration")
idpCfg = map[string]interface{}{}
}
return idpCfg
}

func (s SDK) PlatformIssuer() (string, error) {
idpCfg := s.getIdpConfig()
value, ok := idpCfg["issuer"].(string)
if !ok {
slog.Warn("issuer not found in well-known idp configuration")
return "", ErrPlatformIssuerNotFound
}
return value, nil
}

func (s SDK) PlatformAuthzEndpoint() (string, error) {
idpCfg := s.getIdpConfig()
value, ok := idpCfg["authorization_endpoint"].(string)
if !ok {
slog.Warn("authorization_endpoint not found in well-known idp configuration")
return "", ErrPlatformAuthzEndpointNotFound
}
return value, nil
}

func (s SDK) PlatformTokenEndpoint() (string, error) {
idpCfg := s.getIdpConfig()
value, ok := idpCfg["token_endpoint"].(string)
if !ok {
slog.Warn("platform_issuer not found in platform configuration")
slog.Warn("token_endpoint not found in well-known idp configuration")
return "", ErrPlatformTokenEndpointNotFound
}
return value
return value, nil
}
30 changes: 15 additions & 15 deletions sdk/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"net/url"
Expand All @@ -35,10 +34,13 @@ import (
const (
// Failure while connecting to a service.
// Check your configuration and/or retry.
ErrGrpcDialFailed = Error("failed to dial grpc endpoint")
ErrShutdownFailed = Error("failed to shutdown sdk")
ErrPlatformConfigFailed = Error("failed to retrieve platform configuration")
ErrPlatformEndpointMalformed = Error("platform endpoint is malformed")
ErrGrpcDialFailed = Error("failed to dial grpc endpoint")
ErrShutdownFailed = Error("failed to shutdown sdk")
ErrPlatformConfigFailed = Error("failed to retrieve platform configuration")
ErrPlatformEndpointMalformed = Error("platform endpoint is malformed")
ErrPlatformIssuerNotFound = Error("issuer not found in well-known idp configuration")
ErrPlatformAuthzEndpointNotFound = Error("authorization_endpoint not found in well-known idp configuration")
ErrPlatformTokenEndpointNotFound = Error("token_endpoint not found in well-known idp configuration")
)

type Error string
Expand Down Expand Up @@ -202,14 +204,9 @@ func buildIDPTokenSource(c *config) (auth.AccessTokenSource, error) {
return c.customAccessTokenSource, nil
}

if (c.clientCredentials == nil) != (c.tokenEndpoint == "") {
return nil, errors.New("either both or neither of client credentials and token endpoint must be specified")
}

// at this point we have either both client credentials and a token endpoint or none of the above. if we don't have
// any just return a KAS client that can only get public keys
// If we don't have client-credentials, just return a KAS client that can only get public keys.
// There are uses for uncredentialed clients (i.e. consuming the well-known configuration).
if c.clientCredentials == nil {
slog.Info("no client credentials provided. GRPC requests to KAS and services will not be authenticated.")
return nil, nil //nolint:nilnil // not having credentials is not an error
}

Expand Down Expand Up @@ -326,7 +323,6 @@ func IsValidTdf(reader io.ReadSeeker) (bool, error) {
loader := gojsonschema.NewStringLoader(manifestSchemaString)
manifestStringLoader := gojsonschema.NewStringLoader(manifest)
result, err := gojsonschema.Validate(loader, manifestStringLoader)

if err != nil {
return false, errors.New("could not validate manifest.json")
}
Expand Down Expand Up @@ -373,10 +369,14 @@ func getPlatformConfiguration(conn *grpc.ClientConn) (PlatformConfiguration, err

// TODO: This should be moved to a separate package. We do discovery in ../service/internal/auth/discovery.go
func getTokenEndpoint(c config) (string, error) {
issuerURL, ok := c.platformConfiguration["platform_issuer"].(string)
idpCfg, ok := c.platformConfiguration["idp"].(map[string]interface{})
if !ok {
return "", errors.New("'idp' config not found in well-known configuration")
}

issuerURL, ok := idpCfg["issuer"].(string)
if !ok {
return "", errors.New("platform_issuer is not set, or is not a string")
return "", errors.New("'idp' config 'issuer' is not set, or is not a string in well-known configuration")
}

oidcConfigURL := issuerURL + "/.well-known/openid-configuration"
Expand Down
54 changes: 52 additions & 2 deletions sdk/sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ func GetMethods(i interface{}) []string {
func TestNew_ShouldCreateSDK(t *testing.T) {
s, err := sdk.New(goodPlatformEndpoint,
sdk.WithPlatformConfiguration(sdk.PlatformConfiguration{
"platform_issuer": "https://example.org",
"idp": map[string]interface{}{
"issuer": "https://example.org",
"authorization_endpoint": "https://example.org/auth",
"token_endpoint": "https://example.org/token",
},
}),
sdk.WithClientCredentials("myid", "mysecret", nil),
sdk.WithTokenEndpoint("https://example.org/token"),
Expand All @@ -41,7 +45,19 @@ func TestNew_ShouldCreateSDK(t *testing.T) {
require.NotNil(t, s)

// Check platform issuer
assert.Equal(t, "https://example.org", s.PlatformIssuer())
iss, err := s.PlatformIssuer()
assert.Equal(t, "https://example.org", iss)
assert.Nil(t, err)

// Check platform authz endpoint
authzEndpoint, err := s.PlatformAuthzEndpoint()
assert.Equal(t, "https://example.org/auth", authzEndpoint)
assert.Nil(t, err)

// Check platform token endpoint
tokenEndpoint, err := s.PlatformTokenEndpoint()
assert.Equal(t, "https://example.org/token", tokenEndpoint)
assert.Nil(t, err)

// check if the clients are available
assert.NotNil(t, s.Attributes)
Expand All @@ -50,6 +66,40 @@ func TestNew_ShouldCreateSDK(t *testing.T) {
assert.NotNil(t, s.KeyAccessServerRegistry)
}

func Test_PlatformConfiguration_BadCases(t *testing.T) {
assertions := func(t *testing.T, s *sdk.SDK) {
iss, err := s.PlatformIssuer()
assert.Equal(t, "", iss)
assert.ErrorIs(t, err, sdk.ErrPlatformIssuerNotFound)

authzEndpoint, err := s.PlatformAuthzEndpoint()
assert.Equal(t, "", authzEndpoint)
assert.ErrorIs(t, err, sdk.ErrPlatformAuthzEndpointNotFound)

tokenEndpoint, err := s.PlatformTokenEndpoint()
assert.Equal(t, "", tokenEndpoint)
assert.ErrorIs(t, err, sdk.ErrPlatformTokenEndpointNotFound)
}

noIdpValsSDK, err := sdk.New(goodPlatformEndpoint,
sdk.WithPlatformConfiguration(sdk.PlatformConfiguration{
"idp": map[string]interface{}{},
}),
)
assert.NoError(t, err)
assert.NotNil(t, noIdpValsSDK)

assertions(t, noIdpValsSDK)

noIdpCfgSDK, err := sdk.New(goodPlatformEndpoint,
sdk.WithPlatformConfiguration(sdk.PlatformConfiguration{}),
)
assert.NoError(t, err)
assert.NotNil(t, noIdpCfgSDK)

assertions(t, noIdpCfgSDK)
}

func Test_ShouldCreateNewSDK_NoCredentials(t *testing.T) {
// When
s, err := sdk.New(goodPlatformEndpoint,
Expand Down
14 changes: 9 additions & 5 deletions sdk/tdf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,6 @@ func TestTDF(t *testing.T) {
func (s *TDFSuite) Test_SimpleTDF() {
metaData := []byte(`{"displayName" : "openTDF go sdk"}`)
attributes := []string{

"https://example.com/attr/Classification/value/S",
"https://example.com/attr/Classification/value/X",
}
Expand Down Expand Up @@ -1218,7 +1217,9 @@ func (s *TDFSuite) startBackend() {
"health": map[string]interface{}{
"endpoint": "/healthz",
},
"platform_issuer": "http://localhost:65432/auth",
"idp": map[string]interface{}{
"issuer": "http://localhost:65432/auth",
},
},
}

Expand Down Expand Up @@ -1268,8 +1269,10 @@ func (s *TDFSuite) startBackend() {
listeners[origin] = grpcListener

grpcServer := grpc.NewServer()
s.kases[i] = FakeKas{s: s, privateKey: ki.private, KASInfo: KASInfo{
URL: ki.url, PublicKey: ki.public, KID: "r1", Algorithm: "rsa:2048"},
s.kases[i] = FakeKas{
s: s, privateKey: ki.private, KASInfo: KASInfo{
URL: ki.url, PublicKey: ki.public, KID: "r1", Algorithm: "rsa:2048",
},
legakeys: map[string]keyInfo{},
}
attributespb.RegisterAttributesServiceServer(grpcServer, fa)
Expand All @@ -1285,7 +1288,7 @@ func (s *TDFSuite) startBackend() {

sdk, err := New("localhost:65432",
WithClientCredentials("test", "test", nil),
withCustomAccessTokenSource(&ats),
WithCustomAccessTokenSource(&ats),
WithTokenEndpoint("http://localhost:65432/auth/token"),
WithInsecurePlaintextConn(),
WithExtraDialOptions(grpc.WithContextDialer(dialer)))
Expand Down Expand Up @@ -1365,6 +1368,7 @@ func mockAttributeFor(fqn autoconfigure.AttributeNameFQN) *policy.Attribute {
}
return nil
}

func mockValueFor(fqn autoconfigure.AttributeValueFQN) *policy.Value {
an := fqn.Prefix()
a := mockAttributeFor(an)
Expand Down
10 changes: 10 additions & 0 deletions service/cmd/keycloak_data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ realms:
secret: secret
protocolMappers:
- *customAudMapper
- client:
clientID: opentdf-public
enabled: true
name: opentdf-public
serviceAccountsEnabled: false
publicClient: true
redirectUris:
- 'http://localhost:9000/*'
protocolMappers:
- *customAudMapper
users:
- username: sample-user
enabled: true
Expand Down
1 change: 1 addition & 0 deletions service/cmd/provisionKeycloak.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const (
provKcUsernameFlag = "username"
provKcPasswordFlag = "password"
provKcRealmFlag = "realm"
provKcFilenameFlag = "file"
provKcInsecure = "insecure"
)

Expand Down
4 changes: 2 additions & 2 deletions service/cmd/provisionKeycloakFromConfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

var (
provKeycloakFilename = "./cmd/keycloak_data.yaml"
provKeycloakFilename = "./service/cmd/keycloak_data.yaml"
keycloakData fixtures.KeycloakData
)

Expand Down Expand Up @@ -115,7 +115,7 @@ func init() {
provisionKeycloakFromConfigCmd.Flags().StringP(provKcEndpointFlag, "e", "http://localhost:8888/auth", "Keycloak endpoint")
provisionKeycloakFromConfigCmd.Flags().StringP(provKcUsernameFlag, "u", "admin", "Keycloak username")
provisionKeycloakFromConfigCmd.Flags().StringP(provKcPasswordFlag, "p", "changeme", "Keycloak password")
provisionKeycloakFromConfigCmd.Flags().StringP(provKeycloakFilename, "f", "./cmd/keycloak_data.yaml", "Keycloak config file")
provisionKeycloakFromConfigCmd.Flags().StringP(provKcFilenameFlag, "f", provKeycloakFilename, "Keycloak config file")
// provisionKeycloakFromConfigCmd.Flags().StringP(provKcRealm, "r", "opentdf", "OpenTDF Keycloak Realm name")

provisionCmd.AddCommand(provisionKeycloakFromConfigCmd)
Expand Down
5 changes: 0 additions & 5 deletions service/internal/auth/authn.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,6 @@ func NewAuthenticator(ctx context.Context, cfg Config, logger *logger.Logger, we

a.oidcConfiguration = cfg.AuthNConfig

// Try an register oidc issuer to wellknown service but don't return an error if it fails
if err := wellknownRegistration("platform_issuer", cfg.Issuer); err != nil {
logger.Warn("failed to register platform issuer", slog.String("error", err.Error()))
}

var oidcConfigMap map[string]any

// Create a map of the oidc configuration
Expand Down