mirror of
https://github.com/sigmasternchen/threadule
synced 2025-03-15 08:09:01 +00:00
create initial admin user at startup
This commit is contained in:
parent
59f5b51a9d
commit
0f8f528b76
11 changed files with 167 additions and 20 deletions
|
@ -31,6 +31,12 @@ func main() {
|
|||
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.Logic, err = logic.Setup(ctx)
|
||||
if err != nil {
|
||||
|
@ -38,12 +44,6 @@ func main() {
|
|||
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")
|
||||
handler := router.Setup(ctx)
|
||||
err = web.StartServer(ctx, handler)
|
||||
|
|
|
@ -10,6 +10,7 @@ require (
|
|||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.0-beta.3
|
||||
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
|
||||
gorm.io/driver/mysql v1.1.2
|
||||
gorm.io/gorm v1.21.13
|
||||
|
|
|
@ -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.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=
|
||||
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-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 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
@ -3,6 +3,11 @@ package app
|
|||
import "threadule/backend/internal/data/models"
|
||||
|
||||
type Data interface {
|
||||
CountUsers() (int64, error)
|
||||
CreateUser(user *models.User) error
|
||||
|
||||
AddGroup(group *models.Group) error
|
||||
|
||||
GetSession(id string) (*models.Session, error)
|
||||
UpdateSession(session *models.Session) error
|
||||
CleanupSessions() error
|
||||
|
|
9
backend/internal/data/group.go
Normal file
9
backend/internal/data/group.go
Normal 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
|
||||
}
|
|
@ -4,10 +4,25 @@ type Group struct {
|
|||
BaseModel
|
||||
Users []*User `gorm:"many2many:user_groups;"`
|
||||
|
||||
Name string
|
||||
DisplayName string
|
||||
|
||||
Name string
|
||||
DisplayName string
|
||||
AdminGroup bool
|
||||
ManageUsers bool
|
||||
ManageGroups bool
|
||||
LimitAccounts uint
|
||||
LimitThreads uint
|
||||
LimitTweets uint
|
||||
}
|
||||
|
||||
func GetDefaultAdminGroup() *Group {
|
||||
return &Group{
|
||||
Name: "admin",
|
||||
DisplayName: "Administrators",
|
||||
AdminGroup: true,
|
||||
ManageUsers: true,
|
||||
ManageGroups: true,
|
||||
LimitAccounts: 0,
|
||||
LimitThreads: 0,
|
||||
LimitTweets: 0,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,3 +7,9 @@ type User struct {
|
|||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func GetDefaultAdminUser() *User {
|
||||
return &User{
|
||||
Username: "admin",
|
||||
}
|
||||
}
|
||||
|
|
18
backend/internal/data/user.go
Normal file
18
backend/internal/data/user.go
Normal 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
|
||||
}
|
|
@ -9,13 +9,3 @@ type Logic struct {
|
|||
}
|
||||
|
||||
var _ app.Logic = &Logic{}
|
||||
|
||||
func Setup(ctx *app.Context) (app.Logic, error) {
|
||||
logic := &Logic{
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
logic.startScheduler()
|
||||
|
||||
return logic, nil
|
||||
}
|
||||
|
|
30
backend/internal/logic/password.go
Normal file
30
backend/internal/logic/password.go
Normal 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
|
||||
}
|
65
backend/internal/logic/setup.go
Normal file
65
backend/internal/logic/setup.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue