2024-01-02 18:36:09 +01:00
|
|
|
package group
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2024-04-09 16:51:29 +02:00
|
|
|
"errors"
|
|
|
|
"os"
|
2024-04-14 00:26:03 +02:00
|
|
|
"path/filepath"
|
2024-01-02 18:36:09 +01:00
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2024-04-12 01:27:49 +02:00
|
|
|
func TestMarshalUserDescription(t *testing.T) {
|
|
|
|
tests := []string{
|
|
|
|
`{}`,
|
|
|
|
`{"permissions":"present"}`,
|
|
|
|
`{"password":"secret"}`,
|
|
|
|
`{"password":"secret","permissions":"present"}`,
|
|
|
|
`{"password":"secret","permissions":["present"]}`,
|
|
|
|
`{"password":{"type":"wildcard"},"permissions":"observe"}`,
|
|
|
|
`{"password":{"type":"wildcard"},"permissions":[]}`,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
var u UserDescription
|
|
|
|
err := json.Unmarshal([]byte(test), &u)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Unmarshal %v: %v", t, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
v, err := json.Marshal(u)
|
|
|
|
if err != nil || string(v) != test {
|
|
|
|
t.Errorf("Marshal %v: got %v %v", test, string(v), err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestEmptyJSON(t *testing.T) {
|
|
|
|
type emptyTest struct {
|
|
|
|
value any
|
|
|
|
result string
|
|
|
|
name string
|
|
|
|
}
|
|
|
|
|
|
|
|
emptyTests := []emptyTest{
|
|
|
|
{Password{}, "{}", "password"},
|
|
|
|
{Permissions{}, "null", "permissions"},
|
|
|
|
{UserDescription{}, "{}", "user description"},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range emptyTests {
|
|
|
|
b, err := json.Marshal(v.value)
|
|
|
|
if err != nil || string(b) != v.result {
|
|
|
|
t.Errorf("Marshal empty %v: %#v %v, expected %#v",
|
|
|
|
v.name, string(b), err, v.result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-02 18:36:09 +01:00
|
|
|
var descJSON = `
|
|
|
|
{
|
|
|
|
"max-history-age": 10,
|
|
|
|
"allow-subgroups": true,
|
|
|
|
"users": {
|
|
|
|
"jch": {"password": "topsecret", "permissions": "op"},
|
|
|
|
"john": {"password": "secret", "permissions": "present"},
|
|
|
|
"james": {"password": "secret2", "permissions": "observe"},
|
|
|
|
"peter": {"password": "secret4"}
|
|
|
|
},
|
|
|
|
"fallback-users": [
|
|
|
|
{"permissions": "observe", "password": {"type":"wildcard"}}
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
func TestDescriptionJSON(t *testing.T) {
|
|
|
|
var d Description
|
|
|
|
err := json.Unmarshal([]byte(descJSON), &d)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unmarshal: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dd, err := json.Marshal(d)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("marshal: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var ddd Description
|
|
|
|
err = json.Unmarshal([]byte(dd), &ddd)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unmarshal: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(d, ddd) {
|
|
|
|
t.Errorf("Got %v, expected %v", ddd, d)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var obsoleteJSON = `
|
|
|
|
{
|
|
|
|
"op": [{"username": "jch","password": "topsecret"}],
|
|
|
|
"max-history-age": 10,
|
|
|
|
"allow-subgroups": true,
|
|
|
|
"presenter": [
|
|
|
|
{"username": "john", "password": "secret"}
|
|
|
|
],
|
|
|
|
"other": [
|
|
|
|
{"username": "james", "password": "secret2"},
|
|
|
|
{"username": "peter", "password": "secret4"},
|
|
|
|
{}
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
func TestUpgradeDescription(t *testing.T) {
|
|
|
|
var d1 Description
|
|
|
|
err := json.Unmarshal([]byte(descJSON), &d1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unmarshal: %v", err)
|
|
|
|
}
|
|
|
|
var d2 Description
|
|
|
|
err = json.Unmarshal([]byte(obsoleteJSON), &d2)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unmarshal: %v", err)
|
|
|
|
}
|
|
|
|
err = upgradeDescription(&d2)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("upgradeDescription: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if d2.Op != nil || d2.Presenter != nil || d2.Other != nil {
|
|
|
|
t.Errorf("legacy field is not nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(d1.Users) != len(d2.Users) {
|
|
|
|
t.Errorf("length not equal: %v != %v",
|
|
|
|
len(d1.Users), len(d2.Users))
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v1 := range d1.Users {
|
|
|
|
v2 := d2.Users[k]
|
|
|
|
if !reflect.DeepEqual(v1.Password, v2.Password) ||
|
|
|
|
!permissionsEqual(
|
|
|
|
v1.Permissions.Permissions(&d1),
|
|
|
|
v2.Permissions.Permissions(&d2),
|
|
|
|
) {
|
|
|
|
t.Errorf("%v not equal: %v != %v", k, v1, v2)
|
|
|
|
}
|
|
|
|
}
|
2024-04-12 00:47:51 +02:00
|
|
|
|
|
|
|
if len(d1.FallbackUsers) != len(d2.FallbackUsers) {
|
|
|
|
t.Errorf("length not equal: %v != %v",
|
|
|
|
len(d1.FallbackUsers), len(d2.FallbackUsers))
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v1 := range d1.FallbackUsers {
|
|
|
|
v2 := d2.FallbackUsers[k]
|
|
|
|
if !reflect.DeepEqual(v1.Password, v2.Password) ||
|
|
|
|
!permissionsEqual(
|
|
|
|
v1.Permissions.Permissions(&d1),
|
|
|
|
v2.Permissions.Permissions(&d2),
|
|
|
|
) {
|
|
|
|
t.Errorf("%v not equal: %v != %v", k, v1, v2)
|
|
|
|
}
|
|
|
|
}
|
2024-01-02 18:36:09 +01:00
|
|
|
}
|
2024-04-09 16:51:29 +02:00
|
|
|
|
2024-04-14 00:39:53 +02:00
|
|
|
func setupTest(dir, datadir string, writable bool) error {
|
|
|
|
Directory = dir
|
|
|
|
DataDirectory = datadir
|
|
|
|
f, err := os.Create(filepath.Join(datadir, "config.json"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
conf := `{}`
|
|
|
|
if writable {
|
|
|
|
conf = `{"writableGroups": true}`
|
|
|
|
}
|
|
|
|
_, err = f.WriteString(conf)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-04-09 16:51:29 +02:00
|
|
|
func TestNonWritableGroups(t *testing.T) {
|
2024-04-14 00:39:53 +02:00
|
|
|
err := setupTest(t.TempDir(), t.TempDir(), false)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("setupTest: %v", err)
|
|
|
|
}
|
2024-04-09 16:51:29 +02:00
|
|
|
|
2024-04-14 00:39:53 +02:00
|
|
|
_, err = GetDescription("test")
|
2024-04-09 16:51:29 +02:00
|
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
|
|
t.Errorf("GetDescription: got %#v, expected ErrNotExist", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = UpdateDescription("test", "", &Description{})
|
|
|
|
if !errors.Is(err, ErrDescriptionsNotWritable) {
|
2024-04-12 00:47:51 +02:00
|
|
|
t.Errorf("UpdateDescription: got %#v, "+
|
2024-04-09 16:51:29 +02:00
|
|
|
"expected ErrDescriptionsNotWritable", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWritableGroups(t *testing.T) {
|
2024-04-14 00:39:53 +02:00
|
|
|
err := setupTest(t.TempDir(), t.TempDir(), true)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("setupTest: %v", err)
|
2024-04-09 16:51:29 +02:00
|
|
|
}
|
|
|
|
|
2024-04-14 00:39:53 +02:00
|
|
|
_, err = GetDescription("test")
|
2024-04-09 16:51:29 +02:00
|
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
|
|
t.Errorf("GetDescription: got %v, expected ErrNotExist", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = UpdateDescription("test", "\"etag\"", &Description{})
|
|
|
|
if !errors.Is(err, ErrTagMismatch) {
|
|
|
|
t.Errorf("UpdateDescription: got %v, expected ErrTagMismatch",
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = UpdateDescription("test", "", &Description{})
|
|
|
|
if err != nil {
|
2024-04-14 00:39:53 +02:00
|
|
|
t.Fatalf("UpdateDescription: got %v", err)
|
2024-04-09 16:51:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_, err = GetDescription("test")
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("GetDescription: got %v", err)
|
|
|
|
}
|
|
|
|
|
2024-04-14 00:26:03 +02:00
|
|
|
fi, err := os.Stat(filepath.Join(Directory, "test.json"))
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Stat: %v", err)
|
|
|
|
}
|
|
|
|
if mode := fi.Mode(); mode != 0o600 {
|
|
|
|
t.Errorf("Mode is 0o%03o (expected 0o600)\n", mode)
|
|
|
|
}
|
|
|
|
|
2024-04-09 16:51:29 +02:00
|
|
|
desc, token, err := GetSanitisedDescription("test")
|
|
|
|
if err != nil || token == "" {
|
|
|
|
t.Errorf("GetSanitisedDescription: got %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
desc.DisplayName = "Test"
|
|
|
|
|
|
|
|
err = UpdateDescription("test", "\"badetag\"", desc)
|
|
|
|
if !errors.Is(err, ErrTagMismatch) {
|
|
|
|
t.Errorf("UpdateDescription: got %v, expected ErrTagMismatch",
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = UpdateDescription("test", token, desc)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("UpdateDescription: got %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
desc, err = GetDescription("test")
|
|
|
|
if err != nil || desc.DisplayName != "Test" {
|
|
|
|
t.Errorf("GetDescription: expected %v %v, got %v %v",
|
|
|
|
nil, "Test", err, desc.AllowAnonymous,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, _, err = GetSanitisedUser("test", "jch")
|
|
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
|
|
t.Errorf("GetSanitisedUser: got %v, expected ErrNotExist", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = UpdateUser("test", "jch", "", &UserDescription{
|
|
|
|
Permissions: Permissions{name: "observe"},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("UpdateUser: got %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
user, token, err := GetSanitisedUser("test", "jch")
|
|
|
|
if err != nil || token == "" || user.Permissions.name != "observe" {
|
|
|
|
t.Errorf("GetDescription: got %v %v, expected %v %v",
|
|
|
|
err, user.Permissions.name, nil, "observe",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = UpdateUser("test", "jch", "", &UserDescription{
|
|
|
|
Permissions: Permissions{name: "present"},
|
|
|
|
})
|
|
|
|
if !errors.Is(err, ErrTagMismatch) {
|
|
|
|
t.Errorf("UpdateDescription: got %v, expected ErrTagMismatch",
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = UpdateUser("test", "jch", token, &UserDescription{
|
|
|
|
Permissions: Permissions{name: "present"},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("UpdateUser: got %v", err)
|
|
|
|
}
|
|
|
|
|
2024-04-12 01:05:48 +02:00
|
|
|
pw := "pw"
|
2024-04-09 16:51:29 +02:00
|
|
|
err = SetUserPassword("test", "jch", Password{
|
2024-04-12 01:27:49 +02:00
|
|
|
Type: "plain",
|
2024-04-12 01:05:48 +02:00
|
|
|
Key: &pw,
|
2024-04-09 16:51:29 +02:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("SetUserPassword: got %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
desc, err = GetDescription("test")
|
2024-04-12 01:05:48 +02:00
|
|
|
if err != nil || *desc.Users["jch"].Password.Key != "pw" {
|
2024-04-09 16:51:29 +02:00
|
|
|
t.Errorf("GetDescription: got %v %v, expected %v %v",
|
|
|
|
err, desc.Users["jch"].Password.Key, nil, "pw",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|