mirror of
https://github.com/1Panel-dev/KubePi.git
synced 2025-10-24 07:34:14 +08:00
426 lines
11 KiB
Go
426 lines
11 KiB
Go
package ldap
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
v1 "github.com/KubeOperator/kubepi/internal/model/v1"
|
|
v1Ldap "github.com/KubeOperator/kubepi/internal/model/v1/ldap"
|
|
v1Role "github.com/KubeOperator/kubepi/internal/model/v1/role"
|
|
v1User "github.com/KubeOperator/kubepi/internal/model/v1/user"
|
|
"github.com/KubeOperator/kubepi/internal/server"
|
|
"github.com/KubeOperator/kubepi/internal/service/v1/common"
|
|
"github.com/KubeOperator/kubepi/internal/service/v1/rolebinding"
|
|
"github.com/KubeOperator/kubepi/internal/service/v1/user"
|
|
ldapClient "github.com/KubeOperator/kubepi/pkg/util/ldap"
|
|
"github.com/asdine/storm/v3"
|
|
"github.com/asdine/storm/v3/q"
|
|
"github.com/google/uuid"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Service interface {
|
|
common.DBService
|
|
Create(ldap *v1Ldap.Ldap, options common.DBOptions) error
|
|
List(options common.DBOptions) ([]v1Ldap.Ldap, error)
|
|
Update(id string, ldap *v1Ldap.Ldap, options common.DBOptions) error
|
|
GetById(id string, options common.DBOptions) (*v1Ldap.Ldap, error)
|
|
Delete(id string, options common.DBOptions) error
|
|
Sync(id string, options common.DBOptions) error
|
|
Login(user v1User.User, password string, options common.DBOptions) error
|
|
TestConnect(ldap *v1Ldap.Ldap) (int, error)
|
|
TestLogin(username string, password string) error
|
|
ImportUsers(users []v1User.ImportUser) (v1User.ImportResult, error)
|
|
CheckStatus() bool
|
|
GetLdapUser() ([]v1User.ImportUser, error)
|
|
}
|
|
|
|
func NewService() Service {
|
|
return &service{
|
|
userService: user.NewService(),
|
|
roleBindingService: rolebinding.NewService(),
|
|
}
|
|
}
|
|
|
|
type service struct {
|
|
common.DefaultDBService
|
|
userService user.Service
|
|
roleBindingService rolebinding.Service
|
|
}
|
|
|
|
func (l *service) Create(ldap *v1Ldap.Ldap, options common.DBOptions) error {
|
|
m := make(map[string]string)
|
|
err := json.Unmarshal([]byte(ldap.Mapping), &m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lc := ldapClient.NewLdapClient(ldap.Address, ldap.Port, ldap.Username, ldap.Password, ldap.TLS)
|
|
err = lc.Connect()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
db := l.GetDB(options)
|
|
ldap.UUID = uuid.New().String()
|
|
ldap.CreateAt = time.Now()
|
|
ldap.UpdateAt = time.Now()
|
|
return db.Save(ldap)
|
|
}
|
|
|
|
func (l *service) List(options common.DBOptions) ([]v1Ldap.Ldap, error) {
|
|
db := l.GetDB(options)
|
|
ldap := make([]v1Ldap.Ldap, 0)
|
|
if err := db.All(&ldap); err != nil {
|
|
return nil, err
|
|
}
|
|
return ldap, nil
|
|
}
|
|
|
|
func (l *service) Update(id string, ldap *v1Ldap.Ldap, options common.DBOptions) error {
|
|
m := make(map[string]string)
|
|
err := json.Unmarshal([]byte(ldap.Mapping), &m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lc := ldapClient.NewLdapClient(ldap.Address, ldap.Port, ldap.Username, ldap.Password, ldap.TLS)
|
|
if err := lc.Connect(); err != nil {
|
|
return err
|
|
}
|
|
old, err := l.GetById(id, options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ldap.UUID = old.UUID
|
|
ldap.CreateAt = old.CreateAt
|
|
ldap.UpdateAt = time.Now()
|
|
db := l.GetDB(options)
|
|
if ldap.Enable != old.Enable {
|
|
err = db.UpdateField(ldap, "Enable", ldap.Enable)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if ldap.TLS != old.TLS {
|
|
err = db.UpdateField(ldap, "TLS", ldap.TLS)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return db.Update(ldap)
|
|
}
|
|
|
|
func (l *service) GetById(id string, options common.DBOptions) (*v1Ldap.Ldap, error) {
|
|
db := l.GetDB(options)
|
|
var ldap v1Ldap.Ldap
|
|
query := db.Select(q.Eq("UUID", id))
|
|
if err := query.First(&ldap); err != nil {
|
|
return nil, err
|
|
}
|
|
return &ldap, nil
|
|
}
|
|
|
|
func (l *service) Delete(id string, options common.DBOptions) error {
|
|
db := l.GetDB(options)
|
|
ldap, err := l.GetById(id, options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return db.DeleteStruct(ldap)
|
|
}
|
|
|
|
func (l *service) GetLdapUser() ([]v1User.ImportUser, error) {
|
|
users := []v1User.ImportUser{}
|
|
ldaps, err := l.List(common.DBOptions{})
|
|
if err != nil {
|
|
return users, err
|
|
}
|
|
if len(ldaps) == 0 {
|
|
return users, errors.New("请先保存LDAP配置")
|
|
}
|
|
ldap := ldaps[0]
|
|
if !ldap.Enable {
|
|
return users, errors.New("请先启用LDAP")
|
|
}
|
|
lc := ldapClient.NewLdapClient(ldap.Address, ldap.Port, ldap.Username, ldap.Password, ldap.TLS)
|
|
if err := lc.Connect(); err != nil {
|
|
return users, err
|
|
}
|
|
attributes, err := ldap.GetAttributes()
|
|
if err != nil {
|
|
return users, err
|
|
}
|
|
mappings, err := ldap.GetMappings()
|
|
if err != nil {
|
|
return users, err
|
|
}
|
|
entries, err := lc.Search(ldap.Dn, ldap.Filter, ldap.SizeLimit, ldap.TimeLimit, attributes)
|
|
if err != nil {
|
|
return users, err
|
|
}
|
|
if len(entries) == 0 {
|
|
return users, nil
|
|
}
|
|
for _, entry := range entries {
|
|
us := new(v1User.ImportUser)
|
|
us.Available = true
|
|
rv := reflect.ValueOf(&us).Elem().Elem()
|
|
for _, at := range entry.Attributes {
|
|
for k, v := range mappings {
|
|
if v == at.Name && len(at.Values) > 0 {
|
|
fv := rv.FieldByName(k)
|
|
if fv.IsValid() {
|
|
fv.Set(reflect.ValueOf(strings.Trim(at.Values[0], " ")))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if us.Name == "" {
|
|
continue
|
|
}
|
|
_, err = l.userService.GetByNameOrEmail(us.Name, common.DBOptions{})
|
|
if err == nil {
|
|
us.Available = false
|
|
}
|
|
users = append(users, *us)
|
|
}
|
|
return users, nil
|
|
}
|
|
|
|
func (l *service) TestConnect(ldap *v1Ldap.Ldap) (int, error) {
|
|
users := 0
|
|
if !ldap.Enable {
|
|
return users, errors.New("请先启用LDAP")
|
|
}
|
|
|
|
lc := ldapClient.NewLdapClient(ldap.Address, ldap.Port, ldap.Username, ldap.Password, ldap.TLS)
|
|
if err := lc.Connect(); err != nil {
|
|
return users, err
|
|
}
|
|
attributes, err := ldap.GetAttributes()
|
|
if err != nil {
|
|
return users, err
|
|
}
|
|
entries, err := lc.Search(ldap.Dn, ldap.Filter, ldap.SizeLimit, ldap.TimeLimit, attributes)
|
|
if err != nil {
|
|
return users, err
|
|
}
|
|
if len(entries) == 0 {
|
|
return users, nil
|
|
}
|
|
|
|
return len(entries), nil
|
|
}
|
|
|
|
func (l *service) CheckStatus() bool {
|
|
ldaps, err := l.List(common.DBOptions{})
|
|
if err != nil || len(ldaps) == 0 {
|
|
return false
|
|
}
|
|
ldap := ldaps[0]
|
|
return ldap.Enable
|
|
}
|
|
|
|
func (l *service) TestLogin(username string, password string) error {
|
|
ldaps, err := l.List(common.DBOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(ldaps) == 0 {
|
|
return errors.New("请先保存LDAP配置")
|
|
}
|
|
ldap := ldaps[0]
|
|
|
|
mappings, err := ldap.GetMappings()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var userFilter string
|
|
for k, v := range mappings {
|
|
if k == "Name" {
|
|
userFilter = "(" + v + "=" + username + ")"
|
|
}
|
|
}
|
|
lc := ldapClient.NewLdapClient(ldap.Address, ldap.Port, ldap.Username, ldap.Password, ldap.TLS)
|
|
if err := lc.Connect(); err != nil {
|
|
return err
|
|
}
|
|
return lc.Login(ldap.Dn, userFilter, password, ldap.SizeLimit, ldap.TimeLimit)
|
|
}
|
|
|
|
func (l *service) Login(user v1User.User, password string, options common.DBOptions) error {
|
|
ldaps, err := l.List(options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ldap := ldaps[0]
|
|
|
|
mappings, err := ldap.GetMappings()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var userFilter string
|
|
for k, v := range mappings {
|
|
if k == "Name" {
|
|
userFilter = "(" + v + "=" + user.Name + ")"
|
|
}
|
|
}
|
|
lc := ldapClient.NewLdapClient(ldap.Address, ldap.Port, ldap.Username, ldap.Password, ldap.TLS)
|
|
if err := lc.Connect(); err != nil {
|
|
return err
|
|
}
|
|
return lc.Login(ldap.Dn, userFilter, password, ldap.SizeLimit, ldap.TimeLimit)
|
|
}
|
|
|
|
func (l *service) ImportUsers(users []v1User.ImportUser) (v1User.ImportResult, error) {
|
|
var result v1User.ImportResult
|
|
for _, imp := range users {
|
|
us := &v1User.User{
|
|
NickName: imp.NickName,
|
|
Metadata: v1.Metadata{
|
|
Name: imp.Name,
|
|
},
|
|
Type: v1User.LDAP,
|
|
Email: imp.Email,
|
|
}
|
|
if us.Email == "" {
|
|
us.Email = us.Name + "@example.com"
|
|
}
|
|
if us.NickName == "" {
|
|
us.NickName = us.Name
|
|
}
|
|
|
|
result.Failures = append(result.Failures, us.Name)
|
|
tx, err := server.DB().Begin(true)
|
|
if err != nil {
|
|
server.Logger().Errorf("create tx err: %s", err)
|
|
continue
|
|
}
|
|
err = l.userService.Create(us, common.DBOptions{DB: tx})
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
server.Logger().Errorf("can not insert user %s , err: %s", us.Name, err)
|
|
continue
|
|
}
|
|
roleName := "Common User"
|
|
binding := v1Role.Binding{
|
|
BaseModel: v1.BaseModel{
|
|
Kind: "RoleBind",
|
|
ApiVersion: "v1",
|
|
CreatedBy: "admin",
|
|
},
|
|
Metadata: v1.Metadata{
|
|
Name: fmt.Sprintf("role-binding-%s-%s", roleName, us.Name),
|
|
},
|
|
Subject: v1Role.Subject{
|
|
Kind: "User",
|
|
Name: us.Name,
|
|
},
|
|
RoleRef: roleName,
|
|
}
|
|
if err := l.roleBindingService.CreateRoleBinding(&binding, common.DBOptions{DB: tx}); err != nil {
|
|
_ = tx.Rollback()
|
|
server.Logger().Errorf("can not create user role %s , err: %s", us.Name, err)
|
|
continue
|
|
}
|
|
_ = tx.Commit()
|
|
result.Failures = result.Failures[:len(result.Failures)-1]
|
|
}
|
|
if len(result.Failures) == 0 {
|
|
result.Success = true
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (l *service) Sync(id string, options common.DBOptions) error {
|
|
ldap, err := l.GetById(id, options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lc := ldapClient.NewLdapClient(ldap.Address, ldap.Port, ldap.Username, ldap.Password, ldap.TLS)
|
|
if err := lc.Connect(); err != nil {
|
|
return err
|
|
}
|
|
go func() {
|
|
server.Logger().Info("start sync ldap user")
|
|
insertCount := 0
|
|
attributes, err := ldap.GetAttributes()
|
|
if err != nil {
|
|
server.Logger().Errorf("can not get ldap map attributes")
|
|
return
|
|
}
|
|
mappings, err := ldap.GetMappings()
|
|
if err != nil {
|
|
server.Logger().Errorf("can not get ldap mappings")
|
|
return
|
|
}
|
|
entries, err := lc.Search(ldap.Dn, ldap.Filter, ldap.SizeLimit, ldap.TimeLimit, attributes)
|
|
if err != nil {
|
|
server.Logger().Errorf(err.Error())
|
|
return
|
|
}
|
|
for _, entry := range entries {
|
|
us := new(v1User.User)
|
|
rv := reflect.ValueOf(&us).Elem().Elem()
|
|
|
|
for _, at := range entry.Attributes {
|
|
for k, v := range mappings {
|
|
if v == at.Name && len(at.Values) > 0 {
|
|
fv := rv.FieldByName(k)
|
|
if fv.IsValid() {
|
|
fv.Set(reflect.ValueOf(strings.Trim(at.Values[0], " ")))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if us.Email == "" || us.Name == "" {
|
|
continue
|
|
}
|
|
if us.NickName == "" {
|
|
us.NickName = us.Name
|
|
}
|
|
us.Type = v1User.LDAP
|
|
_, err := l.userService.GetByNameOrEmail(us.Name, options)
|
|
if errors.Is(err, storm.ErrNotFound) {
|
|
tx, err := server.DB().Begin(true)
|
|
if err != nil {
|
|
server.Logger().Errorf("create tx err: %s", err)
|
|
}
|
|
err = l.userService.Create(us, common.DBOptions{DB: tx})
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
server.Logger().Errorf("can not insert user %s , err: %s", us.Name, err)
|
|
continue
|
|
}
|
|
roleName := "Common User"
|
|
binding := v1Role.Binding{
|
|
BaseModel: v1.BaseModel{
|
|
Kind: "RoleBind",
|
|
ApiVersion: "v1",
|
|
CreatedBy: "admin",
|
|
},
|
|
Metadata: v1.Metadata{
|
|
Name: fmt.Sprintf("role-binding-%s-%s", roleName, us.Name),
|
|
},
|
|
Subject: v1Role.Subject{
|
|
Kind: "User",
|
|
Name: us.Name,
|
|
},
|
|
RoleRef: roleName,
|
|
}
|
|
if err := l.roleBindingService.CreateRoleBinding(&binding, common.DBOptions{DB: tx}); err != nil {
|
|
_ = tx.Rollback()
|
|
server.Logger().Errorf("can not create user role %s , err: %s", us.Name, err)
|
|
continue
|
|
}
|
|
_ = tx.Commit()
|
|
insertCount++
|
|
}
|
|
}
|
|
|
|
server.Logger().Infof("sync ldap user %d , insert user %d", len(entries), insertCount)
|
|
}()
|
|
|
|
return nil
|
|
}
|