diff --git a/sda/cmd/api/api.go b/sda/cmd/api/api.go index fc5e4b9d9..22ea74753 100644 --- a/sda/cmd/api/api.go +++ b/sda/cmd/api/api.go @@ -79,6 +79,7 @@ func setup(config *config.Config) *http.Server { r.POST("/file/accession", isAdmin(), setAccession) // assign accession ID to a file r.POST("/dataset/create", isAdmin(), createDataset) // maps a set of files to a dataset r.POST("/dataset/release/*dataset", isAdmin(), releaseDataset) // Releases a dataset to be accessible + r.GET("/users", isAdmin(), listActiveUsers) // Lists all users cfg := &tls.Config{MinVersion: tls.VersionTLS12} @@ -358,3 +359,14 @@ func releaseDataset(c *gin.Context) { c.Status(http.StatusOK) } + +func listActiveUsers(c *gin.Context) { + users, err := Conf.API.DB.ListActiveUsers() + if err != nil { + log.Debugln("ListActiveUsers failed") + c.AbortWithStatusJSON(http.StatusInternalServerError, err.Error()) + + return + } + c.JSON(http.StatusOK, users) +} diff --git a/sda/cmd/api/api.md b/sda/cmd/api/api.md index 5014ffb94..5816d74e0 100644 --- a/sda/cmd/api/api.md +++ b/sda/cmd/api/api.md @@ -88,3 +88,18 @@ Admin endpoints are only available to a set of whitelisted users specified in th ```bash curl -H "Authorization: Bearer $token" -X POST https://HOSTNAME/dataset/release/my-dataset-01 ``` + +- `/users` + - accepts `GET` requests` + - Returns all users with active uploads as a JSON array + + Example: + + ```bash + curl -H "Authorization: Bearer $token" -X GET https://HOSTNAME/users + ``` + +- Error codes + - `200` Query execute ok. + - `401` User is not in the list of admins. + - `500` Internal error due to DB failure. diff --git a/sda/cmd/api/api_test.go b/sda/cmd/api/api_test.go index 0b6676286..e52cfe0dd 100644 --- a/sda/cmd/api/api_test.go +++ b/sda/cmd/api/api_test.go @@ -1080,3 +1080,53 @@ func (suite *TestSuite) TestReleaseDataset_NoDataset() { defer okResponse.Body.Close() assert.Equal(suite.T(), http.StatusBadRequest, okResponse.StatusCode) } + +func (suite *TestSuite) TestListActiveUsers() { + testUsers := []string{"User-A", "User-B", "User-C"} + for _, user := range testUsers { + for i := 0; i < 3; i++ { + fileID, err := Conf.API.DB.RegisterFile(fmt.Sprintf("/%v/TestGetUserFiles-00%d.c4gh", user, i), user) + if err != nil { + suite.FailNow("failed to register file in database") + } + + err = Conf.API.DB.UpdateFileEventLog(fileID, "uploaded", fileID, user, "{}", "{}") + if err != nil { + suite.FailNow("failed to update satus of file in database") + } + + stableID := fmt.Sprintf("accession_%s_0%d", user, i) + err = Conf.API.DB.SetAccessionID(stableID, fileID) + if err != nil { + suite.FailNowf("got (%s) when setting stable ID: %s, %s", err.Error(), stableID, fileID) + } + } + } + + err = Conf.API.DB.MapFilesToDataset("test-dataset-01", []string{"accession_User-A_00", "accession_User-A_01", "accession_User-A_02"}) + if err != nil { + suite.FailNow("failed to map files to dataset") + } + + gin.SetMode(gin.ReleaseMode) + assert.NoError(suite.T(), setupJwtAuth()) + Conf.API.Admins = []string{"dummy"} + + // Mock request and response holders + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/users", http.NoBody) + r.Header.Add("Authorization", "Bearer "+suite.Token) + + _, router := gin.CreateTestContext(w) + router.GET("/users", isAdmin(), listActiveUsers) + + router.ServeHTTP(w, r) + okResponse := w.Result() + defer okResponse.Body.Close() + assert.Equal(suite.T(), http.StatusOK, okResponse.StatusCode) + + var users []string + err = json.NewDecoder(okResponse.Body).Decode(&users) + assert.NoError(suite.T(), err, "failed to list users from DB") + assert.Equal(suite.T(), []string{"User-B", "User-C"}, users) +}