mirror of
https://github.com/cuigh/swirl
synced 2024-12-31 16:23:13 +00:00
Add more LDAP options
This commit is contained in:
parent
902c2bca7c
commit
0ff76725bb
@ -1849,6 +1849,16 @@ var Swirl;
|
|||||||
let enabled = $(e.target).prop("checked");
|
let enabled = $(e.target).prop("checked");
|
||||||
$("#fs-ldap").find("input:not(:checkbox)").prop("readonly", !enabled);
|
$("#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;
|
Setting.IndexPage = IndexPage;
|
||||||
|
File diff suppressed because one or more lines are too long
@ -12,7 +12,7 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 Dispatcher 并绑定事件到页面元素上
|
* Create a Dispatcher instance
|
||||||
*
|
*
|
||||||
* @param elem
|
* @param elem
|
||||||
* @param event
|
* @param event
|
||||||
@ -23,11 +23,11 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册动作事件
|
* Register event
|
||||||
*
|
*
|
||||||
* @param action
|
* @param action
|
||||||
* @param handler
|
* @param handler
|
||||||
* @returns {Mtime.Util.Dispatcher}
|
* @returns {Swirl.Core.Dispatcher}
|
||||||
*/
|
*/
|
||||||
on(action: string, handler: (e: JQueryEventObject) => any): Dispatcher {
|
on(action: string, handler: (e: JQueryEventObject) => any): Dispatcher {
|
||||||
this.events[action] = handler;
|
this.events[action] = handler;
|
||||||
@ -35,10 +35,10 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除动作事件
|
* Unregister event
|
||||||
*
|
*
|
||||||
* @param action
|
* @param action
|
||||||
* @returns {Mtime.Util.Dispatcher}
|
* @returns {Swirl.Core.Dispatcher}
|
||||||
*/
|
*/
|
||||||
off(action: string): Dispatcher {
|
off(action: string): Dispatcher {
|
||||||
delete this.events[action];
|
delete this.events[action];
|
||||||
@ -46,11 +46,11 @@ namespace Swirl.Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定事件到页面元素上
|
* Bind events to element
|
||||||
*
|
*
|
||||||
* @param elem
|
* @param elem
|
||||||
* @param event
|
* @param event
|
||||||
* @returns {Mtime.Util.Dispatcher}
|
* @returns {Swirl.Core.Dispatcher}
|
||||||
*/
|
*/
|
||||||
bind(elem: string | JQuery | Element | Document, event: string = "click"): Dispatcher {
|
bind(elem: string | JQuery | Element | Document, event: string = "click"): Dispatcher {
|
||||||
$(elem).on(event, this.handle.bind(this));
|
$(elem).on(event, this.handle.bind(this));
|
||||||
|
@ -77,7 +77,7 @@ namespace Swirl.Core {
|
|||||||
* Submit form by AJAX
|
* Submit form by AJAX
|
||||||
*
|
*
|
||||||
* @param {string} url submit url
|
* @param {string} url submit url
|
||||||
* @returns {Mtime.Net.AjaxPostRequest}
|
* @returns {Swirl.Core.AjaxPostRequest}
|
||||||
*
|
*
|
||||||
* @memberOf Form
|
* @memberOf Form
|
||||||
*/
|
*/
|
||||||
|
@ -5,6 +5,15 @@ namespace Swirl.Setting {
|
|||||||
let enabled = $(e.target).prop("checked");
|
let enabled = $(e.target).prop("checked");
|
||||||
$("#fs-ldap").find("input:not(:checkbox)").prop("readonly", !enabled);
|
$("#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();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,6 +14,7 @@ var Setting = &settingBiz{}
|
|||||||
type settingBiz struct {
|
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) {
|
func (b *settingBiz) Get() (setting *model.Setting, err error) {
|
||||||
do(func(d dao.Interface) {
|
do(func(d dao.Interface) {
|
||||||
setting, err = d.SettingGet()
|
setting, err = d.SettingGet()
|
||||||
|
11
biz/user.go
11
biz/user.go
@ -193,6 +193,7 @@ func (b *userBiz) loginInternal(user *model.User, pwd string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: support tls and bind auth
|
||||||
func (b *userBiz) loginLDAP(d dao.Interface, user *model.User, pwd string) error {
|
func (b *userBiz) loginLDAP(d dao.Interface, user *model.User, pwd string) error {
|
||||||
setting, err := Setting.Get()
|
setting, err := Setting.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -210,7 +211,9 @@ func (b *userBiz) loginLDAP(d dao.Interface, user *model.User, pwd string) error
|
|||||||
defer l.Close()
|
defer l.Close()
|
||||||
|
|
||||||
// bind
|
// 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 {
|
if err != nil {
|
||||||
log.Get("user").Error("Login by LDAP failed: ", err)
|
log.Get("user").Error("Login by LDAP failed: ", err)
|
||||||
return ErrIncorrectAuth
|
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
|
// If user wasn't exist, we need create it
|
||||||
req := ldap.NewSearchRequest(
|
req := ldap.NewSearchRequest(
|
||||||
setting.LDAP.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
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},
|
[]string{setting.LDAP.NameAttr, setting.LDAP.EmailAttr},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
@ -232,8 +235,10 @@ func (b *userBiz) loginLDAP(d dao.Interface, user *model.User, pwd string) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(sr.Entries) == 0 {
|
if length := len(sr.Entries); length == 0 {
|
||||||
return ErrIncorrectAuth
|
return ErrIncorrectAuth
|
||||||
|
} else if length > 1 {
|
||||||
|
return errors.New("Found more than one account when using LDAP authentication")
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := sr.Entries[0]
|
entry := sr.Entries[0]
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cuigh/auxo/data"
|
||||||
"github.com/cuigh/auxo/errors"
|
"github.com/cuigh/auxo/errors"
|
||||||
"github.com/cuigh/auxo/net/web"
|
"github.com/cuigh/auxo/net/web"
|
||||||
"github.com/cuigh/auxo/util/cast"
|
"github.com/cuigh/auxo/util/cast"
|
||||||
@ -12,7 +13,6 @@ import (
|
|||||||
"github.com/cuigh/swirl/biz/docker"
|
"github.com/cuigh/swirl/biz/docker"
|
||||||
"github.com/cuigh/swirl/misc"
|
"github.com/cuigh/swirl/misc"
|
||||||
"github.com/cuigh/swirl/model"
|
"github.com/cuigh/swirl/model"
|
||||||
"mtime.com/auxo/data/set"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceController is a controller of docker service
|
// ServiceController is a controller of docker service
|
||||||
@ -175,7 +175,11 @@ func serviceNew(ctx web.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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).
|
m := newModel(ctx).Set("Service", service).Set("Registries", registries).
|
||||||
Set("Networks", networks).Set("CheckedNetworks", checkedNetworks).
|
Set("Networks", networks).Set("CheckedNetworks", checkedNetworks).
|
||||||
@ -230,7 +234,8 @@ func serviceEdit(ctx web.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stack := service.Spec.Labels["com.docker.stack.namespace"]
|
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).
|
m := newModel(ctx).Set("Service", model.NewServiceInfo(service)).Set("Stack", stack).
|
||||||
Set("Networks", networks).Set("CheckedNetworks", checkedNetworks).
|
Set("Networks", networks).Set("CheckedNetworks", checkedNetworks).
|
||||||
|
2
main.go
2
main.go
@ -29,7 +29,7 @@ func main() {
|
|||||||
misc.LoadOptions()
|
misc.LoadOptions()
|
||||||
app.Run(server())
|
app.Run(server())
|
||||||
}
|
}
|
||||||
app.Register(flag.All)
|
app.Flags.Register(flag.All)
|
||||||
app.Start()
|
app.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,13 +2,22 @@ package model
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
|
// Setting represents the options of swirl.
|
||||||
type Setting struct {
|
type Setting struct {
|
||||||
LDAP struct {
|
LDAP struct {
|
||||||
Enabled bool `bson:"enabled" json:"enabled,omitempty"`
|
Enabled bool `bson:"enabled" json:"enabled,omitempty"`
|
||||||
Address string `bson:"address" json:"address,omitempty"`
|
Address string `bson:"address" json:"address,omitempty"`
|
||||||
BaseDN string `bson:"base_dn" json:"base_dn,omitempty"`
|
Security int32 `bson:"security" json:"security,omitempty"` // 0-None/1-TLS/2-StartTLS
|
||||||
NameAttr string `bson:"name_attr" json:"name_attr,omitempty"`
|
TLSCert string `bson:"tls_cert" json:"tls_cert,omitempty"` // TLS cert
|
||||||
EmailAttr string `bson:"email_attr" json:"email_attr,omitempty"`
|
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"`
|
} `bson:"ldap" json:"ldap,omitempty"`
|
||||||
TimeZone struct {
|
TimeZone struct {
|
||||||
Name string `bson:"name" json:"name,omitempty"` // Asia/Shanghai
|
Name string `bson:"name" json:"name,omitempty"` // Asia/Shanghai
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ block radio(id, name, value, label, checked, disabled=false) }}
|
{{ 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>
|
<label for="{{id ? id : (name + "-" + value)}}">{{label}}</label>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
@ -65,6 +65,68 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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 is-horizontal">
|
||||||
<div class="field-label is-normal">
|
<div class="field-label is-normal">
|
||||||
<label class="label">Base DN</label>
|
<label class="label">Base DN</label>
|
||||||
@ -79,23 +141,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label is-normal">
|
<div class="field-label is-normal">
|
||||||
<label class="label">Username attribute</label>
|
<label class="label">User filter</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label is-normal">
|
<div class="field-label is-normal">
|
||||||
<label class="label">Email attribute</label>
|
<label class="label">Attributes</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field has-addons">
|
||||||
<div class="control">
|
<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">
|
<input name="ldap.email_attr" value="{{ .Setting.LDAP.EmailAttr }}" class="input" type="text" placeholder="e.g. mail">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user