Added creation a new admin when none exist
This commit is contained in:
parent
d8af3a8a35
commit
ef9f105ab1
14
main.go
14
main.go
|
@ -41,14 +41,14 @@ func main() {
|
||||||
// Init Config
|
// Init Config
|
||||||
err := config.InitConfig()
|
err := config.InitConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error(err.Error())
|
log.Log.Fatal(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Engine
|
// Set Engine
|
||||||
err = models.SetEngine()
|
err = models.SetEngine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error(err.Error())
|
log.Log.Fatal(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,16 @@ func main() {
|
||||||
// Additional swagger information
|
// Additional swagger information
|
||||||
docs.SwaggerInfo.Version = Version
|
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
|
// Start the webserver
|
||||||
e := routes.NewEcho()
|
e := routes.NewEcho()
|
||||||
routes.RegisterRoutes(e)
|
routes.RegisterRoutes(e)
|
||||||
|
|
|
@ -55,6 +55,9 @@ func InitConfig() (err error) {
|
||||||
viper.SetDefault("database.path", "./sofaraum.db")
|
viper.SetDefault("database.path", "./sofaraum.db")
|
||||||
viper.SetDefault("database.showqueries", false)
|
viper.SetDefault("database.showqueries", false)
|
||||||
viper.SetDefault("database.openconnections", 100)
|
viper.SetDefault("database.openconnections", 100)
|
||||||
|
// First Admin
|
||||||
|
viper.SetDefault("firstadmin.username", "admin")
|
||||||
|
viper.SetDefault("firstadmin.password", "admin")
|
||||||
// Cacher
|
// Cacher
|
||||||
viper.SetDefault("cache.enabled", false)
|
viper.SetDefault("cache.enabled", false)
|
||||||
viper.SetDefault("cache.type", "memory")
|
viper.SetDefault("cache.type", "memory")
|
||||||
|
|
|
@ -150,3 +150,51 @@ const ErrCodeCouldNotGetUserID = 2003
|
||||||
func (err ErrCouldNotGetUserID) HTTPError() web.HTTPError {
|
func (err ErrCouldNotGetUserID) HTTPError() web.HTTPError {
|
||||||
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
|
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."}
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
|
"github.com/spf13/viper"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
@ -83,8 +84,8 @@ type APIUserPassword struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIFormat formats an API User into a normal user struct
|
// APIFormat formats an API User into a normal user struct
|
||||||
func (apiUser *APIUserPassword) APIFormat() User {
|
func (apiUser *APIUserPassword) APIFormat() *User {
|
||||||
return User{
|
return &User{
|
||||||
ID: apiUser.ID,
|
ID: apiUser.ID,
|
||||||
Username: apiUser.Username,
|
Username: apiUser.Username,
|
||||||
Password: apiUser.Password,
|
Password: apiUser.Password,
|
||||||
|
@ -155,3 +156,21 @@ func GetCurrentUser(c echo.Context) (user *User, err error) {
|
||||||
|
|
||||||
return
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -117,4 +117,5 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
|
|
||||||
// User stuff
|
// User stuff
|
||||||
a.GET("/user", apiv1.UserShow)
|
a.GET("/user", apiv1.UserShow)
|
||||||
|
a.POST("/user/new", apiv1.CreateUser)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue