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

allow admin to read private repos #203

Merged
merged 1 commit into from
Dec 10, 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
10 changes: 9 additions & 1 deletion component/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,15 @@ func (c *repoComponentImpl) GetUserRepoPermission(ctx context.Context, userName
func (c *repoComponentImpl) CheckCurrentUserPermission(ctx context.Context, userName string, namespace string, role membership.Role) (bool, error) {
ns, err := c.namespaceStore.FindByPath(ctx, namespace)
if err != nil {
return false, err
return false, fmt.Errorf("fail to find namespace '%s', err:%w", namespace, err)
}

u, err := c.userStore.FindByUsername(ctx, userName)
if err != nil {
return false, fmt.Errorf("fail to find user '%s', err:%w", userName, err)
}
if u.CanAdmin() {
return true, nil
}

if ns.NamespaceType == "user" {
Expand Down
213 changes: 213 additions & 0 deletions component/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
"testing"

"github.com/alibabacloud-go/tea/tea"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"opencsg.com/csghub-server/builder/git/gitserver"
"opencsg.com/csghub-server/builder/git/membership"
"opencsg.com/csghub-server/builder/store/database"
"opencsg.com/csghub-server/common/types"
)
Expand Down Expand Up @@ -254,3 +256,214 @@ func TestRepoComponent_DeleteRepo(t *testing.T) {

// })
// }

// func TestRepoComponent_Tree(t *testing.T) {
// {
// t.Run("can read self-owned", func(t *testing.T) {
// ctx := context.TODO()
// repoComp := initializeTestRepoComponent(ctx, t)

// user := database.User{}
// user.Username = "user_name"
// repoComp.mocks.stores.UserMock().EXPECT().FindByUsername(mock.Anything, user.Username).Return(user, nil)

// ns := database.Namespace{}
// ns.NamespaceType = "user"
// ns.Path = "user_name"
// repoComp.mocks.stores.NamespaceMock().EXPECT().FindByPath(mock.Anything, ns.Path).Return(ns, nil)

// repo := &database.Repository{
// Private: true,
// User: user,
// Path: fmt.Sprintf("%s/%s", ns.Path, "repo_name"),
// Source: types.LocalSource,
// }
// repoComp.mocks.stores.RepoMock().EXPECT().FindByPath(mock.Anything, types.ModelRepo, ns.Path, repo.Name).Return(repo, nil)

// tree := []*types.File{}
// repoComp.mocks.gitServer.EXPECT().GetRepoFileTree(mock.Anything, mock.Anything).Return(tree, nil)

// actualTree, err := repoComp.Tree(context.Background(), &types.GetFileReq{
// Namespace: ns.Path,
// Name: repo.Name,
// Path: "",
// RepoType: types.ModelRepo,
// CurrentUser: user.Username,
// })
// require.Nil(t, err)
// require.Equal(t, tree, actualTree)

// })

// t.Run("forbidden anoymous user to read private repo", func(t *testing.T) {
// ctx := context.TODO()
// repoComp := initializeTestRepoComponent(ctx, t)

// repoComp.mocks.stores.RepoMock().EXPECT().FindByPath(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&database.Repository{
// // private repo don't allow read from other user
// Private: true,
// }, nil)

// actualTree, err := repoComp.Tree(context.Background(), &types.GetFileReq{})
// require.Nil(t, actualTree)
// require.Equal(t, err, ErrForbidden)

// })
// }

// }
func TestRepoComponent_checkCurrentUserPermission(t *testing.T) {

t.Run("can read self-owned", func(t *testing.T) {
repoComp := initializeTestRepoComponent(context.TODO(), t)
ns := database.Namespace{}
ns.NamespaceType = "user"
ns.Path = "user_name"
repoComp.mocks.stores.NamespaceMock().EXPECT().FindByPath(mock.Anything, ns.Path).Return(ns, nil)

user := database.User{}
user.Username = "user_name"
repoComp.mocks.stores.UserMock().EXPECT().FindByUsername(mock.Anything, user.Username).Return(user, nil)

yes, err := repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleRead)
require.True(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleWrite)
require.True(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleAdmin)
require.True(t, yes)
require.NoError(t, err)
})

t.Run("can not read other's", func(t *testing.T) {
repoComp := initializeTestRepoComponent(context.TODO(), t)
ns := database.Namespace{}
ns.NamespaceType = "user"
ns.Path = "user_name_other"
repoComp.mocks.stores.NamespaceMock().EXPECT().FindByPath(mock.Anything, ns.Path).Return(ns, nil)

user := database.User{}
user.Username = "user_name"
repoComp.mocks.stores.UserMock().EXPECT().FindByUsername(mock.Anything, user.Username).Return(user, nil)

yes, err := repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleRead)
require.False(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleWrite)
require.False(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleAdmin)
require.False(t, yes)
require.NoError(t, err)
})

t.Run("can not read org's if not org member", func(t *testing.T) {
repoComp := initializeTestRepoComponent(context.TODO(), t)
ns := database.Namespace{}
ns.NamespaceType = "organization"
ns.Path = "org_name"
repoComp.mocks.stores.NamespaceMock().EXPECT().FindByPath(mock.Anything, ns.Path).Return(ns, nil)

user := database.User{}
user.Username = "user_name"
repoComp.mocks.stores.UserMock().EXPECT().FindByUsername(mock.Anything, user.Username).Return(user, nil)

//user not belongs to org
repoComp.mocks.userSvcClient.EXPECT().GetMemberRole(mock.Anything, ns.Path, user.Username).Return(membership.RoleUnknown, nil)

yes, err := repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleRead)
require.False(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleWrite)
require.False(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleAdmin)
require.False(t, yes)
require.NoError(t, err)
})

t.Run("can read org's as org member", func(t *testing.T) {
repoComp := initializeTestRepoComponent(context.TODO(), t)
ns := database.Namespace{}
ns.NamespaceType = "organization"
ns.Path = "org_name"
repoComp.mocks.stores.NamespaceMock().EXPECT().FindByPath(mock.Anything, ns.Path).Return(ns, nil)

user := database.User{}
user.Username = "user_name"
repoComp.mocks.stores.UserMock().EXPECT().FindByUsername(mock.Anything, user.Username).Return(user, nil)

//user is read-only member of the org
repoComp.mocks.userSvcClient.EXPECT().GetMemberRole(mock.Anything, ns.Path, user.Username).Return(membership.RoleRead, nil)

//can read
yes, err := repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleRead)
require.True(t, yes)
require.NoError(t, err)
//can't write
yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleWrite)
require.False(t, yes)
require.NoError(t, err)
//can't admin
yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleAdmin)
require.False(t, yes)
require.NoError(t, err)
})

t.Run("admin read org's", func(t *testing.T) {
repoComp := initializeTestRepoComponent(context.TODO(), t)
ns := database.Namespace{}
ns.NamespaceType = "organization"
ns.Path = "org_name"
repoComp.mocks.stores.NamespaceMock().EXPECT().FindByPath(mock.Anything, ns.Path).Return(ns, nil)

user := database.User{}
user.Username = "user_name_admin"
user.RoleMask = "admin"
repoComp.mocks.stores.UserMock().EXPECT().FindByUsername(mock.Anything, user.Username).Return(user, nil)

yes, err := repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleRead)
require.True(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleWrite)
require.True(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleAdmin)
require.True(t, yes)
require.NoError(t, err)
})

t.Run("admin read other's", func(t *testing.T) {
repoComp := initializeTestRepoComponent(context.TODO(), t)
ns := database.Namespace{}
ns.NamespaceType = "user"
ns.Path = "user_name"
repoComp.mocks.stores.NamespaceMock().EXPECT().FindByPath(mock.Anything, ns.Path).Return(ns, nil)

user := database.User{}
user.Username = "user_name_admin"
user.RoleMask = "admin"
repoComp.mocks.stores.UserMock().EXPECT().FindByUsername(mock.Anything, user.Username).Return(user, nil)

yes, err := repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleRead)
require.True(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleWrite)
require.True(t, yes)
require.NoError(t, err)

yes, err = repoComp.CheckCurrentUserPermission(context.Background(), user.Username, ns.Path, membership.RoleAdmin)
require.True(t, yes)
require.NoError(t, err)
})
}
Loading