Skip to content

Commit

Permalink
feat: add check user resource api
Browse files Browse the repository at this point in the history
  • Loading branch information
mabdh committed Jan 22, 2024
1 parent 7f61c16 commit 288bdbb
Show file tree
Hide file tree
Showing 10 changed files with 4,298 additions and 1,458 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ GOVERSION := $(shell go version | cut -d ' ' -f 3 | cut -d '.' -f 2)

.PHONY: build check fmt lint test test-race vet test-cover-html help install proto
.DEFAULT_GOAL := build
PROTON_COMMIT := "a1795affeb3f5a6bd8431234e7eb69df253a72c9"
PROTON_COMMIT := "e70016e0b1cc6151683e10ce3b1a0eac89443039"

install:
@echo "Clean up imports..."
Expand Down
37 changes: 21 additions & 16 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
---
version: "v1"
version: v1
plugins:
- remote: "buf.build/library/plugins/go:v1.27.1-1"
out: "proto"
opt: "paths=source_relative"
- remote: "buf.build/library/plugins/go-grpc:v1.1.0-2"
out: "proto"
opt: "paths=source_relative"
- remote: buf.build/jirkad/plugins/protoc-gen-validate:v0.6.7
- plugin: buf.build/protocolbuffers/go:v1.32.0
out: proto
opt: paths=source_relative
- plugin: buf.build/grpc/go:v1.3.0
out: proto
opt: paths=source_relative,require_unimplemented_servers=true
- plugin: buf.build/bufbuild/validate-go:v1.0.4
out: proto
opt: paths=source_relative
- plugin: buf.build/grpc-ecosystem/gateway:v2.19.0
out: proto
opt:
- paths=source_relative
- lang=go
- remote: "buf.build/grpc-ecosystem/plugins/grpc-gateway:v2.5.0-1"
out: "proto"
opt: "paths=source_relative"
- remote: "buf.build/grpc-ecosystem/plugins/openapiv2:v2.6.0-1"
out: "proto"
opt: "allow_merge=true"
- allow_repeated_fields_in_body=true
- plugin: buf.build/grpc-ecosystem/openapiv2:v2.19.0
out: proto
opt:
- allow_repeated_fields_in_body=true
- output_format=yaml
- allow_merge=true
- merge_file_name=siren
- openapi_naming_strategy=simple
- json_names_for_fields=false
24 changes: 24 additions & 0 deletions internal/api/v1beta1/permission_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@ type resourcePermissionResult struct {
allowed bool
}

func (h Handler) CheckResourceUserPermission(ctx context.Context, req *shieldv1beta1.CheckResourceUserPermissionRequest) (*shieldv1beta1.CheckResourceUserPermissionResponse, error) {
userCtx := user.SetContextWithEmail(ctx, req.GetId()) // id is e-mail here
resp, err := h.CheckResourcePermission(userCtx, &shieldv1beta1.CheckResourcePermissionRequest{
ResourcePermissions: req.GetResourcePermissions(),
})
if err != nil {
return nil, err
}

var permissionResponses []*shieldv1beta1.CheckResourceUserPermissionResponse_ResourcePermissionResponse
for _, permissionResp := range resp.GetResourcePermissions() {
permissionResponses = append(permissionResponses, &shieldv1beta1.CheckResourceUserPermissionResponse_ResourcePermissionResponse{
ObjectId: permissionResp.GetObjectId(),
ObjectNamespace: permissionResp.GetObjectNamespace(),
Permission: permissionResp.GetPermission(),
Allowed: permissionResp.GetAllowed(),
})
}

return &shieldv1beta1.CheckResourceUserPermissionResponse{
ResourcePermissions: permissionResponses,
}, nil
}

func (h Handler) CheckResourcePermission(ctx context.Context, req *shieldv1beta1.CheckResourcePermissionRequest) (*shieldv1beta1.CheckResourcePermissionResponse, error) {
logger := grpczap.Extract(ctx)
//if err := req.ValidateAll(); err != nil {
Expand Down
279 changes: 279 additions & 0 deletions internal/api/v1beta1/permission_check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,282 @@ func TestHandler_CheckResourcePermission(t *testing.T) {
})
}
}

func TestHandler_CheckResourceUserPermission(t *testing.T) {
var userEmail = "[email protected]"
tests := []struct {
name string
setup func(res *mocks.ResourceService)
request *shieldv1beta1.CheckResourceUserPermissionRequest
want *shieldv1beta1.CheckResourceUserPermissionResponse
wantErr error
}{
{
name: "Deprecated check single resource permission: should return internal error if relation service's CheckAuthz function returns some error",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(false, errors.New("some error"))
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
},
},
},
want: nil,
wantErr: grpcInternalServerError,
},
{
name: "Deprecated check single resource permission: should return true when CheckAuthz function returns true bool",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(true, nil)
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
},
},
},
want: &shieldv1beta1.CheckResourceUserPermissionResponse{
ResourcePermissions: []*shieldv1beta1.CheckResourceUserPermissionResponse_ResourcePermissionResponse{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
Allowed: true,
},
},
},
wantErr: nil,
},
{
name: "Deprecated check single resource permission: should return false when CheckAuthz function returns false bool",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(false, nil)
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
},
},
},
want: &shieldv1beta1.CheckResourceUserPermissionResponse{
ResourcePermissions: []*shieldv1beta1.CheckResourceUserPermissionResponse_ResourcePermissionResponse{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
Allowed: false,
},
},
},
wantErr: nil,
},
{
name: "Deprecated check single resource permission: should return unauthenticated error if relation service's CheckAuthz function returns auth error",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(false, user.ErrInvalidEmail)
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
},
},
},
want: nil,
wantErr: grpcUnauthenticated,
},
{
name: "should return internal error if relation service's CheckAuthz function returns some error",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(false, errors.New("some error"))
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
},
},
},
want: nil,
wantErr: grpcInternalServerError,
},
{
name: "should return unauthenticated error if relation service's CheckAuthz function returns auth error",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(false, user.ErrInvalidEmail)
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
},
},
},
want: nil,
wantErr: grpcUnauthenticated,
},
{
name: "should return validation error if the request has empty resource permission list",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(false, errors.New("some error"))
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{},
},
want: nil,
wantErr: fmt.Errorf("%s: %s", ErrRequestBodyValidation, "resource_permissions"),
},
{
name: "should return true when CheckAuthz function returns true bool",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(true, nil)
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
},
},
},
want: &shieldv1beta1.CheckResourceUserPermissionResponse{
ResourcePermissions: []*shieldv1beta1.CheckResourceUserPermissionResponse_ResourcePermissionResponse{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
Allowed: true,
},
},
},
wantErr: nil,
},
{
name: "should return false when CheckAuthz function returns false bool",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(false, nil)
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
},
},
},
want: &shieldv1beta1.CheckResourceUserPermissionResponse{
ResourcePermissions: []*shieldv1beta1.CheckResourceUserPermissionResponse_ResourcePermissionResponse{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
Allowed: false,
},
},
},
wantErr: nil,
},
{
name: "should return internal error if resource service's CheckAuthz returns some error even with multiple resource check failures",
setup: func(res *mocks.ResourceService) {
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.EditPermission}).Return(false, errors.New("some error"))
res.EXPECT().CheckAuthz(mock.AnythingOfType("*context.valueCtx"), resource.Resource{
Name: testRelationV2.Object.ID,
NamespaceID: testRelationV2.Object.NamespaceID,
}, action.Action{ID: schema.ViewPermission}).Return(false, errors.New("some error")).Twice()
},
request: &shieldv1beta1.CheckResourceUserPermissionRequest{
Id: userEmail,
ResourcePermissions: []*shieldv1beta1.ResourcePermission{
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.EditPermission,
},
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.ViewPermission,
},
{
ObjectId: testRelationV2.Object.ID,
ObjectNamespace: testRelationV2.Object.NamespaceID,
Permission: schema.ViewPermission,
},
},
},
want: nil,
wantErr: grpcInternalServerError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockResourceSrv := new(mocks.ResourceService)
if tt.setup != nil {
tt.setup(mockResourceSrv)
}

mockDep := Handler{resourceService: mockResourceSrv, checkAPILimit: 5}
resp, err := mockDep.CheckResourceUserPermission(context.Background(), tt.request)
assert.EqualValues(t, tt.want, resp)
assert.EqualValues(t, tt.wantErr, err)
})
}
}
Loading

0 comments on commit 288bdbb

Please sign in to comment.