2021-07-17 14:13:00 +08:00
|
|
|
// 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"
|
2021-12-10 22:45:01 +08:00
|
|
|
"strings"
|
|
|
|
|
2022-01-20 14:11:46 +08:00
|
|
|
"github.com/casdoor/casdoor/util"
|
2021-07-17 14:13:00 +08:00
|
|
|
goldap "github.com/go-ldap/ldap/v3"
|
|
|
|
"github.com/thanhpk/randstr"
|
|
|
|
)
|
|
|
|
|
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
2021-12-15 17:45:11 +08:00
|
|
|
func LdapUsersToLdapRespUsers(users []ldapUser) []LdapRespUser {
|
|
|
|
returnAnyNotEmpty := func(strs ...string) string {
|
|
|
|
for _, str := range strs {
|
|
|
|
if str != "" {
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
res := make([]LdapRespUser, 0)
|
|
|
|
for _, user := range users {
|
|
|
|
res = append(res, LdapRespUser{
|
|
|
|
UidNumber: user.UidNumber,
|
|
|
|
Uid: user.Uid,
|
|
|
|
Cn: user.Cn,
|
|
|
|
GroupId: user.GidNumber,
|
|
|
|
Uuid: user.Uuid,
|
|
|
|
Email: returnAnyNotEmpty(user.Email, user.EmailAddress, user.Mail),
|
|
|
|
Phone: returnAnyNotEmpty(user.Mobile, user.MobileTelephoneNumber, user.TelephoneNumber),
|
|
|
|
Address: returnAnyNotEmpty(user.PostalAddress, user.RegisteredAddress),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2021-07-17 14:13:00 +08:00
|
|
|
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]
|
|
|
|
case "uid":
|
|
|
|
ldapUserItem.Uid = attribute.Values[0]
|
|
|
|
case "cn":
|
|
|
|
ldapUserItem.Cn = attribute.Values[0]
|
|
|
|
case "gidNumber":
|
|
|
|
ldapUserItem.GidNumber = attribute.Values[0]
|
|
|
|
case "entryUUID":
|
|
|
|
ldapUserItem.Uuid = attribute.Values[0]
|
|
|
|
case "mail":
|
|
|
|
ldapUserItem.Mail = attribute.Values[0]
|
|
|
|
case "email":
|
|
|
|
ldapUserItem.Email = attribute.Values[0]
|
|
|
|
case "emailAddress":
|
|
|
|
ldapUserItem.EmailAddress = attribute.Values[0]
|
|
|
|
case "telephoneNumber":
|
|
|
|
ldapUserItem.TelephoneNumber = attribute.Values[0]
|
|
|
|
case "mobile":
|
|
|
|
ldapUserItem.Mobile = attribute.Values[0]
|
|
|
|
case "mobileTelephoneNumber":
|
|
|
|
ldapUserItem.MobileTelephoneNumber = attribute.Values[0]
|
|
|
|
case "registeredAddress":
|
|
|
|
ldapUserItem.RegisteredAddress = attribute.Values[0]
|
|
|
|
case "postalAddress":
|
|
|
|
ldapUserItem.PostalAddress = attribute.Values[0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 {
|
2021-12-15 17:45:11 +08:00
|
|
|
found := false
|
2021-07-17 14:13:00 +08:00
|
|
|
if len(existUuids) > 0 {
|
2021-12-15 17:45:11 +08:00
|
|
|
for _, existUuid := range existUuids {
|
2021-07-17 14:13:00 +08:00
|
|
|
if user.Uuid == existUuid {
|
|
|
|
existUsers = append(existUsers, user)
|
2021-12-15 17:45:11 +08:00
|
|
|
found = true
|
2021-07-17 14:13:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-12-15 17:45:11 +08:00
|
|
|
if !found && !AddUser(&User{
|
2021-07-17 14:13:00 +08:00
|
|
|
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",
|
2021-08-28 11:13:38 +08:00
|
|
|
Score: 2000,
|
2021-07-17 14:13:00 +08:00
|
|
|
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
|
2021-12-15 17:45:11 +08:00
|
|
|
existUuidSet := make(map[string]struct{})
|
2021-07-17 14:13:00 +08:00
|
|
|
|
|
|
|
//whereStr := ""
|
|
|
|
//for i, uuid := range uuids {
|
|
|
|
// if i == 0 {
|
|
|
|
// whereStr = fmt.Sprintf("'%s'", uuid)
|
|
|
|
// } else {
|
|
|
|
// whereStr = fmt.Sprintf(",'%s'", uuid)
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
|
2021-08-07 22:02:56 +08:00
|
|
|
err := adapter.Engine.Where(fmt.Sprintf("ldap IN (%s) AND owner = ?", "'"+strings.Join(uuids, "','")+"'"), owner).Find(&results)
|
2021-07-17 14:13:00 +08:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(results) > 0 {
|
|
|
|
for _, result := range results {
|
2021-12-15 17:45:11 +08:00
|
|
|
existUuidSet[result.Ldap] = struct{}{}
|
2021-07-17 14:13:00 +08:00
|
|
|
}
|
|
|
|
}
|
2021-12-15 17:45:11 +08:00
|
|
|
|
|
|
|
for uuid, _ := range existUuidSet {
|
|
|
|
existUuids = append(existUuids, uuid)
|
|
|
|
}
|
2021-07-17 14:13:00 +08:00
|
|
|
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
|
|
|
|
}
|