From 1a10945b1a3cd373c3f9f96ddf33c671d12a8d0a Mon Sep 17 00:00:00 2001 From: David Thorpe Date: Wed, 5 Jun 2024 10:30:42 +0200 Subject: [PATCH 1/4] Updated formatting for tablewriter --- pkg/handler/auth/token.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/handler/auth/token.go b/pkg/handler/auth/token.go index 5b61049..a1dbb92 100644 --- a/pkg/handler/auth/token.go +++ b/pkg/handler/auth/token.go @@ -15,11 +15,11 @@ import ( // TYPES type Token struct { - Name string `json:"name,omitempty"` // Name of the token - Value string `json:"token,omitempty"` // Token value - Expire time.Time `json:"expire_time,omitempty"` // Time of expiration for the token - Time time.Time `json:"access_time"` // Time of last access - Scope []string `json:"scopes,omitempty"` // Authentication scopes + Name string `json:"name,omitempty"` // Name of the token + Value string `json:"token,omitempty"` // Token value + Expire time.Time `json:"expire_time,omitempty" writer:",width:29"` // Time of expiration for the token + Time time.Time `json:"access_time,omitempty" writer:",width:29"` // Time of last access + Scope []string `json:"scopes,omitempty" writer:",wrap"` // Authentication scopes } type TokenCreate struct { From b9a81ca5281c528ff6caa8528042cfda372ea5c4 Mon Sep 17 00:00:00 2001 From: David Thorpe Date: Wed, 5 Jun 2024 10:41:42 +0200 Subject: [PATCH 2/4] Updated endoint scopes --- pkg/handler/auth/endpoints.go | 2 -- pkg/handler/nginx/endpoints.go | 32 +++++++++++++++++++------------- pkg/handler/nginx/scope.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 pkg/handler/nginx/scope.go diff --git a/pkg/handler/auth/endpoints.go b/pkg/handler/auth/endpoints.go index 48171ca..8c2d20a 100644 --- a/pkg/handler/auth/endpoints.go +++ b/pkg/handler/auth/endpoints.go @@ -51,7 +51,6 @@ func (service *auth) AddEndpoints(ctx context.Context, r server.Router) { // Scopes: write // TODO: Add scopes // Description: Create a new token r.AddHandlerFuncRe(ctx, reRoot, service.CreateToken, http.MethodPost).(router.Route). - SetScope(service.ScopeRead()...). SetScope(service.ScopeWrite()...) // Path: / @@ -66,7 +65,6 @@ func (service *auth) AddEndpoints(ctx context.Context, r server.Router) { // Scopes: write // TODO: Add scopes // Description: Delete or update a token r.AddHandlerFuncRe(ctx, reToken, service.UpdateToken, http.MethodDelete, http.MethodPatch).(router.Route). - SetScope(service.ScopeRead()...). SetScope(service.ScopeWrite()...) } diff --git a/pkg/handler/nginx/endpoints.go b/pkg/handler/nginx/endpoints.go index 8a4b3a8..15fc731 100644 --- a/pkg/handler/nginx/endpoints.go +++ b/pkg/handler/nginx/endpoints.go @@ -50,42 +50,48 @@ var ( // PUBLIC METHODS - ENDPOINTS // Add endpoints to the router -func (service *nginx) AddEndpoints(ctx context.Context, router server.Router) { +func (service *nginx) AddEndpoints(ctx context.Context, r server.Router) { // Path: / // Methods: GET - // Scopes: read // TODO: Add scopes + // Scopes: read // Description: Get nginx status (version, uptime, available and enabled configurations) - router.AddHandlerFuncRe(ctx, reRoot, service.GetHealth, http.MethodGet) + r.AddHandlerFuncRe(ctx, reRoot, service.GetHealth, http.MethodGet).(router.Route). + SetScope(service.ScopeRead()...) // Path: /(test|reload|reopen) // Methods: PUT - // Scopes: write // TODO: Add scopes + // Scopes: write // Description: Test, reload and reopen nginx configuration - router.AddHandlerFuncRe(ctx, reAction, service.PutAction, http.MethodPut) + r.AddHandlerFuncRe(ctx, reAction, service.PutAction, http.MethodPut).(router.Route). + SetScope(service.ScopeWrite()...) // Path: /config // Methods: GET - // Scopes: read // TODO: Add scopes + // Scopes: read // Description: Read the current set of configurations - router.AddHandlerFuncRe(ctx, reListConfig, service.ListConfig, http.MethodGet) + r.AddHandlerFuncRe(ctx, reListConfig, service.ListConfig, http.MethodGet).(router.Route). + SetScope(service.ScopeRead()...) // Path: /config // Methods: POST - // Scopes: read // TODO: Add scopes + // Scopes: write // Description: Create a new configuration - router.AddHandlerFuncRe(ctx, reListConfig, service.CreateConfig, http.MethodPost) + r.AddHandlerFuncRe(ctx, reListConfig, service.CreateConfig, http.MethodPost).(router.Route). + SetScope(service.ScopeWrite()...) // Path: /config/{id} // Methods: GET - // Scopes: read // TODO: Add scopes + // Scopes: read // Description: Read a configuration - router.AddHandlerFuncRe(ctx, reConfig, service.ReadConfig, http.MethodGet) + r.AddHandlerFuncRe(ctx, reConfig, service.ReadConfig, http.MethodGet).(router.Route). + SetScope(service.ScopeRead()...) // Path: /config/{id} // Methods: DELETE, POST, PATCH - // Scopes: write // TODO: Add scopes + // Scopes: write // Description: Modify a configuration - router.AddHandlerFuncRe(ctx, reConfig, service.WriteConfig, http.MethodDelete, http.MethodPatch) + r.AddHandlerFuncRe(ctx, reConfig, service.WriteConfig, http.MethodDelete, http.MethodPatch).(router.Route). + SetScope(service.ScopeWrite()...) } /////////////////////////////////////////////////////////////////////////////// diff --git a/pkg/handler/nginx/scope.go b/pkg/handler/nginx/scope.go new file mode 100644 index 0000000..99e4dcc --- /dev/null +++ b/pkg/handler/nginx/scope.go @@ -0,0 +1,33 @@ +package nginx + +import ( + // Packages + "github.com/mutablelogic/go-server/pkg/version" +) + +//////////////////////////////////////////////////////////////////////////////// +// GLOBALS + +var ( + // Prefix + scopePrefix = version.GitSource + "/scope/" +) + +//////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +func (nginx *nginx) ScopeRead() []string { + // Return read (list, get) scopes + return []string{ + scopePrefix + nginx.Label() + "/read", + scopePrefix + defaultName + "/read", + } +} + +func (nginx *nginx) ScopeWrite() []string { + // Return write (create, delete, update) scopes + return []string{ + scopePrefix + nginx.Label() + "/write", + scopePrefix + defaultName + "/write", + } +} From 99ba24c2a6984e91ff08cf03d0c818386f0b17e1 Mon Sep 17 00:00:00 2001 From: David Thorpe Date: Wed, 5 Jun 2024 10:42:36 +0200 Subject: [PATCH 3/4] Removed TODO comments --- pkg/handler/auth/endpoints.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/handler/auth/endpoints.go b/pkg/handler/auth/endpoints.go index 8c2d20a..4946dc7 100644 --- a/pkg/handler/auth/endpoints.go +++ b/pkg/handler/auth/endpoints.go @@ -41,28 +41,28 @@ var ( func (service *auth) AddEndpoints(ctx context.Context, r server.Router) { // Path: / // Methods: GET - // Scopes: read // TODO: Add scopes + // Scopes: read // Description: Get current set of tokens and groups r.AddHandlerFuncRe(ctx, reRoot, service.ListTokens, http.MethodGet).(router.Route). SetScope(service.ScopeRead()...) // Path: / // Methods: POST - // Scopes: write // TODO: Add scopes + // Scopes: write // Description: Create a new token r.AddHandlerFuncRe(ctx, reRoot, service.CreateToken, http.MethodPost).(router.Route). SetScope(service.ScopeWrite()...) // Path: / // Methods: GET - // Scopes: read // TODO: Add scopes + // Scopes: read // Description: Get a token r.AddHandlerFuncRe(ctx, reToken, service.GetToken, http.MethodGet).(router.Route). SetScope(service.ScopeRead()...) // Path: / // Methods: DELETE, PATCH - // Scopes: write // TODO: Add scopes + // Scopes: write // Description: Delete or update a token r.AddHandlerFuncRe(ctx, reToken, service.UpdateToken, http.MethodDelete, http.MethodPatch).(router.Route). SetScope(service.ScopeWrite()...) From f013315ae9608eff4f0bbae1eb6db3dbbd9be1e8 Mon Sep 17 00:00:00 2001 From: David Thorpe Date: Wed, 5 Jun 2024 10:59:59 +0200 Subject: [PATCH 4/4] Added some more scoping functions --- pkg/handler/auth/middleware.go | 4 ++- pkg/handler/router/endpoints.go | 43 +++++++++++++++++++++++++++++++++ pkg/handler/router/interface.go | 3 +++ pkg/handler/router/router.go | 25 +++++++++++++++++++ pkg/handler/router/scope.go | 33 +++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 pkg/handler/router/endpoints.go create mode 100644 pkg/handler/router/scope.go diff --git a/pkg/handler/auth/middleware.go b/pkg/handler/auth/middleware.go index 99a61c3..b892911 100644 --- a/pkg/handler/auth/middleware.go +++ b/pkg/handler/auth/middleware.go @@ -50,8 +50,10 @@ func (middleware *auth) Wrap(ctx context.Context, next http.HandlerFunc) http.Ha authorized := true if token.IsZero() { authorized = false + httpresponse.Error(w, http.StatusUnauthorized, "invalid or missing token") } else if !token.IsValid() { authorized = false + httpresponse.Error(w, http.StatusUnauthorized, "invalid or missing token") } else if token.IsScope(ScopeRoot) { // Allow - token is a super-user token } else if allowedScopes := router.Scope(r.Context()); len(allowedScopes) == 0 { @@ -59,11 +61,11 @@ func (middleware *auth) Wrap(ctx context.Context, next http.HandlerFunc) http.Ha } else if !token.IsScope(allowedScopes...) { // Deny - token does not have the required scopes authorized = false + httpresponse.Error(w, http.StatusUnauthorized, "required scope: ", strings.Join(allowedScopes, ",")) } // Return unauthorized if token is not found or not valid if !authorized { - httpresponse.Error(w, http.StatusUnauthorized) return } diff --git a/pkg/handler/router/endpoints.go b/pkg/handler/router/endpoints.go new file mode 100644 index 0000000..e07324c --- /dev/null +++ b/pkg/handler/router/endpoints.go @@ -0,0 +1,43 @@ +package router + +import ( + "context" + "net/http" + "regexp" + + // Packages + server "github.com/mutablelogic/go-server" + httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse" +) + +/////////////////////////////////////////////////////////////////////////////// +// GLOBALS + +const ( + jsonIndent = 2 +) + +var ( + reRoot = regexp.MustCompile(`^/?$`) +) + +/////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS - ENDPOINTS + +// Add endpoints to the router +func (service *router) AddEndpoints(ctx context.Context, r server.Router) { + // Path: / + // Methods: GET + // Scopes: read + // Description: Get router services + r.AddHandlerFuncRe(ctx, reRoot, service.GetScopes, http.MethodGet).(Route). + SetScope(service.ScopeRead()...) +} + +/////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +// Get registered scopes +func (service *router) GetScopes(w http.ResponseWriter, r *http.Request) { + httpresponse.JSON(w, service.Scopes(), http.StatusOK, jsonIndent) +} diff --git a/pkg/handler/router/interface.go b/pkg/handler/router/interface.go index c7dbbd7..c31033f 100644 --- a/pkg/handler/router/interface.go +++ b/pkg/handler/router/interface.go @@ -15,6 +15,9 @@ type Router interface { // http status code, which will be 200 on success, 404 or 405 and // path parameters extracted from the path. Match(host, method, path string) (*matchedRoute, int) + + // Return all known scopes + Scopes() []string } type Route interface { diff --git a/pkg/handler/router/router.go b/pkg/handler/router/router.go index 34761a8..591a1d6 100644 --- a/pkg/handler/router/router.go +++ b/pkg/handler/router/router.go @@ -184,6 +184,31 @@ func (router *router) Match(host, method, path string) (*matchedRoute, int) { return nil, http.StatusMethodNotAllowed } +func (router *router) Scopes() []string { + scopes := make(map[string]bool) + for _, r := range router.host { + for _, h := range r.prefix { + for _, r := range h.handlers { + for _, s := range r.scopes { + scopes[s] = true + } + } + } + } + + // Gather all scopes + result := make([]string, 0, len(scopes)) + for scope := range scopes { + result = append(result, scope) + } + + // Sort alphabetically + sort.Strings(result) + + // Return the result + return result +} + /////////////////////////////////////////////////////////////////////////////// // PRIVATE METHODS diff --git a/pkg/handler/router/scope.go b/pkg/handler/router/scope.go new file mode 100644 index 0000000..160deb2 --- /dev/null +++ b/pkg/handler/router/scope.go @@ -0,0 +1,33 @@ +package router + +import ( + // Packages + "github.com/mutablelogic/go-server/pkg/version" +) + +//////////////////////////////////////////////////////////////////////////////// +// GLOBALS + +var ( + // Prefix + scopePrefix = version.GitSource + "/scope/" +) + +//////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +func (router *router) ScopeRead() []string { + // Return read (list, get) scopes + return []string{ + scopePrefix + router.Label() + "/read", + scopePrefix + defaultName + "/read", + } +} + +func (router *router) ScopeWrite() []string { + // Return write (create, delete, update) scopes + return []string{ + scopePrefix + router.Label() + "/write", + scopePrefix + defaultName + "/write", + } +}