Re-implement policy handling in react (#1234)

Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
Lenin Alevski
2021-11-18 08:25:01 -08:00
committed by GitHub
parent f5234d2830
commit aae493ac82
30 changed files with 1098 additions and 1132 deletions

View File

@@ -458,7 +458,7 @@ func listExternalBucketsResponse(params user_api.ListExternalBucketsParams) (*mo
// create a minioClient interface implementation
// defining the client to be used
remoteClient := AdminClient{Client: remoteAdmin}
buckets, err := getAccountBuckets(ctx, remoteClient, *params.Body.AccessKey)
buckets, err := getAccountBuckets(ctx, remoteClient)
if err != nil {
return nil, prepareError(err)
}

View File

@@ -3661,12 +3661,6 @@ func init() {
"access": {
"$ref": "#/definitions/bucketAccess"
},
"allowedActions": {
"type": "array",
"items": {
"type": "string"
}
},
"creation_date": {
"type": "string"
},
@@ -3709,9 +3703,6 @@ func init() {
}
}
},
"manage": {
"type": "boolean"
},
"name": {
"type": "string",
"minLength": 3
@@ -5204,9 +5195,14 @@ func init() {
"type": "string"
}
},
"policy": {
"permissions": {
"type": "object",
"$ref": "#/definitions/iamPolicy"
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"status": {
"type": "string",
@@ -9482,12 +9478,6 @@ func init() {
"access": {
"$ref": "#/definitions/bucketAccess"
},
"allowedActions": {
"type": "array",
"items": {
"type": "string"
}
},
"creation_date": {
"type": "string"
},
@@ -9530,9 +9520,6 @@ func init() {
}
}
},
"manage": {
"type": "boolean"
},
"name": {
"type": "string",
"minLength": 3
@@ -11025,9 +11012,14 @@ func init() {
"type": "string"
}
},
"policy": {
"permissions": {
"type": "object",
"$ref": "#/definitions/iamPolicy"
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"status": {
"type": "string",

View File

@@ -24,10 +24,6 @@ import (
"strings"
"time"
"github.com/minio/pkg/bucket/policy/condition"
"github.com/minio/console/pkg/acl"
"github.com/minio/mc/cmd"
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio-go/v7"
@@ -290,25 +286,13 @@ func getBucketVersionedResponse(session *models.Principal, bucketName string) (*
}
// getAccountBuckets fetches a list of all buckets allowed to that particular client from MinIO Servers
func getAccountBuckets(ctx context.Context, client MinioAdmin, accessKey string) ([]*models.Bucket, error) {
func getAccountBuckets(ctx context.Context, client MinioAdmin) ([]*models.Bucket, error) {
info, err := client.AccountInfo(ctx)
if err != nil {
return []*models.Bucket{}, err
}
policyInfo, err := getAccountPolicy(ctx, client)
if err != nil {
return nil, err
}
var bucketInfos []*models.Bucket
for _, bucket := range info.Buckets {
var bucketAdminRole bool
conditionValues := map[string][]string{
condition.AWSUsername.Name(): {accessKey},
}
bucketActions := policyInfo.IsAllowedActions(bucket.Name, "", conditionValues)
bucketAdminRoleActions := bucketActions.Intersection(acl.BucketAdminRole)
bucketAdminRole = len(bucketAdminRoleActions) > 0
bucketElem := &models.Bucket{
CreationDate: bucket.Created.Format(time.RFC3339),
Details: &models.BucketDetails{
@@ -321,7 +305,6 @@ func getAccountBuckets(ctx context.Context, client MinioAdmin, accessKey string)
Name: swag.String(bucket.Name),
Objects: int64(bucket.Objects),
Size: int64(bucket.Size),
Manage: bucketAdminRole,
}
if bucket.Details != nil {
@@ -358,7 +341,7 @@ func getListBucketsResponse(session *models.Principal) (*models.ListBucketsRespo
// create a minioClient interface implementation
// defining the client to be used
adminClient := AdminClient{Client: mAdmin}
buckets, err := getAccountBuckets(ctx, adminClient, session.AccountAccessKey)
buckets, err := getAccountBuckets(ctx, adminClient)
if err != nil {
return nil, prepareError(err)
}
@@ -493,21 +476,12 @@ func getBucketSetPolicyResponse(session *models.Principal, bucketName string, re
// create a minioClient interface implementation
// defining the client to be used
minioClient := minioClient{client: mClient}
mAdmin, err := NewMinioAdminClient(session)
if err != nil {
return nil, prepareError(err)
}
// create a minioClient interface implementation
// defining the client to be used
adminClient := AdminClient{Client: mAdmin}
// set bucket access policy
if err := setBucketAccessPolicy(ctx, minioClient, bucketName, *req.Access); err != nil {
return nil, prepareError(err)
}
// get updated bucket details and return it
bucket, err := getBucketInfo(ctx, minioClient, adminClient, bucketName, session.AccountAccessKey)
bucket, err := getBucketInfo(ctx, minioClient, bucketName)
if err != nil {
return nil, prepareError(err)
}
@@ -566,29 +540,7 @@ func getDeleteBucketResponse(session *models.Principal, params user_api.DeleteBu
}
// getBucketInfo return bucket information including name, policy access, size and creation date
func getBucketInfo(ctx context.Context, client MinioClient, adminClient MinioAdmin, bucketName string, accountName string) (*models.Bucket, error) {
// Get Account Policy
policyInfo, err := getAccountPolicy(ctx, adminClient)
if err != nil {
return nil, err
}
var bucketAdminRole bool
// Retrieve list of allowed bucketActionsArray on the bucket
// TODO: Add all the possible variables
conditionValues := map[string][]string{
condition.AWSUsername.Name(): {accountName},
}
bucketActions := policyInfo.IsAllowedActions(bucketName, "", conditionValues)
// Check if one of these bucketActionsArray belongs to administrative bucketActionsArray
bucketAdminRoleActions := bucketActions.Intersection(acl.BucketAdminRole)
bucketAdminRole = len(bucketAdminRoleActions) > 0
var bucketActionsArray []string
for _, action := range bucketActions.ToSlice() {
bucketActionsArray = append(bucketActionsArray, string(action))
}
func getBucketInfo(ctx context.Context, client MinioClient, bucketName string) (*models.Bucket, error) {
var bucketAccess models.BucketAccess
policyStr, err := client.getBucketPolicy(context.Background(), bucketName)
if err != nil {
@@ -611,29 +563,21 @@ func getBucketInfo(ctx context.Context, client MinioClient, adminClient MinioAdm
bucketAccess = models.BucketAccessCUSTOM
}
bucketTags, err := client.GetBucketTagging(ctx, bucketName)
var bucket *models.Bucket
if err == nil && bucketTags != nil {
bucket = &models.Bucket{
Name: &bucketName,
Access: &bucketAccess,
CreationDate: "", // to be implemented
Size: 0, // to be implemented
AllowedActions: bucketActionsArray,
Manage: bucketAdminRole,
Details: &models.BucketDetails{Tags: bucketTags.ToMap()},
}
} else {
bucket = &models.Bucket{
Name: &bucketName,
Access: &bucketAccess,
CreationDate: "", // to be implemented
Size: 0, // to be implemented
AllowedActions: bucketActionsArray,
Manage: bucketAdminRole,
Details: &models.BucketDetails{},
}
if err != nil {
// we can tolerate this error
LogError("error getting bucket tags: %v", err)
}
return bucket, nil
bucketDetails := &models.BucketDetails{}
if bucketTags != nil {
bucketDetails.Tags = bucketTags.ToMap()
}
return &models.Bucket{
Name: &bucketName,
Access: &bucketAccess,
CreationDate: "", // to be implemented
Size: 0, // to be implemented
Details: bucketDetails,
}, nil
}
// getBucketInfoResponse calls getBucketInfo() to get the bucket's info
@@ -647,16 +591,7 @@ func getBucketInfoResponse(session *models.Principal, params user_api.BucketInfo
// create a minioClient interface implementation
// defining the client to be used
minioClient := minioClient{client: mClient}
mAdmin, err := NewMinioAdminClient(session)
if err != nil {
return nil, prepareError(err)
}
// create a minioClient interface implementation
// defining the client to be used
adminClient := AdminClient{Client: mAdmin}
bucket, err := getBucketInfo(ctx, minioClient, adminClient, params.Name, session.AccountAccessKey)
bucket, err := getBucketInfo(ctx, minioClient, params.Name)
if err != nil {
return nil, prepareError(err)
}

View File

@@ -180,7 +180,7 @@ func TestListBucket(t *testing.T) {
// get list buckets response this response should have Name, CreationDate, Size and Access
// as part of of each bucket
function := "getaAcountUsageInfo()"
bucketList, err := getAccountBuckets(ctx, adminClient, "")
bucketList, err := getAccountBuckets(ctx, adminClient)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
@@ -197,7 +197,7 @@ func TestListBucket(t *testing.T) {
minioAccountInfoMock = func(ctx context.Context) (madmin.AccountInfo, error) {
return madmin.AccountInfo{}, errors.New("error")
}
_, err = getAccountBuckets(ctx, adminClient, "")
_, err = getAccountBuckets(ctx, adminClient)
if assert.Error(err) {
assert.Equal("error", err.Error())
}
@@ -257,7 +257,6 @@ func TestBucketInfo(t *testing.T) {
// mock minIO client
minClient := minioClientMock{}
ctx := context.Background()
adminClient := adminClientMock{}
function := "getBucketInfo()"
// Test-1: getBucketInfo() get a bucket with PRIVATE access
@@ -308,7 +307,7 @@ func TestBucketInfo(t *testing.T) {
return mockBucketList, nil
}
bucketInfo, err := getBucketInfo(ctx, minClient, adminClient, bucketToSet, "user1")
bucketInfo, err := getBucketInfo(ctx, minClient, bucketToSet)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
@@ -330,7 +329,7 @@ func TestBucketInfo(t *testing.T) {
CreationDate: "", // to be implemented
Size: 0, // to be implemented
}
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet, "bucket1")
bucketInfo, err = getBucketInfo(ctx, minClient, bucketToSet)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
@@ -352,7 +351,7 @@ func TestBucketInfo(t *testing.T) {
CreationDate: "", // to be implemented
Size: 0, // to be implemented
}
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet, "bucket1")
bucketInfo, err = getBucketInfo(ctx, minClient, bucketToSet)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
@@ -373,7 +372,7 @@ func TestBucketInfo(t *testing.T) {
CreationDate: "", // to be implemented
Size: 0, // to be implemented
}
_, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet, "bucket1")
_, err = getBucketInfo(ctx, minClient, bucketToSet)
if assert.Error(err) {
assert.Equal("invalid character 'p' looking for beginning of value", err.Error())
}

View File

@@ -23,6 +23,10 @@ import (
"net/url"
"time"
"github.com/minio/pkg/bucket/policy/condition"
minioIAMPolicy "github.com/minio/pkg/iam/policy"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/console/models"
"github.com/minio/console/pkg/acl"
@@ -91,14 +95,82 @@ func getSessionResponse(session *models.Principal) (*models.SessionResponse, *mo
if err != nil {
return nil, prepareError(err, errorGenericInvalidSession)
}
// by default every user starts with an empty array of available actions
// by default every user starts with an empty array of available val
// therefore we would have access only to pages that doesn't require any privilege
// ie: service-account page
var actions []string
// if a policy is assigned to this user we parse the actions from there
// if a policy is assigned to this user we parse the val from there
if policy != nil {
actions = acl.GetActionsStringFromPolicy(policy)
}
// This actions will be global, meaning has to be attached to all resources
conditionValues := map[string][]string{
condition.AWSUsername.Name(): {session.AccountAccessKey},
}
defaultActions := policy.IsAllowedActions("", "", conditionValues)
consoleResourceName := "console-ui"
permissions := map[string]minioIAMPolicy.ActionSet{
consoleResourceName: defaultActions,
}
deniedActions := map[string]minioIAMPolicy.ActionSet{}
for _, statement := range policy.Statements {
for _, resource := range statement.Resources.ToSlice() {
resourceName := resource.String()
statementActions := statement.Actions.ToSlice()
if statement.Effect == "Allow" {
// check if val are denied before adding them to the map
var allowedActions []minioIAMPolicy.Action
if dActions, ok := deniedActions[resourceName]; ok {
for _, action := range statementActions {
if len(dActions.Intersection(minioIAMPolicy.NewActionSet(action))) == 0 {
// It's ok to allow this action
allowedActions = append(allowedActions, action)
}
}
} else {
allowedActions = statementActions
}
// Add validated actions
if resourceActions, ok := permissions[resourceName]; ok {
mergedActions := append(resourceActions.ToSlice(), allowedActions...)
permissions[resourceName] = minioIAMPolicy.NewActionSet(mergedActions...)
} else {
mergedActions := append(defaultActions.ToSlice(), allowedActions...)
permissions[resourceName] = minioIAMPolicy.NewActionSet(mergedActions...)
}
} else {
// Add new banned actions to the map
if resourceActions, ok := deniedActions[resourceName]; ok {
mergedActions := append(resourceActions.ToSlice(), statementActions...)
deniedActions[resourceName] = minioIAMPolicy.NewActionSet(mergedActions...)
} else {
deniedActions[resourceName] = statement.Actions
}
// Remove existing val from key if necessary
if currentResourceActions, ok := permissions[resourceName]; ok {
var newAllowedActions []minioIAMPolicy.Action
for _, action := range currentResourceActions.ToSlice() {
if len(deniedActions[resourceName].Intersection(minioIAMPolicy.NewActionSet(action))) == 0 {
// It's ok to allow this action
newAllowedActions = append(newAllowedActions, action)
}
}
permissions[resourceName] = minioIAMPolicy.NewActionSet(newAllowedActions...)
}
}
}
}
resourcePermissions := map[string][]string{}
for key, val := range permissions {
var resourceActions []string
for _, action := range val.ToSlice() {
resourceActions = append(resourceActions, string(action))
}
resourcePermissions[key] = resourceActions
}
rawPolicy, err := json.Marshal(policy)
if err != nil {
return nil, prepareError(err, errorGenericInvalidSession)
@@ -114,7 +186,7 @@ func getSessionResponse(session *models.Principal) (*models.SessionResponse, *mo
Status: models.SessionResponseStatusOk,
Operator: false,
DistributedMode: isErasureMode(),
Policy: sessionPolicy,
Permissions: resourcePermissions,
}
return sessionResp, nil
}