snapshot of https://github.com/traggo/server.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
build / build (push) Failing after 3s
Some checks failed
build / build (push) Failing after 3s
This commit is contained in:
28
user/create.go
Normal file
28
user/create.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// CreateUser creates a user.
|
||||
func (r *ResolverForUser) CreateUser(ctx context.Context, name string, pass string, admin bool) (*gqlmodel.User, error) {
|
||||
newUser := &model.User{
|
||||
Name: name,
|
||||
Pass: createPassword(pass, r.PassStrength),
|
||||
Admin: admin,
|
||||
}
|
||||
|
||||
if !r.DB.Where("name = ?", newUser.Name).Find(&model.User{}).RecordNotFound() {
|
||||
return nil, fmt.Errorf("user with name '%s' does already exist", newUser.Name)
|
||||
}
|
||||
|
||||
create := r.DB.Create(&newUser)
|
||||
gqlUser := &gqlmodel.User{}
|
||||
copier.Copy(gqlUser, newUser)
|
||||
return gqlUser, create.Error
|
||||
}
|
||||
52
user/create_test.go
Normal file
52
user/create_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
"github.com/traggo/server/test"
|
||||
)
|
||||
|
||||
func TestGQL_CreateUser_succeeds_addsUser(t *testing.T) {
|
||||
createPassword = fakePassword
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
user, err := resolver.CreateUser(context.Background(), "jmattheis", "unicorn", true)
|
||||
|
||||
require.Nil(t, err)
|
||||
expected := &gqlmodel.User{
|
||||
Name: "jmattheis",
|
||||
Admin: true,
|
||||
ID: 1,
|
||||
}
|
||||
require.Equal(t, expected, user)
|
||||
assertUserExist(t, db, model.User{
|
||||
Name: "jmattheis",
|
||||
Pass: unicornPW,
|
||||
ID: 1,
|
||||
Admin: true,
|
||||
})
|
||||
assertUserCount(t, db, 1)
|
||||
}
|
||||
|
||||
func TestGQL_CreateUser_fails_userAlreadyExists(t *testing.T) {
|
||||
createPassword = fakePassword
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
db.Create(&model.User{
|
||||
Name: "jmattheis",
|
||||
Pass: unicornPW,
|
||||
ID: 1,
|
||||
Admin: true,
|
||||
})
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
_, err := resolver.CreateUser(context.Background(), "jmattheis", "unicorn", true)
|
||||
require.EqualError(t, err, "user with name 'jmattheis' does already exist")
|
||||
assertUserCount(t, db, 1)
|
||||
}
|
||||
20
user/current.go
Normal file
20
user/current.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/traggo/server/auth"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
)
|
||||
|
||||
// CurrentUser returns the current user.
|
||||
func (r *ResolverForUser) CurrentUser(ctx context.Context) (*gqlmodel.User, error) {
|
||||
user := auth.GetUser(ctx)
|
||||
if user == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var result gqlmodel.User
|
||||
copier.Copy(&result, user)
|
||||
return &result, nil
|
||||
}
|
||||
39
user/current_test.go
Normal file
39
user/current_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/test"
|
||||
"github.com/traggo/server/test/fake"
|
||||
)
|
||||
|
||||
func TestGQL_CurrentUser_withUser(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB}
|
||||
result, err := resolver.CurrentUser(fake.UserWithPerm(2, true))
|
||||
|
||||
require.Nil(t, err)
|
||||
expected := &gqlmodel.User{
|
||||
ID: 2,
|
||||
Name: "fake",
|
||||
Admin: true,
|
||||
}
|
||||
|
||||
require.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestGQL_CurrentUser_noUser(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB}
|
||||
result, err := resolver.CurrentUser(context.Background())
|
||||
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, result)
|
||||
}
|
||||
18
user/get.go
Normal file
18
user/get.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// Users returns all users.
|
||||
func (r *ResolverForUser) Users(ctx context.Context) ([]*gqlmodel.User, error) {
|
||||
var users []model.User
|
||||
find := r.DB.Find(&users)
|
||||
var result []*gqlmodel.User
|
||||
copier.Copy(&result, &users)
|
||||
return result, find.Error
|
||||
}
|
||||
35
user/get_test.go
Normal file
35
user/get_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/test"
|
||||
)
|
||||
|
||||
func TestGQL_Users(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
db.NewUserPass(1, "jmattheis", unicornPW, true)
|
||||
db.NewUserPass(2, "broderpeters", ponyPW, false)
|
||||
|
||||
users, err := resolver.Users(context.Background())
|
||||
|
||||
require.Nil(t, err)
|
||||
expected := []*gqlmodel.User{
|
||||
{
|
||||
Name: "jmattheis",
|
||||
ID: 1,
|
||||
Admin: true,
|
||||
},
|
||||
{
|
||||
Name: "broderpeters",
|
||||
ID: 2,
|
||||
Admin: false,
|
||||
},
|
||||
}
|
||||
require.Equal(t, expected, users)
|
||||
}
|
||||
17
user/password/password.go
Normal file
17
user/password/password.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package password
|
||||
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
// CreatePassword returns a hashed version of the given password.
|
||||
func CreatePassword(pw string, strength int) []byte {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pw), strength)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hashedPassword
|
||||
}
|
||||
|
||||
// ComparePassword compares a hashed password with its possible plaintext equivalent.
|
||||
func ComparePassword(hashedPassword, password []byte) bool {
|
||||
return bcrypt.CompareHashAndPassword(hashedPassword, password) == nil
|
||||
}
|
||||
21
user/password/password_test.go
Normal file
21
user/password/password_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPasswordSuccess(t *testing.T) {
|
||||
password := CreatePassword("secret", 4)
|
||||
assert.Equal(t, true, ComparePassword(password, []byte("secret")))
|
||||
}
|
||||
|
||||
func TestPasswordFailure(t *testing.T) {
|
||||
password := CreatePassword("secret", 4)
|
||||
assert.Equal(t, false, ComparePassword(password, []byte("secretx")))
|
||||
}
|
||||
|
||||
func TestBCryptFailure(t *testing.T) {
|
||||
assert.Panics(t, func() { CreatePassword("secret", 12312) })
|
||||
}
|
||||
23
user/remove.go
Normal file
23
user/remove.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// RemoveUser removes a user
|
||||
func (r *ResolverForUser) RemoveUser(ctx context.Context, id int) (*gqlmodel.User, error) {
|
||||
user := model.User{ID: id}
|
||||
if r.DB.Find(&user).RecordNotFound() {
|
||||
return nil, fmt.Errorf("user with id %d does not exist", user.ID)
|
||||
}
|
||||
|
||||
remove := r.DB.Delete(&user)
|
||||
gqlUser := &gqlmodel.User{}
|
||||
copier.Copy(gqlUser, &user)
|
||||
return gqlUser, remove.Error
|
||||
}
|
||||
104
user/remove_test.go
Normal file
104
user/remove_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traggo/server/test"
|
||||
)
|
||||
|
||||
func TestGQL_RemoveUser_succeeds_removesUser(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
db.NewUserPass(1, "jmattheis", unicornPW, true)
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
_, err := resolver.RemoveUser(context.Background(), 1)
|
||||
|
||||
require.Nil(t, err)
|
||||
assertUserCount(t, db, 0)
|
||||
}
|
||||
|
||||
func TestGQL_RemoveUser_succeeds_removesDevices(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
user := db.User(1)
|
||||
user.NewDevice(2, "abc", "devicename")
|
||||
other := db.User(2)
|
||||
other.NewDevice(5, "abcx", "devicename")
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
_, err := resolver.RemoveUser(context.Background(), 1)
|
||||
|
||||
require.Nil(t, err)
|
||||
user.AssertHasDevice("devicename", false).AssertExists(false)
|
||||
other.AssertExists(true).AssertHasDevice("devicename", true)
|
||||
}
|
||||
|
||||
func TestGQL_RemoveUser_succeeds_removesTags(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
user := db.User(1)
|
||||
user.NewTagDefinition("oops")
|
||||
other := db.User(2)
|
||||
other.NewTagDefinition("oops")
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
_, err := resolver.RemoveUser(context.Background(), 1)
|
||||
|
||||
require.Nil(t, err)
|
||||
user.AssertExists(false).AssertHasTagDefinition("oops", false)
|
||||
other.AssertExists(true).AssertHasTagDefinition("oops", true)
|
||||
}
|
||||
|
||||
func TestGQL_RemoveUser_succeeds_removesTimeSpans(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
user := db.User(1)
|
||||
other := db.User(2)
|
||||
ts := user.TimeSpan("2019-06-11T18:00:00Z", "2019-06-11T18:00:00Z")
|
||||
ts.Tag("hello", "world")
|
||||
otherTs := other.TimeSpan("2019-06-11T18:00:00Z", "2019-06-11T18:00:00Z")
|
||||
otherTs.Tag("hello", "world")
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
_, err := resolver.RemoveUser(context.Background(), 1)
|
||||
|
||||
require.Nil(t, err)
|
||||
ts.AssertExists(false).AssertHasTag("hello", "world", false)
|
||||
otherTs.AssertExists(true).AssertHasTag("hello", "world", true)
|
||||
}
|
||||
|
||||
func TestGQL_RemoveUser_succeeds_removesDashboard(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
user := db.User(1)
|
||||
other := db.User(2)
|
||||
userDB := user.Dashboard("cool")
|
||||
userDB.Range("cool range")
|
||||
userDB.Entry("cool entry")
|
||||
|
||||
otherDB := other.Dashboard("cool")
|
||||
otherDB.Range("cool range")
|
||||
otherDB.Entry("cool entry")
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
_, err := resolver.RemoveUser(context.Background(), 1)
|
||||
|
||||
require.Nil(t, err)
|
||||
userDB.AssertExists(false).AssertHasRange("cool range", false).AssertHasEntry("cool entry", false)
|
||||
otherDB.AssertExists(true).AssertHasRange("cool range", true).AssertHasEntry("cool entry", true)
|
||||
}
|
||||
|
||||
func TestGQL_RemoveUser_fails_notExistingUser(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
db.NewUserPass(1, "jmattheis", unicornPW, true)
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
_, err := resolver.RemoveUser(context.Background(), 3)
|
||||
|
||||
require.EqualError(t, err, "user with id 3 does not exist")
|
||||
assertUserCount(t, db, 1)
|
||||
}
|
||||
30
user/update.go
Normal file
30
user/update.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
)
|
||||
|
||||
// UpdateUser updates a user.
|
||||
func (r *ResolverForUser) UpdateUser(ctx context.Context, id int, name string, pass *string, admin bool) (*gqlmodel.User, error) {
|
||||
user := new(model.User)
|
||||
if r.DB.Find(user, id).RecordNotFound() {
|
||||
return nil, fmt.Errorf("user with id %d does not exist", id)
|
||||
}
|
||||
|
||||
user.Name = name
|
||||
user.Admin = admin
|
||||
|
||||
if pass != nil {
|
||||
user.Pass = createPassword(*pass, r.PassStrength)
|
||||
}
|
||||
|
||||
update := r.DB.Save(user)
|
||||
gqlUser := &gqlmodel.User{}
|
||||
copier.Copy(gqlUser, user)
|
||||
return gqlUser, update.Error
|
||||
}
|
||||
77
user/update_test.go
Normal file
77
user/update_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traggo/server/generated/gqlmodel"
|
||||
"github.com/traggo/server/model"
|
||||
"github.com/traggo/server/test"
|
||||
)
|
||||
|
||||
func TestGQL_UpdateUser_succeeds_updatesUser(t *testing.T) {
|
||||
createPassword = fakePassword
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
db.NewUserPass(1, "jmattheis", unicornPW, true)
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
user, err := resolver.UpdateUser(context.Background(), 1, "broder", pointer("pony"), false)
|
||||
require.Nil(t, err)
|
||||
|
||||
expected := &gqlmodel.User{
|
||||
Name: "broder",
|
||||
ID: 1,
|
||||
Admin: false,
|
||||
}
|
||||
require.Equal(t, expected, user)
|
||||
assertUserCount(t, db, 1)
|
||||
assertUserExist(t, db, model.User{
|
||||
Name: "broder",
|
||||
ID: 1,
|
||||
Admin: false,
|
||||
Pass: ponyPW,
|
||||
})
|
||||
}
|
||||
|
||||
func TestGQL_UpdateUser_succeeds_preservesPassword(t *testing.T) {
|
||||
createPassword = fakePassword
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
db.NewUserPass(1, "jmattheis", unicornPW, true)
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
user, err := resolver.UpdateUser(context.Background(), 1, "broder", nil, false)
|
||||
require.Nil(t, err)
|
||||
|
||||
expected := &gqlmodel.User{
|
||||
Name: "broder",
|
||||
ID: 1,
|
||||
Admin: false,
|
||||
}
|
||||
require.Equal(t, expected, user)
|
||||
assertUserCount(t, db, 1)
|
||||
assertUserExist(t, db, model.User{
|
||||
Name: "broder",
|
||||
ID: 1,
|
||||
Admin: false,
|
||||
Pass: unicornPW,
|
||||
})
|
||||
}
|
||||
|
||||
func TestGQL_UpdateUser_fails_notExistingUser(t *testing.T) {
|
||||
createPassword = fakePassword
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
resolver := ResolverForUser{DB: db.DB, PassStrength: 4}
|
||||
_, err := resolver.UpdateUser(context.Background(), 1, "broder", nil, false)
|
||||
require.EqualError(t, err, "user with id 1 does not exist")
|
||||
|
||||
assertUserCount(t, db, 0)
|
||||
}
|
||||
|
||||
func pointer(s string) *string {
|
||||
return &s
|
||||
}
|
||||
14
user/userresolver.go
Normal file
14
user/userresolver.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/traggo/server/user/password"
|
||||
)
|
||||
|
||||
var createPassword = password.CreatePassword
|
||||
|
||||
// ResolverForUser resolves user specific things.
|
||||
type ResolverForUser struct {
|
||||
DB *gorm.DB
|
||||
PassStrength int
|
||||
}
|
||||
36
user/utils_test.go
Normal file
36
user/utils_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traggo/server/model"
|
||||
"github.com/traggo/server/test"
|
||||
)
|
||||
|
||||
var (
|
||||
ponyPW = []byte{1}
|
||||
unicornPW = []byte{2}
|
||||
fakePassword = func(pw string, strength int) []byte {
|
||||
if pw == "pony" {
|
||||
return ponyPW
|
||||
} else if pw == "unicorn" {
|
||||
return unicornPW
|
||||
}
|
||||
panic("unknown pw")
|
||||
}
|
||||
)
|
||||
|
||||
func assertUserExist(t *testing.T, db *test.Database, expected model.User) {
|
||||
foundUser := new(model.User)
|
||||
find := db.Find(foundUser, expected.ID)
|
||||
require.Nil(t, find.Error)
|
||||
require.NotNil(t, foundUser)
|
||||
require.Equal(t, expected, *foundUser)
|
||||
}
|
||||
|
||||
func assertUserCount(t *testing.T, db *test.Database, expected int) {
|
||||
count := new(int)
|
||||
db.Model(new(model.User)).Count(count)
|
||||
require.Equal(t, expected, *count)
|
||||
}
|
||||
Reference in New Issue
Block a user