Merge pull request #192 from writeas/T695-reset-user-pass
Resolves T695
This commit is contained in:
commit
5839c2ac4d
5 changed files with 88 additions and 7 deletions
52
admin.go
52
admin.go
|
@ -16,12 +16,14 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/writeas/impart"
|
"github.com/writeas/impart"
|
||||||
"github.com/writeas/web-core/auth"
|
"github.com/writeas/web-core/auth"
|
||||||
"github.com/writeas/web-core/log"
|
"github.com/writeas/web-core/log"
|
||||||
|
"github.com/writeas/web-core/passgen"
|
||||||
"github.com/writeas/writefreely/appstats"
|
"github.com/writeas/writefreely/appstats"
|
||||||
"github.com/writeas/writefreely/config"
|
"github.com/writeas/writefreely/config"
|
||||||
)
|
)
|
||||||
|
@ -170,11 +172,12 @@ func handleViewAdminUser(app *App, u *User, w http.ResponseWriter, r *http.Reque
|
||||||
Config config.AppCfg
|
Config config.AppCfg
|
||||||
Message string
|
Message string
|
||||||
|
|
||||||
User *User
|
User *User
|
||||||
Colls []inspectedCollection
|
Colls []inspectedCollection
|
||||||
LastPost string
|
LastPost string
|
||||||
|
NewPassword string
|
||||||
TotalPosts int64
|
TotalPosts int64
|
||||||
|
ClearEmail string
|
||||||
}{
|
}{
|
||||||
Config: app.cfg.App,
|
Config: app.cfg.App,
|
||||||
Message: r.FormValue("m"),
|
Message: r.FormValue("m"),
|
||||||
|
@ -186,6 +189,14 @@ func handleViewAdminUser(app *App, u *User, w http.ResponseWriter, r *http.Reque
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get user: %v", err)}
|
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get user: %v", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flashes, _ := getSessionFlashes(app, w, r, nil)
|
||||||
|
for _, flash := range flashes {
|
||||||
|
if strings.HasPrefix(flash, "SUCCESS: ") {
|
||||||
|
p.NewPassword = strings.TrimPrefix(flash, "SUCCESS: ")
|
||||||
|
p.ClearEmail = p.User.EmailClear(app.keys)
|
||||||
|
}
|
||||||
|
}
|
||||||
p.UserPage = NewUserPage(app, r, u, p.User.Username, nil)
|
p.UserPage = NewUserPage(app, r, u, p.User.Username, nil)
|
||||||
p.TotalPosts = app.db.GetUserPostsCount(p.User.ID)
|
p.TotalPosts = app.db.GetUserPostsCount(p.User.ID)
|
||||||
lp, err := app.db.GetUserLastPostTime(p.User.ID)
|
lp, err := app.db.GetUserLastPostTime(p.User.ID)
|
||||||
|
@ -230,6 +241,37 @@ func handleViewAdminUser(app *App, u *User, w http.ResponseWriter, r *http.Reque
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleAdminResetUserPass(app *App, u *User, w http.ResponseWriter, r *http.Request) error {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
username := vars["username"]
|
||||||
|
if username == "" {
|
||||||
|
return impart.HTTPError{http.StatusFound, "/admin/users"}
|
||||||
|
}
|
||||||
|
// Generate new random password since none supplied
|
||||||
|
pass := passgen.NewWordish()
|
||||||
|
hashedPass, err := auth.HashPass([]byte(pass))
|
||||||
|
if err != nil {
|
||||||
|
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not create password hash: %v", err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
userIDVal := r.FormValue("user")
|
||||||
|
log.Info("ADMIN: Changing user %s password", userIDVal)
|
||||||
|
id, err := strconv.Atoi(userIDVal)
|
||||||
|
if err != nil {
|
||||||
|
return impart.HTTPError{http.StatusBadRequest, fmt.Sprintf("Invalid user ID: %v", err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.db.ChangePassphrase(int64(id), true, "", hashedPass)
|
||||||
|
if err != nil {
|
||||||
|
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not update passphrase: %v", err)}
|
||||||
|
}
|
||||||
|
log.Info("ADMIN: Successfully changed.")
|
||||||
|
|
||||||
|
addSessionFlash(app, w, r, fmt.Sprintf("SUCCESS: %s", pass), nil)
|
||||||
|
|
||||||
|
return impart.HTTPError{http.StatusFound, fmt.Sprintf("/admin/user/%s", username)}
|
||||||
|
}
|
||||||
|
|
||||||
func handleViewAdminPages(app *App, u *User, w http.ResponseWriter, r *http.Request) error {
|
func handleViewAdminPages(app *App, u *User, w http.ResponseWriter, r *http.Request) error {
|
||||||
p := struct {
|
p := struct {
|
||||||
*UserPage
|
*UserPage
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -45,7 +45,7 @@ require (
|
||||||
github.com/writeas/openssl-go v1.0.0 // indirect
|
github.com/writeas/openssl-go v1.0.0 // indirect
|
||||||
github.com/writeas/saturday v1.7.1
|
github.com/writeas/saturday v1.7.1
|
||||||
github.com/writeas/slug v1.2.0
|
github.com/writeas/slug v1.2.0
|
||||||
github.com/writeas/web-core v1.0.0
|
github.com/writeas/web-core v1.2.0
|
||||||
github.com/writefreely/go-nodeinfo v1.2.0
|
github.com/writefreely/go-nodeinfo v1.2.0
|
||||||
golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f
|
golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f
|
||||||
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1 // indirect
|
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -135,6 +135,8 @@ github.com/writeas/slug v1.2.0 h1:EMQ+cwLiOcA6EtFwUgyw3Ge18x9uflUnOnR6bp/J+/g=
|
||||||
github.com/writeas/slug v1.2.0/go.mod h1:RE8shOqQP3YhsfsQe0L3RnuejfQ4Mk+JjY5YJQFubfQ=
|
github.com/writeas/slug v1.2.0/go.mod h1:RE8shOqQP3YhsfsQe0L3RnuejfQ4Mk+JjY5YJQFubfQ=
|
||||||
github.com/writeas/web-core v1.0.0 h1:5VKkCakQgdKZcbfVKJXtRpc5VHrkflusCl/KRCPzpQ0=
|
github.com/writeas/web-core v1.0.0 h1:5VKkCakQgdKZcbfVKJXtRpc5VHrkflusCl/KRCPzpQ0=
|
||||||
github.com/writeas/web-core v1.0.0/go.mod h1:Si3chV7VWgY8CsV+3gRolMXSO2Vx1ZFAQ/mkrpvmyEE=
|
github.com/writeas/web-core v1.0.0/go.mod h1:Si3chV7VWgY8CsV+3gRolMXSO2Vx1ZFAQ/mkrpvmyEE=
|
||||||
|
github.com/writeas/web-core v1.2.0 h1:CYqvBd+byi1cK4mCr1NZ6CjILuMOFmiFecv+OACcmG0=
|
||||||
|
github.com/writeas/web-core v1.2.0/go.mod h1:vTYajviuNBAxjctPp2NUYdgjofywVkxUGpeaERF3SfI=
|
||||||
github.com/writefreely/go-nodeinfo v1.2.0 h1:La+YbTCvmpTwFhBSlebWDDL81N88Qf/SCAvRLR7F8ss=
|
github.com/writefreely/go-nodeinfo v1.2.0 h1:La+YbTCvmpTwFhBSlebWDDL81N88Qf/SCAvRLR7F8ss=
|
||||||
github.com/writefreely/go-nodeinfo v1.2.0/go.mod h1:UTvE78KpcjYOlRHupZIiSEFcXHioTXuacCbHU+CAcPg=
|
github.com/writefreely/go-nodeinfo v1.2.0/go.mod h1:UTvE78KpcjYOlRHupZIiSEFcXHioTXuacCbHU+CAcPg=
|
||||||
golang.org/x/crypto v0.0.0-20180527072434-ab813273cd59 h1:hk3yo72LXLapY9EXVttc3Z1rLOxT9IuAPPX3GpY2+jo=
|
golang.org/x/crypto v0.0.0-20180527072434-ab813273cd59 h1:hk3yo72LXLapY9EXVttc3Z1rLOxT9IuAPPX3GpY2+jo=
|
||||||
|
|
|
@ -144,6 +144,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
|
||||||
write.HandleFunc("/admin", handler.Admin(handleViewAdminDash)).Methods("GET")
|
write.HandleFunc("/admin", handler.Admin(handleViewAdminDash)).Methods("GET")
|
||||||
write.HandleFunc("/admin/users", handler.Admin(handleViewAdminUsers)).Methods("GET")
|
write.HandleFunc("/admin/users", handler.Admin(handleViewAdminUsers)).Methods("GET")
|
||||||
write.HandleFunc("/admin/user/{username}", handler.Admin(handleViewAdminUser)).Methods("GET")
|
write.HandleFunc("/admin/user/{username}", handler.Admin(handleViewAdminUser)).Methods("GET")
|
||||||
|
write.HandleFunc("/admin/user/{username}/passphrase", handler.Admin(handleAdminResetUserPass)).Methods("POST")
|
||||||
write.HandleFunc("/admin/pages", handler.Admin(handleViewAdminPages)).Methods("GET")
|
write.HandleFunc("/admin/pages", handler.Admin(handleViewAdminPages)).Methods("GET")
|
||||||
write.HandleFunc("/admin/page/{slug}", handler.Admin(handleViewAdminPage)).Methods("GET")
|
write.HandleFunc("/admin/page/{slug}", handler.Admin(handleViewAdminPage)).Methods("GET")
|
||||||
write.HandleFunc("/admin/update/config", handler.AdminApper(handleAdminUpdateConfig)).Methods("POST")
|
write.HandleFunc("/admin/update/config", handler.AdminApper(handleAdminUpdateConfig)).Methods("POST")
|
||||||
|
|
|
@ -7,12 +7,25 @@ table.classy th {
|
||||||
h3 {
|
h3 {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
input.copy-text {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: #555;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="snug content-container">
|
<div class="snug content-container">
|
||||||
{{template "admin-header" .}}
|
{{template "admin-header" .}}
|
||||||
|
|
||||||
<h2 id="posts-header">{{.User.Username}}</h2>
|
<h2 id="posts-header">{{.User.Username}}</h2>
|
||||||
|
{{if .NewPassword}}<div class="alert success">
|
||||||
|
<p>This user's password has been reset to:</p>
|
||||||
|
<p><input type="text" class="copy-text" value="{{.NewPassword}}" onfocus="if (this.select) this.select(); else this.setSelectionRange(0, this.value.length);" readonly /></p>
|
||||||
|
<p>They can use this new password to log in to their account. <strong>This will only be shown once</strong>, so be sure to copy it and send it to them now.</p>
|
||||||
|
{{if .ClearEmail}}<p>Their email address is: <a href="mailto:{{.ClearEmail}}">{{.ClearEmail}}</a></p>{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
<table class="classy export">
|
<table class="classy export">
|
||||||
<tr>
|
<tr>
|
||||||
<th>No.</th>
|
<th>No.</th>
|
||||||
|
@ -38,6 +51,19 @@ h3 {
|
||||||
<th>Last Post</th>
|
<th>Last Post</th>
|
||||||
<td>{{if .LastPost}}{{.LastPost}}{{else}}Never{{end}}</td>
|
<td>{{if .LastPost}}{{.LastPost}}{{else}}Never{{end}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Password</th>
|
||||||
|
<td>
|
||||||
|
{{if ne .Username .User.Username}}
|
||||||
|
<form id="reset-form" action="/admin/user/{{.User.Username}}/passphrase" method="post" autocomplete="false">
|
||||||
|
<input type="hidden" name="user" value="{{.User.ID}}"/>
|
||||||
|
<button type="submit">Reset</button>
|
||||||
|
</form>
|
||||||
|
{{else}}
|
||||||
|
<a href="/me/settings" title="Go to reset password page">Change your password</a>
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>Blogs</h2>
|
<h2>Blogs</h2>
|
||||||
|
@ -83,5 +109,15 @@ h3 {
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
form = document.getElementById("reset-form");
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
agreed = confirm("Reset this user's password? This will generate a new temporary password that you'll need to share with them, and invalidate their old one.");
|
||||||
|
if (agreed === true) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{{template "footer" .}}
|
{{template "footer" .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
Loading…
Add table
Reference in a new issue