mirror of https://github.com/minio/minio.git
				
				
				
			
		
			
				
	
	
		
			1226 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			1226 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright (c) 2015-2021 MinIO, Inc.
 | |
| //
 | |
| // This file is part of MinIO Object Storage stack
 | |
| //
 | |
| // This program is free software: you can redistribute it and/or modify
 | |
| // it under the terms of the GNU Affero General Public License as published by
 | |
| // the Free Software Foundation, either version 3 of the License, or
 | |
| // (at your option) any later version.
 | |
| //
 | |
| // This program is distributed in the hope that it will be useful
 | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| // GNU Affero General Public License for more details.
 | |
| //
 | |
| // You should have received a copy of the GNU Affero General Public License
 | |
| // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
| package cmd
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/minio/madmin-go"
 | |
| 	minio "github.com/minio/minio-go/v7"
 | |
| 	cr "github.com/minio/minio-go/v7/pkg/credentials"
 | |
| 	"github.com/minio/minio-go/v7/pkg/set"
 | |
| )
 | |
| 
 | |
| func runAllIAMSTSTests(suite *TestSuiteIAM, c *check) {
 | |
| 	suite.SetUpSuite(c)
 | |
| 	// The STS for root test needs to be the first one after setup.
 | |
| 	suite.TestSTSForRoot(c)
 | |
| 	suite.TestSTS(c)
 | |
| 	suite.TestSTSWithGroupPolicy(c)
 | |
| 	suite.TearDownSuite(c)
 | |
| }
 | |
| 
 | |
| func TestIAMInternalIDPSTSServerSuite(t *testing.T) {
 | |
| 	baseTestCases := []TestSuiteCommon{
 | |
| 		// Init and run test on FS backend with signature v4.
 | |
| 		{serverType: "FS", signer: signerV4},
 | |
| 		// Init and run test on FS backend, with tls enabled.
 | |
| 		{serverType: "FS", signer: signerV4, secure: true},
 | |
| 		// Init and run test on Erasure backend.
 | |
| 		{serverType: "Erasure", signer: signerV4},
 | |
| 		// Init and run test on ErasureSet backend.
 | |
| 		{serverType: "ErasureSet", signer: signerV4},
 | |
| 	}
 | |
| 	testCases := []*TestSuiteIAM{}
 | |
| 	for _, bt := range baseTestCases {
 | |
| 		testCases = append(testCases,
 | |
| 			newTestSuiteIAM(bt, false),
 | |
| 			newTestSuiteIAM(bt, true),
 | |
| 		)
 | |
| 	}
 | |
| 	for i, testCase := range testCases {
 | |
| 		etcdStr := ""
 | |
| 		if testCase.withEtcdBackend {
 | |
| 			etcdStr = " (with etcd backend)"
 | |
| 		}
 | |
| 		t.Run(
 | |
| 			fmt.Sprintf("Test: %d, ServerType: %s%s", i+1, testCase.serverType, etcdStr),
 | |
| 			func(t *testing.T) {
 | |
| 				runAllIAMSTSTests(testCase, &check{t, testCase.serverType})
 | |
| 			},
 | |
| 		)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (s *TestSuiteIAM) TestSTS(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket creat error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create policy, user and associate policy
 | |
| 	policy := "mypolicy"
 | |
| 	policyBytes := []byte(fmt.Sprintf(`{
 | |
|  "Version": "2012-10-17",
 | |
|  "Statement": [
 | |
|   {
 | |
|    "Effect": "Allow",
 | |
|    "Action": [
 | |
|     "s3:PutObject",
 | |
|     "s3:GetObject",
 | |
|     "s3:ListBucket"
 | |
|    ],
 | |
|    "Resource": [
 | |
|     "arn:aws:s3:::%s/*"
 | |
|    ]
 | |
|   }
 | |
|  ]
 | |
| }`, bucket))
 | |
| 	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("policy add error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	accessKey, secretKey := mustGenerateCredentials(c)
 | |
| 	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Unable to set user: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	err = s.adm.SetPolicy(ctx, policy, accessKey, false)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Unable to set policy: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// confirm that the user is able to access the bucket
 | |
| 	uClient := s.getUserClient(c, accessKey, secretKey, "")
 | |
| 	c.mustListObjects(ctx, uClient, bucket)
 | |
| 
 | |
| 	assumeRole := cr.STSAssumeRole{
 | |
| 		Client:      s.TestSuiteCommon.client,
 | |
| 		STSEndpoint: s.endPoint,
 | |
| 		Options: cr.STSAssumeRoleOptions{
 | |
| 			AccessKey: accessKey,
 | |
| 			SecretKey: secretKey,
 | |
| 			Location:  "",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	value, err := assumeRole.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("err calling assumeRole: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	minioClient, err := minio.New(s.endpoint, &minio.Options{
 | |
| 		Creds:     cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure:    s.secure,
 | |
| 		Transport: s.TestSuiteCommon.client.Transport,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Error initializing client: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that the client from sts creds can access the bucket.
 | |
| 	c.mustListObjects(ctx, minioClient, bucket)
 | |
| 
 | |
| 	// Validate that the client cannot remove any objects
 | |
| 	err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{})
 | |
| 	if err.Error() != "Access Denied." {
 | |
| 		c.Fatalf("unexpected non-access-denied err: %v", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (s *TestSuiteIAM) TestSTSWithGroupPolicy(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket creat error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create policy, user and associate policy
 | |
| 	policy := "mypolicy"
 | |
| 	policyBytes := []byte(fmt.Sprintf(`{
 | |
|  "Version": "2012-10-17",
 | |
|  "Statement": [
 | |
|   {
 | |
|    "Effect": "Allow",
 | |
|    "Action": [
 | |
|     "s3:PutObject",
 | |
|     "s3:GetObject",
 | |
|     "s3:ListBucket"
 | |
|    ],
 | |
|    "Resource": [
 | |
|     "arn:aws:s3:::%s/*"
 | |
|    ]
 | |
|   }
 | |
|  ]
 | |
| }`, bucket))
 | |
| 	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("policy add error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	accessKey, secretKey := mustGenerateCredentials(c)
 | |
| 	err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Unable to set user: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// confirm that the user is unable to access the bucket - we have not
 | |
| 	// yet set any policy
 | |
| 	uClient := s.getUserClient(c, accessKey, secretKey, "")
 | |
| 	c.mustNotListObjects(ctx, uClient, bucket)
 | |
| 
 | |
| 	err = s.adm.UpdateGroupMembers(ctx, madmin.GroupAddRemove{
 | |
| 		Group:   "test-group",
 | |
| 		Members: []string{accessKey},
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("unable to add user to group: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	err = s.adm.SetPolicy(ctx, policy, "test-group", true)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Unable to set policy: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// confirm that the user is able to access the bucket - permission comes
 | |
| 	// from group.
 | |
| 	c.mustListObjects(ctx, uClient, bucket)
 | |
| 
 | |
| 	// Create STS user.
 | |
| 	assumeRole := cr.STSAssumeRole{
 | |
| 		Client:      s.TestSuiteCommon.client,
 | |
| 		STSEndpoint: s.endPoint,
 | |
| 		Options: cr.STSAssumeRoleOptions{
 | |
| 			AccessKey: accessKey,
 | |
| 			SecretKey: secretKey,
 | |
| 			Location:  "",
 | |
| 		},
 | |
| 	}
 | |
| 	value, err := assumeRole.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("err calling assumeRole: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Check that STS user client has access coming from parent user's
 | |
| 	// group.
 | |
| 	minioClient, err := minio.New(s.endpoint, &minio.Options{
 | |
| 		Creds:     cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure:    s.secure,
 | |
| 		Transport: s.TestSuiteCommon.client.Transport,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Error initializing client: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that the client from sts creds can access the bucket.
 | |
| 	c.mustListObjects(ctx, minioClient, bucket)
 | |
| 
 | |
| 	// Validate that the client cannot remove any objects
 | |
| 	err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{})
 | |
| 	if err.Error() != "Access Denied." {
 | |
| 		c.Fatalf("unexpected non-access-denied err: %v", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestSTSForRoot - needs to be the first test after server setup due to the
 | |
| // buckets list check.
 | |
| func (s *TestSuiteIAM) TestSTSForRoot(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket create error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	assumeRole := cr.STSAssumeRole{
 | |
| 		Client:      s.TestSuiteCommon.client,
 | |
| 		STSEndpoint: s.endPoint,
 | |
| 		Options: cr.STSAssumeRoleOptions{
 | |
| 			AccessKey: globalActiveCred.AccessKey,
 | |
| 			SecretKey: globalActiveCred.SecretKey,
 | |
| 			Location:  "",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	value, err := assumeRole.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("err calling assumeRole: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	minioClient, err := minio.New(s.endpoint, &minio.Options{
 | |
| 		Creds:     cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure:    s.secure,
 | |
| 		Transport: s.TestSuiteCommon.client.Transport,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Error initializing client: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that the client from sts creds can access the bucket.
 | |
| 	c.mustListObjects(ctx, minioClient, bucket)
 | |
| 
 | |
| 	// Validate that a bucket can be created
 | |
| 	bucket2 := getRandomBucketName()
 | |
| 	err = minioClient.MakeBucket(ctx, bucket2, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket creat error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that admin APIs can be called - create an madmin client with
 | |
| 	// user creds
 | |
| 	userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
 | |
| 		Creds:  cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure: s.secure,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Err creating user admin client: %v", err)
 | |
| 	}
 | |
| 	userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
 | |
| 
 | |
| 	accInfo, err := userAdmClient.AccountInfo(ctx, madmin.AccountOpts{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("root user STS should be able to get account info: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	gotBuckets := set.NewStringSet()
 | |
| 	for _, b := range accInfo.Buckets {
 | |
| 		gotBuckets.Add(b.Name)
 | |
| 		if !(b.Access.Read && b.Access.Write) {
 | |
| 			c.Fatalf("root user should have read and write access to bucket: %v", b.Name)
 | |
| 		}
 | |
| 	}
 | |
| 	shouldHaveBuckets := set.CreateStringSet(bucket2, bucket)
 | |
| 	if !gotBuckets.Equals(shouldHaveBuckets) {
 | |
| 		c.Fatalf("root user should have access to all buckets")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // SetUpLDAP - expects to setup an LDAP test server using the test LDAP
 | |
| // container and canned data from https://github.com/minio/minio-ldap-testing
 | |
| func (s *TestSuiteIAM) SetUpLDAP(c *check, serverAddr string) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	configCmds := []string{
 | |
| 		"identity_ldap",
 | |
| 		fmt.Sprintf("server_addr=%s", serverAddr),
 | |
| 		"server_insecure=on",
 | |
| 		"lookup_bind_dn=cn=admin,dc=min,dc=io",
 | |
| 		"lookup_bind_password=admin",
 | |
| 		"user_dn_search_base_dn=dc=min,dc=io",
 | |
| 		"user_dn_search_filter=(uid=%s)",
 | |
| 		"group_search_base_dn=ou=swengg,dc=min,dc=io",
 | |
| 		"group_search_filter=(&(objectclass=groupofnames)(member=%d))",
 | |
| 	}
 | |
| 	_, err := s.adm.SetConfigKV(ctx, strings.Join(configCmds, " "))
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("unable to setup LDAP for tests: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	s.RestartIAMSuite(c)
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	EnvTestLDAPServer = "LDAP_TEST_SERVER"
 | |
| )
 | |
| 
 | |
| func TestIAMWithLDAPServerSuite(t *testing.T) {
 | |
| 	baseTestCases := []TestSuiteCommon{
 | |
| 		// Init and run test on FS backend with signature v4.
 | |
| 		{serverType: "FS", signer: signerV4},
 | |
| 		// Init and run test on FS backend, with tls enabled.
 | |
| 		{serverType: "FS", signer: signerV4, secure: true},
 | |
| 		// Init and run test on Erasure backend.
 | |
| 		{serverType: "Erasure", signer: signerV4},
 | |
| 		// Init and run test on ErasureSet backend.
 | |
| 		{serverType: "ErasureSet", signer: signerV4},
 | |
| 	}
 | |
| 	testCases := []*TestSuiteIAM{}
 | |
| 	for _, bt := range baseTestCases {
 | |
| 		testCases = append(testCases,
 | |
| 			newTestSuiteIAM(bt, false),
 | |
| 			newTestSuiteIAM(bt, true),
 | |
| 		)
 | |
| 	}
 | |
| 	for i, testCase := range testCases {
 | |
| 		etcdStr := ""
 | |
| 		if testCase.withEtcdBackend {
 | |
| 			etcdStr = " (with etcd backend)"
 | |
| 		}
 | |
| 		t.Run(
 | |
| 			fmt.Sprintf("Test: %d, ServerType: %s%s", i+1, testCase.serverType, etcdStr),
 | |
| 			func(t *testing.T) {
 | |
| 				c := &check{t, testCase.serverType}
 | |
| 				suite := testCase
 | |
| 
 | |
| 				ldapServer := os.Getenv(EnvTestLDAPServer)
 | |
| 				if ldapServer == "" {
 | |
| 					c.Skip("Skipping LDAP test as no LDAP server is provided.")
 | |
| 				}
 | |
| 
 | |
| 				suite.SetUpSuite(c)
 | |
| 				suite.SetUpLDAP(c, ldapServer)
 | |
| 				suite.TestLDAPSTS(c)
 | |
| 				suite.TestLDAPSTSServiceAccounts(c)
 | |
| 				suite.TestLDAPSTSServiceAccountsWithGroups(c)
 | |
| 				suite.TearDownSuite(c)
 | |
| 			},
 | |
| 		)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (s *TestSuiteIAM) TestLDAPSTS(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket create error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create policy
 | |
| 	policy := "mypolicy"
 | |
| 	policyBytes := []byte(fmt.Sprintf(`{
 | |
|  "Version": "2012-10-17",
 | |
|  "Statement": [
 | |
|   {
 | |
|    "Effect": "Allow",
 | |
|    "Action": [
 | |
|     "s3:PutObject",
 | |
|     "s3:GetObject",
 | |
|     "s3:ListBucket"
 | |
|    ],
 | |
|    "Resource": [
 | |
|     "arn:aws:s3:::%s/*"
 | |
|    ]
 | |
|   }
 | |
|  ]
 | |
| }`, bucket))
 | |
| 	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("policy add error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	ldapID := cr.LDAPIdentity{
 | |
| 		Client:       s.TestSuiteCommon.client,
 | |
| 		STSEndpoint:  s.endPoint,
 | |
| 		LDAPUsername: "dillon",
 | |
| 		LDAPPassword: "dillon",
 | |
| 	}
 | |
| 
 | |
| 	_, err = ldapID.Retrieve()
 | |
| 	if err == nil {
 | |
| 		c.Fatalf("Expected to fail to create STS cred with no associated policy!")
 | |
| 	}
 | |
| 
 | |
| 	// Attempting to set a non-existent policy should fail.
 | |
| 	userDN := "uid=dillon,ou=people,ou=swengg,dc=min,dc=io"
 | |
| 	err = s.adm.SetPolicy(ctx, policy+"x", userDN, false)
 | |
| 	if err == nil {
 | |
| 		c.Fatalf("should not be able to set non-existent policy")
 | |
| 	}
 | |
| 
 | |
| 	err = s.adm.SetPolicy(ctx, policy, userDN, false)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Unable to set policy: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	value, err := ldapID.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Expected to generate STS creds, got err: %#v", err)
 | |
| 	}
 | |
| 
 | |
| 	minioClient, err := minio.New(s.endpoint, &minio.Options{
 | |
| 		Creds:     cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure:    s.secure,
 | |
| 		Transport: s.TestSuiteCommon.client.Transport,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Error initializing client: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that user listing does not return any entries
 | |
| 	usersList, err := s.adm.ListUsers(ctx)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("list users should not fail: %v", err)
 | |
| 	}
 | |
| 	if len(usersList) > 0 {
 | |
| 		c.Fatalf("expected listing to be empty: %v", usersList)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that the client from sts creds can access the bucket.
 | |
| 	c.mustListObjects(ctx, minioClient, bucket)
 | |
| 
 | |
| 	// Validate that the client cannot remove any objects
 | |
| 	err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{})
 | |
| 	if err.Error() != "Access Denied." {
 | |
| 		c.Fatalf("unexpected non-access-denied err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Remove the policy assignment on the user DN:
 | |
| 	err = s.adm.SetPolicy(ctx, "", userDN, false)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Unable to remove policy setting: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	_, err = ldapID.Retrieve()
 | |
| 	if err == nil {
 | |
| 		c.Fatalf("Expected to fail to create a user with no associated policy!")
 | |
| 	}
 | |
| 
 | |
| 	// Set policy via group and validate policy assignment.
 | |
| 	groupDN := "cn=projectb,ou=groups,ou=swengg,dc=min,dc=io"
 | |
| 	err = s.adm.SetPolicy(ctx, policy, groupDN, true)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Unable to set group policy: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	value, err = ldapID.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Expected to generate STS creds, got err: %#v", err)
 | |
| 	}
 | |
| 
 | |
| 	minioClient, err = minio.New(s.endpoint, &minio.Options{
 | |
| 		Creds:     cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure:    s.secure,
 | |
| 		Transport: s.TestSuiteCommon.client.Transport,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Error initializing client: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that the client from sts creds can access the bucket.
 | |
| 	c.mustListObjects(ctx, minioClient, bucket)
 | |
| 
 | |
| 	// Validate that the client cannot remove any objects
 | |
| 	err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{})
 | |
| 	c.Assert(err.Error(), "Access Denied.")
 | |
| }
 | |
| 
 | |
| func (s *TestSuiteIAM) TestLDAPSTSServiceAccounts(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket create error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create policy
 | |
| 	policy := "mypolicy"
 | |
| 	policyBytes := []byte(fmt.Sprintf(`{
 | |
|  "Version": "2012-10-17",
 | |
|  "Statement": [
 | |
|   {
 | |
|    "Effect": "Allow",
 | |
|    "Action": [
 | |
|     "s3:PutObject",
 | |
|     "s3:GetObject",
 | |
|     "s3:ListBucket"
 | |
|    ],
 | |
|    "Resource": [
 | |
|     "arn:aws:s3:::%s/*"
 | |
|    ]
 | |
|   }
 | |
|  ]
 | |
| }`, bucket))
 | |
| 	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("policy add error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	userDN := "uid=dillon,ou=people,ou=swengg,dc=min,dc=io"
 | |
| 	err = s.adm.SetPolicy(ctx, policy, userDN, false)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Unable to set policy: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	ldapID := cr.LDAPIdentity{
 | |
| 		Client:       s.TestSuiteCommon.client,
 | |
| 		STSEndpoint:  s.endPoint,
 | |
| 		LDAPUsername: "dillon",
 | |
| 		LDAPPassword: "dillon",
 | |
| 	}
 | |
| 
 | |
| 	value, err := ldapID.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Expected to generate STS creds, got err: %#v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Check that the LDAP sts cred is actually working.
 | |
| 	minioClient, err := minio.New(s.endpoint, &minio.Options{
 | |
| 		Creds:     cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure:    s.secure,
 | |
| 		Transport: s.TestSuiteCommon.client.Transport,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Error initializing client: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that the client from sts creds can access the bucket.
 | |
| 	c.mustListObjects(ctx, minioClient, bucket)
 | |
| 
 | |
| 	// Create an madmin client with user creds
 | |
| 	userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
 | |
| 		Creds:  cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure: s.secure,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Err creating user admin client: %v", err)
 | |
| 	}
 | |
| 	userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
 | |
| 
 | |
| 	// Create svc acc
 | |
| 	cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient)
 | |
| 
 | |
| 	// 1. Check that svc account appears in listing
 | |
| 	c.assertSvcAccAppearsInListing(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey)
 | |
| 
 | |
| 	// 2. Check that svc account info can be queried
 | |
| 	c.assertSvcAccInfoQueryable(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey, true)
 | |
| 
 | |
| 	// 3. Check S3 access
 | |
| 	c.assertSvcAccS3Access(ctx, s, cr, bucket)
 | |
| 
 | |
| 	// 4. Check that svc account can restrict the policy, and that the
 | |
| 	// session policy can be updated.
 | |
| 	c.assertSvcAccSessionPolicyUpdate(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| 
 | |
| 	// 4. Check that service account's secret key and account status can be
 | |
| 	// updated.
 | |
| 	c.assertSvcAccSecretKeyAndStatusUpdate(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| 
 | |
| 	// 5. Check that service account can be deleted.
 | |
| 	c.assertSvcAccDeletion(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| }
 | |
| 
 | |
| // In this test, the parent users gets their permissions from a group, rather
 | |
| // than having a policy set directly on them.
 | |
| func (s *TestSuiteIAM) TestLDAPSTSServiceAccountsWithGroups(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket create error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create policy
 | |
| 	policy := "mypolicy"
 | |
| 	policyBytes := []byte(fmt.Sprintf(`{
 | |
|  "Version": "2012-10-17",
 | |
|  "Statement": [
 | |
|   {
 | |
|    "Effect": "Allow",
 | |
|    "Action": [
 | |
|     "s3:PutObject",
 | |
|     "s3:GetObject",
 | |
|     "s3:ListBucket"
 | |
|    ],
 | |
|    "Resource": [
 | |
|     "arn:aws:s3:::%s/*"
 | |
|    ]
 | |
|   }
 | |
|  ]
 | |
| }`, bucket))
 | |
| 	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("policy add error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	groupDN := "cn=projecta,ou=groups,ou=swengg,dc=min,dc=io"
 | |
| 	err = s.adm.SetPolicy(ctx, policy, groupDN, true)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Unable to set policy: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	ldapID := cr.LDAPIdentity{
 | |
| 		Client:       s.TestSuiteCommon.client,
 | |
| 		STSEndpoint:  s.endPoint,
 | |
| 		LDAPUsername: "dillon",
 | |
| 		LDAPPassword: "dillon",
 | |
| 	}
 | |
| 
 | |
| 	value, err := ldapID.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Expected to generate STS creds, got err: %#v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Check that the LDAP sts cred is actually working.
 | |
| 	minioClient, err := minio.New(s.endpoint, &minio.Options{
 | |
| 		Creds:     cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure:    s.secure,
 | |
| 		Transport: s.TestSuiteCommon.client.Transport,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Error initializing client: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that the client from sts creds can access the bucket.
 | |
| 	c.mustListObjects(ctx, minioClient, bucket)
 | |
| 
 | |
| 	// Create an madmin client with user creds
 | |
| 	userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
 | |
| 		Creds:  cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure: s.secure,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Err creating user admin client: %v", err)
 | |
| 	}
 | |
| 	userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
 | |
| 
 | |
| 	// Create svc acc
 | |
| 	cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient)
 | |
| 
 | |
| 	// 1. Check that svc account appears in listing
 | |
| 	c.assertSvcAccAppearsInListing(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey)
 | |
| 
 | |
| 	// 2. Check that svc account info can be queried
 | |
| 	c.assertSvcAccInfoQueryable(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey, true)
 | |
| 
 | |
| 	// 3. Check S3 access
 | |
| 	c.assertSvcAccS3Access(ctx, s, cr, bucket)
 | |
| 
 | |
| 	// 4. Check that svc account can restrict the policy, and that the
 | |
| 	// session policy can be updated.
 | |
| 	c.assertSvcAccSessionPolicyUpdate(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| 
 | |
| 	// 4. Check that service account's secret key and account status can be
 | |
| 	// updated.
 | |
| 	c.assertSvcAccSecretKeyAndStatusUpdate(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| 
 | |
| 	// 5. Check that service account can be deleted.
 | |
| 	c.assertSvcAccDeletion(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| }
 | |
| 
 | |
| func (s *TestSuiteIAM) TestOpenIDSTS(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket create error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Generate web identity STS token by interacting with OpenID IDP.
 | |
| 	token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon")
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("mock user err: %v", err)
 | |
| 	}
 | |
| 	// fmt.Printf("TOKEN: %s\n", token)
 | |
| 
 | |
| 	webID := cr.STSWebIdentity{
 | |
| 		Client:      s.TestSuiteCommon.client,
 | |
| 		STSEndpoint: s.endPoint,
 | |
| 		GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) {
 | |
| 			return &cr.WebIdentityToken{
 | |
| 				Token: token,
 | |
| 			}, nil
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// Create policy - with name as one of the groups in OpenID the user is
 | |
| 	// a member of.
 | |
| 	policy := "projecta"
 | |
| 	policyBytes := []byte(fmt.Sprintf(`{
 | |
|  "Version": "2012-10-17",
 | |
|  "Statement": [
 | |
|   {
 | |
|    "Effect": "Allow",
 | |
|    "Action": [
 | |
|     "s3:PutObject",
 | |
|     "s3:GetObject",
 | |
|     "s3:ListBucket"
 | |
|    ],
 | |
|    "Resource": [
 | |
|     "arn:aws:s3:::%s/*"
 | |
|    ]
 | |
|   }
 | |
|  ]
 | |
| }`, bucket))
 | |
| 	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("policy add error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	value, err := webID.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Expected to generate STS creds, got err: %#v", err)
 | |
| 	}
 | |
| 
 | |
| 	minioClient, err := minio.New(s.endpoint, &minio.Options{
 | |
| 		Creds:     cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure:    s.secure,
 | |
| 		Transport: s.TestSuiteCommon.client.Transport,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Error initializing client: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that the client from sts creds can access the bucket.
 | |
| 	c.mustListObjects(ctx, minioClient, bucket)
 | |
| 
 | |
| 	// Validate that the client cannot remove any objects
 | |
| 	err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{})
 | |
| 	if err.Error() != "Access Denied." {
 | |
| 		c.Fatalf("unexpected non-access-denied err: %v", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (s *TestSuiteIAM) TestOpenIDSTSAddUser(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket create error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Generate web identity STS token by interacting with OpenID IDP.
 | |
| 	token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon")
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("mock user err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	webID := cr.STSWebIdentity{
 | |
| 		Client:      s.TestSuiteCommon.client,
 | |
| 		STSEndpoint: s.endPoint,
 | |
| 		GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) {
 | |
| 			return &cr.WebIdentityToken{
 | |
| 				Token: token,
 | |
| 			}, nil
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// Create policy - with name as one of the groups in OpenID the user is
 | |
| 	// a member of.
 | |
| 	policy := "projecta"
 | |
| 	policyBytes := []byte(fmt.Sprintf(`{
 | |
|  "Version": "2012-10-17",
 | |
|  "Statement": [
 | |
|   {
 | |
|    "Effect": "Allow",
 | |
|    "Action": [
 | |
|     "s3:PutObject",
 | |
|     "s3:GetObject",
 | |
|     "s3:ListBucket"
 | |
|    ],
 | |
|    "Resource": [
 | |
|     "arn:aws:s3:::%s/*"
 | |
|    ]
 | |
|   }
 | |
|  ]
 | |
| }`, bucket))
 | |
| 	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("policy add error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	value, err := webID.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Expected to generate STS creds, got err: %#v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create an madmin client with user creds
 | |
| 	userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
 | |
| 		Creds:  cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure: s.secure,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Err creating user admin client: %v", err)
 | |
| 	}
 | |
| 	userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
 | |
| 
 | |
| 	c.mustNotCreateIAMUser(ctx, userAdmClient)
 | |
| 
 | |
| 	// Create admin user policy.
 | |
| 	policyBytes = []byte(`{
 | |
|  "Version": "2012-10-17",
 | |
|  "Statement": [
 | |
|   {
 | |
|    "Effect": "Allow",
 | |
|    "Action": [
 | |
|     "admin:*"
 | |
|    ]
 | |
|   }
 | |
|  ]
 | |
| }`)
 | |
| 	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("policy add error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	cr := c.mustCreateIAMUser(ctx, userAdmClient)
 | |
| 
 | |
| 	userInfo := c.mustGetIAMUserInfo(ctx, userAdmClient, cr.AccessKey)
 | |
| 	c.Assert(userInfo.Status, madmin.AccountEnabled)
 | |
| }
 | |
| 
 | |
| func (s *TestSuiteIAM) TestOpenIDServiceAcc(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket create error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Generate web identity STS token by interacting with OpenID IDP.
 | |
| 	token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon")
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("mock user err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	webID := cr.STSWebIdentity{
 | |
| 		Client:      s.TestSuiteCommon.client,
 | |
| 		STSEndpoint: s.endPoint,
 | |
| 		GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) {
 | |
| 			return &cr.WebIdentityToken{
 | |
| 				Token: token,
 | |
| 			}, nil
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// Create policy - with name as one of the groups in OpenID the user is
 | |
| 	// a member of.
 | |
| 	policy := "projecta"
 | |
| 	policyBytes := []byte(fmt.Sprintf(`{
 | |
|  "Version": "2012-10-17",
 | |
|  "Statement": [
 | |
|   {
 | |
|    "Effect": "Allow",
 | |
|    "Action": [
 | |
|     "s3:PutObject",
 | |
|     "s3:GetObject",
 | |
|     "s3:ListBucket"
 | |
|    ],
 | |
|    "Resource": [
 | |
|     "arn:aws:s3:::%s/*"
 | |
|    ]
 | |
|   }
 | |
|  ]
 | |
| }`, bucket))
 | |
| 	err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("policy add error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	value, err := webID.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Expected to generate STS creds, got err: %#v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create an madmin client with user creds
 | |
| 	userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
 | |
| 		Creds:  cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure: s.secure,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Err creating user admin client: %v", err)
 | |
| 	}
 | |
| 	userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
 | |
| 
 | |
| 	// Create svc acc
 | |
| 	cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient)
 | |
| 
 | |
| 	// 1. Check that svc account appears in listing
 | |
| 	c.assertSvcAccAppearsInListing(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey)
 | |
| 
 | |
| 	// 2. Check that svc account info can be queried
 | |
| 	c.assertSvcAccInfoQueryable(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey, true)
 | |
| 
 | |
| 	// 3. Check S3 access
 | |
| 	c.assertSvcAccS3Access(ctx, s, cr, bucket)
 | |
| 
 | |
| 	// 4. Check that svc account can restrict the policy, and that the
 | |
| 	// session policy can be updated.
 | |
| 	c.assertSvcAccSessionPolicyUpdate(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| 
 | |
| 	// 4. Check that service account's secret key and account status can be
 | |
| 	// updated.
 | |
| 	c.assertSvcAccSecretKeyAndStatusUpdate(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| 
 | |
| 	// 5. Check that service account can be deleted.
 | |
| 	c.assertSvcAccDeletion(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| }
 | |
| 
 | |
| var testAppParams = OpenIDClientAppParams{
 | |
| 	ClientID:     "minio-client-app",
 | |
| 	ClientSecret: "minio-client-app-secret",
 | |
| 	ProviderURL:  "http://127.0.0.1:5556/dex",
 | |
| 	RedirectURL:  "http://127.0.0.1:10000/oauth_callback",
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	EnvTestOpenIDServer = "OPENID_TEST_SERVER"
 | |
| )
 | |
| 
 | |
| // SetUpOpenID - expects to setup an OpenID test server using the test OpenID
 | |
| // container and canned data from https://github.com/minio/minio-ldap-testing
 | |
| func (s *TestSuiteIAM) SetUpOpenID(c *check, serverAddr string, rolePolicy string) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	configCmds := []string{
 | |
| 		"identity_openid",
 | |
| 		fmt.Sprintf("config_url=%s/.well-known/openid-configuration", serverAddr),
 | |
| 		"client_id=minio-client-app",
 | |
| 		"client_secret=minio-client-app-secret",
 | |
| 		"scopes=openid,groups",
 | |
| 		"redirect_uri=http://127.0.0.1:10000/oauth_callback",
 | |
| 	}
 | |
| 	if rolePolicy != "" {
 | |
| 		configCmds = append(configCmds, fmt.Sprintf("role_policy=%s", rolePolicy))
 | |
| 	} else {
 | |
| 		configCmds = append(configCmds, "claim_name=groups")
 | |
| 	}
 | |
| 	_, err := s.adm.SetConfigKV(ctx, strings.Join(configCmds, " "))
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("unable to setup OpenID for tests: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	s.RestartIAMSuite(c)
 | |
| }
 | |
| 
 | |
| func TestIAMWithOpenIDServerSuite(t *testing.T) {
 | |
| 	baseTestCases := []TestSuiteCommon{
 | |
| 		// Init and run test on FS backend with signature v4.
 | |
| 		{serverType: "FS", signer: signerV4},
 | |
| 		// Init and run test on FS backend, with tls enabled.
 | |
| 		{serverType: "FS", signer: signerV4, secure: true},
 | |
| 		// Init and run test on Erasure backend.
 | |
| 		{serverType: "Erasure", signer: signerV4},
 | |
| 		// Init and run test on ErasureSet backend.
 | |
| 		{serverType: "ErasureSet", signer: signerV4},
 | |
| 	}
 | |
| 	testCases := []*TestSuiteIAM{}
 | |
| 	for _, bt := range baseTestCases {
 | |
| 		testCases = append(testCases,
 | |
| 			newTestSuiteIAM(bt, false),
 | |
| 			newTestSuiteIAM(bt, true),
 | |
| 		)
 | |
| 	}
 | |
| 	for i, testCase := range testCases {
 | |
| 		etcdStr := ""
 | |
| 		if testCase.withEtcdBackend {
 | |
| 			etcdStr = " (with etcd backend)"
 | |
| 		}
 | |
| 		t.Run(
 | |
| 			fmt.Sprintf("Test: %d, ServerType: %s%s", i+1, testCase.serverType, etcdStr),
 | |
| 			func(t *testing.T) {
 | |
| 				c := &check{t, testCase.serverType}
 | |
| 				suite := testCase
 | |
| 
 | |
| 				openIDServer := os.Getenv(EnvTestOpenIDServer)
 | |
| 				if openIDServer == "" {
 | |
| 					c.Skip("Skipping OpenID test as no OpenID server is provided.")
 | |
| 				}
 | |
| 
 | |
| 				suite.SetUpSuite(c)
 | |
| 				suite.SetUpOpenID(c, openIDServer, "")
 | |
| 				suite.TestOpenIDSTS(c)
 | |
| 				suite.TestOpenIDServiceAcc(c)
 | |
| 				suite.TestOpenIDSTSAddUser(c)
 | |
| 				suite.TearDownSuite(c)
 | |
| 			},
 | |
| 		)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestIAMWithOpenIDWithRolePolicyServerSuite(t *testing.T) {
 | |
| 	baseTestCases := []TestSuiteCommon{
 | |
| 		// Init and run test on FS backend with signature v4.
 | |
| 		{serverType: "FS", signer: signerV4},
 | |
| 		// Init and run test on FS backend, with tls enabled.
 | |
| 		{serverType: "FS", signer: signerV4, secure: true},
 | |
| 		// Init and run test on Erasure backend.
 | |
| 		{serverType: "Erasure", signer: signerV4},
 | |
| 		// Init and run test on ErasureSet backend.
 | |
| 		{serverType: "ErasureSet", signer: signerV4},
 | |
| 	}
 | |
| 	testCases := []*TestSuiteIAM{}
 | |
| 	for _, bt := range baseTestCases {
 | |
| 		testCases = append(testCases,
 | |
| 			newTestSuiteIAM(bt, false),
 | |
| 			newTestSuiteIAM(bt, true),
 | |
| 		)
 | |
| 	}
 | |
| 	for i, testCase := range testCases {
 | |
| 		etcdStr := ""
 | |
| 		if testCase.withEtcdBackend {
 | |
| 			etcdStr = " (with etcd backend)"
 | |
| 		}
 | |
| 		t.Run(
 | |
| 			fmt.Sprintf("Test: %d, ServerType: %s%s", i+1, testCase.serverType, etcdStr),
 | |
| 			func(t *testing.T) {
 | |
| 				c := &check{t, testCase.serverType}
 | |
| 				suite := testCase
 | |
| 
 | |
| 				openIDServer := os.Getenv(EnvTestOpenIDServer)
 | |
| 				if openIDServer == "" {
 | |
| 					c.Skip("Skipping OpenID test as no OpenID server is provided.")
 | |
| 				}
 | |
| 
 | |
| 				suite.SetUpSuite(c)
 | |
| 				suite.SetUpOpenID(c, openIDServer, "readwrite")
 | |
| 				suite.TestOpenIDSTSWithRolePolicy(c)
 | |
| 				suite.TestOpenIDServiceAccWithRolePolicy(c)
 | |
| 				suite.TearDownSuite(c)
 | |
| 			},
 | |
| 		)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	testRoleARN = "arn:minio:iam:::role/nOybJqMNzNmroqEKq5D0EUsRZw0"
 | |
| )
 | |
| 
 | |
| func (s *TestSuiteIAM) TestOpenIDSTSWithRolePolicy(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket create error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Generate web identity STS token by interacting with OpenID IDP.
 | |
| 	token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon")
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("mock user err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	webID := cr.STSWebIdentity{
 | |
| 		Client:      s.TestSuiteCommon.client,
 | |
| 		STSEndpoint: s.endPoint,
 | |
| 		GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) {
 | |
| 			return &cr.WebIdentityToken{
 | |
| 				Token: token,
 | |
| 			}, nil
 | |
| 		},
 | |
| 		RoleARN: testRoleARN,
 | |
| 	}
 | |
| 
 | |
| 	value, err := webID.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Expected to generate STS creds, got err: %#v", err)
 | |
| 	}
 | |
| 	// fmt.Printf("value: %#v\n", value)
 | |
| 
 | |
| 	minioClient, err := minio.New(s.endpoint, &minio.Options{
 | |
| 		Creds:     cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure:    s.secure,
 | |
| 		Transport: s.TestSuiteCommon.client.Transport,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Error initializing client: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Validate that the client from sts creds can access the bucket.
 | |
| 	c.mustListObjects(ctx, minioClient, bucket)
 | |
| }
 | |
| 
 | |
| func (s *TestSuiteIAM) TestOpenIDServiceAccWithRolePolicy(c *check) {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	bucket := getRandomBucketName()
 | |
| 	err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("bucket create error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Generate web identity STS token by interacting with OpenID IDP.
 | |
| 	token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon")
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("mock user err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	webID := cr.STSWebIdentity{
 | |
| 		Client:      s.TestSuiteCommon.client,
 | |
| 		STSEndpoint: s.endPoint,
 | |
| 		GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) {
 | |
| 			return &cr.WebIdentityToken{
 | |
| 				Token: token,
 | |
| 			}, nil
 | |
| 		},
 | |
| 		RoleARN: testRoleARN,
 | |
| 	}
 | |
| 
 | |
| 	value, err := webID.Retrieve()
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Expected to generate STS creds, got err: %#v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create an madmin client with user creds
 | |
| 	userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{
 | |
| 		Creds:  cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken),
 | |
| 		Secure: s.secure,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		c.Fatalf("Err creating user admin client: %v", err)
 | |
| 	}
 | |
| 	userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport)
 | |
| 
 | |
| 	// Create svc acc
 | |
| 	cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient)
 | |
| 
 | |
| 	// 1. Check that svc account appears in listing
 | |
| 	c.assertSvcAccAppearsInListing(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey)
 | |
| 
 | |
| 	// 2. Check that svc account info can be queried
 | |
| 	c.assertSvcAccInfoQueryable(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey, true)
 | |
| 
 | |
| 	// 3. Check S3 access
 | |
| 	c.assertSvcAccS3Access(ctx, s, cr, bucket)
 | |
| 
 | |
| 	// 4. Check that svc account can restrict the policy, and that the
 | |
| 	// session policy can be updated.
 | |
| 	c.assertSvcAccSessionPolicyUpdate(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| 
 | |
| 	// 4. Check that service account's secret key and account status can be
 | |
| 	// updated.
 | |
| 	c.assertSvcAccSecretKeyAndStatusUpdate(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| 
 | |
| 	// 5. Check that service account can be deleted.
 | |
| 	c.assertSvcAccDeletion(ctx, s, userAdmClient, value.AccessKeyID, bucket)
 | |
| }
 |