diff --git a/satellite/console/consoleweb/consoleapi/apikeys.go b/satellite/console/consoleweb/consoleapi/apikeys.go index a0db5006c..e1e8a49c0 100644 --- a/satellite/console/consoleweb/consoleapi/apikeys.go +++ b/satellite/console/consoleweb/consoleapi/apikeys.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "net/http" + "strconv" "github.com/zeebo/errs" "go.uber.org/zap" @@ -35,7 +36,102 @@ func NewAPIKeys(log *zap.Logger, service *console.Service) *APIKeys { } } -// GetAllAPIKeyNames returns all api key names by project ID. +// GetProjectAPIKeys returns paged API keys by project ID. +func (keys *APIKeys) GetProjectAPIKeys(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + var err error + defer mon.Task()(&ctx)(&err) + + query := r.URL.Query() + + projectIDParam := query.Get("projectID") + if projectIDParam == "" { + keys.serveJSONError(ctx, w, http.StatusBadRequest, errs.New("parameter 'projectID' can't be empty")) + return + } + + projectID, err := uuid.FromString(projectIDParam) + if err != nil { + keys.serveJSONError(ctx, w, http.StatusBadRequest, err) + return + } + + limitParam := query.Get("limit") + if limitParam == "" { + keys.serveJSONError(ctx, w, http.StatusBadRequest, errs.New("parameter 'limit' can't be empty")) + return + } + + limit, err := strconv.ParseUint(limitParam, 10, 32) + if err != nil { + keys.serveJSONError(ctx, w, http.StatusBadRequest, err) + return + } + + pageParam := query.Get("page") + if pageParam == "" { + keys.serveJSONError(ctx, w, http.StatusBadRequest, errs.New("parameter 'page' can't be empty")) + return + } + + page, err := strconv.ParseUint(pageParam, 10, 32) + if err != nil { + keys.serveJSONError(ctx, w, http.StatusBadRequest, err) + return + } + + orderParam := query.Get("order") + if orderParam == "" { + keys.serveJSONError(ctx, w, http.StatusBadRequest, errs.New("parameter 'order' can't be empty")) + return + } + + order, err := strconv.ParseUint(orderParam, 10, 32) + if err != nil { + keys.serveJSONError(ctx, w, http.StatusBadRequest, err) + return + } + + orderDirectionParam := query.Get("orderDirection") + if orderDirectionParam == "" { + keys.serveJSONError(ctx, w, http.StatusBadRequest, errs.New("parameter 'orderDirection' can't be empty")) + return + } + + orderDirection, err := strconv.ParseUint(orderDirectionParam, 10, 32) + if err != nil { + keys.serveJSONError(ctx, w, http.StatusBadRequest, err) + return + } + + searchString := query.Get("search") + + cursor := console.APIKeyCursor{ + Search: searchString, + Limit: uint(limit), + Page: uint(page), + Order: console.APIKeyOrder(order), + OrderDirection: console.OrderDirection(orderDirection), + } + + apiKeys, err := keys.service.GetAPIKeys(ctx, projectID, cursor) + if err != nil { + if console.ErrUnauthorized.Has(err) { + keys.serveJSONError(ctx, w, http.StatusUnauthorized, err) + return + } + + keys.serveJSONError(ctx, w, http.StatusInternalServerError, err) + return + } + + err = json.NewEncoder(w).Encode(apiKeys) + if err != nil { + keys.log.Error("failed to write json all api keys response", zap.Error(ErrAPIKeysAPI.Wrap(err))) + } +} + +// GetAllAPIKeyNames returns all API key names by project ID. func (keys *APIKeys) GetAllAPIKeyNames(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var err error @@ -70,7 +166,7 @@ func (keys *APIKeys) GetAllAPIKeyNames(w http.ResponseWriter, r *http.Request) { } } -// DeleteByNameAndProjectID deletes specific api key by it's name and project ID. +// DeleteByNameAndProjectID deletes specific API key by it's name and project ID. // ID here may be project.publicID or project.ID. func (keys *APIKeys) DeleteByNameAndProjectID(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/satellite/console/consoleweb/endpoints_test.go b/satellite/console/consoleweb/endpoints_test.go index 46b335065..041ceeb5f 100644 --- a/satellite/console/consoleweb/endpoints_test.go +++ b/satellite/console/consoleweb/endpoints_test.go @@ -20,6 +20,7 @@ import ( "storj.io/common/testcontext" "storj.io/common/uuid" "storj.io/storj/private/testplanet" + "storj.io/storj/satellite/console" "storj.io/storj/satellite/payments/storjscan/blockchaintest" ) @@ -438,6 +439,15 @@ func TestAPIKeys(t *testing.T) { require.Contains(t, body, "apiKeysPage") require.Equal(t, http.StatusOK, resp.StatusCode) } + + { // Get_ProjectAPIKeys + var projects console.APIKeyPage + path := "/api-keys/list-paged?projectID=" + test.defaultProjectID() + "&search=''&limit=6&page=1&order=1&orderDirection=1" + resp, body := test.request(http.MethodGet, path, nil) + require.Equal(t, http.StatusOK, resp.StatusCode) + require.NoError(t, json.Unmarshal([]byte(body), &projects)) + require.Contains(t, body, "apiKeys") + } }) } diff --git a/satellite/console/consoleweb/server.go b/satellite/console/consoleweb/server.go index 100dc566d..f434ba831 100644 --- a/satellite/console/consoleweb/server.go +++ b/satellite/console/consoleweb/server.go @@ -355,6 +355,7 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, oidc apiKeysRouter := router.PathPrefix("/api/v0/api-keys").Subrouter() apiKeysRouter.Use(server.withCORS) apiKeysRouter.Use(server.withAuth) + apiKeysRouter.HandleFunc("/list-paged", apiKeysController.GetProjectAPIKeys).Methods(http.MethodGet, http.MethodOptions) apiKeysRouter.HandleFunc("/delete-by-name", apiKeysController.DeleteByNameAndProjectID).Methods(http.MethodDelete, http.MethodOptions) apiKeysRouter.HandleFunc("/api-key-names", apiKeysController.GetAllAPIKeyNames).Methods(http.MethodGet, http.MethodOptions)