Cloak/internal/server/userpanel.go

212 lines
5.7 KiB
Go
Raw Normal View History

package server
import (
2020-04-24 00:05:37 +00:00
"encoding/base64"
2019-08-03 10:17:09 +00:00
"github.com/cbeuw/Cloak/internal/server/usermanager"
"sync"
"sync/atomic"
"time"
mux "github.com/cbeuw/Cloak/internal/multiplex"
2019-08-02 14:45:33 +00:00
log "github.com/sirupsen/logrus"
)
2020-04-06 19:51:21 +00:00
// userPanel is used to authenticate new users and book keep active users
type userPanel struct {
2019-08-03 10:17:09 +00:00
Manager usermanager.UserManager
activeUsersM sync.RWMutex
activeUsers map[[16]byte]*ActiveUser
usageUpdateQueueM sync.Mutex
usageUpdateQueue map[[16]byte]*usagePair
}
2019-08-03 10:17:09 +00:00
func MakeUserPanel(manager usermanager.UserManager) *userPanel {
ret := &userPanel{
Manager: manager,
activeUsers: make(map[[16]byte]*ActiveUser),
usageUpdateQueue: make(map[[16]byte]*usagePair),
}
go ret.regularQueueUpload()
return ret
}
2019-08-20 21:43:04 +00:00
// GetBypassUser does the same as GetUser except it unconditionally creates an ActiveUser when the UID isn't already active
2019-08-04 20:10:59 +00:00
func (panel *userPanel) GetBypassUser(UID []byte) (*ActiveUser, error) {
panel.activeUsersM.Lock()
2020-04-15 20:55:07 +00:00
defer panel.activeUsersM.Unlock()
2019-08-04 20:10:59 +00:00
var arrUID [16]byte
copy(arrUID[:], UID)
if user, ok := panel.activeUsers[arrUID]; ok {
return user, nil
}
user := &ActiveUser{
panel: panel,
valve: mux.UNLIMITED_VALVE,
sessions: make(map[uint32]*mux.Session),
bypass: true,
}
copy(user.arrUID[:], UID)
panel.activeUsers[user.arrUID] = user
return user, nil
}
2019-08-20 21:43:04 +00:00
// GetUser retrieves the reference to an ActiveUser if it's already active, or creates a new ActiveUser of specified
// UID with UserInfo queried from the UserManger, should the particular UID is allowed to connect
func (panel *userPanel) GetUser(UID []byte) (*ActiveUser, error) {
panel.activeUsersM.Lock()
2020-04-15 20:55:07 +00:00
defer panel.activeUsersM.Unlock()
var arrUID [16]byte
copy(arrUID[:], UID)
if user, ok := panel.activeUsers[arrUID]; ok {
return user, nil
}
2019-08-03 10:17:09 +00:00
upRate, downRate, err := panel.Manager.AuthenticateUser(UID)
if err != nil {
return nil, err
}
valve := mux.MakeValve(upRate, downRate)
user := &ActiveUser{
panel: panel,
valve: valve,
sessions: make(map[uint32]*mux.Session),
}
2019-08-04 20:10:59 +00:00
copy(user.arrUID[:], UID)
panel.activeUsers[user.arrUID] = user
return user, nil
}
2019-08-30 21:14:45 +00:00
// TerminateActiveUser terminates a user and deletes its references
func (panel *userPanel) TerminateActiveUser(user *ActiveUser, reason string) {
2020-04-24 00:05:37 +00:00
log.WithFields(log.Fields{
"UID": base64.StdEncoding.EncodeToString(user.arrUID[:]),
"reason": reason,
}).Info("forcefully terminating user")
2019-08-04 20:10:59 +00:00
panel.updateUsageQueueForOne(user)
2019-08-30 21:14:45 +00:00
user.closeAllSessions(reason)
2019-08-04 20:10:59 +00:00
panel.activeUsersM.Lock()
delete(panel.activeUsers, user.arrUID)
panel.activeUsersM.Unlock()
}
func (panel *userPanel) isActive(UID []byte) bool {
var arrUID [16]byte
copy(arrUID[:], UID)
panel.activeUsersM.RLock()
_, ok := panel.activeUsers[arrUID]
panel.activeUsersM.RUnlock()
return ok
}
type usagePair struct {
up *int64
down *int64
}
2019-08-20 21:43:04 +00:00
// updateUsageQueue zeroes the accumulated usage all ActiveUsers valve and put the usage data im usageUpdateQueue
func (panel *userPanel) updateUsageQueue() {
panel.activeUsersM.Lock()
panel.usageUpdateQueueM.Lock()
for _, user := range panel.activeUsers {
2019-08-04 20:10:59 +00:00
if user.bypass {
continue
}
upIncured, downIncured := user.valve.Nullify()
if usage, ok := panel.usageUpdateQueue[user.arrUID]; ok {
atomic.AddInt64(usage.up, upIncured)
atomic.AddInt64(usage.down, downIncured)
} else {
// if the user hasn't been added to the queue
usage = &usagePair{&upIncured, &downIncured}
panel.usageUpdateQueue[user.arrUID] = usage
}
}
panel.activeUsersM.Unlock()
panel.usageUpdateQueueM.Unlock()
}
2019-08-20 21:43:04 +00:00
// updateUsageQueueForOne is the same as updateUsageQueue except it only updates one user's usage
// this is useful when the user is being terminated
func (panel *userPanel) updateUsageQueueForOne(user *ActiveUser) {
// used when one particular user deactivates
2019-08-04 20:10:59 +00:00
if user.bypass {
return
}
upIncured, downIncured := user.valve.Nullify()
panel.usageUpdateQueueM.Lock()
if usage, ok := panel.usageUpdateQueue[user.arrUID]; ok {
atomic.AddInt64(usage.up, upIncured)
atomic.AddInt64(usage.down, downIncured)
} else {
usage = &usagePair{&upIncured, &downIncured}
panel.usageUpdateQueue[user.arrUID] = usage
}
panel.usageUpdateQueueM.Unlock()
}
2019-08-20 21:43:04 +00:00
// commitUpdate put all usageUpdates into a slice of StatusUpdate, calls Manager.UploadStatus, gets the responses
// and act to each user according to the responses
2019-08-02 14:45:33 +00:00
func (panel *userPanel) commitUpdate() error {
panel.usageUpdateQueueM.Lock()
2019-08-03 10:17:09 +00:00
statuses := make([]usermanager.StatusUpdate, 0, len(panel.usageUpdateQueue))
for arrUID, usage := range panel.usageUpdateQueue {
panel.activeUsersM.RLock()
user := panel.activeUsers[arrUID]
panel.activeUsersM.RUnlock()
var numSession int
if user != nil {
2020-04-24 00:05:37 +00:00
if user.bypass {
continue
}
numSession = user.NumSession()
}
2019-08-03 10:17:09 +00:00
status := usermanager.StatusUpdate{
UID: arrUID[:],
2019-08-03 10:17:09 +00:00
Active: panel.isActive(arrUID[:]),
NumSession: numSession,
UpUsage: *usage.up,
DownUsage: *usage.down,
Timestamp: time.Now().Unix(),
}
statuses = append(statuses, status)
}
2020-04-24 00:05:37 +00:00
panel.usageUpdateQueue = make(map[[16]byte]*usagePair)
panel.usageUpdateQueueM.Unlock()
2019-07-24 14:25:09 +00:00
2019-08-03 10:17:09 +00:00
responses, err := panel.Manager.UploadStatus(statuses)
2019-07-24 14:25:09 +00:00
if err != nil {
2019-08-02 14:45:33 +00:00
return err
2019-07-24 14:25:09 +00:00
}
for _, resp := range responses {
var arrUID [16]byte
copy(arrUID[:], resp.UID)
2019-08-03 10:17:09 +00:00
switch resp.Action {
case usermanager.TERMINATE:
2019-07-24 14:25:09 +00:00
panel.activeUsersM.RLock()
user := panel.activeUsers[arrUID]
panel.activeUsersM.RUnlock()
if user != nil {
2019-08-30 21:14:45 +00:00
panel.TerminateActiveUser(user, resp.Message)
2019-07-24 14:25:09 +00:00
}
}
}
2019-08-02 14:45:33 +00:00
return nil
}
func (panel *userPanel) regularQueueUpload() {
for {
time.Sleep(1 * time.Minute)
go func() {
panel.updateUsageQueue()
2019-08-02 14:45:33 +00:00
err := panel.commitUpdate()
if err != nil {
log.Error(err)
}
}()
}
}