github.com/merlinepedra/gophish1@v0.9.0/controllers/api/user.go (about)

     1  package api
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"net/http"
     7  	"strconv"
     8  
     9  	ctx "github.com/gophish/gophish/context"
    10  	log "github.com/gophish/gophish/logger"
    11  	"github.com/gophish/gophish/models"
    12  	"github.com/gophish/gophish/util"
    13  	"github.com/gorilla/mux"
    14  	"github.com/jinzhu/gorm"
    15  )
    16  
    17  // ErrEmptyPassword is thrown when a user provides a blank password to the register
    18  // or change password functions
    19  var ErrEmptyPassword = errors.New("No password provided")
    20  
    21  // ErrUsernameTaken is thrown when a user attempts to register a username that is taken.
    22  var ErrUsernameTaken = errors.New("Username already taken")
    23  
    24  // ErrEmptyUsername is thrown when a user attempts to register a username that is taken.
    25  var ErrEmptyUsername = errors.New("No username provided")
    26  
    27  // ErrEmptyRole is throws when no role is provided when creating or modifying a user.
    28  var ErrEmptyRole = errors.New("No role specified")
    29  
    30  // ErrInsufficientPermission is thrown when a user attempts to change an
    31  // attribute (such as the role) for which they don't have permission.
    32  var ErrInsufficientPermission = errors.New("Permission denied")
    33  
    34  // userRequest is the payload which represents the creation of a new user.
    35  type userRequest struct {
    36  	Username string `json:"username"`
    37  	Password string `json:"password"`
    38  	Role     string `json:"role"`
    39  }
    40  
    41  func (ur *userRequest) Validate(existingUser *models.User) error {
    42  	switch {
    43  	case ur.Username == "":
    44  		return ErrEmptyUsername
    45  	case ur.Role == "":
    46  		return ErrEmptyRole
    47  	}
    48  	// Verify that the username isn't already taken. We consider two cases:
    49  	// * We're creating a new user, in which case any match is a conflict
    50  	// * We're modifying a user, in which case any match with a different ID is
    51  	//   a conflict.
    52  	possibleConflict, err := models.GetUserByUsername(ur.Username)
    53  	if err == nil {
    54  		if existingUser == nil {
    55  			return ErrUsernameTaken
    56  		}
    57  		if possibleConflict.Id != existingUser.Id {
    58  			return ErrUsernameTaken
    59  		}
    60  	}
    61  	// If we have an error which is not simply indicating that no user was found, report it
    62  	if err != nil && err != gorm.ErrRecordNotFound {
    63  		return err
    64  	}
    65  	return nil
    66  }
    67  
    68  // Users contains functions to retrieve a list of existing users or create a
    69  // new user. Users with the ModifySystem permissions can view and create users.
    70  func (as *Server) Users(w http.ResponseWriter, r *http.Request) {
    71  	switch {
    72  	case r.Method == "GET":
    73  		us, err := models.GetUsers()
    74  		if err != nil {
    75  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
    76  			return
    77  		}
    78  		JSONResponse(w, us, http.StatusOK)
    79  		return
    80  	case r.Method == "POST":
    81  		ur := &userRequest{}
    82  		err := json.NewDecoder(r.Body).Decode(ur)
    83  		if err != nil {
    84  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
    85  			return
    86  		}
    87  		err = ur.Validate(nil)
    88  		if err != nil {
    89  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
    90  			return
    91  		}
    92  		if ur.Password == "" {
    93  			JSONResponse(w, models.Response{Success: false, Message: ErrEmptyPassword.Error()}, http.StatusBadRequest)
    94  			return
    95  		}
    96  		hash, err := util.NewHash(ur.Password)
    97  		if err != nil {
    98  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
    99  			return
   100  		}
   101  		role, err := models.GetRoleBySlug(ur.Role)
   102  		if err != nil {
   103  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   104  			return
   105  		}
   106  		user := models.User{
   107  			Username: ur.Username,
   108  			Hash:     hash,
   109  			ApiKey:   util.GenerateSecureKey(),
   110  			Role:     role,
   111  			RoleID:   role.ID,
   112  		}
   113  		err = models.PutUser(&user)
   114  		if err != nil {
   115  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   116  			return
   117  		}
   118  		JSONResponse(w, user, http.StatusOK)
   119  		return
   120  	}
   121  }
   122  
   123  // User contains functions to retrieve or delete a single user. Users with
   124  // the ModifySystem permission can view and modify any user. Otherwise, users
   125  // may only view or delete their own account.
   126  func (as *Server) User(w http.ResponseWriter, r *http.Request) {
   127  	vars := mux.Vars(r)
   128  	id, _ := strconv.ParseInt(vars["id"], 0, 64)
   129  	// If the user doesn't have ModifySystem permissions, we need to verify
   130  	// that they're only taking action on their account.
   131  	currentUser := ctx.Get(r, "user").(models.User)
   132  	hasSystem, err := currentUser.HasPermission(models.PermissionModifySystem)
   133  	if err != nil {
   134  		JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   135  		return
   136  	}
   137  	if !hasSystem && currentUser.Id != id {
   138  		JSONResponse(w, models.Response{Success: false, Message: http.StatusText(http.StatusForbidden)}, http.StatusForbidden)
   139  		return
   140  	}
   141  	existingUser, err := models.GetUser(id)
   142  	if err != nil {
   143  		JSONResponse(w, models.Response{Success: false, Message: "User not found"}, http.StatusNotFound)
   144  		return
   145  	}
   146  	switch {
   147  	case r.Method == "GET":
   148  		JSONResponse(w, existingUser, http.StatusOK)
   149  	case r.Method == "DELETE":
   150  		err = models.DeleteUser(id)
   151  		if err != nil {
   152  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   153  			return
   154  		}
   155  		log.Infof("Deleted user account for %s", existingUser.Username)
   156  		JSONResponse(w, models.Response{Success: true, Message: "User deleted Successfully!"}, http.StatusOK)
   157  	case r.Method == "PUT":
   158  		ur := &userRequest{}
   159  		err = json.NewDecoder(r.Body).Decode(ur)
   160  		if err != nil {
   161  			log.Errorf("error decoding user request: %v", err)
   162  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
   163  			return
   164  		}
   165  		err = ur.Validate(&existingUser)
   166  		if err != nil {
   167  			log.Errorf("invalid user request received: %v", err)
   168  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
   169  			return
   170  		}
   171  		existingUser.Username = ur.Username
   172  		// Only users with the ModifySystem permission are able to update a
   173  		// user's role. This prevents a privilege escalation letting users
   174  		// upgrade their own account.
   175  		if !hasSystem && ur.Role != existingUser.Role.Slug {
   176  			JSONResponse(w, models.Response{Success: false, Message: ErrInsufficientPermission.Error()}, http.StatusBadRequest)
   177  			return
   178  		}
   179  		role, err := models.GetRoleBySlug(ur.Role)
   180  		if err != nil {
   181  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   182  			return
   183  		}
   184  		// If our user is trying to change the role of an admin, we need to
   185  		// ensure that it isn't the last user account with the Admin role.
   186  		if existingUser.Role.Slug == models.RoleAdmin && existingUser.Role.ID != role.ID {
   187  			err = models.EnsureEnoughAdmins()
   188  			if err != nil {
   189  				JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   190  				return
   191  			}
   192  		}
   193  		existingUser.Role = role
   194  		existingUser.RoleID = role.ID
   195  		// We don't force the password to be provided, since it may be an admin
   196  		// managing the user's account, and making a simple change like
   197  		// updating the username or role. However, if it _is_ provided, we'll
   198  		// update the stored hash.
   199  		//
   200  		// Note that we don't force the current password to be provided. The
   201  		// assumption here is that the API key is a proper bearer token proving
   202  		// authenticated access to the account.
   203  		if ur.Password != "" {
   204  			hash, err := util.NewHash(ur.Password)
   205  			if err != nil {
   206  				JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   207  				return
   208  			}
   209  			existingUser.Hash = hash
   210  		}
   211  		err = models.PutUser(&existingUser)
   212  		if err != nil {
   213  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   214  			return
   215  		}
   216  		JSONResponse(w, existingUser, http.StatusOK)
   217  	}
   218  }