create initial admin user at startup

This commit is contained in:
overflowerror 2021-08-14 15:58:14 +02:00
parent 59f5b51a9d
commit 0f8f528b76
11 changed files with 167 additions and 20 deletions

View file

@ -31,6 +31,12 @@ func main() {
AccessLog: log.New(os.Stdout, "access", log.Ldate|log.Lmicroseconds|log.Lmsgprefix), AccessLog: log.New(os.Stdout, "access", log.Ldate|log.Lmicroseconds|log.Lmsgprefix),
} }
ctx.Log.Info("setting up persistence layer")
ctx.Data, err = data.Setup(ctx)
if err != nil {
ctx.Log.Fatal("couldn't setup persistence layer")
}
ctx.Log.Info("setting up logic layer") ctx.Log.Info("setting up logic layer")
ctx.Logic, err = logic.Setup(ctx) ctx.Logic, err = logic.Setup(ctx)
if err != nil { if err != nil {
@ -38,12 +44,6 @@ func main() {
ctx.Log.Fatal(err) ctx.Log.Fatal(err)
} }
ctx.Log.Info("setting up persistence layer")
ctx.Data, err = data.Setup(ctx)
if err != nil {
ctx.Log.Fatal("couldn't setup persistence layer")
}
ctx.Log.Info("setting up routes") ctx.Log.Info("setting up routes")
handler := router.Setup(ctx) handler := router.Setup(ctx)
err = web.StartServer(ctx, handler) err = web.StartServer(ctx, handler)

View file

@ -10,6 +10,7 @@ require (
github.com/kr/pretty v0.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.0-beta.3 github.com/pelletier/go-toml/v2 v2.0.0-beta.3
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gorm.io/driver/mysql v1.1.2 gorm.io/driver/mysql v1.1.2
gorm.io/gorm v1.21.13 gorm.io/gorm v1.21.13

View file

@ -35,8 +35,16 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942 h1:t0lM6y/M5IiUZyvbBTcngso8SZEZICH7is9B6g/obVU= github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942 h1:t0lM6y/M5IiUZyvbBTcngso8SZEZICH7is9B6g/obVU=
github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs= golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e h1:VvfwVmMH40bpMeizC9/K7ipM5Qjucuu16RWfneFPyhQ=
golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -3,6 +3,11 @@ package app
import "threadule/backend/internal/data/models" import "threadule/backend/internal/data/models"
type Data interface { type Data interface {
CountUsers() (int64, error)
CreateUser(user *models.User) error
AddGroup(group *models.Group) error
GetSession(id string) (*models.Session, error) GetSession(id string) (*models.Session, error)
UpdateSession(session *models.Session) error UpdateSession(session *models.Session) error
CleanupSessions() error CleanupSessions() error

View file

@ -0,0 +1,9 @@
package data
import "threadule/backend/internal/data/models"
func (d *Data) AddGroup(group *models.Group) error {
return d.db.
Create(group).
Error
}

View file

@ -4,10 +4,25 @@ type Group struct {
BaseModel BaseModel
Users []*User `gorm:"many2many:user_groups;"` Users []*User `gorm:"many2many:user_groups;"`
Name string Name string
DisplayName string DisplayName string
AdminGroup bool
ManageUsers bool
ManageGroups bool
LimitAccounts uint LimitAccounts uint
LimitThreads uint LimitThreads uint
LimitTweets uint LimitTweets uint
} }
func GetDefaultAdminGroup() *Group {
return &Group{
Name: "admin",
DisplayName: "Administrators",
AdminGroup: true,
ManageUsers: true,
ManageGroups: true,
LimitAccounts: 0,
LimitThreads: 0,
LimitTweets: 0,
}
}

View file

@ -7,3 +7,9 @@ type User struct {
Username string Username string
Password string Password string
} }
func GetDefaultAdminUser() *User {
return &User{
Username: "admin",
}
}

View file

@ -0,0 +1,18 @@
package data
import "threadule/backend/internal/data/models"
func (d *Data) CountUsers() (int64, error) {
var c int64
err := d.db.
Model(&models.User{}).
Count(&c).
Error
return c, err
}
func (d *Data) CreateUser(user *models.User) error {
return d.db.
Create(user).
Error
}

View file

@ -9,13 +9,3 @@ type Logic struct {
} }
var _ app.Logic = &Logic{} var _ app.Logic = &Logic{}
func Setup(ctx *app.Context) (app.Logic, error) {
logic := &Logic{
ctx: ctx,
}
logic.startScheduler()
return logic, nil
}

View file

@ -0,0 +1,30 @@
package logic
import (
"golang.org/x/crypto/bcrypt"
"math/rand"
"strings"
)
const defaultPasswordLength = 16
const defaultPasswordCharSet = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"0123456789" +
"=!$%&+#-_.,;:"
func (l *Logic) defaultPassword() string {
builder := strings.Builder{}
for i := 0; i < defaultPasswordLength; i++ {
builder.WriteRune(rune(defaultPasswordCharSet[rand.Intn(len(defaultPasswordCharSet))]))
}
return builder.String()
}
func (l *Logic) hashPassword(password string) (string, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(hashedPassword), err
}
func (l *Logic) checkPassword(hash, password string) bool {
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
}

View file

@ -0,0 +1,65 @@
package logic
import (
"fmt"
"threadule/backend/internal/app"
"threadule/backend/internal/data/models"
)
func Setup(ctx *app.Context) (app.Logic, error) {
logic := &Logic{
ctx: ctx,
}
logic.startScheduler()
err := logic.firstTimeSetup()
if err != nil {
return nil, err
}
return logic, nil
}
func (l *Logic) firstTimeSetup() error {
c, err := l.ctx.Data.CountUsers()
if err != nil {
l.ctx.Log.Errorf("error during first time setup check: %v", err)
return err
}
if c != 0 {
return nil
}
// no users -> probably first time setup
l.ctx.Log.Info("executing first time setup")
adminGroup := models.GetDefaultAdminGroup()
err = l.ctx.Data.AddGroup(adminGroup)
if err != nil {
l.ctx.Log.Errorf("couldn't create admin group: %v", err)
return err
}
adminUser := models.GetDefaultAdminUser()
adminUser.Groups = []*models.Group{adminGroup}
password := l.defaultPassword()
adminUser.Password, err = l.hashPassword(password)
if err != nil {
// if this fails we can't recover anyway
l.ctx.Log.Fatal(err)
}
err = l.ctx.Data.CreateUser(adminUser)
if err != nil {
l.ctx.Log.Errorf("couldn't create admin user: %v", err)
return err
}
fmt.Println("initial credentials:")
fmt.Printf("Username: %s\n", adminUser.Username)
fmt.Printf("Password: %s\n", password)
return nil
}