diff --git a/.agola/config.jsonnet b/.agola/config.jsonnet index 82ab7c6d4..04a22583c 100644 --- a/.agola/config.jsonnet +++ b/.agola/config.jsonnet @@ -37,7 +37,7 @@ local task_build_go(version, arch) = { { type: 'run', name: 'build docker/k8s drivers tests binary', command: 'CGO_ENABLED=0 go test -c ./internal/services/executor/driver -o ./bin/docker-tests' }, { type: 'run', name: 'build integration tests binary', command: 'go test -tags "sqlite_unlock_notify" -c ./tests -o ./bin/integration-tests' }, { type: 'run', name: 'run tests', command: 'SKIP_DOCKER_TESTS=1 SKIP_K8S_TESTS=1 go test -v -count 1 $(go list ./... | grep -v /tests)' }, - { type: 'run', name: 'fetch gitea binary for integration tests', command: 'curl -L https://github.com/go-gitea/gitea/releases/download/v1.8.3/gitea-1.8.3-linux-amd64 -o ./bin/gitea && chmod +x ./bin/gitea' }, + { type: 'run', name: 'fetch gitea binary for integration tests', command: 'curl -L https://github.com/go-gitea/gitea/releases/download/v1.15.11/gitea-1.15.11-linux-amd64 -o ./bin/gitea && chmod +x ./bin/gitea' }, { type: 'save_to_workspace', contents: [{ source_dir: './bin', dest_dir: '/bin/', paths: ['*'] }] }, ], }; diff --git a/internal/services/gateway/action/user.go b/internal/services/gateway/action/user.go index 3c7695af1..730585fa9 100644 --- a/internal/services/gateway/action/user.go +++ b/internal/services/gateway/action/user.go @@ -57,6 +57,18 @@ func (h *ActionHandler) GetUser(ctx context.Context, userRef string) (*cstypes.U return user, nil } +func (h *ActionHandler) GetUserOrgs(ctx context.Context, userRef string) ([]*csapitypes.UserOrgsResponse, error) { + if !h.IsUserLogged(ctx) { + return nil, errors.Errorf("user not logged in") + } + + orgs, resp, err := h.configstoreClient.GetUserOrgs(ctx, userRef) + if err != nil { + return nil, ErrFromRemote(resp, err) + } + return orgs, nil +} + type GetUsersRequest struct { Start string Limit int diff --git a/internal/services/gateway/api/user.go b/internal/services/gateway/api/user.go index db2a18853..9016b9386 100644 --- a/internal/services/gateway/api/user.go +++ b/internal/services/gateway/api/user.go @@ -23,6 +23,7 @@ import ( "agola.io/agola/internal/services/gateway/action" "agola.io/agola/internal/util" + csapitypes "agola.io/agola/services/configstore/api/types" cstypes "agola.io/agola/services/configstore/types" gwapitypes "agola.io/agola/services/gateway/api/types" @@ -590,3 +591,47 @@ func (h *UserCreateRunHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) h.log.Errorf("err: %+v", err) } } + +type UserOrgsHandler struct { + log *zap.SugaredLogger + ah *action.ActionHandler +} + +func NewUserOrgsHandler(logger *zap.Logger, ah *action.ActionHandler) *UserOrgsHandler { + return &UserOrgsHandler{log: logger.Sugar(), ah: ah} +} + +func (h *UserOrgsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + userIDVal := ctx.Value("userid") + if userIDVal == nil { + httpError(w, util.NewErrBadRequest(errors.Errorf("user not authenticated"))) + return + } + userRef := userIDVal.(string) + + userOrgs, err := h.ah.GetUserOrgs(ctx, userRef) + if httpError(w, err) { + h.log.Errorf("err: %+v", err) + return + } + + res := make([]*gwapitypes.UserOrgsResponse, len(userOrgs)) + for i, userOrg := range userOrgs { + res[i] = createUserOrgsResponse(userOrg) + } + + if err := httpResponse(w, http.StatusOK, res); err != nil { + h.log.Errorf("err: %+v", err) + } +} + +func createUserOrgsResponse(o *csapitypes.UserOrgsResponse) *gwapitypes.UserOrgsResponse { + userOrgs := &gwapitypes.UserOrgsResponse{ + Organization: createOrgResponse(o.Organization), + Role: gwapitypes.MemberRole(o.Role), + } + + return userOrgs +} diff --git a/internal/services/gateway/gateway.go b/internal/services/gateway/gateway.go index f37552c8a..b9f093b1a 100644 --- a/internal/services/gateway/gateway.go +++ b/internal/services/gateway/gateway.go @@ -187,6 +187,7 @@ func (g *Gateway) Run(ctx context.Context) error { createUserHandler := api.NewCreateUserHandler(logger, g.ah) deleteUserHandler := api.NewDeleteUserHandler(logger, g.ah) userCreateRunHandler := api.NewUserCreateRunHandler(logger, g.ah) + userOrgsHandler := api.NewUserOrgsHandler(logger, g.ah) createUserLAHandler := api.NewCreateUserLAHandler(logger, g.ah) deleteUserLAHandler := api.NewDeleteUserLAHandler(logger, g.ah) @@ -283,6 +284,7 @@ func (g *Gateway) Run(ctx context.Context) error { apirouter.Handle("/users", authForcedHandler(createUserHandler)).Methods("POST") apirouter.Handle("/users/{userref}", authForcedHandler(deleteUserHandler)).Methods("DELETE") apirouter.Handle("/user/createrun", authForcedHandler(userCreateRunHandler)).Methods("POST") + apirouter.Handle("/user/orgs", authForcedHandler(userOrgsHandler)).Methods("GET") apirouter.Handle("/users/{userref}/linkedaccounts", authForcedHandler(createUserLAHandler)).Methods("POST") apirouter.Handle("/users/{userref}/linkedaccounts/{laid}", authForcedHandler(deleteUserLAHandler)).Methods("DELETE") diff --git a/internal/testutil/utils.go b/internal/testutil/utils.go index 0faeb39c7..677913596 100644 --- a/internal/testutil/utils.go +++ b/internal/testutil/utils.go @@ -377,11 +377,6 @@ OFFLINE_MODE = false [database] PATH = {{ .Data }}/gitea/gitea.db DB_TYPE = sqlite3 -HOST = -NAME = -USER = -PASSWD = -SSL_MODE = disable [indexer] ISSUE_INDEXER_PATH = {{ .Data }}/gitea/indexers/issues.bleve diff --git a/services/gateway/api/types/user.go b/services/gateway/api/types/user.go index 06f7910e7..2b75d3fdf 100644 --- a/services/gateway/api/types/user.go +++ b/services/gateway/api/types/user.go @@ -106,3 +106,8 @@ type UserCreateRunRequest struct { PullRequestRefRegexes []string `json:"pull_request_ref_regexes,omitempty"` Variables map[string]string `json:"variables,omitempty"` } + +type UserOrgsResponse struct { + Organization *OrgResponse + Role MemberRole +} diff --git a/services/gateway/client/client.go b/services/gateway/client/client.go index 234cd850b..c9e2e884b 100644 --- a/services/gateway/client/client.go +++ b/services/gateway/client/client.go @@ -60,7 +60,6 @@ func (c *Client) doRequest(ctx context.Context, method, path string, query url.V return nil, err } u.RawQuery = query.Encode() - req, err := http.NewRequest(method, u.String(), ibody) req = req.WithContext(ctx) if err != nil { @@ -611,3 +610,9 @@ func (c *Client) GetVersion(ctx context.Context) (*gwapitypes.VersionResponse, * resp, err := c.getParsedResponse(ctx, "GET", "/version", nil, jsonContent, nil, &res) return res, resp, err } + +func (c *Client) GetUserOrgs(ctx context.Context) ([]*gwapitypes.UserOrgsResponse, *http.Response, error) { + userOrgs := []*gwapitypes.UserOrgsResponse{} + resp, err := c.getParsedResponse(ctx, "GET", "/user/orgs", nil, jsonContent, nil, &userOrgs) + return userOrgs, resp, err +} diff --git a/tests/setup_test.go b/tests/setup_test.go index 92712129a..8639e4ce9 100644 --- a/tests/setup_test.go +++ b/tests/setup_test.go @@ -40,6 +40,7 @@ import ( gwapitypes "agola.io/agola/services/gateway/api/types" gwclient "agola.io/agola/services/gateway/client" rstypes "agola.io/agola/services/runservice/types" + "github.com/google/go-cmp/cmp" "code.gitea.io/sdk/gitea" "go.uber.org/zap" @@ -104,7 +105,7 @@ func setupGitea(t *testing.T, dir, dockerBridgeAddress string) *testutil.TestGit // wait for gitea ready err = testutil.Wait(30*time.Second, func() (bool, error) { - cmd := exec.Command(tgitea.GiteaPath, "admin", "create-user", "--name", giteaUser01, "--email", giteaUser01+"@example.com", "--password", "password", "--admin", "--config", tgitea.ConfigPath) + cmd := exec.Command(tgitea.GiteaPath, "admin", "user", "create", "--name", giteaUser01, "--email", giteaUser01+"@example.com", "--password", "password", "--admin", "--config", tgitea.ConfigPath) // just retry until no error if err := cmd.Run(); err != nil { return false, nil @@ -1935,3 +1936,65 @@ def main(ctx): } } } + +func TestUserOrgs(t *testing.T) { + dir, err := ioutil.TempDir("", "agola") + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + defer os.RemoveAll(dir) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + tetcd, tgitea, c := setup(ctx, t, dir) + defer shutdownGitea(tgitea) + defer shutdownEtcd(tetcd) + + gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, "admintoken") + + org01, _, err := gwClient.CreateOrg(ctx, &gwapitypes.CreateOrgRequest{Name: "org01", Visibility: gwapitypes.VisibilityPublic}) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + org02, _, err := gwClient.CreateOrg(ctx, &gwapitypes.CreateOrgRequest{Name: "org02", Visibility: gwapitypes.VisibilityPrivate}) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + _, _, err = gwClient.CreateOrg(ctx, &gwapitypes.CreateOrgRequest{Name: "org03", Visibility: gwapitypes.VisibilityPublic}) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + + _, token := createLinkedAccount(ctx, t, tgitea, c) + + _, _, err = gwClient.AddOrgMember(ctx, "org01", giteaUser01, gwapitypes.MemberRoleMember) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + _, _, err = gwClient.AddOrgMember(ctx, "org02", giteaUser01, gwapitypes.MemberRoleOwner) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + + gwClientNew := gwclient.NewClient(c.Gateway.APIExposedURL, token) + + orgs, _, err := gwClientNew.GetUserOrgs(ctx) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + + expectedOrgs := make([]*gwapitypes.UserOrgsResponse, 2) + + expectedOrgs[0] = &gwapitypes.UserOrgsResponse{ + Organization: &gwapitypes.OrgResponse{ID: org01.ID, Name: "org01", Visibility: gwapitypes.VisibilityPublic}, + Role: gwapitypes.MemberRoleMember} + + expectedOrgs[1] = &gwapitypes.UserOrgsResponse{ + Organization: &gwapitypes.OrgResponse{ID: org02.ID, Name: "org02", Visibility: gwapitypes.VisibilityPrivate}, + Role: gwapitypes.MemberRoleOwner} + + if diff := cmp.Diff(expectedOrgs, orgs); diff != "" { + t.Fatalf("user orgs mismatch (-expected +got):\n%s", diff) + } +}