Browse Source

fix oidc

pull/7160/head
chrislu 2 months ago
parent
commit
9c587dbd51
  1. 94
      test/s3/iam/iam_config.json
  2. 110
      test/s3/iam/s3_iam_framework.go
  3. 66
      test/s3/iam/s3_keycloak_integration_test.go
  4. 50
      weed/iam/oidc/oidc_provider.go
  5. 59
      weed/iam/sts/provider_factory.go
  6. 5
      weed/iam/sts/sts_service.go
  7. 92
      weed/s3api/s3_iam_middleware.go
  8. 24
      weed/s3api/s3api_server.go

94
test/s3/iam/iam_config.json

@ -5,13 +5,51 @@
"issuer": "seaweedfs-sts",
"signingKey": "dGVzdC1zaWduaW5nLWtleS0zMi1jaGFyYWN0ZXJzLWxvbmc="
},
"identityProviders": [
"providers": [
{
"name": "test-oidc",
"type": "mock",
"config": {
"issuer": "test-oidc-issuer"
}
},
{
"name": "keycloak",
"type": "oidc",
"enabled": true,
"config": {
"issuer": "http://localhost:8080/realms/seaweedfs-test",
"clientId": "seaweedfs-s3",
"clientSecret": "seaweedfs-s3-secret",
"jwksUri": "http://localhost:8080/realms/seaweedfs-test/protocol/openid-connect/certs",
"userInfoUri": "http://localhost:8080/realms/seaweedfs-test/protocol/openid-connect/userinfo",
"scopes": ["openid", "profile", "email"],
"claimsMapping": {
"username": "preferred_username",
"email": "email",
"name": "name"
},
"roleMapping": {
"rules": [
{
"claim": "roles",
"value": "s3-admin",
"role": "arn:seaweed:iam::role/KeycloakAdminRole"
},
{
"claim": "roles",
"value": "s3-read-only",
"role": "arn:seaweed:iam::role/KeycloakReadOnlyRole"
},
{
"claim": "roles",
"value": "s3-write-only",
"role": "arn:seaweed:iam::role/KeycloakWriteOnlyRole"
}
],
"defaultRole": "arn:seaweed:iam::role/KeycloakReadOnlyRole"
}
}
}
],
"policy": {
@ -72,6 +110,60 @@
},
"attachedPolicies": ["S3WriteOnlyPolicy"],
"description": "Write-only role for testing"
},
{
"roleName": "KeycloakAdminRole",
"roleArn": "arn:seaweed:iam::role/KeycloakAdminRole",
"trustPolicy": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "keycloak"
},
"Action": ["sts:AssumeRoleWithWebIdentity"]
}
]
},
"attachedPolicies": ["S3AdminPolicy"],
"description": "Admin role for Keycloak users"
},
{
"roleName": "KeycloakReadOnlyRole",
"roleArn": "arn:seaweed:iam::role/KeycloakReadOnlyRole",
"trustPolicy": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "keycloak"
},
"Action": ["sts:AssumeRoleWithWebIdentity"]
}
]
},
"attachedPolicies": ["S3ReadOnlyPolicy"],
"description": "Read-only role for Keycloak users"
},
{
"roleName": "KeycloakWriteOnlyRole",
"roleArn": "arn:seaweed:iam::role/KeycloakWriteOnlyRole",
"trustPolicy": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "keycloak"
},
"Action": ["sts:AssumeRoleWithWebIdentity"]
}
]
},
"attachedPolicies": ["S3WriteOnlyPolicy"],
"description": "Write-only role for Keycloak users"
}
],
"policies": [

110
test/s3/iam/s3_iam_framework.go

@ -114,9 +114,10 @@ func NewKeycloakClient(baseURL, realm, clientID, clientSecret string) *KeycloakC
// isKeycloakAvailable checks if Keycloak is running and accessible
func (f *S3IAMTestFramework) isKeycloakAvailable(keycloakURL string) bool {
client := &http.Client{Timeout: 5 * time.Second}
healthURL := fmt.Sprintf("%s/health/ready", keycloakURL)
// Use realms endpoint instead of health/ready for Keycloak v26+
realmsURL := fmt.Sprintf("%s/realms/master", keycloakURL)
resp, err := client.Get(healthURL)
resp, err := client.Get(realmsURL)
if err != nil {
return false
}
@ -268,16 +269,40 @@ func (t *BearerTokenTransport) RoundTrip(req *http.Request) (*http.Response, err
// Clone the request to avoid modifying the original
newReq := req.Clone(req.Context())
// Add Bearer token authorization header
newReq.Header.Set("Authorization", "Bearer "+t.Token)
// Remove AWS signature headers if present (they conflict with Bearer auth)
// Remove ALL existing Authorization headers first to prevent conflicts
newReq.Header.Del("Authorization")
newReq.Header.Del("X-Amz-Date")
newReq.Header.Del("X-Amz-Content-Sha256")
newReq.Header.Del("X-Amz-Signature")
newReq.Header.Del("X-Amz-Algorithm")
newReq.Header.Del("X-Amz-Credential")
newReq.Header.Del("X-Amz-SignedHeaders")
newReq.Header.Del("X-Amz-Security-Token")
// Add Bearer token authorization header
newReq.Header.Set("Authorization", "Bearer "+t.Token)
// Debug: log the token being sent (first 50 chars) and subject
tokenPreview := t.Token
if len(tokenPreview) > 50 {
tokenPreview = tokenPreview[:50] + "..."
}
// Debug logging can be enabled if needed
// Extract subject from token for debugging
// parts := strings.Split(t.Token, ".")
// subject := "unknown"
// if len(parts) >= 2 {
// if decoded, err := base64.RawURLEncoding.DecodeString(parts[1]); err == nil {
// var claims map[string]interface{}
// if json.Unmarshal(decoded, &claims) == nil {
// if sub, ok := claims["sub"].(string); ok {
// subject = sub[:8] + "..." // First 8 chars of subject
// }
// }
// }
// }
// fmt.Printf("DEBUG: Sending Bearer token (subject: %s): %s\n", subject, tokenPreview)
// Use underlying transport
transport := t.Transport
@ -464,6 +489,79 @@ func (f *S3IAMTestFramework) CreateS3ClientWithSessionToken(sessionToken string)
return s3.New(sess), nil
}
// CreateS3ClientWithKeycloakToken creates an S3 client using a Keycloak JWT token
func (f *S3IAMTestFramework) CreateS3ClientWithKeycloakToken(keycloakToken string) (*s3.S3, error) {
// Create a fresh HTTP transport with aggressive timeouts to prevent hanging
transport := &http.Transport{
DisableKeepAlives: true, // Force new connections for each request
DisableCompression: true, // Disable compression to simplify requests
MaxIdleConns: 0, // No connection pooling
MaxIdleConnsPerHost: 0, // No connection pooling per host
IdleConnTimeout: 1 * time.Second,
TLSHandshakeTimeout: 5 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
// Create a custom HTTP client with aggressive timeouts
httpClient := &http.Client{
Timeout: 30 * time.Second, // Overall request timeout
Transport: &BearerTokenTransport{
Token: keycloakToken,
Transport: transport,
},
}
sess, err := session.NewSession(&aws.Config{
Region: aws.String(TestRegion),
Endpoint: aws.String(TestS3Endpoint),
Credentials: credentials.AnonymousCredentials,
DisableSSL: aws.Bool(true),
S3ForcePathStyle: aws.Bool(true),
HTTPClient: httpClient,
MaxRetries: aws.Int(0), // No retries to avoid delays
})
if err != nil {
return nil, fmt.Errorf("failed to create AWS session: %v", err)
}
return s3.New(sess), nil
}
// TestKeycloakTokenDirectly tests a Keycloak token with direct HTTP request (bypassing AWS SDK)
func (f *S3IAMTestFramework) TestKeycloakTokenDirectly(keycloakToken string) error {
// Create a simple HTTP client with timeout
client := &http.Client{
Timeout: 10 * time.Second,
}
// Create request to list buckets
req, err := http.NewRequest("GET", TestS3Endpoint, nil)
if err != nil {
return fmt.Errorf("failed to create request: %v", err)
}
// Add Bearer token
req.Header.Set("Authorization", "Bearer "+keycloakToken)
req.Header.Set("Host", "localhost:8333")
// Make request
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("request failed: %v", err)
}
defer resp.Body.Close()
// Read response
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response: %v", err)
}
fmt.Printf("Direct HTTP test - Status: %d, Body: %s\n", resp.StatusCode, string(body))
return nil
}
// generateJWTToken creates a JWT token for testing
func (f *S3IAMTestFramework) generateJWTToken(username, roleName string, validDuration time.Duration) (string, error) {
now := time.Now()

66
test/s3/iam/s3_keycloak_integration_test.go

@ -1,7 +1,10 @@
package iam
import (
"encoding/base64"
"encoding/json"
"os"
"strings"
"testing"
"github.com/aws/aws-sdk-go/service/s3"
@ -43,7 +46,7 @@ func TestKeycloakAuthentication(t *testing.T) {
assert.NotEmpty(t, token, "JWT token should not be empty")
// Verify token can be used to create S3 client
s3Client, err := framework.CreateS3ClientWithJWT("admin-user", "S3AdminRole")
s3Client, err := framework.CreateS3ClientWithKeycloakToken(token)
require.NoError(t, err)
assert.NotNil(t, s3Client, "S3 client should be created successfully")
@ -71,17 +74,42 @@ func TestKeycloakAuthentication(t *testing.T) {
require.NoError(t, err)
assert.NotEmpty(t, token, "JWT token should not be empty")
// Create S3 client with read-only user
s3Client, err := framework.CreateS3ClientWithJWT("read-user", "S3ReadOnlyRole")
require.NoError(t, err)
// Debug: decode token to verify it's for read-user
parts := strings.Split(token, ".")
if len(parts) >= 2 {
payload := parts[1]
// Add padding if needed
for len(payload)%4 != 0 {
payload += "="
}
decoded, err := base64.StdEncoding.DecodeString(payload)
if err == nil {
var claims map[string]interface{}
if json.Unmarshal(decoded, &claims) == nil {
t.Logf("Token username: %v", claims["preferred_username"])
t.Logf("Token roles: %v", claims["roles"])
}
}
}
// Test that read-only user can list buckets
_, err = s3Client.ListBuckets(&s3.ListBucketsInput{})
assert.NoError(t, err, "Read-only user should be able to list buckets")
// First test with direct HTTP request to verify OIDC authentication works
t.Logf("Testing with direct HTTP request...")
err = framework.TestKeycloakTokenDirectly(token)
require.NoError(t, err, "Direct HTTP test should succeed")
// Test that read-only user cannot create buckets
err = framework.CreateBucket(s3Client, testKeycloakBucket+"-readonly")
assert.Error(t, err, "Read-only user should not be able to create buckets")
// Create S3 client with Keycloak token
s3Client, err := framework.CreateS3ClientWithKeycloakToken(token)
require.NoError(t, err)
// Test that read-only user can list buckets
t.Logf("Testing ListBuckets with AWS SDK...")
_, err = s3Client.ListBuckets(&s3.ListBucketsInput{})
assert.NoError(t, err, "Read-only user should be able to list buckets")
// Test that read-only user cannot create buckets
t.Logf("Testing CreateBucket with AWS SDK...")
err = framework.CreateBucket(s3Client, testKeycloakBucket+"-readonly")
assert.Error(t, err, "Read-only user should not be able to create buckets")
})
t.Run("invalid_user_authentication", func(t *testing.T) {
@ -110,7 +138,10 @@ func TestKeycloakTokenExpiration(t *testing.T) {
assert.Greater(t, tokenResp.ExpiresIn, 0, "Token should have expiration time")
// Test that token works initially
s3Client, err := framework.CreateS3ClientWithJWT("admin-user", "S3AdminRole")
token, err := framework.getKeycloakToken("admin-user")
require.NoError(t, err)
s3Client, err := framework.CreateS3ClientWithKeycloakToken(token)
require.NoError(t, err)
_, err = s3Client.ListBuckets(&s3.ListBucketsInput{})
@ -158,8 +189,12 @@ func TestKeycloakRoleMapping(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.username, func(t *testing.T) {
// Create S3 client for the user
s3Client, err := framework.CreateS3ClientWithJWT(tc.username, tc.expectedRole)
// Get Keycloak token for the user
token, err := framework.getKeycloakToken(tc.username)
require.NoError(t, err)
// Create S3 client with Keycloak token
s3Client, err := framework.CreateS3ClientWithKeycloakToken(token)
require.NoError(t, err, tc.description)
// Test list buckets permission
@ -192,7 +227,10 @@ func TestKeycloakS3Operations(t *testing.T) {
}
// Use admin user for comprehensive testing
s3Client, err := framework.CreateS3ClientWithJWT("admin-user", "S3AdminRole")
token, err := framework.getKeycloakToken("admin-user")
require.NoError(t, err)
s3Client, err := framework.CreateS3ClientWithKeycloakToken(token)
require.NoError(t, err)
bucketName := testKeycloakBucket + "-operations"

50
weed/iam/oidc/oidc_provider.go

@ -144,11 +144,22 @@ func (p *OIDCProvider) Authenticate(ctx context.Context, token string) (*provide
displayName, _ := claims.GetClaimString("name")
groups, _ := claims.GetClaimStringSlice("groups")
// Map claims to roles using configured role mapping
roles := p.mapClaimsToRolesWithConfig(claims)
// Create attributes map and add roles
attributes := make(map[string]string)
if len(roles) > 0 {
// Store roles as a comma-separated string in attributes
attributes["roles"] = strings.Join(roles, ",")
}
return &providers.ExternalIdentity{
UserID: claims.Subject,
Email: email,
DisplayName: displayName,
Groups: groups,
Attributes: attributes,
Provider: p.name,
}, nil
}
@ -288,11 +299,20 @@ func (p *OIDCProvider) ValidateToken(ctx context.Context, token string) (*provid
return nil, fmt.Errorf("invalid or missing issuer claim")
}
audience, ok := claims["aud"].(string)
if !ok || audience != p.config.ClientID {
// Check audience claim (aud) or authorized party (azp) - Keycloak uses azp
var audience string
if aud, ok := claims["aud"].(string); ok {
audience = aud
} else if azp, ok := claims["azp"].(string); ok {
audience = azp
} else {
return nil, fmt.Errorf("invalid or missing audience claim")
}
if audience != p.config.ClientID {
return nil, fmt.Errorf("invalid audience claim: expected %s, got %s", p.config.ClientID, audience)
}
subject, ok := claims["sub"].(string)
if !ok {
return nil, fmt.Errorf("missing subject claim")
@ -313,7 +333,7 @@ func (p *OIDCProvider) ValidateToken(ctx context.Context, token string) (*provid
return tokenClaims, nil
}
// mapClaimsToRoles maps token claims to SeaweedFS roles
// mapClaimsToRoles maps token claims to SeaweedFS roles (legacy method)
func (p *OIDCProvider) mapClaimsToRoles(claims *providers.TokenClaims) []string {
roles := []string{}
@ -339,6 +359,30 @@ func (p *OIDCProvider) mapClaimsToRoles(claims *providers.TokenClaims) []string
return roles
}
// mapClaimsToRolesWithConfig maps token claims to roles using configured role mapping
func (p *OIDCProvider) mapClaimsToRolesWithConfig(claims *providers.TokenClaims) []string {
if p.config.RoleMapping == nil {
// Fallback to legacy mapping if no role mapping configured
return p.mapClaimsToRoles(claims)
}
roles := []string{}
// Apply role mapping rules
for _, rule := range p.config.RoleMapping.Rules {
if rule.Matches(claims) {
roles = append(roles, rule.Role)
}
}
// Use default role if no rules matched
if len(roles) == 0 && p.config.RoleMapping.DefaultRole != "" {
roles = []string{p.config.RoleMapping.DefaultRole}
}
return roles
}
// getPublicKey retrieves the public key for the given key ID from JWKS
func (p *OIDCProvider) getPublicKey(ctx context.Context, kid string) (interface{}, error) {
// Fetch JWKS if not cached or refresh if needed

59
weed/iam/sts/provider_factory.go

@ -145,6 +145,13 @@ func (f *ProviderFactory) convertToOIDCConfig(configMap map[string]interface{})
}
}
// Convert role mapping
if roleMappingInterface, ok := configMap["roleMapping"]; ok {
if roleMapping, err := f.convertToRoleMapping(roleMappingInterface); err == nil {
config.RoleMapping = roleMapping
}
}
glog.V(3).Infof("Converted OIDC config: issuer=%s, clientId=%s, jwksUri=%s",
config.Issuer, config.ClientID, config.JWKSUri)
@ -225,6 +232,56 @@ func (f *ProviderFactory) LoadProvidersFromConfig(configs []*ProviderConfig) (ma
return providersMap, nil
}
// convertToRoleMapping converts interface{} to *providers.RoleMapping
func (f *ProviderFactory) convertToRoleMapping(value interface{}) (*providers.RoleMapping, error) {
roleMappingMap, ok := value.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("roleMapping must be an object")
}
roleMapping := &providers.RoleMapping{}
// Convert rules
if rulesInterface, ok := roleMappingMap["rules"]; ok {
rulesSlice, ok := rulesInterface.([]interface{})
if !ok {
return nil, fmt.Errorf("rules must be an array")
}
rules := make([]providers.MappingRule, len(rulesSlice))
for i, ruleInterface := range rulesSlice {
ruleMap, ok := ruleInterface.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("rule must be an object")
}
rule := providers.MappingRule{}
if claim, ok := ruleMap["claim"].(string); ok {
rule.Claim = claim
}
if value, ok := ruleMap["value"].(string); ok {
rule.Value = value
}
if role, ok := ruleMap["role"].(string); ok {
rule.Role = role
}
if condition, ok := ruleMap["condition"].(string); ok {
rule.Condition = condition
}
rules[i] = rule
}
roleMapping.Rules = rules
}
// Convert default role
if defaultRole, ok := roleMappingMap["defaultRole"].(string); ok {
roleMapping.DefaultRole = defaultRole
}
return roleMapping, nil
}
// ValidateProviderConfig validates a provider configuration
func (f *ProviderFactory) ValidateProviderConfig(config *ProviderConfig) error {
if config == nil {
@ -263,7 +320,7 @@ func (f *ProviderFactory) validateOIDCConfig(config map[string]interface{}) erro
if _, ok := config[ConfigFieldIssuer]; !ok {
return fmt.Errorf("OIDC provider requires '%s' field", ConfigFieldIssuer)
}
if _, ok := config[ConfigFieldClientID]; !ok {
return fmt.Errorf("OIDC provider requires '%s' field", ConfigFieldClientID)
}

5
weed/iam/sts/sts_service.go

@ -299,6 +299,11 @@ func (s *STSService) RegisterProvider(provider providers.IdentityProvider) error
return nil
}
// GetProviders returns all registered identity providers
func (s *STSService) GetProviders() map[string]providers.IdentityProvider {
return s.providers
}
// AssumeRoleWithWebIdentity assumes a role using a web identity token (OIDC)
// This method is now completely stateless - all session information is embedded in the JWT token
func (s *STSService) AssumeRoleWithWebIdentity(ctx context.Context, request *AssumeRoleWithWebIdentityRequest) (*AssumeRoleResponse, error) {

92
weed/s3api/s3_iam_middleware.go

@ -10,6 +10,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/iam/integration"
"github.com/seaweedfs/seaweedfs/weed/iam/providers"
"github.com/seaweedfs/seaweedfs/weed/iam/sts"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
@ -61,18 +62,36 @@ func (s3iam *S3IAMIntegration) AuthenticateJWT(ctx context.Context, r *http.Requ
return nil, s3err.ErrAccessDenied
}
// Parse JWT token to extract claims
// Try to parse as STS session token first
tokenClaims, err := parseJWTToken(sessionToken)
if err != nil {
glog.V(3).Infof("Failed to parse JWT token: %v", err)
return nil, s3err.ErrAccessDenied
}
// Extract role information from token claims
// Check if this is an STS session token (has "role" claim)
roleName, ok := tokenClaims["role"].(string)
if !ok || roleName == "" {
glog.V(0).Info("No role found in JWT token")
return nil, s3err.ErrAccessDenied
// Not an STS session token, try to validate as OIDC token
identity, err := s3iam.validateOIDCToken(ctx, sessionToken)
if err != nil {
glog.V(0).Infof("Failed to validate as OIDC token: %v", err)
return nil, s3err.ErrAccessDenied
}
// Extract role from OIDC identity
if identity.RoleArn == "" {
glog.V(0).Info("No role found in OIDC token")
return nil, s3err.ErrAccessDenied
}
// Return IAM identity for OIDC token
return &IAMIdentity{
Name: identity.UserID,
Principal: identity.RoleArn,
SessionToken: sessionToken,
Account: nil, // OIDC tokens don't have account info
}, s3err.ErrNone
}
sessionName, ok := tokenClaims["snam"].(string)
@ -409,3 +428,68 @@ func (enhanced *EnhancedS3ApiServer) AuthorizeRequest(r *http.Request, identity
// Use our IAM integration for authorization
return enhanced.iamIntegration.AuthorizeAction(ctx, iamIdentity, action, bucket, object, r)
}
// OIDCIdentity represents an identity validated through OIDC
type OIDCIdentity struct {
UserID string
RoleArn string
Provider string
}
// validateOIDCToken validates an OIDC token using registered identity providers
func (s3iam *S3IAMIntegration) validateOIDCToken(ctx context.Context, token string) (*OIDCIdentity, error) {
if s3iam.iamManager == nil {
return nil, fmt.Errorf("IAM manager not available")
}
// Get STS service to access identity providers
stsService := s3iam.iamManager.GetSTSService()
if stsService == nil {
return nil, fmt.Errorf("STS service not available")
}
// Try to validate token with each registered OIDC provider
providers := stsService.GetProviders()
for providerName, provider := range providers {
// Try to authenticate with this provider
externalIdentity, err := provider.Authenticate(ctx, token)
if err != nil {
glog.V(3).Infof("Provider %s failed to authenticate token: %v", providerName, err)
continue
}
// Extract role from external identity attributes
rolesAttr, exists := externalIdentity.Attributes["roles"]
if !exists || rolesAttr == "" {
glog.V(3).Infof("No roles found in external identity from provider %s", providerName)
continue
}
// Parse roles (stored as comma-separated string)
roles := strings.Split(rolesAttr, ",")
if len(roles) == 0 {
glog.V(3).Infof("Empty roles list from provider %s", providerName)
continue
}
// Use the first role as the primary role
roleArn := roles[0]
return &OIDCIdentity{
UserID: externalIdentity.UserID,
RoleArn: roleArn,
Provider: providerName,
}, nil
}
return nil, fmt.Errorf("token not valid for any registered OIDC provider")
}
// getProviderNames returns a list of provider names for debugging
func getProviderNames(providers map[string]providers.IdentityProvider) []string {
names := make([]string, 0, len(providers))
for name := range providers {
names = append(names, name)
}
return names
}

24
weed/s3api/s3api_server.go

@ -450,6 +450,28 @@ func loadIAMManagerFromConfig(configPath string) (*integration.IAMManager, error
return nil, fmt.Errorf("failed to initialize IAM manager: %w", err)
}
// Load identity providers
providerFactory := sts.NewProviderFactory()
for _, providerConfig := range configRoot.Providers {
provider, err := providerFactory.CreateProvider(&sts.ProviderConfig{
Name: providerConfig["name"].(string),
Type: providerConfig["type"].(string),
Enabled: true,
Config: providerConfig["config"].(map[string]interface{}),
})
if err != nil {
glog.Warningf("Failed to create provider %s: %v", providerConfig["name"], err)
continue
}
if provider != nil {
if err := iamManager.RegisterIdentityProvider(provider); err != nil {
glog.Warningf("Failed to register provider %s: %v", providerConfig["name"], err)
} else {
glog.V(1).Infof("Registered identity provider: %s", providerConfig["name"])
}
}
}
// Load policies
for _, policyDef := range configRoot.Policies {
if err := iamManager.CreatePolicy(context.Background(), policyDef.Name, policyDef.Document); err != nil {
@ -464,7 +486,7 @@ func loadIAMManagerFromConfig(configPath string) (*integration.IAMManager, error
}
}
glog.V(0).Infof("Loaded %d policies and %d roles from config", len(configRoot.Policies), len(configRoot.Roles))
glog.V(0).Infof("Loaded %d providers, %d policies and %d roles from config", len(configRoot.Providers), len(configRoot.Policies), len(configRoot.Roles))
return iamManager, nil
}
Loading…
Cancel
Save