github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/admin-handlers-users_test.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"crypto/sha256"
    24  	"encoding/hex"
    25  	"encoding/json"
    26  	"fmt"
    27  	"io"
    28  	"net/http"
    29  	"net/url"
    30  	"runtime"
    31  	"strings"
    32  	"testing"
    33  	"time"
    34  
    35  	"github.com/minio/madmin-go/v3"
    36  	"github.com/minio/minio-go/v7"
    37  	"github.com/minio/minio-go/v7/pkg/credentials"
    38  	"github.com/minio/minio-go/v7/pkg/s3utils"
    39  	"github.com/minio/minio-go/v7/pkg/set"
    40  	"github.com/minio/minio-go/v7/pkg/signer"
    41  	"github.com/minio/minio/internal/auth"
    42  	"github.com/minio/pkg/v2/env"
    43  )
    44  
    45  const (
    46  	testDefaultTimeout = 30 * time.Second
    47  )
    48  
    49  // API suite container for IAM
    50  type TestSuiteIAM struct {
    51  	TestSuiteCommon
    52  
    53  	ServerTypeDescription string
    54  
    55  	// Flag to turn on tests for etcd backend IAM
    56  	withEtcdBackend bool
    57  
    58  	endpoint string
    59  	adm      *madmin.AdminClient
    60  	client   *minio.Client
    61  }
    62  
    63  func newTestSuiteIAM(c TestSuiteCommon, withEtcdBackend bool) *TestSuiteIAM {
    64  	etcdStr := ""
    65  	if withEtcdBackend {
    66  		etcdStr = " (with etcd backend)"
    67  	}
    68  	return &TestSuiteIAM{
    69  		TestSuiteCommon:       c,
    70  		ServerTypeDescription: fmt.Sprintf("%s%s", c.serverType, etcdStr),
    71  		withEtcdBackend:       withEtcdBackend,
    72  	}
    73  }
    74  
    75  func (s *TestSuiteIAM) iamSetup(c *check) {
    76  	var err error
    77  	// strip url scheme from endpoint
    78  	s.endpoint = strings.TrimPrefix(s.endPoint, "http://")
    79  	if s.secure {
    80  		s.endpoint = strings.TrimPrefix(s.endPoint, "https://")
    81  	}
    82  
    83  	s.adm, err = madmin.New(s.endpoint, s.accessKey, s.secretKey, s.secure)
    84  	if err != nil {
    85  		c.Fatalf("error creating admin client: %v", err)
    86  	}
    87  	// Set transport, so that TLS is handled correctly.
    88  	s.adm.SetCustomTransport(s.TestSuiteCommon.client.Transport)
    89  
    90  	s.client, err = minio.New(s.endpoint, &minio.Options{
    91  		Creds:     credentials.NewStaticV4(s.accessKey, s.secretKey, ""),
    92  		Secure:    s.secure,
    93  		Transport: s.TestSuiteCommon.client.Transport,
    94  	})
    95  	if err != nil {
    96  		c.Fatalf("error creating minio client: %v", err)
    97  	}
    98  }
    99  
   100  // List of all IAM test suites (i.e. test server configuration combinations)
   101  // common to tests.
   102  var iamTestSuites = func() []*TestSuiteIAM {
   103  	baseTestCases := []TestSuiteCommon{
   104  		// Init and run test on ErasureSD backend with signature v4.
   105  		{serverType: "ErasureSD", signer: signerV4},
   106  		// Init and run test on ErasureSD backend, with tls enabled.
   107  		{serverType: "ErasureSD", signer: signerV4, secure: true},
   108  		// Init and run test on Erasure backend.
   109  		{serverType: "Erasure", signer: signerV4},
   110  		// Init and run test on ErasureSet backend.
   111  		{serverType: "ErasureSet", signer: signerV4},
   112  	}
   113  	testCases := []*TestSuiteIAM{}
   114  	for _, bt := range baseTestCases {
   115  		testCases = append(testCases,
   116  			newTestSuiteIAM(bt, false),
   117  			newTestSuiteIAM(bt, true),
   118  		)
   119  	}
   120  	return testCases
   121  }()
   122  
   123  const (
   124  	EnvTestEtcdBackend = "_MINIO_ETCD_TEST_SERVER"
   125  )
   126  
   127  func (s *TestSuiteIAM) setUpEtcd(c *check, etcdServer string) {
   128  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   129  	defer cancel()
   130  
   131  	configCmds := []string{
   132  		"etcd",
   133  		"endpoints=" + etcdServer,
   134  		"path_prefix=" + mustGetUUID(),
   135  	}
   136  	_, err := s.adm.SetConfigKV(ctx, strings.Join(configCmds, " "))
   137  	if err != nil {
   138  		c.Fatalf("unable to setup Etcd for tests: %v", err)
   139  	}
   140  
   141  	s.RestartIAMSuite(c)
   142  }
   143  
   144  func (s *TestSuiteIAM) SetUpSuite(c *check) {
   145  	// If etcd backend is specified and etcd server is not present, the test
   146  	// is skipped.
   147  	etcdServer := env.Get(EnvTestEtcdBackend, "")
   148  	if s.withEtcdBackend && etcdServer == "" {
   149  		c.Skip("Skipping etcd backend IAM test as no etcd server is configured.")
   150  	}
   151  
   152  	s.TestSuiteCommon.SetUpSuite(c)
   153  
   154  	s.iamSetup(c)
   155  
   156  	if s.withEtcdBackend {
   157  		s.setUpEtcd(c, etcdServer)
   158  	}
   159  }
   160  
   161  func (s *TestSuiteIAM) RestartIAMSuite(c *check) {
   162  	s.TestSuiteCommon.RestartTestServer(c)
   163  
   164  	s.iamSetup(c)
   165  }
   166  
   167  func (s *TestSuiteIAM) getAdminClient(c *check, accessKey, secretKey, sessionToken string) *madmin.AdminClient {
   168  	madmClnt, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
   169  		Creds:  credentials.NewStaticV4(accessKey, secretKey, sessionToken),
   170  		Secure: s.secure,
   171  	})
   172  	if err != nil {
   173  		c.Fatalf("error creating user admin client: %s", err)
   174  	}
   175  	madmClnt.SetCustomTransport(s.TestSuiteCommon.client.Transport)
   176  	return madmClnt
   177  }
   178  
   179  func (s *TestSuiteIAM) getUserClient(c *check, accessKey, secretKey, sessionToken string) *minio.Client {
   180  	client, err := minio.New(s.endpoint, &minio.Options{
   181  		Creds:     credentials.NewStaticV4(accessKey, secretKey, sessionToken),
   182  		Secure:    s.secure,
   183  		Transport: s.TestSuiteCommon.client.Transport,
   184  	})
   185  	if err != nil {
   186  		c.Fatalf("error creating user minio client: %s", err)
   187  	}
   188  	return client
   189  }
   190  
   191  func TestIAMInternalIDPServerSuite(t *testing.T) {
   192  	if runtime.GOOS == globalWindowsOSName {
   193  		t.Skip("windows is clunky disable these tests")
   194  	}
   195  	for i, testCase := range iamTestSuites {
   196  		t.Run(
   197  			fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription),
   198  			func(t *testing.T) {
   199  				suite := testCase
   200  				c := &check{t, testCase.serverType}
   201  
   202  				suite.SetUpSuite(c)
   203  				suite.TestUserCreate(c)
   204  				suite.TestUserPolicyEscalationBug(c)
   205  				suite.TestPolicyCreate(c)
   206  				suite.TestCannedPolicies(c)
   207  				suite.TestGroupAddRemove(c)
   208  				suite.TestServiceAccountOpsByAdmin(c)
   209  				suite.TestServiceAccountPrivilegeEscalationBug(c)
   210  				suite.TestServiceAccountOpsByUser(c)
   211  				suite.TestServiceAccountDurationSecondsCondition(c)
   212  				suite.TestAddServiceAccountPerms(c)
   213  				suite.TearDownSuite(c)
   214  			},
   215  		)
   216  	}
   217  }
   218  
   219  func (s *TestSuiteIAM) TestUserCreate(c *check) {
   220  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   221  	defer cancel()
   222  
   223  	// 1. Create a user.
   224  	accessKey, secretKey := mustGenerateCredentials(c)
   225  	err := s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
   226  	if err != nil {
   227  		c.Fatalf("Unable to set user: %v", err)
   228  	}
   229  
   230  	// 2. Check new user appears in listing
   231  	usersMap, err := s.adm.ListUsers(ctx)
   232  	if err != nil {
   233  		c.Fatalf("error listing: %v", err)
   234  	}
   235  	v, ok := usersMap[accessKey]
   236  	if !ok {
   237  		c.Fatalf("user not listed: %s", accessKey)
   238  	}
   239  	c.Assert(v.Status, madmin.AccountEnabled)
   240  
   241  	// 3. Associate policy and check that user can access
   242  	err = s.adm.SetPolicy(ctx, "readwrite", accessKey, false)
   243  	if err != nil {
   244  		c.Fatalf("unable to set policy: %v", err)
   245  	}
   246  
   247  	client := s.getUserClient(c, accessKey, secretKey, "")
   248  	err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{})
   249  	if err != nil {
   250  		c.Fatalf("user could not create bucket: %v", err)
   251  	}
   252  
   253  	// 3.10. Check that user's password can be updated.
   254  	_, newSecretKey := mustGenerateCredentials(c)
   255  	err = s.adm.SetUser(ctx, accessKey, newSecretKey, madmin.AccountEnabled)
   256  	if err != nil {
   257  		c.Fatalf("Unable to update user's secret key: %v", err)
   258  	}
   259  	// 3.10.1 Check that old password no longer works.
   260  	err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{})
   261  	if err == nil {
   262  		c.Fatalf("user was unexpectedly able to create bucket with bad password!")
   263  	}
   264  	// 3.10.2 Check that new password works.
   265  	client = s.getUserClient(c, accessKey, newSecretKey, "")
   266  	err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{})
   267  	if err != nil {
   268  		c.Fatalf("user could not create bucket: %v", err)
   269  	}
   270  
   271  	// 4. Check that user can be disabled and verify it.
   272  	err = s.adm.SetUserStatus(ctx, accessKey, madmin.AccountDisabled)
   273  	if err != nil {
   274  		c.Fatalf("could not set user account to disabled")
   275  	}
   276  	usersMap, err = s.adm.ListUsers(ctx)
   277  	if err != nil {
   278  		c.Fatalf("error listing: %v", err)
   279  	}
   280  	v, ok = usersMap[accessKey]
   281  	if !ok {
   282  		c.Fatalf("user was not listed after disabling: %s", accessKey)
   283  	}
   284  	c.Assert(v.Status, madmin.AccountDisabled)
   285  	err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{})
   286  	if err == nil {
   287  		c.Fatalf("user account was not disabled!")
   288  	}
   289  
   290  	// 5. Check that user can be deleted and verify it.
   291  	err = s.adm.RemoveUser(ctx, accessKey)
   292  	if err != nil {
   293  		c.Fatalf("user could not be deleted: %v", err)
   294  	}
   295  	usersMap, err = s.adm.ListUsers(ctx)
   296  	if err != nil {
   297  		c.Fatalf("error listing: %v", err)
   298  	}
   299  	_, ok = usersMap[accessKey]
   300  	if ok {
   301  		c.Fatalf("user not deleted: %s", accessKey)
   302  	}
   303  	err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{})
   304  	if err == nil {
   305  		c.Fatalf("user account was not deleted!")
   306  	}
   307  }
   308  
   309  func (s *TestSuiteIAM) TestUserPolicyEscalationBug(c *check) {
   310  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   311  	defer cancel()
   312  
   313  	bucket := getRandomBucketName()
   314  	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
   315  	if err != nil {
   316  		c.Fatalf("bucket creat error: %v", err)
   317  	}
   318  
   319  	// 2. Create a user, associate policy and verify access
   320  	accessKey, secretKey := mustGenerateCredentials(c)
   321  	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
   322  	if err != nil {
   323  		c.Fatalf("Unable to set user: %v", err)
   324  	}
   325  	// 2.1 check that user does not have any access to the bucket
   326  	uClient := s.getUserClient(c, accessKey, secretKey, "")
   327  	c.mustNotListObjects(ctx, uClient, bucket)
   328  
   329  	// 2.2 create and associate policy to user
   330  	policy := "mypolicy-test-user-update"
   331  	policyBytes := []byte(fmt.Sprintf(`{
   332   "Version": "2012-10-17",
   333   "Statement": [
   334    {
   335     "Effect": "Allow",
   336     "Action": [
   337      "s3:PutObject",
   338      "s3:GetObject",
   339      "s3:ListBucket"
   340     ],
   341     "Resource": [
   342      "arn:aws:s3:::%s/*"
   343     ]
   344    }
   345   ]
   346  }`, bucket))
   347  	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
   348  	if err != nil {
   349  		c.Fatalf("policy add error: %v", err)
   350  	}
   351  	err = s.adm.SetPolicy(ctx, policy, accessKey, false)
   352  	if err != nil {
   353  		c.Fatalf("Unable to set policy: %v", err)
   354  	}
   355  	// 2.3 check user has access to bucket
   356  	c.mustListObjects(ctx, uClient, bucket)
   357  	// 2.3 check that user cannot delete the bucket
   358  	err = uClient.RemoveBucket(ctx, bucket)
   359  	if err == nil || err.Error() != "Access Denied." {
   360  		c.Fatalf("bucket was deleted unexpectedly or got unexpected err: %v", err)
   361  	}
   362  
   363  	// 3. Craft a request to update the user's permissions
   364  	ep := s.adm.GetEndpointURL()
   365  	urlValue := url.Values{}
   366  	urlValue.Add("accessKey", accessKey)
   367  	u, err := url.Parse(fmt.Sprintf("%s://%s/minio/admin/v3/add-user?%s", ep.Scheme, ep.Host, s3utils.QueryEncode(urlValue)))
   368  	if err != nil {
   369  		c.Fatalf("unexpected url parse err: %v", err)
   370  	}
   371  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil)
   372  	if err != nil {
   373  		c.Fatalf("unexpected new request err: %v", err)
   374  	}
   375  	reqBodyArg := madmin.UserInfo{
   376  		SecretKey:  secretKey,
   377  		PolicyName: "consoleAdmin",
   378  		Status:     madmin.AccountEnabled,
   379  	}
   380  	buf, err := json.Marshal(reqBodyArg)
   381  	if err != nil {
   382  		c.Fatalf("unexpected json encode err: %v", err)
   383  	}
   384  	buf, err = madmin.EncryptData(secretKey, buf)
   385  	if err != nil {
   386  		c.Fatalf("unexpected encryption err: %v", err)
   387  	}
   388  
   389  	req.ContentLength = int64(len(buf))
   390  	sum := sha256.Sum256(buf)
   391  	req.Header.Set("X-Amz-Content-Sha256", hex.EncodeToString(sum[:]))
   392  	req.Body = io.NopCloser(bytes.NewReader(buf))
   393  	req = signer.SignV4(*req, accessKey, secretKey, "", "")
   394  
   395  	// 3.1 Execute the request.
   396  	resp, err := s.TestSuiteCommon.client.Do(req)
   397  	if err != nil {
   398  		c.Fatalf("unexpected request err: %v", err)
   399  	}
   400  	if resp.StatusCode != 200 {
   401  		c.Fatalf("got unexpected response: %#v\n", resp)
   402  	}
   403  
   404  	// 3.2 check that user cannot delete the bucket
   405  	err = uClient.RemoveBucket(ctx, bucket)
   406  	if err == nil || err.Error() != "Access Denied." {
   407  		c.Fatalf("User was able to escalate privileges (Err=%v)!", err)
   408  	}
   409  }
   410  
   411  func (s *TestSuiteIAM) TestAddServiceAccountPerms(c *check) {
   412  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   413  	defer cancel()
   414  
   415  	// 1. Create a policy
   416  	policy1 := "deny-svc"
   417  	policy2 := "allow-svc"
   418  	policyBytes := []byte(`{
   419   "Version": "2012-10-17",
   420   "Statement": [
   421    {
   422     "Effect": "Deny",
   423     "Action": [
   424      "admin:CreateServiceAccount"
   425     ]
   426    }
   427   ]
   428  }`)
   429  
   430  	newPolicyBytes := []byte(`{
   431   "Version": "2012-10-17",
   432   "Statement": [
   433    {
   434     "Effect": "Allow",
   435     "Action": [
   436      "s3:ListBucket"
   437     ],
   438     "Resource": [
   439      "arn:aws:s3:::testbucket/*"
   440     ]
   441    }
   442   ]
   443  }`)
   444  
   445  	err := s.adm.AddCannedPolicy(ctx, policy1, policyBytes)
   446  	if err != nil {
   447  		c.Fatalf("policy add error: %v", err)
   448  	}
   449  
   450  	err = s.adm.AddCannedPolicy(ctx, policy2, newPolicyBytes)
   451  	if err != nil {
   452  		c.Fatalf("policy add error: %v", err)
   453  	}
   454  
   455  	// 2. Verify that policy json is validated by server
   456  	invalidPolicyBytes := policyBytes[:len(policyBytes)-1]
   457  	err = s.adm.AddCannedPolicy(ctx, policy1+"invalid", invalidPolicyBytes)
   458  	if err == nil {
   459  		c.Fatalf("invalid policy creation success")
   460  	}
   461  
   462  	// 3. Create a user, associate policy and verify access
   463  	accessKey, secretKey := mustGenerateCredentials(c)
   464  	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
   465  	if err != nil {
   466  		c.Fatalf("Unable to set user: %v", err)
   467  	}
   468  	// 3.1 check that user does not have any access to the bucket
   469  	uClient := s.getUserClient(c, accessKey, secretKey, "")
   470  	c.mustNotListObjects(ctx, uClient, "testbucket")
   471  
   472  	// 3.2 associate policy to user
   473  	err = s.adm.SetPolicy(ctx, policy1, accessKey, false)
   474  	if err != nil {
   475  		c.Fatalf("Unable to set policy: %v", err)
   476  	}
   477  
   478  	admClnt := s.getAdminClient(c, accessKey, secretKey, "")
   479  
   480  	// 3.3 check user does not have explicit permissions to create service account.
   481  	c.mustNotCreateSvcAccount(ctx, accessKey, admClnt)
   482  
   483  	// 4. Verify the policy appears in listing
   484  	ps, err := s.adm.ListCannedPolicies(ctx)
   485  	if err != nil {
   486  		c.Fatalf("policy list err: %v", err)
   487  	}
   488  	_, ok := ps[policy1]
   489  	if !ok {
   490  		c.Fatalf("policy was missing!")
   491  	}
   492  
   493  	// 3.2 associate policy to user
   494  	err = s.adm.SetPolicy(ctx, policy2, accessKey, false)
   495  	if err != nil {
   496  		c.Fatalf("Unable to set policy: %v", err)
   497  	}
   498  
   499  	// 3.3 check user can create service account implicitly.
   500  	c.mustCreateSvcAccount(ctx, accessKey, admClnt)
   501  
   502  	_, ok = ps[policy2]
   503  	if !ok {
   504  		c.Fatalf("policy was missing!")
   505  	}
   506  
   507  	err = s.adm.RemoveUser(ctx, accessKey)
   508  	if err != nil {
   509  		c.Fatalf("user could not be deleted: %v", err)
   510  	}
   511  
   512  	err = s.adm.RemoveCannedPolicy(ctx, policy1)
   513  	if err != nil {
   514  		c.Fatalf("policy del err: %v", err)
   515  	}
   516  
   517  	err = s.adm.RemoveCannedPolicy(ctx, policy2)
   518  	if err != nil {
   519  		c.Fatalf("policy del err: %v", err)
   520  	}
   521  }
   522  
   523  func (s *TestSuiteIAM) TestPolicyCreate(c *check) {
   524  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   525  	defer cancel()
   526  
   527  	bucket := getRandomBucketName()
   528  	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
   529  	if err != nil {
   530  		c.Fatalf("bucket creat error: %v", err)
   531  	}
   532  
   533  	// 1. Create a policy
   534  	policy := "mypolicy"
   535  	policyBytes := []byte(fmt.Sprintf(`{
   536   "Version": "2012-10-17",
   537   "Statement": [
   538    {
   539     "Effect": "Allow",
   540     "Action": [
   541      "s3:PutObject",
   542      "s3:GetObject",
   543      "s3:ListBucket"
   544     ],
   545     "Resource": [
   546      "arn:aws:s3:::%s/*"
   547     ]
   548    }
   549   ]
   550  }`, bucket))
   551  	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
   552  	if err != nil {
   553  		c.Fatalf("policy add error: %v", err)
   554  	}
   555  
   556  	// 2. Verify that policy json is validated by server
   557  	invalidPolicyBytes := policyBytes[:len(policyBytes)-1]
   558  	err = s.adm.AddCannedPolicy(ctx, policy+"invalid", invalidPolicyBytes)
   559  	if err == nil {
   560  		c.Fatalf("invalid policy creation success")
   561  	}
   562  
   563  	// 3. Create a user, associate policy and verify access
   564  	accessKey, secretKey := mustGenerateCredentials(c)
   565  	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
   566  	if err != nil {
   567  		c.Fatalf("Unable to set user: %v", err)
   568  	}
   569  	// 3.1 check that user does not have any access to the bucket
   570  	uClient := s.getUserClient(c, accessKey, secretKey, "")
   571  	c.mustNotListObjects(ctx, uClient, bucket)
   572  
   573  	// 3.2 associate policy to user
   574  	err = s.adm.SetPolicy(ctx, policy, accessKey, false)
   575  	if err != nil {
   576  		c.Fatalf("Unable to set policy: %v", err)
   577  	}
   578  	// 3.3 check user has access to bucket
   579  	c.mustListObjects(ctx, uClient, bucket)
   580  	// 3.4 Check that user cannot exceed their permissions
   581  	err = uClient.RemoveBucket(ctx, bucket)
   582  	if err == nil {
   583  		c.Fatalf("bucket was deleted!")
   584  	}
   585  
   586  	// 4. Verify the policy appears in listing
   587  	ps, err := s.adm.ListCannedPolicies(ctx)
   588  	if err != nil {
   589  		c.Fatalf("policy list err: %v", err)
   590  	}
   591  	_, ok := ps[policy]
   592  	if !ok {
   593  		c.Fatalf("policy was missing!")
   594  	}
   595  
   596  	// 5. Check that policy cannot be deleted when attached to a user.
   597  	err = s.adm.RemoveCannedPolicy(ctx, policy)
   598  	if err == nil {
   599  		c.Fatalf("policy could be unexpectedly deleted!")
   600  	}
   601  
   602  	// 6. Delete the user and then delete the policy.
   603  	err = s.adm.RemoveUser(ctx, accessKey)
   604  	if err != nil {
   605  		c.Fatalf("user could not be deleted: %v", err)
   606  	}
   607  	err = s.adm.RemoveCannedPolicy(ctx, policy)
   608  	if err != nil {
   609  		c.Fatalf("policy del err: %v", err)
   610  	}
   611  }
   612  
   613  func (s *TestSuiteIAM) TestCannedPolicies(c *check) {
   614  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   615  	defer cancel()
   616  
   617  	policies, err := s.adm.ListCannedPolicies(ctx)
   618  	if err != nil {
   619  		c.Fatalf("unable to list policies: %v", err)
   620  	}
   621  
   622  	defaultPolicies := []string{
   623  		"readwrite",
   624  		"readonly",
   625  		"writeonly",
   626  		"diagnostics",
   627  		"consoleAdmin",
   628  	}
   629  
   630  	for _, v := range defaultPolicies {
   631  		if _, ok := policies[v]; !ok {
   632  			c.Fatalf("Failed to find %s in policies list", v)
   633  		}
   634  	}
   635  
   636  	bucket := getRandomBucketName()
   637  	err = s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
   638  	if err != nil {
   639  		c.Fatalf("bucket creat error: %v", err)
   640  	}
   641  
   642  	policyBytes := []byte(fmt.Sprintf(`{
   643   "Version": "2012-10-17",
   644   "Statement": [
   645    {
   646     "Effect": "Allow",
   647     "Action": [
   648      "s3:PutObject",
   649      "s3:GetObject",
   650      "s3:ListBucket"
   651     ],
   652     "Resource": [
   653      "arn:aws:s3:::%s/*"
   654     ]
   655    }
   656   ]
   657  }`, bucket))
   658  
   659  	// Check that default policies can be overwritten.
   660  	err = s.adm.AddCannedPolicy(ctx, "readwrite", policyBytes)
   661  	if err != nil {
   662  		c.Fatalf("policy add error: %v", err)
   663  	}
   664  
   665  	info, err := s.adm.InfoCannedPolicy(ctx, "readwrite")
   666  	if err != nil {
   667  		c.Fatalf("policy info err: %v", err)
   668  	}
   669  
   670  	infoStr := string(info)
   671  	if !strings.Contains(infoStr, `"s3:PutObject"`) || !strings.Contains(infoStr, ":"+bucket+"/") {
   672  		c.Fatalf("policy contains unexpected content!")
   673  	}
   674  }
   675  
   676  func (s *TestSuiteIAM) TestGroupAddRemove(c *check) {
   677  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   678  	defer cancel()
   679  
   680  	bucket := getRandomBucketName()
   681  	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
   682  	if err != nil {
   683  		c.Fatalf("bucket creat error: %v", err)
   684  	}
   685  
   686  	policy := "mypolicy"
   687  	policyBytes := []byte(fmt.Sprintf(`{
   688   "Version": "2012-10-17",
   689   "Statement": [
   690    {
   691     "Effect": "Allow",
   692     "Action": [
   693      "s3:PutObject",
   694      "s3:GetObject",
   695      "s3:ListBucket"
   696     ],
   697     "Resource": [
   698      "arn:aws:s3:::%s/*"
   699     ]
   700    }
   701   ]
   702  }`, bucket))
   703  	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
   704  	if err != nil {
   705  		c.Fatalf("policy add error: %v", err)
   706  	}
   707  
   708  	accessKey, secretKey := mustGenerateCredentials(c)
   709  	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
   710  	if err != nil {
   711  		c.Fatalf("Unable to set user: %v", err)
   712  	}
   713  
   714  	// 1. Add user to a new group
   715  	group := "mygroup"
   716  	err = s.adm.UpdateGroupMembers(ctx, madmin.GroupAddRemove{
   717  		Group:   group,
   718  		Members: []string{accessKey},
   719  	})
   720  	if err != nil {
   721  		c.Fatalf("Unable to add user to group: %v", err)
   722  	}
   723  
   724  	// 2. Check that user has no access
   725  	uClient := s.getUserClient(c, accessKey, secretKey, "")
   726  	c.mustNotListObjects(ctx, uClient, bucket)
   727  
   728  	// 3. Associate policy to group and check user got access.
   729  	err = s.adm.SetPolicy(ctx, policy, group, true)
   730  	if err != nil {
   731  		c.Fatalf("Unable to set policy: %v", err)
   732  	}
   733  	// 3.1 check user has access to bucket
   734  	c.mustListObjects(ctx, uClient, bucket)
   735  	// 3.2 Check that user cannot exceed their permissions
   736  	err = uClient.RemoveBucket(ctx, bucket)
   737  	if err == nil {
   738  		c.Fatalf("bucket was deleted!")
   739  	}
   740  
   741  	// 4. List groups and members and verify
   742  	groups, err := s.adm.ListGroups(ctx)
   743  	if err != nil {
   744  		c.Fatalf("group list err: %v", err)
   745  	}
   746  	if !set.CreateStringSet(groups...).Contains(group) {
   747  		c.Fatalf("created group not present!")
   748  	}
   749  	groupInfo, err := s.adm.GetGroupDescription(ctx, group)
   750  	if err != nil {
   751  		c.Fatalf("group desc err: %v", err)
   752  	}
   753  	c.Assert(groupInfo.Name, group)
   754  	c.Assert(set.CreateStringSet(groupInfo.Members...), set.CreateStringSet(accessKey))
   755  	c.Assert(groupInfo.Policy, policy)
   756  	c.Assert(groupInfo.Status, string(madmin.GroupEnabled))
   757  
   758  	// 5. Disable/enable the group and verify that user access is revoked/restored.
   759  	err = s.adm.SetGroupStatus(ctx, group, madmin.GroupDisabled)
   760  	if err != nil {
   761  		c.Fatalf("group set status err: %v", err)
   762  	}
   763  	groupInfo, err = s.adm.GetGroupDescription(ctx, group)
   764  	if err != nil {
   765  		c.Fatalf("group desc err: %v", err)
   766  	}
   767  	c.Assert(groupInfo.Status, string(madmin.GroupDisabled))
   768  	c.mustNotListObjects(ctx, uClient, bucket)
   769  
   770  	err = s.adm.SetGroupStatus(ctx, group, madmin.GroupEnabled)
   771  	if err != nil {
   772  		c.Fatalf("group set status err: %v", err)
   773  	}
   774  	groupInfo, err = s.adm.GetGroupDescription(ctx, group)
   775  	if err != nil {
   776  		c.Fatalf("group desc err: %v", err)
   777  	}
   778  	c.Assert(groupInfo.Status, string(madmin.GroupEnabled))
   779  	c.mustListObjects(ctx, uClient, bucket)
   780  
   781  	// 6. Verify that group cannot be deleted with users.
   782  	err = s.adm.UpdateGroupMembers(ctx, madmin.GroupAddRemove{
   783  		Group:    group,
   784  		IsRemove: true,
   785  	})
   786  	if err == nil {
   787  		c.Fatalf("group was removed!")
   788  	}
   789  	groupInfo, err = s.adm.GetGroupDescription(ctx, group)
   790  	if err != nil {
   791  		c.Fatalf("group desc err: %v", err)
   792  	}
   793  	c.Assert(groupInfo.Name, group)
   794  
   795  	// 7. Remove user from group and verify access is revoked.
   796  	err = s.adm.UpdateGroupMembers(ctx, madmin.GroupAddRemove{
   797  		Group:    group,
   798  		Members:  []string{accessKey},
   799  		IsRemove: true,
   800  	})
   801  	if err != nil {
   802  		c.Fatalf("group update err: %v", err)
   803  	}
   804  	c.mustNotListObjects(ctx, uClient, bucket)
   805  
   806  	// 7.1 verify group still exists
   807  	groupInfo, err = s.adm.GetGroupDescription(ctx, group)
   808  	if err != nil {
   809  		c.Fatalf("group desc err: %v", err)
   810  	}
   811  	c.Assert(groupInfo.Name, group)
   812  	c.Assert(len(groupInfo.Members), 0)
   813  
   814  	// 8. Delete group and verify
   815  	err = s.adm.UpdateGroupMembers(ctx, madmin.GroupAddRemove{
   816  		Group:    group,
   817  		IsRemove: true,
   818  	})
   819  	if err != nil {
   820  		c.Fatalf("group update err: %v", err)
   821  	}
   822  	groups, err = s.adm.ListGroups(ctx)
   823  	if err != nil {
   824  		c.Fatalf("group list err: %v", err)
   825  	}
   826  	if set.CreateStringSet(groups...).Contains(group) {
   827  		c.Fatalf("created group still present!")
   828  	}
   829  	_, err = s.adm.GetGroupDescription(ctx, group)
   830  	if err == nil {
   831  		c.Fatalf("group appears to exist")
   832  	}
   833  }
   834  
   835  func (s *TestSuiteIAM) TestServiceAccountOpsByUser(c *check) {
   836  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   837  	defer cancel()
   838  
   839  	bucket := getRandomBucketName()
   840  	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
   841  	if err != nil {
   842  		c.Fatalf("bucket creat error: %v", err)
   843  	}
   844  
   845  	// Create policy, user and associate policy
   846  	policy := "mypolicy"
   847  	policyBytes := []byte(fmt.Sprintf(`{
   848   "Version": "2012-10-17",
   849   "Statement": [
   850    {
   851     "Effect": "Allow",
   852     "Action": [
   853      "s3:PutObject",
   854      "s3:GetObject",
   855      "s3:ListBucket"
   856     ],
   857     "Resource": [
   858      "arn:aws:s3:::%s/*"
   859     ]
   860    }
   861   ]
   862  }`, bucket))
   863  	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
   864  	if err != nil {
   865  		c.Fatalf("policy add error: %v", err)
   866  	}
   867  
   868  	accessKey, secretKey := mustGenerateCredentials(c)
   869  	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
   870  	if err != nil {
   871  		c.Fatalf("Unable to set user: %v", err)
   872  	}
   873  
   874  	err = s.adm.SetPolicy(ctx, policy, accessKey, false)
   875  	if err != nil {
   876  		c.Fatalf("Unable to set policy: %v", err)
   877  	}
   878  
   879  	// Create an madmin client with user creds
   880  	userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
   881  		Creds:  credentials.NewStaticV4(accessKey, secretKey, ""),
   882  		Secure: s.secure,
   883  	})
   884  	if err != nil {
   885  		c.Fatalf("Err creating user admin client: %v", err)
   886  	}
   887  	userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
   888  
   889  	// Create svc acc
   890  	cr := c.mustCreateSvcAccount(ctx, accessKey, userAdmClient)
   891  
   892  	// 1. Check that svc account appears in listing
   893  	c.assertSvcAccAppearsInListing(ctx, userAdmClient, accessKey, cr.AccessKey)
   894  
   895  	// 2. Check that svc account info can be queried
   896  	c.assertSvcAccInfoQueryable(ctx, userAdmClient, accessKey, cr.AccessKey, false)
   897  
   898  	// 3. Check S3 access
   899  	c.assertSvcAccS3Access(ctx, s, cr, bucket)
   900  
   901  	// 5. Check that service account can be deleted.
   902  	c.assertSvcAccDeletion(ctx, s, userAdmClient, accessKey, bucket)
   903  
   904  	// 6. Check that service account cannot be created for some other user.
   905  	c.mustNotCreateSvcAccount(ctx, globalActiveCred.AccessKey, userAdmClient)
   906  }
   907  
   908  func (s *TestSuiteIAM) TestServiceAccountDurationSecondsCondition(c *check) {
   909  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   910  	defer cancel()
   911  
   912  	bucket := getRandomBucketName()
   913  	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
   914  	if err != nil {
   915  		c.Fatalf("bucket creat error: %v", err)
   916  	}
   917  
   918  	// Create policy, user and associate policy
   919  	policy := "mypolicy"
   920  	policyBytes := []byte(fmt.Sprintf(`{
   921   "Version": "2012-10-17",
   922   "Statement": [
   923    {
   924     "Effect": "Deny",
   925     "Action": [
   926       "admin:CreateServiceAccount",
   927       "admin:UpdateServiceAccount"
   928     ],
   929     "Condition": {"NumericGreaterThan": {"svc:DurationSeconds": "3600"}}
   930    },
   931    {
   932     "Effect": "Allow",
   933     "Action": [
   934      "s3:PutObject",
   935      "s3:GetObject",
   936      "s3:ListBucket"
   937     ],
   938     "Resource": [
   939      "arn:aws:s3:::%s/*"
   940     ]
   941    }
   942   ]
   943  }`, bucket))
   944  	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
   945  	if err != nil {
   946  		c.Fatalf("policy add error: %v", err)
   947  	}
   948  
   949  	accessKey, secretKey := mustGenerateCredentials(c)
   950  	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
   951  	if err != nil {
   952  		c.Fatalf("Unable to set user: %v", err)
   953  	}
   954  
   955  	err = s.adm.SetPolicy(ctx, policy, accessKey, false)
   956  	if err != nil {
   957  		c.Fatalf("Unable to set policy: %v", err)
   958  	}
   959  
   960  	// Create an madmin client with user creds
   961  	userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
   962  		Creds:  credentials.NewStaticV4(accessKey, secretKey, ""),
   963  		Secure: s.secure,
   964  	})
   965  	if err != nil {
   966  		c.Fatalf("Err creating user admin client: %v", err)
   967  	}
   968  	userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
   969  
   970  	distantExpiration := time.Now().Add(30 * time.Minute)
   971  	cr, err := userAdmClient.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
   972  		TargetUser: accessKey,
   973  		AccessKey:  "svc-accesskey",
   974  		SecretKey:  "svc-secretkey",
   975  		Expiration: &distantExpiration,
   976  	})
   977  	if err != nil {
   978  		c.Fatalf("Unable to create svc acc: %v", err)
   979  	}
   980  
   981  	c.assertSvcAccS3Access(ctx, s, cr, bucket)
   982  
   983  	closeExpiration := time.Now().Add(2 * time.Hour)
   984  	_, err = userAdmClient.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
   985  		TargetUser: accessKey,
   986  		AccessKey:  "svc-accesskey",
   987  		SecretKey:  "svc-secretkey",
   988  		Expiration: &closeExpiration,
   989  	})
   990  	if err == nil {
   991  		c.Fatalf("Creating a svc acc with distant expiration should fail")
   992  	}
   993  }
   994  
   995  func (s *TestSuiteIAM) TestServiceAccountOpsByAdmin(c *check) {
   996  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
   997  	defer cancel()
   998  
   999  	bucket := getRandomBucketName()
  1000  	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
  1001  	if err != nil {
  1002  		c.Fatalf("bucket creat error: %v", err)
  1003  	}
  1004  
  1005  	// Create policy, user and associate policy
  1006  	policy := "mypolicy"
  1007  	policyBytes := []byte(fmt.Sprintf(`{
  1008   "Version": "2012-10-17",
  1009   "Statement": [
  1010    {
  1011     "Effect": "Allow",
  1012     "Action": [
  1013      "s3:PutObject",
  1014      "s3:GetObject",
  1015      "s3:ListBucket"
  1016     ],
  1017     "Resource": [
  1018      "arn:aws:s3:::%s/*"
  1019     ]
  1020    }
  1021   ]
  1022  }`, bucket))
  1023  	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
  1024  	if err != nil {
  1025  		c.Fatalf("policy add error: %v", err)
  1026  	}
  1027  
  1028  	accessKey, secretKey := mustGenerateCredentials(c)
  1029  	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
  1030  	if err != nil {
  1031  		c.Fatalf("Unable to set user: %v", err)
  1032  	}
  1033  
  1034  	err = s.adm.SetPolicy(ctx, policy, accessKey, false)
  1035  	if err != nil {
  1036  		c.Fatalf("Unable to set policy: %v", err)
  1037  	}
  1038  
  1039  	// 1. Create a service account for the user
  1040  	cr := c.mustCreateSvcAccount(ctx, accessKey, s.adm)
  1041  
  1042  	// 1.2 Check that svc account appears in listing
  1043  	c.assertSvcAccAppearsInListing(ctx, s.adm, accessKey, cr.AccessKey)
  1044  
  1045  	// 1.3 Check that svc account info can be queried
  1046  	c.assertSvcAccInfoQueryable(ctx, s.adm, accessKey, cr.AccessKey, false)
  1047  
  1048  	// 2. Check that svc account can access the bucket
  1049  	c.assertSvcAccS3Access(ctx, s, cr, bucket)
  1050  
  1051  	// 3. Check that svc account can restrict the policy, and that the
  1052  	// session policy can be updated.
  1053  	c.assertSvcAccSessionPolicyUpdate(ctx, s, s.adm, accessKey, bucket)
  1054  
  1055  	// 4. Check that service account's secret key and account status can be
  1056  	// updated.
  1057  	c.assertSvcAccSecretKeyAndStatusUpdate(ctx, s, s.adm, accessKey, bucket)
  1058  
  1059  	// 5. Check that service account can be deleted.
  1060  	c.assertSvcAccDeletion(ctx, s, s.adm, accessKey, bucket)
  1061  }
  1062  
  1063  func (s *TestSuiteIAM) TestServiceAccountPrivilegeEscalationBug(c *check) {
  1064  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
  1065  	defer cancel()
  1066  
  1067  	err := s.client.MakeBucket(ctx, "public", minio.MakeBucketOptions{})
  1068  	if err != nil {
  1069  		c.Fatalf("bucket creat error: %v", err)
  1070  	}
  1071  
  1072  	err = s.client.MakeBucket(ctx, "private", minio.MakeBucketOptions{})
  1073  	if err != nil {
  1074  		c.Fatalf("bucket creat error: %v", err)
  1075  	}
  1076  
  1077  	pubPolicyBytes := []byte(`{
  1078   "Version": "2012-10-17",
  1079   "Statement": [
  1080    {
  1081     "Effect": "Allow",
  1082     "Action": [
  1083      "s3:*"
  1084     ],
  1085     "Resource": [
  1086      "arn:aws:s3:::public",
  1087      "arn:aws:s3:::public/*"
  1088     ]
  1089    }
  1090   ]
  1091  }`)
  1092  
  1093  	fullS3PolicyBytes := []byte(`{
  1094    "Version": "2012-10-17",
  1095    "Statement": [
  1096      {
  1097        "Effect": "Allow",
  1098        "Action": [
  1099          "s3:*"
  1100        ],
  1101        "Resource": [
  1102          "arn:aws:s3:::*"
  1103        ]
  1104      }
  1105    ]
  1106  }
  1107  `)
  1108  
  1109  	// Create a service account for the root user.
  1110  	cr, err := s.adm.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
  1111  		TargetUser: globalActiveCred.AccessKey,
  1112  		Policy:     pubPolicyBytes,
  1113  	})
  1114  	if err != nil {
  1115  		c.Fatalf("admin should be able to create service account for themselves %s", err)
  1116  	}
  1117  
  1118  	svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "")
  1119  
  1120  	// Check that the service account can access the public bucket.
  1121  	buckets, err := svcClient.ListBuckets(ctx)
  1122  	if err != nil {
  1123  		c.Fatalf("err fetching buckets %s", err)
  1124  	}
  1125  	if len(buckets) != 1 || buckets[0].Name != "public" {
  1126  		c.Fatalf("service account should only have access to public bucket")
  1127  	}
  1128  
  1129  	// Create an madmin client with the service account creds.
  1130  	svcAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
  1131  		Creds:  credentials.NewStaticV4(cr.AccessKey, cr.SecretKey, ""),
  1132  		Secure: s.secure,
  1133  	})
  1134  	if err != nil {
  1135  		c.Fatalf("Err creating svcacct admin client: %v", err)
  1136  	}
  1137  	svcAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
  1138  
  1139  	// Attempt to update the policy on the service account.
  1140  	err = svcAdmClient.UpdateServiceAccount(ctx, cr.AccessKey,
  1141  		madmin.UpdateServiceAccountReq{
  1142  			NewPolicy: fullS3PolicyBytes,
  1143  		})
  1144  
  1145  	if err == nil {
  1146  		c.Fatalf("service account should not be able to update policy on itself")
  1147  	} else if !strings.Contains(err.Error(), "Access Denied") {
  1148  		c.Fatalf("unexpected error: %v", err)
  1149  	}
  1150  }
  1151  
  1152  func (s *TestSuiteIAM) SetUpAccMgmtPlugin(c *check) {
  1153  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
  1154  	defer cancel()
  1155  
  1156  	pluginEndpoint := env.Get("_MINIO_POLICY_PLUGIN_ENDPOINT", "")
  1157  	if pluginEndpoint == "" {
  1158  		c.Skip("_MINIO_POLICY_PLUGIN_ENDPOINT not given - skipping.")
  1159  	}
  1160  
  1161  	configCmds := []string{
  1162  		"policy_plugin",
  1163  		"url=" + pluginEndpoint,
  1164  	}
  1165  
  1166  	_, err := s.adm.SetConfigKV(ctx, strings.Join(configCmds, " "))
  1167  	if err != nil {
  1168  		c.Fatalf("unable to setup access management plugin for tests: %v", err)
  1169  	}
  1170  
  1171  	s.RestartIAMSuite(c)
  1172  }
  1173  
  1174  // TestIAM_AMPInternalIDPServerSuite - tests for access management plugin
  1175  func TestIAM_AMPInternalIDPServerSuite(t *testing.T) {
  1176  	for i, testCase := range iamTestSuites {
  1177  		t.Run(
  1178  			fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription),
  1179  			func(t *testing.T) {
  1180  				suite := testCase
  1181  				c := &check{t, testCase.serverType}
  1182  
  1183  				suite.SetUpSuite(c)
  1184  				defer suite.TearDownSuite(c)
  1185  
  1186  				suite.SetUpAccMgmtPlugin(c)
  1187  
  1188  				suite.TestAccMgmtPlugin(c)
  1189  			},
  1190  		)
  1191  	}
  1192  }
  1193  
  1194  // TestAccMgmtPlugin - this test assumes that the access-management-plugin is
  1195  // the same as the example in `docs/iam/access-manager-plugin.go` -
  1196  // specifically, it denies only `s3:Put*` operations on non-root accounts.
  1197  func (s *TestSuiteIAM) TestAccMgmtPlugin(c *check) {
  1198  	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
  1199  	defer cancel()
  1200  
  1201  	// 0. Check that owner is able to make-bucket.
  1202  	bucket := getRandomBucketName()
  1203  	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
  1204  	if err != nil {
  1205  		c.Fatalf("bucket creat error: %v", err)
  1206  	}
  1207  
  1208  	// 1. Create a user.
  1209  	accessKey, secretKey := mustGenerateCredentials(c)
  1210  	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
  1211  	if err != nil {
  1212  		c.Fatalf("Unable to set user: %v", err)
  1213  	}
  1214  
  1215  	// 2. Check new user appears in listing
  1216  	usersMap, err := s.adm.ListUsers(ctx)
  1217  	if err != nil {
  1218  		c.Fatalf("error listing: %v", err)
  1219  	}
  1220  	v, ok := usersMap[accessKey]
  1221  	if !ok {
  1222  		c.Fatalf("user not listed: %s", accessKey)
  1223  	}
  1224  	c.Assert(v.Status, madmin.AccountEnabled)
  1225  
  1226  	// 3. Check that user is able to make a bucket.
  1227  	client := s.getUserClient(c, accessKey, secretKey, "")
  1228  	err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{})
  1229  	if err != nil {
  1230  		c.Fatalf("user not create bucket: %v", err)
  1231  	}
  1232  
  1233  	// 3.1 check user has access to bucket
  1234  	c.mustListObjects(ctx, client, bucket)
  1235  
  1236  	// 3.2 check that user cannot upload an object.
  1237  	_, err = client.PutObject(ctx, bucket, "objectName", bytes.NewBuffer([]byte("some content")), 12, minio.PutObjectOptions{})
  1238  	if err == nil {
  1239  		c.Fatalf("user was able to upload unexpectedly")
  1240  	}
  1241  
  1242  	// Create an madmin client with user creds
  1243  	userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
  1244  		Creds:  credentials.NewStaticV4(accessKey, secretKey, ""),
  1245  		Secure: s.secure,
  1246  	})
  1247  	if err != nil {
  1248  		c.Fatalf("Err creating user admin client: %v", err)
  1249  	}
  1250  	userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
  1251  
  1252  	// Create svc acc
  1253  	cr := c.mustCreateSvcAccount(ctx, accessKey, userAdmClient)
  1254  
  1255  	// 1. Check that svc account appears in listing
  1256  	c.assertSvcAccAppearsInListing(ctx, userAdmClient, accessKey, cr.AccessKey)
  1257  
  1258  	// 2. Check that svc account info can be queried
  1259  	c.assertSvcAccInfoQueryable(ctx, userAdmClient, accessKey, cr.AccessKey, false)
  1260  
  1261  	// 3. Check S3 access
  1262  	c.assertSvcAccS3Access(ctx, s, cr, bucket)
  1263  
  1264  	// Check that session policies do not apply - as policy enforcement is
  1265  	// delegated to plugin.
  1266  	{
  1267  		svcAK, svcSK := mustGenerateCredentials(c)
  1268  
  1269  		// This policy does not allow listing objects.
  1270  		policyBytes := []byte(fmt.Sprintf(`{
  1271   "Version": "2012-10-17",
  1272   "Statement": [
  1273    {
  1274     "Effect": "Allow",
  1275     "Action": [
  1276      "s3:PutObject",
  1277      "s3:GetObject"
  1278     ],
  1279     "Resource": [
  1280      "arn:aws:s3:::%s/*"
  1281     ]
  1282    }
  1283   ]
  1284  }`, bucket))
  1285  		cr, err := userAdmClient.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
  1286  			Policy:     policyBytes,
  1287  			TargetUser: accessKey,
  1288  			AccessKey:  svcAK,
  1289  			SecretKey:  svcSK,
  1290  		})
  1291  		if err != nil {
  1292  			c.Fatalf("Unable to create svc acc: %v", err)
  1293  		}
  1294  		svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "")
  1295  		// Though the attached policy does not allow listing, it will be
  1296  		// ignored because the plugin allows it.
  1297  		c.mustListObjects(ctx, svcClient, bucket)
  1298  	}
  1299  
  1300  	// 4. Check that service account's secret key and account status can be
  1301  	// updated.
  1302  	c.assertSvcAccSecretKeyAndStatusUpdate(ctx, s, userAdmClient, accessKey, bucket)
  1303  
  1304  	// 5. Check that service account can be deleted.
  1305  	c.assertSvcAccDeletion(ctx, s, userAdmClient, accessKey, bucket)
  1306  
  1307  	// 6. Check that service account **can** be created for some other user.
  1308  	// This is possible because the policy enforced in the plugin.
  1309  	c.mustCreateSvcAccount(ctx, globalActiveCred.AccessKey, userAdmClient)
  1310  }
  1311  
  1312  func (c *check) mustCreateIAMUser(ctx context.Context, admClnt *madmin.AdminClient) madmin.Credentials {
  1313  	c.Helper()
  1314  	randUser := mustGetUUID()
  1315  	randPass := mustGetUUID()
  1316  	err := admClnt.AddUser(ctx, randUser, randPass)
  1317  	if err != nil {
  1318  		c.Fatalf("should be able to create a user: %v", err)
  1319  	}
  1320  	return madmin.Credentials{
  1321  		AccessKey: randUser,
  1322  		SecretKey: randPass,
  1323  	}
  1324  }
  1325  
  1326  func (c *check) mustGetIAMUserInfo(ctx context.Context, admClnt *madmin.AdminClient, accessKey string) madmin.UserInfo {
  1327  	c.Helper()
  1328  	ui, err := admClnt.GetUserInfo(ctx, accessKey)
  1329  	if err != nil {
  1330  		c.Fatalf("should be able to get user info: %v", err)
  1331  	}
  1332  	return ui
  1333  }
  1334  
  1335  func (c *check) mustNotCreateIAMUser(ctx context.Context, admClnt *madmin.AdminClient) {
  1336  	c.Helper()
  1337  	randUser := mustGetUUID()
  1338  	randPass := mustGetUUID()
  1339  	err := admClnt.AddUser(ctx, randUser, randPass)
  1340  	if err == nil {
  1341  		c.Fatalf("should not be able to create a user")
  1342  	}
  1343  }
  1344  
  1345  func (c *check) mustCreateSvcAccount(ctx context.Context, tgtUser string, admClnt *madmin.AdminClient) madmin.Credentials {
  1346  	c.Helper()
  1347  	cr, err := admClnt.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
  1348  		TargetUser: tgtUser,
  1349  	})
  1350  	if err != nil {
  1351  		c.Fatalf("user should be able to create service accounts %s", err)
  1352  	}
  1353  	return cr
  1354  }
  1355  
  1356  func (c *check) mustNotCreateSvcAccount(ctx context.Context, tgtUser string, admClnt *madmin.AdminClient) {
  1357  	c.Helper()
  1358  	_, err := admClnt.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
  1359  		TargetUser: tgtUser,
  1360  	})
  1361  	if err == nil {
  1362  		c.Fatalf("user was able to add service accounts unexpectedly!")
  1363  	}
  1364  }
  1365  
  1366  func (c *check) mustNotListObjects(ctx context.Context, client *minio.Client, bucket string) {
  1367  	c.Helper()
  1368  	res := client.ListObjects(ctx, bucket, minio.ListObjectsOptions{})
  1369  	v, ok := <-res
  1370  	if !ok || v.Err == nil {
  1371  		c.Fatalf("user was able to list unexpectedly! on %s", bucket)
  1372  	}
  1373  }
  1374  
  1375  func (c *check) mustPutObjectWithTags(ctx context.Context, client *minio.Client, bucket, object string) {
  1376  	c.Helper()
  1377  	_, err := client.PutObject(ctx, bucket, object, bytes.NewBuffer([]byte("stuff")), 5, minio.PutObjectOptions{
  1378  		UserTags: map[string]string{
  1379  			"security": "public",
  1380  			"virus":    "true",
  1381  		},
  1382  	})
  1383  	if err != nil {
  1384  		c.Fatalf("user was unable to upload the object: %v", err)
  1385  	}
  1386  }
  1387  
  1388  func (c *check) mustGetObject(ctx context.Context, client *minio.Client, bucket, object string) {
  1389  	c.Helper()
  1390  
  1391  	r, err := client.GetObject(ctx, bucket, object, minio.GetObjectOptions{})
  1392  	if err != nil {
  1393  		c.Fatalf("user was unable to download the object: %v", err)
  1394  	}
  1395  	defer r.Close()
  1396  
  1397  	_, err = io.Copy(io.Discard, r)
  1398  	if err != nil {
  1399  		c.Fatalf("user was unable to download the object: %v", err)
  1400  	}
  1401  }
  1402  
  1403  func (c *check) mustHeadObject(ctx context.Context, client *minio.Client, bucket, object string, tagCount int) {
  1404  	c.Helper()
  1405  
  1406  	oinfo, err := client.StatObject(ctx, bucket, object, minio.StatObjectOptions{})
  1407  	if err != nil {
  1408  		c.Fatalf("user was unable to download the object: %v", err)
  1409  	}
  1410  
  1411  	if oinfo.UserTagCount != tagCount {
  1412  		c.Fatalf("expected tagCount: %d, got %d", tagCount, oinfo.UserTagCount)
  1413  	}
  1414  }
  1415  
  1416  func (c *check) mustListObjects(ctx context.Context, client *minio.Client, bucket string) {
  1417  	c.Helper()
  1418  	res := client.ListObjects(ctx, bucket, minio.ListObjectsOptions{})
  1419  	v, ok := <-res
  1420  	if ok && v.Err != nil {
  1421  		c.Fatalf("user was unable to list: %v", v.Err)
  1422  	}
  1423  }
  1424  
  1425  func (c *check) mustListBuckets(ctx context.Context, client *minio.Client) {
  1426  	c.Helper()
  1427  	_, err := client.ListBuckets(ctx)
  1428  	if err != nil {
  1429  		c.Fatalf("user was unable to list buckets: %v", err)
  1430  	}
  1431  }
  1432  
  1433  func (c *check) mustNotDelete(ctx context.Context, client *minio.Client, bucket string, vid string) {
  1434  	c.Helper()
  1435  
  1436  	err := client.RemoveObject(ctx, bucket, "some-object", minio.RemoveObjectOptions{VersionID: vid})
  1437  	if err == nil {
  1438  		c.Fatalf("user must not be allowed to delete")
  1439  	}
  1440  
  1441  	err = client.RemoveObject(ctx, bucket, "some-object", minio.RemoveObjectOptions{})
  1442  	if err != nil {
  1443  		c.Fatal("user must be able to create delete marker")
  1444  	}
  1445  }
  1446  
  1447  func (c *check) mustDownload(ctx context.Context, client *minio.Client, bucket string) {
  1448  	c.Helper()
  1449  	rd, err := client.GetObject(ctx, bucket, "some-object", minio.GetObjectOptions{})
  1450  	if err != nil {
  1451  		c.Fatalf("download did not succeed got %#v", err)
  1452  	}
  1453  	if _, err = io.Copy(io.Discard, rd); err != nil {
  1454  		c.Fatalf("download did not succeed got %#v", err)
  1455  	}
  1456  }
  1457  
  1458  func (c *check) mustUploadReturnVersions(ctx context.Context, client *minio.Client, bucket string) []string {
  1459  	c.Helper()
  1460  	versions := []string{}
  1461  	for i := 0; i < 5; i++ {
  1462  		ui, err := client.PutObject(ctx, bucket, "some-object", bytes.NewBuffer([]byte("stuff")), 5, minio.PutObjectOptions{})
  1463  		if err != nil {
  1464  			c.Fatalf("upload did not succeed got %#v", err)
  1465  		}
  1466  		versions = append(versions, ui.VersionID)
  1467  	}
  1468  	return versions
  1469  }
  1470  
  1471  func (c *check) mustUpload(ctx context.Context, client *minio.Client, bucket string) {
  1472  	c.Helper()
  1473  	_, err := client.PutObject(ctx, bucket, "some-object", bytes.NewBuffer([]byte("stuff")), 5, minio.PutObjectOptions{})
  1474  	if err != nil {
  1475  		c.Fatalf("upload did not succeed got %#v", err)
  1476  	}
  1477  }
  1478  
  1479  func (c *check) mustNotUpload(ctx context.Context, client *minio.Client, bucket string) {
  1480  	c.Helper()
  1481  	_, err := client.PutObject(ctx, bucket, "some-object", bytes.NewBuffer([]byte("stuff")), 5, minio.PutObjectOptions{})
  1482  	if e, ok := err.(minio.ErrorResponse); ok {
  1483  		if e.Code == "AccessDenied" {
  1484  			return
  1485  		}
  1486  	}
  1487  	c.Fatalf("upload did not get an AccessDenied error - got %#v instead", err)
  1488  }
  1489  
  1490  func (c *check) assertSvcAccS3Access(ctx context.Context, s *TestSuiteIAM, cr madmin.Credentials, bucket string) {
  1491  	svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "")
  1492  	c.mustListObjects(ctx, svcClient, bucket)
  1493  }
  1494  
  1495  func (c *check) assertSvcAccAppearsInListing(ctx context.Context, madmClient *madmin.AdminClient, parentAK, svcAK string) {
  1496  	c.Helper()
  1497  	listResp, err := madmClient.ListServiceAccounts(ctx, parentAK)
  1498  	if err != nil {
  1499  		c.Fatalf("unable to list svc accounts: %v", err)
  1500  	}
  1501  	var accessKeys []string
  1502  	for _, item := range listResp.Accounts {
  1503  		accessKeys = append(accessKeys, item.AccessKey)
  1504  	}
  1505  	if !set.CreateStringSet(accessKeys...).Contains(svcAK) {
  1506  		c.Fatalf("service account did not appear in listing!")
  1507  	}
  1508  }
  1509  
  1510  func (c *check) assertSvcAccInfoQueryable(ctx context.Context, madmClient *madmin.AdminClient, parentAK, svcAK string, skipParentUserCheck bool) {
  1511  	infoResp, err := madmClient.InfoServiceAccount(ctx, svcAK)
  1512  	if err != nil {
  1513  		c.Fatalf("unable to get svc acc info: %v", err)
  1514  	}
  1515  	if !skipParentUserCheck {
  1516  		c.Assert(infoResp.ParentUser, parentAK)
  1517  	}
  1518  	c.Assert(infoResp.AccountStatus, "on")
  1519  	c.Assert(infoResp.ImpliedPolicy, true)
  1520  }
  1521  
  1522  // This test assumes that the policy for `accessKey` allows listing on the given
  1523  // bucket. It creates a session policy that restricts listing on the bucket and
  1524  // then enables it again in a session policy update call.
  1525  func (c *check) assertSvcAccSessionPolicyUpdate(ctx context.Context, s *TestSuiteIAM, madmClient *madmin.AdminClient, accessKey, bucket string) {
  1526  	c.Helper()
  1527  	svcAK, svcSK := mustGenerateCredentials(c)
  1528  
  1529  	// This policy does not allow listing objects.
  1530  	policyBytes := []byte(fmt.Sprintf(`{
  1531   "Version": "2012-10-17",
  1532   "Statement": [
  1533    {
  1534     "Effect": "Allow",
  1535     "Action": [
  1536      "s3:PutObject",
  1537      "s3:GetObject"
  1538     ],
  1539     "Resource": [
  1540      "arn:aws:s3:::%s/*"
  1541     ]
  1542    }
  1543   ]
  1544  }`, bucket))
  1545  	cr, err := madmClient.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
  1546  		Policy:     policyBytes,
  1547  		TargetUser: accessKey,
  1548  		AccessKey:  svcAK,
  1549  		SecretKey:  svcSK,
  1550  	})
  1551  	if err != nil {
  1552  		c.Fatalf("Unable to create svc acc: %v", err)
  1553  	}
  1554  	svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "")
  1555  	c.mustNotListObjects(ctx, svcClient, bucket)
  1556  
  1557  	// This policy allows listing objects.
  1558  	newPolicyBytes := []byte(fmt.Sprintf(`{
  1559   "Version": "2012-10-17",
  1560   "Statement": [
  1561    {
  1562     "Effect": "Allow",
  1563     "Action": [
  1564      "s3:ListBucket"
  1565     ],
  1566     "Resource": [
  1567      "arn:aws:s3:::%s/*"
  1568     ]
  1569    }
  1570   ]
  1571  }`, bucket))
  1572  	err = madmClient.UpdateServiceAccount(ctx, svcAK, madmin.UpdateServiceAccountReq{
  1573  		NewPolicy: newPolicyBytes,
  1574  	})
  1575  	if err != nil {
  1576  		c.Fatalf("unable to update session policy for svc acc: %v", err)
  1577  	}
  1578  	c.mustListObjects(ctx, svcClient, bucket)
  1579  }
  1580  
  1581  func (c *check) assertSvcAccSecretKeyAndStatusUpdate(ctx context.Context, s *TestSuiteIAM, madmClient *madmin.AdminClient, accessKey, bucket string) {
  1582  	c.Helper()
  1583  	svcAK, svcSK := mustGenerateCredentials(c)
  1584  	cr, err := madmClient.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
  1585  		TargetUser: accessKey,
  1586  		AccessKey:  svcAK,
  1587  		SecretKey:  svcSK,
  1588  	})
  1589  	if err != nil {
  1590  		c.Fatalf("Unable to create svc acc: %v", err)
  1591  	}
  1592  	svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "")
  1593  	c.mustListObjects(ctx, svcClient, bucket)
  1594  
  1595  	_, svcSK2 := mustGenerateCredentials(c)
  1596  	err = madmClient.UpdateServiceAccount(ctx, svcAK, madmin.UpdateServiceAccountReq{
  1597  		NewSecretKey: svcSK2,
  1598  	})
  1599  	if err != nil {
  1600  		c.Fatalf("unable to update secret key for svc acc: %v", err)
  1601  	}
  1602  	// old creds should not work:
  1603  	c.mustNotListObjects(ctx, svcClient, bucket)
  1604  	// new creds work:
  1605  	svcClient2 := s.getUserClient(c, cr.AccessKey, svcSK2, "")
  1606  	c.mustListObjects(ctx, svcClient2, bucket)
  1607  
  1608  	// update status to disabled
  1609  	err = madmClient.UpdateServiceAccount(ctx, svcAK, madmin.UpdateServiceAccountReq{
  1610  		NewStatus: "off",
  1611  	})
  1612  	if err != nil {
  1613  		c.Fatalf("unable to update secret key for svc acc: %v", err)
  1614  	}
  1615  	c.mustNotListObjects(ctx, svcClient2, bucket)
  1616  }
  1617  
  1618  func (c *check) assertSvcAccDeletion(ctx context.Context, s *TestSuiteIAM, madmClient *madmin.AdminClient, accessKey, bucket string) {
  1619  	c.Helper()
  1620  	svcAK, svcSK := mustGenerateCredentials(c)
  1621  	cr, err := madmClient.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
  1622  		TargetUser: accessKey,
  1623  		AccessKey:  svcAK,
  1624  		SecretKey:  svcSK,
  1625  	})
  1626  	if err != nil {
  1627  		c.Fatalf("Unable to create svc acc: %v", err)
  1628  	}
  1629  	svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "")
  1630  	c.mustListObjects(ctx, svcClient, bucket)
  1631  
  1632  	err = madmClient.DeleteServiceAccount(ctx, svcAK)
  1633  	if err != nil {
  1634  		c.Fatalf("unable to delete svc acc: %v", err)
  1635  	}
  1636  	c.mustNotListObjects(ctx, svcClient, bucket)
  1637  }
  1638  
  1639  func mustGenerateCredentials(c *check) (string, string) {
  1640  	c.Helper()
  1641  	ak, sk, err := auth.GenerateCredentials()
  1642  	if err != nil {
  1643  		c.Fatalf("unable to generate credentials: %v", err)
  1644  	}
  1645  	return ak, sk
  1646  }