Refresh IAM config after policy attachments (#8439)
* Refresh IAM cache after policy attachments * error handling
This commit is contained in:
@@ -51,6 +51,19 @@ func NewEmbeddedIamApi(credentialManager *credential.CredentialManager, iam *Ide
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *EmbeddedIamApi) refreshIAMConfiguration() error {
|
||||||
|
if e.reloadConfigurationFunc != nil {
|
||||||
|
return e.reloadConfigurationFunc()
|
||||||
|
}
|
||||||
|
if e.iam == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := e.iam.LoadS3ApiConfigurationFromCredentialManager(); err != nil {
|
||||||
|
return fmt.Errorf("failed to refresh IAM configuration: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Constants for service account identifiers
|
// Constants for service account identifiers
|
||||||
const (
|
const (
|
||||||
ServiceAccountIDLength = 12 // Length of the service account ID
|
ServiceAccountIDLength = 12 // Length of the service account ID
|
||||||
@@ -903,6 +916,11 @@ func (e *EmbeddedIamApi) AttachUserPolicy(ctx context.Context, values url.Values
|
|||||||
return resp, &iamError{Code: iam.ErrCodeServiceFailureException, Error: err}
|
return resp, &iamError{Code: iam.ErrCodeServiceFailureException, Error: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Best-effort refresh: log any failures but don't fail the API call since the mutation succeeded
|
||||||
|
if err := e.refreshIAMConfiguration(); err != nil {
|
||||||
|
glog.Warningf("Failed to refresh IAM configuration after attaching policy %s to user %s: %v", policyName, userName, err)
|
||||||
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -943,6 +961,11 @@ func (e *EmbeddedIamApi) DetachUserPolicy(ctx context.Context, values url.Values
|
|||||||
return resp, &iamError{Code: iam.ErrCodeServiceFailureException, Error: err}
|
return resp, &iamError{Code: iam.ErrCodeServiceFailureException, Error: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Best-effort refresh: log any failures but don't fail the API call since the mutation succeeded
|
||||||
|
if err := e.refreshIAMConfiguration(); err != nil {
|
||||||
|
glog.Warningf("Failed to refresh IAM configuration after detaching policy %s from user %s: %v", policyName, userName, err)
|
||||||
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import (
|
|||||||
"github.com/seaweedfs/seaweedfs/weed/credential"
|
"github.com/seaweedfs/seaweedfs/weed/credential"
|
||||||
"github.com/seaweedfs/seaweedfs/weed/credential/memory"
|
"github.com/seaweedfs/seaweedfs/weed/credential/memory"
|
||||||
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
||||||
|
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
|
||||||
. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
||||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -468,6 +470,46 @@ func TestEmbeddedIamAttachUserPolicy(t *testing.T) {
|
|||||||
assert.Equal(t, []string{"TestManagedPolicy"}, api.mockConfig.Identities[0].PolicyNames)
|
assert.Equal(t, []string{"TestManagedPolicy"}, api.mockConfig.Identities[0].PolicyNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEmbeddedIamAttachUserPolicyRefreshesIAM(t *testing.T) {
|
||||||
|
api := NewEmbeddedIamApiForTest()
|
||||||
|
ctx := context.Background()
|
||||||
|
cm := api.credentialManager
|
||||||
|
user := &iam_pb.Identity{
|
||||||
|
Name: "policyRefreshUser",
|
||||||
|
Credentials: []*iam_pb.Credential{
|
||||||
|
{AccessKey: "REFRESHACCESS", SecretKey: "REFRESHSECRET"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, cm.CreateUser(ctx, user))
|
||||||
|
policy := policy_engine.PolicyDocument{
|
||||||
|
Version: policy_engine.PolicyVersion2012_10_17,
|
||||||
|
Statement: []policy_engine.PolicyStatement{
|
||||||
|
{
|
||||||
|
Effect: policy_engine.PolicyEffectAllow,
|
||||||
|
Action: policy_engine.NewStringOrStringSlice("s3:GetObject"),
|
||||||
|
Resource: policy_engine.NewStringOrStringSlice("arn:aws:s3:::bucket/*"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, cm.PutPolicy(ctx, "RefreshPolicy", policy))
|
||||||
|
require.NoError(t, api.iam.LoadS3ApiConfigurationFromCredentialManager())
|
||||||
|
|
||||||
|
identity := api.iam.lookupByIdentityName("policyRefreshUser")
|
||||||
|
require.NotNil(t, identity)
|
||||||
|
assert.Empty(t, identity.PolicyNames)
|
||||||
|
|
||||||
|
values := url.Values{}
|
||||||
|
values.Set("UserName", "policyRefreshUser")
|
||||||
|
values.Set("PolicyArn", "arn:aws:iam:::policy/RefreshPolicy")
|
||||||
|
|
||||||
|
_, iamErr := api.AttachUserPolicy(ctx, values)
|
||||||
|
require.Nil(t, iamErr)
|
||||||
|
|
||||||
|
identity = api.iam.lookupByIdentityName("policyRefreshUser")
|
||||||
|
require.NotNil(t, identity)
|
||||||
|
assert.Equal(t, []string{"RefreshPolicy"}, identity.PolicyNames)
|
||||||
|
}
|
||||||
|
|
||||||
// TestEmbeddedIamAttachUserPolicyNoSuchPolicy tests attach failure when managed policy does not exist.
|
// TestEmbeddedIamAttachUserPolicyNoSuchPolicy tests attach failure when managed policy does not exist.
|
||||||
func TestEmbeddedIamAttachUserPolicyNoSuchPolicy(t *testing.T) {
|
func TestEmbeddedIamAttachUserPolicyNoSuchPolicy(t *testing.T) {
|
||||||
api := NewEmbeddedIamApiForTest()
|
api := NewEmbeddedIamApiForTest()
|
||||||
|
|||||||
Reference in New Issue
Block a user