mirror of https://github.com/minio/minio.git
fix: IAM import/export: remove sts group handling (#19422)
There are no separate STS group mappings to be handled. Also add tests for basic import/export sanity.
This commit is contained in:
parent
91f91d8f47
commit
8ff2a7a2b9
|
@ -1756,15 +1756,14 @@ func (a adminAPIHandlers) AttachDetachPolicyBuiltin(w http.ResponseWriter, r *ht
|
|||
}
|
||||
|
||||
const (
|
||||
allPoliciesFile = "policies.json"
|
||||
allUsersFile = "users.json"
|
||||
allGroupsFile = "groups.json"
|
||||
allSvcAcctsFile = "svcaccts.json"
|
||||
userPolicyMappingsFile = "user_mappings.json"
|
||||
groupPolicyMappingsFile = "group_mappings.json"
|
||||
stsUserPolicyMappingsFile = "stsuser_mappings.json"
|
||||
stsGroupPolicyMappingsFile = "stsgroup_mappings.json"
|
||||
iamAssetsDir = "iam-assets"
|
||||
allPoliciesFile = "policies.json"
|
||||
allUsersFile = "users.json"
|
||||
allGroupsFile = "groups.json"
|
||||
allSvcAcctsFile = "svcaccts.json"
|
||||
userPolicyMappingsFile = "user_mappings.json"
|
||||
groupPolicyMappingsFile = "group_mappings.json"
|
||||
stsUserPolicyMappingsFile = "stsuser_mappings.json"
|
||||
iamAssetsDir = "iam-assets"
|
||||
)
|
||||
|
||||
// ExportIAMHandler - exports all iam info as a zipped file
|
||||
|
@ -1813,7 +1812,6 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) {
|
|||
userPolicyMappingsFile,
|
||||
groupPolicyMappingsFile,
|
||||
stsUserPolicyMappingsFile,
|
||||
stsGroupPolicyMappingsFile,
|
||||
}
|
||||
for _, f := range iamFiles {
|
||||
iamFile := pathJoin(iamAssetsDir, f)
|
||||
|
@ -1985,22 +1983,6 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) {
|
|||
writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL)
|
||||
return
|
||||
}
|
||||
case stsGroupPolicyMappingsFile:
|
||||
groupPolicyMap := xsync.NewMapOf[string, MappedPolicy]()
|
||||
err := globalIAMSys.store.loadMappedPolicies(ctx, stsUser, true, groupPolicyMap)
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL)
|
||||
return
|
||||
}
|
||||
grpPolData, err := json.Marshal(mappedPoliciesToMap(groupPolicyMap))
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL)
|
||||
return
|
||||
}
|
||||
if err = rawDataFn(bytes.NewReader(grpPolData), iamFile, len(grpPolData)); err != nil {
|
||||
writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2391,35 +2373,6 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// import sts group policy mappings
|
||||
{
|
||||
f, err := zr.Open(pathJoin(iamAssetsDir, stsGroupPolicyMappingsFile))
|
||||
switch {
|
||||
case errors.Is(err, os.ErrNotExist):
|
||||
case err != nil:
|
||||
writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, ErrInvalidRequest, err, stsGroupPolicyMappingsFile, ""), r.URL)
|
||||
return
|
||||
default:
|
||||
defer f.Close()
|
||||
var grpPolicyMap map[string]MappedPolicy
|
||||
data, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, ErrInvalidRequest, err, stsGroupPolicyMappingsFile, ""), r.URL)
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal(data, &grpPolicyMap); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, ErrAdminConfigBadJSON, err, stsGroupPolicyMappingsFile, ""), r.URL)
|
||||
return
|
||||
}
|
||||
for g, pm := range grpPolicyMap {
|
||||
if _, err := globalIAMSys.PolicyDBSet(ctx, g, pm.Policies, unknownIAMUserType, true); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, importError(ctx, err, stsGroupPolicyMappingsFile, g), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addExpirationToCondValues(exp *time.Time, condValues map[string][]string) {
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -752,6 +755,172 @@ func TestIAMWithLDAPNonNormalizedBaseDNConfigServerSuite(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIAMExportImportWithLDAP(t *testing.T) {
|
||||
for i, testCase := range iamTestSuites {
|
||||
t.Run(
|
||||
fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription),
|
||||
func(t *testing.T) {
|
||||
c := &check{t, testCase.serverType}
|
||||
suite := testCase
|
||||
|
||||
ldapServer := os.Getenv(EnvTestLDAPServer)
|
||||
if ldapServer == "" {
|
||||
c.Skipf("Skipping LDAP test as no LDAP server is provided via %s", EnvTestLDAPServer)
|
||||
}
|
||||
|
||||
iamTestContentCases := []iamTestContent{
|
||||
{
|
||||
policies: map[string][]byte{
|
||||
"mypolicy": []byte(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:GetObject","s3:ListBucket","s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/*"]}]}`),
|
||||
},
|
||||
ldapUserPolicyMappings: map[string][]string{
|
||||
"uid=dillon,ou=people,ou=swengg,dc=min,dc=io": {"mypolicy"},
|
||||
"uid=liza,ou=people,ou=swengg,dc=min,dc=io": {"consoleAdmin"},
|
||||
},
|
||||
ldapGroupPolicyMappings: map[string][]string{
|
||||
"cn=projectb,ou=groups,ou=swengg,dc=min,dc=io": {"mypolicy"},
|
||||
"cn=projecta,ou=groups,ou=swengg,dc=min,dc=io": {"consoleAdmin"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for caseNum, content := range iamTestContentCases {
|
||||
suite.SetUpSuite(c)
|
||||
suite.SetUpLDAP(c, ldapServer)
|
||||
exportedContent := suite.TestIAMExport(c, caseNum, content)
|
||||
suite.TearDownSuite(c)
|
||||
suite.SetUpSuite(c)
|
||||
suite.SetUpLDAP(c, ldapServer)
|
||||
suite.TestIAMImport(c, exportedContent, caseNum, content)
|
||||
suite.TearDownSuite(c)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
type iamTestContent struct {
|
||||
policies map[string][]byte
|
||||
ldapUserPolicyMappings map[string][]string
|
||||
ldapGroupPolicyMappings map[string][]string
|
||||
}
|
||||
|
||||
func (s *TestSuiteIAM) TestIAMExport(c *check, caseNum int, content iamTestContent) []byte {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
for policy, policyBytes := range content.policies {
|
||||
err := s.adm.AddCannedPolicy(ctx, policy, policyBytes)
|
||||
if err != nil {
|
||||
c.Fatalf("export %d: policy add error: %v", caseNum, err)
|
||||
}
|
||||
}
|
||||
|
||||
for userDN, policies := range content.ldapUserPolicyMappings {
|
||||
_, err := s.adm.AttachPolicyLDAP(ctx, madmin.PolicyAssociationReq{
|
||||
Policies: policies,
|
||||
User: userDN,
|
||||
})
|
||||
if err != nil {
|
||||
c.Fatalf("export %d: Unable to attach policy: %v", caseNum, err)
|
||||
}
|
||||
}
|
||||
|
||||
for groupDN, policies := range content.ldapGroupPolicyMappings {
|
||||
_, err := s.adm.AttachPolicyLDAP(ctx, madmin.PolicyAssociationReq{
|
||||
Policies: policies,
|
||||
Group: groupDN,
|
||||
})
|
||||
if err != nil {
|
||||
c.Fatalf("export %d: Unable to attach group policy: %v", caseNum, err)
|
||||
}
|
||||
}
|
||||
|
||||
contentReader, err := s.adm.ExportIAM(ctx)
|
||||
if err != nil {
|
||||
c.Fatalf("export %d: Unable to export IAM: %v", caseNum, err)
|
||||
}
|
||||
defer contentReader.Close()
|
||||
|
||||
expContent, err := io.ReadAll(contentReader)
|
||||
if err != nil {
|
||||
c.Fatalf("export %d: Unable to read exported content: %v", caseNum, err)
|
||||
}
|
||||
|
||||
return expContent
|
||||
}
|
||||
|
||||
type dummyCloser struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (d dummyCloser) Close() error { return nil }
|
||||
|
||||
func (s *TestSuiteIAM) TestIAMImport(c *check, exportedContent []byte, caseNum int, content iamTestContent) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
dummyCloser := dummyCloser{bytes.NewReader(exportedContent)}
|
||||
err := s.adm.ImportIAM(ctx, dummyCloser)
|
||||
if err != nil {
|
||||
c.Fatalf("import %d: Unable to import IAM: %v", caseNum, err)
|
||||
}
|
||||
|
||||
gotContent := iamTestContent{
|
||||
policies: make(map[string][]byte),
|
||||
ldapUserPolicyMappings: make(map[string][]string),
|
||||
ldapGroupPolicyMappings: make(map[string][]string),
|
||||
}
|
||||
policyContentMap, err := s.adm.ListCannedPolicies(ctx)
|
||||
if err != nil {
|
||||
c.Fatalf("import %d: Unable to list policies: %v", caseNum, err)
|
||||
}
|
||||
defaultCannedPolicies := set.CreateStringSet("consoleAdmin", "readwrite", "readonly",
|
||||
"diagnostics", "writeonly")
|
||||
for policy, policyBytes := range policyContentMap {
|
||||
if defaultCannedPolicies.Contains(policy) {
|
||||
continue
|
||||
}
|
||||
gotContent.policies[policy] = policyBytes
|
||||
}
|
||||
|
||||
policyQueryRes, err := s.adm.GetLDAPPolicyEntities(ctx, madmin.PolicyEntitiesQuery{})
|
||||
if err != nil {
|
||||
c.Fatalf("import %d: Unable to get policy entities: %v", caseNum, err)
|
||||
}
|
||||
|
||||
for _, entity := range policyQueryRes.PolicyMappings {
|
||||
m := gotContent.ldapUserPolicyMappings
|
||||
for _, user := range entity.Users {
|
||||
m[user] = append(m[user], entity.Policy)
|
||||
}
|
||||
m = gotContent.ldapGroupPolicyMappings
|
||||
for _, group := range entity.Groups {
|
||||
m[group] = append(m[group], entity.Policy)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// We don't compare the values of the canned policies because server is
|
||||
// re-encoding them. (FIXME?)
|
||||
for k := range content.policies {
|
||||
content.policies[k] = nil
|
||||
gotContent.policies[k] = nil
|
||||
}
|
||||
if !reflect.DeepEqual(content.policies, gotContent.policies) {
|
||||
c.Fatalf("import %d: policies mismatch: expected: %v, got: %v", caseNum, content.policies, gotContent.policies)
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(content.ldapUserPolicyMappings, gotContent.ldapUserPolicyMappings) {
|
||||
c.Fatalf("import %d: user policy mappings mismatch: expected: %v, got: %v", caseNum, content.ldapUserPolicyMappings, gotContent.ldapUserPolicyMappings)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(content.ldapGroupPolicyMappings, gotContent.ldapGroupPolicyMappings) {
|
||||
c.Fatalf("import %d: group policy mappings mismatch: expected: %v, got: %v", caseNum, content.ldapGroupPolicyMappings, gotContent.ldapGroupPolicyMappings)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TestSuiteIAM) TestLDAPSTS(c *check) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout)
|
||||
defer cancel()
|
||||
|
|
Loading…
Reference in New Issue