mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-07 08:44:07 +01:00
feat(ac): add access control middleware (#321)
This commit is contained in:
parent
abe78666d4
commit
65069c1787
@ -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)
|
||||
|
50
management/server/http/middleware/access_control.go
Normal file
50
management/server/http/middleware/access_control.go
Normal 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)
|
||||
})
|
||||
}
|
@ -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)
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user