package main // Queue for pending authentication requests import ( "crypto/rand" "math/big" "sync" "time" ) type AuthenticationMap struct { requestMap map[string]*AuthRequest app *App requestMapMutex *sync.RWMutex } // Returns empty string when no request by that id is present func (q *AuthenticationMap) GetRequestByID(ID string) *AuthRequest { q.requestMapMutex.RLock() req, ok := q.requestMap[ID] q.requestMapMutex.RUnlock() if !ok { return nil } return req } func GenerateUniqueID() (string, error) { length := 64 const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" ret := make([]byte, length) for i := 0; i < length; i++ { num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) if err != nil { return "", err } ret[i] = letters[num.Int64()] } return string(ret), nil } func (q *AuthenticationMap) QueueRequest(r *AuthRequest) error { var ID string var err error for { ID, err = GenerateUniqueID() if err != nil { return err } q.requestMapMutex.Lock() if _, exists := q.requestMap[ID]; exists { q.requestMapMutex.Unlock() continue } q.requestMapMutex.Unlock() break } err = r.SetId(ID) if err != nil { return err } r.Queued = time.Now() q.requestMapMutex.Lock() q.requestMap[ID] = r q.requestMapMutex.Unlock() (*q.app.Logger).Info("Queued request by id: ", ID) return nil } func (q *AuthenticationMap) Init() { q.requestMap = make(map[string]*AuthRequest) q.requestMapMutex = &sync.RWMutex{} q.startRequestMapCleaner() } func (q *AuthenticationMap) startRequestMapCleaner() { go func() { for _ = range time.Tick(10 * time.Minute) { q.requestMapMutex.Lock() for key, req := range q.requestMap { // If the request has been queued for more than 30 // minutes, it's time to delete it. if req.Queued.Add(30 * time.Minute).Before(time.Now()) { (*q.app.Logger).Info("Deleting inactive request with id: ", req.ID) req.Cancel() delete(q.requestMap, key) } } q.requestMapMutex.Unlock() } }() } func (q *AuthenticationMap) Delete(ID string) { q.requestMapMutex.Lock() delete(q.requestMap, ID) q.requestMapMutex.Unlock() }