-
Somnath Ghorpade authored599dd835
session.go 4.17 KiB
// package sessionmdl provides APIs to Add, Validate and Delete user sessions. These APIs must be used along with JWT Auth.
// If you want to use this functionality, a jwt token must contain `userId` and `sessionId` fields.
// A user can have multiple active sessions for different use cases. To check if user has an active session for particular usecase use `CheckForSessionAvailability()`.
// And to check user session on each request use `ValidateSessionFromToken()`.
//
// An in memory cache is used to store sessions. It automatically falls back to redis cache if -gridmode=1 is set.
//
// The expiraion of session must be same as of token expiration.
package sessionmdl
import (
"errors"
"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/cachemdl"
)
type Session struct {
SessionFor string
SessionId string
}
// store is used to store sessions in memory, falls back to redis cache on grid mode.
var store cachemdl.Cacher
var (
ErrUserNotFound = errors.New("user not found")
ErrSessionNotFound = errors.New("session not found")
ErrInvalidSessionInstance = errors.New("got invalid session instance id")
ErrSessionValidationFailed = errors.New("session validation failed")
)
// Init initializes sessions with provided cache. Subsequent calls will not have any effect after first initialization.
func Init(cache cachemdl.Cacher) {
if store != nil {
return
}
store = cache
}
// Set stores the sessions for provided userId. Session is appended to the list. It does not check if the same session exists or not.
func Set(userId string, s ...Session) {
i, ok := store.Get(userId)
if !ok || i == nil {
set(userId, s)
return
}
sessions, ok := i.([]Session)
if !ok {
set(userId, s)
return
}
set(userId, append(sessions, s...))
}
func set(key string, val interface{}) {
// Set the user sessions with no expiry as each session can have different expiry depending on the JWT token expiry.
store.SetNoExpiration(key, val)
}
// Get returns all the available sessions for the user. This may contain expired but not deleted sessions.
func Get(userId string) ([]Session, error) {
var (
s []Session
i interface{}
ok bool
)
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
i, ok = store.Get(userId)
if !ok {
return s, ErrUserNotFound
}
s, _ = i.([]Session)
// if !ok {
// return s, errors.New("failed to retrieve previous sessions")
// }
return s, nil
}
// Delete removes all the sessions associated with the user.
func Delete(userId string) {
store.Delete(userId)
}
// DeleteSession removes a particular session for user, if present.
func DeleteSession(userId, sessionFor string) {
sessions, err := Get(userId)
if err != nil {
return
}
for i := 0; i < len(sessions); i++ {
if sessions[i].SessionFor == sessionFor {
sessions[i] = sessions[len(sessions)-1]
sessions = sessions[:len(sessions)-1]
}
}
if len(sessions) == 0 {
store.Delete(userId)
return
}
set(userId, sessions)
}
// ValidateSessionFromToken checks for session id in claims against available sessions.
// Validate only if a nonempty `sessionId` is present. The claims must contain `userId` field if session is present.
func ValidateSessionFromToken(claims map[string]interface{}) error {
// check for sessionId field, if not present then it is ignored at the time of token generation.
// This means user doesn't want to validate session.
i, ok := claims["sessionId"]
if !ok || i == nil {
return nil
}
sessionId, _ := i.(string)
if len(sessionId) == 0 {
return errors.New("\"sessionId\" field is empty")
}
i, ok = claims["userId"]
if !ok {
return errors.New("\"userId\" field not found in token")
}
userId, _ := i.(string)
if len(userId) == 0 {
return errors.New("\"userId\" field is empty")
}
sessions, err := Get(userId)
if err != nil {
141142143144145146147148149150151152153154155156157158159160161162163164165166
return err
}
for i := range sessions {
if sessions[i].SessionId == sessionId {
return nil
}
}
return ErrSessionNotFound
}
// CheckForSessionAvailability checks if the user has active session for provided `sessionFor`. Returns true if session is available.
func CheckForSessionAvailability(userId, sessionFor string) bool {
sessions, _ := Get(userId)
for i := range sessions {
if sessions[i].SessionFor == sessionFor {
return true
}
}
return false
}