Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RBAC V2 - Roles APIs #245

Merged
merged 3 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ ci: | golint test coverage ## Setup dev database and run tests.
.PHONY: test
test: ## Runs unit tests.
@echo Running unit tests...
@go test -v -timeout 30s -cover -short -tags testtools ./...
@go test -v -timeout 120s -cover -short -tags testtools ./...

.PHONY: coverage
coverage: ## Generates a test coverage report.
@echo Generating coverage report...
@go test -timeout 30s -tags testtools ./... -coverprofile=coverage.out -covermode=atomic
@go test -timeout 120s -tags testtools ./... -coverprofile=coverage.out -covermode=atomic
@go tool cover -func=coverage.out
@go tool cover -html=coverage.out

Expand Down
6 changes: 2 additions & 4 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import (
"go.infratographer.com/permissions-api/internal/storage"
)

var (
apiDefaultListen = "0.0.0.0:7602"
)
var apiDefaultListen = "0.0.0.0:7602"

var serverCmd = &cobra.Command{
Use: "server",
Expand Down Expand Up @@ -89,7 +87,7 @@ func serve(_ context.Context, cfg *config.AppConfig) {
logger.Fatalw("invalid spicedb policy", "error", err)
}

engine, err := query.NewEngine("infratographer", spiceClient, kv, store, query.WithPolicy(policy))
engine, err := query.NewEngine("infratographer", spiceClient, kv, store, query.WithPolicy(policy), query.WithLogger(logger))
if err != nil {
logger.Fatalw("error creating engine", "error", err)
}
Expand Down
7 changes: 4 additions & 3 deletions internal/api/assignments.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"go.infratographer.com/x/gidx"

"go.infratographer.com/permissions-api/internal/iapl"
"go.infratographer.com/permissions-api/internal/query"
"go.infratographer.com/permissions-api/internal/types"

Expand Down Expand Up @@ -62,7 +63,7 @@ func (r *Router) assignmentCreate(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, "error getting role").SetInternal(err)
}

if err := r.checkActionWithResponse(ctx, subjectResource, actionRoleUpdate, resource); err != nil {
if err := r.checkActionWithResponse(ctx, subjectResource, string(iapl.RoleActionUpdate), resource); err != nil {
return err
}

Expand Down Expand Up @@ -112,7 +113,7 @@ func (r *Router) assignmentsList(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, "error getting role").SetInternal(err)
}

if err := r.checkActionWithResponse(ctx, subjectResource, actionRoleGet, resource); err != nil {
if err := r.checkActionWithResponse(ctx, subjectResource, string(iapl.RoleActionGet), resource); err != nil {
return err
}

Expand Down Expand Up @@ -190,7 +191,7 @@ func (r *Router) assignmentDelete(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, "error getting role").SetInternal(err)
}

if err := r.checkActionWithResponse(ctx, subjectResource, actionRoleUpdate, resource); err != nil {
if err := r.checkActionWithResponse(ctx, subjectResource, string(iapl.RoleActionUpdate), resource); err != nil {
return err
}

Expand Down
20 changes: 10 additions & 10 deletions internal/api/assignments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestAssignmentCreate(t *testing.T) {
"subject_id": "bad-id",
},
},
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand All @@ -67,7 +67,7 @@ func TestAssignmentCreate(t *testing.T) {
"subject_id": "idntusr-abc123",
},
},
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand All @@ -92,7 +92,7 @@ func TestAssignmentCreate(t *testing.T) {
"subject_id": "idntusr-def456",
},
},
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand All @@ -119,7 +119,7 @@ func TestAssignmentCreate(t *testing.T) {
"subject_id": "idntusr-def456",
},
},
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand Down Expand Up @@ -211,7 +211,7 @@ func TestAssignmentsList(t *testing.T) {
{
Name: "RoleResourceNotFound",
Input: "/api/v1/roles/permrol-abc123/assignments",
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand All @@ -233,7 +233,7 @@ func TestAssignmentsList(t *testing.T) {
{
Name: "AssignmentsRetrieved",
Input: "/api/v1/roles/permrol-abc123/assignments",
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand Down Expand Up @@ -327,7 +327,7 @@ func TestAssignmentDelete(t *testing.T) {
"subject_id": "bad-id",
},
},
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand All @@ -352,7 +352,7 @@ func TestAssignmentDelete(t *testing.T) {
"subject_id": "idntusr-abc123",
},
},
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand All @@ -377,7 +377,7 @@ func TestAssignmentDelete(t *testing.T) {
"subject_id": "idntusr-def456",
},
},
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand All @@ -404,7 +404,7 @@ func TestAssignmentDelete(t *testing.T) {
"subject_id": "idntusr-def456",
},
},
SetupFn: func(ctx context.Context, t *testing.T) context.Context {
SetupFn: func(ctx context.Context, _ *testing.T) context.Context {
engine := mock.Engine{
Namespace: "test",
}
Expand Down
10 changes: 10 additions & 0 deletions internal/api/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package api

import "errors"

var (
// ErrInvalidID is returned when the ID is invalid
ErrInvalidID = errors.New("invalid ID")
// ErrParsingRequestBody is returned when failing to parse the request body
ErrParsingRequestBody = errors.New("error parsing request body")
)
39 changes: 39 additions & 0 deletions internal/api/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ package api

import (
"errors"
"fmt"
"net/http"

"github.com/labstack/echo/v4"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"go.infratographer.com/permissions-api/internal/query"
"go.infratographer.com/permissions-api/internal/storage"
)

// ErrorResponse represents the data that the server will return on any given call
Expand All @@ -20,3 +29,33 @@ var (
// ErrResourceAlreadyExists is returned when the resource already exists
ErrResourceAlreadyExists = errors.New("resource already exists")
)

func (r *Router) errorResponse(basemsg string, err error) *echo.HTTPError {
msg := fmt.Sprintf("%s: %s", basemsg, err.Error())
httpstatus := http.StatusInternalServerError

switch {
case
errors.Is(err, query.ErrInvalidType),
errors.Is(err, query.ErrInvalidArgument),
errors.Is(err, query.ErrInvalidAction),
errors.Is(err, query.ErrInvalidNamespace),
errors.Is(err, ErrInvalidID),
status.Code(err) == codes.InvalidArgument,
status.Code(err) == codes.FailedPrecondition:
jnschaeffer marked this conversation as resolved.
Show resolved Hide resolved
httpstatus = http.StatusBadRequest
case
errors.Is(err, storage.ErrNoRoleFound),
errors.Is(err, query.ErrRoleNotFound),
errors.Is(err, query.ErrRoleBindingNotFound):
httpstatus = http.StatusNotFound
case
errors.Is(err, storage.ErrRoleAlreadyExists),
errors.Is(err, storage.ErrRoleNameTaken):
httpstatus = http.StatusConflict
default:
msg = basemsg
}

return echo.NewHTTPError(httpstatus, msg).SetInternal(err)
}
32 changes: 16 additions & 16 deletions internal/api/roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,19 @@ package api
import (
"errors"
"net/http"
"strings"
"time"

"github.com/labstack/echo/v4"
"go.infratographer.com/x/gidx"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"

"go.infratographer.com/permissions-api/internal/iapl"
"go.infratographer.com/permissions-api/internal/query"
"go.infratographer.com/permissions-api/internal/storage"
)

const (
actionRoleCreate = "role_create"
actionRoleGet = "role_get"
actionRoleList = "role_list"
actionRoleUpdate = "role_update"
actionRoleDelete = "role_delete"
)

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

Expand Down Expand Up @@ -50,11 +44,14 @@ func (r *Router) roleCreate(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "error creating resource").SetInternal(err)
}

if err := r.checkActionWithResponse(ctx, subjectResource, actionRoleCreate, resource); err != nil {
if err := r.checkActionWithResponse(ctx, subjectResource, string(iapl.RoleActionCreate), resource); err != nil {
return err
}

role, err := r.engine.CreateRole(ctx, subjectResource, resource, reqBody.Name, reqBody.Actions)
role, err := r.engine.CreateRole(
ctx, subjectResource, resource,
strings.TrimSpace(reqBody.Name), reqBody.Actions,
)

switch {
case err == nil:
Expand Down Expand Up @@ -120,11 +117,14 @@ func (r *Router) roleUpdate(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, "error getting resource").SetInternal(err)
}

if err := r.checkActionWithResponse(ctx, subjectResource, actionRoleUpdate, resource); err != nil {
if err := r.checkActionWithResponse(ctx, subjectResource, string(iapl.RoleActionUpdate), resource); err != nil {
return err
}

role, err := r.engine.UpdateRole(ctx, subjectResource, roleResource, reqBody.Name, reqBody.Actions)
role, err := r.engine.UpdateRole(
ctx, subjectResource, roleResource,
strings.TrimSpace(reqBody.Name), reqBody.Actions,
)

switch {
case err == nil:
Expand Down Expand Up @@ -185,7 +185,7 @@ func (r *Router) roleGet(c echo.Context) error {

// TODO: This shows an error for the role's resource, not the role. Determine if that
// matters.
if err := r.checkActionWithResponse(ctx, subjectResource, actionRoleGet, resource); err != nil {
if err := r.checkActionWithResponse(ctx, subjectResource, string(iapl.RoleActionGet), resource); err != nil {
return err
}

Expand Down Expand Up @@ -234,7 +234,7 @@ func (r *Router) rolesList(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "error creating resource").SetInternal(err)
}

if err := r.checkActionWithResponse(ctx, subjectResource, actionRoleList, resource); err != nil {
if err := r.checkActionWithResponse(ctx, subjectResource, string(iapl.RoleActionList), resource); err != nil {
return err
}

Expand Down Expand Up @@ -297,7 +297,7 @@ func (r *Router) roleDelete(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, "error getting resource").SetInternal(err)
}

if err := r.checkActionWithResponse(ctx, subjectResource, actionRoleDelete, resource); err != nil {
if err := r.checkActionWithResponse(ctx, subjectResource, string(iapl.RoleActionDelete), resource); err != nil {
return err
}

Expand Down Expand Up @@ -351,7 +351,7 @@ func (r *Router) roleGetResource(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, "error getting role").SetInternal(err)
}

if err := r.checkActionWithResponse(ctx, subjectResource, actionRoleGet, resource); err != nil {
if err := r.checkActionWithResponse(ctx, subjectResource, string(iapl.RoleActionGet), resource); err != nil {
return err
}

Expand Down
Loading
Loading