gophish/controllers/api/user_test.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)
}