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

add check to only switch to workspaces you are a member of #141

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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"message":"You can only join workspaces you are a member of"}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"workspace":{"id":"00000000-0000-0000-0000-000000000000","plan_id":"00000000-0000-0000-0000-000000000000","metadata":{"team":{},"deck":{},"updates":{},"integrations":{},"dashboard":{},"data_room":{}},"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z"},"message":"user's default workspace updated"}
{"workspace":{"id":"8ce0f580-4d6d-429e-9d0e-a78eb99f62c2","plan_id":"00000000-0000-0000-0000-000000000000","metadata":{"team":{},"deck":{},"updates":{},"integrations":{},"dashboard":{},"data_room":{}},"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z"},"message":"user's default workspace updated"}
5 changes: 5 additions & 0 deletions server/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ func (wo *workspaceHandler) switchCurrentWorkspaceForUser(
"could not fetch the workspace by reference to switch to"), StatusFailed
}

if !user.CanAccessWorkspace(workspace.ID) {
return newAPIStatus(http.StatusForbidden,
"You can only join workspaces you are a member of"), StatusFailed
}

user.Metadata.CurrentWorkspace = workspace.ID

if err := wo.userRepo.Update(ctx, user); err != nil {
Expand Down
124 changes: 77 additions & 47 deletions server/workspace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/ayinke-llc/malak"
malak_mocks "github.com/ayinke-llc/malak/mocks"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"go.uber.org/zap"
Expand All @@ -27,6 +28,9 @@ func getLogger(t *testing.T) *zap.Logger {
}

func TestWorkspaceHandler_SwitchWorkspace(t *testing.T) {

workspaceID := uuid.MustParse("8ce0f580-4d6d-429e-9d0e-a78eb99f62c2")

for _, v := range generateWorkspaceSwitchTable() {

t.Run(v.name, func(t *testing.T) {
Expand Down Expand Up @@ -55,13 +59,25 @@ func TestWorkspaceHandler_SwitchWorkspace(t *testing.T) {

req = req.WithContext(writeUserToCtx(req.Context(), &malak.User{
Metadata: &malak.UserMetadata{},
Roles: malak.UserRoles{
{
WorkspaceID: workspaceID,
Role: malak.RoleAdmin,
},
},
}))

req = req.WithContext(writeWorkspaceToCtx(req.Context(), &malak.Workspace{
ID: workspaceID,
}))

ctx := chi.NewRouteContext()
ctx.URLParams.Add("provider", "reference")
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, ctx))

WrapMalakHTTPHandler(getLogger(t), a.switchCurrentWorkspaceForUser, getConfig(), "workspaces.switch").
WrapMalakHTTPHandler(getLogger(t),
a.switchCurrentWorkspaceForUser, getConfig(),
"workspaces.switch").
ServeHTTP(rr, req)

require.Equal(t, v.expectedStatusCode, rr.Code)
Expand All @@ -70,6 +86,66 @@ func TestWorkspaceHandler_SwitchWorkspace(t *testing.T) {
}
}

func generateWorkspaceSwitchTable() []struct {
name string
mockFn func(workspaceRepo *malak_mocks.MockWorkspaceRepository, userRepo *malak_mocks.MockUserRepository)
expectedStatusCode int
} {

return []struct {
name string
mockFn func(workspaceRepo *malak_mocks.MockWorkspaceRepository, userRepo *malak_mocks.MockUserRepository)
expectedStatusCode int
}{
{
name: "could not find reference",
mockFn: func(workspaceRepo *malak_mocks.MockWorkspaceRepository, usr *malak_mocks.MockUserRepository) {
workspaceRepo.EXPECT().Get(gomock.Any(), gomock.Any()).
Times(1).
Return(nil, errors.New("could not find workspace"))
},
expectedStatusCode: http.StatusInternalServerError,
},
{
name: "no access to workspace",
mockFn: func(workspaceRepo *malak_mocks.MockWorkspaceRepository, userRepo *malak_mocks.MockUserRepository) {
workspaceRepo.EXPECT().Get(gomock.Any(), gomock.Any()).
Times(1).Return(&malak.Workspace{
ID: uuid.New(),
}, nil)
},
expectedStatusCode: http.StatusForbidden,
},
{
name: "could not update user repo",
mockFn: func(workspaceRepo *malak_mocks.MockWorkspaceRepository, userRepo *malak_mocks.MockUserRepository) {
workspaceRepo.EXPECT().Get(gomock.Any(), gomock.Any()).
Times(1).Return(&malak.Workspace{
ID: uuid.MustParse("8ce0f580-4d6d-429e-9d0e-a78eb99f62c2"),
}, nil)

userRepo.EXPECT().Update(gomock.Any(), gomock.Any()).
Times(1).Return(errors.New("could not update"))
},
expectedStatusCode: http.StatusInternalServerError,
},
{
name: "updated current workspace",
mockFn: func(workspaceRepo *malak_mocks.MockWorkspaceRepository, userRepo *malak_mocks.MockUserRepository) {
workspaceRepo.EXPECT().Get(gomock.Any(), gomock.Any()).
Times(1).Return(&malak.Workspace{
ID: uuid.MustParse("8ce0f580-4d6d-429e-9d0e-a78eb99f62c2"),
}, nil)

userRepo.EXPECT().Update(gomock.Any(), gomock.Any()).
Times(1).
Return(nil)
},
expectedStatusCode: http.StatusOK,
},
}
}

func TestWorkspaceHandler_Create(t *testing.T) {
for _, v := range generateWorkspaceTestTable() {

Expand Down Expand Up @@ -602,49 +678,3 @@ func generateWorkspaceTestTable() []struct {
},
}
}

func generateWorkspaceSwitchTable() []struct {
name string
mockFn func(workspaceRepo *malak_mocks.MockWorkspaceRepository, userRepo *malak_mocks.MockUserRepository)
expectedStatusCode int
} {

return []struct {
name string
mockFn func(workspaceRepo *malak_mocks.MockWorkspaceRepository, userRepo *malak_mocks.MockUserRepository)
expectedStatusCode int
}{
{
name: "could not find reference",
mockFn: func(workspaceRepo *malak_mocks.MockWorkspaceRepository, usr *malak_mocks.MockUserRepository) {
workspaceRepo.EXPECT().Get(gomock.Any(), gomock.Any()).
Times(1).
Return(nil, errors.New("could not find workspace"))
},
expectedStatusCode: http.StatusInternalServerError,
},
{
name: "could not update user repo",
mockFn: func(workspaceRepo *malak_mocks.MockWorkspaceRepository, userRepo *malak_mocks.MockUserRepository) {
workspaceRepo.EXPECT().Get(gomock.Any(), gomock.Any()).
Times(1).Return(&malak.Workspace{}, nil)

userRepo.EXPECT().Update(gomock.Any(), gomock.Any()).
Times(1).Return(errors.New("could not update"))
},
expectedStatusCode: http.StatusInternalServerError,
},
{
name: "updated current workspace",
mockFn: func(workspaceRepo *malak_mocks.MockWorkspaceRepository, userRepo *malak_mocks.MockUserRepository) {
workspaceRepo.EXPECT().Get(gomock.Any(), gomock.Any()).
Times(1).Return(&malak.Workspace{}, nil)

userRepo.EXPECT().Update(gomock.Any(), gomock.Any()).
Times(1).
Return(nil)
},
expectedStatusCode: http.StatusOK,
},
}
}
10 changes: 10 additions & 0 deletions user.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ type User struct {

func (u *User) HasWorkspace() bool { return u.Metadata.CurrentWorkspace != uuid.Nil }

func (u *User) CanAccessWorkspace(id uuid.UUID) bool {
for _, role := range u.Roles {
if role.WorkspaceID.String() == id.String() {
return true
}
}

return false
}

type FindUserOptions struct {
Email Email `json:"email,omitempty"`
ID uuid.UUID
Expand Down
42 changes: 39 additions & 3 deletions user_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
package malak

import "testing"
import (
"testing"

func TestUser_HasWorkspace(t *testing.T) {
// t.Skip()
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)

func TestUser_AccessWorkspace(t *testing.T) {

workspaceID := uuid.New()

t.Run("has access", func(t *testing.T) {
user := &User{
Roles: UserRoles{
{
WorkspaceID: workspaceID,
},
{
WorkspaceID: uuid.New(),
},
},
}

require.True(t, user.CanAccessWorkspace(workspaceID))
})

t.Run("no access", func(t *testing.T) {
user := &User{
Roles: UserRoles{
{
WorkspaceID: workspaceID,
},
{
WorkspaceID: uuid.New(),
},
},
}

require.False(t, user.CanAccessWorkspace(uuid.New()))
})
}
Loading