Added creation a new admin when none exist

This commit is contained in:
kolaente 2018-12-07 21:53:08 +01:00
parent d8af3a8a35
commit ef9f105ab1
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
7 changed files with 278 additions and 4 deletions

14
main.go
View File

@ -41,14 +41,14 @@ func main() {
// Init Config
err := config.InitConfig()
if err != nil {
log.Log.Error(err.Error())
log.Log.Fatal(err.Error())
os.Exit(1)
}
// Set Engine
err = models.SetEngine()
if err != nil {
log.Log.Error(err.Error())
log.Log.Fatal(err.Error())
os.Exit(1)
}
@ -58,6 +58,16 @@ func main() {
// Additional swagger information
docs.SwaggerInfo.Version = Version
// Create first admin if needed
firstAdmin, err := models.CreateFirstAdmin()
if err != nil {
log.Log.Fatal("Could not create first admin.", err)
os.Exit(1)
}
if firstAdmin != nil {
log.Log.Infof("Created first admin user with name %s", firstAdmin.Username)
}
// Start the webserver
e := routes.NewEcho()
routes.RegisterRoutes(e)

View File

@ -55,6 +55,9 @@ func InitConfig() (err error) {
viper.SetDefault("database.path", "./sofaraum.db")
viper.SetDefault("database.showqueries", false)
viper.SetDefault("database.openconnections", 100)
// First Admin
viper.SetDefault("firstadmin.username", "admin")
viper.SetDefault("firstadmin.password", "admin")
// Cacher
viper.SetDefault("cache.enabled", false)
viper.SetDefault("cache.type", "memory")

View File

@ -150,3 +150,51 @@ const ErrCodeCouldNotGetUserID = 2003
func (err ErrCouldNotGetUserID) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
}
// ErrUsernameExists represents a "UsernameAlreadyExists" kind of error.
type ErrUsernameExists struct {
UserID int64
Username string
}
// IsErrUsernameExists checks if an error is a ErrUsernameExists.
func IsErrUsernameExists(err error) bool {
_, ok := err.(ErrUsernameExists)
return ok
}
func (err ErrUsernameExists) Error() string {
return fmt.Sprintf("User with that username already exists [user id: %d, username: %s]", err.UserID, err.Username)
}
// ErrorCodeUsernameExists holds the unique world-error code of this error
const ErrorCodeUsernameExists = 2004
// HTTPError holds the http error description
func (err ErrUsernameExists) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUsernameExists, Message: "A user with this username already exists."}
}
// ErrUserEmailExists represents a "UserEmailExists" kind of error.
type ErrUserEmailExists struct {
UserID int64
Email string
}
// IsErrUserEmailExists checks if an error is a ErrUserEmailExists.
func IsErrUserEmailExists(err error) bool {
_, ok := err.(ErrUserEmailExists)
return ok
}
func (err ErrUserEmailExists) Error() string {
return fmt.Sprintf("User with that email already exists [user id: %d, email: %s]", err.UserID, err.Email)
}
// ErrorCodeUserEmailExists holds the unique world-error code of this error
const ErrorCodeUserEmailExists = 2005
// HTTPError holds the http error description
func (err ErrUserEmailExists) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUserEmailExists, Message: "A user with this email address already exists."}
}

View File

@ -23,6 +23,7 @@ import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
"github.com/spf13/viper"
"golang.org/x/crypto/bcrypt"
"reflect"
)
@ -83,8 +84,8 @@ type APIUserPassword struct {
}
// APIFormat formats an API User into a normal user struct
func (apiUser *APIUserPassword) APIFormat() User {
return User{
func (apiUser *APIUserPassword) APIFormat() *User {
return &User{
ID: apiUser.ID,
Username: apiUser.Username,
Password: apiUser.Password,
@ -155,3 +156,21 @@ func GetCurrentUser(c echo.Context) (user *User, err error) {
return
}
// CreateFirstAdmin checks if there is at least one user and creates a new one if not
func CreateFirstAdmin() (firstAdmin *User, err error) {
count, err := x.Count(User{})
if err != nil {
return
}
if count < 1 {
firstAdmin, err = CreateUser(&User{
Username: viper.GetString("firstadmin.username"),
Password: viper.GetString("firstadmin.password"),
})
return
}
return
}

View File

@ -0,0 +1,141 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
import (
"golang.org/x/crypto/bcrypt"
)
// CreateUser creates a new user and inserts it into the database
func CreateUser(user *User) (newUser *User, err error) {
newUser = user
// Check if we have all needed informations
if newUser.Password == "" || newUser.Username == "" {
return &User{}, ErrNoUsernamePassword{}
}
// Check if the user already existst with that username
var exists = true
existingUser, err := GetUser(&User{Username: newUser.Username})
if err != nil {
if IsErrUserDoesNotExist(err) {
exists = false
} else {
return &User{}, err
}
}
if exists {
return &User{}, ErrUsernameExists{newUser.ID, newUser.Username}
}
// Check if the user already existst with that email
if newUser.Email != "" {
existingUser, err = GetUser(&User{Email: newUser.Email})
if err != nil {
if IsErrUserDoesNotExist(err) {
exists = false
} else {
return &User{}, err
}
}
if exists {
return &User{}, ErrUserEmailExists{existingUser.ID, existingUser.Email}
}
}
// Hash the password
newUser.Password, err = hashPassword(user.Password)
if err != nil {
return &User{}, err
}
newUser.IsActive = true
// Insert it
_, err = x.Insert(newUser)
if err != nil {
return &User{}, err
}
return newUser, err
}
// HashPassword hashes a password
func hashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}
// UpdateUser updates a user
func UpdateUser(user *User) (updatedUser *User, err error) {
// Check if it exists
theUser, err := GetUserByID(user.ID)
if err != nil {
return &User{}, err
}
// Check if we have at least a username
if user.Username == "" {
//return User{}, ErrNoUsername{user.ID}
user.Username = theUser.Username // Dont change the username if we dont have one
}
user.Password = theUser.Password // set the password to the one in the database to not accedently resetting it
// Update it
_, err = x.Id(user.ID).Update(user)
if err != nil {
return &User{}, err
}
// Get the newly updated user
updatedUser, err = GetUserByID(user.ID)
if err != nil {
return &User{}, err
}
return updatedUser, err
}
// UpdateUserPassword updates the password of a user
func UpdateUserPassword(user *User, newPassword string) (err error) {
// Get all user details
theUser, err := GetUserByID(user.ID)
if err != nil {
return err
}
// Hash the new password and set it
hashed, err := hashPassword(newPassword)
if err != nil {
return err
}
theUser.Password = hashed
// Update it
_, err = x.Id(user.ID).Update(theUser)
if err != nil {
return err
}
return err
}

View File

@ -0,0 +1,52 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package v1
import (
"code.sofaraum.de/server/pkg/models"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
)
// RegisterUser is the register handler
// @Summary Register
// @Description Creates a new user account.
// @tags user
// @Accept json
// @Produce json
// @Param credentials body models.APIUserPassword true "The user credentials"
// @Success 200 {object} models.User
// @Failure 400 {object} code.vikunja.io/web.HTTPError "No or invalid user register object provided / User already exists."
// @Failure 500 {object} models.Message "Internal error"
// @Router /register [post]
func CreateUser(c echo.Context) error {
// Check for Request Content
var datUser *models.APIUserPassword
if err := c.Bind(&datUser); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid user model provided.")
}
// Insert the user
newUser, err := models.CreateUser(datUser.APIFormat())
if err != nil {
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, newUser)
}

View File

@ -117,4 +117,5 @@ func RegisterRoutes(e *echo.Echo) {
// User stuff
a.GET("/user", apiv1.UserShow)
a.POST("/user/new", apiv1.CreateUser)
}