satellite/console: create project salt endpoint on satellite web server
Introduces a new endpoint on the satellite web server to get the project's salt. The endpoint utilizes a new console service method GetSalt which in turn calls the project DB GetSalt method if the user is authorized. It returns the project salt bytes as a base64 encoded string in the response. Change-Id: Ia13b5a4b8580e7bdad0dbb98014a276b1c74b46d
This commit is contained in:
parent
fa4af92392
commit
d8fb082f89
69
satellite/console/consoleweb/consoleapi/projects.go
Normal file
69
satellite/console/consoleweb/consoleapi/projects.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (C) 2021 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package consoleapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/zeebo/errs"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"storj.io/common/uuid"
|
||||||
|
"storj.io/storj/satellite/console"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Projects is an api controller that exposes projects related functionality.
|
||||||
|
type Projects struct {
|
||||||
|
log *zap.Logger
|
||||||
|
service *console.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProjects is a constructor for api analytics controller.
|
||||||
|
func NewProjects(log *zap.Logger, service *console.Service) *Projects {
|
||||||
|
return &Projects{
|
||||||
|
log: log,
|
||||||
|
service: service,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSalt returns the project's salt.
|
||||||
|
func (p *Projects) GetSalt(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
var err error
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
idParam, ok := mux.Vars(r)["id"]
|
||||||
|
if !ok {
|
||||||
|
p.serveJSONError(w, http.StatusBadRequest, errs.New("missing id route param"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := uuid.FromString(idParam)
|
||||||
|
if err != nil {
|
||||||
|
p.serveJSONError(w, http.StatusBadRequest, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
salt, err := p.service.GetSalt(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
p.serveJSONError(w, http.StatusUnauthorized, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b64SaltString := base64.StdEncoding.EncodeToString(salt)
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(b64SaltString)
|
||||||
|
if err != nil {
|
||||||
|
p.serveJSONError(w, http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveJSONError writes JSON error to response output stream.
|
||||||
|
func (p *Projects) serveJSONError(w http.ResponseWriter, status int, err error) {
|
||||||
|
ServeJSONError(p.log, w, status, err)
|
||||||
|
}
|
@ -4,7 +4,9 @@
|
|||||||
package consoleweb_test
|
package consoleweb_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -17,6 +19,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"storj.io/common/testcontext"
|
"storj.io/common/testcontext"
|
||||||
|
"storj.io/common/uuid"
|
||||||
"storj.io/storj/private/testplanet"
|
"storj.io/storj/private/testplanet"
|
||||||
"storj.io/storj/satellite/payments/storjscan/blockchaintest"
|
"storj.io/storj/satellite/payments/storjscan/blockchaintest"
|
||||||
)
|
)
|
||||||
@ -362,6 +365,23 @@ func TestProjects(t *testing.T) {
|
|||||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // Get_Salt
|
||||||
|
projectID := test.defaultProjectID()
|
||||||
|
id, err := uuid.FromString(projectID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// get salt from endpoint
|
||||||
|
var b64Salt string
|
||||||
|
resp, body := test.request(http.MethodGet, fmt.Sprintf("/projects/%s/salt", test.defaultProjectID()), nil)
|
||||||
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
require.NoError(t, json.Unmarshal([]byte(body), &b64Salt))
|
||||||
|
|
||||||
|
// get salt from db and base64 encode it
|
||||||
|
salt, err := planet.Satellites[0].DB.Console().Projects().GetSalt(ctx, id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, b64Salt, base64.StdEncoding.EncodeToString(salt))
|
||||||
|
}
|
||||||
|
|
||||||
{ // Get_ProjectInfo
|
{ // Get_ProjectInfo
|
||||||
resp, body := test.request(http.MethodPost, "/graphql",
|
resp, body := test.request(http.MethodPost, "/graphql",
|
||||||
test.toJSON(map[string]interface{}{
|
test.toJSON(map[string]interface{}{
|
||||||
|
@ -248,6 +248,12 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, oidc
|
|||||||
consoleapi.NewUserManagement(logger, mon, server.service, router, &apiAuth{&server})
|
consoleapi.NewUserManagement(logger, mon, server.service, router, &apiAuth{&server})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
projectsController := consoleapi.NewProjects(logger, service)
|
||||||
|
router.Handle(
|
||||||
|
"/api/v0/projects/{id}/salt",
|
||||||
|
server.withAuth(http.HandlerFunc(projectsController.GetSalt)),
|
||||||
|
).Methods(http.MethodGet)
|
||||||
|
|
||||||
router.HandleFunc("/registrationToken/", server.createRegistrationTokenHandler)
|
router.HandleFunc("/registrationToken/", server.createRegistrationTokenHandler)
|
||||||
router.HandleFunc("/robots.txt", server.seoHandler)
|
router.HandleFunc("/robots.txt", server.seoHandler)
|
||||||
|
|
||||||
|
@ -1406,6 +1406,21 @@ func (s *Service) GetProject(ctx context.Context, projectID uuid.UUID) (p *Proje
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSalt is a method for querying project salt by id.
|
||||||
|
func (s *Service) GetSalt(ctx context.Context, projectID uuid.UUID) (salt []byte, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
user, err := s.getUserAndAuditLog(ctx, "get project salt", zap.String("projectID", projectID.String()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, Error.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = s.isProjectMember(ctx, user.ID, projectID); err != nil {
|
||||||
|
return nil, Error.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.store.Projects().GetSalt(ctx, projectID)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUsersProjects is a method for querying all projects.
|
// GetUsersProjects is a method for querying all projects.
|
||||||
func (s *Service) GetUsersProjects(ctx context.Context) (ps []Project, err error) {
|
func (s *Service) GetUsersProjects(ctx context.Context) (ps []Project, err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
@ -71,6 +71,18 @@ func TestService(t *testing.T) {
|
|||||||
require.Nil(t, project)
|
require.Nil(t, project)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("GetSalt", func(t *testing.T) {
|
||||||
|
// Getting project salt as a member should work
|
||||||
|
salt, err := service.GetSalt(userCtx1, up1Pro1.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, salt)
|
||||||
|
|
||||||
|
// Getting project salt as a non-member should not work
|
||||||
|
salt, err = service.GetSalt(userCtx1, up2Pro1.ID)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Nil(t, salt)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("UpdateProject", func(t *testing.T) {
|
t.Run("UpdateProject", func(t *testing.T) {
|
||||||
updatedName := "newName"
|
updatedName := "newName"
|
||||||
updatedDescription := "newDescription"
|
updatedDescription := "newDescription"
|
||||||
|
Loading…
Reference in New Issue
Block a user