95 lines
3.4 KiB
Go
95 lines
3.4 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestCollectionShareFollowsEachSubjectAndKeepsItsScope(t *testing.T) {
|
|
f := newFixture(t)
|
|
f.write("私有/文章.md", "最新正文")
|
|
f.write("私有/秘密.md", "不能公开")
|
|
f.write("文件夹/内部.md", "内部正文")
|
|
sh := decode[shareView](t, f.do(f.admin, "POST", "/api/v1/shares", `{"paths":["文件夹/内部.md","私有/文章.md","文件夹"]}`, nil))
|
|
if len(sh.Items) != 2 || sh.Items[0].Path != "文件夹" {
|
|
t.Fatalf("未消除重叠:%+v", sh)
|
|
}
|
|
base := "/api/v1/public/shares/" + sh.Token
|
|
guest := newClient()
|
|
root := decode[struct {
|
|
Collection bool
|
|
Items []struct {
|
|
ID int
|
|
Title string
|
|
Exists bool
|
|
}
|
|
}](t, f.do(guest, "GET", base, "", nil))
|
|
if !root.Collection || len(root.Items) != 2 {
|
|
t.Fatalf("集合首页不符:%+v", root)
|
|
}
|
|
for _, item := range root.Items {
|
|
if strings.Contains(item.Title, "私有/") {
|
|
t.Fatal("公开了主体的父目录")
|
|
}
|
|
}
|
|
for _, endpoint := range []string{base + "?path=秘密.md", base + "?item=99", base + "?item=1&path=秘密.md", base + "/assets/../秘密.md?item=1", base + "/assets/秘密.md?item=1"} {
|
|
response := f.do(guest, "GET", endpoint, "", nil)
|
|
response.Body.Close()
|
|
if response.StatusCode == http.StatusOK {
|
|
t.Fatalf("越权访问成功:%s", endpoint)
|
|
}
|
|
}
|
|
if code := f.status(f.admin, "POST", "/api/v1/move", `{"from":"私有/文章.md","to":"移动后.md"}`, nil); code != 200 {
|
|
t.Fatalf("移动失败:%d", code)
|
|
}
|
|
content := decode[struct{ Content string }](t, f.do(guest, "GET", base+"?item=1", "", nil))
|
|
if content.Content != "最新正文" {
|
|
t.Fatalf("未跟随主体:%+v", content)
|
|
}
|
|
// 内部文件不是独立主体,移出文件夹后不能继续从该主体读取。
|
|
f.do(f.admin, "POST", "/api/v1/move", `{"from":"文件夹/内部.md","to":"移出.md"}`, nil).Body.Close()
|
|
if code := f.status(guest, "GET", base+"?item=0&path=内部.md", "", nil); code != 404 {
|
|
t.Fatalf("移出后仍公开:%d", code)
|
|
}
|
|
f.do(f.admin, "DELETE", "/api/v1/files/移动后.md", "", nil).Body.Close()
|
|
root = decode[struct {
|
|
Collection bool
|
|
Items []struct {
|
|
ID int
|
|
Title string
|
|
Exists bool
|
|
}
|
|
}](t, f.do(guest, "GET", base, "", nil))
|
|
if !root.Items[0].Exists || root.Items[1].Exists {
|
|
t.Fatalf("部分删除状态不符:%+v", root)
|
|
}
|
|
if code := f.status(guest, "GET", base+"?item=0", "", nil); code != 200 {
|
|
t.Fatalf("部分删除影响其余主体:%d", code)
|
|
}
|
|
}
|
|
|
|
func TestCollectionPasswordAssetsAndRevocation(t *testing.T) {
|
|
f := newFixture(t)
|
|
f.write("a.md", "文章")
|
|
f.write("目录/b.md", "文章二")
|
|
sh := decode[shareView](t, f.do(f.admin, "POST", "/api/v1/shares", `{"paths":["a.md","目录"],"password":"访问密码","expiresIn":86400}`, nil))
|
|
base := "/api/v1/public/shares/" + sh.Token
|
|
guest := newClient()
|
|
if code := f.status(guest, "GET", base, "", nil); code != 401 {
|
|
t.Fatal(code)
|
|
}
|
|
f.do(guest, "POST", base+"/unlock", `{"password":"访问密码"}`, nil).Body.Close()
|
|
if code := f.status(guest, "GET", base+"?item=1&path=b.md", "", nil); code != 200 {
|
|
t.Fatal(code)
|
|
}
|
|
if code := f.status(guest, "GET", base+"/assets/b.md?item=1", "", nil); code != 200 {
|
|
t.Fatal(code)
|
|
}
|
|
f.do(f.admin, "DELETE", "/api/v1/shares/"+sh.Token, "", nil).Body.Close()
|
|
if code := f.status(guest, "GET", fmt.Sprintf("%s?item=1", base), "", nil); code != 404 {
|
|
t.Fatal(code)
|
|
}
|
|
}
|