github.com/minio/console@v1.4.1/api/admin_policies_test.go (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package api
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"reflect"
    26  	"testing"
    27  
    28  	"github.com/minio/console/models"
    29  	iampolicy "github.com/minio/pkg/v3/policy"
    30  	"github.com/stretchr/testify/assert"
    31  )
    32  
    33  func TestListPolicies(t *testing.T) {
    34  	ctx, cancel := context.WithCancel(context.Background())
    35  	defer cancel()
    36  	funcAssert := assert.New(t)
    37  	adminClient := AdminClientMock{}
    38  	// mock function response from listPolicies()
    39  	minioListPoliciesMock = func() (map[string]*iampolicy.Policy, error) {
    40  		var readonly iampolicy.Policy
    41  		var readwrite iampolicy.Policy
    42  		var diagnostis iampolicy.Policy
    43  
    44  		for _, p := range iampolicy.DefaultPolicies {
    45  			switch p.Name {
    46  			case "readonly":
    47  				readonly = p.Definition
    48  			case "readwrite":
    49  				readwrite = p.Definition
    50  			case "diagnostics":
    51  				diagnostis = p.Definition
    52  			}
    53  		}
    54  
    55  		return map[string]*iampolicy.Policy{
    56  			"readonly":    &readonly,
    57  			"readwrite":   &readwrite,
    58  			"diagnostics": &diagnostis,
    59  		}, nil
    60  	}
    61  	// Test-1 : listPolicies() Get response from minio client with three Canned Policies and return the same number on listPolicies()
    62  	function := "listPolicies()"
    63  	policiesList, err := listPolicies(ctx, adminClient)
    64  	if err != nil {
    65  		t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
    66  	}
    67  	// verify length of Policies is correct
    68  	funcAssert.Equal(3, len(policiesList), fmt.Sprintf("Failed on %s: length of Policies's lists is not the same", function))
    69  	// Test-2 : listPolicies() Return error and see that the error is handled correctly and returned
    70  	minioListPoliciesMock = func() (map[string]*iampolicy.Policy, error) {
    71  		return nil, errors.New("error")
    72  	}
    73  	_, err = listPolicies(ctx, adminClient)
    74  	if funcAssert.Error(err) {
    75  		funcAssert.Equal("error", err.Error())
    76  	}
    77  }
    78  
    79  func TestRemovePolicy(t *testing.T) {
    80  	ctx, cancel := context.WithCancel(context.Background())
    81  	defer cancel()
    82  	funcAssert := assert.New(t)
    83  	adminClient := AdminClientMock{}
    84  	// Test-1 : removePolicy() remove an existing policy
    85  	policyToRemove := "console-policy"
    86  	minioRemovePolicyMock = func(_ string) error {
    87  		return nil
    88  	}
    89  	function := "removePolicy()"
    90  	if err := removePolicy(ctx, adminClient, policyToRemove); err != nil {
    91  		t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
    92  	}
    93  	// Test-2 : removePolicy() Return error and see that the error is handled correctly and returned
    94  	minioRemovePolicyMock = func(_ string) error {
    95  		return errors.New("error")
    96  	}
    97  	if err := removePolicy(ctx, adminClient, policyToRemove); funcAssert.Error(err) {
    98  		funcAssert.Equal("error", err.Error())
    99  	}
   100  }
   101  
   102  func TestAddPolicy(t *testing.T) {
   103  	ctx, cancel := context.WithCancel(context.Background())
   104  	defer cancel()
   105  	funcAssert := assert.New(t)
   106  	adminClient := AdminClientMock{}
   107  	policyName := "new-policy"
   108  	policyDefinition := "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"s3:GetBucketLocation\",\"s3:GetObject\",\"s3:ListAllMyBuckets\"],\"Resource\":[\"arn:aws:s3:::*\"]}]}"
   109  	minioAddPolicyMock = func(_ string, _ *iampolicy.Policy) error {
   110  		return nil
   111  	}
   112  	minioGetPolicyMock = func(_ string) (*iampolicy.Policy, error) {
   113  		policy := "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"s3:GetBucketLocation\",\"s3:GetObject\",\"s3:ListAllMyBuckets\"],\"Resource\":[\"arn:aws:s3:::*\"]}]}"
   114  		iamp, err := iampolicy.ParseConfig(bytes.NewReader([]byte(policy)))
   115  		if err != nil {
   116  			return nil, err
   117  		}
   118  		return iamp, nil
   119  	}
   120  	assertPolicy := models.Policy{
   121  		Name:   "new-policy",
   122  		Policy: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"s3:GetBucketLocation\",\"s3:GetObject\",\"s3:ListAllMyBuckets\"],\"Resource\":[\"arn:aws:s3:::*\"]}]}",
   123  	}
   124  	// Test-1 : addPolicy() adds a new policy
   125  	function := "addPolicy()"
   126  	policy, err := addPolicy(ctx, adminClient, policyName, policyDefinition)
   127  	if err != nil {
   128  		t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
   129  	} else {
   130  		funcAssert.Equal(policy.Name, assertPolicy.Name)
   131  
   132  		var expectedPolicy iampolicy.Policy
   133  		var actualPolicy iampolicy.Policy
   134  		err1 := json.Unmarshal([]byte(policy.Policy), &expectedPolicy)
   135  		funcAssert.NoError(err1)
   136  		err2 := json.Unmarshal([]byte(assertPolicy.Policy), &actualPolicy)
   137  		funcAssert.NoError(err2)
   138  		funcAssert.Equal(expectedPolicy, actualPolicy)
   139  	}
   140  	// Test-2 : addPolicy() got an error while adding policy
   141  	minioAddPolicyMock = func(_ string, _ *iampolicy.Policy) error {
   142  		return errors.New("error")
   143  	}
   144  	if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); funcAssert.Error(err) {
   145  		funcAssert.Equal("error", err.Error())
   146  	}
   147  	// Test-3 : addPolicy() got an error while retrieving policy
   148  	minioAddPolicyMock = func(_ string, _ *iampolicy.Policy) error {
   149  		return nil
   150  	}
   151  	minioGetPolicyMock = func(_ string) (*iampolicy.Policy, error) {
   152  		return nil, errors.New("error")
   153  	}
   154  	if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); funcAssert.Error(err) {
   155  		funcAssert.Equal("error", err.Error())
   156  	}
   157  }
   158  
   159  func TestSetPolicy(t *testing.T) {
   160  	ctx, cancel := context.WithCancel(context.Background())
   161  	defer cancel()
   162  	funcAssert := assert.New(t)
   163  	adminClient := AdminClientMock{}
   164  	policyName := "readOnly"
   165  	entityName := "alevsk"
   166  	entityObject := models.PolicyEntityUser
   167  	minioSetPolicyMock = func(_, _ string, _ bool) error {
   168  		return nil
   169  	}
   170  	// Test-1 : SetPolicy() set policy to user
   171  	function := "SetPolicy()"
   172  	err := SetPolicy(ctx, adminClient, policyName, entityName, entityObject)
   173  	if err != nil {
   174  		t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
   175  	}
   176  	// Test-2 : SetPolicy() set policy to group
   177  	entityObject = models.PolicyEntityGroup
   178  	err = SetPolicy(ctx, adminClient, policyName, entityName, entityObject)
   179  	if err != nil {
   180  		t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
   181  	}
   182  	// Test-3 : SetPolicy() set policy to user and get error
   183  	entityObject = models.PolicyEntityUser
   184  	minioSetPolicyMock = func(_, _ string, _ bool) error {
   185  		return errors.New("error")
   186  	}
   187  	if err := SetPolicy(ctx, adminClient, policyName, entityName, entityObject); funcAssert.Error(err) {
   188  		funcAssert.Equal("error", err.Error())
   189  	}
   190  	// Test-4 : SetPolicy() set policy to group and get error
   191  	entityObject = models.PolicyEntityGroup
   192  	minioSetPolicyMock = func(_, _ string, _ bool) error {
   193  		return errors.New("error")
   194  	}
   195  	if err := SetPolicy(ctx, adminClient, policyName, entityName, entityObject); funcAssert.Error(err) {
   196  		funcAssert.Equal("error", err.Error())
   197  	}
   198  }
   199  
   200  func Test_SetPolicyMultiple(t *testing.T) {
   201  	ctx, cancel := context.WithCancel(context.Background())
   202  	defer cancel()
   203  	adminClient := AdminClientMock{}
   204  
   205  	type args struct {
   206  		policyName    string
   207  		users         []models.IamEntity
   208  		groups        []models.IamEntity
   209  		setPolicyFunc func(policyName, entityName string, isGroup bool) error
   210  	}
   211  	tests := []struct {
   212  		name          string
   213  		args          args
   214  		errorExpected error
   215  	}{
   216  		{
   217  			name: "Set policy to multiple users and groups",
   218  			args: args{
   219  				policyName: "readonly",
   220  				users:      []models.IamEntity{"user1", "user2"},
   221  				groups:     []models.IamEntity{"group1", "group2"},
   222  				setPolicyFunc: func(_, _ string, _ bool) error {
   223  					return nil
   224  				},
   225  			},
   226  			errorExpected: nil,
   227  		},
   228  		{
   229  			name: "Return error on set policy function",
   230  			args: args{
   231  				policyName: "readonly",
   232  				users:      []models.IamEntity{"user1", "user2"},
   233  				groups:     []models.IamEntity{"group1", "group2"},
   234  				setPolicyFunc: func(_, _ string, _ bool) error {
   235  					return errors.New("error set")
   236  				},
   237  			},
   238  			errorExpected: errors.New("error set"),
   239  		},
   240  		{
   241  			// Description: Empty lists of users and groups are acceptable
   242  			name: "Empty lists of users and groups",
   243  			args: args{
   244  				policyName: "readonly",
   245  				users:      []models.IamEntity{},
   246  				groups:     []models.IamEntity{},
   247  				setPolicyFunc: func(_, _ string, _ bool) error {
   248  					return nil
   249  				},
   250  			},
   251  			errorExpected: nil,
   252  		},
   253  	}
   254  	for _, tt := range tests {
   255  		t.Run(tt.name, func(_ *testing.T) {
   256  			minioSetPolicyMock = tt.args.setPolicyFunc
   257  			got := setPolicyMultipleEntities(ctx, adminClient, tt.args.policyName, tt.args.users, tt.args.groups)
   258  			if !reflect.DeepEqual(got, tt.errorExpected) {
   259  				ji, _ := json.Marshal(got)
   260  				vi, _ := json.Marshal(tt.errorExpected)
   261  				t.Errorf("got %s want %s", ji, vi)
   262  			}
   263  		})
   264  	}
   265  }
   266  
   267  func Test_policyMatchesBucket(t *testing.T) {
   268  	type args struct {
   269  		ctx    context.Context
   270  		policy *models.Policy
   271  		bucket string
   272  	}
   273  	tests := []struct {
   274  		name string
   275  		args args
   276  		want bool
   277  	}{
   278  		{
   279  			name: "Test1",
   280  			args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
   281      "Version": "2012-10-17",
   282      "Statement": [
   283          {
   284              "Effect": "Allow",
   285              "Action": [
   286                  "admin:*"
   287              ]
   288          },
   289          {
   290              "Effect": "Allow",
   291              "Action": [
   292                  "s3:*"
   293              ],
   294              "Resource": [
   295                  "arn:aws:s3:::*"
   296              ]
   297          }
   298      ]
   299  }`}, bucket: "test1"},
   300  			want: true,
   301  		},
   302  		{
   303  			name: "Test2",
   304  			args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
   305  				"Version": "2012-10-17",
   306  				"Statement": [
   307  			{
   308  				"Effect": "Allow",
   309  				"Action": [
   310  				"s3:*"
   311  			],
   312  				"Resource": [
   313  				"arn:aws:s3:::bucket1"
   314  			]
   315  			}
   316  			]
   317  			}`}, bucket: "test1"},
   318  			want: false,
   319  		},
   320  		{
   321  			name: "Test3",
   322  			args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
   323      "Version": "2012-10-17",
   324      "Statement": [
   325          {
   326              "Sid": "VisualEditor0",
   327              "Effect": "Allow",
   328              "Action": [
   329                  "s3:ListStorageLensConfigurations",
   330                  "s3:GetAccessPoint",
   331                  "s3:PutAccountPublicAccessBlock",
   332                  "s3:GetAccountPublicAccessBlock",
   333                  "s3:ListAllMyBuckets",
   334                  "s3:ListAccessPoints",
   335                  "s3:ListJobs",
   336                  "s3:PutStorageLensConfiguration",
   337                  "s3:CreateJob"
   338              ],
   339              "Resource": "*"
   340          },
   341          {
   342              "Sid": "VisualEditor1",
   343              "Effect": "Allow",
   344              "Action": "s3:*",
   345              "Resource": [
   346                  "arn:aws:s3:::test",
   347                  "arn:aws:s3:::test/*",
   348                  "arn:aws:s3:::lkasdkljasd090901",
   349                  "arn:aws:s3:::lkasdkljasd090901/*"
   350                  ]
   351          }
   352      ]
   353  	}`}, bucket: "test1"},
   354  			want: false,
   355  		},
   356  		{
   357  			name: "Test4",
   358  			args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
   359  				"Version": "2012-10-17",
   360  				"Statement": [
   361  			{
   362  				"Effect": "Allow",
   363  				"Action": [
   364  				"s3:*"
   365  			],
   366  				"Resource": [
   367  				"arn:aws:s3:::bucket1"
   368  			]
   369  			}
   370  			]
   371  			}`}, bucket: "bucket1"},
   372  			want: true,
   373  		},
   374  	}
   375  	for _, tt := range tests {
   376  		t.Run(tt.name, func(_ *testing.T) {
   377  			if got := policyMatchesBucket(tt.args.ctx, tt.args.policy, tt.args.bucket); got != tt.want {
   378  				t.Errorf("policyMatchesBucket() = %v, want %v", got, tt.want)
   379  			}
   380  		})
   381  	}
   382  }