diff --git a/controllers/ldap.go b/controllers/ldap.go new file mode 100644 index 00000000..334e3ca8 --- /dev/null +++ b/controllers/ldap.go @@ -0,0 +1,209 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controllers + +import ( + "encoding/json" + "github.com/casdoor/casdoor/object" + "github.com/casdoor/casdoor/util" +) + +type LdapServer struct { + Host string `json:"host"` + Port int `json:"port"` + Admin string `json:"admin"` + Passwd string `json:"passwd"` + BaseDn string `json:"baseDn"` +} + +type LdapResp struct { + //Groups []LdapRespGroup `json:"groups"` + Users []object.LdapRespUser `json:"users"` +} + +//type LdapRespGroup struct { +// GroupId string +// GroupName string +//} + +type LdapSyncResp struct { + Exist []object.LdapRespUser `json:"exist"` + Failed []object.LdapRespUser `json:"failed"` +} + +func (c *ApiController) GetLdapUser() { + ldapServer := LdapServer{} + err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer) + if err != nil || util.IsStrsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) { + c.ResponseError("Missing parameter") + return + } + + var resp LdapResp + + conn, err := object.GetLdapConn(ldapServer.Host, ldapServer.Port, ldapServer.Admin, ldapServer.Passwd) + if err != nil { + c.Data["json"] = Response{Status: "error", Msg: err.Error()} + c.ServeJSON() + return + } + + //groupsMap, err := conn.GetLdapGroups(ldapServer.BaseDn) + //if err != nil { + // c.Data["json"] = Response{Status: "error", Msg: err.Error()} + // c.ServeJSON() + // return + //} + + //for _, group := range groupsMap { + // resp.Groups = append(resp.Groups, LdapRespGroup{ + // GroupId: group.GidNumber, + // GroupName: group.Cn, + // }) + //} + + users, err := conn.GetLdapUsers(ldapServer.BaseDn) + if err != nil { + c.Data["json"] = Response{Status: "error", Msg: err.Error()} + c.ServeJSON() + return + } + + for _, user := range users { + resp.Users = append(resp.Users, object.LdapRespUser{ + UidNumber: user.UidNumber, + Uid: user.Uid, + Cn: user.Cn, + GroupId: user.GidNumber, + //GroupName: groupsMap[user.GidNumber].Cn, + Uuid: user.Uuid, + Email: util.GetMaxLenStr(user.Mail, user.Email, user.EmailAddress), + Phone: util.GetMaxLenStr(user.TelephoneNumber, user.Mobile, user.MobileTelephoneNumber), + Address: util.GetMaxLenStr(user.RegisteredAddress, user.PostalAddress), + }) + } + + c.Data["json"] = Response{Status: "ok", Data: resp} + c.ServeJSON() + return +} + +func (c *ApiController) GetLdaps() { + owner := c.Input().Get("owner") + + c.Data["json"] = Response{Status: "ok", Data: object.GetLdaps(owner)} + c.ServeJSON() +} + +func (c *ApiController) GetLdap() { + id := c.Input().Get("id") + + if util.IsStrsEmpty(id) { + c.ResponseError("Missing parameter") + return + } + + c.Data["json"] = Response{Status: "ok", Data: object.GetLdap(id)} + c.ServeJSON() +} + +func (c *ApiController) AddLdap() { + var ldap object.Ldap + err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap) + if err != nil { + c.ResponseError("Missing parameter") + return + } + + if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) { + c.ResponseError("Missing parameter") + return + } + + if object.CheckLdapExist(&ldap) { + c.ResponseError("Ldap server exist") + return + } + + affected := object.AddLdap(&ldap) + resp := wrapActionResponse(affected) + if affected { + resp.Data2 = ldap + } + + c.Data["json"] = resp + c.ServeJSON() +} + +func (c *ApiController) UpdateLdap() { + var ldap object.Ldap + err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap) + if err != nil || util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) { + c.ResponseError("Missing parameter") + return + } + + affected := object.UpdateLdap(&ldap) + resp := wrapActionResponse(affected) + if affected { + resp.Data2 = ldap + } + + c.Data["json"] = resp + c.ServeJSON() +} + +func (c *ApiController) DeleteLdap() { + var ldap object.Ldap + err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap) + if err != nil { + panic(err) + } + + c.Data["json"] = wrapActionResponse(object.DeleteLdap(&ldap)) + c.ServeJSON() +} + +func (c *ApiController) SyncLdapUsers() { + owner := c.Input().Get("owner") + ldapId := c.Input().Get("ldapId") + var users []object.LdapRespUser + err := json.Unmarshal(c.Ctx.Input.RequestBody, &users) + if err != nil { + panic(err) + } + + object.UpdateLdapSyncTime(ldapId) + + exist, failed := object.SyncLdapUsers(owner, users) + c.Data["json"] = &Response{Status: "ok", Data: &LdapSyncResp{ + Exist: *exist, + Failed: *failed, + }} + c.ServeJSON() +} + +func (c *ApiController) CheckLdapUsersExist() { + owner := c.Input().Get("owner") + var uuids []string + err := json.Unmarshal(c.Ctx.Input.RequestBody, &uuids) + if err != nil { + panic(err) + } + + exist := object.CheckLdapUuidExist(owner, uuids) + c.Data["json"] = &Response{Status: "ok", Data: exist} + c.ServeJSON() +} diff --git a/go.mod b/go.mod index 454a9b73..3eaf865d 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df + github.com/go-ldap/ldap/v3 v3.3.0 github.com/go-sql-driver/mysql v1.5.0 github.com/google/uuid v1.2.0 github.com/jinzhu/configor v1.2.1 // indirect diff --git a/go.sum b/go.sum index ec73a2ed..75dd984a 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -63,10 +65,14 @@ github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw= +github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= +github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df h1:Bao6dhmbTA1KFVxmJ6nBoMuOJit2yjEgLJpIMYpop0E= github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ= +github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= @@ -220,6 +226,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/object/adapter.go b/object/adapter.go index 2ac7a111..05c8ac8e 100644 --- a/object/adapter.go +++ b/object/adapter.go @@ -137,4 +137,9 @@ func (a *Adapter) createTable() { if err != nil { panic(err) } + + err = a.Engine.Sync2(new(Ldap)) + if err != nil { + panic(err) + } } diff --git a/object/captcha.go b/object/captcha.go index dbc03e3b..11ef9255 100644 --- a/object/captcha.go +++ b/object/captcha.go @@ -1,3 +1,17 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package object import ( diff --git a/object/init.go b/object/init.go index 49c31fde..55f1c3ce 100644 --- a/object/init.go +++ b/object/init.go @@ -1,3 +1,17 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package object import "github.com/casdoor/casdoor/util" @@ -6,6 +20,7 @@ func InitDb() { initBuiltInOrganization() initBuiltInUser() initBuiltInApplication() + initBuiltInLdap() } func initBuiltInOrganization() { @@ -79,3 +94,24 @@ func initBuiltInApplication() { } AddApplication(application) } + +func initBuiltInLdap() { + ldap := GetLdap("ldap-built-in") + if ldap != nil { + return + } + + ldap = &Ldap{ + Id: "ldap-built-in", + Owner: "built-in", + ServerName: "BuildIn LDAP Server", + Host: "example.com", + Port: 389, + Admin: "cn=buildin,dc=example,dc=com", + Passwd: "123", + BaseDn: "ou=BuildIn,dc=example,dc=com", + AutoSync: 0, + LastSync: "", + } + AddLdap(ldap) +} diff --git a/object/ldap.go b/object/ldap.go new file mode 100644 index 00000000..31d68fc2 --- /dev/null +++ b/object/ldap.go @@ -0,0 +1,381 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package object + +import ( + "errors" + "fmt" + "github.com/casdoor/casdoor/util" + goldap "github.com/go-ldap/ldap/v3" + "github.com/thanhpk/randstr" + "strings" +) + +type Ldap struct { + Id string `xorm:"varchar(100) notnull pk" json:"id"` + Owner string `xorm:"varchar(100)" json:"owner"` + CreatedTime string `xorm:"varchar(100)" json:"createdTime"` + + ServerName string `xorm:"varchar(100)" json:"serverName"` + Host string `xorm:"varchar(100)" json:"host"` + Port int `json:"port"` + Admin string `xorm:"varchar(100)" json:"admin"` + Passwd string `xorm:"varchar(100)" json:"passwd"` + BaseDn string `xorm:"varchar(100)" json:"baseDn"` + + AutoSync int `json:"autoSync"` + LastSync string `xorm:"varchar(100)" json:"lastSync"` +} + +type ldapConn struct { + Conn *goldap.Conn +} + +//type ldapGroup struct { +// GidNumber string +// Cn string +//} + +type ldapUser struct { + UidNumber string + Uid string + Cn string + GidNumber string + //Gcn string + Uuid string + Mail string + Email string + EmailAddress string + TelephoneNumber string + Mobile string + MobileTelephoneNumber string + RegisteredAddress string + PostalAddress string +} + +type LdapRespUser struct { + UidNumber string `json:"uidNumber"` + Uid string `json:"uid"` + Cn string `json:"cn"` + GroupId string `json:"groupId"` + //GroupName string `json:"groupName"` + Uuid string `json:"uuid"` + Email string `json:"email"` + Phone string `json:"phone"` + Address string `json:"address"` +} + +func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*ldapConn, error) { + conn, err := goldap.Dial("tcp", fmt.Sprintf("%s:%d", host, port)) + if err != nil { + return nil, err + } + + err = conn.Bind(adminUser, adminPasswd) + if err != nil { + return nil, fmt.Errorf("fail to login Ldap server with [%s]", adminUser) + } + + return &ldapConn{Conn: conn}, nil +} + +//FIXME: The Base DN does not necessarily contain the Group +//func (l *ldapConn) GetLdapGroups(baseDn string) (map[string]ldapGroup, error) { +// SearchFilter := "(objectClass=posixGroup)" +// SearchAttributes := []string{"cn", "gidNumber"} +// groupMap := make(map[string]ldapGroup) +// +// searchReq := goldap.NewSearchRequest(baseDn, +// goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false, +// SearchFilter, SearchAttributes, nil) +// searchResult, err := l.Conn.Search(searchReq) +// if err != nil { +// return nil, err +// } +// +// if len(searchResult.Entries) == 0 { +// return nil, errors.New("no result") +// } +// +// for _, entry := range searchResult.Entries { +// var ldapGroupItem ldapGroup +// for _, attribute := range entry.Attributes { +// switch attribute.Name { +// case "gidNumber": +// ldapGroupItem.GidNumber = attribute.Values[0] +// break +// case "cn": +// ldapGroupItem.Cn = attribute.Values[0] +// break +// } +// } +// groupMap[ldapGroupItem.GidNumber] = ldapGroupItem +// } +// +// return groupMap, nil +//} + +func (l *ldapConn) GetLdapUsers(baseDn string) ([]ldapUser, error) { + SearchFilter := "(objectClass=posixAccount)" + SearchAttributes := []string{"uidNumber", "uid", "cn", "gidNumber", "entryUUID", "mail", "email", + "emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress"} + + searchReq := goldap.NewSearchRequest(baseDn, + goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false, + SearchFilter, SearchAttributes, nil) + searchResult, err := l.Conn.Search(searchReq) + if err != nil { + return nil, err + } + + if len(searchResult.Entries) == 0 { + return nil, errors.New("no result") + } + + var ldapUsers []ldapUser + + for _, entry := range searchResult.Entries { + var ldapUserItem ldapUser + for _, attribute := range entry.Attributes { + switch attribute.Name { + case "uidNumber": + ldapUserItem.UidNumber = attribute.Values[0] + break + case "uid": + ldapUserItem.Uid = attribute.Values[0] + break + case "cn": + ldapUserItem.Cn = attribute.Values[0] + break + case "gidNumber": + ldapUserItem.GidNumber = attribute.Values[0] + break + case "entryUUID": + ldapUserItem.Uuid = attribute.Values[0] + break + case "mail": + ldapUserItem.Mail = attribute.Values[0] + break + case "email": + ldapUserItem.Email = attribute.Values[0] + break + case "emailAddress": + ldapUserItem.EmailAddress = attribute.Values[0] + break + case "telephoneNumber": + ldapUserItem.TelephoneNumber = attribute.Values[0] + break + case "mobile": + ldapUserItem.Mobile = attribute.Values[0] + break + case "mobileTelephoneNumber": + ldapUserItem.MobileTelephoneNumber = attribute.Values[0] + break + case "registeredAddress": + ldapUserItem.RegisteredAddress = attribute.Values[0] + break + case "postalAddress": + ldapUserItem.PostalAddress = attribute.Values[0] + break + } + } + ldapUsers = append(ldapUsers, ldapUserItem) + } + + return ldapUsers, nil +} + +func AddLdap(ldap *Ldap) bool { + if len(ldap.Id) == 0 { + ldap.Id = util.GenerateId() + } + + if len(ldap.CreatedTime) == 0 { + ldap.CreatedTime = util.GetCurrentTime() + } + + affected, err := adapter.Engine.Insert(ldap) + if err != nil { + panic(err) + } + + return affected != 0 +} + +func CheckLdapExist(ldap *Ldap) bool { + var result []*Ldap + err := adapter.Engine.Find(&result, &Ldap{ + Owner: ldap.Owner, + Host: ldap.Host, + Port: ldap.Port, + Admin: ldap.Admin, + Passwd: ldap.Passwd, + BaseDn: ldap.BaseDn, + }) + if err != nil { + panic(err) + } + + if len(result) > 0 { + return true + } + + return false +} + +func GetLdaps(owner string) []*Ldap { + var ldaps []*Ldap + err := adapter.Engine.Desc("created_time").Find(&ldaps, &Ldap{Owner: owner}) + if err != nil { + panic(err) + } + + return ldaps +} + +func GetLdap(id string) *Ldap { + if util.IsStrsEmpty(id) { + return nil + } + + ldap := Ldap{Id: id} + existed, err := adapter.Engine.Get(&ldap) + if err != nil { + panic(err) + } + + if existed { + return &ldap + } else { + return nil + } +} + +func UpdateLdap(ldap *Ldap) bool { + if GetLdap(ldap.Id) == nil { + return false + } + + affected, err := adapter.Engine.ID(ldap.Id).Cols("owner", "server_name", "host", + "port", "admin", "passwd", "base_dn", "auto_sync").Update(ldap) + if err != nil { + panic(err) + } + + return affected != 0 +} + +func DeleteLdap(ldap *Ldap) bool { + affected, err := adapter.Engine.ID(ldap.Id).Delete(&Ldap{}) + if err != nil { + panic(err) + } + + return affected != 0 +} + +func SyncLdapUsers(owner string, users []LdapRespUser) (*[]LdapRespUser, *[]LdapRespUser) { + var existUsers []LdapRespUser + var failedUsers []LdapRespUser + var uuids []string + + for _, user := range users { + uuids = append(uuids, user.Uuid) + } + + existUuids := CheckLdapUuidExist(owner, uuids) + + for _, user := range users { + if len(existUuids) > 0 { + for index, existUuid := range existUuids { + if user.Uuid == existUuid { + existUsers = append(existUsers, user) + existUuids = append(existUuids[:index], existUuids[index+1:]...) + } + } + } + if !AddUser(&User{ + Owner: owner, + Name: buildLdapUserName(user.Uid, user.UidNumber), + CreatedTime: util.GetCurrentTime(), + Password: "123", + DisplayName: user.Cn, + Avatar: "https://casbin.org/img/casbin.svg", + Email: user.Email, + Phone: user.Phone, + Address: []string{user.Address}, + Affiliation: "Example Inc.", + Tag: "staff", + Ldap: user.Uuid, + }) { + failedUsers = append(failedUsers, user) + continue + } + } + + return &existUsers, &failedUsers +} + +func UpdateLdapSyncTime(ldapId string) { + _, err := adapter.Engine.ID(ldapId).Update(&Ldap{LastSync: util.GetCurrentTime()}) + if err != nil { + panic(err) + } +} + +func CheckLdapUuidExist(owner string, uuids []string) []string { + var results []User + var existUuids []string + + //whereStr := "" + //for i, uuid := range uuids { + // if i == 0 { + // whereStr = fmt.Sprintf("'%s'", uuid) + // } else { + // whereStr = fmt.Sprintf(",'%s'", uuid) + // } + //} + + err := adapter.Engine.Where(fmt.Sprintf("ldap IN (%s) AND owner = ?", "'" + strings.Join(uuids, "','") + "'"), owner).Find(&results) + if err != nil { + panic(err) + } + + if len(results) > 0 { + for _, result := range results { + existUuids = append(existUuids, result.Ldap) + } + } + return existUuids +} + +func buildLdapUserName(uid, uidNum string) string { + var result User + uidWithNumber := fmt.Sprintf("%s_%s", uid, uidNum) + + has, err := adapter.Engine.Where("name = ? or name = ?", uid, uidWithNumber).Get(&result) + if err != nil { + panic(err) + } + + if has { + if result.Name == uid { + return uidWithNumber + } + return fmt.Sprintf("%s_%s", uidWithNumber, randstr.Hex(6)) + } + + return uid +} diff --git a/object/user.go b/object/user.go index 4800702f..1804abb8 100644 --- a/object/user.go +++ b/object/user.go @@ -56,6 +56,7 @@ type User struct { Gitee string `xorm:"gitee varchar(100)" json:"gitee"` LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"` + Ldap string `xorm:"ldap varchar(100)" json:"ldap"` Properties map[string]string `json:"properties"` } @@ -142,7 +143,9 @@ func UpdateUser(id string, user *User) bool { user.UpdateUserHash() - affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols("owner", "display_name", "avatar", "address","language", "affiliation", "score", "tag", "is_admin", "is_global_admin", "is_forbidden", "hash", "properties").Update(user) + affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols("owner", "display_name", "avatar", + "address", "language", "affiliation", "score", "tag", "is_admin", "is_global_admin", "is_forbidden", + "hash", "properties").Update(user) if err != nil { panic(err) } diff --git a/object/user_test.go b/object/user_test.go index fc8716d6..4f939d47 100644 --- a/object/user_test.go +++ b/object/user_test.go @@ -1,3 +1,17 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package object import ( diff --git a/routers/router.go b/routers/router.go index 4c663882..1de18e39 100644 --- a/routers/router.go +++ b/routers/router.go @@ -64,6 +64,14 @@ func initAPI() { beego.Router("/api/send-verification-code", &controllers.ApiController{}, "POST:SendVerificationCode") beego.Router("/api/reset-email-or-phone", &controllers.ApiController{}, "POST:ResetEmailOrPhone") beego.Router("/api/get-human-check", &controllers.ApiController{}, "GET:GetHumanCheck") + beego.Router("/api/get-ldap-user", &controllers.ApiController{}, "POST:GetLdapUser") + beego.Router("/api/get-ldaps", &controllers.ApiController{}, "POST:GetLdaps") + beego.Router("/api/get-ldap", &controllers.ApiController{}, "POST:GetLdap") + beego.Router("/api/add-ldap", &controllers.ApiController{}, "POST:AddLdap") + beego.Router("/api/update-ldap", &controllers.ApiController{}, "POST:UpdateLdap") + beego.Router("/api/delete-ldap", &controllers.ApiController{}, "POST:DeleteLdap") + beego.Router("/api/check-ldap-users-exist", &controllers.ApiController{}, "POST:CheckLdapUsersExist") + beego.Router("/api/sync-ldap-users", &controllers.ApiController{}, "POST:SyncLdapUsers") beego.Router("/api/get-providers", &controllers.ApiController{}, "GET:GetProviders") beego.Router("/api/get-provider", &controllers.ApiController{}, "GET:GetProvider") diff --git a/util/json.go b/util/json.go index 6adb06f1..8e71e42e 100644 --- a/util/json.go +++ b/util/json.go @@ -1,3 +1,17 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package util import "encoding/json" diff --git a/util/log.go b/util/log.go index 08e16330..4a8ff8ed 100644 --- a/util/log.go +++ b/util/log.go @@ -1,3 +1,17 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package util import ( diff --git a/util/string.go b/util/string.go index c17c2476..227b920c 100644 --- a/util/string.go +++ b/util/string.go @@ -55,3 +55,39 @@ func GetMd5Hash(text string) string { hash := md5.Sum([]byte(text)) return hex.EncodeToString(hash[:]) } + +func IsStrsEmpty(strs ...string) bool { + r := false + for _, str := range strs { + if len(str) == 0 { + r = true + } + } + return r +} + +func GetMaxLenStr(strs ...string) string { + m := 0 + i := 0 + for j, str := range strs { + l := len(str) + if l > m { + m = l + i = j + } + } + return strs[i] +} + +func GetMinLenStr(strs ...string) string { + m := int(^uint(0) >> 1) + i := 0 + for j, str := range strs { + l := len(str) + if l > m { + m = l + i = j + } + } + return strs[i] +} diff --git a/util/time.go b/util/time.go index 4bc22091..d489497b 100644 --- a/util/time.go +++ b/util/time.go @@ -1,3 +1,17 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package util import ( diff --git a/web/src/App.js b/web/src/App.js index 4cb78bd5..473b4bb5 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -27,6 +27,8 @@ import ProviderListPage from "./ProviderListPage"; import ProviderEditPage from "./ProviderEditPage"; import ApplicationListPage from "./ApplicationListPage"; import ApplicationEditPage from "./ApplicationEditPage"; +import LdapEditPage from "./LdapEditPage"; +import LdapSyncPage from "./LdapSyncPage"; import TokenListPage from "./TokenListPage"; import TokenEditPage from "./TokenEditPage"; import RecordListPage from "./RecordListPage"; @@ -327,7 +329,7 @@ class App extends Component { ); } res.push( -