mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-29 21:54:13 +01:00
Merge pull request #3911 from stonezdj/ldap_search_level
Ambiguous UI and internal values ldap_scope
This commit is contained in:
commit
f8af1f275e
@ -257,7 +257,9 @@ func DeleteUser(userID int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ChangeUserProfile ...
|
||||
// ChangeUserProfile - Update user in local db,
|
||||
// cols to specify the columns need to update,
|
||||
// Email, and RealName, Comment are updated by default.
|
||||
func ChangeUserProfile(user models.User, cols ...string) error {
|
||||
o := GetOrmer()
|
||||
if len(cols) == 0 {
|
||||
|
@ -23,19 +23,6 @@ type Authentication struct {
|
||||
}
|
||||
*/
|
||||
|
||||
// LDAP ...
|
||||
type LDAP struct {
|
||||
URL string `json:"url"`
|
||||
SearchDN string `json:"search_dn"`
|
||||
SearchPassword string `json:"search_password"`
|
||||
BaseDN string `json:"base_dn"`
|
||||
Filter string `json:"filter"`
|
||||
UID string `json:"uid"`
|
||||
Scope int `json:"scope"`
|
||||
Timeout int `json:"timeout"` // in second
|
||||
VerifyCert bool `json:"verify_cert"`
|
||||
}
|
||||
|
||||
// Database ...
|
||||
type Database struct {
|
||||
Type string `json:"type"`
|
||||
@ -114,6 +101,7 @@ type ConfigEntry struct {
|
||||
Key string `orm:"column(k)" json:"k"`
|
||||
Value string `orm:"column(v)" json:"v"`
|
||||
}
|
||||
|
||||
// TableName ...
|
||||
func (ce *ConfigEntry) TableName() string {
|
||||
return "properties"
|
||||
|
@ -37,7 +37,6 @@ type Session struct {
|
||||
|
||||
//LoadSystemLdapConfig - load LDAP configure from adminserver
|
||||
func LoadSystemLdapConfig() (*Session, error) {
|
||||
var session Session
|
||||
|
||||
authMode, err := config.AuthMode()
|
||||
if err != nil {
|
||||
@ -49,87 +48,30 @@ func LoadSystemLdapConfig() (*Session, error) {
|
||||
return nil, fmt.Errorf("system auth_mode isn't ldap_auth, please check configuration")
|
||||
}
|
||||
|
||||
ldap, err := config.LDAP()
|
||||
ldapConf, err := config.LDAPConf()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ldap.URL == "" {
|
||||
return nil, fmt.Errorf("can not get any available LDAP_URL")
|
||||
return CreateWithConfig(*ldapConf)
|
||||
}
|
||||
|
||||
ldapURL, err := formatURL(ldap.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session.ldapConfig.LdapURL = ldapURL
|
||||
session.ldapConfig.LdapSearchDn = ldap.SearchDN
|
||||
session.ldapConfig.LdapSearchPassword = ldap.SearchPassword
|
||||
session.ldapConfig.LdapBaseDn = ldap.BaseDN
|
||||
session.ldapConfig.LdapFilter = ldap.Filter
|
||||
session.ldapConfig.LdapUID = ldap.UID
|
||||
session.ldapConfig.LdapConnectionTimeout = ldap.Timeout
|
||||
session.ldapConfig.LdapVerifyCert = ldap.VerifyCert
|
||||
log.Debugf("Load system configuration: %v", ldap)
|
||||
|
||||
switch ldap.Scope {
|
||||
case 1:
|
||||
session.ldapConfig.LdapScope = goldap.ScopeBaseObject
|
||||
case 2:
|
||||
session.ldapConfig.LdapScope = goldap.ScopeSingleLevel
|
||||
case 3:
|
||||
session.ldapConfig.LdapScope = goldap.ScopeWholeSubtree
|
||||
default:
|
||||
log.Errorf("invalid ldap search scope %v", ldap.Scope)
|
||||
return nil, fmt.Errorf("invalid ldap search scope")
|
||||
}
|
||||
|
||||
return &session, nil
|
||||
}
|
||||
|
||||
// CreateWithUIConfig - create a Session with config from UI
|
||||
func CreateWithUIConfig(ldapConfs models.LdapConf) (*Session, error) {
|
||||
|
||||
switch ldapConfs.LdapScope {
|
||||
case 1:
|
||||
ldapConfs.LdapScope = goldap.ScopeBaseObject
|
||||
case 2:
|
||||
ldapConfs.LdapScope = goldap.ScopeSingleLevel
|
||||
case 3:
|
||||
ldapConfs.LdapScope = goldap.ScopeWholeSubtree
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid ldap search scope")
|
||||
}
|
||||
|
||||
return createWithInternalConfig(ldapConfs)
|
||||
}
|
||||
|
||||
// createWithInternalConfig - create a Session with internal config
|
||||
func createWithInternalConfig(ldapConfs models.LdapConf) (*Session, error) {
|
||||
|
||||
// CreateWithConfig - create a Session with internal config
|
||||
func CreateWithConfig(ldapConf models.LdapConf) (*Session, error) {
|
||||
var session Session
|
||||
|
||||
if ldapConfs.LdapURL == "" {
|
||||
if ldapConf.LdapURL == "" {
|
||||
return nil, fmt.Errorf("can not get any available LDAP_URL")
|
||||
}
|
||||
|
||||
ldapURL, err := formatURL(ldapConfs.LdapURL)
|
||||
ldapURL, err := formatURL(ldapConf.LdapURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session.ldapConfig.LdapURL = ldapURL
|
||||
session.ldapConfig.LdapSearchDn = ldapConfs.LdapSearchDn
|
||||
session.ldapConfig.LdapSearchPassword = ldapConfs.LdapSearchPassword
|
||||
session.ldapConfig.LdapBaseDn = ldapConfs.LdapBaseDn
|
||||
session.ldapConfig.LdapFilter = ldapConfs.LdapFilter
|
||||
session.ldapConfig.LdapUID = ldapConfs.LdapUID
|
||||
session.ldapConfig.LdapConnectionTimeout = ldapConfs.LdapConnectionTimeout
|
||||
session.ldapConfig.LdapVerifyCert = ldapConfs.LdapVerifyCert
|
||||
session.ldapConfig.LdapScope = ldapConfs.LdapScope
|
||||
ldapConf.LdapURL = ldapURL
|
||||
session.ldapConfig = ldapConf
|
||||
return &session, nil
|
||||
|
||||
}
|
||||
|
||||
func formatURL(ldapURL string) (string, error) {
|
||||
@ -208,7 +150,7 @@ func ConnectionTestWithConfig(ldapConfig models.LdapConf) error {
|
||||
ldapConfig.LdapSearchPassword = session.ldapConfig.LdapSearchPassword
|
||||
}
|
||||
|
||||
testSession, err := createWithInternalConfig(ldapConfig)
|
||||
testSession, err := CreateWithConfig(ldapConfig)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -138,10 +138,6 @@ func TestLoadSystemLdapConfig(t *testing.T) {
|
||||
t.Errorf("unexpected LdapURL: %s != %s", session.ldapConfig.LdapURL, "ldap://127.0.0.1:389")
|
||||
}
|
||||
|
||||
if session.ldapConfig.LdapScope != 2 {
|
||||
t.Errorf("unexpected LdapScope: %d != %d", session.ldapConfig.LdapScope, 2)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestConnectTest(t *testing.T) {
|
||||
@ -156,7 +152,7 @@ func TestConnectTest(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestCreateUIConfig(t *testing.T) {
|
||||
func TestCreateWithConfig(t *testing.T) {
|
||||
var testConfigs = []struct {
|
||||
config models.LdapConf
|
||||
internalValue int
|
||||
@ -184,7 +180,7 @@ func TestCreateUIConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, val := range testConfigs {
|
||||
session, err := CreateWithUIConfig(val.config)
|
||||
_, err := CreateWithConfig(val.config)
|
||||
if val.internalValue < 0 {
|
||||
if err == nil {
|
||||
t.Fatalf("Should have error with url :%v", val.config)
|
||||
@ -194,9 +190,6 @@ func TestCreateUIConfig(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Can not create with ui config, err:%v", err)
|
||||
}
|
||||
if session.ldapConfig.LdapScope != val.internalValue {
|
||||
t.Fatalf("Test failed expected %v, actual %v", val.internalValue, session.ldapConfig.LdapScope)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -245,28 +245,28 @@ func validateCfg(c map[string]interface{}) (bool, error) {
|
||||
}
|
||||
|
||||
if mode == common.LDAPAuth {
|
||||
ldap, err := config.LDAP()
|
||||
ldapConf, err := config.LDAPConf()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
if len(ldap.URL) == 0 {
|
||||
if len(ldapConf.LdapURL) == 0 {
|
||||
if _, ok := strMap[common.LDAPURL]; !ok {
|
||||
return false, fmt.Errorf("%s is missing", common.LDAPURL)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ldap.BaseDN) == 0 {
|
||||
if len(ldapConf.LdapBaseDn) == 0 {
|
||||
if _, ok := strMap[common.LDAPBaseDN]; !ok {
|
||||
return false, fmt.Errorf("%s is missing", common.LDAPBaseDN)
|
||||
}
|
||||
}
|
||||
if len(ldap.UID) == 0 {
|
||||
if len(ldapConf.LdapUID) == 0 {
|
||||
if _, ok := strMap[common.LDAPUID]; !ok {
|
||||
return false, fmt.Errorf("%s is missing", common.LDAPUID)
|
||||
}
|
||||
}
|
||||
if ldap.Scope == 0 {
|
||||
if ldapConf.LdapScope == 0 {
|
||||
if _, ok := numMap[common.LDAPScope]; !ok {
|
||||
return false, fmt.Errorf("%s is missing", common.LDAPScope)
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func (l *LdapAPI) Search() {
|
||||
}
|
||||
} else {
|
||||
l.DecodeJSONReqAndValidate(&ldapConfs)
|
||||
ldapSession, err = ldapUtils.CreateWithUIConfig(ldapConfs)
|
||||
ldapSession, err = ldapUtils.CreateWithConfig(ldapConfs)
|
||||
}
|
||||
|
||||
if err = ldapSession.Open(); err != nil {
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
)
|
||||
|
||||
var l = NewUserLock(2 * time.Second)
|
||||
@ -61,3 +62,21 @@ func TestLock(t *testing.T) {
|
||||
t.Errorf("daniel has never been locked, he should not be locked")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultAuthenticate(t *testing.T) {
|
||||
authHelper := DefaultAuthenticateHelper{}
|
||||
m := models.AuthModel{}
|
||||
user, err := authHelper.Authenticate(m)
|
||||
if user != nil || err != nil {
|
||||
t.Fatal("Default implementation should return nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultOnBoardUser(t *testing.T) {
|
||||
user := &models.User{}
|
||||
authHelper := DefaultAuthenticateHelper{}
|
||||
err := authHelper.OnBoardUser(user)
|
||||
if err != nil {
|
||||
t.Fatal("Default implementation should return nil")
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +128,7 @@ func getHelper() (AuthenticateHelper, error) {
|
||||
// OnBoardUser will check if a user exists in user table, if not insert the user and
|
||||
// put the id in the pointer of user model, if it does exist, return the user's profile.
|
||||
func OnBoardUser(user *models.User) error {
|
||||
log.Debugf("OnBoardUser, user %+v", user)
|
||||
helper, err := getHelper()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -76,11 +76,11 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
|
||||
|
||||
dn := ldapUsers[0].DN
|
||||
|
||||
log.Debugf("username: %s, dn: %s", u.Username, dn)
|
||||
if err = ldapSession.Bind(dn, m.Password); err != nil {
|
||||
log.Warningf("Failed to bind user, username: %s, dn: %s, error: %v", u.Username, dn, err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
@ -120,6 +120,7 @@ func (l *Auth) SearchUser(username string) (*models.User, error) {
|
||||
|
||||
user.Username = strings.TrimSpace(ldapUsers[0].Username)
|
||||
user.Realname = strings.TrimSpace(ldapUsers[0].Realname)
|
||||
user.Email = strings.TrimSpace(ldapUsers[0].Email)
|
||||
|
||||
log.Debugf("Found ldap user %v", user)
|
||||
} else {
|
||||
|
@ -14,12 +14,12 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
//"fmt"
|
||||
//"strings"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
@ -136,6 +136,21 @@ func TestAuthenticate(t *testing.T) {
|
||||
if user != nil {
|
||||
t.Errorf("Nil user for empty credentials")
|
||||
}
|
||||
|
||||
//authenticate the second time
|
||||
person2 := models.AuthModel{
|
||||
Principal: "test",
|
||||
Password: "123456",
|
||||
}
|
||||
user2, err := auth.Authenticate(person2)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("unexpected ldap error: %v", err)
|
||||
}
|
||||
|
||||
if user2 == nil {
|
||||
t.Errorf("Can not login user with person2 %+v", person2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchUser(t *testing.T) {
|
||||
@ -174,6 +189,43 @@ func TestOnboardUser(t *testing.T) {
|
||||
if user.UserID <= 0 {
|
||||
t.Errorf("Failed to onboard user")
|
||||
}
|
||||
assert.Equal(t, "sample@example.com", user.Email)
|
||||
}
|
||||
|
||||
func TestOnboardUser_02(t *testing.T) {
|
||||
user := &models.User{
|
||||
Username: "sample02",
|
||||
Realname: "Sample02",
|
||||
}
|
||||
var auth *Auth
|
||||
err := auth.OnBoardUser(user)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to onboard user")
|
||||
}
|
||||
if user.UserID <= 0 {
|
||||
t.Errorf("Failed to onboard user")
|
||||
}
|
||||
|
||||
assert.Equal(t, "sample02@placeholder.com", user.Email)
|
||||
dao.CleanUser(int64(user.UserID))
|
||||
}
|
||||
|
||||
func TestOnboardUser_03(t *testing.T) {
|
||||
user := &models.User{
|
||||
Username: "sample03@example.com",
|
||||
Realname: "Sample03",
|
||||
}
|
||||
var auth *Auth
|
||||
err := auth.OnBoardUser(user)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to onboard user")
|
||||
}
|
||||
if user.UserID <= 0 {
|
||||
t.Errorf("Failed to onboard user")
|
||||
}
|
||||
|
||||
assert.Equal(t, "sample03@example.com", user.Email)
|
||||
dao.CleanUser(int64(user.UserID))
|
||||
}
|
||||
|
||||
func TestAuthenticateHelperOnboardUser(t *testing.T) {
|
||||
|
@ -181,28 +181,28 @@ func AuthMode() (string, error) {
|
||||
return cfg[common.AUTHMode].(string), nil
|
||||
}
|
||||
|
||||
// LDAP returns the setting of ldap server
|
||||
func LDAP() (*models.LDAP, error) {
|
||||
// LDAPConf returns the setting of ldap server
|
||||
func LDAPConf() (*models.LdapConf, error) {
|
||||
cfg, err := mg.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ldap := &models.LDAP{}
|
||||
ldap.URL = cfg[common.LDAPURL].(string)
|
||||
ldap.SearchDN = cfg[common.LDAPSearchDN].(string)
|
||||
ldap.SearchPassword = cfg[common.LDAPSearchPwd].(string)
|
||||
ldap.BaseDN = cfg[common.LDAPBaseDN].(string)
|
||||
ldap.UID = cfg[common.LDAPUID].(string)
|
||||
ldap.Filter = cfg[common.LDAPFilter].(string)
|
||||
ldap.Scope = int(cfg[common.LDAPScope].(float64))
|
||||
ldap.Timeout = int(cfg[common.LDAPTimeout].(float64))
|
||||
ldapConf := &models.LdapConf{}
|
||||
ldapConf.LdapURL = cfg[common.LDAPURL].(string)
|
||||
ldapConf.LdapSearchDn = cfg[common.LDAPSearchDN].(string)
|
||||
ldapConf.LdapSearchPassword = cfg[common.LDAPSearchPwd].(string)
|
||||
ldapConf.LdapBaseDn = cfg[common.LDAPBaseDN].(string)
|
||||
ldapConf.LdapUID = cfg[common.LDAPUID].(string)
|
||||
ldapConf.LdapFilter = cfg[common.LDAPFilter].(string)
|
||||
ldapConf.LdapScope = int(cfg[common.LDAPScope].(float64))
|
||||
ldapConf.LdapConnectionTimeout = int(cfg[common.LDAPTimeout].(float64))
|
||||
if cfg[common.LDAPVerifyCert] != nil {
|
||||
ldap.VerifyCert = cfg[common.LDAPVerifyCert].(bool)
|
||||
ldapConf.LdapVerifyCert = cfg[common.LDAPVerifyCert].(bool)
|
||||
} else {
|
||||
ldap.VerifyCert = true
|
||||
ldapConf.LdapVerifyCert = true
|
||||
}
|
||||
|
||||
return ldap, nil
|
||||
return ldapConf, nil
|
||||
}
|
||||
|
||||
// TokenExpiration returns the token expiration time (in minute)
|
||||
@ -407,6 +407,7 @@ func ClairDB() (*models.PostGreSQL, error){
|
||||
clairDB.Database = cfg[common.ClairDB].(string)
|
||||
return clairDB, nil
|
||||
}
|
||||
|
||||
// AdmiralEndpoint returns the URL of admiral, if Harbor is not deployed with admiral it should return an empty string.
|
||||
func AdmiralEndpoint() string {
|
||||
cfg, err := mg.Get()
|
||||
|
@ -18,8 +18,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/src/common/utils/test"
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/utils/test"
|
||||
)
|
||||
|
||||
// test functions under package ui/config
|
||||
@ -71,7 +71,7 @@ func TestConfig(t *testing.T) {
|
||||
t.Errorf("unexpected mode: %s != %s", mode, "db_auth")
|
||||
}
|
||||
|
||||
if _, err := LDAP(); err != nil {
|
||||
if _, err := LDAPConf(); err != nil {
|
||||
t.Fatalf("failed to get ldap settings: %v", err)
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ func TestConfig(t *testing.T) {
|
||||
t.Fatalf("failed to get database: %v", err)
|
||||
}
|
||||
|
||||
clairDB, err := ClairDB();
|
||||
clairDB, err := ClairDB()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get clair DB %v", err)
|
||||
}
|
||||
|
@ -126,9 +126,9 @@
|
||||
<label for="ldapScope">{{'CONFIG.LDAP.SCOPE' | translate}}</label>
|
||||
<div class="select">
|
||||
<select id="ldapScope" name="ldapScope" [(ngModel)]="currentConfig.ldap_scope.value" [disabled]="disabled(currentConfig.ldap_scope)">
|
||||
<option value="1">{{'CONFIG.SCOPE_BASE' | translate }}</option>
|
||||
<option value="2">{{'CONFIG.SCOPE_ONE_LEVEL' | translate }}</option>
|
||||
<option value="3">{{'CONFIG.SCOPE_SUBTREE' | translate }}</option>
|
||||
<option value="0">{{'CONFIG.SCOPE_BASE' | translate }}</option>
|
||||
<option value="1">{{'CONFIG.SCOPE_ONE_LEVEL' | translate }}</option>
|
||||
<option value="2">{{'CONFIG.SCOPE_SUBTREE' | translate }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right" style="top:-1px;">
|
||||
|
@ -37,3 +37,9 @@ Switch to User Tag
|
||||
|
||||
Administration Tag Should Display
|
||||
Page Should Contain Element xpath=${administration_tag_xpath}
|
||||
|
||||
User Email Should Exist
|
||||
[Arguments] ${email}
|
||||
Sign In Harbor ${HARBOR_URL} %{HARBOR_ADMIN} %{HARBOR_PASSWORD}
|
||||
Switch to User Tag
|
||||
Page Should Contain Element xpath=//clr-dg-cell[contains(., '${email}')]
|
@ -180,3 +180,11 @@ User Should Be Admin
|
||||
Page Should Contain Element xpath=//clr-dg-row[contains(.,'${user}')]//clr-dg-cell[contains(.,'Admin')]
|
||||
Logout Harbor
|
||||
Push Image With Tag ${ip} ${user} ${pwd} ${project} hello-world ${ip}/${project}/hello-world:v2
|
||||
|
||||
Project Should Have Member
|
||||
[Arguments] ${project} ${user}
|
||||
Sign In Harbor ${HARBOR_URL} %{HARBOR_ADMIN} %{HARBOR_PASSWORD}
|
||||
Go Into Project ${project}
|
||||
Switch To Member
|
||||
Page Should Contain Element xpath=//clr-dg-cell[contains(., '${user}')]
|
||||
Logout Harbor
|
||||
|
@ -353,6 +353,8 @@ Test Case - Ldap User Create Project
|
||||
Create An New Project project${d}
|
||||
Logout Harbor
|
||||
Manage Project Member %{HARBOR_ADMIN} %{HARBOR_PASSWORD} project${d} mike02 Add
|
||||
Project Should Have Member project${d} mike02
|
||||
User Email Should Exist mike02@example.com
|
||||
Close Browser
|
||||
|
||||
Test Case - Ldap User Push An Image
|
||||
|
Loading…
Reference in New Issue
Block a user