Documentation ¶
Index ¶
- Constants
- Variables
- type AuthorizationService
- func (authorizationService *AuthorizationService) IsAuthorized(token string, permissionsRequired []string) bool
- func (authorizationService *AuthorizationService) WithClient(client *Client) *AuthorizationService
- func (authorizationService *AuthorizationService) WithClientProvider(provider *ClientProvider) *AuthorizationService
- func (authorizationService *AuthorizationService) WithClients(clients []*Client) *AuthorizationService
- func (authorizationService *AuthorizationService) WithToken(token string) *AuthorizationService
- func (authorizationService *AuthorizationService) WithTokens(tokens []string) *AuthorizationService
- type Client
- type ClientProvider
- type Gate
- func (gate *Gate) Protect(handler http.Handler) http.Handler
- func (gate *Gate) ProtectFunc(handlerFunc http.HandlerFunc) http.HandlerFunc
- func (gate *Gate) ProtectFuncWithPermission(handlerFunc http.HandlerFunc, permission string) http.HandlerFunc
- func (gate *Gate) ProtectFuncWithPermissions(handlerFunc http.HandlerFunc, permissions []string) http.HandlerFunc
- func (gate *Gate) ProtectWithPermission(handler http.Handler, permission string) http.Handler
- func (gate *Gate) ProtectWithPermissions(handler http.Handler, permissions []string) http.Handler
- func (gate *Gate) WithCustomUnauthorizedResponseBody(unauthorizedResponseBody []byte) *Gate
- func (gate *Gate) WithRateLimit(maximumRequestsPerSecond int) *Gate
- type RateLimiter
Constants ¶
const ( // AuthorizationHeader is the header in which g8 looks for the authorization bearer token AuthorizationHeader = "Authorization" DefaultUnauthorizedResponseBody = "Authorization Bearer token is missing or invalid" // DefaultTooManyRequestsResponseBody is the default response body returned if a request exceeded the allowed rate limit DefaultTooManyRequestsResponseBody = "Too Many Requests" )
Variables ¶
var ( // ErrNoExpiration is the error returned by ClientProvider.StartCacheJanitor if there was an attempt to start the // janitor despite no expiration being configured. // To clarify, this is because the cache janitor is only useful when an expiration is set. ErrNoExpiration = errors.New("no point starting the janitor if the TTL is set to not expire") // ErrCacheNotInitialized is the error returned by ClientProvider.StartCacheJanitor if there was an attempt to start // the janitor despite the cache not having been initialized using ClientProvider.WithCache ErrCacheNotInitialized = errors.New("cannot cache not configured") )
Functions ¶
This section is empty.
Types ¶
type AuthorizationService ¶
type AuthorizationService struct {
// contains filtered or unexported fields
}
AuthorizationService is the service that manages client/token registry and client fallback as well as the service that determines whether a token meets the specific requirements to be authorized by a Gate or not.
func NewAuthorizationService ¶
func NewAuthorizationService() *AuthorizationService
NewAuthorizationService creates a new AuthorizationService
func (*AuthorizationService) IsAuthorized ¶
func (authorizationService *AuthorizationService) IsAuthorized(token string, permissionsRequired []string) bool
IsAuthorized checks whether a client with a given token exists and has the permissions required.
If permissionsRequired is nil or empty and a client with the given token exists, said client will have access to all handlers that are not protected by a given permission.
func (*AuthorizationService) WithClient ¶
func (authorizationService *AuthorizationService) WithClient(client *Client) *AuthorizationService
WithClient is used to specify a single client for which authorization will be granted
When compared to WithToken, the advantage of using this function is that you may specify the client's permissions and thus, be a lot more granular with what endpoint a token has access to.
In other words, if you were to do the following:
gate := g8.NewGate(g8.NewAuthorizationService().WithClient(g8.NewClient("12345").WithPermission("mod")))
The following handlers would be accessible with the token 12345:
router.Handle("/1st-handler", gate.ProtectWithPermissions(yourHandler, []string{"mod"})) router.Handle("/2nd-handler", gate.Protect(yourOtherHandler))
But not this one, because the user does not have the permission "admin":
router.Handle("/3rd-handler", gate.ProtectWithPermissions(yetAnotherHandler, []string{"admin"}))
Calling this function multiple times will add multiple clients, though you may want to use WithClients instead if you plan to add multiple clients
func (*AuthorizationService) WithClientProvider ¶
func (authorizationService *AuthorizationService) WithClientProvider(provider *ClientProvider) *AuthorizationService
WithClientProvider allows specifying a custom provider to fetch clients by token.
For example, you can use it to fallback to making a call in your database when a request is made with a token that hasn't been specified via WithToken, WithTokens, WithClient or WithClients.
func (*AuthorizationService) WithClients ¶
func (authorizationService *AuthorizationService) WithClients(clients []*Client) *AuthorizationService
WithClients is used to specify a slice of clients for which authorization will be granted
func (*AuthorizationService) WithToken ¶
func (authorizationService *AuthorizationService) WithToken(token string) *AuthorizationService
WithToken is used to specify a single token for which authorization will be granted
The client that will be created from this token will have access to all handlers that are not protected with a specific permission.
In other words, if you were to do the following:
gate := g8.NewGate(g8.NewAuthorizationService().WithToken("12345"))
The following handler would be accessible with the token 12345:
router.Handle("/1st-handler", gate.Protect(yourHandler))
But not this one would not be accessible with the token 12345:
router.Handle("/2nd-handler", gate.ProtectWithPermissions(yourOtherHandler, []string{"admin"}))
Calling this function multiple times will add multiple clients, though you may want to use WithTokens instead if you plan to add multiple clients
If you wish to configure advanced permissions, consider using WithClient instead.
func (*AuthorizationService) WithTokens ¶
func (authorizationService *AuthorizationService) WithTokens(tokens []string) *AuthorizationService
WithTokens is used to specify a slice of tokens for which authorization will be granted
type Client ¶
type Client struct { // Token is the Bearer token found in the value of a Authorization header Token string // Permissions is a slice of extra permissions that may be used for more granular access control. // // If you only wish to use Gate.Protect and Gate.ProtectFunc, you do not have to worry about this, // since they're only used by Gate.ProtectWithPermissions and Gate.ProtectFuncWithPermissions Permissions []string }
Client is a struct containing both a Token and a slice of extra Permissions that said token has.
func NewClientWithPermissions ¶
NewClientWithPermissions creates a Client with a slice of permissions Equivalent to using NewClient and WithPermissions
func (Client) HasPermission ¶
HasPermission checks whether a client has a given permission
func (Client) HasPermissions ¶
HasPermissions checks whether a client has the all permissions passed
func (*Client) WithPermission ¶
WithPermission adds a permission to a client
func (*Client) WithPermissions ¶
WithPermissions adds a slice of permissions to a client
type ClientProvider ¶
type ClientProvider struct {
// contains filtered or unexported fields
}
ClientProvider has the task of retrieving a Client from an external source (e.g. a database) when provided with a token. It should be used when you have a lot of tokens and it wouldn't make sense to register all of them using AuthorizationService's WithToken, WithTokens, WithClient or WithClients.
Note that the provider is used as a fallback source. As such, if a token is explicitly registered using one of the 4 aforementioned functions, the client provider will not be used by the AuthorizationService when a request is made with said token. It will, however, be called upon if a token that is not explicitly registered in AuthorizationService is sent alongside a request going through the Gate.
clientProvider := g8.NewClientProvider(func(token string) *g8.Client { // We'll assume that the following function calls your database and returns a struct "User" that // has the user's token as well as the permissions granted to said user user := database.GetUserByToken(token) if user != nil { return g8.NewClient(user.Token).WithPermissions(user.Permissions) } return nil }) gate := g8.NewGate(g8.NewAuthorizationService().WithClientProvider(clientProvider))
func NewClientProvider ¶
func NewClientProvider(getClientByTokenFunc func(token string) *Client) *ClientProvider
NewClientProvider creates a ClientProvider The parameter that must be passed is a function that the provider will use to retrieve a client by a given token
Example:
clientProvider := g8.NewClientProvider(func(token string) *g8.Client { // We'll assume that the following function calls your database and returns a struct "User" that // has the user's token as well as the permissions granted to said user user := database.GetUserByToken(token) if user != nil { return g8.NewClient(user.Token).WithPermissions(user.Permissions) } return nil }) gate := g8.NewGate(g8.NewAuthorizationService().WithClientProvider(clientProvider))
func (*ClientProvider) GetClientByToken ¶
func (provider *ClientProvider) GetClientByToken(token string) *Client
GetClientByToken retrieves a client by its token through the provided getClientByTokenFunc.
func (*ClientProvider) StartCacheJanitor ¶ added in v0.2.0
func (provider *ClientProvider) StartCacheJanitor() error
StartCacheJanitor starts the cache janitor, which passively deletes expired cache entries in the background.
Not really necessary unless you have a lot of clients (100000+).
Even without the janitor, active eviction will still happen (i.e. when GetClientByToken is called, but the cache entry for the given token has expired, the cache entry will be automatically deleted and re-fetched from the user-defined getClientByTokenFunc)
func (*ClientProvider) StopCacheJanitor ¶ added in v0.2.0
func (provider *ClientProvider) StopCacheJanitor()
StopCacheJanitor stops the cache janitor
Not required unless your application initializes multiple providers over the course of its lifecycle. In English, that means if you initialize a ClientProvider only once on application start and it stays up until your application shuts down, you don't need to call this function.
func (*ClientProvider) WithCache ¶ added in v0.2.0
func (provider *ClientProvider) WithCache(ttl time.Duration, maxSize int) *ClientProvider
WithCache adds cache options to the ClientProvider.
ttl is the time until the cache entry will expire. A TTL of gocache.NoExpiration (-1) means no expiration maxSize is the maximum amount of entries that can be in the cache at any given time. If a value of gocache.NoMaxSize (0) or less is provided for maxSize, there will be no maximum size.
Example:
clientProvider := g8.NewClientProvider(func(token string) *g8.Client { // We'll assume that the following function calls your database and returns a struct "User" that // has the user's token as well as the permissions granted to said user user := database.GetUserByToken(token) if user != nil { return g8.NewClient(user.Token).WithPermissions(user.Permissions) } return nil }) gate := g8.NewGate(g8.NewAuthorizationService().WithClientProvider(clientProvider.WithCache(time.Hour, 70000)))
type Gate ¶
type Gate struct {
// contains filtered or unexported fields
}
Gate is lock to the front door of your API, letting only those you allow through.
func NewGate ¶
func NewGate(authorizationService *AuthorizationService) *Gate
NewGate creates a new Gate.
func (*Gate) Protect ¶
Protect secures a handler, requiring requests going through to have a valid Authorization Bearer token. Unlike ProtectWithPermissions, Protect will allow access to any registered tokens, regardless of their permissions or lack thereof.
Example:
gate := g8.NewGate(g8.NewAuthorizationService().WithToken("token")) router := http.NewServeMux() // Without protection router.Handle("/handle", yourHandler) // With protection router.Handle("/handle", gate.Protect(yourHandler))
func (*Gate) ProtectFunc ¶
func (gate *Gate) ProtectFunc(handlerFunc http.HandlerFunc) http.HandlerFunc
ProtectFunc secures a handlerFunc, requiring requests going through to have a valid Authorization Bearer token. Unlike ProtectFuncWithPermissions, ProtectFunc will allow access to any registered tokens, regardless of their permissions or lack thereof.
Example:
gate := g8.NewGate(g8.NewAuthorizationService().WithToken("token")) router := http.NewServeMux() // Without protection router.HandleFunc("/handle", yourHandlerFunc) // With protection router.HandleFunc("/handle", gate.ProtectFunc(yourHandlerFunc))
func (*Gate) ProtectFuncWithPermission ¶ added in v0.1.0
func (gate *Gate) ProtectFuncWithPermission(handlerFunc http.HandlerFunc, permission string) http.HandlerFunc
ProtectFuncWithPermission does the same thing as ProtectFuncWithPermissions, but for a single permission instead of a slice of permissions
See ProtectFuncWithPermissions for further documentation
func (*Gate) ProtectFuncWithPermissions ¶
func (gate *Gate) ProtectFuncWithPermissions(handlerFunc http.HandlerFunc, permissions []string) http.HandlerFunc
ProtectFuncWithPermissions secures a handler, requiring requests going through to have a valid Authorization Bearer token as well as a slice of permissions that must be met.
Example:
gate := g8.NewGate(g8.NewAuthorizationService().WithClient(g8.NewClient("token").WithPermission("admin"))) router := http.NewServeMux() // Without protection router.HandleFunc("/handle", yourHandlerFunc) // With protection router.HandleFunc("/handle", gate.ProtectFuncWithPermissions(yourHandlerFunc, []string{"admin"}))
func (*Gate) ProtectWithPermission ¶ added in v0.1.0
ProtectWithPermission does the same thing as ProtectWithPermissions, but for a single permission instead of a slice of permissions
See ProtectWithPermissions for further documentation
func (*Gate) ProtectWithPermissions ¶
ProtectWithPermissions secures a handler, requiring requests going through to have a valid Authorization Bearer token as well as a slice of permissions that must be met.
Example:
gate := g8.NewGate(g8.NewAuthorizationService().WithClient(g8.NewClient("token").WithPermission("admin"))) router := http.NewServeMux() // Without protection router.Handle("/handle", yourHandler) // With protection router.Handle("/handle", gate.ProtectWithPermissions(yourHandler, []string{"admin"}))
func (*Gate) WithCustomUnauthorizedResponseBody ¶
WithCustomUnauthorizedResponseBody sets a custom response body when Gate determines that a request must be blocked
func (*Gate) WithRateLimit ¶ added in v1.1.0
WithRateLimit adds rate limiting to the Gate
type RateLimiter ¶ added in v1.1.0
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter is a fixed rate limiter
func NewRateLimiter ¶ added in v1.1.0
func NewRateLimiter(maximumExecutionsPerSecond int) *RateLimiter
NewRateLimiter creates a RateLimiter
func (*RateLimiter) Try ¶ added in v1.1.0
func (r *RateLimiter) Try() bool
Try updates the number of executions if the rate limit quota hasn't been reached and returns whether the attempt was successful or not.
Returns false if the execution was not successful (rate limit quota has been reached) Returns true if the execution was successful (rate limit quota has not been reached)