mirror of
https://github.com/gophish/gophish
synced 2024-11-14 00:07:19 +00:00
Added functionality to lock accounts (+bug fix) (#2060)
* Added functionality to lock accounts * Fixed typo and added test case for locked account
This commit is contained in:
parent
8b8e88b077
commit
ced5261678
9 changed files with 58 additions and 11 deletions
|
@ -33,6 +33,7 @@ type userRequest struct {
|
|||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
PasswordChangeRequired bool `json:"password_change_required"`
|
||||
AccountLocked bool `json:"account_locked"`
|
||||
}
|
||||
|
||||
func (ur *userRequest) Validate(existingUser *models.User) error {
|
||||
|
@ -102,11 +103,12 @@ func (as *Server) Users(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
user := models.User{
|
||||
Username: ur.Username,
|
||||
Hash: hash,
|
||||
ApiKey: auth.GenerateSecureKey(auth.APIKeyLength),
|
||||
Role: role,
|
||||
RoleID: role.ID,
|
||||
Username: ur.Username,
|
||||
Hash: hash,
|
||||
ApiKey: auth.GenerateSecureKey(auth.APIKeyLength),
|
||||
Role: role,
|
||||
RoleID: role.ID,
|
||||
PasswordChangeRequired: ur.PasswordChangeRequired,
|
||||
}
|
||||
err = models.PutUser(&user)
|
||||
if err != nil {
|
||||
|
|
|
@ -49,6 +49,14 @@ func setupTest(t *testing.T) *testContext {
|
|||
if err != nil {
|
||||
t.Fatalf("error getting first user from database: %v", err)
|
||||
}
|
||||
|
||||
// Create a second user to test account locked status
|
||||
u2 := models.User{Username: "houdini", Hash: hash, AccountLocked: true}
|
||||
models.PutUser(&u2)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating new user: %v", err)
|
||||
}
|
||||
|
||||
ctx.apiKey = u.ApiKey
|
||||
// Start the phishing server
|
||||
ctx.phishServer = httptest.NewUnstartedServer(NewPhishingServer(ctx.config.PhishConf).server.Handler)
|
||||
|
|
|
@ -304,9 +304,9 @@ func (as *AdminServer) nextOrIndex(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, next, 302)
|
||||
}
|
||||
|
||||
func (as *AdminServer) handleInvalidLogin(w http.ResponseWriter, r *http.Request) {
|
||||
func (as *AdminServer) handleInvalidLogin(w http.ResponseWriter, r *http.Request, message string) {
|
||||
session := ctx.Get(r, "session").(*sessions.Session)
|
||||
Flash(w, r, "danger", "Invalid Username/Password")
|
||||
Flash(w, r, "danger", message)
|
||||
params := struct {
|
||||
User models.User
|
||||
Title string
|
||||
|
@ -376,14 +376,18 @@ func (as *AdminServer) Login(w http.ResponseWriter, r *http.Request) {
|
|||
u, err := models.GetUserByUsername(username)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
as.handleInvalidLogin(w, r)
|
||||
as.handleInvalidLogin(w, r, "Invalid Username/Password")
|
||||
return
|
||||
}
|
||||
// Validate the user's password
|
||||
err = auth.ValidatePassword(password, u.Hash)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
as.handleInvalidLogin(w, r)
|
||||
as.handleInvalidLogin(w, r, "Invalid Username/Password")
|
||||
return
|
||||
}
|
||||
if u.AccountLocked == true {
|
||||
as.handleInvalidLogin(w, r, "Account Locked")
|
||||
return
|
||||
}
|
||||
u.LastLogin = time.Now().UTC()
|
||||
|
|
|
@ -117,3 +117,14 @@ func TestSuccessfulRedirect(t *testing.T) {
|
|||
t.Fatalf("unexpected Location header received. expected %s got %s", next, url.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountLocked(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
defer tearDown(t, ctx)
|
||||
resp := attemptLogin(t, ctx, nil, "houdini", "gophish", "")
|
||||
got := resp.StatusCode
|
||||
expected := http.StatusUnauthorized
|
||||
if got != expected {
|
||||
t.Fatalf("invalid status code received. expected %d got %d", expected, got)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
-- +goose Up
|
||||
-- SQL in section 'Up' is executed when this migration is applied
|
||||
ALTER TABLE `users` ADD COLUMN account_locked BOOLEAN;
|
||||
|
||||
-- +goose Down
|
||||
-- SQL section 'Down' is executed when this migration is rolled back
|
|
@ -0,0 +1,6 @@
|
|||
-- +goose Up
|
||||
-- SQL in section 'Up' is executed when this migration is applied
|
||||
ALTER TABLE users ADD COLUMN account_locked BOOLEAN;
|
||||
|
||||
-- +goose Down
|
||||
-- SQL section 'Down' is executed when this migration is rolled back
|
|
@ -21,6 +21,7 @@ type User struct {
|
|||
Role Role `json:"role" gorm:"association_autoupdate:false;association_autocreate:false"`
|
||||
RoleID int64 `json:"-"`
|
||||
PasswordChangeRequired bool `json:"password_change_required"`
|
||||
AccountLocked bool `json:"account_locked"`
|
||||
LastLogin time.Time `json:"last_login"`
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ const save = (id) => {
|
|||
username: $("#username").val(),
|
||||
password: $("#password").val(),
|
||||
role: $("#role").val(),
|
||||
password_change_required: $("#force_password_change_checkbox").prop('checked')
|
||||
password_change_required: $("#force_password_change_checkbox").prop('checked'),
|
||||
account_locked: $("#account_locked_checkbox").prop('checked')
|
||||
}
|
||||
// Submit the user
|
||||
if (id != -1) {
|
||||
|
@ -49,6 +50,8 @@ const dismiss = () => {
|
|||
$("#password").val("")
|
||||
$("#confirm_password").val("")
|
||||
$("#role").val("")
|
||||
$("#force_password_change_checkbox").prop('checked', true)
|
||||
$("#account_locked_checkbox").prop('checked', false)
|
||||
$("#modal\\.flashes").empty()
|
||||
}
|
||||
|
||||
|
@ -66,7 +69,8 @@ const edit = (id) => {
|
|||
$("#username").val(user.username)
|
||||
$("#role").val(user.role.slug)
|
||||
$("#role").trigger("change")
|
||||
$("#force_password_change_checkbox").prop('checked', false)
|
||||
$("#force_password_change_checkbox").prop('checked', user.password_change_required)
|
||||
$("#account_locked_checkbox").prop('checked', user.account_locked)
|
||||
})
|
||||
.error(function () {
|
||||
errorFlash("Error fetching user")
|
||||
|
|
|
@ -67,6 +67,10 @@
|
|||
<input id="force_password_change_checkbox" type="checkbox" checked>
|
||||
<label for="force_password_change_checkbox">Require the user to set a new password</label>
|
||||
</div>
|
||||
<div class="checkbox checkbox-primary">
|
||||
<input id="account_locked_checkbox" type="checkbox">
|
||||
<label for="account_locked_checkbox">Account Locked</label>
|
||||
</div>
|
||||
<label class="control-label" for="role">Role:</label>
|
||||
<div class="form-group" id="role-select">
|
||||
<select class="form-control" placeholder="" id="role" />
|
||||
|
|
Loading…
Reference in a new issue