Import sources from Kontor

This commit is contained in:
Thomas Peetz
2019-06-18 18:33:38 +02:00
parent fa3286c8e2
commit f326e994da
20 changed files with 1262 additions and 3 deletions
+23
View File
@@ -0,0 +1,23 @@
package admin
import (
"gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/auth"
"gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/util"
"github.com/gin-gonic/gin"
)
// GetRoutes sets the routes for the administrative data urls.
func GetRoutes(router *gin.Engine) {
adminRoutes := router.Group("/admin")
{
adminRoutes.GET("/", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showAdminIndex)
adminRoutes.GET("/user", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showUserIndex)
adminRoutes.POST("/user", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showUserIndex)
adminRoutes.GET("/user/view/:userid", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showUserDetails)
adminRoutes.POST("/user/view", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), validateUserCreation)
adminRoutes.GET("/user/create", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showUserCreation)
adminRoutes.POST("/user/create", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), validateUserCreation)
adminRoutes.GET("/data", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), util.ShowIndexPage)
}
}
+18
View File
@@ -0,0 +1,18 @@
package admin
import (
"gopkg.in/mgo.v2/bson"
)
// User defines the data model for application users with id,email, user name,
// first and family name, password and admin status.
type User struct {
ID bson.ObjectId `json:"_id" bson:"_id,omitempty"`
Email string `json:"email" bson:"email,omitempty"`
Username string `json:"username" bson:"username,omitempty"`
Firstname string `json:"firstname" bson:"firstname,omitempty"`
Lastname string `json:"lastname" bson:"lastname,omitempty"`
Password string `json:"password" bson:"password,omitempty"`
IsAdmin bool `json:"is_admin" bson:"is_admin,omitempty"`
Model string `json:"model" bson:"model,omitempty"`
}
+156
View File
@@ -0,0 +1,156 @@
package admin
import (
"gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao"
"log"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
// UserDAO extends the type BaseDAO.
type UserDAO struct {
Db dao.BaseDAO
}
const (
// USERCOLLECTION defines the collection name for storing application user data.
USERCOLLECTION = "user"
// USERMODEL defines the name of the user data model.
USERMODEL = "kalorienmanager.admin.user"
)
// HashPassword returns the encrypted password from password string.
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}
// CheckPasswordHash returns if password correlates with pasword hash.
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
// FindAll retrieves the list of users from the database.
func (m *UserDAO) FindAll() ([]User, error) {
m.Db.Connect()
var users []User
err := m.Db.MongoDb.C(USERCOLLECTION).Find(bson.M{"model": USERMODEL}).All(&users)
return users, err
}
// FindByID returns a user with given id or returns the error.
func (m *UserDAO) FindByID(id string) (User, error) {
m.Db.Connect()
var user User
err := m.Db.MongoDb.C(USERCOLLECTION).FindId(bson.ObjectIdHex(id)).One(&user)
return user, err
}
// FindByUsername returns a user with given name or returns the error.
func (m *UserDAO) FindByUsername(username string) (User, error) {
m.Db.Connect()
var user User
err := m.Db.MongoDb.C(USERCOLLECTION).Find(bson.M{"username": username, "model": USERMODEL}).One(&user)
return user, err
}
// Insert a user into database.
func (m *UserDAO) Insert(user User) error {
m.Db.Connect()
user.Model = USERMODEL
err := m.Db.MongoDb.C(USERCOLLECTION).Insert(&user)
return err
}
// Upsert a user into database.
func (m *UserDAO) Upsert(user User) (*mgo.ChangeInfo, error) {
m.Db.Connect()
user.Model = USERMODEL
info, err := m.Db.MongoDb.C(USERCOLLECTION).Upsert(bson.M{"username": user.Username}, &user)
return info, err
}
// Update an existing user.
func (m *UserDAO) Update(user User) error {
m.Db.Connect()
err := m.Db.MongoDb.C(USERCOLLECTION).UpdateId(user.ID, &user)
return err
}
// Delete an existing user.
func (m *UserDAO) Delete(user User) error {
m.Db.Connect()
err := m.Db.MongoDb.C(USERCOLLECTION).Remove(&user)
return err
}
// IsUserValid checks if the username and password combination is valid
func (m *UserDAO) IsUserValid(username, password string) bool {
if gin.IsDebugging() {
log.Printf("UserDAO.IsUserValid(%s)", username)
}
user, err := m.FindByUsername(username)
if gin.IsDebugging() {
log.Printf("UserDAO.IsUserValid: %v, %v", user, err)
}
if &user == nil || err != nil {
return false
}
return CheckPasswordHash(password, user.Password)
}
// IsUserAdmin checks if user identified by name has admin rights.
func (m *UserDAO) IsUserAdmin(username string) bool {
user, err := m.FindByUsername(username)
if &user == nil || err != nil {
return false
}
return user.IsAdmin
}
// IsUsernameAvailable checks if the supplied username is available.
func (m *UserDAO) IsUsernameAvailable(username string) bool {
user, err := m.FindByUsername(username)
if &user == nil || err != nil {
return true
}
return false
}
// adduser adds user in database. TODO is method necessary?
func addUser(username, password, firstname, lastname string, isAdmin bool) (*User, error) {
// passwordHash, _ := HashPassword(password)
// var user = User{Username: username, Password: passwordHash, Firstname: firstname, Lastname: lastname, IsAdmin: isAdmin, Model: USERMODEL}
// conn, err := util.GetCollection(USERCOLLECTION)
// err = conn.Insert(&user)
// if err != nil {
// return nil, err
// }
// return &user, nil
return nil, nil
}
// changeuser changes user in database. TODO is method necessary?
func changeUser(userid, username, password, firstname, lastname string, isAdmin bool) (*User, error) {
// conn, err := util.GetCollection(USERCOLLECTION)
// var user *User
// err = conn.Find(bson.M{"_id": bson.ObjectIdHex(userid)}).One(&user)
// if err != nil {
// return nil, err
// }
// var change bson.M
// if password != "" {
// passwordHash, _ := HashPassword(password)
// change = bson.M{"$set": bson.M{"username": username, "password": passwordHash, "firstname": firstname, "lastname": lastname, "is_admin": isAdmin}}
// } else {
// change = bson.M{"$set": bson.M{"username": username, "firstname": firstname, "lastname": lastname, "is_admin": isAdmin}}
// }
// err = conn.Update(bson.M{"_id": bson.ObjectIdHex(userid)}, change)
// return user, nil
return nil, nil
}
+105
View File
@@ -0,0 +1,105 @@
package admin
import (
"gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao"
"reflect"
"testing"
"gopkg.in/mgo.v2/bson"
)
var userModelTestTable = []struct {
name string
typeName string
}{
{"Id", "string"},
{"Email", "string"},
{"Username", "string"},
{"Firstname", "string"},
{"Lastname", "string"},
{"Password", "string"},
{"IsAdmin", "bool"},
{"Model", "string"},
}
func TestUserModel(t *testing.T) {
m := User{}
if reflect.TypeOf(m).NumField() != len(userModelTestTable) {
t.Fail()
}
for index, testData := range userModelTestTable {
givenType := reflect.TypeOf(m).Field(index).Type.Kind().String()
if givenType != testData.typeName {
t.Fail()
}
}
}
func TestListUsers(t *testing.T) {
var userDao = UserDAO{Db: dao.TestDb}
users, err := userDao.FindAll()
if err != nil {
t.Fail()
}
if users != nil {
t.Fail()
}
}
func TestInsertUser(t *testing.T) {
var (
userDao = UserDAO{Db: dao.TestDb}
user = User{}
users []User
)
user.ID = bson.NewObjectId()
user.Username = "test"
err := userDao.Insert(user)
if err != nil {
t.Fail()
}
users, err = userDao.FindAll()
if err != nil {
t.Fail()
}
if len(users) != 1 {
t.Fail()
}
}
func TestUpsertUser(t *testing.T) {
var (
userDao = UserDAO{Db: dao.TestDb}
user = User{}
)
user.ID = bson.NewObjectId()
user.Username = "test2"
userDao.Upsert(user)
users, err := userDao.FindAll()
if err != nil {
t.Fail()
}
if len(users) != 2 {
t.Fail()
}
}
func TestDeleteUser(t *testing.T) {
var (
userDao = UserDAO{Db: dao.TestDb}
)
users, err := userDao.FindAll()
if err != nil {
t.Fail()
}
for _, user := range users {
userDao.Delete(user)
}
users, err = userDao.FindAll()
if err != nil {
t.Fail()
}
if len(users) != 0 {
t.Fail()
}
}
+128
View File
@@ -0,0 +1,128 @@
package admin
import (
"gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/auth"
"gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao"
"gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/util"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// ShowLoginPage renders login page.
func ShowLoginPage(c *gin.Context) {
// Call the render function with the name of the template to render
util.Render(c, gin.H{"title": "Login"}, "login.html")
}
// PerformLogin reads data from login form and validates input.
func PerformLogin(c *gin.Context) {
// Obtain the POSTed username and password values
username := c.PostForm("username")
password := c.PostForm("password")
var userDao = UserDAO{Db: dao.KalorienmanagerDb}
// Check if the username/password combination is valid
if userDao.IsUserValid(username, password) {
sessionInterface, _ := c.Get("session")
sessionID := sessionInterface.(string)
user, _ := userDao.FindByUsername(username)
sessionDao := auth.SessionDAO{Db: dao.KalorienmanagerDb}
session, _ := sessionDao.FindByID(sessionID)
session.Username = username
session.IsAdmin = user.IsAdmin
sessionDao.Update(session)
util.Render(c, gin.H{"title": "Successful Login", "InfoMessage": "Login successfull"}, "kontor/index.html")
} else {
// If the username/password combination is invalid,
// show the error message on the login page
c.HTML(http.StatusBadRequest, "login.html", gin.H{
"ErrorTitle": "Login Failed",
"ErrorMessage": "Invalid credentials provided"})
}
}
// Logout invalidates session.
func Logout(c *gin.Context) {
sessionInterface, _ := c.Get("session")
sessionID := sessionInterface.(string)
c.SetCookie("session", sessionID, -1, "", "", false, true)
// Redirect to the home page
c.Redirect(http.StatusTemporaryRedirect, "/")
}
func showAdminIndex(c *gin.Context) {
// Call the render function with the name of the template to render
util.Render(c, gin.H{"title": "Kontor", "payload": nil}, "kontor/admin.html")
}
func showUserIndex(c *gin.Context) {
var dao = UserDAO{Db: dao.KalorienmanagerDb}
if users, err := dao.FindAll(); err == nil && users != nil {
util.Render(c, gin.H{"title": "Kontor User Administration", "payload": users}, "kontor/users.html")
} else {
util.Render(c, gin.H{"title": "Kontor User Administration", "payload": users, "ErrorMessage": err}, "kontor/users.html")
}
}
func showUserDetails(c *gin.Context) {
userID := c.Param("userid")
var userDao = UserDAO{Db: dao.KalorienmanagerDb}
if user, err := userDao.FindByID(userID); err == nil && &user != nil {
util.Render(c, gin.H{"title": "Kontor User Administration", "payload": user, "action": util.SaveAction}, "kontor/user-detail.html")
} else {
c.AbortWithError(http.StatusNotFound, err)
}
}
func showUserCreation(c *gin.Context) {
var user = User{}
util.Render(c, gin.H{"title": "Kontor User Administration", "payload": user, "action": util.AddAction}, "kontor/user-detail.html")
}
func validateUserCreation(c *gin.Context) {
// Obtain the POSTed username and password values
username := c.PostForm("username")
firstname := c.PostForm("firstname")
lastname := c.PostForm("lastname")
password := c.PostForm("password")
adminFormVar := c.PostForm("admin")
action := c.PostForm("action")
userid := c.PostForm("userid")
isAdmin, _ := strconv.ParseBool(adminFormVar)
var err error
var dao = UserDAO{Db: dao.KalorienmanagerDb}
var user = User{}
switch action {
case util.AddAction:
user.Username = username
user.Firstname = firstname
user.Lastname = lastname
user.IsAdmin = isAdmin
user.Password, _ = HashPassword(password)
_, err = dao.Upsert(user)
case util.SaveAction:
user, _ = dao.FindByID(userid)
user.Username = username
user.Firstname = firstname
user.Lastname = lastname
user.IsAdmin = isAdmin
user.Password, _ = HashPassword(password)
err = dao.Update(user)
case util.DeleteAction:
user, _ = dao.FindByID(userid)
err = dao.Delete(user)
}
if err == nil {
c.Redirect(http.StatusTemporaryRedirect, "/admin/user")
} else {
c.HTML(http.StatusBadRequest, "kontor/create-user.html", gin.H{
"ErrorTitle": "User Creation Failed",
"ErrorMessage": err.Error()})
}
}