123 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			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
 | |
| }
 |