mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-24 00:44:18 +00:00
Revert "Implement IAM propagation to S3 servers (#8130)"
This reverts commit 551a31e156.
This commit is contained in:
+96
-775
File diff suppressed because it is too large
Load Diff
@@ -13,9 +13,6 @@ import (
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
||||
jsonpb "google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
_ "github.com/seaweedfs/seaweedfs/weed/credential/filer_etc"
|
||||
_ "github.com/seaweedfs/seaweedfs/weed/credential/memory"
|
||||
)
|
||||
|
||||
func TestIdentityListFileFormat(t *testing.T) {
|
||||
@@ -745,48 +742,3 @@ func TestSignatureVerificationDoesNotCheckPermissions(t *testing.T) {
|
||||
t.Log("Signature verification no longer checks for Write permission")
|
||||
t.Log("This allows list-only and read-only users to authenticate via AWS Signature V4")
|
||||
}
|
||||
|
||||
func TestStaticIdentityProtection(t *testing.T) {
|
||||
iam := NewIdentityAccessManagement(&S3ApiServerOption{})
|
||||
|
||||
// Add a static identity
|
||||
staticIdent := &Identity{
|
||||
Name: "static-user",
|
||||
IsStatic: true,
|
||||
}
|
||||
iam.m.Lock()
|
||||
if iam.nameToIdentity == nil {
|
||||
iam.nameToIdentity = make(map[string]*Identity)
|
||||
}
|
||||
iam.identities = append(iam.identities, staticIdent)
|
||||
iam.nameToIdentity[staticIdent.Name] = staticIdent
|
||||
iam.m.Unlock()
|
||||
|
||||
// Add a dynamic identity
|
||||
dynamicIdent := &Identity{
|
||||
Name: "dynamic-user",
|
||||
IsStatic: false,
|
||||
}
|
||||
iam.m.Lock()
|
||||
iam.identities = append(iam.identities, dynamicIdent)
|
||||
iam.nameToIdentity[dynamicIdent.Name] = dynamicIdent
|
||||
iam.m.Unlock()
|
||||
|
||||
// Try to remove static identity
|
||||
iam.RemoveIdentity("static-user")
|
||||
|
||||
// Verify static identity still exists
|
||||
iam.m.RLock()
|
||||
_, ok := iam.nameToIdentity["static-user"]
|
||||
iam.m.RUnlock()
|
||||
assert.True(t, ok, "Static identity should not be removed")
|
||||
|
||||
// Try to remove dynamic identity
|
||||
iam.RemoveIdentity("dynamic-user")
|
||||
|
||||
// Verify dynamic identity is removed
|
||||
iam.m.RLock()
|
||||
_, ok = iam.nameToIdentity["dynamic-user"]
|
||||
iam.m.RUnlock()
|
||||
assert.False(t, ok, "Dynamic identity should have been removed")
|
||||
}
|
||||
|
||||
@@ -83,16 +83,6 @@ func (bpe *BucketPolicyEngine) HasPolicyForBucket(bucket string) bool {
|
||||
return bpe.engine.HasPolicyForBucket(bucket)
|
||||
}
|
||||
|
||||
// GetBucketPolicy gets the policy for a bucket
|
||||
func (bpe *BucketPolicyEngine) GetBucketPolicy(bucket string) (*policy_engine.PolicyDocument, error) {
|
||||
return bpe.engine.GetBucketPolicy(bucket)
|
||||
}
|
||||
|
||||
// ListBucketPolicies returns all buckets that have policies
|
||||
func (bpe *BucketPolicyEngine) ListBucketPolicies() []string {
|
||||
return bpe.engine.GetAllBucketsWithPolicies()
|
||||
}
|
||||
|
||||
// EvaluatePolicy evaluates whether an action is allowed by bucket policy
|
||||
//
|
||||
// Parameters:
|
||||
|
||||
@@ -38,15 +38,13 @@ type EmbeddedIamApi struct {
|
||||
getS3ApiConfigurationFunc func(*iam_pb.S3ApiConfiguration) error
|
||||
putS3ApiConfigurationFunc func(*iam_pb.S3ApiConfiguration) error
|
||||
reloadConfigurationFunc func() error
|
||||
readOnly bool
|
||||
}
|
||||
|
||||
// NewEmbeddedIamApi creates a new embedded IAM API handler.
|
||||
func NewEmbeddedIamApi(credentialManager *credential.CredentialManager, iam *IdentityAccessManagement, readOnly bool) *EmbeddedIamApi {
|
||||
func NewEmbeddedIamApi(credentialManager *credential.CredentialManager, iam *IdentityAccessManagement) *EmbeddedIamApi {
|
||||
return &EmbeddedIamApi{
|
||||
credentialManager: credentialManager,
|
||||
iam: iam,
|
||||
readOnly: readOnly,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,8 +160,6 @@ func (e *EmbeddedIamApi) writeIamErrorResponse(w http.ResponseWriter, r *http.Re
|
||||
s3err.WriteXMLResponse(w, r, http.StatusConflict, errorResp)
|
||||
case iam.ErrCodeMalformedPolicyDocumentException, iam.ErrCodeInvalidInputException:
|
||||
s3err.WriteXMLResponse(w, r, http.StatusBadRequest, errorResp)
|
||||
case "AccessDenied", iam.ErrCodeLimitExceededException:
|
||||
s3err.WriteXMLResponse(w, r, http.StatusForbidden, errorResp)
|
||||
case iam.ErrCodeServiceFailureException:
|
||||
s3err.WriteXMLResponse(w, r, http.StatusInternalServerError, internalErrorResponse)
|
||||
default:
|
||||
@@ -194,7 +190,6 @@ func (e *EmbeddedIamApi) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration)
|
||||
|
||||
// ReloadConfiguration reloads the IAM configuration from the credential manager.
|
||||
func (e *EmbeddedIamApi) ReloadConfiguration() error {
|
||||
glog.V(4).Infof("IAM: reloading configuration via EmbeddedIamApi")
|
||||
if e.reloadConfigurationFunc != nil {
|
||||
return e.reloadConfigurationFunc()
|
||||
}
|
||||
@@ -1048,22 +1043,11 @@ func (e *EmbeddedIamApi) AuthIam(f http.HandlerFunc, _ Action) http.HandlerFunc
|
||||
}
|
||||
|
||||
// ExecuteAction executes an IAM action with the given values.
|
||||
// If skipPersist is true, the changed configuration is not saved to the persistent store.
|
||||
func (e *EmbeddedIamApi) ExecuteAction(values url.Values, skipPersist bool) (interface{}, *iamError) {
|
||||
func (e *EmbeddedIamApi) ExecuteAction(values url.Values) (interface{}, *iamError) {
|
||||
// Lock to prevent concurrent read-modify-write race conditions
|
||||
e.policyLock.Lock()
|
||||
defer e.policyLock.Unlock()
|
||||
|
||||
action := values.Get("Action")
|
||||
if e.readOnly {
|
||||
switch action {
|
||||
case "ListUsers", "ListAccessKeys", "GetUser", "GetUserPolicy", "ListServiceAccounts", "GetServiceAccount":
|
||||
// Allowed read-only actions
|
||||
default:
|
||||
return nil, &iamError{Code: s3err.GetAPIError(s3err.ErrAccessDenied).Code, Error: fmt.Errorf("IAM write operations are disabled on this server")}
|
||||
}
|
||||
}
|
||||
|
||||
s3cfg := &iam_pb.S3ApiConfiguration{}
|
||||
if err := e.GetS3ApiConfiguration(s3cfg); err != nil && !errors.Is(err, filer_pb.ErrNotFound) {
|
||||
return nil, &iamError{Code: s3err.GetAPIError(s3err.ErrInternalError).Code, Error: fmt.Errorf("failed to get s3 api configuration: %v", err)}
|
||||
@@ -1181,11 +1165,9 @@ func (e *EmbeddedIamApi) ExecuteAction(values url.Values, skipPersist bool) (int
|
||||
return nil, &iamError{Code: s3err.GetAPIError(s3err.ErrNotImplemented).Code, Error: errors.New(s3err.GetAPIError(s3err.ErrNotImplemented).Description)}
|
||||
}
|
||||
if changed {
|
||||
if !skipPersist {
|
||||
if err := e.PutS3ApiConfiguration(s3cfg); err != nil {
|
||||
iamErr = &iamError{Code: iam.ErrCodeServiceFailureException, Error: err}
|
||||
return nil, iamErr
|
||||
}
|
||||
if err := e.PutS3ApiConfiguration(s3cfg); err != nil {
|
||||
iamErr = &iamError{Code: iam.ErrCodeServiceFailureException, Error: err}
|
||||
return nil, iamErr
|
||||
}
|
||||
// Reload in-memory identity maps so subsequent LookupByAccessKey calls
|
||||
// can see newly created or deleted keys immediately
|
||||
@@ -1214,7 +1196,7 @@ func (e *EmbeddedIamApi) DoActions(w http.ResponseWriter, r *http.Request) {
|
||||
values.Set("CreatedBy", createdBy)
|
||||
}
|
||||
|
||||
response, iamErr := e.ExecuteAction(values, false)
|
||||
response, iamErr := e.ExecuteAction(values)
|
||||
if iamErr != nil {
|
||||
e.writeIamErrorResponse(w, r, iamErr)
|
||||
return
|
||||
|
||||
@@ -87,19 +87,7 @@ func (e *EmbeddedIamApiForTest) DoActions(w http.ResponseWriter, r *http.Request
|
||||
var iamErr *iamError
|
||||
changed := true
|
||||
|
||||
action := r.Form.Get("Action")
|
||||
|
||||
if e.readOnly {
|
||||
switch action {
|
||||
case "ListUsers", "ListAccessKeys", "GetUser", "GetUserPolicy", "ListServiceAccounts", "GetServiceAccount":
|
||||
// Allowed read-only actions
|
||||
default:
|
||||
e.writeIamErrorResponse(w, r, &iamError{Code: s3err.GetAPIError(s3err.ErrAccessDenied).Code, Error: fmt.Errorf("IAM write operations are disabled on this server")})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch action {
|
||||
switch r.Form.Get("Action") {
|
||||
case "ListUsers":
|
||||
response = e.ListUsers(s3cfg, values)
|
||||
changed = false
|
||||
@@ -1703,7 +1691,7 @@ func TestEmbeddedIamExecuteAction(t *testing.T) {
|
||||
vals.Set("Action", "CreateUser")
|
||||
vals.Set("UserName", "ExecuteActionUser")
|
||||
|
||||
resp, iamErr := api.ExecuteAction(vals, false)
|
||||
resp, iamErr := api.ExecuteAction(vals)
|
||||
assert.Nil(t, iamErr)
|
||||
|
||||
// Verify response type
|
||||
@@ -1715,33 +1703,3 @@ func TestEmbeddedIamExecuteAction(t *testing.T) {
|
||||
assert.Len(t, api.mockConfig.Identities, 1)
|
||||
assert.Equal(t, "ExecuteActionUser", api.mockConfig.Identities[0].Name)
|
||||
}
|
||||
|
||||
// TestEmbeddedIamReadOnly tests that write operations are blocked when readOnly is true
|
||||
func TestEmbeddedIamReadOnly(t *testing.T) {
|
||||
api := NewEmbeddedIamApiForTest()
|
||||
api.readOnly = true
|
||||
|
||||
// Try CreateUser (Write)
|
||||
userName := aws.String("ReadOnlyUser")
|
||||
params := &iam.CreateUserInput{UserName: userName}
|
||||
req, _ := iam.New(session.New()).CreateUserRequest(params)
|
||||
_ = req.Build()
|
||||
|
||||
response, err := executeEmbeddedIamRequest(api, req.HTTPRequest, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, response.Code)
|
||||
|
||||
code, msg := extractEmbeddedIamErrorCodeAndMessage(response)
|
||||
assert.Equal(t, "AccessDenied", code)
|
||||
assert.Contains(t, msg, "IAM write operations are disabled")
|
||||
|
||||
// Try ListUsers (Read) - Should succeed
|
||||
paramsList := &iam.ListUsersInput{}
|
||||
reqList, _ := iam.New(session.New()).ListUsersRequest(paramsList)
|
||||
_ = reqList.Build()
|
||||
|
||||
outList := iamListUsersResponse{}
|
||||
responseList, err := executeEmbeddedIamRequest(api, reqList.HTTPRequest, &outList)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, responseList.Code)
|
||||
}
|
||||
|
||||
@@ -52,14 +52,11 @@ type S3ApiServerOption struct {
|
||||
ConcurrentUploadLimit int64
|
||||
ConcurrentFileUploadLimit int64
|
||||
EnableIam bool // Enable embedded IAM API on the same port
|
||||
IamReadOnly bool // Disable IAM write operations on this server
|
||||
Cipher bool // encrypt data on volume servers
|
||||
BindIp string
|
||||
GrpcPort int
|
||||
}
|
||||
|
||||
type S3ApiServer struct {
|
||||
s3_pb.UnimplementedSeaweedS3IamCacheServer
|
||||
s3_pb.UnimplementedSeaweedS3Server
|
||||
option *S3ApiServerOption
|
||||
iam *IdentityAccessManagement
|
||||
iamIntegration *S3IAMIntegration // Advanced IAM integration for JWT authentication
|
||||
@@ -117,17 +114,13 @@ func NewS3ApiServerWithStore(router *mux.Router, option *S3ApiServerOption, expl
|
||||
// Uses the battle-tested vidMap with filer-based lookups
|
||||
// Supports multiple filer addresses with automatic failover for high availability
|
||||
var filerClient *wdclient.FilerClient
|
||||
if len(option.Masters) > 0 {
|
||||
if len(option.Masters) > 0 && option.FilerGroup != "" {
|
||||
// Enable filer discovery via master
|
||||
masterMap := make(map[string]pb.ServerAddress)
|
||||
for i, addr := range option.Masters {
|
||||
masterMap[fmt.Sprintf("master%d", i)] = addr
|
||||
}
|
||||
clientHost := option.BindIp
|
||||
if clientHost == "0.0.0.0" || clientHost == "" {
|
||||
clientHost = util.DetectedHostAddress()
|
||||
}
|
||||
masterClient := wdclient.NewMasterClient(option.GrpcDialOption, option.FilerGroup, cluster.S3Type, pb.ServerAddress(util.JoinHostPort(clientHost, option.GrpcPort)), "", "", *pb.NewServiceDiscoveryFromMap(masterMap))
|
||||
masterClient := wdclient.NewMasterClient(option.GrpcDialOption, option.FilerGroup, cluster.S3Type, "", "", "", *pb.NewServiceDiscoveryFromMap(masterMap))
|
||||
// Start the master client connection loop - required for GetMaster() to work
|
||||
go masterClient.KeepConnectedToMaster(context.Background())
|
||||
|
||||
@@ -210,12 +203,8 @@ func NewS3ApiServerWithStore(router *mux.Router, option *S3ApiServerOption, expl
|
||||
|
||||
// Initialize embedded IAM API if enabled
|
||||
if option.EnableIam {
|
||||
s3ApiServer.embeddedIam = NewEmbeddedIamApi(s3ApiServer.credentialManager, iam, option.IamReadOnly)
|
||||
if option.IamReadOnly {
|
||||
glog.V(1).Infof("Embedded IAM API initialized in read-only mode (use -s3.iam.readOnly=false to enable write operations)")
|
||||
} else {
|
||||
glog.V(1).Infof("Embedded IAM API initialized in writable mode (WARNING: updates will not be propagated to other S3 servers)")
|
||||
}
|
||||
s3ApiServer.embeddedIam = NewEmbeddedIamApi(s3ApiServer.credentialManager, iam)
|
||||
glog.V(1).Infof("Embedded IAM API initialized (use -iam=false to disable)")
|
||||
}
|
||||
|
||||
if option.Config != "" {
|
||||
|
||||
@@ -2,6 +2,8 @@ package s3api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
||||
@@ -36,7 +38,92 @@ func (s3a *S3ApiServer) RemoveIdentity(ctx context.Context, req *iam_pb.RemoveId
|
||||
return &iam_pb.RemoveIdentityResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) executeAction(ctx context.Context, values url.Values) (interface{}, error) {
|
||||
if s3a.embeddedIam == nil {
|
||||
return nil, fmt.Errorf("embedded iam is disabled")
|
||||
}
|
||||
response, iamErr := s3a.embeddedIam.ExecuteAction(ctx, values)
|
||||
if iamErr != nil {
|
||||
return nil, fmt.Errorf("IAM error: %s - %v", iamErr.Code, iamErr.Error)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) ListUsers(ctx context.Context, req *iam_pb.ListUsersRequest) (*iam_pb.ListUsersResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "ListUsers")
|
||||
resp, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamResp, ok := resp.(iamListUsersResponse)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected IAM ListUsers response type %T", resp)
|
||||
}
|
||||
var usernames []string
|
||||
for _, user := range iamResp.ListUsersResult.Users {
|
||||
if user != nil && user.UserName != nil {
|
||||
usernames = append(usernames, *user.UserName)
|
||||
}
|
||||
}
|
||||
return &iam_pb.ListUsersResponse{Usernames: usernames}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) CreateUser(ctx context.Context, req *iam_pb.CreateUserRequest) (*iam_pb.CreateUserResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Identity == nil || req.Identity.Name == "" {
|
||||
return nil, fmt.Errorf("username name is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "CreateUser")
|
||||
values.Set("UserName", req.Identity.Name)
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.CreateUserResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) GetUser(ctx context.Context, req *iam_pb.GetUserRequest) (*iam_pb.GetUserResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "username is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "GetUser")
|
||||
values.Set("UserName", req.Username)
|
||||
resp, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamResp, ok := resp.(iamGetUserResponse)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected IAM GetUser response type %T", resp)
|
||||
}
|
||||
|
||||
var username string
|
||||
if iamResp.GetUserResult.User.UserName != nil {
|
||||
username = *iamResp.GetUserResult.User.UserName
|
||||
}
|
||||
|
||||
return &iam_pb.GetUserResponse{
|
||||
Identity: &iam_pb.Identity{
|
||||
Name: username,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) PutPolicy(ctx context.Context, req *iam_pb.PutPolicyRequest) (*iam_pb.PutPolicyResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Name == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "policy name is required")
|
||||
}
|
||||
@@ -58,6 +145,9 @@ func (s3a *S3ApiServer) PutPolicy(ctx context.Context, req *iam_pb.PutPolicyRequ
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) DeletePolicy(ctx context.Context, req *iam_pb.DeletePolicyRequest) (*iam_pb.DeletePolicyResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Name == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "policy name is required")
|
||||
}
|
||||
@@ -76,6 +166,9 @@ func (s3a *S3ApiServer) DeletePolicy(ctx context.Context, req *iam_pb.DeletePoli
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) GetPolicy(ctx context.Context, req *iam_pb.GetPolicyRequest) (*iam_pb.GetPolicyResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Name == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "policy name is required")
|
||||
}
|
||||
@@ -84,22 +177,320 @@ func (s3a *S3ApiServer) GetPolicy(ctx context.Context, req *iam_pb.GetPolicyRequ
|
||||
}
|
||||
policy, err := s3a.iam.GetPolicy(req.Name)
|
||||
if err != nil {
|
||||
return &iam_pb.GetPolicyResponse{}, nil // Not found is fine for cache
|
||||
return nil, status.Errorf(codes.NotFound, "policy not found: %s", req.Name)
|
||||
}
|
||||
return &iam_pb.GetPolicyResponse{
|
||||
Name: policy.Name,
|
||||
Content: policy.Content,
|
||||
Policy: &iam_pb.Policy{
|
||||
Name: req.Name,
|
||||
Content: policy,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) ListPolicies(ctx context.Context, req *iam_pb.ListPoliciesRequest) (*iam_pb.ListPoliciesResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
resp := &iam_pb.ListPoliciesResponse{}
|
||||
if s3a.iam == nil {
|
||||
return nil, status.Errorf(codes.Internal, "IAM not initialized")
|
||||
}
|
||||
policies := s3a.iam.ListPolicies()
|
||||
for _, policy := range policies {
|
||||
resp.Policies = append(resp.Policies, policy)
|
||||
}
|
||||
// TODO: Iterate over policies in map?
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) UpdateUser(ctx context.Context, req *iam_pb.UpdateUserRequest) (*iam_pb.UpdateUserResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return nil, fmt.Errorf("username is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "UpdateUser")
|
||||
values.Set("UserName", req.Username)
|
||||
// UpdateUser in DoActions expects "NewUserName" if renaming, but CreateUser just takes UserName.
|
||||
// Looking at s3api_embedded_iam.go, UpdateUser uses "NewUserName" to change name.
|
||||
if req.Identity != nil && req.Identity.Name != "" {
|
||||
values.Set("NewUserName", req.Identity.Name)
|
||||
}
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.UpdateUserResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) DeleteUser(ctx context.Context, req *iam_pb.DeleteUserRequest) (*iam_pb.DeleteUserResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return nil, fmt.Errorf("username is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "DeleteUser")
|
||||
values.Set("UserName", req.Username)
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.DeleteUserResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) ListAccessKeys(ctx context.Context, req *iam_pb.ListAccessKeysRequest) (*iam_pb.ListAccessKeysResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return nil, fmt.Errorf("username is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "ListAccessKeys")
|
||||
values.Set("UserName", req.Username)
|
||||
resp, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamResp, ok := resp.(iamListAccessKeysResponse)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected IAM ListAccessKeys response type %T", resp)
|
||||
}
|
||||
var accessKeys []*iam_pb.Credential
|
||||
for _, meta := range iamResp.ListAccessKeysResult.AccessKeyMetadata {
|
||||
if meta != nil && meta.AccessKeyId != nil && meta.Status != nil {
|
||||
accessKeys = append(accessKeys, &iam_pb.Credential{
|
||||
AccessKey: *meta.AccessKeyId,
|
||||
Status: *meta.Status,
|
||||
})
|
||||
}
|
||||
}
|
||||
return &iam_pb.ListAccessKeysResponse{AccessKeys: accessKeys}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) CreateAccessKey(ctx context.Context, req *iam_pb.CreateAccessKeyRequest) (*iam_pb.CreateAccessKeyResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return nil, fmt.Errorf("username is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "CreateAccessKey")
|
||||
values.Set("UserName", req.Username)
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.CreateAccessKeyResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) DeleteAccessKey(ctx context.Context, req *iam_pb.DeleteAccessKeyRequest) (*iam_pb.DeleteAccessKeyResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return nil, fmt.Errorf("username is required")
|
||||
}
|
||||
if req.AccessKey == "" {
|
||||
return nil, fmt.Errorf("access key is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "DeleteAccessKey")
|
||||
values.Set("UserName", req.Username)
|
||||
values.Set("AccessKeyId", req.AccessKey)
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.DeleteAccessKeyResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) PutUserPolicy(ctx context.Context, req *iam_pb.PutUserPolicyRequest) (*iam_pb.PutUserPolicyResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return nil, fmt.Errorf("username is required")
|
||||
}
|
||||
if req.PolicyName == "" {
|
||||
return nil, fmt.Errorf("policy name is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "PutUserPolicy")
|
||||
values.Set("UserName", req.Username)
|
||||
values.Set("PolicyName", req.PolicyName)
|
||||
values.Set("PolicyDocument", req.PolicyDocument)
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.PutUserPolicyResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) GetUserPolicy(ctx context.Context, req *iam_pb.GetUserPolicyRequest) (*iam_pb.GetUserPolicyResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return nil, fmt.Errorf("username is required")
|
||||
}
|
||||
if req.PolicyName == "" {
|
||||
return nil, fmt.Errorf("policy name is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "GetUserPolicy")
|
||||
values.Set("UserName", req.Username)
|
||||
values.Set("PolicyName", req.PolicyName)
|
||||
resp, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamResp, ok := resp.(iamGetUserPolicyResponse)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected IAM GetUserPolicy response type %T", resp)
|
||||
}
|
||||
return &iam_pb.GetUserPolicyResponse{
|
||||
Username: iamResp.GetUserPolicyResult.UserName,
|
||||
PolicyName: iamResp.GetUserPolicyResult.PolicyName,
|
||||
PolicyDocument: iamResp.GetUserPolicyResult.PolicyDocument,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) DeleteUserPolicy(ctx context.Context, req *iam_pb.DeleteUserPolicyRequest) (*iam_pb.DeleteUserPolicyResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return nil, fmt.Errorf("username is required")
|
||||
}
|
||||
if req.PolicyName == "" {
|
||||
return nil, fmt.Errorf("policy name is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "DeleteUserPolicy")
|
||||
values.Set("UserName", req.Username)
|
||||
values.Set("PolicyName", req.PolicyName)
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.DeleteUserPolicyResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) ListServiceAccounts(ctx context.Context, req *iam_pb.ListServiceAccountsRequest) (*iam_pb.ListServiceAccountsResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "ListServiceAccounts")
|
||||
resp, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamResp, ok := resp.(iamListServiceAccountsResponse)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected IAM ListServiceAccounts response type %T", resp)
|
||||
}
|
||||
var serviceAccounts []*iam_pb.ServiceAccount
|
||||
for _, sa := range iamResp.ListServiceAccountsResult.ServiceAccounts {
|
||||
if sa != nil {
|
||||
serviceAccounts = append(serviceAccounts, &iam_pb.ServiceAccount{
|
||||
Id: sa.ServiceAccountId,
|
||||
ParentUser: sa.ParentUser,
|
||||
Description: sa.Description,
|
||||
Credential: &iam_pb.Credential{
|
||||
AccessKey: sa.AccessKeyId,
|
||||
Status: sa.Status,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
return &iam_pb.ListServiceAccountsResponse{ServiceAccounts: serviceAccounts}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) CreateServiceAccount(ctx context.Context, req *iam_pb.CreateServiceAccountRequest) (*iam_pb.CreateServiceAccountResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.ServiceAccount == nil || req.ServiceAccount.CreatedBy == "" {
|
||||
return nil, fmt.Errorf("service account owner is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "CreateServiceAccount")
|
||||
values.Set("CreatedBy", req.ServiceAccount.CreatedBy)
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.CreateServiceAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) UpdateServiceAccount(ctx context.Context, req *iam_pb.UpdateServiceAccountRequest) (*iam_pb.UpdateServiceAccountResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Id == "" {
|
||||
return nil, fmt.Errorf("service account id is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "UpdateServiceAccount")
|
||||
values.Set("ServiceAccountId", req.Id)
|
||||
if req.ServiceAccount != nil && req.ServiceAccount.Disabled {
|
||||
values.Set("Status", "Inactive")
|
||||
}
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.UpdateServiceAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) DeleteServiceAccount(ctx context.Context, req *iam_pb.DeleteServiceAccountRequest) (*iam_pb.DeleteServiceAccountResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Id == "" {
|
||||
return nil, fmt.Errorf("service account id is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "DeleteServiceAccount")
|
||||
values.Set("ServiceAccountId", req.Id)
|
||||
_, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &iam_pb.DeleteServiceAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) GetServiceAccount(ctx context.Context, req *iam_pb.GetServiceAccountRequest) (*iam_pb.GetServiceAccountResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Id == "" {
|
||||
return nil, fmt.Errorf("service account id is required")
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("Action", "GetServiceAccount")
|
||||
values.Set("ServiceAccountId", req.Id)
|
||||
resp, err := s3a.executeAction(ctx, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamResp, ok := resp.(iamGetServiceAccountResponse)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected IAM GetServiceAccount response type %T", resp)
|
||||
}
|
||||
|
||||
return &iam_pb.GetServiceAccountResponse{
|
||||
ServiceAccount: &iam_pb.ServiceAccount{
|
||||
Id: iamResp.GetServiceAccountResult.ServiceAccount.ServiceAccountId,
|
||||
ParentUser: iamResp.GetServiceAccountResult.ServiceAccount.ParentUser,
|
||||
Description: iamResp.GetServiceAccountResult.ServiceAccount.Description,
|
||||
Credential: &iam_pb.Credential{
|
||||
AccessKey: iamResp.GetServiceAccountResult.ServiceAccount.AccessKeyId,
|
||||
Status: iamResp.GetServiceAccountResult.ServiceAccount.Status,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func setupRoutingTestServer(t *testing.T) *S3ApiServer {
|
||||
option: opt,
|
||||
iam: iam,
|
||||
credentialManager: iam.credentialManager,
|
||||
embeddedIam: NewEmbeddedIamApi(iam.credentialManager, iam, false),
|
||||
embeddedIam: NewEmbeddedIamApi(iam.credentialManager, iam),
|
||||
stsHandlers: &STSHandlers{},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user