Merge pull request #3911 from stonezdj/ldap_search_level

Ambiguous UI and internal values ldap_scope
This commit is contained in:
Daniel Jiang 2018-01-08 14:53:55 +08:00 committed by GitHub
commit f8af1f275e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 144 additions and 129 deletions

View File

@ -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 {

View File

@ -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"

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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")
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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) {

View File

@ -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()

View File

@ -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)
}

View File

@ -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;">

View File

@ -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}')]

View File

@ -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

View File

@ -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