🎣 Open-Source Phishing Toolkit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
8.1 KiB

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