diff --git a/pkg/auth/flow.go b/pkg/auth/flow.go index 1174aa9..e757a77 100644 --- a/pkg/auth/flow.go +++ b/pkg/auth/flow.go @@ -18,15 +18,43 @@ type AuthFlowManager struct { type LoginFlow struct { flow.FlowHandlerBase + + ClientID common.ClientID + FlowContext +} + +type FlowContext struct { + IPAddr string + CredentialOnly bool + RedirectURI string +} + +type LoginFlowRequest struct { + ClientID common.ClientID `json:"client_id"` + Handler []*string `json:"handler"` + RedirectURI string `json:"redirect_uri"` + Type *string `json:"type"` + + ip string `json:"-"` +} + +func (r *LoginFlowRequest) FlowContext() FlowContext { + return FlowContext{ + IPAddr: r.ip, + RedirectURI: r.RedirectURI, + CredentialOnly: r.Type != nil && *r.Type == "link_user", + } } func NewAuthFlowManager() *AuthFlowManager { return &AuthFlowManager{FlowManager: flow.NewFlowManager()} } -func (afm *AuthFlowManager) NewLoginFlow(f *flow.FlowRequest, prov provider.AuthProvider) *LoginFlow { +func (afm *AuthFlowManager) NewLoginFlow(req *LoginFlowRequest, prov provider.AuthProvider) *LoginFlow { lf := &LoginFlow{ - FlowHandlerBase: flow.NewFlowHandlerBase(f, prov, prov.ProviderType()), + FlowHandlerBase: flow.NewFlowHandlerBase(prov, prov.ProviderType()), + ClientID: req.ClientID, + FlowContext: req.FlowContext(), } afm.Register(lf) @@ -34,7 +62,7 @@ func (afm *AuthFlowManager) NewLoginFlow(f *flow.FlowRequest, prov provider.Auth return lf } -func (a *Authenticator) NewFlow(r *flow.FlowRequest) *flow.FlowResult { +func (a *Authenticator) NewFlow(r *LoginFlowRequest) *flow.Result { var prov provider.AuthProvider for _, h := range r.Handler { @@ -42,7 +70,7 @@ func (a *Authenticator) NewFlow(r *flow.FlowRequest) *flow.FlowResult { break } - prov = a.Provider(h.String()) + prov = a.Provider(*h) if prov != nil { break } @@ -79,7 +107,7 @@ func (f *LoginFlow) progress(a *Authenticator, c echo.Context) error { user, err := a.Check(f, c.Request(), rm) switch err { case nil: - finishedFlow := flow.FlowResult{} + finishedFlow := flow.Result{} a.flows.Remove(f) copier.Copy(&finishedFlow, f) finishedFlow.Type = flow.TypeCreateEntry @@ -95,13 +123,15 @@ func (f *LoginFlow) progress(a *Authenticator, c echo.Context) error { case ErrInvalidAuth: fallthrough default: - fr := f.ShowForm(map[string]interface{}{ + return c.JSON(http.StatusOK, f.ShowForm(map[string]interface{}{ "base": "invalid_auth", - }) - return c.JSON(http.StatusOK, fr) + })) } default: - return c.String(http.StatusBadRequest, "unknown flow step") + return c.JSON(http.StatusOK, f.ShowForm(map[string]interface{}{ + "base": "unknown_flow_step", + })) + } } @@ -127,12 +157,14 @@ func setJSON(c echo.Context) { func (a *Authenticator) BeginLoginFlowHandler(c echo.Context) error { setJSON(c) - var flowReq flow.FlowRequest + var flowReq LoginFlowRequest err := c.Bind(&flowReq) if err != nil { return c.String(http.StatusBadRequest, err.Error()) } + flowReq.ip = c.Request().RemoteAddr + resp := a.NewFlow(&flowReq) if resp == nil { diff --git a/pkg/auth/provider/hass/provider.go b/pkg/auth/provider/hass/provider.go index f692eec..e6c2624 100644 --- a/pkg/auth/provider/hass/provider.go +++ b/pkg/auth/provider/hass/provider.go @@ -127,8 +127,8 @@ func (hap *HomeAssistantProvider) NewCredData() interface{} { return &HAUser{} } -func (hap *HomeAssistantProvider) FlowSchema() flow.FlowSchema { - return []flow.FlowSchemaItem{ +func (hap *HomeAssistantProvider) FlowSchema() flow.Schema { + return []flow.SchemaItem{ { Type: "string", Name: "username", diff --git a/pkg/auth/provider/provider.go b/pkg/auth/provider/provider.go index 2738a6c..f3aeb5b 100644 --- a/pkg/auth/provider/provider.go +++ b/pkg/auth/provider/provider.go @@ -14,7 +14,7 @@ var Providers = make(map[string]Constructor) type AuthProvider interface { // TODO: this should include stepping AuthProviderMetadata ProviderBase() AuthProviderBase - FlowSchema() flow.FlowSchema + FlowSchema() flow.Schema NewCredData() interface{} ValidateCreds(r *http.Request, reqMap map[string]interface{}) (user ProviderUser, success bool) Lookup(ProviderUser) ProviderUser diff --git a/pkg/auth/provider/trustednets/trustednets.go b/pkg/auth/provider/trustednets/trustednets.go index 68108e4..93dfe05 100644 --- a/pkg/auth/provider/trustednets/trustednets.go +++ b/pkg/auth/provider/trustednets/trustednets.go @@ -62,8 +62,8 @@ func (hap *TrustedNetworksProvider) NewCredData() interface{} { return &UserData{} } -func (hap *TrustedNetworksProvider) FlowSchema() flow.FlowSchema { - return []flow.FlowSchemaItem{ +func (hap *TrustedNetworksProvider) FlowSchema() flow.Schema { + return []flow.SchemaItem{ { Type: "string", Name: "username", diff --git a/pkg/flow/flow.go b/pkg/flow/flow.go index cacc76e..a65a920 100644 --- a/pkg/flow/flow.go +++ b/pkg/flow/flow.go @@ -5,16 +5,15 @@ import ( "fmt" "time" - "dynatron.me/x/blasphem/internal/common" "dynatron.me/x/blasphem/internal/generate" ) type ( - FlowResultType string - FlowID string - Step string - HandlerKey string - Errors interface{} + ResultType string + FlowID string + Step string + HandlerKey string + Errors interface{} Context interface{} @@ -24,14 +23,14 @@ type ( flows FlowStore } - FlowResult struct { - Type FlowResultType `json:"type"` + Result struct { + Type ResultType `json:"type"` ID FlowID `json:"flow_id"` Handler []*HandlerKey `json:"handler"` Title *string `json:"title,omitempty"` Data map[string]interface{} `json:"data,omitempty"` StepID *Step `json:"step_id,omitempty"` - Schema []FlowSchemaItem `json:"data_schema"` + Schema []SchemaItem `json:"data_schema"` Extra *string `json:"extra,omitempty"` Required *bool `json:"required,omitempty"` Errors interface{} `json:"errors"` @@ -46,29 +45,24 @@ type ( Version *int `json:"version,omitempty"` } - FlowSchemaItem struct { + SchemaItem struct { Type string `json:"type"` Name string `json:"name"` Required bool `json:"required"` } - FlowSchema []FlowSchemaItem - - FlowRequest struct { - ClientID common.ClientID `json:"client_id"` - Handler []*HandlerKey `json:"handler"` - RedirectURI string `json:"redirect_uri"` - } + Schema []SchemaItem ) type ( Schemer interface { - FlowSchema() FlowSchema + FlowSchema() Schema } Handler interface { Base() FlowHandlerBase FlowID() FlowID + flowCtime() time.Time } ) @@ -77,7 +71,7 @@ const ( StepInit Step = "init" ) -func (fs *FlowSchema) CheckRequired(rm map[string]interface{}) error { +func (fs *Schema) CheckRequired(rm map[string]interface{}) error { for _, si := range *fs { if si.Required { if _, ok := rm[si.Name]; !ok { @@ -98,12 +92,10 @@ func NewFlowManager() *FlowManager { func stepPtr(s Step) *Step { return &s } type FlowHandlerBase struct { - ID FlowID // ID is the FlowID - Handler HandlerKey // Handler key - Context Context // flow Context - ClientID common.ClientID - RedirectURI string - Schema FlowSchema + ID FlowID // ID is the FlowID + Handler HandlerKey // Handler key + Context Context // flow Context + Schema Schema // curStep is the current step set by the flow manager curStep Step @@ -121,13 +113,11 @@ func (f *FlowHandlerBase) FlowID() FlowID { func (f *FlowHandlerBase) flowCtime() time.Time { return f.ctime } -func NewFlowHandlerBase(f *FlowRequest, sch Schemer, hand string) FlowHandlerBase { +func NewFlowHandlerBase(sch Schemer, hand string) FlowHandlerBase { return FlowHandlerBase{ - ID: FlowID(generate.UUID()), - Handler: HandlerKey(hand), - ClientID: f.ClientID, - RedirectURI: f.RedirectURI, - Schema: sch.FlowSchema(), + ID: FlowID(generate.UUID()), + Handler: HandlerKey(hand), + Schema: sch.FlowSchema(), curStep: StepInit, ctime: time.Now(), @@ -150,8 +140,8 @@ func resultErrs(e Errors) Errors { return e } -func (fm *FlowHandlerBase) ShowForm(errs Errors) *FlowResult { - res := &FlowResult{ +func (fm *FlowHandlerBase) ShowForm(errs Errors) *Result { + res := &Result{ Type: TypeForm, ID: fm.ID, StepID: stepPtr(fm.curStep), @@ -168,14 +158,14 @@ func (fm *FlowManager) Delete(id FlowID) { } const ( - TypeForm FlowResultType = "form" - TypeCreateEntry FlowResultType = "create_entry" - TypeAbort FlowResultType = "abort" - TypeExternalStep FlowResultType = "external" - TypeExternalStepDone FlowResultType = "external_done" - TypeShowProgress FlowResultType = "progress" - TypeShowProgressDone FlowResultType = "progress_done" - TypeMenu FlowResultType = "menu" + TypeForm ResultType = "form" + TypeCreateEntry ResultType = "create_entry" + TypeAbort ResultType = "abort" + TypeExternalStep ResultType = "external" + TypeExternalStepDone ResultType = "external_done" + TypeShowProgress ResultType = "progress" + TypeShowProgressDone ResultType = "progress_done" + TypeMenu ResultType = "menu" ) func (f *FlowHandlerBase) touch() {