Significantly enhanced LDAP support in Gogs.
This commit is contained in:
parent
631c85ba4f
commit
7d84d4a8f0
23 changed files with 295 additions and 250 deletions
|
@ -663,8 +663,6 @@ auths.auth_name=Име на удостоверяването
|
||||||
auths.domain=Домейн
|
auths.domain=Домейн
|
||||||
auths.host=Хост
|
auths.host=Хост
|
||||||
auths.port=Порт
|
auths.port=Порт
|
||||||
auths.base_dn=Основен DN
|
|
||||||
auths.attribute_username=Атрибут на потребителско име
|
|
||||||
auths.attribute_name=Атрибут име
|
auths.attribute_name=Атрибут име
|
||||||
auths.attribute_surname=Атрибут фамилия
|
auths.attribute_surname=Атрибут фамилия
|
||||||
auths.attribute_mail=E-Mail атрибут
|
auths.attribute_mail=E-Mail атрибут
|
||||||
|
|
|
@ -722,8 +722,6 @@ auths.auth_name=Authentifizierungsname
|
||||||
auths.domain=Domain
|
auths.domain=Domain
|
||||||
auths.host=Host
|
auths.host=Host
|
||||||
auths.port=Port
|
auths.port=Port
|
||||||
auths.base_dn=Base DN
|
|
||||||
auths.attribute_username=Benutzername Attribut
|
|
||||||
auths.attribute_name=Vorname Attribut
|
auths.attribute_name=Vorname Attribut
|
||||||
auths.attribute_surname=Nachname Attribut
|
auths.attribute_surname=Nachname Attribut
|
||||||
auths.attribute_mail=E-Mail Attribut
|
auths.attribute_mail=E-Mail Attribut
|
||||||
|
|
|
@ -722,12 +722,13 @@ auths.auth_name = Authorization Name
|
||||||
auths.domain = Domain
|
auths.domain = Domain
|
||||||
auths.host = Host
|
auths.host = Host
|
||||||
auths.port = Port
|
auths.port = Port
|
||||||
auths.base_dn = Base DN
|
auths.bind_dn = Bind DN
|
||||||
auths.attribute_username = Username attribute
|
auths.bind_password = Bind Password
|
||||||
|
auths.user_base = User Search Base
|
||||||
auths.attribute_name = First name attribute
|
auths.attribute_name = First name attribute
|
||||||
auths.attribute_surname = Surname attribute
|
auths.attribute_surname = Surname attribute
|
||||||
auths.attribute_mail = E-mail attribute
|
auths.attribute_mail = E-mail attribute
|
||||||
auths.filter = Search Filter
|
auths.filter = User Filter
|
||||||
auths.ms_ad_sa = Ms Ad SA
|
auths.ms_ad_sa = Ms Ad SA
|
||||||
auths.smtp_auth = SMTP Authorization Type
|
auths.smtp_auth = SMTP Authorization Type
|
||||||
auths.smtphost = SMTP Host
|
auths.smtphost = SMTP Host
|
||||||
|
@ -859,4 +860,4 @@ raw_minutes = minutes
|
||||||
default_message = Drop files here or click to upload.
|
default_message = Drop files here or click to upload.
|
||||||
invalid_input_type = You can't upload files of this type.
|
invalid_input_type = You can't upload files of this type.
|
||||||
file_too_big = File size({{filesize}} MB) exceeds maximum size({{maxFilesize}} MB).
|
file_too_big = File size({{filesize}} MB) exceeds maximum size({{maxFilesize}} MB).
|
||||||
remove_file = Remove file
|
remove_file = Remove file
|
||||||
|
|
|
@ -663,13 +663,10 @@ auths.auth_name=Nombre de Autorización
|
||||||
auths.domain=Dominio
|
auths.domain=Dominio
|
||||||
auths.host=Host
|
auths.host=Host
|
||||||
auths.port=Puerto
|
auths.port=Puerto
|
||||||
auths.base_dn=Base DN
|
|
||||||
auths.attribute_username=Atributo username
|
|
||||||
auths.attribute_name=Atributo nombre
|
auths.attribute_name=Atributo nombre
|
||||||
auths.attribute_surname=Atributo apellido
|
auths.attribute_surname=Atributo apellido
|
||||||
auths.attribute_mail=Atributo correo electrónico
|
auths.attribute_mail=Atributo correo electrónico
|
||||||
auths.filter=Filtro de Búsqueda
|
auths.filter=Filtro de Búsqueda
|
||||||
auths.ms_ad_sa=Ms Ad SA
|
|
||||||
auths.smtp_auth=Tipo de Autorización SMTP
|
auths.smtp_auth=Tipo de Autorización SMTP
|
||||||
auths.smtphost=SMTP Host
|
auths.smtphost=SMTP Host
|
||||||
auths.smtpport=Puerto SMTP
|
auths.smtpport=Puerto SMTP
|
||||||
|
|
|
@ -722,8 +722,6 @@ auths.auth_name=Nom d'Autorisation
|
||||||
auths.domain=Domaine
|
auths.domain=Domaine
|
||||||
auths.host=Hôte
|
auths.host=Hôte
|
||||||
auths.port=Port
|
auths.port=Port
|
||||||
auths.base_dn=Base DN (Nom Distingué)
|
|
||||||
auths.attribute_username=Attribut du nom d'utilisateur
|
|
||||||
auths.attribute_name=Attribut du prénom
|
auths.attribute_name=Attribut du prénom
|
||||||
auths.attribute_surname=Attribut du nom de famille
|
auths.attribute_surname=Attribut du nom de famille
|
||||||
auths.attribute_mail=Attribut de l'e-mail
|
auths.attribute_mail=Attribut de l'e-mail
|
||||||
|
|
|
@ -663,8 +663,6 @@ auths.auth_name=Nome Autorizzazione
|
||||||
auths.domain=Dominio
|
auths.domain=Dominio
|
||||||
auths.host=Host
|
auths.host=Host
|
||||||
auths.port=Porta
|
auths.port=Porta
|
||||||
auths.base_dn=Base DN
|
|
||||||
auths.attribute_username=Attributo Nome Utente
|
|
||||||
auths.attribute_name=Attributo Nome
|
auths.attribute_name=Attributo Nome
|
||||||
auths.attribute_surname=Attributo Cognome
|
auths.attribute_surname=Attributo Cognome
|
||||||
auths.attribute_mail=Attributo Email
|
auths.attribute_mail=Attributo Email
|
||||||
|
|
|
@ -663,8 +663,6 @@ auths.auth_name=認証名
|
||||||
auths.domain=ドメイン
|
auths.domain=ドメイン
|
||||||
auths.host=ホスト
|
auths.host=ホスト
|
||||||
auths.port=ポート
|
auths.port=ポート
|
||||||
auths.base_dn=ベースのドメイン名
|
|
||||||
auths.attribute_username=ユーザー名属性
|
|
||||||
auths.attribute_name=名前属性
|
auths.attribute_name=名前属性
|
||||||
auths.attribute_surname=名字属性
|
auths.attribute_surname=名字属性
|
||||||
auths.attribute_mail=Eメール属性
|
auths.attribute_mail=Eメール属性
|
||||||
|
|
|
@ -663,8 +663,6 @@ auths.auth_name=Autorizācijas nosaukums
|
||||||
auths.domain=Domēns
|
auths.domain=Domēns
|
||||||
auths.host=Resursdators
|
auths.host=Resursdators
|
||||||
auths.port=Ports
|
auths.port=Ports
|
||||||
auths.base_dn=Pamata DN
|
|
||||||
auths.attribute_username=Username attribute
|
|
||||||
auths.attribute_name=First name attribute
|
auths.attribute_name=First name attribute
|
||||||
auths.attribute_surname=Surname attribute
|
auths.attribute_surname=Surname attribute
|
||||||
auths.attribute_mail=E-mail attribute
|
auths.attribute_mail=E-mail attribute
|
||||||
|
|
|
@ -663,8 +663,6 @@ auths.auth_name=Autorisatienaam
|
||||||
auths.domain=Domein
|
auths.domain=Domein
|
||||||
auths.host=Host
|
auths.host=Host
|
||||||
auths.port=Poort
|
auths.port=Poort
|
||||||
auths.base_dn=Base DN
|
|
||||||
auths.attribute_username=Gebruikersnaam attribuut
|
|
||||||
auths.attribute_name=Voornaam attribuut
|
auths.attribute_name=Voornaam attribuut
|
||||||
auths.attribute_surname=Achternaam attribuut
|
auths.attribute_surname=Achternaam attribuut
|
||||||
auths.attribute_mail=E-mail attribuut
|
auths.attribute_mail=E-mail attribuut
|
||||||
|
|
|
@ -663,8 +663,6 @@ auths.auth_name=Nazwa autoryzacji
|
||||||
auths.domain=Domena
|
auths.domain=Domena
|
||||||
auths.host=Host
|
auths.host=Host
|
||||||
auths.port=Port
|
auths.port=Port
|
||||||
auths.base_dn=Base DN
|
|
||||||
auths.attribute_username=Atrybut username
|
|
||||||
auths.attribute_name=Atrybut imienia
|
auths.attribute_name=Atrybut imienia
|
||||||
auths.attribute_surname=Atrybut nazwiska
|
auths.attribute_surname=Atrybut nazwiska
|
||||||
auths.attribute_mail=Atrybut email
|
auths.attribute_mail=Atrybut email
|
||||||
|
|
|
@ -663,8 +663,6 @@ auths.auth_name=Nome da Autorização
|
||||||
auths.domain=Domínio
|
auths.domain=Domínio
|
||||||
auths.host=Host
|
auths.host=Host
|
||||||
auths.port=Porta
|
auths.port=Porta
|
||||||
auths.base_dn=Base DN
|
|
||||||
auths.attribute_username=Atributo nome de usuário
|
|
||||||
auths.attribute_name=Atributo primeiro nome
|
auths.attribute_name=Atributo primeiro nome
|
||||||
auths.attribute_surname=Atributo sobrenome
|
auths.attribute_surname=Atributo sobrenome
|
||||||
auths.attribute_mail=Atributo e-mail
|
auths.attribute_mail=Atributo e-mail
|
||||||
|
|
|
@ -663,8 +663,6 @@ auths.auth_name=Название авторизации
|
||||||
auths.domain=Домен
|
auths.domain=Домен
|
||||||
auths.host=Хост
|
auths.host=Хост
|
||||||
auths.port=Порт
|
auths.port=Порт
|
||||||
auths.base_dn=Base DN
|
|
||||||
auths.attribute_username=Username attribute
|
|
||||||
auths.attribute_name=First name attribute
|
auths.attribute_name=First name attribute
|
||||||
auths.attribute_surname=Surname attribute
|
auths.attribute_surname=Surname attribute
|
||||||
auths.attribute_mail=E-mail attribute
|
auths.attribute_mail=E-mail attribute
|
||||||
|
|
|
@ -722,8 +722,6 @@ auths.auth_name=授权名称
|
||||||
auths.domain=域名
|
auths.domain=域名
|
||||||
auths.host=主机地址
|
auths.host=主机地址
|
||||||
auths.port=主机端口
|
auths.port=主机端口
|
||||||
auths.base_dn=Base DN
|
|
||||||
auths.attribute_username=用户名属性
|
|
||||||
auths.attribute_name=名字属性
|
auths.attribute_name=名字属性
|
||||||
auths.attribute_surname=姓氏属性
|
auths.attribute_surname=姓氏属性
|
||||||
auths.attribute_mail=邮箱属性
|
auths.attribute_mail=邮箱属性
|
||||||
|
|
|
@ -663,8 +663,6 @@ auths.auth_name=授權名稱
|
||||||
auths.domain=域名
|
auths.domain=域名
|
||||||
auths.host=主機地址
|
auths.host=主機地址
|
||||||
auths.port=主機端口
|
auths.port=主機端口
|
||||||
auths.base_dn=Base DN
|
|
||||||
auths.attribute_username=用戶名屬性
|
|
||||||
auths.attribute_name=名子屬性
|
auths.attribute_name=名子屬性
|
||||||
auths.attribute_surname=姓氏屬性
|
auths.attribute_surname=姓氏屬性
|
||||||
auths.attribute_mail=電子郵箱屬性
|
auths.attribute_mail=電子郵箱屬性
|
||||||
|
|
|
@ -19,7 +19,6 @@ import (
|
||||||
"github.com/gogits/gogs/modules/auth/ldap"
|
"github.com/gogits/gogs/modules/auth/ldap"
|
||||||
"github.com/gogits/gogs/modules/auth/pam"
|
"github.com/gogits/gogs/modules/auth/pam"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"github.com/gogits/gogs/modules/log"
|
||||||
"github.com/gogits/gogs/modules/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginType int
|
type LoginType int
|
||||||
|
@ -258,18 +257,19 @@ func UserSignIn(uname, passwd string) (*User, error) {
|
||||||
// Return the same LoginUserPlain semantic
|
// Return the same LoginUserPlain semantic
|
||||||
// FIXME: https://github.com/gogits/gogs/issues/672
|
// FIXME: https://github.com/gogits/gogs/issues/672
|
||||||
func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) {
|
func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) {
|
||||||
name, fn, sn, mail, logged := cfg.Ldapsource.SearchEntry(name, passwd)
|
fn, sn, mail, logged := cfg.Ldapsource.SearchEntry(name, passwd)
|
||||||
if !logged {
|
if !logged {
|
||||||
// User not in LDAP, do nothing
|
// User not in LDAP, do nothing
|
||||||
return nil, ErrUserNotExist{u.Id, u.Name}
|
return nil, ErrUserNotExist{0, name}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !autoRegister {
|
if !autoRegister {
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback.
|
// Fallback.
|
||||||
if len(mail) == 0 {
|
if len(mail) == 0 {
|
||||||
mail = uuid.NewV4().String() + "@localhost"
|
mail = fmt.Sprintf("%s@localhost", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
u = &User{
|
u = &User{
|
||||||
|
|
|
@ -13,17 +13,16 @@ type AuthenticationForm struct {
|
||||||
ID int64 `form:"id"`
|
ID int64 `form:"id"`
|
||||||
Type int
|
Type int
|
||||||
Name string `binding:"Required;MaxSize(50)"`
|
Name string `binding:"Required;MaxSize(50)"`
|
||||||
Domain string
|
|
||||||
Host string
|
Host string
|
||||||
Port int
|
Port int
|
||||||
UseSSL bool `form:"usessl"`
|
UseSSL bool `form:"use_ssl"`
|
||||||
BaseDN string `form:"base_dn"`
|
BindDN string `form:"bind_dn"`
|
||||||
AttributeUsername string
|
BindPassword string
|
||||||
|
UserBase string
|
||||||
AttributeName string
|
AttributeName string
|
||||||
AttributeSurname string
|
AttributeSurname string
|
||||||
AttributeMail string
|
AttributeMail string
|
||||||
Filter string
|
Filter string
|
||||||
MsAdSA string `form:"ms_ad_sa"`
|
|
||||||
IsActived bool
|
IsActived bool
|
||||||
SMTPAuth string `form:"smtp_auth"`
|
SMTPAuth string `form:"smtp_auth"`
|
||||||
SMTPHost string `form:"smtp_host"`
|
SMTPHost string `form:"smtp_host"`
|
||||||
|
|
|
@ -1,43 +1,64 @@
|
||||||
LDAP authentication
|
Gogs LDAP Authentication Module
|
||||||
===================
|
===============================
|
||||||
|
|
||||||
## Goal
|
## About
|
||||||
|
|
||||||
Authenticat user against LDAP directories
|
This authentication module attempts to authorize and authenticate a user
|
||||||
|
against an LDAP server. Like most LDAP authentication systems, this module does
|
||||||
It will bind with the user's login/pasword and query attributs ("mail" for instance) in a pool of directory servers
|
this in two steps. First, it queries the LDAP server using a Bind DN and
|
||||||
|
searches for the user that is attempting to sign in. If the user is found, the
|
||||||
The first OK wins.
|
module attempts to bind to the server using the user's supplied credentials. If
|
||||||
|
this succeeds, the user has been authenticated, and his account information is
|
||||||
If there's connection error, the server will be disabled and won't be checked again
|
retrieved and passed to the Gogs login infrastructure.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
In the [security] section, set
|
To use this module, add an LDAP authentication source via the Authentications
|
||||||
> LDAP_AUTH = true
|
section in the admin panel. The fields should be set as follows:
|
||||||
|
|
||||||
then for each LDAP source, set
|
Authorization Name (required)
|
||||||
|
A name to assign to the new method of authorization.
|
||||||
|
|
||||||
> [LdapSource-someuniquename]
|
Host (required)
|
||||||
> name=canonicalName
|
The address where the LDAP server can be reached.
|
||||||
> host=hostname-or-ip
|
Example: mydomain.com
|
||||||
> port=3268 # or regular LDAP port
|
|
||||||
> # the following settings depend highly how you've configured your AD
|
|
||||||
> basedn=dc=ACME,dc=COM
|
|
||||||
> MSADSAFORMAT=%s@ACME.COM
|
|
||||||
> filter=(&(objectClass=user)(sAMAccountName=%s))
|
|
||||||
|
|
||||||
### Limitation
|
Port (required)
|
||||||
|
The port to use when connecting to the server.
|
||||||
|
Example: 636
|
||||||
|
|
||||||
Only tested on an MS 2008R2 DC, using global catalog (TCP/3268)
|
Enable TLS Encryption (optional)
|
||||||
|
Whether to use TLS when connecting to the LDAP server.
|
||||||
|
|
||||||
This MSAD is a mess.
|
Bind DN (optional)
|
||||||
|
The DN to bind to the LDAP server with when searching for the user.
|
||||||
|
This may be left blank to perform an anonymous search.
|
||||||
|
Example: cn=Search,dc=mydomain,dc=com
|
||||||
|
|
||||||
The way how one checks the directory (CN, DN etc...) may be highly depending local custom configuration
|
Bind Password (optional)
|
||||||
|
The password for the Bind DN specified above, if any.
|
||||||
|
|
||||||
### Todo
|
User Search Base (required)
|
||||||
* Define a timeout per server
|
The LDAP base at which user accounts will be searched for.
|
||||||
* Check servers marked as "Disabled" when they'll come back online
|
Example: ou=Users,dc=mydomain,dc=com
|
||||||
* Find a more flexible way to define filter/MSADSAFORMAT/Attributes etc... maybe text/template ?
|
|
||||||
* Check OpenLDAP server
|
User Filter (required)
|
||||||
* SSL support ?
|
An LDAP filter declaring how to find the user record that is attempting
|
||||||
|
to authenticate. The '%s' matching parameter will be substituted with
|
||||||
|
the user's username.
|
||||||
|
Example: (&(objectClass=posixAccount)(uid=%s))
|
||||||
|
|
||||||
|
First name attribute (optional)
|
||||||
|
The attribute of the user's LDAP record containing the user's first
|
||||||
|
name. This will be used to populate their account information.
|
||||||
|
Example: givenName
|
||||||
|
|
||||||
|
Surname name attribute (optional)
|
||||||
|
The attribute of the user's LDAP record containing the user's surname
|
||||||
|
This will be used to populate their account information.
|
||||||
|
Example: sn
|
||||||
|
|
||||||
|
E-mail attribute (required)
|
||||||
|
The attribute of the user's LDAP record containing the user's email
|
||||||
|
address. This will be used to populate their account information.
|
||||||
|
Example: mail
|
||||||
|
|
|
@ -19,82 +19,114 @@ type Ldapsource struct {
|
||||||
Host string // LDAP host
|
Host string // LDAP host
|
||||||
Port int // port number
|
Port int // port number
|
||||||
UseSSL bool // Use SSL
|
UseSSL bool // Use SSL
|
||||||
BaseDN string // Base DN
|
BindDN string // DN to bind with
|
||||||
AttributeUsername string // Username attribute
|
BindPassword string // Bind DN password
|
||||||
|
UserBase string // Base search path for users
|
||||||
AttributeName string // First name attribute
|
AttributeName string // First name attribute
|
||||||
AttributeSurname string // Surname attribute
|
AttributeSurname string // Surname attribute
|
||||||
AttributeMail string // E-mail attribute
|
AttributeMail string // E-mail attribute
|
||||||
Filter string // Query filter to validate entry
|
Filter string // Query filter to validate entry
|
||||||
MsAdSAFormat string // in the case of MS AD Simple Authen, the format to use (see: http://msdn.microsoft.com/en-us/library/cc223499.aspx)
|
|
||||||
Enabled bool // if this source is disabled
|
Enabled bool // if this source is disabled
|
||||||
}
|
}
|
||||||
|
|
||||||
//Global LDAP directory pool
|
func (ls Ldapsource) FindUserDN(name string) (userDN string, success bool) {
|
||||||
var (
|
userDN = ""
|
||||||
Authensource []Ldapsource
|
success = false
|
||||||
)
|
|
||||||
|
|
||||||
// Add a new source (LDAP directory) to the global pool
|
|
||||||
func AddSource(name string, host string, port int, usessl bool, basedn string, attribcn string, attribname string, attribsn string, attribmail string, filter string, msadsaformat string) {
|
|
||||||
ldaphost := Ldapsource{name, host, port, usessl, basedn, attribcn, attribname, attribsn, attribmail, filter, msadsaformat, true}
|
|
||||||
Authensource = append(Authensource, ldaphost)
|
|
||||||
}
|
|
||||||
|
|
||||||
//LoginUser : try to login an user to LDAP sources, return requested (attribute,true) if ok, ("",false) other wise
|
|
||||||
//First match wins
|
|
||||||
//Returns first attribute if exists
|
|
||||||
func LoginUser(name, passwd string) (cn, fn, sn, mail string, r bool) {
|
|
||||||
r = false
|
|
||||||
for _, ls := range Authensource {
|
|
||||||
cn, fn, sn, mail, r = ls.SearchEntry(name, passwd)
|
|
||||||
if r {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// searchEntry : search an LDAP source if an entry (name, passwd) is valide and in the specific filter
|
|
||||||
func (ls Ldapsource) SearchEntry(name, passwd string) (string, string, string, string, bool) {
|
|
||||||
l, err := ldapDial(ls)
|
l, err := ldapDial(ls)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
|
log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
|
||||||
ls.Enabled = false
|
ls.Enabled = false
|
||||||
return "", "", "", "", false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer l.Close()
|
||||||
|
log.Trace("Search for LDAP user: %s", name)
|
||||||
|
if ls.BindDN != "" && ls.BindPassword != "" {
|
||||||
|
err = l.Bind(ls.BindDN, ls.BindPassword)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to bind as BindDN: %s, %s", ls.BindDN, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Trace("Bound as BindDN %s", ls.BindDN)
|
||||||
|
} else {
|
||||||
|
log.Trace("Proceeding with anonymous LDAP search.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// A search for the user.
|
||||||
|
userFilter := fmt.Sprintf(ls.Filter, name)
|
||||||
|
log.Trace("Searching using filter %s", userFilter)
|
||||||
|
search := ldap.NewSearchRequest(
|
||||||
|
ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0,
|
||||||
|
false, userFilter, []string{}, nil)
|
||||||
|
|
||||||
|
// Ensure we found a user
|
||||||
|
sr, err := l.Search(search)
|
||||||
|
if err != nil || len(sr.Entries) < 1 {
|
||||||
|
log.Debug("Failed search using filter %s: %s", userFilter, err.Error())
|
||||||
|
return
|
||||||
|
} else if len(sr.Entries) > 1 {
|
||||||
|
log.Debug("Filter '%s' returned more than one user.", userFilter)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userDN = sr.Entries[0].DN
|
||||||
|
if userDN == "" {
|
||||||
|
log.Error(4, "LDAP search was succesful, but found no DN!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
success = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// searchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter
|
||||||
|
func (ls Ldapsource) SearchEntry(name, passwd string) (string, string, string, bool) {
|
||||||
|
userDN, found := ls.FindUserDN(name)
|
||||||
|
if !found {
|
||||||
|
return "", "", "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := ldapDial(ls)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
|
||||||
|
ls.Enabled = false
|
||||||
|
return "", "", "", false
|
||||||
|
}
|
||||||
|
|
||||||
defer l.Close()
|
defer l.Close()
|
||||||
|
|
||||||
nx := fmt.Sprintf(ls.MsAdSAFormat, name)
|
log.Trace("Binding with userDN: %s", userDN)
|
||||||
err = l.Bind(nx, passwd)
|
err = l.Bind(userDN, passwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("LDAP Authan failed for %s, reason: %s", nx, err.Error())
|
log.Debug("LDAP auth. failed for %s, reason: %s", userDN, err.Error())
|
||||||
return "", "", "", "", false
|
return "", "", "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Trace("Bound successfully with userDN: %s", userDN)
|
||||||
|
userFilter := fmt.Sprintf(ls.Filter, name)
|
||||||
search := ldap.NewSearchRequest(
|
search := ldap.NewSearchRequest(
|
||||||
ls.BaseDN,
|
userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
|
||||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
[]string{ls.AttributeName, ls.AttributeSurname, ls.AttributeMail},
|
||||||
fmt.Sprintf(ls.Filter, name),
|
|
||||||
[]string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail},
|
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
sr, err := l.Search(search)
|
sr, err := l.Search(search)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("LDAP Authen OK but not in filter %s", name)
|
log.Error(4, "LDAP Search failed unexpectedly! (%s)", err.Error())
|
||||||
return "", "", "", "", false
|
return "", "", "", false
|
||||||
|
} else if len(sr.Entries) < 1 {
|
||||||
|
log.Error(4, "LDAP Search failed unexpectedly! (0 entries)")
|
||||||
|
return "", "", "", false
|
||||||
}
|
}
|
||||||
log.Debug("LDAP Authen OK: %s", name)
|
|
||||||
if len(sr.Entries) > 0 {
|
name_attr := sr.Entries[0].GetAttributeValue(ls.AttributeName)
|
||||||
cn := sr.Entries[0].GetAttributeValue(ls.AttributeUsername)
|
sn_attr := sr.Entries[0].GetAttributeValue(ls.AttributeSurname)
|
||||||
name := sr.Entries[0].GetAttributeValue(ls.AttributeName)
|
mail_attr := sr.Entries[0].GetAttributeValue(ls.AttributeMail)
|
||||||
sn := sr.Entries[0].GetAttributeValue(ls.AttributeSurname)
|
return name_attr, sn_attr, mail_attr, true
|
||||||
mail := sr.Entries[0].GetAttributeValue(ls.AttributeMail)
|
|
||||||
return cn, name, sn, mail, true
|
|
||||||
}
|
|
||||||
return "", "", "", "", true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ldapDial(ls Ldapsource) (*ldap.Conn, error) {
|
func ldapDial(ls Ldapsource) (*ldap.Conn, error) {
|
||||||
if ls.UseSSL {
|
if ls.UseSSL {
|
||||||
|
log.Debug("Using TLS for LDAP")
|
||||||
return ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port), nil)
|
return ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port), nil)
|
||||||
} else {
|
} else {
|
||||||
return ldap.Dial("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port))
|
return ldap.Dial("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port))
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
package ldap
|
|
||||||
|
|
||||||
// import (
|
|
||||||
// "fmt"
|
|
||||||
// "testing"
|
|
||||||
// )
|
|
||||||
|
|
||||||
// var ldapServer = "ldap.itd.umich.edu"
|
|
||||||
// var ldapPort = 389
|
|
||||||
// var baseDN = "dc=umich,dc=edu"
|
|
||||||
// var filter = []string{
|
|
||||||
// "(cn=cis-fac)",
|
|
||||||
// "(&(objectclass=rfc822mailgroup)(cn=*Computer*))",
|
|
||||||
// "(&(objectclass=rfc822mailgroup)(cn=*Mathematics*))"}
|
|
||||||
// var attributes = []string{
|
|
||||||
// "cn",
|
|
||||||
// "description"}
|
|
||||||
// var msadsaformat = ""
|
|
||||||
|
|
||||||
// func TestLDAP(t *testing.T) {
|
|
||||||
// AddSource("test", ldapServer, ldapPort, baseDN, attributes, filter, msadsaformat)
|
|
||||||
// user, err := LoginUserLdap("xiaolunwen", "")
|
|
||||||
// if err != nil {
|
|
||||||
// t.Error(err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fmt.Println(user)
|
|
||||||
// }
|
|
File diff suppressed because one or more lines are too long
|
@ -63,18 +63,18 @@ func NewAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
|
||||||
case models.LDAP:
|
case models.LDAP:
|
||||||
u = &models.LDAPConfig{
|
u = &models.LDAPConfig{
|
||||||
Ldapsource: ldap.Ldapsource{
|
Ldapsource: ldap.Ldapsource{
|
||||||
|
Name: form.Name,
|
||||||
Host: form.Host,
|
Host: form.Host,
|
||||||
Port: form.Port,
|
Port: form.Port,
|
||||||
UseSSL: form.UseSSL,
|
UseSSL: form.UseSSL,
|
||||||
BaseDN: form.BaseDN,
|
BindDN: form.BindDN,
|
||||||
AttributeUsername: form.AttributeUsername,
|
BindPassword: form.BindPassword,
|
||||||
|
UserBase: form.UserBase,
|
||||||
|
Filter: form.Filter,
|
||||||
AttributeName: form.AttributeName,
|
AttributeName: form.AttributeName,
|
||||||
AttributeSurname: form.AttributeSurname,
|
AttributeSurname: form.AttributeSurname,
|
||||||
AttributeMail: form.AttributeMail,
|
AttributeMail: form.AttributeMail,
|
||||||
Filter: form.Filter,
|
|
||||||
MsAdSAFormat: form.MsAdSA,
|
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Name: form.Name,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case models.SMTP:
|
case models.SMTP:
|
||||||
|
@ -149,18 +149,18 @@ func EditAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
|
||||||
case models.LDAP:
|
case models.LDAP:
|
||||||
config = &models.LDAPConfig{
|
config = &models.LDAPConfig{
|
||||||
Ldapsource: ldap.Ldapsource{
|
Ldapsource: ldap.Ldapsource{
|
||||||
|
Name: form.Name,
|
||||||
Host: form.Host,
|
Host: form.Host,
|
||||||
Port: form.Port,
|
Port: form.Port,
|
||||||
UseSSL: form.UseSSL,
|
UseSSL: form.UseSSL,
|
||||||
BaseDN: form.BaseDN,
|
BindDN: form.BindDN,
|
||||||
AttributeUsername: form.AttributeUsername,
|
BindPassword: form.BindPassword,
|
||||||
|
UserBase: form.UserBase,
|
||||||
AttributeName: form.AttributeName,
|
AttributeName: form.AttributeName,
|
||||||
AttributeSurname: form.AttributeSurname,
|
AttributeSurname: form.AttributeSurname,
|
||||||
AttributeMail: form.AttributeMail,
|
AttributeMail: form.AttributeMail,
|
||||||
Filter: form.Filter,
|
Filter: form.Filter,
|
||||||
MsAdSAFormat: form.MsAdSA,
|
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Name: form.Name,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case models.SMTP:
|
case models.SMTP:
|
||||||
|
|
|
@ -31,10 +31,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if eq $type 2}}
|
{{if eq $type 2}}
|
||||||
<div class="field">
|
|
||||||
<label class="req" for="domain">{{.i18n.Tr "admin.auths.domain"}}</label>
|
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Domain}}ipt-error{{end}}" id="domain" name="domain" value="{{.Source.LDAP.Name}}" required />
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="req" for="host">{{.i18n.Tr "admin.auths.host"}}</label>
|
<label class="req" for="host">{{.i18n.Tr "admin.auths.host"}}</label>
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Host}}ipt-error{{end}}" id="host" name="host" value="{{.Source.LDAP.Host}}" required />
|
<input class="ipt ipt-large ipt-radius {{if .Err_Host}}ipt-error{{end}}" id="host" name="host" value="{{.Source.LDAP.Host}}" required />
|
||||||
|
@ -44,12 +40,24 @@
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Port}}ipt-error{{end}}" id="port" name="port" value="{{.Source.LDAP.Port}}" required />
|
<input class="ipt ipt-large ipt-radius {{if .Err_Port}}ipt-error{{end}}" id="port" name="port" value="{{.Source.LDAP.Port}}" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="req" for="base_dn">{{.i18n.Tr "admin.auths.base_dn"}}</label>
|
<label for="use_ssl">{{.i18n.Tr "admin.auths.enable_tls"}}</label>
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_BaseDN}}ipt-error{{end}}" id="base_dn" name="base_dn" value="{{.Source.LDAP.BaseDN}}" />
|
<input name="use_ssl" type="checkbox" {{if .Source.LDAP.UseSSL}}checked{{end}}>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="req" for="attribute_username">{{.i18n.Tr "admin.auths.attribute_username"}}</label>
|
<label for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label>
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Attributes}}ipt-error{{end}}" id="attribute_username" name="attribute_username" value="{{.Source.LDAP.AttributeUsername}}" />
|
<input class="ipt ipt-large ipt-radius {{if .Err_BindDN}}ipt-error{{end}}" id="bind_dn" name="bind_dn" value="{{.Source.LDAP.BindDN}}" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="bind_password">{{.i18n.Tr "admin.auths.bind_password"}}</label>
|
||||||
|
<input class="ipt ipt-large ipt-radius {{if .Err_BindPassword}}ipt-error{{end}}" id="bind_password" name="bind_password" value="{{.Source.LDAP.BindPassword}}" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="req" for="user_base">{{.i18n.Tr "admin.auths.user_base"}}</label>
|
||||||
|
<input class="ipt ipt-large ipt-radius {{if .Err_UserBase}}ipt-error{{end}}" id="user_base" name="user_base" value="{{.Source.LDAP.UserBase}}" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="req" for="filter">{{.i18n.Tr "admin.auths.filter"}}</label>
|
||||||
|
<input class="ipt ipt-large ipt-radius {{if .Err_Filter}}ipt-error{{end}}" id="filter" name="filter" value="{{.Source.LDAP.Filter}}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="attribute_name">{{.i18n.Tr "admin.auths.attribute_name"}}</label>
|
<label for="attribute_name">{{.i18n.Tr "admin.auths.attribute_name"}}</label>
|
||||||
|
@ -63,14 +71,6 @@
|
||||||
<label class="req" for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label>
|
<label class="req" for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label>
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Attributes}}ipt-error{{end}}" id="attribute_mail" name="attribute_mail" value="{{.Source.LDAP.AttributeMail}}" />
|
<input class="ipt ipt-large ipt-radius {{if .Err_Attributes}}ipt-error{{end}}" id="attribute_mail" name="attribute_mail" value="{{.Source.LDAP.AttributeMail}}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
|
||||||
<label class="req" for="filter">{{.i18n.Tr "admin.auths.filter"}}</label>
|
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Filter}}ipt-error{{end}}" id="filter" name="filter" value="{{.Source.LDAP.Filter}}" />
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="req" for="ms_ad_sa">{{.i18n.Tr "admin.auths.ms_ad_sa"}}</label>
|
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_MsAdSA}}ipt-error{{end}}" id="ms_ad_sa" name="ms_ad_sa" value="{{.Source.LDAP.MsAdSAFormat}}" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{else if eq $type 3}}
|
{{else if eq $type 3}}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
|
@ -27,10 +27,6 @@
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_AuthName}}ipt-error{{end}}" id="name" name="name" value="{{.name}}" required />
|
<input class="ipt ipt-large ipt-radius {{if .Err_AuthName}}ipt-error{{end}}" id="name" name="name" value="{{.name}}" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="ldap">
|
<div class="ldap">
|
||||||
<div class="field">
|
|
||||||
<label class="req" for="domain">{{.i18n.Tr "admin.auths.domain"}}</label>
|
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Domain}}ipt-error{{end}}" id="domain" name="domain" value="{{.domain}}" />
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="req" for="host">{{.i18n.Tr "admin.auths.host"}}</label>
|
<label class="req" for="host">{{.i18n.Tr "admin.auths.host"}}</label>
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Host}}ipt-error{{end}}" id="host" name="host" value="{{.host}}" />
|
<input class="ipt ipt-large ipt-radius {{if .Err_Host}}ipt-error{{end}}" id="host" name="host" value="{{.host}}" />
|
||||||
|
@ -40,12 +36,24 @@
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Port}}ipt-error{{end}}" id="port" name="port" value="{{.port}}" />
|
<input class="ipt ipt-large ipt-radius {{if .Err_Port}}ipt-error{{end}}" id="port" name="port" value="{{.port}}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="req" for="base_dn">{{.i18n.Tr "admin.auths.base_dn"}}</label>
|
<label for="use_ssl">{{.i18n.Tr "admin.auths.enable_tls"}}</label>
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_BaseDN}}ipt-error{{end}}" id="base_dn" name="base_dn" value="{{.base_dn}}" />
|
<input name="use_ssl" type="checkbox" {{if .use_ssl}}checked{{end}}>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="req" for="attribute_username">{{.i18n.Tr "admin.auths.attribute_username"}}</label>
|
<label class="req" for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label>
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_AttributeUsername}}ipt-error{{end}}" id="attribute_username" name="attribute_username" value="{{.attribute_username}}" />
|
<input class="ipt ipt-large ipt-radius {{if .Err_BindDN}}ipt-error{{end}}" id="bind_dn" name="bind_dn" value="{{.bind_dn}}" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="req" for="bind_password">{{.i18n.Tr "admin.auths.bind_password"}}</label>
|
||||||
|
<input class="ipt ipt-large ipt-radius {{if .Err_BindPassword}}ipt-error{{end}}" id="bind_password" name="bind_password" value="{{.bind_password}}" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="req" for="user_base">{{.i18n.Tr "admin.auths.user_base"}}</label>
|
||||||
|
<input class="ipt ipt-large ipt-radius {{if .Err_UserBase}}ipt-error{{end}}" id="user_base" name="user_base" value="{{.user_base}}" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="req" for="filter">{{.i18n.Tr "admin.auths.filter"}}</label>
|
||||||
|
<input class="ipt ipt-large ipt-radius {{if .Err_Filter}}ipt-error{{end}}" id="filter" name="filter" value="{{.filter}}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="attribute_name">{{.i18n.Tr "admin.auths.attribute_name"}}</label>
|
<label for="attribute_name">{{.i18n.Tr "admin.auths.attribute_name"}}</label>
|
||||||
|
@ -59,14 +67,6 @@
|
||||||
<label class="req" for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label>
|
<label class="req" for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label>
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_AttributeMail}}ipt-error{{end}}" id="attribute_mail" name="attribute_mail" value="{{.attribute_mail}}" />
|
<input class="ipt ipt-large ipt-radius {{if .Err_AttributeMail}}ipt-error{{end}}" id="attribute_mail" name="attribute_mail" value="{{.attribute_mail}}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
|
||||||
<label class="req" for="filter">{{.i18n.Tr "admin.auths.filter"}}</label>
|
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_Filter}}ipt-error{{end}}" id="filter" name="filter" value="{{.filter}}" />
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="req" for="ms_ad_sa">{{.i18n.Tr "admin.auths.ms_ad_sa"}}</label>
|
|
||||||
<input class="ipt ipt-large ipt-radius {{if .Err_MsAdSA}}ipt-error{{end}}" id="ms_ad_sa" name="ms_ad_sa" value="{{.ms_ad_sa}}" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="smtp hidden">
|
<div class="smtp hidden">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
Loading…
Reference in a new issue