summaryrefslogtreecommitdiff
path: root/rhimport/session_store.go
diff options
context:
space:
mode:
Diffstat (limited to 'rhimport/session_store.go')
-rw-r--r--rhimport/session_store.go310
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
+}