Skip to content

Commit 0b7aa6a

Browse files
authored
Skip non existent ldap entities while import (minio#20352)
Dont hard error for nonexisting LDAP entries instead of logging them report them via `mc` Signed-off-by: Shubhendu Ram Tripathi <[email protected]>
1 parent 8c9ab85 commit 0b7aa6a

File tree

5 files changed

+105
-36
lines changed

5 files changed

+105
-36
lines changed

cmd/admin-handlers-users.go

+86-16
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ import (
2626
"io"
2727
"net/http"
2828
"os"
29+
"slices"
2930
"sort"
3031
"strconv"
32+
"strings"
3133
"time"
3234
"unicode/utf8"
3335

@@ -2046,6 +2048,16 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) {
20462048

20472049
// ImportIAM - imports all IAM info into MinIO
20482050
func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
2051+
a.importIAM(w, r, "")
2052+
}
2053+
2054+
// ImportIAMV2 - imports all IAM info into MinIO
2055+
func (a adminAPIHandlers) ImportIAMV2(w http.ResponseWriter, r *http.Request) {
2056+
a.importIAM(w, r, "v2")
2057+
}
2058+
2059+
// ImportIAM - imports all IAM info into MinIO
2060+
func (a adminAPIHandlers) importIAM(w http.ResponseWriter, r *http.Request, apiVer string) {
20492061
ctx := r.Context()
20502062

20512063
// Get current object layer instance.
@@ -2070,6 +2082,10 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
20702082
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
20712083
return
20722084
}
2085+
2086+
var skipped, removed, added madmin.IAMEntities
2087+
var failed madmin.IAMErrEntities
2088+
20732089
// import policies first
20742090
{
20752091

@@ -2095,8 +2111,10 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
20952111
for policyName, policy := range allPolicies {
20962112
if policy.IsEmpty() {
20972113
err = globalIAMSys.DeletePolicy(ctx, policyName, true)
2114+
removed.Policies = append(removed.Policies, policyName)
20982115
} else {
20992116
_, err = globalIAMSys.SetPolicy(ctx, policyName, policy)
2117+
added.Policies = append(added.Policies, policyName)
21002118
}
21012119
if err != nil {
21022120
writeErrorResponseJSON(ctx, w, importError(ctx, err, allPoliciesFile, policyName), r.URL)
@@ -2175,8 +2193,9 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
21752193
return
21762194
}
21772195
if _, err = globalIAMSys.CreateUser(ctx, accessKey, ureq); err != nil {
2178-
writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, toAdminAPIErrCode(ctx, err), err, allUsersFile, accessKey), r.URL)
2179-
return
2196+
failed.Users = append(failed.Users, madmin.IAMErrEntity{Name: accessKey, Error: err})
2197+
} else {
2198+
added.Users = append(added.Users, accessKey)
21802199
}
21812200

21822201
}
@@ -2214,8 +2233,9 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
22142233
}
22152234
}
22162235
if _, gerr := globalIAMSys.AddUsersToGroup(ctx, group, grpInfo.Members); gerr != nil {
2217-
writeErrorResponseJSON(ctx, w, importError(ctx, gerr, allGroupsFile, group), r.URL)
2218-
return
2236+
failed.Groups = append(failed.Groups, madmin.IAMErrEntity{Name: group, Error: err})
2237+
} else {
2238+
added.Groups = append(added.Groups, group)
22192239
}
22202240
}
22212241
}
@@ -2244,14 +2264,18 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
22442264

22452265
// Validations for LDAP enabled deployments.
22462266
if globalIAMSys.LDAPConfig.Enabled() {
2247-
err := globalIAMSys.NormalizeLDAPAccessKeypairs(ctx, serviceAcctReqs)
2267+
skippedAccessKeys, err := globalIAMSys.NormalizeLDAPAccessKeypairs(ctx, serviceAcctReqs)
2268+
skipped.ServiceAccounts = append(skipped.ServiceAccounts, skippedAccessKeys...)
22482269
if err != nil {
22492270
writeErrorResponseJSON(ctx, w, importError(ctx, err, allSvcAcctsFile, ""), r.URL)
22502271
return
22512272
}
22522273
}
22532274

22542275
for user, svcAcctReq := range serviceAcctReqs {
2276+
if slices.Contains(skipped.ServiceAccounts, user) {
2277+
continue
2278+
}
22552279
var sp *policy.Policy
22562280
var err error
22572281
if len(svcAcctReq.SessionPolicy) > 0 {
@@ -2309,10 +2333,10 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
23092333
}
23102334

23112335
if _, _, err = globalIAMSys.NewServiceAccount(ctx, svcAcctReq.Parent, svcAcctReq.Groups, opts); err != nil {
2312-
writeErrorResponseJSON(ctx, w, importError(ctx, err, allSvcAcctsFile, user), r.URL)
2313-
return
2336+
failed.ServiceAccounts = append(failed.ServiceAccounts, madmin.IAMErrEntity{Name: user, Error: err})
2337+
} else {
2338+
added.ServiceAccounts = append(added.ServiceAccounts, user)
23142339
}
2315-
23162340
}
23172341
}
23182342
}
@@ -2349,8 +2373,15 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
23492373
return
23502374
}
23512375
if _, err := globalIAMSys.PolicyDBSet(ctx, u, pm.Policies, regUser, false); err != nil {
2352-
writeErrorResponseJSON(ctx, w, importError(ctx, err, userPolicyMappingsFile, u), r.URL)
2353-
return
2376+
failed.UserPolicies = append(
2377+
failed.UserPolicies,
2378+
madmin.IAMErrPolicyEntity{
2379+
Name: u,
2380+
Policies: strings.Split(pm.Policies, ","),
2381+
Error: err,
2382+
})
2383+
} else {
2384+
added.UserPolicies = append(added.UserPolicies, map[string][]string{u: strings.Split(pm.Policies, ",")})
23542385
}
23552386
}
23562387
}
@@ -2380,17 +2411,28 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
23802411
// Validations for LDAP enabled deployments.
23812412
if globalIAMSys.LDAPConfig.Enabled() {
23822413
isGroup := true
2383-
err := globalIAMSys.NormalizeLDAPMappingImport(ctx, isGroup, grpPolicyMap)
2414+
skippedDN, err := globalIAMSys.NormalizeLDAPMappingImport(ctx, isGroup, grpPolicyMap)
2415+
skipped.Groups = append(skipped.Groups, skippedDN...)
23842416
if err != nil {
23852417
writeErrorResponseJSON(ctx, w, importError(ctx, err, groupPolicyMappingsFile, ""), r.URL)
23862418
return
23872419
}
23882420
}
23892421

23902422
for g, pm := range grpPolicyMap {
2423+
if slices.Contains(skipped.Groups, g) {
2424+
continue
2425+
}
23912426
if _, err := globalIAMSys.PolicyDBSet(ctx, g, pm.Policies, unknownIAMUserType, true); err != nil {
2392-
writeErrorResponseJSON(ctx, w, importError(ctx, err, groupPolicyMappingsFile, g), r.URL)
2393-
return
2427+
failed.GroupPolicies = append(
2428+
failed.GroupPolicies,
2429+
madmin.IAMErrPolicyEntity{
2430+
Name: g,
2431+
Policies: strings.Split(pm.Policies, ","),
2432+
Error: err,
2433+
})
2434+
} else {
2435+
added.GroupPolicies = append(added.GroupPolicies, map[string][]string{g: strings.Split(pm.Policies, ",")})
23942436
}
23952437
}
23962438
}
@@ -2420,13 +2462,17 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
24202462
// Validations for LDAP enabled deployments.
24212463
if globalIAMSys.LDAPConfig.Enabled() {
24222464
isGroup := true
2423-
err := globalIAMSys.NormalizeLDAPMappingImport(ctx, !isGroup, userPolicyMap)
2465+
skippedDN, err := globalIAMSys.NormalizeLDAPMappingImport(ctx, !isGroup, userPolicyMap)
2466+
skipped.Users = append(skipped.Users, skippedDN...)
24242467
if err != nil {
24252468
writeErrorResponseJSON(ctx, w, importError(ctx, err, stsUserPolicyMappingsFile, ""), r.URL)
24262469
return
24272470
}
24282471
}
24292472
for u, pm := range userPolicyMap {
2473+
if slices.Contains(skipped.Users, u) {
2474+
continue
2475+
}
24302476
// disallow setting policy mapping if user is a temporary user
24312477
ok, _, err := globalIAMSys.IsTempUser(u)
24322478
if err != nil && err != errNoSuchUser {
@@ -2439,12 +2485,36 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
24392485
}
24402486

24412487
if _, err := globalIAMSys.PolicyDBSet(ctx, u, pm.Policies, stsUser, false); err != nil {
2442-
writeErrorResponseJSON(ctx, w, importError(ctx, err, stsUserPolicyMappingsFile, u), r.URL)
2443-
return
2488+
failed.STSPolicies = append(
2489+
failed.STSPolicies,
2490+
madmin.IAMErrPolicyEntity{
2491+
Name: u,
2492+
Policies: strings.Split(pm.Policies, ","),
2493+
Error: err,
2494+
})
2495+
} else {
2496+
added.STSPolicies = append(added.STSPolicies, map[string][]string{u: strings.Split(pm.Policies, ",")})
24442497
}
24452498
}
24462499
}
24472500
}
2501+
2502+
if apiVer == "v2" {
2503+
iamr := madmin.ImportIAMResult{
2504+
Skipped: skipped,
2505+
Removed: removed,
2506+
Added: added,
2507+
Failed: failed,
2508+
}
2509+
2510+
b, err := json.Marshal(iamr)
2511+
if err != nil {
2512+
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
2513+
return
2514+
}
2515+
2516+
writeSuccessResponseJSON(w, b)
2517+
}
24482518
}
24492519

24502520
func addExpirationToCondValues(exp *time.Time, condValues map[string][]string) error {

cmd/admin-router.go

+1
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) {
290290

291291
// Import IAM info
292292
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/import-iam").HandlerFunc(adminMiddleware(adminAPI.ImportIAM, noGZFlag))
293+
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/import-iam-v2").HandlerFunc(adminMiddleware(adminAPI.ImportIAMV2, noGZFlag))
293294

294295
// IDentity Provider configuration APIs
295296
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/idp-config/{type}/{name}").HandlerFunc(adminMiddleware(adminAPI.AddIdentityProviderCfg))

cmd/iam.go

+15-17
Original file line numberDiff line numberDiff line change
@@ -1568,16 +1568,16 @@ func (sys *IAMSys) updateGroupMembershipsForLDAP(ctx context.Context) {
15681568
// accounts) for LDAP users. This normalizes the parent user and the group names
15691569
// whenever the parent user parses validly as a DN.
15701570
func (sys *IAMSys) NormalizeLDAPAccessKeypairs(ctx context.Context, accessKeyMap map[string]madmin.SRSvcAccCreate,
1571-
) (err error) {
1571+
) (skippedAccessKeys []string, err error) {
15721572
conn, err := sys.LDAPConfig.LDAP.Connect()
15731573
if err != nil {
1574-
return err
1574+
return skippedAccessKeys, err
15751575
}
15761576
defer conn.Close()
15771577

15781578
// Bind to the lookup user account
15791579
if err = sys.LDAPConfig.LDAP.LookupBind(conn); err != nil {
1580-
return err
1580+
return skippedAccessKeys, err
15811581
}
15821582

15831583
var collectedErrors []error
@@ -1602,8 +1602,7 @@ func (sys *IAMSys) NormalizeLDAPAccessKeypairs(ctx context.Context, accessKeyMap
16021602
continue
16031603
}
16041604
if validatedParent == nil || !isUnderBaseDN {
1605-
err := fmt.Errorf("DN parent was not found in the LDAP directory")
1606-
collectedErrors = append(collectedErrors, err)
1605+
skippedAccessKeys = append(skippedAccessKeys, ak)
16071606
continue
16081607
}
16091608

@@ -1621,8 +1620,7 @@ func (sys *IAMSys) NormalizeLDAPAccessKeypairs(ctx context.Context, accessKeyMap
16211620
continue
16221621
}
16231622
if validatedGroup == nil {
1624-
err := fmt.Errorf("DN group was not found in the LDAP directory")
1625-
collectedErrors = append(collectedErrors, err)
1623+
// DN group was not found in the LDAP directory for access-key
16261624
continue
16271625
}
16281626

@@ -1643,15 +1641,15 @@ func (sys *IAMSys) NormalizeLDAPAccessKeypairs(ctx context.Context, accessKeyMap
16431641

16441642
// if there are any errors, return a collected error.
16451643
if len(collectedErrors) > 0 {
1646-
return fmt.Errorf("errors validating LDAP DN: %w", errors.Join(collectedErrors...))
1644+
return skippedAccessKeys, fmt.Errorf("errors validating LDAP DN: %w", errors.Join(collectedErrors...))
16471645
}
16481646

16491647
for k, v := range updatedKeysMap {
16501648
// Replace the map values with the updated ones
16511649
accessKeyMap[k] = v
16521650
}
16531651

1654-
return nil
1652+
return skippedAccessKeys, nil
16551653
}
16561654

16571655
func (sys *IAMSys) getStoredLDAPPolicyMappingKeys(ctx context.Context, isGroup bool) set.StringSet {
@@ -1677,16 +1675,16 @@ func (sys *IAMSys) getStoredLDAPPolicyMappingKeys(ctx context.Context, isGroup b
16771675
// normalized form.
16781676
func (sys *IAMSys) NormalizeLDAPMappingImport(ctx context.Context, isGroup bool,
16791677
policyMap map[string]MappedPolicy,
1680-
) error {
1678+
) ([]string, error) {
16811679
conn, err := sys.LDAPConfig.LDAP.Connect()
16821680
if err != nil {
1683-
return err
1681+
return []string{}, err
16841682
}
16851683
defer conn.Close()
16861684

16871685
// Bind to the lookup user account
16881686
if err = sys.LDAPConfig.LDAP.LookupBind(conn); err != nil {
1689-
return err
1687+
return []string{}, err
16901688
}
16911689

16921690
// We map keys that correspond to LDAP DNs and validate that they exist in
@@ -1699,6 +1697,7 @@ func (sys *IAMSys) NormalizeLDAPMappingImport(ctx context.Context, isGroup bool,
16991697
// map of normalized DN keys to original keys.
17001698
normalizedDNKeysMap := make(map[string][]string)
17011699
var collectedErrors []error
1700+
var skipped []string
17021701
for k := range policyMap {
17031702
_, err := ldap.NormalizeDN(k)
17041703
if err != nil {
@@ -1711,8 +1710,7 @@ func (sys *IAMSys) NormalizeLDAPMappingImport(ctx context.Context, isGroup bool,
17111710
continue
17121711
}
17131712
if validatedDN == nil || !underBaseDN {
1714-
err := fmt.Errorf("DN was not found in the LDAP directory")
1715-
collectedErrors = append(collectedErrors, err)
1713+
skipped = append(skipped, k)
17161714
continue
17171715
}
17181716

@@ -1723,7 +1721,7 @@ func (sys *IAMSys) NormalizeLDAPMappingImport(ctx context.Context, isGroup bool,
17231721

17241722
// if there are any errors, return a collected error.
17251723
if len(collectedErrors) > 0 {
1726-
return fmt.Errorf("errors validating LDAP DN: %w", errors.Join(collectedErrors...))
1724+
return []string{}, fmt.Errorf("errors validating LDAP DN: %w", errors.Join(collectedErrors...))
17271725
}
17281726

17291727
entityKeysInStorage := sys.getStoredLDAPPolicyMappingKeys(ctx, isGroup)
@@ -1744,7 +1742,7 @@ func (sys *IAMSys) NormalizeLDAPMappingImport(ctx context.Context, isGroup bool,
17441742
}
17451743

17461744
if policiesDiffer {
1747-
return fmt.Errorf("multiple DNs map to the same LDAP DN[%s]: %v; please remove DNs that are not needed",
1745+
return []string{}, fmt.Errorf("multiple DNs map to the same LDAP DN[%s]: %v; please remove DNs that are not needed",
17481746
normKey, origKeys)
17491747
}
17501748

@@ -1788,7 +1786,7 @@ func (sys *IAMSys) NormalizeLDAPMappingImport(ctx context.Context, isGroup bool,
17881786
}
17891787
}
17901788
}
1791-
return nil
1789+
return skipped, nil
17921790
}
17931791

17941792
// CheckKey validates the incoming accessKey

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ require (
5151
github.com/minio/highwayhash v1.0.3
5252
github.com/minio/kms-go/kes v0.3.0
5353
github.com/minio/kms-go/kms v0.4.0
54-
github.com/minio/madmin-go/v3 v3.0.64
54+
github.com/minio/madmin-go/v3 v3.0.66
5555
github.com/minio/minio-go/v7 v7.0.76
5656
github.com/minio/mux v1.9.0
5757
github.com/minio/pkg/v3 v3.0.13

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,8 @@ github.com/minio/kms-go/kes v0.3.0 h1:SU8VGVM/Hk9w1OiSby3OatkcojooUqIdDHl6dtM6Nk
426426
github.com/minio/kms-go/kes v0.3.0/go.mod h1:w6DeVT878qEOU3nUrYVy1WOT5H1Ig9hbDIh698NYJKY=
427427
github.com/minio/kms-go/kms v0.4.0 h1:cLPZceEp+05xHotVBaeFJrgL7JcXM4lBy6PU0idkE7I=
428428
github.com/minio/kms-go/kms v0.4.0/go.mod h1:q12CehiIy2qgBnDKq6Q7wmPi2PHSyRVug5DKp0HAVeE=
429-
github.com/minio/madmin-go/v3 v3.0.64 h1:Btwgs3CrgSciVaCWv/3clOxuDdUzylo/oTQp0M8GkwE=
430-
github.com/minio/madmin-go/v3 v3.0.64/go.mod h1:IFAwr0XMrdsLovxAdCcuq/eoL4nRuMVQQv0iubJANQw=
429+
github.com/minio/madmin-go/v3 v3.0.66 h1:O4w7L3vTxhORqTeyegFdbuO4kKVbAUarJfcmsDXQMTs=
430+
github.com/minio/madmin-go/v3 v3.0.66/go.mod h1:IFAwr0XMrdsLovxAdCcuq/eoL4nRuMVQQv0iubJANQw=
431431
github.com/minio/mc v0.0.0-20240826104958-a55d9a8d17da h1:/JNoAwFPhCG0hUkc9k/wUlzMUO7tA9WLIoWgqQjYph4=
432432
github.com/minio/mc v0.0.0-20240826104958-a55d9a8d17da/go.mod h1:g/LyYpzVUVY+ZxfIDtzigg+L3NjAn88EBsLAkFvo+M0=
433433
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=

0 commit comments

Comments
 (0)