This commit is contained in:
Hannes Kohlsaat 2024-11-27 01:22:52 -05:00 committed by GitHub
commit 242f8cb6b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -103,12 +103,13 @@ type userInteractiveFlow struct {
// the user already has a valid access token, but we want to double-check // the user already has a valid access token, but we want to double-check
// that it isn't stolen by re-authenticating them. // that it isn't stolen by re-authenticating them.
type UserInteractive struct { type UserInteractive struct {
sync.RWMutex flows []userInteractiveFlow
Flows []userInteractiveFlow
// Map of login type to implementation // Map of login type to implementation
Types map[string]Type types map[string]Type
mutex sync.RWMutex
// Map of session ID to completed login types, will need to be extended in future // Map of session ID to completed login types, will need to be extended in future
Sessions map[string][]string sessions map[string][]string
} }
func NewUserInteractive(userAccountAPI api.UserLoginAPI, cfg *config.ClientAPI) *UserInteractive { func NewUserInteractive(userAccountAPI api.UserLoginAPI, cfg *config.ClientAPI) *UserInteractive {
@ -117,22 +118,20 @@ func NewUserInteractive(userAccountAPI api.UserLoginAPI, cfg *config.ClientAPI)
Config: cfg, Config: cfg,
} }
return &UserInteractive{ return &UserInteractive{
Flows: []userInteractiveFlow{ flows: []userInteractiveFlow{
{ {
Stages: []string{typePassword.Name()}, Stages: []string{typePassword.Name()},
}, },
}, },
Types: map[string]Type{ types: map[string]Type{
typePassword.Name(): typePassword, typePassword.Name(): typePassword,
}, },
Sessions: make(map[string][]string), sessions: make(map[string][]string),
} }
} }
func (u *UserInteractive) IsSingleStageFlow(authType string) bool { func (u *UserInteractive) IsSingleStageFlow(authType string) bool {
u.RLock() for _, f := range u.flows {
defer u.RUnlock()
for _, f := range u.Flows {
if len(f.Stages) == 1 && f.Stages[0] == authType { if len(f.Stages) == 1 && f.Stages[0] == authType {
return true return true
} }
@ -141,10 +140,10 @@ func (u *UserInteractive) IsSingleStageFlow(authType string) bool {
} }
func (u *UserInteractive) AddCompletedStage(sessionID, authType string) { func (u *UserInteractive) AddCompletedStage(sessionID, authType string) {
u.Lock() u.mutex.Lock()
// TODO: Handle multi-stage flows // TODO: Handle multi-stage flows
delete(u.Sessions, sessionID) delete(u.sessions, sessionID)
u.Unlock() u.mutex.Unlock()
} }
type Challenge struct { type Challenge struct {
@ -157,16 +156,15 @@ type Challenge struct {
// Challenge returns an HTTP 401 with the supported flows for authenticating // Challenge returns an HTTP 401 with the supported flows for authenticating
func (u *UserInteractive) challenge(sessionID string) *util.JSONResponse { func (u *UserInteractive) challenge(sessionID string) *util.JSONResponse {
u.RLock() u.mutex.RLock()
completed := u.Sessions[sessionID] completed := u.sessions[sessionID]
flows := u.Flows u.mutex.RUnlock()
u.RUnlock()
return &util.JSONResponse{ return &util.JSONResponse{
Code: 401, Code: 401,
JSON: Challenge{ JSON: Challenge{
Completed: completed, Completed: completed,
Flows: flows, Flows: u.flows,
Session: sessionID, Session: sessionID,
Params: make(map[string]interface{}), Params: make(map[string]interface{}),
}, },
@ -183,9 +181,9 @@ func (u *UserInteractive) NewSession() *util.JSONResponse {
JSON: spec.InternalServerError{}, JSON: spec.InternalServerError{},
} }
} }
u.Lock() u.mutex.Lock()
u.Sessions[sessionID] = []string{} u.sessions[sessionID] = []string{}
u.Unlock() u.mutex.Unlock()
return u.challenge(sessionID) return u.challenge(sessionID)
} }
@ -233,9 +231,7 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
// extract the type so we know which login type to use // extract the type so we know which login type to use
authType := gjson.GetBytes(bodyBytes, "auth.type").Str authType := gjson.GetBytes(bodyBytes, "auth.type").Str
u.RLock() loginType, ok := u.types[authType]
loginType, ok := u.Types[authType]
u.RUnlock()
if !ok { if !ok {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
@ -247,9 +243,9 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
// retrieve the session // retrieve the session
sessionID := gjson.GetBytes(bodyBytes, "auth.session").Str sessionID := gjson.GetBytes(bodyBytes, "auth.session").Str
u.RLock() u.mutex.RLock()
_, ok = u.Sessions[sessionID] _, ok = u.sessions[sessionID]
u.RUnlock() u.mutex.RUnlock()
if !ok { if !ok {
// if the login type is part of a single stage flow then allow them to omit the session ID // if the login type is part of a single stage flow then allow them to omit the session ID