satellite/{consoleweb,consoleapi}: add cross user api tests

This change adds tests to ensure critical endpoints are not able to be
called by users for other users. It asserts that if cases like that
do happen, a 401 response will be sent.

Issue: https://github.com/storj/storj-private/issues/407

Change-Id: I70097a80f691a7d0fcb0bc5dbce8291144177720
This commit is contained in:
Wilfred Asomani 2023-08-21 22:50:02 +00:00
parent 5c155752d2
commit fe9f69a757
4 changed files with 88 additions and 12 deletions

View File

@ -67,7 +67,7 @@ func (keys *APIKeys) CreateAPIKey(w http.ResponseWriter, r *http.Request) {
info, key, err := keys.service.CreateAPIKey(ctx, projectID, name)
if err != nil {
if console.ErrUnauthorized.Has(err) {
if console.ErrUnauthorized.Has(err) || console.ErrNoMembership.Has(err) {
keys.serveJSONError(ctx, w, http.StatusUnauthorized, err)
return
}

View File

@ -262,6 +262,10 @@ func (p *Projects) GetMembersAndInvitations(w http.ResponseWriter, r *http.Reque
project, err := p.service.GetProject(ctx, publicID)
if err != nil {
if console.ErrUnauthorized.Has(err) || console.ErrNoMembership.Has(err) {
p.serveJSONError(ctx, w, http.StatusUnauthorized, err)
return
}
p.serveJSONError(ctx, w, http.StatusInternalServerError, err)
return
}
@ -424,6 +428,10 @@ func (p *Projects) InviteUsers(w http.ResponseWriter, r *http.Request) {
_, err = p.service.InviteProjectMembers(ctx, id, data.Emails)
if err != nil {
if console.ErrUnauthorized.Has(err) || console.ErrNoMembership.Has(err) {
p.serveJSONError(ctx, w, http.StatusUnauthorized, err)
return
}
p.serveJSONError(ctx, w, http.StatusInternalServerError, err)
}
}
@ -590,6 +598,10 @@ func (p *Projects) DeleteMembersAndInvitations(w http.ResponseWriter, r *http.Re
err = p.service.DeleteProjectMembersAndInvitations(ctx, id, emails)
if err != nil {
if console.ErrUnauthorized.Has(err) || console.ErrNoMembership.Has(err) {
p.serveJSONError(ctx, w, http.StatusUnauthorized, err)
return
}
p.serveJSONError(ctx, w, http.StatusInternalServerError, err)
}
}

View File

@ -64,7 +64,7 @@ func (ul *UsageLimits) ProjectUsageLimits(w http.ResponseWriter, r *http.Request
usageLimits, err := ul.service.GetProjectUsageLimits(ctx, projectID)
if err != nil {
switch {
case console.ErrUnauthorized.Has(err):
case console.ErrUnauthorized.Has(err) || console.ErrNoMembership.Has(err):
ul.serveJSONError(ctx, w, http.StatusUnauthorized, err)
return
case accounting.ErrInvalidArgument.Has(err):
@ -140,7 +140,7 @@ func (ul *UsageLimits) DailyUsage(w http.ResponseWriter, r *http.Request) {
dailyUsage, err := ul.service.GetDailyProjectUsage(ctx, projectID, since, before)
if err != nil {
if console.ErrUnauthorized.Has(err) {
if console.ErrUnauthorized.Has(err) || console.ErrNoMembership.Has(err) {
ul.serveJSONError(ctx, w, http.StatusUnauthorized, err)
return
}

View File

@ -500,16 +500,80 @@ func TestWrongUser(t *testing.T) {
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
test := newTest(t, ctx, planet)
user := test.defaultUser()
_ = user
user2 := test.registerUser("user@mail.test", "#$Rnkl12i3nkljfds")
test.login(user2.email, user2.password)
authorizedUser := test.defaultUser()
unauthorizedUser := test.registerUser("user@mail.test", "#$Rnkl12i3nkljfds")
test.login(unauthorizedUser.email, unauthorizedUser.password)
{ // Get_ProjectUsageLimitById
resp, body := test.request(http.MethodGet, `/projects/`+test.defaultProjectID()+`/usage-limits`, nil)
require.Contains(t, body, "not authorized")
// TODO: wrong error code
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
type endpointTest struct {
endpoint string
method string
body interface{}
}
baseProjectsUrl := "/projects"
baseProjectIdUrl := fmt.Sprintf("%s/%s", baseProjectsUrl, test.defaultProjectID())
getProjectResourceUrl := func(resource string) string {
return fmt.Sprintf("%s/%s", baseProjectIdUrl, resource)
}
testCases := []endpointTest{
{
endpoint: baseProjectIdUrl,
method: http.MethodPatch,
body: map[string]interface{}{
"name": "new name",
},
},
{
endpoint: getProjectResourceUrl("members") + "?emails=" + "some@email.com",
method: http.MethodDelete,
},
{
endpoint: getProjectResourceUrl("salt"),
method: http.MethodGet,
},
{
endpoint: getProjectResourceUrl("members"),
method: http.MethodGet,
},
{
endpoint: getProjectResourceUrl("invite"),
method: http.MethodPost,
body: map[string]interface{}{
"emails": []string{"some@email.com"},
},
},
{
endpoint: getProjectResourceUrl("usage-limits"),
method: http.MethodGet,
},
{
endpoint: getProjectResourceUrl("daily-usage") + "?from=100000000&to=200000000000",
method: http.MethodGet,
},
{
endpoint: "/api-keys/create/" + test.defaultProjectID(),
method: http.MethodPost,
body: "name",
},
}
for _, testCase := range testCases {
t.Run(fmt.Sprintf("Unauthorized on %s", testCase.endpoint), func(t *testing.T) {
resp, body := test.request(testCase.method, testCase.endpoint, test.toJSON(testCase.body))
require.Contains(t, body, "not authorized")
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
// login with correct user to make sure they have access.
test.login(authorizedUser.email, authorizedUser.password)
for _, testCase := range testCases {
t.Run(fmt.Sprintf("Authorized on %s", testCase.endpoint), func(t *testing.T) {
resp, body := test.request(testCase.method, testCase.endpoint, test.toJSON(testCase.body))
require.NotContains(t, body, "not authorized")
require.NotEqual(t, http.StatusUnauthorized, resp.StatusCode)
})
}
})
}