2018-11-07 21:16:13 +00:00
|
|
|
package usermanager
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
2018-11-22 21:55:23 +00:00
|
|
|
"encoding/hex"
|
2018-11-07 21:16:13 +00:00
|
|
|
"errors"
|
2018-11-22 21:55:23 +00:00
|
|
|
"os"
|
|
|
|
"strconv"
|
2018-11-07 21:16:13 +00:00
|
|
|
"sync"
|
2018-11-11 19:30:40 +00:00
|
|
|
"time"
|
2018-11-08 19:47:53 +00:00
|
|
|
|
|
|
|
"github.com/boltdb/bolt"
|
2018-11-07 21:16:13 +00:00
|
|
|
)
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
var Uint32 = binary.BigEndian.Uint32
|
|
|
|
var Uint64 = binary.BigEndian.Uint64
|
|
|
|
var PutUint16 = binary.BigEndian.PutUint16
|
|
|
|
var PutUint32 = binary.BigEndian.PutUint32
|
|
|
|
var PutUint64 = binary.BigEndian.PutUint64
|
|
|
|
|
2018-11-07 21:16:13 +00:00
|
|
|
type Userpanel struct {
|
|
|
|
db *bolt.DB
|
|
|
|
|
|
|
|
activeUsersM sync.RWMutex
|
2018-11-08 19:47:53 +00:00
|
|
|
activeUsers map[[32]byte]*User
|
2018-11-07 21:16:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func MakeUserpanel(dbPath string) (*Userpanel, error) {
|
|
|
|
db, err := bolt.Open(dbPath, 0600, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
up := &Userpanel{
|
|
|
|
db: db,
|
2018-11-08 19:47:53 +00:00
|
|
|
activeUsers: make(map[[32]byte]*User),
|
2018-11-07 21:16:13 +00:00
|
|
|
}
|
2018-11-11 19:30:40 +00:00
|
|
|
go func() {
|
2018-11-22 21:55:23 +00:00
|
|
|
for {
|
|
|
|
time.Sleep(time.Second * 10)
|
|
|
|
up.updateCredits()
|
|
|
|
}
|
2018-11-11 19:30:40 +00:00
|
|
|
}()
|
2018-11-07 21:16:13 +00:00
|
|
|
return up, nil
|
|
|
|
}
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
// credits of all users are updated together so that there is only 1 goroutine managing it
|
|
|
|
func (up *Userpanel) updateCredits() {
|
|
|
|
up.activeUsersM.RLock()
|
|
|
|
for _, u := range up.activeUsers {
|
|
|
|
up.db.Update(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket(u.arrUID[:])
|
|
|
|
if b == nil {
|
|
|
|
return ErrUserNotFound
|
|
|
|
}
|
|
|
|
if err := b.Put([]byte("UpCredit"), i64ToB(u.valve.GetRxCredit())); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := b.Put([]byte("DownCredit"), i64ToB(u.valve.GetTxCredit())); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
up.activeUsersM.RUnlock()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: prefixed backup path
|
|
|
|
func (up *Userpanel) backupDB(bakPath string) error {
|
|
|
|
_, err := os.Stat(bakPath)
|
|
|
|
if err == nil {
|
|
|
|
return errors.New("Attempting to overwrite a file during backup!")
|
|
|
|
}
|
|
|
|
var bak *os.File
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
bak, err = os.Create(bakPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = up.db.View(func(tx *bolt.Tx) error {
|
|
|
|
_, err := tx.WriteTo(bak)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var ErrUserNotFound = errors.New("User does not exist in db")
|
|
|
|
var ErrUserNotActive = errors.New("User is not active")
|
|
|
|
|
2018-12-11 23:26:05 +00:00
|
|
|
func (up *Userpanel) GetAndActivateAdminUser(AdminUID []byte) (*User, error) {
|
|
|
|
up.activeUsersM.Lock()
|
|
|
|
var arrUID [32]byte
|
|
|
|
copy(arrUID[:], AdminUID)
|
|
|
|
if user, ok := up.activeUsers[arrUID]; ok {
|
|
|
|
up.activeUsersM.Unlock()
|
|
|
|
return user, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
uinfo := UserInfo{
|
|
|
|
UID: AdminUID,
|
|
|
|
SessionsCap: 1e9,
|
|
|
|
UpRate: 1e12,
|
|
|
|
DownRate: 1e12,
|
|
|
|
UpCredit: 1e15,
|
|
|
|
DownCredit: 1e15,
|
|
|
|
ExpiryTime: 1e15,
|
|
|
|
}
|
|
|
|
|
|
|
|
user := MakeUser(up, &uinfo)
|
|
|
|
up.activeUsers[arrUID] = user
|
|
|
|
up.activeUsersM.Unlock()
|
|
|
|
return user, nil
|
|
|
|
}
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
// TODO: expiry check
|
2018-11-07 21:16:13 +00:00
|
|
|
|
|
|
|
// GetUser is used to retrieve a user if s/he is active, or to retrieve the user's infor
|
|
|
|
// from the db and mark it as an active user
|
2018-11-22 21:55:23 +00:00
|
|
|
func (up *Userpanel) GetAndActivateUser(UID []byte) (*User, error) {
|
2018-11-08 19:47:53 +00:00
|
|
|
up.activeUsersM.Lock()
|
2018-11-22 21:55:23 +00:00
|
|
|
var arrUID [32]byte
|
|
|
|
copy(arrUID[:], UID)
|
|
|
|
if user, ok := up.activeUsers[arrUID]; ok {
|
|
|
|
up.activeUsersM.Unlock()
|
2018-11-07 21:16:13 +00:00
|
|
|
return user, nil
|
|
|
|
}
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
var uinfo UserInfo
|
|
|
|
uinfo.UID = UID
|
2018-11-07 21:16:13 +00:00
|
|
|
err := up.db.View(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket(UID[:])
|
|
|
|
if b == nil {
|
|
|
|
return ErrUserNotFound
|
|
|
|
}
|
2018-11-22 21:55:23 +00:00
|
|
|
uinfo.SessionsCap = Uint32(b.Get([]byte("SessionsCap")))
|
|
|
|
uinfo.UpRate = int64(Uint64(b.Get([]byte("UpRate"))))
|
|
|
|
uinfo.DownRate = int64(Uint64(b.Get([]byte("DownRate"))))
|
|
|
|
uinfo.UpCredit = int64(Uint64(b.Get([]byte("UpCredit")))) // reee brackets
|
|
|
|
uinfo.DownCredit = int64(Uint64(b.Get([]byte("DownCredit"))))
|
|
|
|
uinfo.ExpiryTime = int64(Uint64(b.Get([]byte("ExpiryTime"))))
|
2018-11-07 21:16:13 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
2018-11-22 21:55:23 +00:00
|
|
|
up.activeUsersM.Unlock()
|
2018-11-07 21:16:13 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2018-12-08 15:30:46 +00:00
|
|
|
u := MakeUser(up, &uinfo)
|
2018-11-22 21:55:23 +00:00
|
|
|
up.activeUsers[arrUID] = u
|
|
|
|
up.activeUsersM.Unlock()
|
2018-11-07 21:16:13 +00:00
|
|
|
return u, nil
|
|
|
|
}
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
func (up *Userpanel) updateDBEntryUint32(UID []byte, key string, value uint32) error {
|
2018-11-07 21:16:13 +00:00
|
|
|
err := up.db.Update(func(tx *bolt.Tx) error {
|
2018-11-22 21:55:23 +00:00
|
|
|
b := tx.Bucket(UID)
|
|
|
|
if b == nil {
|
|
|
|
return ErrUserNotFound
|
2018-11-07 21:16:13 +00:00
|
|
|
}
|
2018-11-22 21:55:23 +00:00
|
|
|
if err := b.Put([]byte(key), u32ToB(value)); err != nil {
|
2018-11-07 21:16:13 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
func (up *Userpanel) updateDBEntryInt64(UID []byte, key string, value int64) error {
|
2018-11-07 21:16:13 +00:00
|
|
|
err := up.db.Update(func(tx *bolt.Tx) error {
|
2018-11-22 21:55:23 +00:00
|
|
|
b := tx.Bucket(UID)
|
2018-11-07 21:16:13 +00:00
|
|
|
if b == nil {
|
|
|
|
return ErrUserNotFound
|
|
|
|
}
|
2018-11-22 21:55:23 +00:00
|
|
|
if err := b.Put([]byte(key), i64ToB(value)); err != nil {
|
2018-11-07 21:16:13 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
// This is used when all sessions of a user close
|
|
|
|
func (up *Userpanel) delActiveUser(UID []byte) {
|
|
|
|
var arrUID [32]byte
|
|
|
|
copy(arrUID[:], UID)
|
|
|
|
up.activeUsersM.Lock()
|
|
|
|
delete(up.activeUsers, arrUID)
|
|
|
|
up.activeUsersM.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (up *Userpanel) getActiveUser(UID []byte) *User {
|
|
|
|
var arrUID [32]byte
|
|
|
|
copy(arrUID[:], UID)
|
|
|
|
up.activeUsersM.RLock()
|
|
|
|
ret := up.activeUsers[arrUID]
|
|
|
|
up.activeUsersM.RUnlock()
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// below are remote control utilised functions
|
|
|
|
|
|
|
|
func (up *Userpanel) listActiveUsers() [][]byte {
|
|
|
|
var ret [][]byte
|
|
|
|
up.activeUsersM.RLock()
|
|
|
|
for _, u := range up.activeUsers {
|
|
|
|
ret = append(ret, u.UID)
|
|
|
|
}
|
|
|
|
up.activeUsersM.RUnlock()
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (up *Userpanel) listAllUsers() []UserInfo {
|
|
|
|
var ret []UserInfo
|
|
|
|
up.db.View(func(tx *bolt.Tx) error {
|
|
|
|
tx.ForEach(func(UID []byte, b *bolt.Bucket) error {
|
|
|
|
// if we want to avoid writing every single key out,
|
|
|
|
// we would have to either make UserInfo a map,
|
|
|
|
// or use reflect.
|
|
|
|
// neither is convinient
|
|
|
|
var uinfo UserInfo
|
|
|
|
uinfo.UID = UID
|
|
|
|
uinfo.SessionsCap = Uint32(b.Get([]byte("SessionsCap")))
|
|
|
|
uinfo.UpRate = int64(Uint64(b.Get([]byte("UpRate"))))
|
|
|
|
uinfo.DownRate = int64(Uint64(b.Get([]byte("DownRate"))))
|
|
|
|
uinfo.UpCredit = int64(Uint64(b.Get([]byte("UpCredit"))))
|
|
|
|
uinfo.DownCredit = int64(Uint64(b.Get([]byte("DownCredit"))))
|
|
|
|
uinfo.ExpiryTime = int64(Uint64(b.Get([]byte("ExpiryTime"))))
|
|
|
|
ret = append(ret, uinfo)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (up *Userpanel) getUserInfo(UID []byte) (UserInfo, error) {
|
|
|
|
var uinfo UserInfo
|
|
|
|
err := up.db.View(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket(UID)
|
2018-11-07 21:16:13 +00:00
|
|
|
if b == nil {
|
|
|
|
return ErrUserNotFound
|
|
|
|
}
|
2018-11-22 21:55:23 +00:00
|
|
|
uinfo.UID = UID
|
|
|
|
uinfo.SessionsCap = Uint32(b.Get([]byte("SessionsCap")))
|
|
|
|
uinfo.UpRate = int64(Uint64(b.Get([]byte("UpRate"))))
|
|
|
|
uinfo.DownRate = int64(Uint64(b.Get([]byte("DownRate"))))
|
|
|
|
uinfo.UpCredit = int64(Uint64(b.Get([]byte("UpCredit"))))
|
|
|
|
uinfo.DownCredit = int64(Uint64(b.Get([]byte("DownCredit"))))
|
|
|
|
uinfo.ExpiryTime = int64(Uint64(b.Get([]byte("ExpiryTime"))))
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return uinfo, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// In boltdb, the value argument for bucket.Put has to be valid for the duration
|
|
|
|
// of the transaction.
|
|
|
|
// This basically means that you cannot reuse a byte slice for two different keys
|
|
|
|
// in a transaction. So we need to allocate a fresh byte slice for each value
|
|
|
|
func u32ToB(value uint32) []byte {
|
|
|
|
quad := make([]byte, 4)
|
|
|
|
PutUint32(quad, value)
|
|
|
|
return quad
|
|
|
|
}
|
|
|
|
|
|
|
|
func i64ToB(value int64) []byte {
|
|
|
|
oct := make([]byte, 8)
|
|
|
|
PutUint64(oct, uint64(value))
|
|
|
|
return oct
|
|
|
|
}
|
|
|
|
|
|
|
|
func (up *Userpanel) addNewUser(uinfo UserInfo) error {
|
|
|
|
err := up.db.Update(func(tx *bolt.Tx) error {
|
|
|
|
b, err := tx.CreateBucket(uinfo.UID[:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = b.Put([]byte("SessionsCap"), u32ToB(uinfo.SessionsCap)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = b.Put([]byte("UpRate"), i64ToB(uinfo.UpRate)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = b.Put([]byte("DownRate"), i64ToB(uinfo.DownRate)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = b.Put([]byte("UpCredit"), i64ToB(uinfo.UpCredit)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = b.Put([]byte("DownCredit"), i64ToB(uinfo.DownCredit)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = b.Put([]byte("ExpiryTime"), i64ToB(uinfo.ExpiryTime)); err != nil {
|
2018-11-07 21:16:13 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
func (up *Userpanel) delUser(UID []byte) error {
|
|
|
|
err := up.backupDB(strconv.FormatInt(time.Now().Unix(), 10) + "_pre_del_" + hex.EncodeToString(UID) + ".bak")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = up.db.Update(func(tx *bolt.Tx) error {
|
|
|
|
return tx.DeleteBucket(UID)
|
|
|
|
})
|
|
|
|
return err
|
2018-11-07 21:16:13 +00:00
|
|
|
}
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
func (up *Userpanel) syncMemFromDB(UID []byte) error {
|
|
|
|
var uinfo UserInfo
|
|
|
|
err := up.db.View(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket(UID)
|
|
|
|
if b == nil {
|
|
|
|
return ErrUserNotFound
|
|
|
|
}
|
|
|
|
uinfo.UID = UID
|
|
|
|
uinfo.SessionsCap = Uint32(b.Get([]byte("SessionsCap")))
|
|
|
|
uinfo.UpRate = int64(Uint64(b.Get([]byte("UpRate"))))
|
|
|
|
uinfo.DownRate = int64(Uint64(b.Get([]byte("DownRate"))))
|
|
|
|
uinfo.UpCredit = int64(Uint64(b.Get([]byte("UpCredit"))))
|
|
|
|
uinfo.DownCredit = int64(Uint64(b.Get([]byte("DownCredit"))))
|
|
|
|
uinfo.ExpiryTime = int64(Uint64(b.Get([]byte("ExpiryTime"))))
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
u := up.getActiveUser(UID)
|
|
|
|
if u == nil {
|
|
|
|
return ErrUserNotActive
|
|
|
|
}
|
|
|
|
u.updateInfo(uinfo)
|
|
|
|
return nil
|
2018-11-07 21:16:13 +00:00
|
|
|
}
|
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
// the following functions will return err==nil if user is not active
|
|
|
|
|
|
|
|
func (up *Userpanel) setSessionsCap(UID []byte, cap uint32) error {
|
|
|
|
err := up.updateDBEntryUint32(UID, "SessionsCap", cap)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-11-07 21:16:13 +00:00
|
|
|
}
|
2018-11-22 21:55:23 +00:00
|
|
|
u := up.getActiveUser(UID)
|
|
|
|
if u == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
u.setSessionsCap(cap)
|
|
|
|
return nil
|
2018-11-07 21:16:13 +00:00
|
|
|
}
|
2018-11-11 19:30:40 +00:00
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
func (up *Userpanel) setUpRate(UID []byte, rate int64) error {
|
|
|
|
err := up.updateDBEntryInt64(UID, "UpRate", rate)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
u := up.getActiveUser(UID)
|
|
|
|
if u == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
u.setUpRate(rate)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (up *Userpanel) setDownRate(UID []byte, rate int64) error {
|
|
|
|
err := up.updateDBEntryInt64(UID, "DownRate", rate)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
u := up.getActiveUser(UID)
|
|
|
|
if u == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
u.setDownRate(rate)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (up *Userpanel) setUpCredit(UID []byte, n int64) error {
|
|
|
|
err := up.updateDBEntryInt64(UID, "UpCredit", n)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
u := up.getActiveUser(UID)
|
|
|
|
if u == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
u.setUpCredit(n)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (up *Userpanel) setDownCredit(UID []byte, n int64) error {
|
|
|
|
err := up.updateDBEntryInt64(UID, "DownCredit", n)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
u := up.getActiveUser(UID)
|
|
|
|
if u == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
u.setDownCredit(n)
|
|
|
|
return nil
|
|
|
|
}
|
2018-11-11 19:30:40 +00:00
|
|
|
|
2018-11-22 21:55:23 +00:00
|
|
|
func (up *Userpanel) setExpiryTime(UID []byte, time int64) error {
|
|
|
|
err := up.updateDBEntryInt64(UID, "ExpiryTime", time)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
u := up.getActiveUser(UID)
|
|
|
|
if u == nil {
|
|
|
|
return nil
|
2018-11-11 19:30:40 +00:00
|
|
|
}
|
2018-11-22 21:55:23 +00:00
|
|
|
u.setExpiryTime(time)
|
|
|
|
return nil
|
2018-11-11 19:30:40 +00:00
|
|
|
}
|