mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-01 00:48:17 +02:00
140 lines
3 KiB
Go
140 lines
3 KiB
Go
package manager
|
|
|
|
import (
|
|
"cmp"
|
|
"slices"
|
|
"sync"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
"github.com/pomerium/pomerium/pkg/grpc/session"
|
|
"github.com/pomerium/pomerium/pkg/grpc/user"
|
|
)
|
|
|
|
// dataStore stores session and user data. All public methods are thread-safe.
|
|
type dataStore struct {
|
|
mu sync.Mutex
|
|
sessions map[string]*session.Session
|
|
users map[string]*user.User
|
|
userIDToSessionIDs map[string]map[string]struct{}
|
|
}
|
|
|
|
func newDataStore() *dataStore {
|
|
return &dataStore{
|
|
sessions: make(map[string]*session.Session),
|
|
users: make(map[string]*user.User),
|
|
userIDToSessionIDs: make(map[string]map[string]struct{}),
|
|
}
|
|
}
|
|
|
|
// DeleteSession deletes a session.
|
|
func (ds *dataStore) DeleteSession(sID string) {
|
|
ds.mu.Lock()
|
|
ds.deleteSessionLocked(sID)
|
|
ds.mu.Unlock()
|
|
}
|
|
|
|
// DeleteUser deletes a user.
|
|
func (ds *dataStore) DeleteUser(userID string) {
|
|
ds.mu.Lock()
|
|
delete(ds.users, userID)
|
|
ds.mu.Unlock()
|
|
}
|
|
|
|
// GetSessionAndUser gets a session and its associated user.
|
|
func (ds *dataStore) GetSessionAndUser(sessionID string) (s *session.Session, u *user.User) {
|
|
ds.mu.Lock()
|
|
s = ds.sessions[sessionID]
|
|
if s.GetUserId() != "" {
|
|
u = ds.users[s.GetUserId()]
|
|
}
|
|
ds.mu.Unlock()
|
|
|
|
// clone to avoid sharing memory
|
|
s = clone(s)
|
|
u = clone(u)
|
|
return s, u
|
|
}
|
|
|
|
// GetUserAndSessions gets a user and all of its associated sessions.
|
|
func (ds *dataStore) GetUserAndSessions(userID string) (u *user.User, ss []*session.Session) {
|
|
ds.mu.Lock()
|
|
u = ds.users[userID]
|
|
for sessionID := range ds.userIDToSessionIDs[userID] {
|
|
ss = append(ss, ds.sessions[sessionID])
|
|
}
|
|
ds.mu.Unlock()
|
|
|
|
// remove nils and sort by id
|
|
ss = slices.Compact(ss)
|
|
slices.SortFunc(ss, func(a, b *session.Session) int {
|
|
return cmp.Compare(a.GetId(), b.GetId())
|
|
})
|
|
|
|
// clone to avoid sharing memory
|
|
u = clone(u)
|
|
for i := range ss {
|
|
ss[i] = clone(ss[i])
|
|
}
|
|
return u, ss
|
|
}
|
|
|
|
// PutSession stores the session.
|
|
func (ds *dataStore) PutSession(s *session.Session) {
|
|
// clone to avoid sharing memory
|
|
s = clone(s)
|
|
|
|
ds.mu.Lock()
|
|
if s.GetId() != "" {
|
|
ds.deleteSessionLocked(s.GetId())
|
|
ds.sessions[s.GetId()] = s
|
|
if s.GetUserId() != "" {
|
|
m, ok := ds.userIDToSessionIDs[s.GetUserId()]
|
|
if !ok {
|
|
m = make(map[string]struct{})
|
|
ds.userIDToSessionIDs[s.GetUserId()] = m
|
|
}
|
|
m[s.GetId()] = struct{}{}
|
|
}
|
|
}
|
|
ds.mu.Unlock()
|
|
}
|
|
|
|
// PutUser stores the user.
|
|
func (ds *dataStore) PutUser(u *user.User) {
|
|
// clone to avoid sharing memory
|
|
u = clone(u)
|
|
|
|
ds.mu.Lock()
|
|
if u.GetId() != "" {
|
|
ds.users[u.GetId()] = u
|
|
}
|
|
ds.mu.Unlock()
|
|
}
|
|
|
|
func (ds *dataStore) deleteSessionLocked(sID string) {
|
|
s := ds.sessions[sID]
|
|
delete(ds.sessions, sID)
|
|
if s.GetUserId() == "" {
|
|
return
|
|
}
|
|
|
|
m := ds.userIDToSessionIDs[s.GetUserId()]
|
|
if m != nil {
|
|
delete(m, s.GetId())
|
|
}
|
|
if len(m) == 0 {
|
|
delete(ds.userIDToSessionIDs, s.GetUserId())
|
|
}
|
|
}
|
|
|
|
// clone clones a protobuf message
|
|
func clone[T any, U interface {
|
|
*T
|
|
proto.Message
|
|
}](src U) U {
|
|
if src == nil {
|
|
return src
|
|
}
|
|
return proto.Clone(src).(U)
|
|
}
|