1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-01-08 10:51:09 +00:00

Working user panel

See https://mycorrhiza.wiki/hypha/idea/user_panel

It's not pretty, but it works. The next step is to make it look good.
This commit is contained in:
handlerug 2021-06-29 22:10:48 +07:00
parent c7e4281398
commit 11e98b2368
No known key found for this signature in database
GPG Key ID: 38009F0605051491
7 changed files with 255 additions and 50 deletions

View File

@ -124,6 +124,10 @@ func readTokensToUsers() {
log.Println("Found", len(tmp), "active sessions") log.Println("Found", len(tmp), "active sessions")
} }
func SaveUserDatabase() error {
return dumpRegistrationCredentials()
}
func dumpRegistrationCredentials() error { func dumpRegistrationCredentials() error {
tmp := []*User{} tmp := []*User{}

View File

@ -23,7 +23,7 @@ func FromRequest(rq *http.Request) *User {
if err != nil { if err != nil {
return EmptyUser() return EmptyUser()
} }
return userByToken(cookie.Value) return UserByToken(cookie.Value)
} }
// LogoutFromRequest logs the user in `rq` out and rewrites the cookie in `w`. // LogoutFromRequest logs the user in `rq` out and rewrites the cookie in `w`.

View File

@ -51,6 +51,14 @@ var minimalRights = map[string]int{
"admin/shutdown": 4, "admin/shutdown": 4,
} }
var groups = []string{
"anon",
"editor",
"trusted",
"moderator",
"admin",
}
// Group — Right // Group — Right
var groupRight = map[string]int{ var groupRight = map[string]int{
"anon": 0, "anon": 0,
@ -60,6 +68,15 @@ var groupRight = map[string]int{
"admin": 4, "admin": 4,
} }
func ValidGroup(group string) bool {
for _, grp := range groups {
if grp == group {
return true
}
}
return false
}
func EmptyUser() *User { func EmptyUser() *User {
return &User{ return &User{
Name: "anon", Name: "anon",

View File

@ -56,18 +56,18 @@ func HasUsername(username string) bool {
} }
func CredentialsOK(username, password string) bool { func CredentialsOK(username, password string) bool {
return userByName(username).isCorrectPassword(password) return UserByName(username).isCorrectPassword(password)
} }
func userByToken(token string) *User { func UserByToken(token string) *User {
if usernameUntyped, ok := tokens.Load(token); ok { if usernameUntyped, ok := tokens.Load(token); ok {
username := usernameUntyped.(string) username := usernameUntyped.(string)
return userByName(username) return UserByName(username)
} }
return EmptyUser() return EmptyUser()
} }
func userByName(username string) *User { func UserByName(username string) *User {
if userUntyped, ok := users.Load(username); ok { if userUntyped, ok := users.Load(username); ok {
user := userUntyped.(*User) user := userUntyped.(*User)
return user return user

View File

@ -5,7 +5,7 @@
<main class="main-width"> <main class="main-width">
<h1>Manage users</h1> <h1>Manage users</h1>
<form action="/admin/reindex-users" method="get"> <form action="/admin/reindex-users" method="post">
<button class="btn btn_accent" type="submit">Reindex users</button> <button class="btn btn_accent" type="submit">Reindex users</button>
</form> </form>
@ -17,14 +17,18 @@
<th>Name</th> <th>Name</th>
<th>Group</th> <th>Group</th>
<th>Registered at</th> <th>Registered at</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for _, u := range userList %} {% for _, u := range userList %}
<tr> <tr>
<td>{%s= u.Name %}</td> <td>{%s u.Name %}</td>
<td>{%s= u.Group %}</td> <td>{%s u.Group %}</td>
<td>{%s= u.RegisteredAt.Format("2006-01-02 15:04:05-0700") %}</td> <td>{%s u.RegisteredAt.Format("2006-01-02 15:04:05-0700") %}</td>
<td>
<a href="/admin/users/{%u u.Name %}/edit">Edit</a>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -32,3 +36,26 @@
</main> </main>
</div> </div>
{% endfunc %} {% endfunc %}
{% func AdminUsersUserHTML(u *user.User) %}
<div class="layout">
<main class="main-width">
<h1>{%s u.Name %}</h1>
<form action="" method="post">
<label for="group">Group:</label>
<select id="group" name="group">
<option{% if u.Group == "anon" %} selected{% endif %}>anon</option>
<option{% if u.Group == "editor" %} selected{% endif %}>editor</option>
<option{% if u.Group == "trusted" %} selected{% endif %}>trusted</option>
<option{% if u.Group == "moderator" %} selected{% endif %}>moderator</option>
<option{% if u.Group == "admin" %} selected{% endif %}>admin</option>
</select>
<br>
<br>
<button class="btn btn_accent" type="submit">Update</button>
</form>
</main>
</div>
{% endfunc %}

View File

@ -28,7 +28,7 @@ func StreamAdminUsersPanelHTML(qw422016 *qt422016.Writer, userList []*user.User)
<main class="main-width"> <main class="main-width">
<h1>Manage users</h1> <h1>Manage users</h1>
<form action="/admin/reindex-users" method="get"> <form action="/admin/reindex-users" method="post">
<button class="btn btn_accent" type="submit">Reindex users</button> <button class="btn btn_accent" type="submit">Reindex users</button>
</form> </form>
@ -40,66 +40,172 @@ func StreamAdminUsersPanelHTML(qw422016 *qt422016.Writer, userList []*user.User)
<th>Name</th> <th>Name</th>
<th>Group</th> <th>Group</th>
<th>Registered at</th> <th>Registered at</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
`) `)
//line views/admin.qtpl:23 //line views/admin.qtpl:24
for _, u := range userList { for _, u := range userList {
//line views/admin.qtpl:23 //line views/admin.qtpl:24
qw422016.N().S(` qw422016.N().S(`
<tr> <tr>
<td>`) <td>`)
//line views/admin.qtpl:25
qw422016.N().S(u.Name)
//line views/admin.qtpl:25
qw422016.N().S(`</td>
<td>`)
//line views/admin.qtpl:26 //line views/admin.qtpl:26
qw422016.N().S(u.Group) qw422016.E().S(u.Name)
//line views/admin.qtpl:26 //line views/admin.qtpl:26
qw422016.N().S(`</td> qw422016.N().S(`</td>
<td>`) <td>`)
//line views/admin.qtpl:27 //line views/admin.qtpl:27
qw422016.N().S(u.RegisteredAt.Format("2006-01-02 15:04:05-0700")) qw422016.E().S(u.Group)
//line views/admin.qtpl:27 //line views/admin.qtpl:27
qw422016.N().S(`</td> qw422016.N().S(`</td>
<td>`)
//line views/admin.qtpl:28
qw422016.E().S(u.RegisteredAt.Format("2006-01-02 15:04:05-0700"))
//line views/admin.qtpl:28
qw422016.N().S(`</td>
<td>
<a href="/admin/users/`)
//line views/admin.qtpl:30
qw422016.N().U(u.Name)
//line views/admin.qtpl:30
qw422016.N().S(`/edit">Edit</a>
</td>
</tr> </tr>
`) `)
//line views/admin.qtpl:29 //line views/admin.qtpl:33
} }
//line views/admin.qtpl:29 //line views/admin.qtpl:33
qw422016.N().S(` qw422016.N().S(`
</tbody> </tbody>
</table> </table>
</main> </main>
</div> </div>
`) `)
//line views/admin.qtpl:34 //line views/admin.qtpl:38
} }
//line views/admin.qtpl:34 //line views/admin.qtpl:38
func WriteAdminUsersPanelHTML(qq422016 qtio422016.Writer, userList []*user.User) { func WriteAdminUsersPanelHTML(qq422016 qtio422016.Writer, userList []*user.User) {
//line views/admin.qtpl:34 //line views/admin.qtpl:38
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line views/admin.qtpl:34 //line views/admin.qtpl:38
StreamAdminUsersPanelHTML(qw422016, userList) StreamAdminUsersPanelHTML(qw422016, userList)
//line views/admin.qtpl:34 //line views/admin.qtpl:38
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line views/admin.qtpl:34 //line views/admin.qtpl:38
} }
//line views/admin.qtpl:34 //line views/admin.qtpl:38
func AdminUsersPanelHTML(userList []*user.User) string { func AdminUsersPanelHTML(userList []*user.User) string {
//line views/admin.qtpl:34 //line views/admin.qtpl:38
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line views/admin.qtpl:34 //line views/admin.qtpl:38
WriteAdminUsersPanelHTML(qb422016, userList) WriteAdminUsersPanelHTML(qb422016, userList)
//line views/admin.qtpl:34 //line views/admin.qtpl:38
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line views/admin.qtpl:34 //line views/admin.qtpl:38
qt422016.ReleaseByteBuffer(qb422016) qt422016.ReleaseByteBuffer(qb422016)
//line views/admin.qtpl:34 //line views/admin.qtpl:38
return qs422016 return qs422016
//line views/admin.qtpl:34 //line views/admin.qtpl:38
}
//line views/admin.qtpl:40
func StreamAdminUsersUserHTML(qw422016 *qt422016.Writer, u *user.User) {
//line views/admin.qtpl:40
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<h1>`)
//line views/admin.qtpl:43
qw422016.E().S(u.Name)
//line views/admin.qtpl:43
qw422016.N().S(`</h1>
<form action="" method="post">
<label for="group">Group:</label>
<select id="group" name="group">
<option`)
//line views/admin.qtpl:48
if u.Group == "anon" {
//line views/admin.qtpl:48
qw422016.N().S(` selected`)
//line views/admin.qtpl:48
}
//line views/admin.qtpl:48
qw422016.N().S(`>anon</option>
<option`)
//line views/admin.qtpl:49
if u.Group == "editor" {
//line views/admin.qtpl:49
qw422016.N().S(` selected`)
//line views/admin.qtpl:49
}
//line views/admin.qtpl:49
qw422016.N().S(`>editor</option>
<option`)
//line views/admin.qtpl:50
if u.Group == "trusted" {
//line views/admin.qtpl:50
qw422016.N().S(` selected`)
//line views/admin.qtpl:50
}
//line views/admin.qtpl:50
qw422016.N().S(`>trusted</option>
<option`)
//line views/admin.qtpl:51
if u.Group == "moderator" {
//line views/admin.qtpl:51
qw422016.N().S(` selected`)
//line views/admin.qtpl:51
}
//line views/admin.qtpl:51
qw422016.N().S(`>moderator</option>
<option`)
//line views/admin.qtpl:52
if u.Group == "admin" {
//line views/admin.qtpl:52
qw422016.N().S(` selected`)
//line views/admin.qtpl:52
}
//line views/admin.qtpl:52
qw422016.N().S(`>admin</option>
</select>
<br>
<br>
<button class="btn btn_accent" type="submit">Update</button>
</form>
</main>
</div>
`)
//line views/admin.qtpl:61
}
//line views/admin.qtpl:61
func WriteAdminUsersUserHTML(qq422016 qtio422016.Writer, u *user.User) {
//line views/admin.qtpl:61
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/admin.qtpl:61
StreamAdminUsersUserHTML(qw422016, u)
//line views/admin.qtpl:61
qt422016.ReleaseWriter(qw422016)
//line views/admin.qtpl:61
}
//line views/admin.qtpl:61
func AdminUsersUserHTML(u *user.User) string {
//line views/admin.qtpl:61
qb422016 := qt422016.AcquireByteBuffer()
//line views/admin.qtpl:61
WriteAdminUsersUserHTML(qb422016, u)
//line views/admin.qtpl:61
qs422016 := string(qb422016.B)
//line views/admin.qtpl:61
qt422016.ReleaseByteBuffer(qb422016)
//line views/admin.qtpl:61
return qs422016
//line views/admin.qtpl:61
} }

View File

@ -1,10 +1,13 @@
package web package web
import ( import (
"fmt"
"mime"
"io" "io"
"log" "log"
"net/http" "net/http"
"sort" "sort"
"strings"
"github.com/bouncepaw/mycorrhiza/cfg" "github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/user" "github.com/bouncepaw/mycorrhiza/user"
@ -19,7 +22,7 @@ func initAdmin() {
http.HandleFunc("/admin/shutdown", handlerAdminShutdown) http.HandleFunc("/admin/shutdown", handlerAdminShutdown)
http.HandleFunc("/admin/reindex-users", handlerAdminReindexUsers) http.HandleFunc("/admin/reindex-users", handlerAdminReindexUsers)
http.HandleFunc("/admin/users", handlerAdminUsers) http.HandleFunc("/admin/users/", handlerAdminUsers)
} }
} }
@ -49,31 +52,79 @@ func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
if user.CanProceed(rq, "admin") && rq.Method == "POST" { if user.CanProceed(rq, "admin") && rq.Method == "POST" {
user.ReadUsersFromFilesystem() user.ReadUsersFromFilesystem()
http.Redirect(w, rq, "/hypha/"+cfg.UserHypha, http.StatusSeeOther) redirectTo := rq.Referer()
if redirectTo == "" {
redirectTo = "/hypha/"+cfg.UserHypha
}
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
} }
} }
func handlerAdminUsers(w http.ResponseWriter, r *http.Request) { func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
util.PrepareRq(r) util.PrepareRq(r)
if user.CanProceed(r, "admin") { if user.CanProceed(r, "admin") {
// Get a sorted list of users path := strings.TrimPrefix(r.URL.Path, "/admin/users")
var userList []*user.User parts := strings.Split(path, "/")[1:]
for u := range user.YieldUsers() {
userList = append(userList, u) // Users dashboard
if len(parts) == 0 {
// Get a sorted list of users
var userList []*user.User
for u := range user.YieldUsers() {
userList = append(userList, u)
}
sort.Slice(userList, func(i, j int) bool {
less := userList[i].RegisteredAt.Before(userList[j].RegisteredAt)
return less
})
html := views.AdminUsersPanelHTML(userList)
html = views.BaseHTML("Manage users", html, user.FromRequest(r))
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
if _, err := io.WriteString(w, html); err != nil {
log.Println(err)
}
return
} }
sort.Slice(userList, func(i, j int) bool { // User edit page
less := userList[i].RegisteredAt.Before(userList[j].RegisteredAt) if len(parts) == 2 && parts[1] == "edit" {
return less u := user.UserByName(parts[0])
})
html := views.AdminUsersPanelHTML(userList) if u != nil && u.Name != "anon" {
html = views.BaseHTML("Manage users", html, user.FromRequest(r)) if r.Method == http.MethodGet {
html := views.AdminUsersUserHTML(u)
html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(r))
w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
_, err := io.WriteString(w, html) if _, err := io.WriteString(w, html); err != nil {
if err != nil { log.Println(err)
log.Println(err) }
return
} else if r.Method == http.MethodPost {
oldGroup := u.Group
newGroup := r.PostFormValue("group")
if user.ValidGroup(newGroup) {
u.Group = newGroup
if err := user.SaveUserDatabase(); err != nil {
u.Group = oldGroup
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
io.WriteString(w, err.Error())
} else {
http.Redirect(w, r, "/admin/users/", http.StatusSeeOther)
}
} else {
w.WriteHeader(http.StatusBadRequest)
io.WriteString(w, "invalid group")
}
return
}
}
} }
util.HTTP404Page(w, "404 page not found")
} }
} }