Skip to content

Commit

Permalink
add getting of a role
Browse files Browse the repository at this point in the history
Signed-off-by: Mike Mason <[email protected]>
  • Loading branch information
mikemrm committed Jul 24, 2023
1 parent b1835ab commit 511dda9
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 2 deletions.
31 changes: 30 additions & 1 deletion internal/api/roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,39 @@ func (r *Router) roleCreate(c echo.Context) error {
return c.JSON(http.StatusCreated, resp)
}

func (r *Router) roleGet(c echo.Context) error {
roleIDStr := c.Param("role_id")

ctx, span := tracer.Start(c.Request().Context(), "api.roleGet", trace.WithAttributes(attribute.String("id", roleIDStr)))
defer span.End()

roleResourceID, err := gidx.Parse(roleIDStr)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "error getting resource").SetInternal(err)
}

roleResource, err := r.engine.NewResourceFromID(roleResourceID)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "error getting resource").SetInternal(err)
}

role, err := r.engine.GetRole(ctx, roleResource, "")
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "error getting resource").SetInternal(err)
}

resp := roleResponse{
ID: role.ID,
Actions: role.Actions,
}

return c.JSON(http.StatusOK, resp)
}

func (r *Router) rolesList(c echo.Context) error {
resourceIDStr := c.Param("id")

ctx, span := tracer.Start(c.Request().Context(), "api.roleGet", trace.WithAttributes(attribute.String("id", resourceIDStr)))
ctx, span := tracer.Start(c.Request().Context(), "api.rolesList", trace.WithAttributes(attribute.String("id", resourceIDStr)))
defer span.End()

resourceID, err := gidx.Parse(resourceIDStr)
Expand Down
1 change: 1 addition & 0 deletions internal/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func (r *Router) Routes(rg *echo.Group) {
v1.GET("/resources/:id/relationships", r.relationshipListFrom)
v1.GET("/relationships/from/:id", r.relationshipListFrom)
v1.GET("/relationships/to/:id", r.relationshipListTo)
v1.GET("/roles/:role_id", r.roleGet)
v1.DELETE("/roles/:id", r.roleDelete)
v1.POST("/roles/:role_id/assignments", r.assignmentCreate)
v1.DELETE("/roles/:role_id/assignments", r.assignmentDelete)
Expand Down
5 changes: 5 additions & 0 deletions internal/query/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func (e *Engine) CreateRole(ctx context.Context, res types.Resource, actions []s
return role, "", nil
}

// GetRole returns nothing but satisfies the Engine interface.
func (e *Engine) GetRole(ctx context.Context, roleResource types.Resource, queryToken string) (types.Role, error) {
return types.Role{}, nil
}

// ListAssignments returns nothing but satisfies the Engine interface.
func (e *Engine) ListAssignments(ctx context.Context, role types.Role, queryToken string) ([]types.Resource, error) {
return nil, nil
Expand Down
38 changes: 38 additions & 0 deletions internal/query/relations.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,44 @@ func (e *engine) listRoleResourceActions(ctx context.Context, role types.Resourc
return resourceActions, nil
}

// GetRole gets the role with it's actions.
func (e *engine) GetRole(ctx context.Context, roleResource types.Resource, queryToken string) (types.Role, error) {
var (
resActions map[types.Resource][]string
err error
)

for _, resType := range e.schemaRoleables {
resActions, err = e.listRoleResourceActions(ctx, roleResource, resType.Name, queryToken)
if err != nil {
return types.Role{}, err
}

// roles are only ever created for a single resource, so we can break after the first one is found.
if len(resActions) != 0 {
break
}
}

if len(resActions) > 1 {
e.logger.Warnw("role is assigned to more than one resource", "role.id", roleResource.ID.String())
}

// returns the first resources actions.
for _, actions := range resActions {
for i, action := range actions {
actions[i] = relationToAction(action)
}

return types.Role{
ID: roleResource.ID,
Actions: actions,
}, nil
}

return types.Role{}, ErrRoleNotFound
}

// DeleteRole removes all role actions from the assigned resource.
func (e *engine) DeleteRole(ctx context.Context, roleResource types.Resource, queryToken string) (string, error) {
var (
Expand Down
55 changes: 54 additions & 1 deletion internal/query/relations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func cleanDB(ctx context.Context, t *testing.T, client *authzed.Client, namespac
}
}

func TestRoles(t *testing.T) {
func TestCreateRoles(t *testing.T) {
namespace := "testroles"
ctx := context.Background()
e := testEngine(ctx, t, namespace)
Expand Down Expand Up @@ -141,6 +141,59 @@ func TestRoles(t *testing.T) {
testingx.RunTests(ctx, t, testCases, testFn)
}

func TestGetRoles(t *testing.T) {
namespace := "testroles"
ctx := context.Background()
e := testEngine(ctx, t, namespace)
tenID, err := gidx.NewID("tnntten")
require.NoError(t, err)
tenRes, err := e.NewResourceFromID(tenID)
require.NoError(t, err)

role, queryToken, err := e.CreateRole(ctx, tenRes, []string{"loadbalancer_get"})
require.NoError(t, err)
roleRes, err := e.NewResourceFromID(role.ID)
require.NoError(t, err)

missingRes, err := e.NewResourceFromID(gidx.PrefixedID("permrol-notfound"))
require.NoError(t, err)

testCases := []testingx.TestCase[types.Resource, types.Role]{
{
Name: "GetRoleNotFound",
Input: missingRes,
CheckFn: func(ctx context.Context, t *testing.T, res testingx.TestResult[types.Role]) {
assert.ErrorIs(t, res.Err, ErrRoleNotFound)
},
},
{
Name: "GetSuccess",
Input: roleRes,
CheckFn: func(ctx context.Context, t *testing.T, res testingx.TestResult[types.Role]) {
expActions := []string{
"loadbalancer_get",
}

assert.NoError(t, res.Err)
require.NotEmpty(t, res.Success.ID)

assert.Equal(t, expActions, res.Success.Actions)
},
},
}

testFn := func(ctx context.Context, roleResource types.Resource) testingx.TestResult[types.Role] {
roles, err := e.GetRole(ctx, roleResource, queryToken)

return testingx.TestResult[types.Role]{
Success: roles,
Err: err,
}
}

testingx.RunTests(ctx, t, testCases, testFn)
}

func TestRoleDelete(t *testing.T) {
namespace := "testroles"
ctx := context.Background()
Expand Down
1 change: 1 addition & 0 deletions internal/query/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type Engine interface {
UnassignSubjectRole(ctx context.Context, subject types.Resource, role types.Role) (string, error)
CreateRelationships(ctx context.Context, rels []types.Relationship) (string, error)
CreateRole(ctx context.Context, res types.Resource, actions []string) (types.Role, string, error)
GetRole(ctx context.Context, roleResource types.Resource, queryToken string) (types.Role, error)
ListAssignments(ctx context.Context, role types.Role, queryToken string) ([]types.Resource, error)
ListRelationshipsFrom(ctx context.Context, resource types.Resource, queryToken string) ([]types.Relationship, error)
ListRelationshipsTo(ctx context.Context, resource types.Resource, queryToken string) ([]types.Relationship, error)
Expand Down

0 comments on commit 511dda9

Please sign in to comment.