feat(ac): add access control middleware (#321)

This commit is contained in:
Givi Khojanashvili 2022-05-25 20:26:50 +04:00 committed by GitHub
parent abe78666d4
commit 65069c1787
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 2 deletions

View File

@ -35,6 +35,7 @@ type AccountManager interface {
GetAccountById(accountId string) (*Account, error)
GetAccountByUserOrAccountId(userId, accountId, domain string) (*Account, error)
GetAccountWithAuthorizationClaims(claims jwtclaims.AuthorizationClaims) (*Account, error)
IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error)
AccountExists(accountId string) (*bool, error)
AddAccount(accountId, userId, domain string) (*Account, error)
GetPeer(peerKey string) (*Peer, error)

View File

@ -0,0 +1,50 @@
package middleware
import (
"fmt"
"net/http"
"github.com/netbirdio/netbird/management/server/jwtclaims"
)
type IsUserAdminFunc func(claims jwtclaims.AuthorizationClaims) (bool, error)
// AccessControll middleware to restrict to make POST/PUT/DELETE requests by admin only
type AccessControll struct {
jwtExtractor jwtclaims.ClaimsExtractor
isUserAdmin IsUserAdminFunc
audience string
}
// NewAccessControll instance constructor
func NewAccessControll(audience string, isUserAdmin IsUserAdminFunc) *AccessControll {
return &AccessControll{
isUserAdmin: isUserAdmin,
audience: audience,
jwtExtractor: *jwtclaims.NewClaimsExtractor(nil),
}
}
// Handler method of the middleware which forbinneds all modify requests for non admin users
func (a *AccessControll) Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
jwtClaims := a.jwtExtractor.ExtractClaimsFromRequestContext(r, a.audience)
ok, err := a.isUserAdmin(jwtClaims)
if err != nil {
http.Error(w, fmt.Sprintf("error get user from JWT: %v", err), http.StatusUnauthorized)
return
}
if !ok {
switch r.Method {
case http.MethodDelete, http.MethodPost, http.MethodPatch, http.MethodPut:
http.Error(w, "user is not admin", http.StatusForbidden)
return
}
}
h.ServeHTTP(w, r)
})
}

View File

@ -92,8 +92,12 @@ func (s *Server) Start() error {
corsMiddleware := cors.AllowAll()
acMiddleware := middleware.NewAccessControll(
s.config.AuthAudience,
s.accountManager.IsUserAdmin)
r := mux.NewRouter()
r.Use(jwtMiddleware.Handler, corsMiddleware.Handler)
r.Use(jwtMiddleware.Handler, corsMiddleware.Handler, acMiddleware.Handler)
groupsHandler := handler.NewGroups(s.accountManager, s.config.AuthAudience)
rulesHandler := handler.NewRules(s.accountManager, s.config.AuthAudience)

View File

@ -17,6 +17,7 @@ type MockAccountManager struct {
GetAccountByIdFunc func(accountId string) (*server.Account, error)
GetAccountByUserOrAccountIdFunc func(userId, accountId, domain string) (*server.Account, error)
GetAccountWithAuthorizationClaimsFunc func(claims jwtclaims.AuthorizationClaims) (*server.Account, error)
IsUserAdminFunc func(claims jwtclaims.AuthorizationClaims) (bool, error)
AccountExistsFunc func(accountId string) (*bool, error)
AddAccountFunc func(accountId, userId, domain string) (*server.Account, error)
GetPeerFunc func(peerKey string) (*server.Peer, error)
@ -193,7 +194,11 @@ func (am *MockAccountManager) GetNetworkMap(peerKey string) (*server.NetworkMap,
return nil, status.Errorf(codes.Unimplemented, "method GetNetworkMap not implemented")
}
func (am *MockAccountManager) AddPeer(setupKey string, userId string, peer *server.Peer) (*server.Peer, error) {
func (am *MockAccountManager) AddPeer(
setupKey string,
userId string,
peer *server.Peer,
) (*server.Peer, error) {
if am.AddPeerFunc != nil {
return am.AddPeerFunc(setupKey, userId, peer)
}
@ -283,3 +288,10 @@ func (am *MockAccountManager) UpdatePeerMeta(peerKey string, meta server.PeerSys
}
return status.Errorf(codes.Unimplemented, "method UpdatePeerMetaFunc not implemented")
}
func (am *MockAccountManager) IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error) {
if am.IsUserAdminFunc != nil {
return am.IsUserAdminFunc(claims)
}
return false, status.Errorf(codes.Unimplemented, "method IsUserAdmin not implemented")
}

View File

@ -1,10 +1,13 @@
package server
import (
"fmt"
"strings"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/netbirdio/netbird/management/server/jwtclaims"
)
const (
@ -87,3 +90,18 @@ func (am *DefaultAccountManager) GetAccountByUser(userId string) (*Account, erro
return am.Store.GetUserAccount(userId)
}
// IsUserAdmin flag for current user authenticated by JWT token
func (am *DefaultAccountManager) IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error) {
account, err := am.GetAccountWithAuthorizationClaims(claims)
if err != nil {
return false, fmt.Errorf("get account: %v", err)
}
user, ok := account.Users[claims.UserId]
if !ok {
return false, fmt.Errorf("no such user")
}
return user.Role == UserRoleAdmin, nil
}