Files
openlan/pkg/cache/user.go
2024-04-09 20:12:54 +08:00

252 lines
4.8 KiB
Go
Executable File

package cache
import (
"bufio"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"strings"
"sync"
"time"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/models"
)
type user struct {
Lock sync.RWMutex
File string
Cert string
Users *libol.SafeStrMap
LdapCfg *libol.LDAPConfig
LdapSvc *libol.LDAPService
}
func (w *user) Load() {
file := w.File
reader, err := libol.OpenRead(file)
if err != nil {
return
}
defer reader.Close()
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
columns := strings.SplitN(line, ":", 4)
if len(columns) < 2 {
continue
}
user := columns[0]
pass := columns[1]
role := "guest"
leStr := ""
if len(columns) > 2 {
role = columns[2]
}
if len(columns) > 3 {
leStr = columns[3]
}
lease, _ := libol.GetLeaseTime(leStr)
obj := &models.User{
Name: user,
Password: pass,
Role: role,
Lease: lease,
}
obj.Update()
w.Add(obj)
}
if err := scanner.Err(); err != nil {
libol.Warn("User.Load %v", err)
}
}
func (w *user) Save() error {
if w.File == "" {
return nil
}
fp, err := libol.OpenTrunk(w.File)
if err != nil {
return err
}
for obj := range w.List() {
if obj == nil {
break
}
if obj.Role == "ldap" {
continue
}
line := obj.Id()
line += ":" + obj.Password
line += ":" + obj.Role
line += ":" + obj.Lease.Format(libol.LeaseTime)
_, _ = fp.WriteString(line + "\n")
}
return nil
}
func (w *user) SetFile(value string) {
w.File = value
}
func (w *user) Init(size int) {
w.Users = libol.NewSafeStrMap(size)
}
func (w *user) Add(user *models.User) {
libol.Debug("user.Add %v", user)
key := user.Id()
if older := w.Get(key); older == nil {
_ = w.Users.Set(key, user)
} else { // Update pass and role.
if user.Role != "" {
older.Role = user.Role
}
if user.Password != "" {
older.Password = user.Password
}
if user.Alias != "" {
older.Alias = user.Alias
}
older.UpdateAt = user.UpdateAt
if !user.Lease.IsZero() {
older.Lease = user.Lease
}
}
}
func (w *user) Del(key string) {
libol.Debug("user.Add %s", key)
w.Users.Del(key)
}
func (w *user) Get(key string) *models.User {
if v := w.Users.Get(key); v != nil {
return v.(*models.User)
}
return nil
}
func (w *user) List() <-chan *models.User {
c := make(chan *models.User, 128)
go func() {
w.Users.Iter(func(k string, v interface{}) {
c <- v.(*models.User)
})
c <- nil //Finish channel by nil.
}()
return c
}
func (w *user) CheckLdap(obj *models.User) *models.User {
svc := w.GetLdap()
if svc == nil {
return nil
}
u := w.Get(obj.Id())
libol.Debug("CheckLdap %s", u)
if u != nil && u.Role != "ldap" {
return nil
}
if ok, err := svc.Login(obj.Id(), obj.Password); !ok {
libol.Warn("CheckLdap %s", err)
return nil
}
user := &models.User{
Name: obj.Id(),
Password: obj.Password,
Role: "ldap",
Alias: obj.Alias,
}
user.Update()
w.Add(user)
return user
}
func (w *user) Timeout(user *models.User) bool {
if user.Role == "ldap" {
return time.Now().Unix()-user.UpdateAt > w.LdapCfg.Timeout
}
return true
}
func (w *user) Check(obj *models.User) (*models.User, error) {
if w.Cert != "" {
pemData, err := ioutil.ReadFile(w.Cert)
if err != nil {
return nil, err
}
block, rest := pem.Decode(pemData)
if block == nil || len(rest) > 0 {
return nil, libol.NewErr("certificate decoding error")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
now := time.Now()
if now.Before(cert.NotBefore) {
return nil, libol.NewErr("certificate isn't yet valid")
} else if now.After(cert.NotAfter) {
return nil, libol.NewErr("certificate has expired")
}
}
if u := w.Get(obj.Id()); u != nil {
if u.Role == "" || u.Role == "admin" || u.Role == "guest" {
if u.Password == obj.Password {
t0 := time.Now()
t1 := u.Lease
if t1.Year() < 2000 || t1.After(t0) {
return u, nil
}
return nil, libol.NewErr("out of date")
}
}
}
if u := w.CheckLdap(obj); u != nil {
return u, nil
}
return nil, libol.NewErr("wrong password")
}
func (w *user) GetLdap() *libol.LDAPService {
w.Lock.Lock()
defer w.Lock.Unlock()
if w.LdapCfg == nil {
return nil
}
if w.LdapSvc == nil || w.LdapSvc.Conn.IsClosing() {
if l, err := libol.NewLDAPService(*w.LdapCfg); err != nil {
libol.Warn("user.GetLdap %s", err)
w.LdapSvc = nil
} else {
w.LdapSvc = l
}
}
return w.LdapSvc
}
func (w *user) SetLdap(cfg *libol.LDAPConfig) error {
w.Lock.Lock()
defer w.Lock.Unlock()
if l, err := libol.NewLDAPService(*cfg); err != nil {
libol.Warn("user.SetLdap %s", err)
return err
} else {
w.LdapCfg = cfg
w.LdapSvc = l
libol.Info("user.SetLdap %s", w.LdapCfg.Server)
}
return nil
}
func (w *user) SetCert(cfg *libol.CertConfig) {
w.Cert = cfg.Crt
}
var User = user{
Users: libol.NewSafeStrMap(1024),
}