mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 22:37:28 +00:00
files: /me and /public are two separate per-user areas
Per feedback: /me is PRIVATE and the public folder must be its own top-level area, not nested under /me. - A member now has two sibling areas over SFTP: /me (private, <root>/files/users/<name>) and /public (their own public files, <root>/files/public/<name>), served anonymously at ~<name>/public. - Drop the global shared /public web route and the /me/public nesting. The anon surface only exposes ~name/public; /me has no anon route. - Both owned areas count toward the quota gauge. - Index publish hint, docs, and setup.sh updated to scp :/public/. files.<host> stays a file server; member sites remain on the BBS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a2d4817a8e
commit
f2bcb7e063
7 changed files with 135 additions and 135 deletions
|
|
@ -103,28 +103,21 @@ func TestSymlinkEscapeBlocked(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPublicWriteACL(t *testing.T) {
|
||||
svc, st, u := newTestService(t)
|
||||
func TestOwnPublicWritable(t *testing.T) {
|
||||
svc, _, u := newTestService(t)
|
||||
|
||||
// Default: members may write to the public area.
|
||||
// A member's /public is their own area (served at ~name/public) — they can
|
||||
// always write to it, and it resolves under <root>/public/<name>.
|
||||
sess, _ := svc.newSession(u)
|
||||
r := sftp.NewRequest("Mkdir", "/public/uploads")
|
||||
if err := sess.Filecmd(r); err != nil {
|
||||
t.Fatalf("public mkdir should succeed by default: %v", err)
|
||||
if err := sess.Filecmd(sftp.NewRequest("Mkdir", "/public/uploads")); err != nil {
|
||||
t.Fatalf("own /public mkdir should succeed: %v", err)
|
||||
}
|
||||
|
||||
// Turn public write off → writes denied, reads still fine.
|
||||
if err := svc.SetPublicWrite(false); err != nil {
|
||||
t.Fatal(err)
|
||||
res, err := sess.resolve("/public/uploads")
|
||||
if err != nil || !res.writable {
|
||||
t.Fatalf("own /public should resolve writable: %+v err=%v", res, err)
|
||||
}
|
||||
_ = st
|
||||
sess2, _ := svc.newSession(u)
|
||||
if err := sess2.Filecmd(sftp.NewRequest("Mkdir", "/public/more")); err != sftp.ErrSSHFxPermissionDenied {
|
||||
t.Errorf("public write should be denied when off, got %v", err)
|
||||
}
|
||||
res, err := sess2.resolve("/public/uploads")
|
||||
if err != nil || res.writable {
|
||||
t.Errorf("public should resolve read-only when write is off: %+v err=%v", res, err)
|
||||
if want := svc.userPub(u.Name); !within(want, res.real) && res.real != want {
|
||||
t.Errorf("/public resolved to %q, want under %q", res.real, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,21 +162,27 @@ func TestUsage(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestUsageCountsPublicHome(t *testing.T) {
|
||||
func TestUsageCountsOwnedAreas(t *testing.T) {
|
||||
svc, _, u := newTestService(t)
|
||||
if err := svc.ensurePublicHome(u.Name); err != nil {
|
||||
if err := svc.ensureWorkspace(u.Name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// A member's ~/public folder lives inside their private /me home, so both the
|
||||
// top-level private file and the public file count toward the quota gauge;
|
||||
// the shared /public area does not.
|
||||
if err := svc.ensureUserPub(u.Name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Both of the member's owned areas — private /me and their public /public
|
||||
// (<root>/public/<name>) — count toward the quota gauge. Another member's
|
||||
// public area does not.
|
||||
if err := os.WriteFile(filepath.Join(svc.privRoot(u.Name), "a.txt"), []byte(strings.Repeat("x", 512)), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(svc.publicHome(u.Name), "b.txt"), []byte(strings.Repeat("y", 256)), 0o644); err != nil {
|
||||
if err := os.WriteFile(filepath.Join(svc.userPub(u.Name), "b.txt"), []byte(strings.Repeat("y", 256)), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(svc.pubRoot(), "shared.txt"), []byte(strings.Repeat("z", 9999)), 0o644); err != nil {
|
||||
if err := os.MkdirAll(svc.userPub("someoneelse"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(svc.userPub("someoneelse"), "x.txt"), []byte(strings.Repeat("z", 9999)), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
usage, err := svc.Usage(u)
|
||||
|
|
@ -191,7 +190,7 @@ func TestUsageCountsPublicHome(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
if usage.Bytes != 768 {
|
||||
t.Errorf("usage = %d, want 768 (512 /me + 256 /me/public, shared /public excluded)", usage.Bytes)
|
||||
t.Errorf("usage = %d, want 768 (512 /me + 256 /public, other members excluded)", usage.Bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue