mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 02:05:41 +01:00
Parallel attach ldap group (#20705)
Parallel attach LDAP group Add configure LDAP group attach parallel UI Change the /c/login timeout from 60 (nginx default) to 900 seconds in nginx.conf Signed-off-by: stonezdj <stone.zhang@broadcom.com>
This commit is contained in:
parent
1f75b7aaef
commit
eb5193e0ef
@ -8989,6 +8989,9 @@ definitions:
|
|||||||
ldap_group_search_scope:
|
ldap_group_search_scope:
|
||||||
$ref: '#/definitions/IntegerConfigItem'
|
$ref: '#/definitions/IntegerConfigItem'
|
||||||
description: The scope to search ldap group. ''0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE''
|
description: The scope to search ldap group. ''0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE''
|
||||||
|
ldap_group_attach_parallel:
|
||||||
|
$ref: '#/definitions/BoolConfigItem'
|
||||||
|
description: Attach LDAP user group information in parallel.
|
||||||
ldap_scope:
|
ldap_scope:
|
||||||
$ref: '#/definitions/IntegerConfigItem'
|
$ref: '#/definitions/IntegerConfigItem'
|
||||||
description: The scope to search ldap users,'0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE'
|
description: The scope to search ldap users,'0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE'
|
||||||
@ -9179,6 +9182,11 @@ definitions:
|
|||||||
description: The scope to search ldap group. ''0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE''
|
description: The scope to search ldap group. ''0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE''
|
||||||
x-omitempty: true
|
x-omitempty: true
|
||||||
x-isnullable: true
|
x-isnullable: true
|
||||||
|
ldap_group_attach_parallel:
|
||||||
|
type: boolean
|
||||||
|
description: Attach LDAP user group information in parallel, the parallel worker count is 5
|
||||||
|
x-omitempty: true
|
||||||
|
x-isnullable: true
|
||||||
ldap_scope:
|
ldap_scope:
|
||||||
type: integer
|
type: integer
|
||||||
description: The scope to search ldap users,'0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE'
|
description: The scope to search ldap users,'0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE'
|
||||||
|
@ -101,6 +101,9 @@ http {
|
|||||||
|
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
proxy_request_buffering off;
|
proxy_request_buffering off;
|
||||||
|
|
||||||
|
proxy_send_timeout 900;
|
||||||
|
proxy_read_timeout 900;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /api/ {
|
location /api/ {
|
||||||
|
@ -134,6 +134,7 @@ const (
|
|||||||
OIDCGroupType = 3
|
OIDCGroupType = 3
|
||||||
LDAPGroupAdminDn = "ldap_group_admin_dn"
|
LDAPGroupAdminDn = "ldap_group_admin_dn"
|
||||||
LDAPGroupMembershipAttribute = "ldap_group_membership_attribute"
|
LDAPGroupMembershipAttribute = "ldap_group_membership_attribute"
|
||||||
|
LDAPGroupAttachParallel = "ldap_group_attach_parallel"
|
||||||
DefaultRegistryControllerEndpoint = "http://registryctl:8080"
|
DefaultRegistryControllerEndpoint = "http://registryctl:8080"
|
||||||
DefaultPortalURL = "http://portal:8080"
|
DefaultPortalURL = "http://portal:8080"
|
||||||
DefaultRegistryCtlURL = "http://registryctl:8080"
|
DefaultRegistryCtlURL = "http://registryctl:8080"
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
goldap "github.com/go-ldap/ldap/v3"
|
goldap "github.com/go-ldap/ldap/v3"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
@ -38,6 +39,10 @@ import (
|
|||||||
ugModel "github.com/goharbor/harbor/src/pkg/usergroup/model"
|
ugModel "github.com/goharbor/harbor/src/pkg/usergroup/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
workerCount = 5
|
||||||
|
)
|
||||||
|
|
||||||
// Auth implements AuthenticateHelper interface to authenticate against LDAP
|
// Auth implements AuthenticateHelper interface to authenticate against LDAP
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
auth.DefaultAuthenticateHelper
|
auth.DefaultAuthenticateHelper
|
||||||
@ -117,24 +122,94 @@ func (l *Auth) attachLDAPGroup(ctx context.Context, ldapUsers []model.User, u *m
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
userGroups := make([]ugModel.UserGroup, 0)
|
userGroups := make([]ugModel.UserGroup, 0)
|
||||||
|
if groupCfg.AttachParallel {
|
||||||
|
log.Debug("Attach LDAP group in parallel")
|
||||||
|
l.attachGroupParallel(ctx, ldapUsers, u)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Attach LDAP group sequencially
|
||||||
for _, dn := range ldapUsers[0].GroupDNList {
|
for _, dn := range ldapUsers[0].GroupDNList {
|
||||||
lGroups, err := sess.SearchGroupByDN(dn)
|
if lgroup, exist := verifyGroupInLDAP(dn, sess); exist {
|
||||||
if err != nil {
|
userGroups = append(userGroups, ugModel.UserGroup{GroupName: lgroup.Name, LdapGroupDN: dn, GroupType: common.LDAPGroupType})
|
||||||
log.Warningf("Can not get the ldap group name with DN %v, error %v", dn, err)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if len(lGroups) == 0 {
|
|
||||||
log.Warningf("Can not get the ldap group name with DN %v", dn)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
userGroups = append(userGroups, ugModel.UserGroup{GroupName: lGroups[0].Name, LdapGroupDN: dn, GroupType: common.LDAPGroupType})
|
|
||||||
}
|
}
|
||||||
u.GroupIDs, err = ugCtl.Ctl.Populate(ctx, userGroups)
|
u.GroupIDs, err = ugCtl.Ctl.Populate(ctx, userGroups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Failed to fetch ldap group configuration:%v", err)
|
log.Warningf("Failed to populate ldap group, error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Auth) attachGroupParallel(ctx context.Context, ldapUsers []model.User, u *models.User) {
|
||||||
|
userGroupsList := make([][]ugModel.UserGroup, workerCount)
|
||||||
|
gdsList := make([][]string, workerCount)
|
||||||
|
// Divide the groupDNs into workerCount parts
|
||||||
|
for index, dn := range ldapUsers[0].GroupDNList {
|
||||||
|
idx := index % workerCount
|
||||||
|
gdsList[idx] = append(gdsList[idx], dn)
|
||||||
|
}
|
||||||
|
g := new(errgroup.Group)
|
||||||
|
g.SetLimit(workerCount)
|
||||||
|
|
||||||
|
for i := 0; i < workerCount; i++ {
|
||||||
|
curIndex := i
|
||||||
|
g.Go(func() error {
|
||||||
|
userGroups := make([]ugModel.UserGroup, 0)
|
||||||
|
groups := gdsList[curIndex]
|
||||||
|
if len(groups) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// use different ldap session for each go routine
|
||||||
|
ldapSession, err := ldapCtl.Ctl.Session(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = ldapSession.Open(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer ldapSession.Close()
|
||||||
|
log.Debugf("Current worker index is %v", curIndex)
|
||||||
|
// verify and populate group
|
||||||
|
for _, dn := range groups {
|
||||||
|
if lgroup, exist := verifyGroupInLDAP(dn, ldapSession); exist {
|
||||||
|
userGroups = append(userGroups, ugModel.UserGroup{GroupName: lgroup.Name, LdapGroupDN: dn, GroupType: common.LDAPGroupType})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userGroupsList[curIndex] = userGroups
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := g.Wait(); err != nil {
|
||||||
|
log.Warningf("failed to verify and populate ldap group parallel, error %v", err)
|
||||||
|
}
|
||||||
|
ugs := make([]ugModel.UserGroup, 0)
|
||||||
|
for _, userGroups := range userGroupsList {
|
||||||
|
ugs = append(ugs, userGroups...)
|
||||||
|
}
|
||||||
|
|
||||||
|
groupIDsList, err := ugCtl.Ctl.Populate(ctx, ugs)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("Failed to populate user groups :%v", err)
|
||||||
|
}
|
||||||
|
u.GroupIDs = groupIDsList
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyGroupInLDAP(groupDN string, sess *ldap.Session) (*model.Group, bool) {
|
||||||
|
if _, err := goldap.ParseDN(groupDN); err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
lGroups, err := sess.SearchGroupByDN(groupDN)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("Can not get the ldap group name with DN %v, error %v", groupDN, err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
if len(lGroups) == 0 {
|
||||||
|
log.Warningf("Can not get the ldap group name with DN %v", groupDN)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return &lGroups[0], true
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Auth) syncUserInfoFromDB(ctx context.Context, u *models.User) {
|
func (l *Auth) syncUserInfoFromDB(ctx context.Context, u *models.User) {
|
||||||
// Retrieve SysAdminFlag from DB so that it transfer to session
|
// Retrieve SysAdminFlag from DB so that it transfer to session
|
||||||
dbUser, err := l.userMgr.GetByName(ctx, u.Username)
|
dbUser, err := l.userMgr.GetByName(ctx, u.Username)
|
||||||
|
@ -96,6 +96,7 @@ var (
|
|||||||
{Name: common.LDAPURL, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_URL", DefaultValue: "", ItemType: &NonEmptyStringType{}, Editable: false, Description: `The URL of LDAP server`},
|
{Name: common.LDAPURL, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_URL", DefaultValue: "", ItemType: &NonEmptyStringType{}, Editable: false, Description: `The URL of LDAP server`},
|
||||||
{Name: common.LDAPVerifyCert, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_VERIFY_CERT", DefaultValue: "true", ItemType: &BoolType{}, Editable: false, Description: `Whether verify your OIDC server certificate, disable it if your OIDC server is hosted via self-hosted certificate.`},
|
{Name: common.LDAPVerifyCert, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_VERIFY_CERT", DefaultValue: "true", ItemType: &BoolType{}, Editable: false, Description: `Whether verify your OIDC server certificate, disable it if your OIDC server is hosted via self-hosted certificate.`},
|
||||||
{Name: common.LDAPGroupMembershipAttribute, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_GROUP_MEMBERSHIP_ATTRIBUTE", DefaultValue: "memberof", ItemType: &StringType{}, Editable: true, Description: `The user attribute to identify the group membership`},
|
{Name: common.LDAPGroupMembershipAttribute, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_GROUP_MEMBERSHIP_ATTRIBUTE", DefaultValue: "memberof", ItemType: &StringType{}, Editable: true, Description: `The user attribute to identify the group membership`},
|
||||||
|
{Name: common.LDAPGroupAttachParallel, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_GROUP_ATTACH_PARALLEL", DefaultValue: "false", ItemType: &BoolType{}, Editable: true, Description: `Attach LDAP group information to Harbor in parallel`},
|
||||||
|
|
||||||
{Name: common.MaxJobWorkers, Scope: SystemScope, Group: BasicGroup, EnvKey: "MAX_JOB_WORKERS", DefaultValue: "10", ItemType: &IntType{}, Editable: false},
|
{Name: common.MaxJobWorkers, Scope: SystemScope, Group: BasicGroup, EnvKey: "MAX_JOB_WORKERS", DefaultValue: "10", ItemType: &IntType{}, Editable: false},
|
||||||
{Name: common.ScanAllPolicy, Scope: UserScope, Group: BasicGroup, EnvKey: "", DefaultValue: "", ItemType: &MapType{}, Editable: false, Description: `The policy to scan images`},
|
{Name: common.ScanAllPolicy, Scope: UserScope, Group: BasicGroup, EnvKey: "", DefaultValue: "", ItemType: &MapType{}, Editable: false, Description: `The policy to scan images`},
|
||||||
|
@ -94,6 +94,7 @@ type GroupConf struct {
|
|||||||
SearchScope int `json:"ldap_group_search_scope"`
|
SearchScope int `json:"ldap_group_search_scope"`
|
||||||
AdminDN string `json:"ldap_group_admin_dn,omitempty"`
|
AdminDN string `json:"ldap_group_admin_dn,omitempty"`
|
||||||
MembershipAttribute string `json:"ldap_group_membership_attribute,omitempty"`
|
MembershipAttribute string `json:"ldap_group_membership_attribute,omitempty"`
|
||||||
|
AttachParallel bool `json:"ldap_group_attach_parallel,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GDPRSetting struct {
|
type GDPRSetting struct {
|
||||||
|
@ -81,6 +81,7 @@ func LDAPGroupConf(ctx context.Context) (*cfgModels.GroupConf, error) {
|
|||||||
SearchScope: mgr.Get(ctx, common.LDAPGroupSearchScope).GetInt(),
|
SearchScope: mgr.Get(ctx, common.LDAPGroupSearchScope).GetInt(),
|
||||||
AdminDN: mgr.Get(ctx, common.LDAPGroupAdminDn).GetString(),
|
AdminDN: mgr.Get(ctx, common.LDAPGroupAdminDn).GetString(),
|
||||||
MembershipAttribute: mgr.Get(ctx, common.LDAPGroupMembershipAttribute).GetString(),
|
MembershipAttribute: mgr.Get(ctx, common.LDAPGroupMembershipAttribute).GetString(),
|
||||||
|
AttachParallel: mgr.Get(ctx, common.LDAPGroupAttachParallel).GetBool(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,6 +498,37 @@
|
|||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</clr-select-container>
|
</clr-select-container>
|
||||||
|
<clr-checkbox-container>
|
||||||
|
<label for="ldapGroupAttachParallel">
|
||||||
|
{{ 'CONFIG.LDAP.GROUP_ATTACH_PARALLEL' | translate }}
|
||||||
|
<clr-tooltip>
|
||||||
|
<clr-icon
|
||||||
|
clrTooltipTrigger
|
||||||
|
shape="info-circle"
|
||||||
|
size="24"></clr-icon>
|
||||||
|
<clr-tooltip-content
|
||||||
|
*clrIfOpen
|
||||||
|
clrPosition="top-right"
|
||||||
|
clrSize="lg">
|
||||||
|
<span>{{
|
||||||
|
'CONFIG.LDAP.GROUP_ATTACH_PARALLEL_INFO' | translate
|
||||||
|
}}</span>
|
||||||
|
</clr-tooltip-content>
|
||||||
|
</clr-tooltip>
|
||||||
|
</label>
|
||||||
|
<clr-checkbox-wrapper>
|
||||||
|
<input
|
||||||
|
(ngModelChange)="setLdapGroupAttachParallelValue($event)"
|
||||||
|
[disabled]="
|
||||||
|
disabled(currentConfig.ldap_group_attach_parallel)
|
||||||
|
"
|
||||||
|
[ngModel]="currentConfig.ldap_group_attach_parallel.value"
|
||||||
|
clrCheckbox
|
||||||
|
id="ldapGroupAttachParallel"
|
||||||
|
name="ldapGroupAttachParallel"
|
||||||
|
type="checkbox" />
|
||||||
|
</clr-checkbox-wrapper>
|
||||||
|
</clr-checkbox-container>
|
||||||
</section>
|
</section>
|
||||||
<clr-checkbox-container *ngIf="showSelfReg">
|
<clr-checkbox-container *ngIf="showSelfReg">
|
||||||
<label for="selfReg"
|
<label for="selfReg"
|
||||||
|
@ -11,15 +11,15 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, ViewChild, OnInit } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { NgForm } from '@angular/forms';
|
import { NgForm } from '@angular/forms';
|
||||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||||
import { AppConfigService } from '../../../../services/app-config.service';
|
import { AppConfigService } from '../../../../services/app-config.service';
|
||||||
import { ConfigurationService } from '../../../../services/config.service';
|
import { ConfigurationService } from '../../../../services/config.service';
|
||||||
import { SystemInfoService } from '../../../../shared/services';
|
import { SystemInfoService } from '../../../../shared/services';
|
||||||
import {
|
import {
|
||||||
isEmpty,
|
|
||||||
getChanges as getChangesFunc,
|
getChanges as getChangesFunc,
|
||||||
|
isEmpty,
|
||||||
} from '../../../../shared/units/utils';
|
} from '../../../../shared/units/utils';
|
||||||
import { CONFIG_AUTH_MODE } from '../../../../shared/entities/shared.const';
|
import { CONFIG_AUTH_MODE } from '../../../../shared/entities/shared.const';
|
||||||
import { errorHandler } from '../../../../shared/units/shared.utils';
|
import { errorHandler } from '../../../../shared/units/shared.utils';
|
||||||
@ -132,6 +132,9 @@ export class ConfigurationAuthComponent implements OnInit {
|
|||||||
this.currentConfig.ldap_verify_cert.value = $event;
|
this.currentConfig.ldap_verify_cert.value = $event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLdapGroupAttachParallelValue($event: any) {
|
||||||
|
this.currentConfig.ldap_group_attach_parallel.value = $event;
|
||||||
|
}
|
||||||
public pingTestServer(): void {
|
public pingTestServer(): void {
|
||||||
if (this.testingOnGoing) {
|
if (this.testingOnGoing) {
|
||||||
return; // Should not come here
|
return; // Should not come here
|
||||||
|
@ -72,6 +72,7 @@ export class Configuration {
|
|||||||
ldap_group_search_scope: NumberValueItem;
|
ldap_group_search_scope: NumberValueItem;
|
||||||
ldap_group_membership_attribute: StringValueItem;
|
ldap_group_membership_attribute: StringValueItem;
|
||||||
ldap_group_admin_dn: StringValueItem;
|
ldap_group_admin_dn: StringValueItem;
|
||||||
|
ldap_group_attach_parallel: BoolValueItem;
|
||||||
uaa_client_id: StringValueItem;
|
uaa_client_id: StringValueItem;
|
||||||
uaa_client_secret?: StringValueItem;
|
uaa_client_secret?: StringValueItem;
|
||||||
uaa_endpoint: StringValueItem;
|
uaa_endpoint: StringValueItem;
|
||||||
|
@ -928,7 +928,9 @@
|
|||||||
"LDAP_GROUP_MEMBERSHIP": "LDAP Group Membership",
|
"LDAP_GROUP_MEMBERSHIP": "LDAP Group Membership",
|
||||||
"LDAP_GROUP_MEMBERSHIP_INFO": "The attribute indicates the membership of LDAP group, default value is memberof, in some LDAP server it could be \"ismemberof\". This field cannot be empty if you need to enable the LDAP group related feature.",
|
"LDAP_GROUP_MEMBERSHIP_INFO": "The attribute indicates the membership of LDAP group, default value is memberof, in some LDAP server it could be \"ismemberof\". This field cannot be empty if you need to enable the LDAP group related feature.",
|
||||||
"GROUP_SCOPE": "LDAP Group Search Scope",
|
"GROUP_SCOPE": "LDAP Group Search Scope",
|
||||||
"GROUP_SCOPE_INFO": "The scope to search for groups, select Subtree by default."
|
"GROUP_SCOPE_INFO": "The scope to search for groups, select Subtree by default.",
|
||||||
|
"GROUP_ATTACH_PARALLEL": "LDAP Group Attached In Parallel",
|
||||||
|
"GROUP_ATTACH_PARALLEL_INFO": "Enable this option to attach group in parallel to avoid timeout when there are too many groups. If disabled, the LDAP group information will be attached sequentially."
|
||||||
|
|
||||||
},
|
},
|
||||||
"UAA": {
|
"UAA": {
|
||||||
|
@ -926,7 +926,10 @@
|
|||||||
"LDAP_GROUP_MEMBERSHIP": "LDAP 组成员",
|
"LDAP_GROUP_MEMBERSHIP": "LDAP 组成员",
|
||||||
"LDAP_GROUP_MEMBERSHIP_INFO": "LDAP组成员的membership属性,默认为 memberof, 在某些LDAP服务器会变为 ismemberof。如果要开启LDAP组功能,则此项必填",
|
"LDAP_GROUP_MEMBERSHIP_INFO": "LDAP组成员的membership属性,默认为 memberof, 在某些LDAP服务器会变为 ismemberof。如果要开启LDAP组功能,则此项必填",
|
||||||
"GROUP_SCOPE": "LDAP组搜索范围",
|
"GROUP_SCOPE": "LDAP组搜索范围",
|
||||||
"GROUP_SCOPE_INFO": "搜索组的范围,默认值为\"子树\""
|
"GROUP_SCOPE_INFO": "搜索组的范围,默认值为\"子树\"",
|
||||||
|
"GROUP_ATTACH_PARALLEL": "LDAP组并行同步",
|
||||||
|
"GROUP_ATTACH_PARALLEL_INFO": "打开这个选项时,LDAP组的信息是并行同步到Harbor, 这样可以防止用户组太多时造成的登录超时,如果关闭这个选项,LDAP组信息是顺序同步到Harbor"
|
||||||
|
|
||||||
},
|
},
|
||||||
"UAA": {
|
"UAA": {
|
||||||
"ENDPOINT": "UAA Endpoint",
|
"ENDPOINT": "UAA Endpoint",
|
||||||
|
Loading…
Reference in New Issue
Block a user