From ab62f66bf5b3c31d198e373fd93174597e0abb36 Mon Sep 17 00:00:00 2001 From: Richard LT Date: Mon, 24 Jan 2022 10:20:59 +0100 Subject: [PATCH] refactor(api,cdsctl): remove group and project permission import (#6068) --- cli/cdsctl/group.go | 52 ------ cli/cdsctl/project.go | 1 - cli/cdsctl/project_group.go | 85 --------- engine/api/api_routes.go | 3 - engine/api/group.go | 165 ------------------ engine/api/group_test.go | 100 ----------- engine/api/project_group.go | 116 ++---------- engine/api/project_group_test.go | 105 +++++++++++ engine/api/workflow_group_test.go | 86 ++++----- sdk/cdsclient/client_group.go | 18 -- sdk/cdsclient/client_project.go | 17 -- sdk/cdsclient/interface.go | 3 - .../mock_cdsclient/interface_mock.go | 120 ------------- sdk/project.go | 9 + tests/03_clictl_project.yml | 36 ---- tests/fixtures/group_to_add.yml | 10 -- 16 files changed, 174 insertions(+), 752 deletions(-) delete mode 100644 cli/cdsctl/project_group.go delete mode 100644 tests/fixtures/group_to_add.yml diff --git a/cli/cdsctl/group.go b/cli/cdsctl/group.go index 71ef1ed3be..1fdef2156a 100644 --- a/cli/cdsctl/group.go +++ b/cli/cdsctl/group.go @@ -8,8 +8,6 @@ import ( "github.com/ovh/cds/cli" "github.com/ovh/cds/sdk" - "github.com/ovh/cds/sdk/cdsclient" - "github.com/ovh/cds/sdk/exportentities" ) var groupCmd = cli.Command{ @@ -27,8 +25,6 @@ func group() *cobra.Command { cli.NewDeleteCommand(groupDeleteCmd, groupDeleteRun, nil, withAllCommandModifiers()...), cli.NewCommand(groupGrantCmd, groupGrantRun, nil, withAllCommandModifiers()...), cli.NewCommand(groupRevokeCmd, groupRevokeRun, nil, withAllCommandModifiers()...), - cli.NewCommand(groupExportCmd, groupExportRun, nil, withAllCommandModifiers()...), - cli.NewCommand(groupImportCmd, groupImportRun, nil, withAllCommandModifiers()...), groupMember(), }) } @@ -206,51 +202,3 @@ func groupRevokeRun(v cli.Values) error { return nil } - -var groupExportCmd = cli.Command{ - Name: "export", - Short: "Export a CDS group", - Args: []cli.Arg{ - { - Name: "group-name", - }, - }, - Flags: []cli.Flag{ - { - Name: "format", - Usage: "Specify export format (json or yaml)", - Default: "yaml", - }, - }, -} - -func groupExportRun(v cli.Values) error { - buf, err := client.GroupExport(v.GetString("group-name"), cdsclient.Format(v.GetString("format"))) - if err != nil { - return err - } - fmt.Println(string(buf)) - return nil -} - -var groupImportCmd = cli.Command{ - Name: "import", - Short: "Import a group", - Args: []cli.Arg{ - {Name: "path"}, - }, -} - -func groupImportRun(c cli.Values) error { - path := c.GetString("path") - contentFile, format, err := exportentities.OpenPath(path) - if err != nil { - return err - } - defer contentFile.Close() //nolint - if _, err := client.GroupImport(contentFile, cdsclient.ContentType(format.ContentType())); err != nil { - return err - } - fmt.Println("Group imported.") - return nil -} diff --git a/cli/cdsctl/project.go b/cli/cdsctl/project.go index 1056ea09a8..0d7894f171 100644 --- a/cli/cdsctl/project.go +++ b/cli/cdsctl/project.go @@ -27,7 +27,6 @@ func projectCommands() []*cobra.Command { cli.NewDeleteCommand(projectDeleteCmd, projectDeleteRun, nil, withAllCommandModifiers()...), cli.NewCommand(projectFavoriteCmd, projectFavoriteRun, nil, withAllCommandModifiers()...), projectKey(), - projectGroup(), projectVariable(), projectIntegration(), projectRepositoryManager(), diff --git a/cli/cdsctl/project_group.go b/cli/cdsctl/project_group.go deleted file mode 100644 index 02886c18ae..0000000000 --- a/cli/cdsctl/project_group.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "github.com/ovh/cds/cli" - "github.com/ovh/cds/sdk" - "github.com/ovh/cds/sdk/cdsclient" - "github.com/ovh/cds/sdk/exportentities" -) - -var projectGroupCmd = cli.Command{ - Name: "group", - Aliases: []string{"groups"}, - Short: "Manage CDS group linked to a project", -} - -func projectGroup() *cobra.Command { - return cli.NewCommand(projectGroupCmd, nil, []*cobra.Command{ - cli.NewCommand(projectGroupImportCmd, projectGroupImportRun, nil, withAllCommandModifiers()...), - }) -} - -var projectGroupImportCmd = cli.Command{ - Name: "import", - Short: "Import group linked to a CDS project", - Ctx: []cli.Arg{ - {Name: _ProjectKey}, - }, - Args: []cli.Arg{ - {Name: "path"}, - }, - Flags: []cli.Flag{ - { - Name: "force", - Usage: "Use force flag to replace groups in your project", - IsValid: func(s string) bool { - if s != "true" && s != "false" { - return false - } - return true - }, - Default: "false", - Type: cli.FlagBool, - }, - }, -} - -func projectGroupImportRun(v cli.Values) error { - path := v.GetString("path") - - var reader io.ReadCloser - var err error - if sdk.IsURL(path) { - reader, err = exportentities.OpenURL(path) - } else { - reader, err = exportentities.OpenFile(path) - } - if err != nil { - return err - } - defer reader.Close() // nolint - - format, err := exportentities.GetFormatFromPath(path) - if err != nil { - return err - } - - mods := []cdsclient.RequestModifier{ - cdsclient.ContentType(format.ContentType()), - } - if v.GetBool("force") { - mods = append(mods, cdsclient.Force()) - } - - if _, err := client.ProjectGroupsImport(v.GetString(_ProjectKey), reader, mods...); err != nil { - return err - } - fmt.Printf("Groups imported in project %s with success\n", v.GetString(_ProjectKey)) - - return nil -} diff --git a/engine/api/api_routes.go b/engine/api/api_routes.go index c6b78c05ce..eeaedf0e6a 100644 --- a/engine/api/api_routes.go +++ b/engine/api/api_routes.go @@ -117,9 +117,7 @@ func (api *API) InitRouter() { // Group r.Handle("/group", Scope(sdk.AuthConsumerScopeGroup), r.GET(api.getGroupsHandler), r.POST(api.postGroupHandler)) - r.Handle("/group/import", Scope(sdk.AuthConsumerScopeGroup), r.POST(api.postGroupImportHandler)) r.Handle("/group/{permGroupName}", Scope(sdk.AuthConsumerScopeGroup), r.GET(api.getGroupHandler), r.PUT(api.putGroupHandler), r.DELETE(api.deleteGroupHandler)) - r.Handle("/group/{permGroupName}/export", Scope(sdk.AuthConsumerScopeGroup), r.GET(api.getGroupExportHandler)) r.Handle("/group/{permGroupName}/user", Scope(sdk.AuthConsumerScopeGroup), r.POST(api.postGroupUserHandler)) r.Handle("/group/{permGroupName}/user/{username}", Scope(sdk.AuthConsumerScopeGroup), r.PUT(api.putGroupUserHandler), r.DELETE(api.deleteGroupUserHandler)) r.Handle("/group/{permGroupName}/project", Scope(sdk.AuthConsumerScopeGroup), r.GET(api.getProjectGroupHandler)) @@ -162,7 +160,6 @@ func (api *API) InitRouter() { r.Handle("/project/{permProjectKey}", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getProjectHandler), r.PUT(api.updateProjectHandler), r.DELETE(api.deleteProjectHandler)) r.Handle("/project/{permProjectKey}/labels", Scope(sdk.AuthConsumerScopeProject), r.PUT(api.putProjectLabelsHandler)) r.Handle("/project/{permProjectKey}/group", Scope(sdk.AuthConsumerScopeProject), r.POST(api.postGroupInProjectHandler)) - r.Handle("/project/{permProjectKey}/group/import", Scope(sdk.AuthConsumerScopeProject), r.POST(api.postImportGroupsInProjectHandler)) r.Handle("/project/{permProjectKey}/group/{groupName}", Scope(sdk.AuthConsumerScopeProject), r.PUT(api.putGroupRoleOnProjectHandler), r.DELETE(api.deleteGroupFromProjectHandler)) r.Handle("/project/{permProjectKey}/variable", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getVariablesInProjectHandler)) r.Handle("/project/{permProjectKey}/encrypt", Scope(sdk.AuthConsumerScopeProject), r.POST(api.postEncryptVariableHandler), r.DELETE(api.deleteEncryptVariableHandler)) diff --git a/engine/api/group.go b/engine/api/group.go index fd72eb88f2..7b706e19a8 100644 --- a/engine/api/group.go +++ b/engine/api/group.go @@ -2,7 +2,6 @@ package api import ( "context" - "io/ioutil" "net/http" "github.com/gorilla/mux" @@ -14,7 +13,6 @@ import ( "github.com/ovh/cds/engine/api/user" "github.com/ovh/cds/engine/service" "github.com/ovh/cds/sdk" - "github.com/ovh/cds/sdk/exportentities" ) func (api *API) getGroupsHandler() service.Handler { @@ -78,40 +76,6 @@ func (api *API) getGroupHandler() service.Handler { } } -func (api *API) getGroupExportHandler() service.Handler { - return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - vars := mux.Vars(r) - - groupName := vars["permGroupName"] - - format := FormString(r, "format") - if format == "" { - format = "yaml" - } - f, err := exportentities.GetFormat(format) - if err != nil { - return err - } - - g, err := group.LoadByName(ctx, api.mustDB(), groupName, group.LoadOptions.Default) - if err != nil { - return err - } - - b, err := exportentities.Marshal(g, f) - if err != nil { - return err - } - - if _, err := w.Write(b); err != nil { - return sdk.WithStack(err) - } - - w.Header().Add("Content-Type", f.ContentType()) - return nil - } -} - func (api *API) postGroupHandler() service.Handler { return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { var newGroup sdk.Group @@ -493,132 +457,3 @@ func (api *API) deleteGroupUserHandler() service.Handler { return service.WriteJSON(w, g, http.StatusOK) } } - -func (api *API) postGroupImportHandler() service.Handler { - return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return sdk.NewErrorWithStack(err, sdk.NewErrorFrom(sdk.ErrWrongRequest, "unable to read body")) - } - - contentType := r.Header.Get("Content-Type") - if contentType == "" { - contentType = http.DetectContentType(body) - } - format, err := exportentities.GetFormatFromContentType(contentType) - if err != nil { - return err - } - - var data sdk.Group - if err := exportentities.Unmarshal(body, format, &data); err != nil { - return err - } - if err := data.IsValid(); err != nil { - return err - } - if err := data.Members.IsValid(); err != nil { - return err - } - - tx, err := api.mustDB().Begin() - if err != nil { - return sdk.WrapError(err, "cannot start transaction") - } - defer tx.Rollback() // nolint - - oldGroup, err := group.LoadByName(ctx, tx, data.Name, - group.LoadOptions.WithMembers, - group.LoadOptions.WithOrganization, - ) - if err != nil && !sdk.ErrorIs(err, sdk.ErrNotFound) { - return sdk.WrapError(err, "cannot load group: %s", data.Name) - } - if oldGroup != nil { - if !isGroupAdmin(ctx, oldGroup) { - if isAdmin(ctx) { - trackSudo(ctx, w) - } else { - return sdk.WithStack(sdk.ErrForbidden) - } - } - } - - // Add user data and validate members - for i := range data.Members { - var u *sdk.AuthentifiedUser - if data.Members[i].ID != "" { - u, err = user.LoadByID(ctx, tx, data.Members[i].ID) - } else { - u, err = user.LoadByUsername(ctx, tx, data.Members[i].Username) - } - if err != nil { - return err - } - data.Members[i].ID = u.ID - } - - if err := group.Upsert(ctx, tx, oldGroup, &data); err != nil { - return sdk.WrapError(err, "cannot update group with id: %d", data.ID) - } - - // Check that user's Organization match group Organization - if err := group.EnsureOrganization(ctx, tx, &data); err != nil { - return err - } - - if oldGroup != nil { - // Remove the group from consumers for removed users - removedUserIDs := oldGroup.Members.DiffUserIDs(data.Members) - for i := range removedUserIDs { - u, err := user.LoadByID(ctx, tx, removedUserIDs[i]) - if err != nil { - return err - } - if err := authentication.ConsumerInvalidateGroupForUser(ctx, tx, &data, u); err != nil { - return err - } - } - - // Restore group on consumers for added users - addedUserIDs := data.Members.DiffUserIDs(oldGroup.Members) - for i := range addedUserIDs { - if err := authentication.ConsumerRestoreInvalidatedGroupForUser(ctx, tx, data.ID, addedUserIDs[i]); err != nil { - return err - } - } - } - - if err := tx.Commit(); err != nil { - return sdk.WithStack(err) - } - - // In case where the user remove himself from group, do not return it - consumer := getAPIConsumer(ctx) - for i := range data.Members { - var found bool - if data.Members[i].ID == consumer.AuthentifiedUser.ID { - found = true - break - } - if !found { - return service.WriteJSON(w, nil, http.StatusOK) - } - } - - // Load extra data for group - if err := group.LoadOptions.Default(ctx, api.mustDB(), &data); err != nil { - return err - } - - b, err := exportentities.Marshal(data, format) - if err != nil { - return err - } - if _, err := w.Write(b); err != nil { - return sdk.WithStack(err) - } - w.Header().Add("Content-Type", format.ContentType()) - return nil - } -} diff --git a/engine/api/group_test.go b/engine/api/group_test.go index 7f28fc7bc8..17cfca9ab0 100644 --- a/engine/api/group_test.go +++ b/engine/api/group_test.go @@ -331,103 +331,3 @@ func Test_deleteGroupUserHandler(t *testing.T) { require.Equal(t, http.StatusOK, rec.Code) assert.Equal(t, "null", rec.Body.String()) } - -func Test_postImportGroupHandler(t *testing.T) { - api, db, _ := newTestAPI(t) - - g1Name := sdk.RandomString(10) - u1, jwtRaw := assets.InsertLambdaUser(t, db, &sdk.Group{Name: g1Name}) - u2, _ := assets.InsertLambdaUser(t, db) - - // Get the group - uri := api.Router.GetRoute(http.MethodGet, api.getGroupHandler, map[string]string{ - "permGroupName": g1Name, - }) - require.NotEmpty(t, uri) - req := assets.NewJWTAuthentifiedRequest(t, jwtRaw, http.MethodGet, uri, nil) - rec := httptest.NewRecorder() - api.Router.Mux.ServeHTTP(rec, req) - require.Equal(t, http.StatusOK, rec.Code) - var g1 sdk.Group - require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &g1)) - require.Len(t, g1.Members, 1) - require.Equal(t, u1.ID, g1.Members[0].ID) - - // Add a new member - g1.Members = append(g1.Members, sdk.GroupMember{ID: u2.ID}) - uri = api.Router.GetRoute(http.MethodPost, api.postGroupImportHandler, nil) - require.NotEmpty(t, uri) - req = assets.NewJWTAuthentifiedRequest(t, jwtRaw, http.MethodPost, uri, g1) - req.Header.Set("Content-Type", "application/json") - rec = httptest.NewRecorder() - api.Router.Mux.ServeHTTP(rec, req) - require.Equal(t, http.StatusOK, rec.Code) - require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &g1)) - require.Len(t, g1.Members, 2) - var foundU1, foundU2 bool - for i := range g1.Members { - if g1.Members[i].ID == u1.ID { - foundU1 = true - require.True(t, g1.Members[i].Admin) - } - if g1.Members[i].ID == u2.ID { - foundU2 = true - require.False(t, g1.Members[i].Admin) - } - } - require.True(t, foundU1 && foundU2) - - // Change name - g1NewName := sdk.RandomString(10) - g1.Name = g1NewName - uri = api.Router.GetRoute(http.MethodPost, api.postGroupImportHandler, nil) - require.NotEmpty(t, uri) - req = assets.NewJWTAuthentifiedRequest(t, jwtRaw, http.MethodPost, uri, g1) - req.Header.Set("Content-Type", "application/json") - rec = httptest.NewRecorder() - api.Router.Mux.ServeHTTP(rec, req) - require.Equal(t, http.StatusOK, rec.Code) - require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &g1)) - require.Equal(t, g1NewName, g1.Name) - g1Name = g1.Name - - // Cannot change override another group - g2 := assets.InsertGroup(t, db) - g1.Name = g2.Name - uri = api.Router.GetRoute(http.MethodPost, api.postGroupImportHandler, nil) - require.NotEmpty(t, uri) - req = assets.NewJWTAuthentifiedRequest(t, jwtRaw, http.MethodPost, uri, g1) - req.Header.Set("Content-Type", "application/json") - rec = httptest.NewRecorder() - api.Router.Mux.ServeHTTP(rec, req) - require.Equal(t, http.StatusForbidden, rec.Code) - - // Cannot send members list without admin - g1.Name = g1Name - for i := range g1.Members { - g1.Members[i].Admin = false - } - uri = api.Router.GetRoute(http.MethodPost, api.postGroupImportHandler, nil) - require.NotEmpty(t, uri) - req = assets.NewJWTAuthentifiedRequest(t, jwtRaw, http.MethodPost, uri, g1) - req.Header.Set("Content-Type", "application/json") - rec = httptest.NewRecorder() - api.Router.Mux.ServeHTTP(rec, req) - require.Equal(t, http.StatusBadRequest, rec.Code) - var resultError sdk.Error - require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resultError)) - require.Equal(t, "invalid given group members, at least one admin required", resultError.From) - - // Remove a member - g1.Members = []sdk.GroupMember{{ID: u1.ID, Admin: true}} - uri = api.Router.GetRoute(http.MethodPost, api.postGroupImportHandler, nil) - require.NotEmpty(t, uri) - req = assets.NewJWTAuthentifiedRequest(t, jwtRaw, http.MethodPost, uri, g1) - req.Header.Set("Content-Type", "application/json") - rec = httptest.NewRecorder() - api.Router.Mux.ServeHTTP(rec, req) - require.Equal(t, http.StatusOK, rec.Code) - require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &g1)) - require.Len(t, g1.Members, 1) - require.Equal(t, u1.ID, g1.Members[0].ID) -} diff --git a/engine/api/project_group.go b/engine/api/project_group.go index ef8de1fbf8..340eda1e35 100644 --- a/engine/api/project_group.go +++ b/engine/api/project_group.go @@ -3,7 +3,6 @@ package api import ( "context" "database/sql" - "io" "net/http" "github.com/go-gorp/gorp" @@ -15,7 +14,6 @@ import ( "github.com/ovh/cds/engine/api/workflow" "github.com/ovh/cds/engine/service" "github.com/ovh/cds/sdk" - "github.com/ovh/cds/sdk/exportentities" ) func (api *API) deleteGroupFromProjectHandler() service.Handler { @@ -88,7 +86,7 @@ func (api *API) putGroupRoleOnProjectHandler() service.Handler { return sdk.WrapError(err, "cannot load project %s", key) } - grp, err := group.LoadByName(ctx, tx, groupName, group.LoadOptions.WithOrganization) + grp, err := group.LoadByName(ctx, tx, groupName, group.LoadOptions.WithOrganization, group.LoadOptions.WithMembers) if err != nil { return sdk.WrapError(err, "cannot find %s", groupName) } @@ -105,6 +103,13 @@ func (api *API) putGroupRoleOnProjectHandler() service.Handler { if err != nil { return err } + if !isGroupAdmin(ctx, grp) && data.Permission > oldLink.Role { + if isAdmin(ctx) { + trackSudo(ctx, w) + } else { + return sdk.WithStack(sdk.ErrInvalidGroupAdmin) + } + } newLink := *oldLink newLink.Role = data.Permission @@ -178,11 +183,19 @@ func (api *API) postGroupInProjectHandler() service.Handler { return sdk.WrapError(err, "cannot load %s", key) } - grp, err := group.LoadByName(ctx, tx, data.Group.Name, group.LoadOptions.WithOrganization) + grp, err := group.LoadByName(ctx, tx, data.Group.Name, group.LoadOptions.WithOrganization, group.LoadOptions.WithMembers) if err != nil { return sdk.WrapError(err, "cannot find %s", data.Group.Name) } + if !isGroupAdmin(ctx, grp) { + if isAdmin(ctx) { + trackSudo(ctx, w) + } else { + return sdk.WithStack(sdk.ErrInvalidGroupAdmin) + } + } + if group.IsDefaultGroupID(grp.ID) && data.Permission > sdk.PermissionRead { return sdk.NewErrorFrom(sdk.ErrDefaultGroupPermission, "only read permission is allowed to default group") } @@ -234,101 +247,6 @@ func (api *API) postGroupInProjectHandler() service.Handler { } } -func (api *API) postImportGroupsInProjectHandler() service.Handler { - return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - vars := mux.Vars(r) - key := vars[permProjectKey] - force := service.FormBool(r, "force") - - proj, err := project.Load(ctx, api.mustDB(), key) - if err != nil { - return sdk.WrapError(err, "cannot load project %s", key) - } - - body, err := io.ReadAll(r.Body) - if err != nil { - return sdk.NewErrorWithStack(err, sdk.NewErrorFrom(sdk.ErrWrongRequest, "unable to read body")) - } - - contentType := r.Header.Get("Content-Type") - if contentType == "" { - contentType = http.DetectContentType(body) - } - format, err := exportentities.GetFormatFromContentType(contentType) - if err != nil { - return err - } - - var data []sdk.GroupPermission - if err := exportentities.Unmarshal(body, format, &data); err != nil { - return err - } - for i := range data { - if err := data[i].IsValid(); err != nil { - return err - } - } - - tx, err := api.mustDB().Begin() - if err != nil { - return sdk.WrapError(err, "cannot start transaction") - } - defer tx.Rollback() // nolint - - // Check and set group on all given group permission - for i := range data { - grp, err := group.LoadByName(ctx, tx, data[i].Group.Name) - if err != nil { - return err - } - data[i].Group = *grp - } - - if force { - if err := group.DeleteLinksGroupProjectForProjectID(tx, proj.ID); err != nil { - return sdk.WrapError(err, "cannot delete all groups for project %s", proj.Name) - } - } else { - linksForProject, err := group.LoadLinksGroupProjectForProjectIDs(ctx, tx, []int64{proj.ID}) - if err != nil { - return err - } - for i := range data { - var exist bool - for j := range linksForProject { - if linksForProject[j].GroupID == data[i].Group.ID { - exist = true - break - } - } - if exist { - return sdk.WrapError(sdk.ErrGroupExists, "permission already set in project %s for group %s", proj.Name, data[i].Group.Name) - } - } - } - - for i := range data { - if err := group.InsertLinkGroupProject(ctx, tx, &group.LinkGroupProject{ - GroupID: data[i].Group.ID, - ProjectID: proj.ID, - Role: data[i].Permission, - }); err != nil { - return sdk.WrapError(err, "cannot add group %v in project %s", data[i].Group.Name, proj.Name) - } - } - - if err := tx.Commit(); err != nil { - return sdk.WithStack(err) - } - - if err := project.LoadOptions.WithGroups(ctx, api.mustDB(), proj); err != nil { - return err - } - - return service.WriteJSON(w, proj, http.StatusOK) - } -} - func projectPermissionCheckOrganizationMatch(ctx context.Context, db gorp.SqlExecutor, proj *sdk.Project, grp *sdk.Group, role int) error { if role > sdk.PermissionRead { if len(proj.ProjectGroups) == 0 { diff --git a/engine/api/project_group_test.go b/engine/api/project_group_test.go index 87b07ae4ec..752c89c3da 100644 --- a/engine/api/project_group_test.go +++ b/engine/api/project_group_test.go @@ -2,10 +2,12 @@ package api import ( "encoding/json" + "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/ovh/cds/engine/api/pipeline" "github.com/ovh/cds/engine/api/test" @@ -13,6 +15,109 @@ import ( "github.com/ovh/cds/sdk" ) +func Test_putGroupRoleOnProjectHandler(t *testing.T) { + api, db, _ := newTestAPI(t) + + proj := assets.InsertTestProject(t, db, api.Cache, sdk.RandomString(10), sdk.RandomString(10)) + g1 := proj.ProjectGroups[0].Group + g2 := assets.InsertTestGroup(t, db, sdk.RandomString(10)) + + // Create a lambda user 1 that is admin of g1 and g2 + u1, jwtLambda1 := assets.InsertLambdaUser(t, db, &g1, g2) + assets.SetUserGroupAdmin(t, db, g1.ID, u1.ID) + assets.SetUserGroupAdmin(t, db, g2.ID, u1.ID) + // Create a lambda user 2 that is member of g1 + _, jwtLambda2 := assets.InsertLambdaUser(t, db, &g1) + + // User 1 can add g2 on project because admin of it + uri := api.Router.GetRoute(http.MethodPost, api.postGroupInProjectHandler, map[string]string{ + "permProjectKey": proj.Key, + }) + require.NotEmpty(t, uri) + req := assets.NewJWTAuthentifiedRequest(t, jwtLambda1, http.MethodPost, uri, sdk.GroupPermission{Group: *g2, Permission: sdk.PermissionReadExecute}) + rec := httptest.NewRecorder() + api.Router.Mux.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Code) + + // User 2 cannot add more permission on group g2 because not admin of it + uri = api.Router.GetRoute(http.MethodPut, api.putGroupRoleOnProjectHandler, map[string]string{ + "permProjectKey": proj.Key, + "groupName": g2.Name, + }) + require.NotEmpty(t, uri) + req = assets.NewJWTAuthentifiedRequest(t, jwtLambda2, http.MethodPut, uri, sdk.GroupPermission{Group: *g2, Permission: sdk.PermissionReadWriteExecute}) + rec = httptest.NewRecorder() + api.Router.Mux.ServeHTTP(rec, req) + require.Equal(t, http.StatusForbidden, rec.Code) + + // User 2 can downgrade permission on group g2 + uri = api.Router.GetRoute(http.MethodPut, api.putGroupRoleOnProjectHandler, map[string]string{ + "permProjectKey": proj.Key, + "groupName": g2.Name, + }) + require.NotEmpty(t, uri) + req = assets.NewJWTAuthentifiedRequest(t, jwtLambda2, http.MethodPut, uri, sdk.GroupPermission{Group: *g2, Permission: sdk.PermissionRead}) + rec = httptest.NewRecorder() + api.Router.Mux.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Code) + + // User 2 can remove permission on group g2 + uri = api.Router.GetRoute(http.MethodDelete, api.deleteGroupFromProjectHandler, map[string]string{ + "permProjectKey": proj.Key, + "groupName": g2.Name, + }) + require.NotEmpty(t, uri) + req = assets.NewJWTAuthentifiedRequest(t, jwtLambda2, http.MethodDelete, uri, nil) + rec = httptest.NewRecorder() + api.Router.Mux.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Code) +} + +func Test_postGroupInProjectHandler(t *testing.T) { + api, db, _ := newTestAPI(t) + + proj := assets.InsertTestProject(t, db, api.Cache, sdk.RandomString(10), sdk.RandomString(10)) + g1 := proj.ProjectGroups[0].Group + g2 := assets.InsertTestGroup(t, db, sdk.RandomString(10)) + g3 := assets.InsertTestGroup(t, db, sdk.RandomString(10)) + g4 := assets.InsertTestGroup(t, db, sdk.RandomString(10)) + + // Create a lambda user that is admin on g1 and g2 + u, jwtLambda := assets.InsertLambdaUser(t, db, &g1, g2, g3) + assets.SetUserGroupAdmin(t, db, g1.ID, u.ID) + assets.SetUserGroupAdmin(t, db, g2.ID, u.ID) + + // User is admin of g2 + uri := api.Router.GetRoute(http.MethodPost, api.postGroupInProjectHandler, map[string]string{ + "permProjectKey": proj.Key, + }) + require.NotEmpty(t, uri) + req := assets.NewJWTAuthentifiedRequest(t, jwtLambda, http.MethodPost, uri, sdk.GroupPermission{Group: *g2, Permission: sdk.PermissionRead}) + rec := httptest.NewRecorder() + api.Router.Mux.ServeHTTP(rec, req) + require.Equal(t, http.StatusOK, rec.Code) + + // User is not admin of g3 + uri = api.Router.GetRoute(http.MethodPost, api.postGroupInProjectHandler, map[string]string{ + "permProjectKey": proj.Key, + }) + require.NotEmpty(t, uri) + req = assets.NewJWTAuthentifiedRequest(t, jwtLambda, http.MethodPost, uri, sdk.GroupPermission{Group: *g3, Permission: sdk.PermissionRead}) + rec = httptest.NewRecorder() + api.Router.Mux.ServeHTTP(rec, req) + require.Equal(t, http.StatusForbidden, rec.Code) + + // User is not member of g4 + uri = api.Router.GetRoute(http.MethodPost, api.postGroupInProjectHandler, map[string]string{ + "permProjectKey": proj.Key, + }) + require.NotEmpty(t, uri) + req = assets.NewJWTAuthentifiedRequest(t, jwtLambda, http.MethodPost, uri, sdk.GroupPermission{Group: *g4, Permission: sdk.PermissionRead}) + rec = httptest.NewRecorder() + api.Router.Mux.ServeHTTP(rec, req) + require.Equal(t, http.StatusForbidden, rec.Code) +} + // Test_ProjectPerms Useful to test permission on project func Test_ProjectPerms(t *testing.T) { api, db, router := newTestAPI(t) diff --git a/engine/api/workflow_group_test.go b/engine/api/workflow_group_test.go index 8c9c56a9dd..704a3838ee 100644 --- a/engine/api/workflow_group_test.go +++ b/engine/api/workflow_group_test.go @@ -3,6 +3,7 @@ package api import ( "context" "encoding/json" + "net/http" "net/http/httptest" "testing" @@ -347,17 +348,24 @@ func Test_UpdateProjectPermsWithWorkflow(t *testing.T) { api, db, router := newTestAPI(t) proj := assets.InsertTestProject(t, db, api.Cache, sdk.RandomString(10), sdk.RandomString(10)) - u, pass := assets.InsertLambdaUser(t, db, &proj.ProjectGroups[0].Group) + g1 := proj.ProjectGroups[0].Group + g2 := assets.InsertTestGroup(t, db, sdk.RandomString(10)) - //First pipeline + // Create a lambda user that is admin of g1 and g2 + u, jwt := assets.InsertLambdaUser(t, db, &g1, g2) + assets.SetUserGroupAdmin(t, db, g1.ID, u.ID) + assets.SetUserGroupAdmin(t, db, g2.ID, u.ID) + + // Create a pipeline pip := sdk.Pipeline{ ProjectID: proj.ID, ProjectKey: proj.Key, Name: "pip1", } - test.NoError(t, pipeline.InsertPipeline(api.mustDB(), &pip)) + require.NoError(t, pipeline.InsertPipeline(api.mustDB(), &pip)) - newWf := sdk.Workflow{ + // Post a new workflow + wf := sdk.Workflow{ Name: sdk.RandomString(10), WorkflowData: sdk.WorkflowData{ Node: sdk.Node{ @@ -371,56 +379,48 @@ func Test_UpdateProjectPermsWithWorkflow(t *testing.T) { ProjectID: proj.ID, ProjectKey: proj.Key, } - - //Prepare request - vars := map[string]string{ + uri := router.GetRoute(http.MethodPost, api.postWorkflowHandler, map[string]string{ "permProjectKey": proj.Key, - } - uri := router.GetRoute("POST", api.postWorkflowHandler, vars) - test.NotEmpty(t, uri) - req := assets.NewAuthentifiedRequest(t, u, pass, "POST", uri, &newWf) - //Do the request + }) + require.NotEmpty(t, uri) + req := assets.NewAuthentifiedRequest(t, u, jwt, http.MethodPost, uri, &wf) w := httptest.NewRecorder() router.Mux.ServeHTTP(w, req) - assert.Equal(t, 201, w.Code) - - test.NoError(t, json.Unmarshal(w.Body.Bytes(), &newWf)) - assert.NotEqual(t, 0, newWf.ID) + require.Equal(t, http.StatusCreated, w.Code) + require.NoError(t, json.Unmarshal(w.Body.Bytes(), &wf)) + require.NotEqual(t, 0, wf.ID) - newGr := assets.InsertTestGroup(t, db, sdk.RandomString(10)) - newGp := sdk.GroupPermission{ - Group: *newGr, + // Add group g2 on the project + uri = router.GetRoute(http.MethodPost, api.postGroupInProjectHandler, map[string]string{ + "permProjectKey": proj.Key, + }) + require.NotEmpty(t, uri) + req = assets.NewAuthentifiedRequest(t, u, jwt, http.MethodPost, uri, sdk.GroupPermission{ + Group: *g2, Permission: sdk.PermissionReadWriteExecute, - } - - uri = router.GetRoute("POST", api.postGroupInProjectHandler, vars) - test.NotEmpty(t, uri) - req = assets.NewAuthentifiedRequest(t, u, pass, "POST", uri, &newGp) - //Do the request + }) w = httptest.NewRecorder() router.Mux.ServeHTTP(w, req) - assert.Equal(t, 200, w.Code) + require.Equal(t, 200, w.Code) - proj2, errP := project.Load(context.TODO(), api.mustDB(), proj.Key, + // Check project and workflow permissions + projResult, err := project.Load(context.TODO(), api.mustDB(), proj.Key, project.LoadOptions.WithPipelines, project.LoadOptions.WithGroups, ) - test.NoError(t, errP) - wfLoaded, errL := workflow.Load(context.Background(), db, api.Cache, *proj2, newWf.Name, workflow.LoadOptions{}) - test.NoError(t, errL) - - assert.Equal(t, 2, len(wfLoaded.Groups)) - checked := 0 - for _, grProj := range proj2.ProjectGroups { - for _, grWf := range wfLoaded.Groups { - if grProj.Group.Name == grWf.Group.Name { - checked++ - assert.Equal(t, grProj.Permission, grWf.Permission) - break - } - } - } - assert.Equal(t, 2, checked, "Haven't checked all groups") + require.NoError(t, err) + require.Equal(t, 2, len(projResult.ProjectGroups)) + require.NotNil(t, projResult.ProjectGroups.GetByGroupID(g1.ID)) + require.Equal(t, sdk.PermissionReadWriteExecute, projResult.ProjectGroups.GetByGroupID(g1.ID).Permission) + require.NotNil(t, projResult.ProjectGroups.GetByGroupID(g2.ID)) + require.Equal(t, sdk.PermissionReadWriteExecute, projResult.ProjectGroups.GetByGroupID(g2.ID).Permission) + wfResult, err := workflow.Load(context.TODO(), db, api.Cache, *proj, wf.Name, workflow.LoadOptions{}) + require.NoError(t, err) + require.Equal(t, 2, len(wfResult.Groups)) + require.NotNil(t, wfResult.Groups.GetByGroupID(g1.ID)) + require.Equal(t, sdk.PermissionReadWriteExecute, wfResult.Groups.GetByGroupID(g1.ID).Permission) + require.NotNil(t, wfResult.Groups.GetByGroupID(g2.ID)) + require.Equal(t, sdk.PermissionReadWriteExecute, wfResult.Groups.GetByGroupID(g2.ID).Permission) } // Test_PermissionOnWorkflowInferiorOfProject Useful to test when permission on wf is superior than permission on project diff --git a/sdk/cdsclient/client_group.go b/sdk/cdsclient/client_group.go index 41635feef0..c0f4bd8c33 100644 --- a/sdk/cdsclient/client_group.go +++ b/sdk/cdsclient/client_group.go @@ -3,7 +3,6 @@ package cdsclient import ( "context" "fmt" - "io" "github.com/ovh/cds/sdk" ) @@ -31,23 +30,6 @@ func (c *client) GroupGet(name string, mods ...RequestModifier) (*sdk.Group, err return group, nil } -func (c *client) GroupExport(name string, mods ...RequestModifier) ([]byte, error) { - path := fmt.Sprintf("/group/%s/export", name) - body, _, _, err := c.Request(context.Background(), "GET", path, nil, mods...) - if err != nil { - return nil, err - } - return body, nil -} - -func (c *client) GroupImport(content io.Reader, mods ...RequestModifier) ([]byte, error) { - btes, _, _, err := c.Request(context.Background(), "POST", "/group/import", content, mods...) - if err != nil { - return nil, err - } - return btes, nil -} - func (c *client) GroupList() ([]sdk.Group, error) { groups := []sdk.Group{} if _, err := c.GetJSON(context.Background(), "/group", &groups); err != nil { diff --git a/sdk/cdsclient/client_project.go b/sdk/cdsclient/client_project.go index 422910705c..0ceb26abaf 100644 --- a/sdk/cdsclient/client_project.go +++ b/sdk/cdsclient/client_project.go @@ -3,7 +3,6 @@ package cdsclient import ( "context" "fmt" - "io" "net/url" "github.com/ovh/cds/sdk" @@ -63,22 +62,6 @@ func (c *client) ProjectList(withApplications, withWorkflows bool, filters ...Fi return p, nil } -func (c *client) ProjectGroupsImport(projectKey string, content io.Reader, mods ...RequestModifier) (sdk.Project, error) { - var proj sdk.Project - - path := fmt.Sprintf("/project/%s/group/import", projectKey) - btes, _, _, err := c.Request(context.Background(), "POST", path, content, mods...) - if err != nil { - return proj, err - } - - if err := sdk.JSONUnmarshal(btes, &proj); err != nil { - return proj, err - } - - return proj, nil -} - func (c *client) ProjectAccess(ctx context.Context, projectKey, sessionID string, itemType sdk.CDNItemType) error { url := fmt.Sprintf("/project/%s/type/%s/access", projectKey, itemType) if _, err := c.GetJSON(ctx, url, nil, SetHeader(sdk.CDSSessionID, sessionID)); err != nil { diff --git a/sdk/cdsclient/interface.go b/sdk/cdsclient/interface.go index da74643346..69ff5dcf18 100644 --- a/sdk/cdsclient/interface.go +++ b/sdk/cdsclient/interface.go @@ -178,8 +178,6 @@ type ActionClient interface { type GroupClient interface { GroupList() ([]sdk.Group, error) GroupGet(name string, mods ...RequestModifier) (*sdk.Group, error) - GroupExport(name string, mods ...RequestModifier) ([]byte, error) - GroupImport(content io.Reader, mods ...RequestModifier) ([]byte, error) GroupCreate(group *sdk.Group) error GroupRename(oldName, newName string) error GroupDelete(name string) error @@ -220,7 +218,6 @@ type ProjectClient interface { ProjectList(withApplications, withWorkflow bool, filters ...Filter) ([]sdk.Project, error) ProjectKeysClient ProjectVariablesClient - ProjectGroupsImport(projectKey string, content io.Reader, mods ...RequestModifier) (sdk.Project, error) ProjectIntegrationImport(projectKey string, content io.Reader, mods ...RequestModifier) (sdk.ProjectIntegration, error) ProjectIntegrationGet(projectKey string, integrationName string, clearPassword bool) (sdk.ProjectIntegration, error) ProjectIntegrationList(projectKey string) ([]sdk.ProjectIntegration, error) diff --git a/sdk/cdsclient/mock_cdsclient/interface_mock.go b/sdk/cdsclient/mock_cdsclient/interface_mock.go index 1974fc0d6b..78d5597f3f 100644 --- a/sdk/cdsclient/mock_cdsclient/interface_mock.go +++ b/sdk/cdsclient/mock_cdsclient/interface_mock.go @@ -2078,26 +2078,6 @@ func (mr *MockGroupClientMockRecorder) GroupDelete(name interface{}) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupDelete", reflect.TypeOf((*MockGroupClient)(nil).GroupDelete), name) } -// GroupExport mocks base method. -func (m *MockGroupClient) GroupExport(name string, mods ...cdsclient.RequestModifier) ([]byte, error) { - m.ctrl.T.Helper() - varargs := []interface{}{name} - for _, a := range mods { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "GroupExport", varargs...) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GroupExport indicates an expected call of GroupExport. -func (mr *MockGroupClientMockRecorder) GroupExport(name interface{}, mods ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{name}, mods...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupExport", reflect.TypeOf((*MockGroupClient)(nil).GroupExport), varargs...) -} - // GroupGet mocks base method. func (m *MockGroupClient) GroupGet(name string, mods ...cdsclient.RequestModifier) (*sdk.Group, error) { m.ctrl.T.Helper() @@ -2118,26 +2098,6 @@ func (mr *MockGroupClientMockRecorder) GroupGet(name interface{}, mods ...interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupGet", reflect.TypeOf((*MockGroupClient)(nil).GroupGet), varargs...) } -// GroupImport mocks base method. -func (m *MockGroupClient) GroupImport(content io.Reader, mods ...cdsclient.RequestModifier) ([]byte, error) { - m.ctrl.T.Helper() - varargs := []interface{}{content} - for _, a := range mods { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "GroupImport", varargs...) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GroupImport indicates an expected call of GroupImport. -func (mr *MockGroupClientMockRecorder) GroupImport(content interface{}, mods ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{content}, mods...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupImport", reflect.TypeOf((*MockGroupClient)(nil).GroupImport), varargs...) -} - // GroupList mocks base method. func (m *MockGroupClient) GroupList() ([]sdk.Group, error) { m.ctrl.T.Helper() @@ -2528,26 +2488,6 @@ func (mr *MockProjectClientMockRecorder) ProjectGroupDelete(projectKey, groupNam return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectGroupDelete", reflect.TypeOf((*MockProjectClient)(nil).ProjectGroupDelete), projectKey, groupName) } -// ProjectGroupsImport mocks base method. -func (m *MockProjectClient) ProjectGroupsImport(projectKey string, content io.Reader, mods ...cdsclient.RequestModifier) (sdk.Project, error) { - m.ctrl.T.Helper() - varargs := []interface{}{projectKey, content} - for _, a := range mods { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "ProjectGroupsImport", varargs...) - ret0, _ := ret[0].(sdk.Project) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ProjectGroupsImport indicates an expected call of ProjectGroupsImport. -func (mr *MockProjectClientMockRecorder) ProjectGroupsImport(projectKey, content interface{}, mods ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{projectKey, content}, mods...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectGroupsImport", reflect.TypeOf((*MockProjectClient)(nil).ProjectGroupsImport), varargs...) -} - // ProjectIntegrationDelete mocks base method. func (m *MockProjectClient) ProjectIntegrationDelete(projectKey, integrationName string) error { m.ctrl.T.Helper() @@ -6116,26 +6056,6 @@ func (mr *MockInterfaceMockRecorder) GroupDelete(name interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupDelete", reflect.TypeOf((*MockInterface)(nil).GroupDelete), name) } -// GroupExport mocks base method. -func (m *MockInterface) GroupExport(name string, mods ...cdsclient.RequestModifier) ([]byte, error) { - m.ctrl.T.Helper() - varargs := []interface{}{name} - for _, a := range mods { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "GroupExport", varargs...) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GroupExport indicates an expected call of GroupExport. -func (mr *MockInterfaceMockRecorder) GroupExport(name interface{}, mods ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{name}, mods...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupExport", reflect.TypeOf((*MockInterface)(nil).GroupExport), varargs...) -} - // GroupGet mocks base method. func (m *MockInterface) GroupGet(name string, mods ...cdsclient.RequestModifier) (*sdk.Group, error) { m.ctrl.T.Helper() @@ -6156,26 +6076,6 @@ func (mr *MockInterfaceMockRecorder) GroupGet(name interface{}, mods ...interfac return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupGet", reflect.TypeOf((*MockInterface)(nil).GroupGet), varargs...) } -// GroupImport mocks base method. -func (m *MockInterface) GroupImport(content io.Reader, mods ...cdsclient.RequestModifier) ([]byte, error) { - m.ctrl.T.Helper() - varargs := []interface{}{content} - for _, a := range mods { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "GroupImport", varargs...) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GroupImport indicates an expected call of GroupImport. -func (mr *MockInterfaceMockRecorder) GroupImport(content interface{}, mods ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{content}, mods...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupImport", reflect.TypeOf((*MockInterface)(nil).GroupImport), varargs...) -} - // GroupList mocks base method. func (m *MockInterface) GroupList() ([]sdk.Group, error) { m.ctrl.T.Helper() @@ -6810,26 +6710,6 @@ func (mr *MockInterfaceMockRecorder) ProjectGroupDelete(projectKey, groupName in return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectGroupDelete", reflect.TypeOf((*MockInterface)(nil).ProjectGroupDelete), projectKey, groupName) } -// ProjectGroupsImport mocks base method. -func (m *MockInterface) ProjectGroupsImport(projectKey string, content io.Reader, mods ...cdsclient.RequestModifier) (sdk.Project, error) { - m.ctrl.T.Helper() - varargs := []interface{}{projectKey, content} - for _, a := range mods { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "ProjectGroupsImport", varargs...) - ret0, _ := ret[0].(sdk.Project) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ProjectGroupsImport indicates an expected call of ProjectGroupsImport. -func (mr *MockInterfaceMockRecorder) ProjectGroupsImport(projectKey, content interface{}, mods ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{projectKey, content}, mods...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectGroupsImport", reflect.TypeOf((*MockInterface)(nil).ProjectGroupsImport), varargs...) -} - // ProjectIntegrationDelete mocks base method. func (m *MockInterface) ProjectIntegrationDelete(projectKey, integrationName string) error { m.ctrl.T.Helper() diff --git a/sdk/project.go b/sdk/project.go index 9ea6a358ed..3e3cd0fff0 100644 --- a/sdk/project.go +++ b/sdk/project.go @@ -71,6 +71,15 @@ func (g GroupPermissions) ComputeOrganization() (string, error) { return org, nil } +func (g GroupPermissions) GetByGroupID(groupID int64) *GroupPermission { + for i := range g { + if g[i].Group.ID == groupID { + return &g[i] + } + } + return nil +} + type Permissions struct { Readable bool `json:"readable"` Writable bool `json:"writable"` diff --git a/tests/03_clictl_project.yml b/tests/03_clictl_project.yml index f8017aeeaf..af63f615dc 100644 --- a/tests/03_clictl_project.yml +++ b/tests/03_clictl_project.yml @@ -1,10 +1,5 @@ name: Project Command TestSuite with CDS ctl testcases: -- name: assert filepath, your current directory must be at the root of this project - steps: - - script: '[ -f ./fixtures/group_to_add.yml ]' - assertions: - - result.code ShouldEqual 0 - name: prepare test steps: - script: {{.cdsctl}} -f {{.cdsctl.config}} group remove ITCLIAPPPRJ --force @@ -26,37 +21,6 @@ testcases: - result.code ShouldEqual 0 - result.systemout ShouldContainSubstring ITCLIAPPPRJ -- name: project group import without force - steps: - - script: {{.cdsctl}} -f {{.cdsctl.config}} project group import ITCLIAPPPRJ ./fixtures/group_to_add.yml - assertions: - - result.code ShouldEqual 0 - - result.systemout ShouldContainSubstring success - - result.systemout ShouldContainSubstring ITCLIAPPPRJ - -- name: project group import without force and existing groups - steps: - - script: {{.cdsctl}} -f {{.cdsctl.config}} project group import ITCLIAPPPRJ ./fixtures/group_to_add.yml - assertions: - - result.code ShouldEqual 50 - - result.systemout ShouldContainSubstring 'group already exists' - -- name: project group import without force and unknown project - steps: - - script: {{.cdsctl}} -f {{.cdsctl.config}} project group import ITCLIFAPPPRJ ./fixtures/group_to_add.yml - assertions: - - result.code ShouldEqual 50 - - result.systemout ShouldContainSubstring project - - result.systemout ShouldContainSubstring 'not exist' - -- name: project group import with force - steps: - - script: {{.cdsctl}} -f {{.cdsctl.config}} project group import ITCLIAPPPRJ ./fixtures/group_to_add.yml --force - assertions: - - result.code ShouldEqual 0 - - result.systemout ShouldContainSubstring success - - result.systemout ShouldContainSubstring ITCLIAPPPRJ - - name: project favorite add steps: - script: {{.cdsctl}} -f {{.cdsctl.config}} project favorite ITCLIAPPPRJ diff --git a/tests/fixtures/group_to_add.yml b/tests/fixtures/group_to_add.yml deleted file mode 100644 index 211070f023..0000000000 --- a/tests/fixtures/group_to_add.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- group: - name: ITCLIAPP_GRP_EDIT_TEST - permission: 7 -- group: - name: ITCLIAPP_GRP_EDIT_TEST1 - permission: 7 -- group: - name: ITCLIAPP_GRP_EDIT_TEST2 - permission: 5