Skip to content

Commit

Permalink
Add v1 API logic to RBAC plugin (#488)
Browse files Browse the repository at this point in the history
* add initial test file

Signed-off-by: Maia Iyer <[email protected]>

* Added rbac v1 checks

Signed-off-by: Maia Iyer <[email protected]>

* fixed agents ban in rbac v1 list

Signed-off-by: Maia Iyer <[email protected]>

* nit fix

Signed-off-by: Maia Iyer <[email protected]>

* nit fix

Signed-off-by: Maia Iyer <[email protected]>

---------

Signed-off-by: Maia Iyer <[email protected]>
  • Loading branch information
maia-iyer committed Sep 17, 2024
1 parent b2b997f commit e8845c4
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 9 deletions.
72 changes: 63 additions & 9 deletions pkg/agent/authorization/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import (
"net/http"
"github.com/pkg/errors"
"fmt"
"strings"
"github.com/spiffe/tornjak/pkg/agent/authentication/user"
)

type RBACAuthorizer struct {
name string
roleList map[string]string
apiMapping map[string][]string
apiV1Mapping map[string]map[string][]string
}

// TODO put this in a common constants file
Expand Down Expand Up @@ -39,7 +41,7 @@ var staticAPIV1List = map[string]map[string]struct{}{
"/api/v1/spire/healthcheck" :{"GET": {}},
"/api/v1/spire/entries" :{"GET": {}, "POST": {}, "DELETE": {}},
"/api/v1/spire/agents" :{"GET": {}, "POST": {}, "DELETE": {}},
"/api/v1/spire/agent/ban" :{"POST": {}},
"/api/v1/spire/agents/ban" :{"POST": {}},
"/api/v1/spire/agents/jointoken" :{"POST": {}},
"/api/v1/tornjak/clusters" :{"GET": {}, "POST": {}, "PATCH": {}, "DELETE": {}},
"/api/v1/tornjak/selectors" :{"GET": {}, "POST": {}},
Expand All @@ -50,6 +52,9 @@ var staticAPIV1List = map[string]map[string]struct{}{
}

func validateInitParameters(roleList map[string]string, apiMapping map[string][]string, apiV1Mapping map[string]map[string][]string) error {
if roleList == nil {
return errors.Errorf("No roles defined")
}
for api, allowList := range apiMapping {
// check that API exists
if _, ok := staticAPIList[api]; !ok {
Expand All @@ -67,13 +72,13 @@ func validateInitParameters(roleList map[string]string, apiMapping map[string][]
for method, allowList := range method_dict {
// check that API exists
if _, ok := staticAPIV1List[path][method]; !ok {
return errors.Errorf("API path %s does not exist with method %s", path, method)
return errors.Errorf("API V1 path %s does not exist with method %s", path, method)
}

// check that each role exists in roleList
for _, allowedRole := range allowList {
if _, ok := roleList[allowedRole]; !ok {
return errors.Errorf("API %s lists undefined role %s", path, allowedRole)
return errors.Errorf("API V1 %s lists undefined role %s", path, allowedRole)
}
}
}
Expand All @@ -91,19 +96,42 @@ func NewRBACAuthorizer(policyName string, roleList map[string]string, apiMapping
name: policyName,
roleList: roleList,
apiMapping: apiMapping,
apiV1Mapping: apiV1Mapping,
}, nil
}

func (a *RBACAuthorizer) AuthorizeRequest(r *http.Request, u *user.UserInfo) error {
// if not authenticated fail and return error
if u.AuthenticationError != nil {
return errors.Errorf("Authentication error: %v", u.AuthenticationError)
func (a *RBACAuthorizer) authorizeAPIRequest(r *http.Request, u *user.UserInfo) error {
userRoles := u.Roles
apiPath := r.URL.Path

allowedRoles := a.apiMapping[apiPath]

// if no role listed for api, reject
if len(allowedRoles) == 0 {
return errors.New("Unauthorized request")
}

// check each allowed role
for _, allowedRole := range allowedRoles {
if allowedRole == "" { // all authenticated allowed
return nil
}
for _, role := range userRoles {
// user has role
if role == allowedRole {
return nil
}
}
}
return errors.New("Unauthorized Request")
}

func (a *RBACAuthorizer) authorizeAPIV1Request(r *http.Request, u *user.UserInfo) error {
userRoles := u.Roles
apiPath := r.URL.Path
apiMethod := r.Method

allowedRoles := a.apiMapping[apiPath]
allowedRoles := a.apiV1Mapping[apiPath][apiMethod]

// if no role listed for api, reject
if len(allowedRoles) == 0 {
Expand All @@ -122,6 +150,32 @@ func (a *RBACAuthorizer) AuthorizeRequest(r *http.Request, u *user.UserInfo) err
}
}
}
return errors.New("Unauthorized Request")
}

return errors.New("Unauthorized request")
func (a *RBACAuthorizer) AuthorizeRequest(r *http.Request, u *user.UserInfo) error {
// if not authenticated fail and return error
if u.AuthenticationError != nil {
return errors.Errorf("Authentication error: %v", u.AuthenticationError)
}

// based on path
apiPath := r.URL.Path
isV1 := strings.HasPrefix(apiPath, "/api/v1")

if !isV1 {
// check old API Request
err := a.authorizeAPIRequest(r, u)
if err != nil {
return errors.Errorf("Tornjak API Authorization error: %v", err)
}
} else {
// check API V1 Request
err := a.authorizeAPIV1Request(r, u)
if err != nil {
return errors.Errorf("Tornjak API V1 Authorization error: %v", err)
}
}

return nil
}
15 changes: 15 additions & 0 deletions pkg/agent/authorization/rbac_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package authorization

import (
"testing"
//"github.com/spiffe/tornjak/pkg/agent/authentication/user"
)

func TestNewRBACAuthorizer(t *testing.T) {
// INIT failures
// fail when no roles defined
_, err := NewRBACAuthorizer("", nil, nil, nil)
if err == nil {
t.Fatal("ERROR: successfully initialized RBAC without roles")
}
}

0 comments on commit e8845c4

Please sign in to comment.