Add more LDAP options

This commit is contained in:
cuigh 2017-11-09 17:16:18 +08:00
parent 902c2bca7c
commit 0ff76725bb
12 changed files with 140 additions and 27 deletions

View File

@ -1849,6 +1849,16 @@ var Swirl;
let enabled = $(e.target).prop("checked");
$("#fs-ldap").find("input:not(:checkbox)").prop("readonly", !enabled);
});
$("#ldap-auth-simple,#ldap-auth-bind").click(e => {
if ($(e.target).val() == "0") {
$("#div-auth-simple").show();
$("#div-auth-bind").hide();
}
else {
$("#div-auth-simple").hide();
$("#div-auth-bind").show();
}
});
}
}
Setting.IndexPage = IndexPage;

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,7 @@ namespace Swirl.Core {
}
/**
* Dispatcher
* Create a Dispatcher instance
*
* @param elem
* @param event
@ -23,11 +23,11 @@ namespace Swirl.Core {
}
/**
*
* Register event
*
* @param action
* @param handler
* @returns {Mtime.Util.Dispatcher}
* @returns {Swirl.Core.Dispatcher}
*/
on(action: string, handler: (e: JQueryEventObject) => any): Dispatcher {
this.events[action] = handler;
@ -35,10 +35,10 @@ namespace Swirl.Core {
}
/**
*
* Unregister event
*
* @param action
* @returns {Mtime.Util.Dispatcher}
* @returns {Swirl.Core.Dispatcher}
*/
off(action: string): Dispatcher {
delete this.events[action];
@ -46,11 +46,11 @@ namespace Swirl.Core {
}
/**
*
* Bind events to element
*
* @param elem
* @param event
* @returns {Mtime.Util.Dispatcher}
* @returns {Swirl.Core.Dispatcher}
*/
bind(elem: string | JQuery | Element | Document, event: string = "click"): Dispatcher {
$(elem).on(event, this.handle.bind(this));

View File

@ -77,7 +77,7 @@ namespace Swirl.Core {
* Submit form by AJAX
*
* @param {string} url submit url
* @returns {Mtime.Net.AjaxPostRequest}
* @returns {Swirl.Core.AjaxPostRequest}
*
* @memberOf Form
*/

View File

@ -5,6 +5,15 @@ namespace Swirl.Setting {
let enabled = $(e.target).prop("checked");
$("#fs-ldap").find("input:not(:checkbox)").prop("readonly", !enabled);
});
$("#ldap-auth-simple,#ldap-auth-bind").click(e => {
if ($(e.target).val() == "0") {
$("#div-auth-simple").show();
$("#div-auth-bind").hide();
} else {
$("#div-auth-simple").hide();
$("#div-auth-bind").show();
}
});
}
}
}

View File

@ -14,6 +14,7 @@ var Setting = &settingBiz{}
type settingBiz struct {
}
// Get returns settings of swirl. If not found, default settings will be returned.
func (b *settingBiz) Get() (setting *model.Setting, err error) {
do(func(d dao.Interface) {
setting, err = d.SettingGet()

View File

@ -193,6 +193,7 @@ func (b *userBiz) loginInternal(user *model.User, pwd string) error {
return nil
}
// TODO: support tls and bind auth
func (b *userBiz) loginLDAP(d dao.Interface, user *model.User, pwd string) error {
setting, err := Setting.Get()
if err != nil {
@ -210,7 +211,9 @@ func (b *userBiz) loginLDAP(d dao.Interface, user *model.User, pwd string) error
defer l.Close()
// bind
err = l.Bind(user.LoginName, pwd)
//err = l.Bind(user.LoginName, pwd)
//err = l.Bind(setting.LDAP.BindDN, setting.LDAP.BindPassword) // bind auth
err = l.Bind(fmt.Sprintf(setting.LDAP.UserDN, user.LoginName), pwd) // simple auth
if err != nil {
log.Get("user").Error("Login by LDAP failed: ", err)
return ErrIncorrectAuth
@ -224,7 +227,7 @@ func (b *userBiz) loginLDAP(d dao.Interface, user *model.User, pwd string) error
// If user wasn't exist, we need create it
req := ldap.NewSearchRequest(
setting.LDAP.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=organizationalPerson)(userPrincipalName=%s))", user.LoginName),
fmt.Sprintf(setting.LDAP.UserFilter, user.LoginName),
[]string{setting.LDAP.NameAttr, setting.LDAP.EmailAttr},
nil,
)
@ -232,8 +235,10 @@ func (b *userBiz) loginLDAP(d dao.Interface, user *model.User, pwd string) error
if err != nil {
return err
}
if len(sr.Entries) == 0 {
if length := len(sr.Entries); length == 0 {
return ErrIncorrectAuth
} else if length > 1 {
return errors.New("Found more than one account when using LDAP authentication")
}
entry := sr.Entries[0]

View File

@ -5,6 +5,7 @@ import (
"strconv"
"strings"
"github.com/cuigh/auxo/data"
"github.com/cuigh/auxo/errors"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/auxo/util/cast"
@ -12,7 +13,6 @@ import (
"github.com/cuigh/swirl/biz/docker"
"github.com/cuigh/swirl/misc"
"github.com/cuigh/swirl/model"
"mtime.com/auxo/data/set"
)
// ServiceController is a controller of docker service
@ -175,7 +175,11 @@ func serviceNew(ctx web.Context) error {
if err != nil {
return err
}
checkedNetworks := set.FromSlice(service.Networks, func(i int) interface{} { return service.Networks[i] })
checkedNetworks := data.NewSet()
checkedNetworks.AddSlice(service.Networks, func(i int) interface{} {
return service.Networks[i]
})
m := newModel(ctx).Set("Service", service).Set("Registries", registries).
Set("Networks", networks).Set("CheckedNetworks", checkedNetworks).
@ -230,7 +234,8 @@ func serviceEdit(ctx web.Context) error {
}
stack := service.Spec.Labels["com.docker.stack.namespace"]
checkedNetworks := set.FromSlice(service.Endpoint.VirtualIPs, func(i int) interface{} { return service.Endpoint.VirtualIPs[i].NetworkID })
checkedNetworks := data.NewSet()
checkedNetworks.AddSlice(service.Endpoint.VirtualIPs, func(i int) interface{} { return service.Endpoint.VirtualIPs[i].NetworkID })
m := newModel(ctx).Set("Service", model.NewServiceInfo(service)).Set("Stack", stack).
Set("Networks", networks).Set("CheckedNetworks", checkedNetworks).

View File

@ -29,7 +29,7 @@ func main() {
misc.LoadOptions()
app.Run(server())
}
app.Register(flag.All)
app.Flags.Register(flag.All)
app.Start()
}

View File

@ -2,13 +2,22 @@ package model
import "time"
// Setting represents the options of swirl.
type Setting struct {
LDAP struct {
Enabled bool `bson:"enabled" json:"enabled,omitempty"`
Address string `bson:"address" json:"address,omitempty"`
BaseDN string `bson:"base_dn" json:"base_dn,omitempty"`
NameAttr string `bson:"name_attr" json:"name_attr,omitempty"`
EmailAttr string `bson:"email_attr" json:"email_attr,omitempty"`
Enabled bool `bson:"enabled" json:"enabled,omitempty"`
Address string `bson:"address" json:"address,omitempty"`
Security int32 `bson:"security" json:"security,omitempty"` // 0-None/1-TLS/2-StartTLS
TLSCert string `bson:"tls_cert" json:"tls_cert,omitempty"` // TLS cert
TLSVerify bool `bson:"tls_verify" json:"tls_verify,omitempty"` // Verify cert
Authentication int32 `bson:"auth" json:"auth,omitempty"` // 0-Bind/1-Simple
BindDN string `bson:"bind_dn" json:"bind_dn,omitempty"` // DN to bind with
BindPassword string `bson:"bind_pwd" json:"bind_pwd,omitempty"` // Bind DN password
BaseDN string `bson:"base_dn" json:"base_dn,omitempty"` // Base search path for users
UserDN string `bson:"user_dn" json:"user_dn,omitempty"` // Template for the DN of the user for simple auth
UserFilter string `bson:"user_filter" json:"user_filter,omitempty"` // Search filter for user
NameAttr string `bson:"name_attr" json:"name_attr,omitempty"`
EmailAttr string `bson:"email_attr" json:"email_attr,omitempty"`
} `bson:"ldap" json:"ldap,omitempty"`
TimeZone struct {
Name string `bson:"name" json:"name,omitempty"` // Asia/Shanghai

View File

@ -9,7 +9,7 @@
{{ end }}
{{ block radio(id, name, value, label, checked, disabled=false) }}
<input id="{{id ? id : (name + "-" + value)}}" name="{{name}}" value="{{value}}" type="radio" class="is-radio"{{if checked == value}} checked{{end}}{{if disabled}} disabled{{end}}{{yield content}}>
<input id="{{id ? id : (name + "-" + value)}}" name="{{name}}" value="{{value}}" type="radio" class="is-radio"{{if eq(checked, value)}} checked{{end}}{{if disabled}} disabled{{end}}{{yield content}}>
<label for="{{id ? id : (name + "-" + value)}}">{{label}}</label>
{{ end }}

View File

@ -65,6 +65,68 @@
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Security</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
{{ yield radio(id="ldap.security-none", name="ldap.security", value="0", label="None", checked=.Setting.LDAP.Security) content}} data-type="integer"{{end}}
{{ yield radio(id="ldap.security-tls", name="ldap.security", value="1", label="TLS", disabled=true) content}} data-type="integer"{{end}}
{{ yield radio(id="ldap.security-starttls", name="ldap.security", value="2", label="StartTLS", disabled=true) content}} data-type="integer"{{end}}
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Authentication</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
{{ yield radio(id="ldap-auth-simple", name="ldap.auth", value="0", label="Simple", checked=.Setting.LDAP.Authentication) content}} data-type="integer"{{end}}
{{ yield radio(id="ldap-auth-bind", name="ldap.auth", value="1", label="Bind", checked=.Setting.LDAP.Authentication, disabled=true) content}} data-type="integer"{{end}}
</div>
</div>
</div>
</div>
<div id="div-auth-simple" class="field is-horizontal"{{ if .Setting.LDAP.Authentication == 1 }} style="display: none" {{ end }}>
<div class="field-label is-normal">
<label class="label">User DN</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input name="ldap.user_dn" value="{{ .Setting.LDAP.UserDN }}" class="input" type="text" placeholder="e.g. cn=%s,dc=xxx,dc=com">
</div>
</div>
</div>
</div>
<div id="div-auth-bind" class="field is-horizontal"{{ if .Setting.LDAP.Authentication == 0 }} style="display: none" {{ end }}>
<div class="field-label is-normal">
<label class="label">Bind</label>
</div>
<div class="field-body">
<div class="field has-addons">
<p class="control">
<a class="button is-static">DN</a>
</p>
<div class="control is-expanded">
<input name="ldap.bind_dn" value="{{ .Setting.LDAP.BindDN }}" class="input" type="text" placeholder="e.g. cn=search,dc=xxx,dc=com">
</div>
</div>
<div class="field has-addons">
<p class="control">
<a class="button is-static">Password</a>
</p>
<div class="control is-expanded">
<input name="ldap.bind_pwd" value="{{ .Setting.LDAP.BindPassword }}" class="input" type="password" autocomplete="new-password" placeholder="Bind DN password">
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Base DN</label>
@ -79,23 +141,35 @@
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Username attribute</label>
<label class="label">User filter</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input name="ldap.name_attr" value="{{ .Setting.LDAP.NameAttr }}" class="input" type="text" placeholder="e.g. displayName">
<input name="ldap.user_filter" value="{{ .Setting.LDAP.UserFilter }}" class="input" type="text" placeholder="e.g. (&(objectClass=organizationalPerson)(sAMAccountName=%s))">
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Email attribute</label>
<label class="label">Attributes</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<div class="field has-addons">
<p class="control">
<a class="button is-static">Username</a>
</p>
<div class="control is-expanded">
<input name="ldap.name_attr" value="{{ .Setting.LDAP.NameAttr }}" class="input" type="text" placeholder="e.g. displayName">
</div>
</div>
<div class="field has-addons">
<p class="control">
<a class="button is-static">Email</a>
</p>
<div class="control is-expanded">
<input name="ldap.email_attr" value="{{ .Setting.LDAP.EmailAttr }}" class="input" type="text" placeholder="e.g. mail">
</div>
</div>