feat: organizations decide who sees whom; sharing only by owners
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m30s

- Server admins manage organizations (one per user); new admin section,
  organization on user creation and invites, setup creates the first one
- Sharing modes private / organizations / open, enforced centrally
  (CanSee, CanShareWith) for directory, all shares and groups; unknown and
  invisible targets give the same error
- Losing visibility (org change, mode change, leaving a group) prunes shares,
  group memberships and task assignments; dry run warns before tightening
- Only owners share; editors can no longer reshare lists or remove others'
  shares; legacy task_lists.team_id and old /organizations endpoints removed
  (FLAW-06, FLAW-10, FLAW-24)
- Schema version: outdated databases are wiped and recreated on start
- Fix setup sample data (createSampleData was ignored); setup and admin
  use a shared ToggleSwitch instead of checkboxes
- Docs: sharing-and-permissions rewritten, ui-patterns, REST, DB, flaws

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-28 22:00:33 +01:00
1 parent 73fad1c5db
commit 84395f3322
26 files changed
+1421 -609

No files matched your search

+25 -4
View File
@@ -15,6 +15,7 @@ func TestStoreLifecycle(t *testing.T) {
dbPath := filepath.Join(tmpDir, "test_mindsift.db")
store, err := NewStore(dbPath)
openSharing(t, store)
if err != nil {
t.Fatalf("NewStore failed: %v", err)
}
@@ -106,7 +107,7 @@ func TestStoreLifecycle(t *testing.T) {
}
// 6. Test Organizations & Teams
org, err := store.CreateOrganization(user.ID, "Mindsift Inc")
org, err := store.CreateOrganization("Mindsift Inc")
if err != nil || org.ID == "" {
t.Fatalf("CreateOrganization failed: %v", err)
}
@@ -138,7 +139,7 @@ func TestStoreLifecycle(t *testing.T) {
if err != nil || sysSettings.InstanceName == "" {
t.Fatalf("GetSystemSettings failed: %v", err)
}
sysSettings.SharingMode = "teams_only"
sysSettings.SharingMode = SharingOpen
err = store.UpdateSystemSettings(sysSettings)
if err != nil {
t.Fatalf("UpdateSystemSettings failed: %v", err)
@@ -179,7 +180,7 @@ func TestStoreLifecycle(t *testing.T) {
t.Fatalf("viewer must not delete tasks, got %v", err)
}
newTitle := "Umbenannt"
if _, err := store.UpdateTaskList(user2.ID, taskList.ID, &newTitle, nil, nil, nil, nil, nil); !errors.Is(err, ErrTaskListReadOnly) {
if _, err := store.UpdateTaskList(user2.ID, taskList.ID, &newTitle, nil, nil, nil, nil); !errors.Is(err, ErrTaskListReadOnly) {
t.Fatalf("viewer must not update task list, got %v", err)
}
if _, err := store.AddTaskListShare(taskList.ID, "user", user2.ID, "editor", user.ID); err != nil {
@@ -236,6 +237,7 @@ func TestPerUserDocPinning(t *testing.T) {
dbPath := filepath.Join(tmpDir, "pin_test.db")
store, err := NewStore(dbPath)
openSharing(t, store)
if err != nil {
t.Fatalf("NewStore failed: %v", err)
}
@@ -312,6 +314,7 @@ func TestEnsureDemoAccountsAndLoginLookup(t *testing.T) {
dbPath := filepath.Join(tmpDir, "demo_test.db")
store, err := NewStore(dbPath)
openSharing(t, store)
if err != nil {
t.Fatalf("NewStore failed: %v", err)
}
@@ -368,6 +371,7 @@ func TestFolderManagementAndDeletion(t *testing.T) {
dbPath := filepath.Join(tmpDir, "folder_test.db")
store, err := NewStore(dbPath)
openSharing(t, store)
if err != nil {
t.Fatalf("NewStore failed: %v", err)
}
@@ -441,6 +445,7 @@ func TestWorkspaceBlockHashtagsAndDetails(t *testing.T) {
dbPath := filepath.Join(tmpDir, "tags_test.db")
store, err := NewStore(dbPath)
openSharing(t, store)
if err != nil {
t.Fatalf("NewStore failed: %v", err)
}
@@ -542,6 +547,7 @@ func TestDatabaseFirstWorkspaceArchitecture(t *testing.T) {
dbPath := filepath.Join(tmpDir, "test_db_first.db")
store, err := NewStore(dbPath)
openSharing(t, store)
if err != nil {
t.Fatalf("NewStore failed: %v", err)
}
@@ -692,6 +698,7 @@ func TestBlockListAndDerivedData(t *testing.T) {
dbPath := filepath.Join(tmpDir, "tabula_rasa_test.db")
store, err := NewStore(dbPath)
openSharing(t, store)
if err != nil {
t.Fatalf("NewStore failed: %v", err)
}
@@ -752,4 +759,18 @@ func TestBlockListAndDerivedData(t *testing.T) {
}
}
// openSharing lets tests that are not about visibility share freely between their users.
func openSharing(t *testing.T, store *Store) {
t.Helper()
if store == nil {
return
}
sys, err := store.GetSystemSettings()
if err != nil {
t.Fatalf("GetSystemSettings: %v", err)
}
sys.SharingMode = SharingOpen
if err := store.UpdateSystemSettings(sys); err != nil {
t.Fatalf("UpdateSystemSettings: %v", err)
}
}