snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
169
server/auth/request/auth_user.go
Normal file
169
server/auth/request/auth_user.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/auth/settings"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
type (
|
||||
// handles authentication state, keeps info about permanent login and
|
||||
// status of the MFA
|
||||
authUser struct {
|
||||
User *types.User
|
||||
|
||||
// is user logged-in with "remember-me"?
|
||||
PermSession bool
|
||||
|
||||
MFAStatus map[authType]authStatus
|
||||
}
|
||||
|
||||
authStatus uint
|
||||
authType string
|
||||
)
|
||||
|
||||
const (
|
||||
// not enforced by user or by global settings
|
||||
authStatusDisabled authStatus = iota
|
||||
|
||||
// when mfa is unconfigured on user but enforced globally
|
||||
// this can happen on when new user registers or when global enforcement is later enabled
|
||||
authStatusUnconfigured
|
||||
|
||||
// enforced by user or global settings, but not yet verified
|
||||
authStatusPending
|
||||
|
||||
// verified
|
||||
authStatusOK
|
||||
)
|
||||
|
||||
const (
|
||||
// password does not really have any function,
|
||||
// more for demonstration purposes
|
||||
authByPassword = "password"
|
||||
authByEmailOTP = "email-otp"
|
||||
authByTOTP = "totp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(&authUser{})
|
||||
}
|
||||
|
||||
func NewAuthUser(s *settings.Settings, u *types.User, perm bool) *authUser {
|
||||
au := &authUser{
|
||||
User: u,
|
||||
PermSession: perm,
|
||||
MFAStatus: make(map[authType]authStatus),
|
||||
}
|
||||
|
||||
au.set(s, u)
|
||||
return au
|
||||
}
|
||||
|
||||
func (au *authUser) Update(s *settings.Settings, u *types.User) {
|
||||
au.set(s, u)
|
||||
}
|
||||
|
||||
func (au *authUser) set(s *settings.Settings, u *types.User) {
|
||||
if u == nil || s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if u.Meta == nil {
|
||||
u.Meta = &types.UserMeta{}
|
||||
}
|
||||
|
||||
// User's MFA security policy
|
||||
umsp := u.Meta.SecurityPolicy.MFA
|
||||
|
||||
// Global MFA security policy
|
||||
gmsp := s.MultiFactor
|
||||
|
||||
mfaStatus := map[authType]authStatus{
|
||||
authByPassword: authStatusOK,
|
||||
authByEmailOTP: authStatusDisabled,
|
||||
authByTOTP: authStatusDisabled,
|
||||
}
|
||||
|
||||
// determinate mfa status for email OTP
|
||||
if !gmsp.EmailOTP.Enabled {
|
||||
mfaStatus[authByEmailOTP] = authStatusDisabled
|
||||
} else if umsp.EnforcedEmailOTP || gmsp.EmailOTP.Enforced {
|
||||
mfaStatus[authByEmailOTP] = authStatusPending
|
||||
}
|
||||
|
||||
// determinate mfa status for TOTP
|
||||
if !gmsp.TOTP.Enabled {
|
||||
mfaStatus[authByTOTP] = authStatusDisabled
|
||||
} else if !umsp.EnforcedTOTP && gmsp.TOTP.Enforced {
|
||||
// TOTP not enforced on user but enforced globally
|
||||
mfaStatus[authByTOTP] = authStatusUnconfigured
|
||||
} else if umsp.EnforcedTOTP || gmsp.TOTP.Enforced {
|
||||
mfaStatus[authByTOTP] = authStatusPending
|
||||
}
|
||||
|
||||
au.MFAStatus = mfaStatus
|
||||
}
|
||||
|
||||
func (au authUser) DisabledEmailOTP() bool {
|
||||
return au.MFAStatus[authByEmailOTP] == authStatusDisabled
|
||||
}
|
||||
|
||||
func (au authUser) PendingEmailOTP() bool {
|
||||
return au.MFAStatus[authByEmailOTP] == authStatusPending
|
||||
}
|
||||
|
||||
func (au authUser) DisabledTOTP() bool {
|
||||
return au.MFAStatus[authByTOTP] == authStatusDisabled
|
||||
}
|
||||
|
||||
func (au authUser) UnconfiguredTOTP() bool {
|
||||
return au.MFAStatus[authByTOTP] == authStatusUnconfigured
|
||||
}
|
||||
|
||||
func (au authUser) PendingTOTP() bool {
|
||||
return au.MFAStatus[authByTOTP] == authStatusPending
|
||||
}
|
||||
|
||||
// PendingMFA Returns true if any of MFAs are pending
|
||||
func (au authUser) PendingMFA() bool {
|
||||
for _, st := range au.MFAStatus {
|
||||
if st == authStatusPending {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (au *authUser) CompleteEmailOTP() {
|
||||
au.MFAStatus[authByEmailOTP] = authStatusOK
|
||||
}
|
||||
|
||||
func (au *authUser) CompleteTOTP() {
|
||||
au.MFAStatus[authByTOTP] = authStatusOK
|
||||
}
|
||||
|
||||
func (au *authUser) ResetTOTP() {
|
||||
au.MFAStatus[authByTOTP] = authStatusUnconfigured
|
||||
}
|
||||
|
||||
func (au *authUser) Forget(ses *sessions.Session) {
|
||||
delete(ses.Values, keyAuthUser)
|
||||
delete(ses.Values, keyRememberMe)
|
||||
}
|
||||
|
||||
func (au *authUser) Save(ses *sessions.Session) {
|
||||
ses.Values[keyAuthUser] = au
|
||||
|
||||
// explicitly save roles
|
||||
ses.Values[keyRoles] = au.User.Roles()
|
||||
|
||||
if au.PermSession {
|
||||
ses.Values[keyRememberMe] = true
|
||||
} else {
|
||||
delete(ses.Values, keyRememberMe)
|
||||
}
|
||||
}
|
||||
136
server/auth/request/context.go
Normal file
136
server/auth/request/context.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
type (
|
||||
localeService interface {
|
||||
NS(ctx context.Context, ns string) func(key string, rr ...string) string
|
||||
}
|
||||
|
||||
// auth context simplifies auth request & response handling
|
||||
AuthReq struct {
|
||||
// HTTP request sent
|
||||
Request *http.Request
|
||||
|
||||
Response http.ResponseWriter
|
||||
|
||||
// Sessions
|
||||
Session *sessions.Session
|
||||
|
||||
// Authenticated user
|
||||
AuthUser *authUser
|
||||
|
||||
// Current client (when in oauth2 flow)
|
||||
Client *types.AuthClient
|
||||
|
||||
Locale localeService
|
||||
|
||||
// Redirect to
|
||||
RedirectTo string
|
||||
|
||||
// Template to render
|
||||
Template string
|
||||
|
||||
// Data to render with the template
|
||||
Data map[string]interface{}
|
||||
|
||||
// handling flash alerts of all types
|
||||
//
|
||||
// should not be used for form errors; store them into sep. session keys
|
||||
PrevAlerts, NewAlerts []Alert
|
||||
|
||||
// HTTP status to send
|
||||
Status int
|
||||
}
|
||||
|
||||
Alert struct {
|
||||
// primary, secondary, danger, warning...
|
||||
Type string
|
||||
Text string
|
||||
Html template.URL
|
||||
}
|
||||
|
||||
// ExtraReqInfo serves as transport struct for additional
|
||||
// request information we want to store with the oauth2 token
|
||||
//
|
||||
// There is effortless way to extend token info that is created inside go-oauth2 lib
|
||||
// so we'll attach this struct to (request's) context with middleware (see MountHttpRoutes)
|
||||
// and unpack from context when token is created in CortezaTokenStore.Create()
|
||||
//
|
||||
// ExtraReqInfo struct also serves as context value key!
|
||||
ExtraReqInfo struct {
|
||||
RemoteAddr string
|
||||
UserAgent string
|
||||
}
|
||||
)
|
||||
|
||||
func (req AuthReq) Context() context.Context { return req.Request.Context() }
|
||||
|
||||
func (req *AuthReq) SetInternalError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
req.Status = http.StatusInternalServerError
|
||||
req.Data["error"] = err
|
||||
return true
|
||||
}
|
||||
|
||||
func (req *AuthReq) PushAlert(text string) {
|
||||
req.NewAlerts = append(req.NewAlerts, Alert{Type: "primary", Text: text})
|
||||
}
|
||||
|
||||
func (req *AuthReq) PushDangerAlert(text string) {
|
||||
req.NewAlerts = append(req.NewAlerts, Alert{Type: "danger", Text: text})
|
||||
}
|
||||
|
||||
func (req *AuthReq) PopAlerts() []Alert {
|
||||
val, has := req.Session.Values["alerts"]
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
|
||||
delete(req.Session.Values, "alerts")
|
||||
|
||||
return val.([]Alert)
|
||||
}
|
||||
|
||||
func (req *AuthReq) SetAlerts(aa ...Alert) {
|
||||
if len(aa) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
req.Session.Values["alerts"] = aa
|
||||
}
|
||||
|
||||
// retrives key-value from session, stored under request-uri key
|
||||
func (req *AuthReq) GetKV() map[string]string {
|
||||
val, has := req.Session.Values["KV:"+req.Request.RequestURI]
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
|
||||
return val.(map[string]string)
|
||||
}
|
||||
|
||||
// sets key-value value to session under request-uri key
|
||||
func (req *AuthReq) SetKV(val map[string]string) {
|
||||
if val == nil {
|
||||
delete(req.Session.Values, "KV:"+req.Request.RequestURI)
|
||||
} else {
|
||||
req.Session.Values["KV:"+req.Request.RequestURI] = val
|
||||
}
|
||||
}
|
||||
|
||||
func (req *AuthReq) PopKV() map[string]string {
|
||||
val := req.GetKV()
|
||||
req.SetKV(nil)
|
||||
return val
|
||||
}
|
||||
244
server/auth/request/corteza_session_store.go
Normal file
244
server/auth/request/corteza_session_store.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rand"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
// cortezaSessionStore implements the session store and bridge to corteza store
|
||||
type (
|
||||
cortezaSessionStore struct {
|
||||
// session store does not accept context on functions
|
||||
// so we'll store the general one here on struct so we
|
||||
// can capture termination signals..
|
||||
//ctx context.Context
|
||||
store store.AuthSessions
|
||||
|
||||
//cookies sessions.CookieStore
|
||||
//stopCleanup chan bool
|
||||
|
||||
Codecs []securecookie.Codec
|
||||
|
||||
opt options.AuthOpt
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// wrapper around time.Now() that will aid service testing
|
||||
now = func() *time.Time {
|
||||
c := time.Now().Round(time.Second)
|
||||
return &c
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(map[string]string{})
|
||||
}
|
||||
|
||||
func CortezaSessionStore(store store.AuthSessions, opt options.AuthOpt) *cortezaSessionStore {
|
||||
var domain = opt.SessionCookieDomain
|
||||
if strings.Contains(domain, ":") {
|
||||
// do not set domain on the cookie if it contains port
|
||||
domain = ""
|
||||
}
|
||||
|
||||
return &cortezaSessionStore{
|
||||
store: store,
|
||||
Codecs: securecookie.CodecsFromPairs([]byte(opt.Secret)),
|
||||
opt: opt,
|
||||
}
|
||||
}
|
||||
|
||||
func (s cortezaSessionStore) Get(r *http.Request, name string) (*sessions.Session, error) {
|
||||
return sessions.GetRegistry(r).Get(s, name)
|
||||
}
|
||||
|
||||
func (s cortezaSessionStore) New(r *http.Request, name string) (session *sessions.Session, err error) {
|
||||
var (
|
||||
domain = s.opt.SessionCookieDomain
|
||||
)
|
||||
|
||||
if strings.Contains(domain, ":") {
|
||||
// do not set domain on the cookie if it contains colon
|
||||
// this most likely means it contains port
|
||||
//
|
||||
// browsers might misbehave
|
||||
domain = ""
|
||||
}
|
||||
|
||||
session = sessions.NewSession(s, name)
|
||||
session.IsNew = true
|
||||
session.Options = &sessions.Options{
|
||||
Path: s.opt.SessionCookiePath,
|
||||
Domain: domain,
|
||||
Secure: s.opt.SessionCookieSecure,
|
||||
HttpOnly: true,
|
||||
}
|
||||
|
||||
if cook, errCookie := r.Cookie(name); errCookie == nil {
|
||||
err = securecookie.DecodeMulti(name, cook.Value, &session.ID, s.Codecs...)
|
||||
if err == nil {
|
||||
err = s.load(r.Context(), session)
|
||||
if err == nil {
|
||||
session.IsNew = false
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s cortezaSessionStore) load(ctx context.Context, ses *sessions.Session) error {
|
||||
cortezaSession, err := s.store.LookupAuthSessionByID(ctx, ses.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if now().After(cortezaSession.ExpiresAt) {
|
||||
return fmt.Errorf("session expired")
|
||||
}
|
||||
|
||||
if err = gob.NewDecoder(bytes.NewReader(cortezaSession.Data)).Decode(&ses.Values); err != nil {
|
||||
return fmt.Errorf("failed to decode session: %w", err)
|
||||
}
|
||||
|
||||
// Store original session back for ref
|
||||
ses.Values[keyOriginalSession] = cortezaSession
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s cortezaSessionStore) Save(r *http.Request, w http.ResponseWriter, ses *sessions.Session) (err error) {
|
||||
// Set delete if max-age is < 0
|
||||
if ses.Options.MaxAge < 0 {
|
||||
err = s.Delete(r.Context(), ses)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
http.SetCookie(w, sessions.NewCookie(ses.Name(), "", ses.Options))
|
||||
|
||||
newSes, err := s.New(r, ses.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ses.Options = newSes.Options
|
||||
ses.IsNew = true
|
||||
ses.ID = ""
|
||||
ses.Values = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(ses.Values) == 0 && ses.IsNew {
|
||||
// no values set, nothing to save
|
||||
return nil
|
||||
}
|
||||
|
||||
if ses.ID == "" || ses.IsNew {
|
||||
ses.ID = string(rand.Bytes(64))
|
||||
}
|
||||
|
||||
var (
|
||||
// make a copy of options for cookie
|
||||
cOpt = *ses.Options
|
||||
|
||||
// expiration for stored session
|
||||
exp time.Time
|
||||
)
|
||||
|
||||
if IsPermLogin(ses) {
|
||||
// if permanent login, make sure max-age is recalculated
|
||||
// so that it slides in the future with every new session-save
|
||||
cOpt.MaxAge = int(s.opt.SessionPermLifetime / time.Second)
|
||||
|
||||
// recalculate expiration for the stored (db) session
|
||||
exp = now().Add(s.opt.SessionPermLifetime)
|
||||
} else {
|
||||
// Warning!
|
||||
// when session is not permanent, max-age is NOT set on the cookie
|
||||
// making cookie last only for the length of the session
|
||||
|
||||
// recalculate expiration for the stored (db) session
|
||||
// we do that even if the cookie does not have a value
|
||||
exp = now().Add(s.opt.SessionLifetime)
|
||||
}
|
||||
|
||||
if err = s.save(r.Context(), ses, exp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Keep the session ID key in a cookie so that it can be looked up in DB later.
|
||||
encoded, err := securecookie.EncodeMulti(ses.Name(), ses.ID, s.Codecs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
http.SetCookie(w, sessions.NewCookie(ses.Name(), encoded, &cOpt))
|
||||
return nil
|
||||
}
|
||||
|
||||
// save writes encoded session.Values to a database record.
|
||||
// writes to http_sessions table by default.
|
||||
func (s cortezaSessionStore) save(ctx context.Context, ses *sessions.Session, expires time.Time) (err error) {
|
||||
var (
|
||||
buf = &bytes.Buffer{}
|
||||
cortezaSession *types.AuthSession
|
||||
)
|
||||
|
||||
// retrieve original session value
|
||||
if aux, is := ses.Values[keyOriginalSession]; is {
|
||||
cortezaSession = aux.(*types.AuthSession)
|
||||
} else {
|
||||
cortezaSession = &types.AuthSession{
|
||||
ID: ses.ID,
|
||||
CreatedAt: *now(),
|
||||
}
|
||||
|
||||
// new session does not belong to anyone yet.
|
||||
// retrieve user id from ses. values
|
||||
if au := GetAuthUser(ses); au != nil {
|
||||
cortezaSession.UserID = au.User.ID
|
||||
}
|
||||
|
||||
extra := auth.GetExtraReqInfoFromContext(ctx)
|
||||
cortezaSession.UserAgent = extra.UserAgent
|
||||
cortezaSession.RemoteAddr = extra.RemoteAddr
|
||||
}
|
||||
|
||||
cortezaSession.ExpiresAt = expires
|
||||
|
||||
delete(ses.Values, keyOriginalSession)
|
||||
if err = gob.NewEncoder(buf).Encode(ses.Values); err != nil {
|
||||
return fmt.Errorf("failed to encode session: %w", err)
|
||||
}
|
||||
|
||||
cortezaSession.Data = buf.Bytes()
|
||||
|
||||
return s.store.UpsertAuthSession(ctx, cortezaSession)
|
||||
}
|
||||
|
||||
func (s cortezaSessionStore) Delete(ctx context.Context, ses *sessions.Session) error {
|
||||
if len(ses.ID) > 0 {
|
||||
return s.store.DeleteAuthSessionByID(ctx, ses.ID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ sessions.Store = &cortezaSessionStore{}
|
||||
60
server/auth/request/manager.go
Normal file
60
server/auth/request/manager.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/gorilla/sessions"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
SessionManager struct {
|
||||
cstore store.AuthSessions
|
||||
sstore sessions.Store
|
||||
opt options.AuthOpt
|
||||
log *zap.Logger
|
||||
}
|
||||
)
|
||||
|
||||
func NewSessionManager(store store.AuthSessions, opt options.AuthOpt, log *zap.Logger) *SessionManager {
|
||||
m := &SessionManager{opt: opt, log: log}
|
||||
m.cstore = store
|
||||
m.sstore = CortezaSessionStore(store, opt)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m SessionManager) Store() sessions.Store { return m.sstore }
|
||||
|
||||
func (m *SessionManager) Get(r *http.Request) *sessions.Session {
|
||||
ses, _ := m.sstore.Get(r, m.opt.SessionCookieName)
|
||||
return ses
|
||||
}
|
||||
|
||||
func (m *SessionManager) Save(w http.ResponseWriter, r *http.Request) {
|
||||
if err := m.Get(r).Save(r, w); err != nil {
|
||||
m.log.Warn("failed to save sessions", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// Returns all users sessions
|
||||
func (m *SessionManager) Search(ctx context.Context, userID uint64) (set []*types.AuthSession, err error) {
|
||||
set, _, err = m.cstore.SearchAuthSessions(ctx, types.AuthSessionFilter{UserID: userID})
|
||||
for i := range set {
|
||||
set[i].Data = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteByUserID removes session by user ID
|
||||
func (m *SessionManager) DeleteByUserID(ctx context.Context, userID uint64) (err error) {
|
||||
return m.cstore.DeleteAuthSessionsByUserID(ctx, userID)
|
||||
}
|
||||
|
||||
// DeleteByID removes session by it's ID
|
||||
func (m *SessionManager) DeleteByID(ctx context.Context, sessionID string) (err error) {
|
||||
return m.cstore.DeleteAuthSessionByID(ctx, sessionID)
|
||||
}
|
||||
109
server/auth/request/session.go
Normal file
109
server/auth/request/session.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
const (
|
||||
keyRememberMe = "remember-me"
|
||||
keyOriginalSession = "originalSession"
|
||||
keyAuthUser = "authUser"
|
||||
keyRoles = "roles"
|
||||
keyOAuth2AuthParams = "oauth2AuthParams"
|
||||
keyOAuth2Client = "oauth2ClientID"
|
||||
keyOAuth2ClientAuthorized = "oauth2ClientAuthorized"
|
||||
)
|
||||
|
||||
// GetUser is wrapper to get value from session
|
||||
func GetAuthUser(ses *sessions.Session) *authUser {
|
||||
val, has := ses.Values[keyAuthUser]
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
|
||||
au := val.(*authUser)
|
||||
if au.User != nil {
|
||||
au.User.SetRoles(getRoleMemberships(ses)...)
|
||||
}
|
||||
|
||||
return au
|
||||
}
|
||||
|
||||
// GetRoleMemberships is wrapper to get value from session
|
||||
func getRoleMemberships(ses *sessions.Session) []uint64 {
|
||||
val, has := ses.Values[keyRoles]
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
|
||||
return val.([]uint64)
|
||||
}
|
||||
|
||||
// GetOAuth2AuthParams is wrapper to get value from session
|
||||
func GetOAuth2AuthParams(ses *sessions.Session) url.Values {
|
||||
val, has := ses.Values[keyOAuth2AuthParams]
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
|
||||
return val.(url.Values)
|
||||
}
|
||||
|
||||
// SetOauth2AuthParams is a session value setting wrapper for Oauth2AuthParams
|
||||
func SetOauth2AuthParams(ses *sessions.Session, val url.Values) {
|
||||
if val != nil {
|
||||
ses.Values[keyOAuth2AuthParams] = val
|
||||
} else {
|
||||
delete(ses.Values, keyOAuth2AuthParams)
|
||||
}
|
||||
}
|
||||
|
||||
// GetOauth2Client is wrapper to get value from session
|
||||
func GetOauth2Client(ses *sessions.Session) *types.AuthClient {
|
||||
val, has := ses.Values[keyOAuth2Client]
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
|
||||
return val.(*types.AuthClient)
|
||||
}
|
||||
|
||||
// SetOauth2Client is a session value setting wrapper for Oauth2Client
|
||||
func SetOauth2Client(ses *sessions.Session, val *types.AuthClient) {
|
||||
if val != nil {
|
||||
ses.Values[keyOAuth2Client] = val
|
||||
} else {
|
||||
delete(ses.Values, keyOAuth2Client)
|
||||
}
|
||||
}
|
||||
|
||||
// IsOauth2ClientAuthorized is wrapper to get value from session
|
||||
func IsOauth2ClientAuthorized(ses *sessions.Session) bool {
|
||||
val, has := ses.Values[keyOAuth2ClientAuthorized]
|
||||
if !has {
|
||||
return false
|
||||
}
|
||||
|
||||
return val.(bool)
|
||||
}
|
||||
|
||||
// SetOauth2ClientAuthorized is a session value setting wrapper for Oauth2ClientAuthorized
|
||||
func SetOauth2ClientAuthorized(ses *sessions.Session, val bool) {
|
||||
if val {
|
||||
ses.Values[keyOAuth2ClientAuthorized] = true
|
||||
} else {
|
||||
delete(ses.Values, keyOAuth2ClientAuthorized)
|
||||
}
|
||||
}
|
||||
|
||||
// IsPermLogin decodes remember-me flag from the session and returns true if set
|
||||
func IsPermLogin(ses *sessions.Session) (p bool) {
|
||||
if aux, has := ses.Values[keyRememberMe]; has {
|
||||
p, _ = aux.(bool)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user