2016-02-01 12:59:10 +01:00
|
|
|
/*
|
|
|
|
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
2016-02-26 11:54:14 +01:00
|
|
|
|
2016-02-01 12:59:10 +01:00
|
|
|
package ldap
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
2016-03-25 11:41:10 +01:00
|
|
|
"github.com/vmware/harbor/utils/log"
|
2016-03-25 11:17:42 +01:00
|
|
|
|
2016-02-25 06:40:08 +01:00
|
|
|
"github.com/vmware/harbor/auth"
|
2016-02-01 12:59:10 +01:00
|
|
|
"github.com/vmware/harbor/dao"
|
|
|
|
"github.com/vmware/harbor/models"
|
|
|
|
|
|
|
|
"github.com/mqu/openldap"
|
|
|
|
)
|
|
|
|
|
2016-02-26 11:35:55 +01:00
|
|
|
// Auth implements Authenticator interface to authenticate against LDAP
|
2016-02-25 06:40:08 +01:00
|
|
|
type Auth struct{}
|
2016-02-01 12:59:10 +01:00
|
|
|
|
2016-02-25 06:40:08 +01:00
|
|
|
const metaChars = "&|!=~*<>()"
|
2016-02-01 12:59:10 +01:00
|
|
|
|
2016-07-19 10:46:21 +02:00
|
|
|
// Authenticate checks user's credential against LDAP based on basedn template and LDAP URL,
|
|
|
|
// if the check is successful a dummy record will be inserted into DB, such that this user can
|
2016-02-26 11:35:55 +01:00
|
|
|
// be associated to other entities in the system.
|
2016-02-25 06:40:08 +01:00
|
|
|
func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
|
2016-02-01 12:59:10 +01:00
|
|
|
|
2016-02-25 06:40:08 +01:00
|
|
|
p := m.Principal
|
|
|
|
for _, c := range metaChars {
|
2016-02-01 12:59:10 +01:00
|
|
|
if strings.ContainsRune(p, c) {
|
2016-02-25 06:40:08 +01:00
|
|
|
return nil, fmt.Errorf("the principal contains meta char: %q", c)
|
2016-02-01 12:59:10 +01:00
|
|
|
}
|
|
|
|
}
|
2016-08-18 12:31:41 +02:00
|
|
|
ldapURL := os.Getenv("LDAP_URL")
|
|
|
|
if ldapURL == "" {
|
|
|
|
return nil, errors.New("Can not get any available LDAP_URL.")
|
|
|
|
}
|
|
|
|
log.Debug("ldapURL:", ldapURL)
|
2016-02-25 06:40:08 +01:00
|
|
|
ldap, err := openldap.Initialize(ldapURL)
|
2016-02-01 12:59:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ldap.SetOption(openldap.LDAP_OPT_PROTOCOL_VERSION, openldap.LDAP_VERSION3)
|
|
|
|
|
|
|
|
ldapBaseDn := os.Getenv("LDAP_BASE_DN")
|
|
|
|
if ldapBaseDn == "" {
|
|
|
|
return nil, errors.New("Can not get any available LDAP_BASE_DN.")
|
|
|
|
}
|
2016-08-18 12:31:41 +02:00
|
|
|
log.Debug("baseDn:", ldapBaseDn)
|
2016-02-01 12:59:10 +01:00
|
|
|
|
2016-08-18 12:31:41 +02:00
|
|
|
ldapSearchDn := os.Getenv("LDAP_SEARCH_DN")
|
|
|
|
if ldapSearchDn != "" {
|
|
|
|
log.Debug("Search DN: ", ldapSearchDn)
|
|
|
|
ldapSearchPwd := os.Getenv("LDAP_SEARCH_PWD")
|
|
|
|
err = ldap.Bind(ldapSearchDn, ldapSearchPwd)
|
|
|
|
if err != nil {
|
|
|
|
log.Debug("Bind search dn error", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2016-02-01 12:59:10 +01:00
|
|
|
|
2016-08-18 12:31:41 +02:00
|
|
|
attrName := os.Getenv("LDAP_UID")
|
|
|
|
filter := os.Getenv("LDAP_FILTER")
|
|
|
|
if filter != "" {
|
2016-09-22 09:23:55 +02:00
|
|
|
filter = "(&" + filter + "(" + attrName + "=" + m.Principal + "))"
|
2016-08-18 12:31:41 +02:00
|
|
|
} else {
|
|
|
|
filter = "(" + attrName + "=" + m.Principal + ")"
|
|
|
|
}
|
|
|
|
log.Debug("one or more filter", filter)
|
|
|
|
|
|
|
|
ldapScope := os.Getenv("LDAP_SCOPE")
|
|
|
|
var scope int
|
|
|
|
if ldapScope == "1" {
|
|
|
|
scope = openldap.LDAP_SCOPE_BASE
|
|
|
|
} else if ldapScope == "2" {
|
|
|
|
scope = openldap.LDAP_SCOPE_ONELEVEL
|
|
|
|
} else {
|
|
|
|
scope = openldap.LDAP_SCOPE_SUBTREE
|
|
|
|
}
|
|
|
|
attributes := []string{"uid", "cn", "mail", "email"}
|
|
|
|
result, err := ldap.SearchAll(ldapBaseDn, scope, filter, attributes)
|
2016-02-01 12:59:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-08-18 12:31:41 +02:00
|
|
|
if len(result.Entries()) == 0 {
|
|
|
|
log.Warningf("Not found an entry.")
|
|
|
|
return nil, nil
|
|
|
|
} else if len(result.Entries()) != 1 {
|
|
|
|
log.Warningf("Found more than one entry.")
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
en := result.Entries()[0]
|
|
|
|
bindDN := en.Dn()
|
|
|
|
log.Debug("found entry:", en)
|
|
|
|
err = ldap.Bind(bindDN, m.Password)
|
2016-02-01 12:59:10 +01:00
|
|
|
if err != nil {
|
2016-08-18 12:31:41 +02:00
|
|
|
log.Debug("Bind user error", err)
|
2016-02-01 12:59:10 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
2016-08-18 12:31:41 +02:00
|
|
|
defer ldap.Close()
|
|
|
|
|
2016-02-01 12:59:10 +01:00
|
|
|
u := models.User{}
|
2016-08-18 12:31:41 +02:00
|
|
|
for _, attr := range en.Attributes() {
|
|
|
|
val := attr.Values()[0]
|
|
|
|
switch attr.Name() {
|
|
|
|
case "uid":
|
|
|
|
u.Realname = val
|
|
|
|
case "cn":
|
|
|
|
u.Realname = val
|
|
|
|
case "mail":
|
|
|
|
u.Email = val
|
|
|
|
case "email":
|
|
|
|
u.Email = val
|
2016-02-01 12:59:10 +01:00
|
|
|
}
|
|
|
|
}
|
2016-04-25 13:00:36 +02:00
|
|
|
u.Username = m.Principal
|
|
|
|
log.Debug("username:", u.Username, ",email:", u.Email)
|
2016-02-01 12:59:10 +01:00
|
|
|
exist, err := dao.UserExists(u, "username")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if exist {
|
|
|
|
currentUser, err := dao.GetUser(u)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-26 03:15:01 +01:00
|
|
|
u.UserID = currentUser.UserID
|
2016-02-01 12:59:10 +01:00
|
|
|
} else {
|
2016-04-25 13:00:36 +02:00
|
|
|
u.Realname = m.Principal
|
2016-02-01 12:59:10 +01:00
|
|
|
u.Password = "12345678AbC"
|
|
|
|
u.Comment = "registered from LDAP."
|
2016-06-01 11:51:06 +02:00
|
|
|
if u.Email == "" {
|
2016-06-02 11:33:10 +02:00
|
|
|
u.Email = u.Username + "@placeholder.com"
|
2016-06-01 11:51:06 +02:00
|
|
|
}
|
2016-02-25 06:40:08 +01:00
|
|
|
userID, err := dao.Register(u)
|
2016-02-01 12:59:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-26 03:15:01 +01:00
|
|
|
u.UserID = int(userID)
|
2016-02-01 12:59:10 +01:00
|
|
|
}
|
|
|
|
return &u, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2016-02-25 06:40:08 +01:00
|
|
|
auth.Register("ldap_auth", &Auth{})
|
2016-02-01 12:59:10 +01:00
|
|
|
}
|