gatus/vendor/github.com/TwiN/g8/authorization.go

123 lines
5.2 KiB
Go

package g8
import (
"sync"
)
// 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.
type AuthorizationService struct {
clients map[string]*Client
clientProvider *ClientProvider
mutex sync.RWMutex
}
// NewAuthorizationService creates a new AuthorizationService
func NewAuthorizationService() *AuthorizationService {
return &AuthorizationService{
clients: make(map[string]*Client),
}
}
// 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.New().WithAuthorizationService(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 *AuthorizationService) WithToken(token string) *AuthorizationService {
authorizationService.mutex.Lock()
authorizationService.clients[token] = NewClient(token)
authorizationService.mutex.Unlock()
return authorizationService
}
// WithTokens is used to specify a slice of tokens for which authorization will be granted
func (authorizationService *AuthorizationService) WithTokens(tokens []string) *AuthorizationService {
authorizationService.mutex.Lock()
for _, token := range tokens {
authorizationService.clients[token] = NewClient(token)
}
authorizationService.mutex.Unlock()
return 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.New().WithAuthorizationService(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 *AuthorizationService) WithClient(client *Client) *AuthorizationService {
authorizationService.mutex.Lock()
authorizationService.clients[client.Token] = client
authorizationService.mutex.Unlock()
return authorizationService
}
// WithClients is used to specify a slice of clients for which authorization will be granted
func (authorizationService *AuthorizationService) WithClients(clients []*Client) *AuthorizationService {
authorizationService.mutex.Lock()
for _, client := range clients {
authorizationService.clients[client.Token] = client
}
authorizationService.mutex.Unlock()
return 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 *AuthorizationService) WithClientProvider(provider *ClientProvider) *AuthorizationService {
authorizationService.clientProvider = provider
return authorizationService
}
// 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 *AuthorizationService) IsAuthorized(token string, permissionsRequired []string) bool {
if len(token) == 0 {
return false
}
authorizationService.mutex.RLock()
client, _ := authorizationService.clients[token]
authorizationService.mutex.RUnlock()
// If there's no clients with the given token directly stored in the AuthorizationService, fall back to the
// client provider, if there's one configured.
if client == nil && authorizationService.clientProvider != nil {
client = authorizationService.clientProvider.GetClientByToken(token)
}
if client != nil {
return client.HasPermissions(permissionsRequired)
}
return false
}