feat(security): library visibility enforced at the source; per-library series; annotation ownership; admin user creation
The family-lens hardening: library_visibility was only enforced where file bytes flowed (download/reader/OPDS) — metadata surfaces leaked hidden libraries to any authenticated account, and registration was open by default with no admin-side user creation. - Series are grouped PER LIBRARY now (same name in two libraries = two entries: manga adaptations / LN splits no longer merge); /api/series aggregates only the requester's VISIBLE libraries and /api/series/books REQUIRES a visible library_id (breaking contract; web + app clients updated in the same change). - /api/media-items, item detail, and the reader content endpoints (chapters/panels/outline/THUMBNAILS — page images leaked unguarded next to a gated GetPage) all enforce the visible set; resolveLibrary no longer honors a hand-typed library_id outside the visible set. - Annotation/note/bookmark mutations (update/delete/tombstone, sync LWW paths incl. conflict resolution) carry ownership predicates: another account's bare row id is a 404, not a silent delete. - Admin user creation: POST /api/auth/users (AdminMiddleware) + a Create User card in the admin Users UI — accounts stay creatable with self-service registration OFF, which is now the schema default (fresh installs; the first-admin bootstrap exception is unchanged). - Tests: series contract re-pinned (per-library, visibility 403s), new ownership + admin-create integration suites, OPDS all=1 test now passes an explicit page size (shared-DB growth made the fresh books fall off page 1), device-cap helper switched to the admin endpoint.
This commit is contained in:
1 parent
97fbdf19fb
commit
431fec05ad
60 files changed
+1573
-691
No files matched your search
+15
-15
@@ -38,7 +38,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(doc.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 11, Col: 21}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 11, Col: 21}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -60,7 +60,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var3).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 1, Col: 0}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -102,7 +102,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(section.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 50, Col: 30}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 50, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -133,7 +133,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var6 templ.SafeURL
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(item.URL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 56, Col: 29}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 56, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -146,7 +146,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(item.Icon)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 57, Col: 75}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 57, Col: 75}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -159,7 +159,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 58, Col: 30}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 58, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -187,7 +187,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var9 templ.SafeURL
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinURLErrs(item.URL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 65, Col: 29}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 65, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -200,7 +200,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(item.Icon)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 66, Col: 75}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 66, Col: 75}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -213,7 +213,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 67, Col: 30}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 67, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -257,7 +257,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var12 templ.SafeURL
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinURLErrs(crumb.URL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 82, Col: 28}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 82, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -270,7 +270,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(crumb.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 82, Col: 129}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 82, Col: 129}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -293,7 +293,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(doc.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 86, Col: 104}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 86, Col: 104}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -324,7 +324,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var15 templ.SafeURL
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinURLErrs("#" + item.Anchor)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 96, Col: 35}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 96, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -337,7 +337,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("margin-left: " + fmt.Sprintf("%drem", item.Level) + "; color: var(--text-secondary);")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 98, Col: 105}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 98, Col: 105}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -350,7 +350,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 100, Col: 23}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `docs.templ`, Line: 100, Col: 23}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
||||
Reference in new issue
Block a user