Merge branch 'develop' of https://github.com/SergioBenitez/gogs into develop

# Conflicts:
#	modules/bindata/bindata.go
This commit is contained in:
Unknwon 2015-08-16 14:31:54 +08:00
commit 55ccb00a47
23 changed files with 225 additions and 231 deletions

View file

@ -13,17 +13,16 @@ type AuthenticationForm struct {
ID int64 `form:"id"`
Type int
Name string `binding:"Required;MaxSize(50)"`
Domain string
Host string
Port int
UseSSL bool `form:"usessl"`
BaseDN string `form:"base_dn"`
AttributeUsername string
UseSSL bool `form:"use_ssl"`
BindDN string `form:"bind_dn"`
BindPassword string
UserBase string
AttributeName string
AttributeSurname string
AttributeMail string
Filter string
MsAdSA string `form:"ms_ad_sa"`
IsActived bool
SMTPAuth string `form:"smtp_auth"`
SMTPHost string `form:"smtp_host"`

View file

@ -1,43 +1,64 @@
LDAP authentication
===================
Gogs LDAP Authentication Module
===============================
## Goal
## About
Authenticat user against LDAP directories
It will bind with the user's login/pasword and query attributs ("mail" for instance) in a pool of directory servers
The first OK wins.
If there's connection error, the server will be disabled and won't be checked again
This authentication module attempts to authorize and authenticate a user
against an LDAP server. Like most LDAP authentication systems, this module does
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
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
retrieved and passed to the Gogs login infrastructure.
## Usage
In the [security] section, set
> LDAP_AUTH = true
To use this module, add an LDAP authentication source via the Authentications
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]
> name=canonicalName
> host=hostname-or-ip
> 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))
* Host **(required)**
* The address where the LDAP server can be reached.
* Example: mydomain.com
### 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
* Define a timeout per server
* Check servers marked as "Disabled" when they'll come back online
* Find a more flexible way to define filter/MSADSAFORMAT/Attributes etc... maybe text/template ?
* Check OpenLDAP server
* SSL support ?
* User Search Base **(required)**
* The LDAP base at which user accounts will be searched for.
* Example: ou=Users,dc=mydomain,dc=com
* User Filter **(required)**
* 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

View file

@ -1,8 +1,8 @@
// Copyright github.com/juju2013. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// package ldap provide functions & structure to query a LDAP ldap directory
// Package ldap provide functions & structure to query a LDAP ldap directory
// For now, it's mainly tested again an MS Active Directory service, see README.md for more information
package ldap
@ -15,86 +15,115 @@ import (
// Basic LDAP authentication service
type Ldapsource struct {
Name string // canonical name (ie. corporate.ad)
Host string // LDAP host
Port int // port number
UseSSL bool // Use SSL
BaseDN string // Base DN
AttributeUsername string // Username attribute
AttributeName string // First name attribute
AttributeSurname string // Surname attribute
AttributeMail string // E-mail attribute
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
Name string // canonical name (ie. corporate.ad)
Host string // LDAP host
Port int // port number
UseSSL bool // Use SSL
BindDN string // DN to bind with
BindPassword string // Bind DN password
UserBase string // Base search path for users
AttributeName string // First name attribute
AttributeSurname string // Surname attribute
AttributeMail string // E-mail attribute
Filter string // Query filter to validate entry
Enabled bool // if this source is disabled
}
//Global LDAP directory pool
var (
Authensource []Ldapsource
)
// 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) {
func (ls Ldapsource) FindUserDN(name string) (string, bool) {
l, err := ldapDial(ls)
if err != nil {
log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
ls.Enabled = false
return "", "", "", "", false
return "", false
}
defer l.Close()
nx := fmt.Sprintf(ls.MsAdSAFormat, name)
err = l.Bind(nx, passwd)
if err != nil {
log.Debug("LDAP Authan failed for %s, reason: %s", nx, err.Error())
return "", "", "", "", false
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 "", false
}
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.BaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf(ls.Filter, name),
[]string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail},
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 "", false
} else if len(sr.Entries) > 1 {
log.Debug("Filter '%s' returned more than one user.", userFilter)
return "", false
}
userDN := sr.Entries[0].DN
if userDN == "" {
log.Error(4, "LDAP search was succesful, but found no DN!")
return "", false
}
return userDN, true
}
// 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()
log.Trace("Binding with userDN: %s", userDN)
err = l.Bind(userDN, passwd)
if err != nil {
log.Debug("LDAP auth. failed for %s, reason: %s", userDN, err.Error())
return "", "", "", false
}
log.Trace("Bound successfully with userDN: %s", userDN)
userFilter := fmt.Sprintf(ls.Filter, name)
search := ldap.NewSearchRequest(
userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
[]string{ls.AttributeName, ls.AttributeSurname, ls.AttributeMail},
nil)
sr, err := l.Search(search)
if err != nil {
log.Debug("LDAP Authen OK but not in filter %s", name)
return "", "", "", "", false
log.Error(4, "LDAP Search failed unexpectedly! (%s)", err.Error())
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 {
cn := sr.Entries[0].GetAttributeValue(ls.AttributeUsername)
name := sr.Entries[0].GetAttributeValue(ls.AttributeName)
sn := sr.Entries[0].GetAttributeValue(ls.AttributeSurname)
mail := sr.Entries[0].GetAttributeValue(ls.AttributeMail)
return cn, name, sn, mail, true
}
return "", "", "", "", true
name_attr := sr.Entries[0].GetAttributeValue(ls.AttributeName)
sn_attr := sr.Entries[0].GetAttributeValue(ls.AttributeSurname)
mail_attr := sr.Entries[0].GetAttributeValue(ls.AttributeMail)
return name_attr, sn_attr, mail_attr, true
}
func ldapDial(ls Ldapsource) (*ldap.Conn, error) {
if ls.UseSSL {
log.Debug("Using TLS for LDAP")
return ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port), nil)
} else {
return ldap.Dial("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port))

View file

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