Skip to content

Commit

Permalink
Merge pull request #69 from mutablelogic/v4
Browse files Browse the repository at this point in the history
Scope updates
  • Loading branch information
djthorpe authored Jun 5, 2024
2 parents b365d21 + f013315 commit e39a8d5
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 25 deletions.
10 changes: 4 additions & 6 deletions pkg/handler/auth/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,32 +41,30 @@ 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.ScopeRead()...).
SetScope(service.ScopeWrite()...)

// Path: /<token-name>
// 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: /<token-name>
// 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.ScopeRead()...).
SetScope(service.ScopeWrite()...)

}
Expand Down
4 changes: 3 additions & 1 deletion pkg/handler/auth/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,22 @@ 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 {
// Allow - no scopes have been defined on this endpoint
} 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
}

Expand Down
10 changes: 5 additions & 5 deletions pkg/handler/auth/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
32 changes: 19 additions & 13 deletions pkg/handler/nginx/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()...)
}

///////////////////////////////////////////////////////////////////////////////
Expand Down
33 changes: 33 additions & 0 deletions pkg/handler/nginx/scope.go
Original file line number Diff line number Diff line change
@@ -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",
}
}
43 changes: 43 additions & 0 deletions pkg/handler/router/endpoints.go
Original file line number Diff line number Diff line change
@@ -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)
}
3 changes: 3 additions & 0 deletions pkg/handler/router/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
25 changes: 25 additions & 0 deletions pkg/handler/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
33 changes: 33 additions & 0 deletions pkg/handler/router/scope.go
Original file line number Diff line number Diff line change
@@ -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",
}
}

0 comments on commit e39a8d5

Please sign in to comment.