mirror of https://github.com/gophish/gophish
189 lines
5.9 KiB
Go
189 lines
5.9 KiB
Go
|
package api
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"net/http/httptest"
|
||
|
|
||
|
"golang.org/x/crypto/bcrypt"
|
||
|
|
||
|
ctx "github.com/gophish/gophish/context"
|
||
|
"github.com/gophish/gophish/models"
|
||
|
)
|
||
|
|
||
|
func (s *APISuite) createUnpriviledgedUser(slug string) *models.User {
|
||
|
role, err := models.GetRoleBySlug(slug)
|
||
|
s.Nil(err)
|
||
|
unauthorizedUser := &models.User{
|
||
|
Username: "foo",
|
||
|
Hash: "bar",
|
||
|
ApiKey: "12345",
|
||
|
Role: role,
|
||
|
RoleID: role.ID,
|
||
|
}
|
||
|
err = models.PutUser(unauthorizedUser)
|
||
|
s.Nil(err)
|
||
|
return unauthorizedUser
|
||
|
}
|
||
|
|
||
|
func (s *APISuite) TestGetUsers() {
|
||
|
r := httptest.NewRequest(http.MethodGet, "/api/users", nil)
|
||
|
r = ctx.Set(r, "user", s.admin)
|
||
|
w := httptest.NewRecorder()
|
||
|
|
||
|
s.apiServer.Users(w, r)
|
||
|
s.Equal(w.Code, http.StatusOK)
|
||
|
|
||
|
got := []models.User{}
|
||
|
err := json.NewDecoder(w.Body).Decode(&got)
|
||
|
s.Nil(err)
|
||
|
|
||
|
// We only expect one user
|
||
|
s.Equal(1, len(got))
|
||
|
// And it should be the admin user
|
||
|
s.Equal(s.admin.Id, got[0].Id)
|
||
|
}
|
||
|
|
||
|
func (s *APISuite) TestCreateUser() {
|
||
|
payload := &userRequest{
|
||
|
Username: "foo",
|
||
|
Password: "bar",
|
||
|
Role: models.RoleUser,
|
||
|
}
|
||
|
body, err := json.Marshal(payload)
|
||
|
s.Nil(err)
|
||
|
|
||
|
r := httptest.NewRequest(http.MethodPost, "/api/users", bytes.NewBuffer(body))
|
||
|
r.Header.Set("Content-Type", "application/json")
|
||
|
r = ctx.Set(r, "user", s.admin)
|
||
|
w := httptest.NewRecorder()
|
||
|
|
||
|
s.apiServer.Users(w, r)
|
||
|
s.Equal(w.Code, http.StatusOK)
|
||
|
|
||
|
got := &models.User{}
|
||
|
err = json.NewDecoder(w.Body).Decode(got)
|
||
|
s.Nil(err)
|
||
|
s.Equal(got.Username, payload.Username)
|
||
|
s.Equal(got.Role.Slug, payload.Role)
|
||
|
}
|
||
|
|
||
|
// TestModifyUser tests that a user with the appropriate access is able to
|
||
|
// modify their username and password.
|
||
|
func (s *APISuite) TestModifyUser() {
|
||
|
unpriviledgedUser := s.createUnpriviledgedUser(models.RoleUser)
|
||
|
newPassword := "new-password"
|
||
|
newUsername := "new-username"
|
||
|
payload := userRequest{
|
||
|
Username: newUsername,
|
||
|
Password: newPassword,
|
||
|
Role: unpriviledgedUser.Role.Slug,
|
||
|
}
|
||
|
body, err := json.Marshal(payload)
|
||
|
s.Nil(err)
|
||
|
url := fmt.Sprintf("/api/users/%d", unpriviledgedUser.Id)
|
||
|
r := httptest.NewRequest(http.MethodPut, url, bytes.NewBuffer(body))
|
||
|
r.Header.Set("Content-Type", "application/json")
|
||
|
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", unpriviledgedUser.ApiKey))
|
||
|
w := httptest.NewRecorder()
|
||
|
|
||
|
s.apiServer.ServeHTTP(w, r)
|
||
|
response := &models.User{}
|
||
|
err = json.NewDecoder(w.Body).Decode(response)
|
||
|
s.Nil(err)
|
||
|
s.Equal(w.Code, http.StatusOK)
|
||
|
s.Equal(response.Username, newUsername)
|
||
|
got, err := models.GetUser(unpriviledgedUser.Id)
|
||
|
s.Nil(err)
|
||
|
s.Equal(response.Username, got.Username)
|
||
|
s.Equal(newUsername, got.Username)
|
||
|
err = bcrypt.CompareHashAndPassword([]byte(got.Hash), []byte(newPassword))
|
||
|
s.Nil(err)
|
||
|
}
|
||
|
|
||
|
// TestUnauthorizedListUsers ensures that users without the ModifySystem
|
||
|
// permission are unable to list the users registered in Gophish.
|
||
|
func (s *APISuite) TestUnauthorizedListUsers() {
|
||
|
// First, let's create a standard user which doesn't
|
||
|
// have ModifySystem permissions.
|
||
|
unauthorizedUser := s.createUnpriviledgedUser(models.RoleUser)
|
||
|
// We'll try to make a request to the various users API endpoints to
|
||
|
// ensure that they fail. Previously, we could hit the handlers directly
|
||
|
// but we need to go through the router for this test to ensure the
|
||
|
// middleware gets applied.
|
||
|
r := httptest.NewRequest(http.MethodGet, "/api/users/", nil)
|
||
|
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", unauthorizedUser.ApiKey))
|
||
|
w := httptest.NewRecorder()
|
||
|
|
||
|
s.apiServer.ServeHTTP(w, r)
|
||
|
s.Equal(w.Code, http.StatusForbidden)
|
||
|
}
|
||
|
|
||
|
// TestUnauthorizedModifyUsers verifies that users without ModifySystem
|
||
|
// permission (a "standard" user) can only get or modify their own information.
|
||
|
func (s *APISuite) TestUnauthorizedGetUser() {
|
||
|
// First, we'll make sure that a user with the "user" role is unable to
|
||
|
// get the information of another user (in this case, the main admin).
|
||
|
unauthorizedUser := s.createUnpriviledgedUser(models.RoleUser)
|
||
|
url := fmt.Sprintf("/api/users/%d", s.admin.Id)
|
||
|
r := httptest.NewRequest(http.MethodGet, url, nil)
|
||
|
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", unauthorizedUser.ApiKey))
|
||
|
w := httptest.NewRecorder()
|
||
|
|
||
|
s.apiServer.ServeHTTP(w, r)
|
||
|
s.Equal(w.Code, http.StatusForbidden)
|
||
|
}
|
||
|
|
||
|
// TestUnauthorizedModifyRole ensures that users without the ModifySystem
|
||
|
// privilege are unable to modify their own role, preventing a potential
|
||
|
// privilege escalation issue.
|
||
|
func (s *APISuite) TestUnauthorizedSetRole() {
|
||
|
unauthorizedUser := s.createUnpriviledgedUser(models.RoleUser)
|
||
|
url := fmt.Sprintf("/api/users/%d", unauthorizedUser.Id)
|
||
|
payload := &userRequest{
|
||
|
Username: unauthorizedUser.Username,
|
||
|
Role: models.RoleAdmin,
|
||
|
}
|
||
|
body, err := json.Marshal(payload)
|
||
|
s.Nil(err)
|
||
|
r := httptest.NewRequest(http.MethodPut, url, bytes.NewBuffer(body))
|
||
|
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", unauthorizedUser.ApiKey))
|
||
|
w := httptest.NewRecorder()
|
||
|
|
||
|
s.apiServer.ServeHTTP(w, r)
|
||
|
s.Equal(w.Code, http.StatusBadRequest)
|
||
|
response := &models.Response{}
|
||
|
err = json.NewDecoder(w.Body).Decode(response)
|
||
|
s.Nil(err)
|
||
|
s.Equal(response.Message, ErrInsufficientPermission.Error())
|
||
|
}
|
||
|
|
||
|
// TestModifyWithExistingUsername verifies that it's not possible to modify
|
||
|
// an user's username to one which already exists.
|
||
|
func (s *APISuite) TestModifyWithExistingUsername() {
|
||
|
unauthorizedUser := s.createUnpriviledgedUser(models.RoleUser)
|
||
|
payload := &userRequest{
|
||
|
Username: s.admin.Username,
|
||
|
Role: unauthorizedUser.Role.Slug,
|
||
|
}
|
||
|
body, err := json.Marshal(payload)
|
||
|
s.Nil(err)
|
||
|
url := fmt.Sprintf("/api/users/%d", unauthorizedUser.Id)
|
||
|
r := httptest.NewRequest(http.MethodPut, url, bytes.NewReader(body))
|
||
|
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", unauthorizedUser.ApiKey))
|
||
|
w := httptest.NewRecorder()
|
||
|
|
||
|
s.apiServer.ServeHTTP(w, r)
|
||
|
s.Equal(w.Code, http.StatusBadRequest)
|
||
|
expected := &models.Response{
|
||
|
Message: ErrUsernameTaken.Error(),
|
||
|
Success: false,
|
||
|
}
|
||
|
got := &models.Response{}
|
||
|
err = json.NewDecoder(w.Body).Decode(got)
|
||
|
s.Nil(err)
|
||
|
s.Equal(got.Message, expected.Message)
|
||
|
}
|