mirror of
https://github.com/netbirdio/netbird.git
synced 2025-06-19 17:31:39 +02: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)
|
GetAccountById(accountId string) (*Account, error)
|
||||||
GetAccountByUserOrAccountId(userId, accountId, domain string) (*Account, error)
|
GetAccountByUserOrAccountId(userId, accountId, domain string) (*Account, error)
|
||||||
GetAccountWithAuthorizationClaims(claims jwtclaims.AuthorizationClaims) (*Account, error)
|
GetAccountWithAuthorizationClaims(claims jwtclaims.AuthorizationClaims) (*Account, error)
|
||||||
|
IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error)
|
||||||
AccountExists(accountId string) (*bool, error)
|
AccountExists(accountId string) (*bool, error)
|
||||||
AddAccount(accountId, userId, domain string) (*Account, error)
|
AddAccount(accountId, userId, domain string) (*Account, error)
|
||||||
GetPeer(peerKey string) (*Peer, 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()
|
corsMiddleware := cors.AllowAll()
|
||||||
|
|
||||||
|
acMiddleware := middleware.NewAccessControll(
|
||||||
|
s.config.AuthAudience,
|
||||||
|
s.accountManager.IsUserAdmin)
|
||||||
|
|
||||||
r := mux.NewRouter()
|
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)
|
groupsHandler := handler.NewGroups(s.accountManager, s.config.AuthAudience)
|
||||||
rulesHandler := handler.NewRules(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)
|
GetAccountByIdFunc func(accountId string) (*server.Account, error)
|
||||||
GetAccountByUserOrAccountIdFunc func(userId, accountId, domain string) (*server.Account, error)
|
GetAccountByUserOrAccountIdFunc func(userId, accountId, domain string) (*server.Account, error)
|
||||||
GetAccountWithAuthorizationClaimsFunc func(claims jwtclaims.AuthorizationClaims) (*server.Account, error)
|
GetAccountWithAuthorizationClaimsFunc func(claims jwtclaims.AuthorizationClaims) (*server.Account, error)
|
||||||
|
IsUserAdminFunc func(claims jwtclaims.AuthorizationClaims) (bool, error)
|
||||||
AccountExistsFunc func(accountId string) (*bool, error)
|
AccountExistsFunc func(accountId string) (*bool, error)
|
||||||
AddAccountFunc func(accountId, userId, domain string) (*server.Account, error)
|
AddAccountFunc func(accountId, userId, domain string) (*server.Account, error)
|
||||||
GetPeerFunc func(peerKey string) (*server.Peer, 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")
|
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 {
|
if am.AddPeerFunc != nil {
|
||||||
return am.AddPeerFunc(setupKey, userId, peer)
|
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")
|
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
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -87,3 +90,18 @@ func (am *DefaultAccountManager) GetAccountByUser(userId string) (*Account, erro
|
|||||||
|
|
||||||
return am.Store.GetUserAccount(userId)
|
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…
x
Reference in New Issue
Block a user