Add user management interface

This commit is contained in:
Las Zenow 2018-04-08 10:55:13 +00:00
parent 6cd5b1bc5e
commit 6f906ccae4
8 changed files with 124 additions and 8 deletions

View file

@ -21,7 +21,9 @@ type DB interface {
AddRawUser(name string, hpass []byte, salt []byte, role string) error AddRawUser(name string, hpass []byte, salt []byte, role string) error
GetRole(name string) (string, error) GetRole(name string) (string, error)
SetPassword(name string, pass string) error SetPassword(name string, pass string) error
SetRole(name, role string) error
ValidPassword(name string, pass string) bool ValidPassword(name string, pass string) bool
ListUsers() ([]User, error)
AddNews(text string) error AddNews(text string) error
AddRawNews(text string, date time.Time) error AddRawNews(text string, date time.Time) error
GetNews(num int, days int) (news []New, err error) GetNews(num int, days int) (news []New, err error)
@ -94,7 +96,7 @@ func (db pgDB) Close() error {
} }
func (db pgDB) create() error { func (db pgDB) create() error {
models := []interface{}{&Book{}, &New{}, &user{}, &Visit{}, &Submission{}} models := []interface{}{&Book{}, &New{}, &User{}, &Visit{}, &Submission{}}
for _, model := range models { for _, model := range models {
options := &orm.CreateTableOptions{ options := &orm.CreateTableOptions{
IfNotExists: true, IfNotExists: true,

View file

@ -65,6 +65,14 @@ func (db *roDB) SetPassword(name string, pass string) error {
return errors.New("RO database") return errors.New("RO database")
} }
func (db *roDB) SetRole(name, role string) error {
return errors.New("RO database")
}
func (db *roDB) ListUsers() ([]User, error) {
return db.db.ListUsers()
}
func (db *roDB) AddNews(text string) error { func (db *roDB) AddNews(text string) error {
return errors.New("RO database") return errors.New("RO database")
} }

View file

@ -12,7 +12,7 @@ import (
"golang.org/x/crypto/scrypt" "golang.org/x/crypto/scrypt"
) )
type user struct { type User struct {
ID int `sql:"type:serial"` ID int `sql:"type:serial"`
Username string `sql:"type:varchar(255),unique"` Username string `sql:"type:varchar(255),unique"`
Password []byte Password []byte
@ -25,7 +25,7 @@ func (db *pgDB) AddUser(name string, pass string) error {
if !validUserName(name) { if !validUserName(name) {
return errors.New("Invalid user name") return errors.New("Invalid user name")
} }
num, err := db.sql.Model(&user{}).Where("lower(username) = lower(?)", name).Count() num, err := db.sql.Model(&User{}).Where("lower(username) = lower(?)", name).Count()
if err != nil { if err != nil {
log.Error("Error on database checking user ", name, ": ", err) log.Error("Error on database checking user ", name, ": ", err)
return errors.New("An error happen on the database") return errors.New("An error happen on the database")
@ -43,7 +43,7 @@ func (db *pgDB) AddUser(name string, pass string) error {
} }
func (db *pgDB) AddRawUser(name string, hpass []byte, salt []byte, role string) error { func (db *pgDB) AddRawUser(name string, hpass []byte, salt []byte, role string) error {
u := user{ u := User{
Username: name, Username: name,
Password: hpass, Password: hpass,
Salt: salt, Salt: salt,
@ -53,13 +53,21 @@ func (db *pgDB) AddRawUser(name string, hpass []byte, salt []byte, role string)
} }
func (db *pgDB) GetRole(name string) (string, error) { func (db *pgDB) GetRole(name string) (string, error) {
var u user var u User
err := db.sql.Model(&u).Where("username = ?", name).Select() err := db.sql.Model(&u).Where("username = ?", name).Select()
return u.Role, err return u.Role, err
} }
func (db *pgDB) SetRole(name, role string) error {
_, err := db.sql.Model(&User{}).
Set("role = ?", role).
Where("username = ?", name).
Update()
return err
}
func (db *pgDB) ValidPassword(name string, pass string) bool { func (db *pgDB) ValidPassword(name string, pass string) bool {
var u user var u User
err := db.sql.Model(&u).Where("lower(username) = lower(?)", name).Select() err := db.sql.Model(&u).Where("lower(username) = lower(?)", name).Select()
if err != nil { if err != nil {
return false return false
@ -73,7 +81,7 @@ func (db *pgDB) ValidPassword(name string, pass string) bool {
return false return false
} }
_, err = db.sql.Model(&user{}). _, err = db.sql.Model(&User{}).
Set("last_login = CURRENT_TIMESTAMP"). Set("last_login = CURRENT_TIMESTAMP").
Where("id = ?", u.ID). Where("id = ?", u.ID).
Update() Update()
@ -88,13 +96,19 @@ func (db *pgDB) SetPassword(name string, pass string) error {
if err != nil { if err != nil {
return err return err
} }
_, err = db.sql.Model(&user{}). _, err = db.sql.Model(&User{}).
Set("password = ?, salt = ?", hash, salt). Set("password = ?, salt = ?", hash, salt).
Where("username = ?", name). Where("username = ?", name).
Update() Update()
return err return err
} }
func (db *pgDB) ListUsers() ([]User, error) {
var users []User
err := db.sql.Model(&users).Select()
return users, err
}
func validUserName(name string) bool { func validUserName(name string) bool {
switch name { switch name {
case "", "admin", "webmaster", "postmaster", "info", "root", "news": case "", "admin", "webmaster", "postmaster", "info", "root", "news":

View file

@ -73,6 +73,7 @@ func InitTemplate(assetsPath string) *Template {
path.Join(templatePath, "dashboard.html"), path.Join(templatePath, "dashboard.html"),
path.Join(templatePath, "settings.html"), path.Join(templatePath, "settings.html"),
path.Join(templatePath, "help.html"), path.Join(templatePath, "help.html"),
path.Join(templatePath, "user_admin.html"),
)) ))
t.rss = txt_tmpl.Must(txt_tmpl.ParseFiles( t.rss = txt_tmpl.Must(txt_tmpl.ParseFiles(

View file

@ -196,6 +196,8 @@ func InitRouter(db database.DB, sg *StatsGatherer, assetsPath string) http.Handl
r.HandleFunc("/edit/{id:"+idPattern+"}", sg.Gather(editHandler)) r.HandleFunc("/edit/{id:"+idPattern+"}", sg.Gather(editHandler))
r.HandleFunc("/store/{ids:(?:"+idPattern+"/)+}", sg.Gather(storeHandler)) r.HandleFunc("/store/{ids:(?:"+idPattern+"/)+}", sg.Gather(storeHandler))
r.HandleFunc("/delete/{ids:(?:"+idPattern+"/)+}", sg.Gather(deleteHandler)) r.HandleFunc("/delete/{ids:(?:"+idPattern+"/)+}", sg.Gather(deleteHandler))
r.HandleFunc("/admin/users/", sg.Gather(userAdminHandler)).Methods("GET")
r.HandleFunc("/admin/users/", sg.Gather(userAdminPostHandler)).Methods("POST")
r.HandleFunc("/news/", sg.Gather(newsHandler)) r.HandleFunc("/news/", sg.Gather(newsHandler))
r.HandleFunc("/news/edit", sg.Gather(editNewsHandler)).Methods("GET") r.HandleFunc("/news/edit", sg.Gather(editNewsHandler)).Methods("GET")

View file

@ -2,6 +2,7 @@ package trantor
import ( import (
log "github.com/cihub/seelog" log "github.com/cihub/seelog"
"gitlab.com/trantor/trantor/lib/database"
"net/http" "net/http"
) )
@ -95,3 +96,56 @@ func settingsHandler(h handler) {
data.S.Title = "Settings -- " + data.S.Title data.S.Title = "Settings -- " + data.S.Title
h.load("settings", data) h.load("settings", data)
} }
func userAdminHandler(h handler) {
if !h.sess.IsAdmin() {
notFound(h)
return
}
users, err := h.db.ListUsers()
if err != nil {
log.Error("Something went wrong listing users: ", err)
notFound(h)
return
}
var data userAdminData
data.S = GetStatus(h)
data.S.Title = "Users admin -- " + data.S.Title
data.Users = users
h.load("user_admin", data)
}
func userAdminPostHandler(h handler) {
if !h.sess.IsAdmin() {
notFound(h)
return
}
username := h.r.FormValue("username")
password := h.r.FormValue("password")
role := h.r.FormValue("role")
if password != "" {
err := h.db.SetPassword(username, password)
if err != nil {
h.sess.Notify("An error ocurred!", err.Error(), "error")
} else {
h.sess.Notify("Password updated!", "", "success")
}
} else if role != "" {
err := h.db.SetRole(username, role)
if err != nil {
h.sess.Notify("An error ocurred!", err.Error(), "error")
} else {
h.sess.Notify("Role updated!", "", "success")
}
}
userAdminHandler(h)
}
type userAdminData struct {
S Status
Users []database.User
}

View file

@ -72,6 +72,7 @@
<li><a href="/new/"><i class="icon-book"></i> New books</a></li> <li><a href="/new/"><i class="icon-book"></i> New books</a></li>
{{if eq .Role "admin"}} {{if eq .Role "admin"}}
<li><a href="/news/edit"><i class="icon-certificate"></i> Edit news</a></li> <li><a href="/news/edit"><i class="icon-certificate"></i> Edit news</a></li>
<li><a href="/admin/users/"><i class="icon-user"></i> Users admin</a></li>
{{end}} {{end}}
<li class="divider"></li> <li class="divider"></li>
{{end}} {{end}}

34
templates/user_admin.html Normal file
View file

@ -0,0 +1,34 @@
{{template "header.html" .S}}
<h4>Users:</h4>
<table class="table table-hover">
<thead>
<th>username</th>
<th>role</th>
<th>password</th>
<th>last login</th>
</thead>
<tbody>
{{range .Users}}
<tr>
<td>{{.Username}}</td>
<td><form class="row form-inline" method="POST" action="/admin/users/">
<input type="hidden" id="username" name="username" value="{{.Username}}">
<input type="text" id="role" name="role" value="{{.Role}}">
<button type="submit" class="btn btn-primary">Update</button>
</form>
</td>
<td><form class="row form-inline" method="POST" action="/admin/users/">
<input type="hidden" id="username" name="username" value="{{.Username}}">
<input type="password" id="password" name="password" placeholder="password">
<button type="submit" class="btn btn-primary">Update</button>
</form>
</td>
<td>{{.LastLogin.Format "2006-01-02"}}</td>
</tr>
{{end}}
</tbody>
</table>
{{template "footer.html"}}