Add user management interface
This commit is contained in:
parent
6cd5b1bc5e
commit
6f906ccae4
8 changed files with 124 additions and 8 deletions
|
@ -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,
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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")
|
||||||
|
|
54
lib/user.go
54
lib/user.go
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
34
templates/user_admin.html
Normal 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"}}
|
Reference in a new issue