Cloak/internal/server/usermanager/localmanager.go

253 lines
7.0 KiB
Go
Raw Normal View History

2019-08-03 10:17:09 +00:00
package usermanager
import (
"encoding/binary"
2020-04-17 13:21:17 +00:00
"github.com/cbeuw/Cloak/internal/common"
2019-08-02 14:45:33 +00:00
log "github.com/sirupsen/logrus"
bolt "go.etcd.io/bbolt"
)
2020-08-17 16:56:05 +00:00
var u32 = binary.BigEndian.Uint32
var u64 = binary.BigEndian.Uint64
func i64ToB(value int64) []byte {
oct := make([]byte, 8)
2020-04-17 13:21:17 +00:00
binary.BigEndian.PutUint64(oct, uint64(value))
return oct
}
func i32ToB(value int32) []byte {
nib := make([]byte, 4)
2020-04-17 13:21:17 +00:00
binary.BigEndian.PutUint32(nib, uint32(value))
return nib
}
2020-04-17 13:21:17 +00:00
// localManager is responsible for managing the local user database
type localManager struct {
2020-04-17 13:21:17 +00:00
db *bolt.DB
world common.WorldState
}
2020-04-17 13:21:17 +00:00
func MakeLocalManager(dbPath string, worldState common.WorldState) (*localManager, error) {
db, err := bolt.Open(dbPath, 0600, nil)
if err != nil {
return nil, err
}
ret := &localManager{
2020-04-17 13:21:17 +00:00
db: db,
world: worldState,
}
return ret, nil
}
2019-08-20 21:43:04 +00:00
// Authenticate user returns err==nil along with the users' up and down bandwidths if the UID is allowed to connect
// More specifically it checks that the user exists, that it has positive credit and that it hasn't expired
2019-08-03 10:17:09 +00:00
func (manager *localManager) AuthenticateUser(UID []byte) (int64, int64, error) {
var upRate, downRate, upCredit, downCredit, expiryTime int64
err := manager.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(UID)
if bucket == nil {
return ErrUserNotFound
}
2020-08-17 16:56:05 +00:00
upRate = int64(u64(bucket.Get([]byte("UpRate"))))
downRate = int64(u64(bucket.Get([]byte("DownRate"))))
upCredit = int64(u64(bucket.Get([]byte("UpCredit"))))
downCredit = int64(u64(bucket.Get([]byte("DownCredit"))))
expiryTime = int64(u64(bucket.Get([]byte("ExpiryTime"))))
return nil
})
if err != nil {
return 0, 0, err
}
if upCredit <= 0 {
return 0, 0, ErrNoUpCredit
}
if downCredit <= 0 {
return 0, 0, ErrNoDownCredit
}
2020-04-17 13:21:17 +00:00
if expiryTime < manager.world.Now().Unix() {
return 0, 0, ErrUserExpired
}
return upRate, downRate, nil
}
2019-08-20 21:43:04 +00:00
// AuthoriseNewSession returns err==nil when the user is allowed to make a new session
// More specifically it checks that the user exists, has credit, hasn't expired and hasn't reached sessionsCap
2019-08-20 16:35:17 +00:00
func (manager *localManager) AuthoriseNewSession(UID []byte, ainfo AuthorisationInfo) error {
2019-08-03 10:17:09 +00:00
var arrUID [16]byte
copy(arrUID[:], UID)
var sessionsCap int
var upCredit, downCredit, expiryTime int64
err := manager.db.View(func(tx *bolt.Tx) error {
2019-08-03 10:17:09 +00:00
bucket := tx.Bucket(arrUID[:])
if bucket == nil {
return ErrUserNotFound
}
2020-08-17 16:56:05 +00:00
sessionsCap = int(u32(bucket.Get([]byte("SessionsCap"))))
upCredit = int64(u64(bucket.Get([]byte("UpCredit"))))
downCredit = int64(u64(bucket.Get([]byte("DownCredit"))))
expiryTime = int64(u64(bucket.Get([]byte("ExpiryTime"))))
return nil
})
if err != nil {
return err
}
if upCredit <= 0 {
return ErrNoUpCredit
}
if downCredit <= 0 {
return ErrNoDownCredit
}
2020-04-17 13:21:17 +00:00
if expiryTime < manager.world.Now().Unix() {
return ErrUserExpired
}
2019-08-20 16:35:17 +00:00
if ainfo.NumExistingSessions >= sessionsCap {
return ErrSessionsCapReached
}
return nil
}
2019-08-20 21:43:04 +00:00
// UploadStatus gets StatusUpdates representing the recent status of each user, and update them in the database
// it returns a slice of StatusResponse, which represents actions need to be taken for specific users.
// If no action is needed, there won't be a StatusResponse entry for that user
2019-08-03 10:17:09 +00:00
func (manager *localManager) UploadStatus(uploads []StatusUpdate) ([]StatusResponse, error) {
var responses []StatusResponse
2020-04-12 00:56:33 +00:00
if len(uploads) == 0 {
return responses, nil
}
err := manager.db.Update(func(tx *bolt.Tx) error {
for _, status := range uploads {
2019-08-03 10:17:09 +00:00
var resp StatusResponse
bucket := tx.Bucket(status.UID)
if bucket == nil {
2019-08-03 10:17:09 +00:00
resp = StatusResponse{
status.UID,
TERMINATE,
"User no longer exists",
}
responses = append(responses, resp)
}
2019-07-24 14:25:09 +00:00
2020-08-17 16:56:05 +00:00
oldUp := int64(u64(bucket.Get([]byte("UpCredit"))))
2019-08-03 10:17:09 +00:00
newUp := oldUp - status.UpUsage
2019-07-24 14:25:09 +00:00
if newUp <= 0 {
2019-08-03 10:17:09 +00:00
resp = StatusResponse{
2019-07-24 14:25:09 +00:00
status.UID,
TERMINATE,
"No upload credit left",
}
responses = append(responses, resp)
}
err := bucket.Put([]byte("UpCredit"), i64ToB(newUp))
if err != nil {
2019-08-02 14:45:33 +00:00
log.Error(err)
2019-07-24 14:25:09 +00:00
}
2020-08-17 16:56:05 +00:00
oldDown := int64(u64(bucket.Get([]byte("DownCredit"))))
2019-08-03 10:17:09 +00:00
newDown := oldDown - status.DownUsage
2019-07-24 14:25:09 +00:00
if newDown <= 0 {
2019-08-03 10:17:09 +00:00
resp = StatusResponse{
2019-07-24 14:25:09 +00:00
status.UID,
TERMINATE,
"No download credit left",
}
responses = append(responses, resp)
}
err = bucket.Put([]byte("DownCredit"), i64ToB(newDown))
if err != nil {
2019-08-02 14:45:33 +00:00
log.Error(err)
2019-07-24 14:25:09 +00:00
}
2020-08-17 16:56:05 +00:00
expiry := int64(u64(bucket.Get([]byte("ExpiryTime"))))
2020-04-17 13:21:17 +00:00
if manager.world.Now().Unix() > expiry {
2019-08-03 10:17:09 +00:00
resp = StatusResponse{
2019-07-24 14:25:09 +00:00
status.UID,
TERMINATE,
"User has expired",
}
responses = append(responses, resp)
}
}
return nil
})
2019-07-24 14:25:09 +00:00
return responses, err
}
2019-08-04 20:10:59 +00:00
2020-04-17 13:21:17 +00:00
func (manager *localManager) ListAllUsers() (infos []UserInfo, err error) {
err = manager.db.View(func(tx *bolt.Tx) error {
err = tx.ForEach(func(UID []byte, bucket *bolt.Bucket) error {
var uinfo UserInfo
uinfo.UID = UID
2020-08-17 16:56:05 +00:00
uinfo.SessionsCap = int32(u32(bucket.Get([]byte("SessionsCap"))))
uinfo.UpRate = int64(u64(bucket.Get([]byte("UpRate"))))
uinfo.DownRate = int64(u64(bucket.Get([]byte("DownRate"))))
uinfo.UpCredit = int64(u64(bucket.Get([]byte("UpCredit"))))
uinfo.DownCredit = int64(u64(bucket.Get([]byte("DownCredit"))))
uinfo.ExpiryTime = int64(u64(bucket.Get([]byte("ExpiryTime"))))
2020-04-17 13:21:17 +00:00
infos = append(infos, uinfo)
return nil
})
return err
})
return
}
func (manager *localManager) GetUserInfo(UID []byte) (uinfo UserInfo, err error) {
err = manager.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(UID)
if bucket == nil {
return ErrUserNotFound
}
uinfo.UID = UID
2020-08-17 16:56:05 +00:00
uinfo.SessionsCap = int32(u32(bucket.Get([]byte("SessionsCap"))))
uinfo.UpRate = int64(u64(bucket.Get([]byte("UpRate"))))
uinfo.DownRate = int64(u64(bucket.Get([]byte("DownRate"))))
uinfo.UpCredit = int64(u64(bucket.Get([]byte("UpCredit"))))
uinfo.DownCredit = int64(u64(bucket.Get([]byte("DownCredit"))))
uinfo.ExpiryTime = int64(u64(bucket.Get([]byte("ExpiryTime"))))
2020-04-17 13:21:17 +00:00
return nil
})
return
}
func (manager *localManager) WriteUserInfo(uinfo UserInfo) (err error) {
err = manager.db.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(uinfo.UID)
if err != nil {
return err
}
if err = bucket.Put([]byte("SessionsCap"), i32ToB(int32(uinfo.SessionsCap))); err != nil {
return err
}
if err = bucket.Put([]byte("UpRate"), i64ToB(uinfo.UpRate)); err != nil {
return err
}
if err = bucket.Put([]byte("DownRate"), i64ToB(uinfo.DownRate)); err != nil {
return err
}
if err = bucket.Put([]byte("UpCredit"), i64ToB(uinfo.UpCredit)); err != nil {
return err
}
if err = bucket.Put([]byte("DownCredit"), i64ToB(uinfo.DownCredit)); err != nil {
return err
}
if err = bucket.Put([]byte("ExpiryTime"), i64ToB(uinfo.ExpiryTime)); err != nil {
return err
}
return nil
})
return
}
func (manager *localManager) DeleteUser(UID []byte) (err error) {
err = manager.db.Update(func(tx *bolt.Tx) error {
return tx.DeleteBucket(UID)
})
return
}
2019-08-04 20:10:59 +00:00
func (manager *localManager) Close() error {
return manager.db.Close()
}