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

# Conflicts:
#	modules/bindata/bindata.go
This commit is contained in:
Unknwon 2015-09-10 14:55:29 -04:00
commit cbd6276200
10 changed files with 211 additions and 146 deletions

View File

@ -817,6 +817,7 @@ auths.port = Port
auths.bind_dn = Bind DN auths.bind_dn = Bind DN
auths.bind_password = Bind Password auths.bind_password = Bind Password
auths.user_base = User Search Base auths.user_base = User Search Base
auths.user_dn = User DN
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

View File

@ -27,6 +27,7 @@ const (
NOTYPE LoginType = iota NOTYPE LoginType = iota
PLAIN PLAIN
LDAP LDAP
DLDAP
SMTP SMTP
PAM PAM
) )
@ -38,9 +39,10 @@ var (
) )
var LoginTypes = map[LoginType]string{ var LoginTypes = map[LoginType]string{
LDAP: "LDAP", LDAP: "LDAP (via BindDN)",
SMTP: "SMTP", DLDAP: "LDAP (simple auth)",
PAM: "PAM", SMTP: "SMTP",
PAM: "PAM",
} }
// Ensure structs implemented interface. // Ensure structs implemented interface.
@ -106,6 +108,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
case "type": case "type":
switch LoginType((*val).(int64)) { switch LoginType((*val).(int64)) {
case LDAP: case LDAP:
fallthrough
case DLDAP:
source.Cfg = new(LDAPConfig) source.Cfg = new(LDAPConfig)
case SMTP: case SMTP:
source.Cfg = new(SMTPConfig) source.Cfg = new(SMTPConfig)
@ -171,84 +175,74 @@ func DelLoginSource(source *LoginSource) error {
// UserSignIn validates user name and password. // UserSignIn validates user name and password.
func UserSignIn(uname, passwd string) (*User, error) { func UserSignIn(uname, passwd string) (*User, error) {
u := new(User) var u *User
if strings.Contains(uname, "@") { if strings.Contains(uname, "@") {
u = &User{Email: uname} u = &User{Email: uname}
} else { } else {
u = &User{LowerName: strings.ToLower(uname)} u = &User{LowerName: strings.ToLower(uname)}
} }
has, err := x.Get(u) userExists, err := x.Get(u)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if u.LoginType == NOTYPE && has { if userExists {
u.LoginType = PLAIN switch u.LoginType {
} case NOTYPE:
fallthrough
// For plain login, user must exist to reach this line. case PLAIN:
// Now verify password. if u.ValidatePassword(passwd) {
if u.LoginType == PLAIN { return u, nil
if !u.ValidatePassword(passwd) {
return nil, ErrUserNotExist{u.Id, u.Name}
}
return u, nil
}
if !has {
var sources []LoginSource
if err = x.UseBool().Find(&sources,
&LoginSource{IsActived: true, AllowAutoRegister: true}); err != nil {
return nil, err
}
for _, source := range sources {
if source.Type == LDAP {
u, err := LoginUserLdapSource(nil, uname, passwd,
source.ID, source.Cfg.(*LDAPConfig), true)
if err == nil {
return u, nil
}
log.Warn("Fail to login(%s) by LDAP(%s): %v", uname, source.Name, err)
} else if source.Type == SMTP {
u, err := LoginUserSMTPSource(nil, uname, passwd,
source.ID, source.Cfg.(*SMTPConfig), true)
if err == nil {
return u, nil
}
log.Warn("Fail to login(%s) by SMTP(%s): %v", uname, source.Name, err)
} else if source.Type == PAM {
u, err := LoginUserPAMSource(nil, uname, passwd,
source.ID, source.Cfg.(*PAMConfig), true)
if err == nil {
return u, nil
}
log.Warn("Fail to login(%s) by PAM(%s): %v", uname, source.Name, err)
} }
}
return nil, ErrUserNotExist{u.Id, u.Name} return nil, ErrUserNotExist{u.Id, u.Name}
default:
var source LoginSource
hasSource, err := x.Id(u.LoginSource).Get(&source)
if err != nil {
return nil, err
} else if !hasSource {
return nil, ErrLoginSourceNotExist
}
return ExternalUserLogin(u, u.LoginName, passwd, &source, false)
}
} }
var source LoginSource var sources []LoginSource
hasSource, err := x.Id(u.LoginSource).Get(&source) if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true, AllowAutoRegister: true}); err != nil {
if err != nil {
return nil, err return nil, err
} else if !hasSource { }
return nil, ErrLoginSourceNotExist
} else if !source.IsActived { for _, source := range sources {
u, err := ExternalUserLogin(nil, uname, passwd, &source, true)
if err == nil {
return u, nil
}
log.Warn("Failed to login '%s' via '%s': %v", uname, source.Name, err)
}
return nil, ErrUserNotExist{u.Id, u.Name}
}
func ExternalUserLogin(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
if !source.IsActived {
return nil, ErrLoginSourceNotActived return nil, ErrLoginSourceNotActived
} }
switch u.LoginType { switch source.Type {
case LDAP: case LDAP:
return LoginUserLdapSource(u, u.LoginName, passwd, source.ID, source.Cfg.(*LDAPConfig), false) fallthrough
case DLDAP:
return LoginUserLdapSource(u, name, passwd, source, autoRegister)
case SMTP: case SMTP:
return LoginUserSMTPSource(u, u.LoginName, passwd, source.ID, source.Cfg.(*SMTPConfig), false) return LoginUserSMTPSource(u, name, passwd, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
case PAM: case PAM:
return LoginUserPAMSource(u, u.LoginName, passwd, source.ID, source.Cfg.(*PAMConfig), false) return LoginUserPAMSource(u, name, passwd, source.ID, source.Cfg.(*PAMConfig), autoRegister)
} }
return nil, ErrUnsupportedLoginType return nil, ErrUnsupportedLoginType
} }
@ -256,8 +250,10 @@ func UserSignIn(uname, passwd string) (*User, error) {
// Create a local user if success // Create a local user if success
// 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, source *LoginSource, autoRegister bool) (*User, error) {
fn, sn, mail, admin, logged := cfg.Ldapsource.SearchEntry(name, passwd) cfg := source.Cfg.(*LDAPConfig)
directBind := (source.Type == DLDAP)
fn, sn, mail, admin, logged := cfg.Ldapsource.SearchEntry(name, passwd, directBind)
if !logged { if !logged {
// User not in LDAP, do nothing // User not in LDAP, do nothing
return nil, ErrUserNotExist{0, name} return nil, ErrUserNotExist{0, name}
@ -276,8 +272,8 @@ func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAP
LowerName: strings.ToLower(name), LowerName: strings.ToLower(name),
Name: name, Name: name,
FullName: fn + " " + sn, FullName: fn + " " + sn,
LoginType: LDAP, LoginType: source.Type,
LoginSource: sourceId, LoginSource: source.ID,
LoginName: name, LoginName: name,
Passwd: passwd, Passwd: passwd,
Email: mail, Email: mail,

View File

@ -19,6 +19,7 @@ type AuthenticationForm struct {
BindDN string `form:"bind_dn"` BindDN string `form:"bind_dn"`
BindPassword string BindPassword string
UserBase string UserBase string
UserDN string `form:"user_dn"`
AttributeName string AttributeName string
AttributeSurname string AttributeSurname string
AttributeMail string AttributeMail string

View File

@ -4,61 +4,98 @@ Gogs LDAP Authentication Module
## About ## About
This authentication module attempts to authorize and authenticate a user This authentication module attempts to authorize and authenticate a user
against an LDAP server. Like most LDAP authentication systems, this module does against an LDAP server. It provides two methods of authentication: LDAP via
this in two steps. First, it queries the LDAP server using a Bind DN and BindDN, and LDAP simple authentication.
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 LDAP via BindDN functions like most LDAP authentication systems. First, it
this succeeds, the user has been authenticated, and his account information is queries the LDAP server using a Bind DN and searches for the user that is
retrieved and passed to the Gogs login infrastructure. 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.
LDAP simple authentication does not utilize a Bind DN. Instead, it binds
directly with the LDAP server using the user's supplied credentials. If the bind
succeeds and no filter rules out the user, the user is authenticated.
LDAP via BindDN is recommended for most users. By using a Bind DN, the server
can perform authorization by restricting which entries the Bind DN account can
read. Further, using a Bind DN with reduced permissions can reduce security risk
in the face of application bugs.
## Usage ## Usage
To use this module, add an LDAP authentication source via the Authentications To use this module, add an LDAP authentication source via the Authentications
section in the admin panel. The fields should be set as follows: section in the admin panel. Both the LDAP via BindDN and the simple auth LDAP
share the following fields:
* Authorization Name **(required)** * Authorization Name **(required)**
* A name to assign to the new method of authorization. * A name to assign to the new method of authorization.
* Host **(required)** * Host **(required)**
* The address where the LDAP server can be reached. * The address where the LDAP server can be reached.
* Example: mydomain.com * Example: mydomain.com
* Port **(required)** * Port **(required)**
* The port to use when connecting to the server. * The port to use when connecting to the server.
* Example: 636 * Example: 636
* Enable TLS Encryption (optional) * Enable TLS Encryption (optional)
* Whether to use TLS when connecting to the LDAP server. * Whether to use TLS when connecting to the LDAP server.
* Bind DN (optional) * Admin Filter (optional)
* The DN to bind to the LDAP server with when searching for the user. * An LDAP filter specifying if a user should be given administrator
This may be left blank to perform an anonymous search. privileges. If a user accounts passes the filter, the user will be
* Example: cn=Search,dc=mydomain,dc=com privileged as an administrator.
* Example: (objectClass=adminAccount)
* Bind Password (optional)
* The password for the Bind DN specified above, if any.
* 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) * First name attribute (optional)
* The attribute of the user's LDAP record containing the user's first * The attribute of the user's LDAP record containing the user's first name.
name. This will be used to populate their account information. This will be used to populate their account information.
* Example: givenName * Example: givenName
* Surname name attribute (optional) * Surname attribute (optional)
* The attribute of the user's LDAP record containing the user's surname * The attribute of the user's LDAP record containing the user's surname This
This will be used to populate their account information. will be used to populate their account information.
* Example: sn * Example: sn
* E-mail attribute **(required)** * E-mail attribute **(required)**
* The attribute of the user's LDAP record containing the user's email * The attribute of the user's LDAP record containing the user's email
address. This will be used to populate their account information. address. This will be used to populate their account information.
* Example: mail * Example: mail
**LDAP via BindDN** adds the following fields:
* 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
* Bind Password (optional)
* The password for the Bind DN specified above, if any. _Note: The password
is stored in plaintext at the server. As such, ensure that your Bind DN
has as few privileges as possible._
* 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))
**LDAP using simple auth** adds the following fields:
* User DN **(required)**
* A template to use as the user's DN. The `%s` matching parameter will be
substituted with the user's username.
* Example: cn=%s,ou=Users,dc=mydomain,dc=com
* Example: uid=%s,ou=Users,dc=mydomain,dc=com
* User Filter **(required)**
* An LDAP filter declaring when a user should be allowed to log in. The `%s`
matching parameter will be substituted with the user's username.
* Example: (&(objectClass=posixAccount)(cn=%s))
* Example: (&(objectClass=posixAccount)(uid=%s))

View File

@ -22,6 +22,7 @@ type Ldapsource struct {
BindDN string // DN to bind with BindDN string // DN to bind with
BindPassword string // Bind DN password BindPassword string // Bind DN password
UserBase string // Base search path for users UserBase string // Base search path for users
UserDN string // Template for the DN of the user for simple auth
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
@ -78,10 +79,19 @@ func (ls Ldapsource) FindUserDN(name string) (string, bool) {
} }
// searchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter // 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, bool) { func (ls Ldapsource) SearchEntry(name, passwd string, directBind bool) (string, string, string, bool, bool) {
userDN, found := ls.FindUserDN(name) var userDN string
if !found { if directBind {
return "", "", "", false, false log.Trace("LDAP will bind directly via UserDN template: %s", ls.UserDN)
userDN = fmt.Sprintf(ls.UserDN, name)
} else {
log.Trace("LDAP will use BindDN.")
var found bool
userDN, found = ls.FindUserDN(name)
if !found {
return "", "", "", false, false
}
} }
l, err := ldapDial(ls) l, err := ldapDial(ls)
@ -112,7 +122,12 @@ func (ls Ldapsource) SearchEntry(name, passwd string) (string, string, string, b
log.Error(4, "LDAP Search failed unexpectedly! (%v)", err) log.Error(4, "LDAP Search failed unexpectedly! (%v)", err)
return "", "", "", false, false return "", "", "", false, false
} else if len(sr.Entries) < 1 { } else if len(sr.Entries) < 1 {
log.Error(4, "LDAP Search failed unexpectedly! (0 entries)") if directBind {
log.Error(4, "User filter inhibited user login.")
} else {
log.Error(4, "LDAP Search failed unexpectedly! (0 entries)")
}
return "", "", "", false, false return "", "", "", false, false
} }

File diff suppressed because one or more lines are too long

View File

@ -57,10 +57,10 @@ var Gogs = {};
}); });
$.fn.extend({ $.fn.extend({
toggleHide: function () { toggleHide: function () {
$(this).addClass("hidden"); $(this).each(function(n, v) { $(v).addClass("hidden"); });
}, },
toggleShow: function () { toggleShow: function () {
$(this).removeClass("hidden"); $(this).each(function(n, v) { $(v).removeClass("hidden"); });
}, },
toggleAjax: function (successCallback, errorCallback) { toggleAjax: function (successCallback, errorCallback) {
var url = $(this).data("ajax"); var url = $(this).data("ajax");
@ -775,24 +775,20 @@ function initAdmin() {
$form.attr('action', $form.data('delete-url')); $form.attr('action', $form.data('delete-url'));
}); });
// Create authorization. // Create authorization. Keep list in sync with models/login.go.
var all_auths = ['none', 'plain', 'ldap', 'dldap', 'smtp', 'pam'];
$('#auth-type').on("change", function () { $('#auth-type').on("change", function () {
var v = $(this).val(); var v = $(this).val();
if (v == 2) { if (v >= all_auths.length) return;
$('.ldap').toggleShow();
$('.smtp').toggleHide(); // Hide all through their class names.
$('.pam').toggleHide(); $.each(all_auths, function(i, type) {
} $('.' + type).toggleHide();
if (v == 3) { });
$('.smtp').toggleShow();
$('.ldap').toggleHide(); // Show the selected one.
$('.pam').toggleHide(); var selected = all_auths[v];
} $('.' + selected).toggleShow();
if (v == 4) {
$('.pam').toggleShow();
$('.smtp').toggleHide();
$('.ldap').toggleHide();
}
}); });
// Delete authorization. // Delete authorization.

View File

@ -61,6 +61,8 @@ func NewAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
var u core.Conversion var u core.Conversion
switch models.LoginType(form.Type) { switch models.LoginType(form.Type) {
case models.LDAP: case models.LDAP:
fallthrough
case models.DLDAP:
u = &models.LDAPConfig{ u = &models.LDAPConfig{
Ldapsource: ldap.Ldapsource{ Ldapsource: ldap.Ldapsource{
Name: form.Name, Name: form.Name,
@ -68,13 +70,14 @@ func NewAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
Port: form.Port, Port: form.Port,
UseSSL: form.UseSSL, UseSSL: form.UseSSL,
BindDN: form.BindDN, BindDN: form.BindDN,
UserDN: form.UserDN,
BindPassword: form.BindPassword, BindPassword: form.BindPassword,
UserBase: form.UserBase, UserBase: form.UserBase,
Filter: form.Filter,
AdminFilter: form.AdminFilter,
AttributeName: form.AttributeName, AttributeName: form.AttributeName,
AttributeSurname: form.AttributeSurname, AttributeSurname: form.AttributeSurname,
AttributeMail: form.AttributeMail, AttributeMail: form.AttributeMail,
Filter: form.Filter,
AdminFilter: form.AdminFilter,
Enabled: true, Enabled: true,
}, },
} }
@ -149,6 +152,8 @@ func EditAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
var config core.Conversion var config core.Conversion
switch models.LoginType(form.Type) { switch models.LoginType(form.Type) {
case models.LDAP: case models.LDAP:
fallthrough
case models.DLDAP:
config = &models.LDAPConfig{ config = &models.LDAPConfig{
Ldapsource: ldap.Ldapsource{ Ldapsource: ldap.Ldapsource{
Name: form.Name, Name: form.Name,
@ -156,6 +161,7 @@ func EditAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) {
Port: form.Port, Port: form.Port,
UseSSL: form.UseSSL, UseSSL: form.UseSSL,
BindDN: form.BindDN, BindDN: form.BindDN,
UserDN: form.UserDN,
BindPassword: form.BindPassword, BindPassword: form.BindPassword,
UserBase: form.UserBase, UserBase: form.UserBase,
AttributeName: form.AttributeName, AttributeName: form.AttributeName,

View File

@ -30,7 +30,7 @@
<input class="ipt ipt-large ipt-radius {{if .Err_AuthName}}ipt-error{{end}}" id="name" name="name" value="{{.Source.Name}}" required /> <input class="ipt ipt-large ipt-radius {{if .Err_AuthName}}ipt-error{{end}}" id="name" name="name" value="{{.Source.Name}}" required />
</div> </div>
{{if eq $type 2}} {{if eq $type 2 3}}
<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 />
@ -43,6 +43,7 @@
<label for="use_ssl">{{.i18n.Tr "admin.auths.enable_tls"}}</label> <label for="use_ssl">{{.i18n.Tr "admin.auths.enable_tls"}}</label>
<input name="use_ssl" type="checkbox" {{if .Source.LDAP.UseSSL}}checked{{end}}> <input name="use_ssl" type="checkbox" {{if .Source.LDAP.UseSSL}}checked{{end}}>
</div> </div>
{{if eq $type 2}}
<div class="field"> <div class="field">
<label for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label> <label for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label>
<input class="ipt ipt-large ipt-radius {{if .Err_BindDN}}ipt-error{{end}}" id="bind_dn" name="bind_dn" value="{{.Source.LDAP.BindDN}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_BindDN}}ipt-error{{end}}" id="bind_dn" name="bind_dn" value="{{.Source.LDAP.BindDN}}" />
@ -55,6 +56,13 @@
<label class="req" for="user_base">{{.i18n.Tr "admin.auths.user_base"}}</label> <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}}" /> <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>
{{end}}
{{if eq $type 3}}
<div class="field">
<label class="req" for="user_dn">{{.i18n.Tr "admin.auths.user_dn"}}</label>
<input class="ipt ipt-large ipt-radius {{if .Err_UserDN}}ipt-error{{end}}" id="user_dn" name="user_dn" value="{{.Source.LDAP.UserDN}}" />
</div>
{{end}}
<div class="field"> <div class="field">
<label class="req" for="filter">{{.i18n.Tr "admin.auths.filter"}}</label> <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}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_Filter}}ipt-error{{end}}" id="filter" name="filter" value="{{.Source.LDAP.Filter}}" />
@ -76,7 +84,8 @@
<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>
{{else if eq $type 3}}
{{else if eq $type 4}}
<div class="field"> <div class="field">
<label class="req">{{.i18n.Tr "admin.auths.smtp_auth"}}</label> <label class="req">{{.i18n.Tr "admin.auths.smtp_auth"}}</label>
<select name="smtp_auth"> <select name="smtp_auth">
@ -96,7 +105,7 @@
<input class="ipt ipt-large ipt-radius {{if .Err_SmtpPort}}ipt-error{{end}}" id="smtp_port" name="smtp_port" value="{{.Source.SMTP.Port}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_SmtpPort}}ipt-error{{end}}" id="smtp_port" name="smtp_port" value="{{.Source.SMTP.Port}}" />
</div> </div>
{{else if eq $type 4}} {{else if eq $type 5}}
<div class="field"> <div class="field">
<label class="req" for="pam_service_name">{{.i18n.Tr "admin.auths.pam_service_name"}}</label> <label class="req" for="pam_service_name">{{.i18n.Tr "admin.auths.pam_service_name"}}</label>
<input class="ipt ipt-large ipt-radius {{if .Err_PAMServiceName}}ipt-error{{end}}" id="pam_service_name" name="pam_service_name" value="{{.Source.PAM.ServiceName}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_PAMServiceName}}ipt-error{{end}}" id="pam_service_name" name="pam_service_name" value="{{.Source.PAM.ServiceName}}" />
@ -104,7 +113,7 @@
{{end}} {{end}}
<div class="field"> <div class="field">
{{if eq $type 3}} {{if eq $type 4}}
<label></label> <label></label>
<input name="tls" type="checkbox" {{if .Source.SMTP.TLS}}checked{{end}}> <input name="tls" type="checkbox" {{if .Source.SMTP.TLS}}checked{{end}}>
<strong>{{.i18n.Tr "admin.auths.enable_tls"}}</strong> <strong>{{.i18n.Tr "admin.auths.enable_tls"}}</strong>

View File

@ -26,48 +26,52 @@
<label class="req" for="name">{{.i18n.Tr "admin.auths.auth_name"}}</label> <label class="req" for="name">{{.i18n.Tr "admin.auths.auth_name"}}</label>
<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="dldap ldap">
<div class="field"> <div class="dldap ldap 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}}" />
</div> </div>
<div class="field"> <div class="dldap ldap field">
<label class="req" for="port">{{.i18n.Tr "admin.auths.port"}}</label> <label class="req" for="port">{{.i18n.Tr "admin.auths.port"}}</label>
<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="dldap ldap field">
<label for="use_ssl">{{.i18n.Tr "admin.auths.enable_tls"}}</label> <label for="use_ssl">{{.i18n.Tr "admin.auths.enable_tls"}}</label>
<input name="use_ssl" type="checkbox" {{if .use_ssl}}checked{{end}}> <input name="use_ssl" type="checkbox" {{if .use_ssl}}checked{{end}}>
</div> </div>
<div class="field"> <div class="ldap field">
<label class="req" for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label> <label class="req" for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label>
<input class="ipt ipt-large ipt-radius {{if .Err_BindDN}}ipt-error{{end}}" id="bind_dn" name="bind_dn" value="{{.bind_dn}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_BindDN}}ipt-error{{end}}" id="bind_dn" name="bind_dn" value="{{.bind_dn}}" />
</div> </div>
<div class="field"> <div class="ldap field">
<label class="req" for="bind_password">{{.i18n.Tr "admin.auths.bind_password"}}</label> <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" type="password" value="{{.bind_password}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_BindPassword}}ipt-error{{end}}" id="bind_password" name="bind_password" type="password" value="{{.bind_password}}" />
</div> </div>
<div class="field"> <div class="ldap field">
<label class="req" for="user_base">{{.i18n.Tr "admin.auths.user_base"}}</label> <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}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_UserBase}}ipt-error{{end}}" id="user_base" name="user_base" value="{{.user_base}}" />
</div> </div>
<div class="field"> <div class="dldap field hidden">
<label class="req" for="user_dn">{{.i18n.Tr "admin.auths.user_dn"}}</label>
<input class="ipt ipt-large ipt-radius {{if .Err_UserDN}}ipt-error{{end}}" id="user_dn" name="user_dn" value="{{.user_dn}}" />
</div>
<div class="dldap ldap field">
<label class="req" for="filter">{{.i18n.Tr "admin.auths.filter"}}</label> <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}}" /> <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="dldap ldap field">
<label for="filter">{{.i18n.Tr "admin.auths.admin_filter"}}</label> <label for="filter">{{.i18n.Tr "admin.auths.admin_filter"}}</label>
<input class="ipt ipt-large ipt-radius {{if .Err_AdminFilter}}ipt-error{{end}}" id="admin_filter" name="admin_filter" value="{{.admin_filter}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_AdminFilter}}ipt-error{{end}}" id="admin_filter" name="admin_filter" value="{{.admin_filter}}" />
</div> </div>
<div class="field"> <div class="dldap ldap field">
<label for="attribute_name">{{.i18n.Tr "admin.auths.attribute_name"}}</label> <label for="attribute_name">{{.i18n.Tr "admin.auths.attribute_name"}}</label>
<input class="ipt ipt-large ipt-radius {{if .Err_AttributeName}}ipt-error{{end}}" id="attribute_name" name="attribute_name" value="{{.attribute_name}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_AttributeName}}ipt-error{{end}}" id="attribute_name" name="attribute_name" value="{{.attribute_name}}" />
</div> </div>
<div class="field"> <div class="dldap ldap field">
<label for="attribute_surname">{{.i18n.Tr "admin.auths.attribute_surname"}}</label> <label for="attribute_surname">{{.i18n.Tr "admin.auths.attribute_surname"}}</label>
<input class="ipt ipt-large ipt-radius {{if .Err_AttributeSurname}}ipt-error{{end}}" id="attribute_surname" name="attribute_surname" value="{{.attribute_surname}}" /> <input class="ipt ipt-large ipt-radius {{if .Err_AttributeSurname}}ipt-error{{end}}" id="attribute_surname" name="attribute_surname" value="{{.attribute_surname}}" />
</div> </div>
<div class="field"> <div class="dldap ldap field">
<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>