diff options
Diffstat (limited to 'rhimport/session_store.go')
-rw-r--r-- | rhimport/session_store.go | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/rhimport/session_store.go b/rhimport/session_store.go new file mode 100644 index 0000000..e47366e --- /dev/null +++ b/rhimport/session_store.go @@ -0,0 +1,310 @@ +// +// rhimportd +// +// The Radio Helsinki Rivendell Import Daemon +// +// +// Copyright (C) 2015-2016 Christian Pointner <equinox@helsinki.at> +// +// This file is part of rhimportd. +// +// rhimportd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// rhimportd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with rhimportd. If not, see <http://www.gnu.org/licenses/>. +// + +package rhimport + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "helsinki.at/rhrd-go/rddb" + "net/http" +) + +type newSessionResponse struct { + id string + session *SessionChan + responsecode int + errorstring string +} + +type newSessionRequest struct { + ctx *Context + refId string + response chan newSessionResponse +} + +type getSessionResponse struct { + session *SessionChan + refId string + responsecode int + errorstring string +} + +type getSessionRequest struct { + user string + id string + refId string + response chan getSessionResponse +} + +type listSessionsResponse struct { + sessions map[string]string + responsecode int + errorstring string +} + +type listSessionsRequest struct { + user string + password string + trusted bool + response chan listSessionsResponse +} + +type removeSessionResponse struct { + responsecode int + errorstring string +} + +type removeSessionRequest struct { + user string + id string + response chan removeSessionResponse +} + +type SessionStoreElement struct { + s *Session + refId string +} + +type SessionStore struct { + store map[string]map[string]*SessionStoreElement + conf *Config + db *rddb.DBChan + quit chan bool + done chan bool + newChan chan newSessionRequest + getChan chan getSessionRequest + listChan chan listSessionsRequest + removeChan chan removeSessionRequest +} + +func generateSessionId() (string, error) { + var b [32]byte + if _, err := rand.Read(b[:]); err != nil { + return "", err + } + return base64.RawStdEncoding.EncodeToString(b[:]), nil +} + +func (self *SessionStore) new(ctx *Context, refId string) (resp newSessionResponse) { + resp.responsecode = http.StatusOK + resp.errorstring = "OK" + if !ctx.Trusted { + if ok, err := self.db.CheckPassword(ctx.UserName, ctx.Password); err != nil { + resp.responsecode = http.StatusInternalServerError + resp.errorstring = err.Error() + return + } else if !ok { + resp.responsecode = http.StatusUnauthorized + resp.errorstring = "invalid username and/or password" + return + } + } + if id, err := generateSessionId(); err != nil { + resp.responsecode = http.StatusInternalServerError + resp.errorstring = err.Error() + } else { + resp.id = id + if _, exists := self.store[ctx.UserName]; !exists { + self.store[ctx.UserName] = make(map[string]*SessionStoreElement) + } + ctx.conf = self.conf + ctx.db = self.db + s := &SessionStoreElement{newSession(ctx, func() { self.GetInterface().Remove(ctx.UserName, resp.id) }), refId} + self.store[ctx.UserName][resp.id] = s + resp.session = self.store[ctx.UserName][resp.id].s.getInterface() + rhdl.Printf("SessionStore: created session for '%s' -> %s", ctx.UserName, resp.id) + } + return +} + +func (self *SessionStore) get(user, id string) (resp getSessionResponse) { + resp.responsecode = http.StatusOK + resp.errorstring = "OK" + if session, exists := self.store[user][id]; exists { + resp.session = session.s.getInterface() + resp.refId = session.refId + } else { + resp.responsecode = http.StatusNotFound + resp.errorstring = fmt.Sprintf("SessionStore: session '%s/%s' not found", user, id) + } + return +} + +func (self *SessionStore) list(user, password string, trusted bool) (resp listSessionsResponse) { + resp.responsecode = http.StatusOK + resp.errorstring = "OK" + if !trusted { + if ok, err := self.db.CheckPassword(user, password); err != nil { + resp.responsecode = http.StatusInternalServerError + resp.errorstring = err.Error() + return + } else if !ok { + resp.responsecode = http.StatusUnauthorized + resp.errorstring = "invalid username and/or password" + return + } + } + resp.sessions = make(map[string]string) + if sessions, exists := self.store[user]; exists { + for id, e := range sessions { + resp.sessions[id] = e.refId + } + } + return +} + +func (self *SessionStore) remove(user, id string) (resp removeSessionResponse) { + resp.responsecode = http.StatusOK + resp.errorstring = "OK" + if session, exists := self.store[user][id]; exists { + go session.s.cleanup() // cleanup could take a while -> don't block all the other stuff + delete(self.store[user], id) + rhdl.Printf("SessionStore: removed session '%s/%s'", user, id) + if userstore, exists := self.store[user]; exists { + if len(userstore) == 0 { + delete(self.store, user) + rhdl.Printf("SessionStore: removed user '%s'", user) + } + } + } else { + resp.responsecode = http.StatusNotFound + resp.errorstring = fmt.Sprintf("SessionStore: session '%s/%s' not found", user, id) + } + return +} + +func (self *SessionStore) dispatchRequests() { + defer func() { self.done <- true }() + for { + select { + case <-self.quit: + return + case req := <-self.newChan: + req.response <- self.new(req.ctx, req.refId) + case req := <-self.getChan: + req.response <- self.get(req.user, req.id) + case req := <-self.listChan: + req.response <- self.list(req.user, req.password, req.trusted) + case req := <-self.removeChan: + req.response <- self.remove(req.user, req.id) + } + } +} + +// ********************************************************* +// Public Interface + +type SessionStoreChan struct { + newChan chan<- newSessionRequest + getChan chan<- getSessionRequest + listChan chan listSessionsRequest + removeChan chan<- removeSessionRequest +} + +func (self *SessionStoreChan) New(ctx *Context, refId string) (string, *SessionChan, int, string) { + resCh := make(chan newSessionResponse) + req := newSessionRequest{} + req.ctx = ctx + req.refId = refId + req.response = resCh + self.newChan <- req + + res := <-resCh + return res.id, res.session, res.responsecode, res.errorstring +} + +func (self *SessionStoreChan) Get(user, id string) (*SessionChan, string, int, string) { + resCh := make(chan getSessionResponse) + req := getSessionRequest{} + req.user = user + req.id = id + req.response = resCh + self.getChan <- req + + res := <-resCh + return res.session, res.refId, res.responsecode, res.errorstring +} + +func (self *SessionStoreChan) List(user, password string, trusted bool) (map[string]string, int, string) { + resCh := make(chan listSessionsResponse) + req := listSessionsRequest{} + req.user = user + req.password = password + req.trusted = trusted + req.response = resCh + self.listChan <- req + + res := <-resCh + return res.sessions, res.responsecode, res.errorstring +} + +func (self *SessionStoreChan) Remove(user, id string) (int, string) { + resCh := make(chan removeSessionResponse) + req := removeSessionRequest{} + req.user = user + req.id = id + req.response = resCh + self.removeChan <- req + + res := <-resCh + return res.responsecode, res.errorstring +} + +func (self *SessionStore) GetInterface() *SessionStoreChan { + ch := &SessionStoreChan{} + ch.newChan = self.newChan + ch.getChan = self.getChan + ch.listChan = self.listChan + ch.removeChan = self.removeChan + return ch +} + +func (self *SessionStore) Cleanup() { + self.quit <- true + <-self.done + close(self.quit) + close(self.done) + close(self.newChan) + close(self.getChan) + close(self.listChan) + close(self.removeChan) +} + +func NewSessionStore(conf *Config, db *rddb.DBChan) (store *SessionStore, err error) { + store = new(SessionStore) + store.conf = conf + store.db = db + store.quit = make(chan bool) + store.done = make(chan bool) + store.store = make(map[string]map[string]*SessionStoreElement) + store.newChan = make(chan newSessionRequest, 10) + store.getChan = make(chan getSessionRequest, 10) + store.listChan = make(chan listSessionsRequest, 10) + store.removeChan = make(chan removeSessionRequest, 10) + + go store.dispatchRequests() + return +} |