github.com/cs3org/reva/v2@v2.27.7/pkg/siteacc/html/sessionmanager.go (about) 1 // Copyright 2018-2020 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package html 20 21 import ( 22 "fmt" 23 "net/http" 24 "sync" 25 "time" 26 27 "github.com/cs3org/reva/v2/pkg/siteacc/config" 28 "github.com/pkg/errors" 29 "github.com/rs/zerolog" 30 ) 31 32 // SessionManager manages HTML sessions. 33 type SessionManager struct { 34 conf *config.Configuration 35 log *zerolog.Logger 36 37 sessions map[string]*Session 38 39 sessionName string 40 41 mutex sync.Mutex 42 } 43 44 func (mngr *SessionManager) initialize(name string, conf *config.Configuration, log *zerolog.Logger) error { 45 if name == "" { 46 return errors.Errorf("no session name provided") 47 } 48 mngr.sessionName = name 49 50 if conf == nil { 51 return errors.Errorf("no configuration provided") 52 } 53 mngr.conf = conf 54 55 if log == nil { 56 return errors.Errorf("no logger provided") 57 } 58 mngr.log = log 59 60 mngr.sessions = make(map[string]*Session, 100) 61 62 return nil 63 } 64 65 // HandleRequest performs all session-related tasks during an HTML request. Always returns a valid session object. 66 func (mngr *SessionManager) HandleRequest(w http.ResponseWriter, r *http.Request) (*Session, error) { 67 mngr.mutex.Lock() 68 defer mngr.mutex.Unlock() 69 70 var session *Session 71 var sessionErr error 72 73 // Try to get the session ID from the request; if none has been set yet, a new one will be assigned 74 cookie, err := r.Cookie(mngr.sessionName) 75 if err == nil { 76 session = mngr.findSession(cookie.Value) 77 if session != nil { 78 mngr.logSessionInfo(session, r, "existing session found") 79 80 // Verify the request against the session: If it is invalid, set an error; if the session has expired, create a new one; if it has already passed its halftime, migrate to a new one 81 if err := session.VerifyRequest(r, mngr.conf.Webserver.VerifyRemoteAddress); err == nil { 82 if session.HasExpired() { 83 // The session has expired, so a new one needs to be created 84 session = nil 85 86 mngr.logSessionInfo(session, r, "session expired") 87 } else if session.HalftimePassed() { 88 // The session has passed its halftime, so migrate it to a new one (makes hijacking session IDs harder) 89 session, err = mngr.migrateSession(session, r) 90 if err != nil { 91 session = nil 92 sessionErr = errors.Wrap(err, "unable to migrate session") 93 } 94 95 mngr.logSessionInfo(session, r, "session migrated") 96 } 97 } else { 98 session = nil 99 sessionErr = errors.Wrap(err, "invalid session") 100 101 mngr.logSessionInfo(session, r, "session invalid (verify failed)") 102 } 103 } 104 } else if err != http.ErrNoCookie { 105 // The session cookie exists but seems to be invalid, so set an error 106 session = nil 107 sessionErr = errors.Wrap(err, "unable to get the session ID from the client") 108 109 mngr.logSessionInfo(session, r, fmt.Sprintf("session cookie error: %v", err)) 110 } 111 112 if session == nil { 113 // No session found for the client, so create a new one; this will always succeed 114 session = mngr.createSession(r) 115 116 mngr.logSessionInfo(session, r, "assigned new session") 117 } 118 119 // Store the session ID on the client side 120 session.Save(mngr.conf.Webserver.URL, w) 121 122 return session, sessionErr 123 } 124 125 // PurgeSessions removes any expired sessions. 126 func (mngr *SessionManager) PurgeSessions() { 127 mngr.mutex.Lock() 128 defer mngr.mutex.Unlock() 129 130 var expiredSessions []string 131 for id, session := range mngr.sessions { 132 if session.HasExpired() { 133 expiredSessions = append(expiredSessions, id) 134 } 135 } 136 137 for _, id := range expiredSessions { 138 delete(mngr.sessions, id) 139 } 140 } 141 142 func (mngr *SessionManager) createSession(r *http.Request) *Session { 143 session := NewSession(mngr.sessionName, time.Duration(mngr.conf.Webserver.SessionTimeout)*time.Second, r) 144 mngr.sessions[session.ID] = session 145 return session 146 } 147 148 func (mngr *SessionManager) findSession(id string) *Session { 149 if session, ok := mngr.sessions[id]; ok { 150 return session 151 } 152 return nil 153 } 154 155 func (mngr *SessionManager) migrateSession(session *Session, r *http.Request) (*Session, error) { 156 sessionNew := mngr.createSession(r) 157 158 // Carry over the old session information, thus preserving the existing session 159 sessionNew.MigrationID = session.ID 160 sessionNew.Data = session.Data 161 162 if user := session.LoggedInUser(); user != nil { 163 sessionNew.LoginUser(user.Account, user.Site) 164 } else { 165 sessionNew.LogoutUser() 166 } 167 168 // Delete the old session 169 delete(mngr.sessions, session.ID) 170 171 return sessionNew, nil 172 } 173 174 func (mngr *SessionManager) logSessionInfo(session *Session, r *http.Request, info string) { 175 if mngr.conf.Webserver.LogSessions { 176 if session != nil { 177 mngr.log.Debug().Str("id", session.ID).Str("address", r.RemoteAddr).Str("path", r.URL.Path).Msg(info) 178 } else { 179 mngr.log.Debug().Str("address", r.RemoteAddr).Str("path", r.URL.Path).Msg(info) 180 } 181 } 182 } 183 184 // NewSessionManager creates a new session manager. 185 func NewSessionManager(name string, conf *config.Configuration, log *zerolog.Logger) (*SessionManager, error) { 186 mngr := &SessionManager{} 187 if err := mngr.initialize(name, conf, log); err != nil { 188 return nil, errors.Wrap(err, "unable to initialize the session manager") 189 } 190 return mngr, nil 191 }