From 293c29d6e7965fd158b7f5b99c7acde0cc9fb41a Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Mon, 6 Aug 2018 04:20:59 +0300 Subject: [PATCH] preparing for the Iris control panel -- stay tuned when you hear my new project name 'cornea' Former-commit-id: 8c0ada865ee17801efe90b197bf49bdbd55f636b --- core/router/macro/template.go | 16 ++++++++-------- core/router/route.go | 14 +++++++------- middleware/basicauth/basicauth.go | 15 +++++++++++---- middleware/basicauth/config.go | 22 +++++++++++++++++++++- 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/core/router/macro/template.go b/core/router/macro/template.go index b1a3afa3..f26c1d06 100644 --- a/core/router/macro/template.go +++ b/core/router/macro/template.go @@ -13,21 +13,21 @@ import ( // path, i.e the min as param name and 1 as the param argument. type Template struct { // Src is the original template given by the client - Src string - Params []TemplateParam + Src string `json:"src"` + Params []TemplateParam `json:"params"` } // TemplateParam is the parsed macro parameter's template // they are being used to describe the param's syntax result. type TemplateParam struct { - Src string // the unparsed param'false source + Src string `json:"src"` // the unparsed param'false source // Type is not useful anywhere here but maybe // it's useful on host to decide how to convert the path template to specific router's syntax - Type ast.ParamType - Name string - ErrCode int - TypeEvaluator EvaluatorFunc - Funcs []EvaluatorFunc + Type ast.ParamType `json:"type"` + Name string `json:"name"` + ErrCode int `json:"errCode"` + TypeEvaluator EvaluatorFunc `json:"-"` + Funcs []EvaluatorFunc `json:"-"` } // Parse takes a full route path and a macro map (macro map contains the macro types with their registered param functions) diff --git a/core/router/route.go b/core/router/route.go index 346c068d..460f5c67 100644 --- a/core/router/route.go +++ b/core/router/route.go @@ -12,24 +12,24 @@ import ( // If any of the following fields are changed then the // caller should Refresh the router. type Route struct { - Name string // "userRoute" - Method string // "GET" - Subdomain string // "admin." + Name string `json:"name"` // "userRoute" + Method string `json:"method"` // "GET" + Subdomain string `json:"subdomain"` // "admin." tmpl *macro.Template // Tmpl().Src: "/api/user/{id:int}" - Path string // "/api/user/:id" // temp storage, they're appended to the Handlers on build. // Execution happens before Handlers, can be empty. beginHandlers context.Handlers // Handlers are the main route's handlers, executed by order. // Cannot be empty. - Handlers context.Handlers - MainHandlerName string + Handlers context.Handlers `json:"-"` + MainHandlerName string `json:"mainHandlerName"` // temp storage, they're appended to the Handlers on build. // Execution happens after Begin and main Handler(s), can be empty. doneHandlers context.Handlers + Path string `json:"path"` // "/api/user/:id" // FormattedPath all dynamic named parameters (if any) replaced with %v, // used by Application to validate param values of a Route based on its name. - FormattedPath string + FormattedPath string `json:"formattedPath"` } // NewRoute returns a new route based on its method, diff --git a/middleware/basicauth/basicauth.go b/middleware/basicauth/basicauth.go index be3cebec..3016f4b4 100644 --- a/middleware/basicauth/basicauth.go +++ b/middleware/basicauth/basicauth.go @@ -26,7 +26,11 @@ type ( // these are filled from the config.Users map at the startup auth encodedUsers realmHeaderValue string - expireEnabled bool // if the config.Expires is a valid date, default disabled + + // The below can be removed but they are here because on the future we may add dynamic options for those two fields, + // it is a bit faster to check the b.$bool as well. + expireEnabled bool // if the config.Expires is a valid date, default is disabled. + askHandlerEnabled bool // if the config.OnAsk is not nil, defaults to false. } ) @@ -43,6 +47,7 @@ func New(c Config) context.Handler { } config.Users = c.Users config.Expires = c.Expires + config.OnAsk = c.OnAsk b := &basicAuthMiddleware{config: config} b.init() @@ -72,9 +77,8 @@ func (b *basicAuthMiddleware) init() { // set the auth realm header's value b.realmHeaderValue = "Basic realm=" + strconv.Quote(b.config.Realm) - if b.config.Expires > 0 { - b.expireEnabled = true - } + b.expireEnabled = b.config.Expires > 0 + b.askHandlerEnabled = b.config.OnAsk != nil } func (b *basicAuthMiddleware) findAuth(headerValue string) (auth *encodedUser, found bool) { @@ -96,6 +100,9 @@ func (b *basicAuthMiddleware) findAuth(headerValue string) (auth *encodedUser, f func (b *basicAuthMiddleware) askForCredentials(ctx context.Context) { ctx.Header("WWW-Authenticate", b.realmHeaderValue) ctx.StatusCode(iris.StatusUnauthorized) + if b.askHandlerEnabled { + b.config.OnAsk(ctx) + } } // Serve the actual middleware diff --git a/middleware/basicauth/config.go b/middleware/basicauth/config.go index 3d0cc807..5ecd430d 100644 --- a/middleware/basicauth/config.go +++ b/middleware/basicauth/config.go @@ -22,11 +22,31 @@ type Config struct { Realm string // Expires expiration duration, default is 0 never expires Expires time.Duration + + // OnAsk fires each time the server asks to the client for credentials in order to gain access and continue to the next handler. + // + // You could also ignore this option and + // - just add a listener for unauthorized status codes with: + // `app.OnErrorCode(iris.StatusUnauthorized, unauthorizedWantsAccessHandler)` + // - or register a middleware which will force `ctx.Next/or direct call` + // the basicauth middleware and check its `ctx.GetStatusCode()`. + // + // However, this option is very useful when you want the framework to fire a handler + // ONLY when the Basic Authentication sends an `iris.StatusUnauthorized`, + // and free the error code listener to catch other types of unauthorized access, i.e Kerberos. + // Also with this one, not recommended at all but, you are able to "force-allow" other users by calling the `ctx.StatusCode` inside this handler; + // i.e when it is possible to create authorized users dynamically but + // if that is the case then you should go with something like sessions instead of basic authentication. + // + // Usage: basicauth.New(basicauth.Config{..., OnAsk: unauthorizedWantsAccessViaBasicAuthHandler}) + // + // Defaults to nil. + OnAsk context.Handler } // DefaultConfig returns the default configs for the BasicAuth middleware func DefaultConfig() Config { - return Config{make(map[string]string), DefaultBasicAuthRealm, 0} + return Config{make(map[string]string), DefaultBasicAuthRealm, 0, nil} } // User returns the user from context key same as ctx.Request().BasicAuth().