feat: implement automatic synchronization for ldap users (#371)

Signed-off-by: Товарищ программист <2962928213@qq.com>
This commit is contained in:
Товарищ программист 2021-12-15 17:45:11 +08:00 committed by GitHub
parent 4ca5f4b196
commit f43d01c5c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 157 additions and 6 deletions

View File

@ -151,6 +151,9 @@ func (c *ApiController) AddLdap() {
if affected { if affected {
resp.Data2 = ldap resp.Data2 = ldap
} }
if ldap.AutoSync != 0 {
object.GetLdapAutoSynchronizer().StartAutoSync(ldap.Id)
}
c.Data["json"] = resp c.Data["json"] = resp
c.ServeJSON() c.ServeJSON()
@ -172,6 +175,9 @@ func (c *ApiController) UpdateLdap() {
if affected { if affected {
resp.Data2 = ldap resp.Data2 = ldap
} }
if ldap.AutoSync != 0 {
object.GetLdapAutoSynchronizer().StartAutoSync(ldap.Id)
}
c.Data["json"] = resp c.Data["json"] = resp
c.ServeJSON() c.ServeJSON()
@ -187,6 +193,7 @@ func (c *ApiController) DeleteLdap() {
panic(err) panic(err)
} }
object.GetLdapAutoSynchronizer().StopAutoSync(ldap.Id)
c.Data["json"] = wrapActionResponse(object.DeleteLdap(&ldap)) c.Data["json"] = wrapActionResponse(object.DeleteLdap(&ldap))
c.ServeJSON() c.ServeJSON()
} }

View File

@ -32,6 +32,7 @@ func main() {
object.InitAdapter() object.InitAdapter()
object.InitDb() object.InitDb()
object.InitDefaultStorageProvider() object.InitDefaultStorageProvider()
object.InitLdapAutoSynchronizer()
proxy.InitHttpClient() proxy.InitHttpClient()
authz.InitAuthz() authz.InitAuthz()
@ -73,7 +74,7 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
logs.SetLevel(logs.LevelInformational) //logs.SetLevel(logs.LevelInformational)
logs.SetLogFuncCall(false) logs.SetLogFuncCall(false)
beego.Run() beego.Run()

View File

@ -78,6 +78,32 @@ type LdapRespUser struct {
Address string `json:"address"` Address string `json:"address"`
} }
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
}
func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*ldapConn, error) { func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*ldapConn, error) {
conn, err := goldap.Dial("tcp", fmt.Sprintf("%s:%d", host, port)) conn, err := goldap.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil { if err != nil {
@ -286,15 +312,16 @@ func SyncLdapUsers(owner string, users []LdapRespUser) (*[]LdapRespUser, *[]Ldap
existUuids := CheckLdapUuidExist(owner, uuids) existUuids := CheckLdapUuidExist(owner, uuids)
for _, user := range users { for _, user := range users {
found := false
if len(existUuids) > 0 { if len(existUuids) > 0 {
for index, existUuid := range existUuids { for _, existUuid := range existUuids {
if user.Uuid == existUuid { if user.Uuid == existUuid {
existUsers = append(existUsers, user) existUsers = append(existUsers, user)
existUuids = append(existUuids[:index], existUuids[index+1:]...) found = true
} }
} }
} }
if !AddUser(&User{ if !found && !AddUser(&User{
Owner: owner, Owner: owner,
Name: buildLdapUserName(user.Uid, user.UidNumber), Name: buildLdapUserName(user.Uid, user.UidNumber),
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
@ -327,6 +354,7 @@ func UpdateLdapSyncTime(ldapId string) {
func CheckLdapUuidExist(owner string, uuids []string) []string { func CheckLdapUuidExist(owner string, uuids []string) []string {
var results []User var results []User
var existUuids []string var existUuids []string
existUuidSet := make(map[string]struct{})
//whereStr := "" //whereStr := ""
//for i, uuid := range uuids { //for i, uuid := range uuids {
@ -344,9 +372,13 @@ func CheckLdapUuidExist(owner string, uuids []string) []string {
if len(results) > 0 { if len(results) > 0 {
for _, result := range results { for _, result := range results {
existUuids = append(existUuids, result.Ldap) existUuidSet[result.Ldap] = struct{}{}
} }
} }
for uuid, _ := range existUuidSet {
existUuids = append(existUuids, uuid)
}
return existUuids return existUuids
} }

111
object/ldap_autosync.go Normal file
View File

@ -0,0 +1,111 @@
package object
import (
"fmt"
"sync"
"time"
"github.com/astaxie/beego/logs"
)
type LdapAutoSynchronizer struct {
sync.Mutex
ldapIdToStopChan map[string]chan struct{}
}
var globalLdapAutoSynchronizer *LdapAutoSynchronizer
func InitLdapAutoSynchronizer() {
globalLdapAutoSynchronizer = NewLdapAutoSynchronizer()
globalLdapAutoSynchronizer.LdapAutoSynchronizerStartUpAll()
}
func NewLdapAutoSynchronizer() *LdapAutoSynchronizer {
return &LdapAutoSynchronizer{
ldapIdToStopChan: make(map[string]chan struct{}),
}
}
func GetLdapAutoSynchronizer() *LdapAutoSynchronizer {
return globalLdapAutoSynchronizer
}
//start autosync for specified ldap, old existing autosync goroutine will be ceased
func (l *LdapAutoSynchronizer) StartAutoSync(ldapId string) error {
l.Lock()
defer l.Unlock()
ldap := GetLdap(ldapId)
if ldap == nil {
return fmt.Errorf("ldap %s doesn't exist", ldapId)
}
if res, ok := l.ldapIdToStopChan[ldapId]; ok {
res <- struct{}{}
delete(l.ldapIdToStopChan, ldapId)
}
stopChan := make(chan struct{})
l.ldapIdToStopChan[ldapId] = stopChan
logs.Info(fmt.Sprintf("autoSync started for %s", ldap.Id))
go l.syncRoutine(ldap, stopChan)
return nil
}
func (l *LdapAutoSynchronizer) StopAutoSync(ldapId string) {
l.Lock()
defer l.Unlock()
if res, ok := l.ldapIdToStopChan[ldapId]; ok {
res <- struct{}{}
delete(l.ldapIdToStopChan, ldapId)
}
}
//autosync goroutine
func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
ticker := time.NewTicker(time.Duration(ldap.AutoSync) * time.Minute)
defer ticker.Stop()
for {
UpdateLdapSyncTime(ldap.Id)
//fetch all users
conn, err := GetLdapConn(ldap.Host, ldap.Port, ldap.Admin, ldap.Passwd)
if err != nil {
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
continue
}
users, err := conn.GetLdapUsers(ldap.BaseDn)
if err != nil {
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
continue
}
existed, failed := SyncLdapUsers(ldap.Owner, LdapUsersToLdapRespUsers(users))
if len(*failed) != 0 {
logs.Warning(fmt.Sprintf("ldap autosync,%d new users,but %d user failed during :", len(users)-len(*existed)-len(*failed), len(*failed)), *failed)
} else {
logs.Info(fmt.Sprintf("ldap autosync success, %d new users, %d existing users", len(users)-len(*existed), len(*existed)))
}
select {
case <-stopChan:
logs.Info(fmt.Sprintf("autoSync goroutine for %s stopped", ldap.Id))
return
case <-ticker.C:
}
}
}
//start all autosync goroutine for existing ldap servers in each organizations
func (l *LdapAutoSynchronizer) LdapAutoSynchronizerStartUpAll() {
organizations := []*Organization{}
err := adapter.Engine.Desc("created_time").Find(&organizations)
if err != nil {
logs.Info("failed to Star up LdapAutoSynchronizer; ")
}
for _, org := range organizations {
for _, ldap := range GetLdaps(org.Name) {
if ldap.AutoSync != 0 {
l.StartAutoSync(ldap.Id)
}
}
}
}

View File

@ -178,7 +178,7 @@ class LdapEditPage extends React.Component {
{Setting.getLabel(i18next.t("ldap:Auto Sync"), i18next.t("ldap:Auto Sync - Tooltip"))} : {Setting.getLabel(i18next.t("ldap:Auto Sync"), i18next.t("ldap:Auto Sync - Tooltip"))} :
</Col> </Col>
<Col span={21}> <Col span={21}>
<InputNumber min={0} formatter={value => value.replace(/\$\s?|(,*)/g, "")} disabled={true} <InputNumber min={0} formatter={value => value.replace(/\$\s?|(,*)/g, "")} disabled={false}
value={this.state.ldap.autoSync} onChange={value => { value={this.state.ldap.autoSync} onChange={value => {
this.updateLdapField("autoSync", value); this.updateLdapField("autoSync", value);
}}/><span>&nbsp;mins</span> }}/><span>&nbsp;mins</span>