Commit Graph
10 Commits
Author SHA1 Message Date
Chris LuandGitHub 4a2879abad admin: show a copyable S3 object URL in the bucket file browser (#10933)
* admin: offer copyable S3 object URLs in the bucket file browser

* admin: hide object urls when the bucket type lookup fails

* admin: ignore an s3.public_endpoint that is not an absolute http url

* mini: build the seeded s3 endpoint with JoinHostPort for ipv6

* admin: reject a query or fragment in s3.public_endpoint

* mini: drop the seeded s3 endpoint when a later run disables s3

* admin: reject userinfo and bare delimiters in s3.public_endpoint, redact the warning

* mini: pass its s3 endpoint as an admin option instead of mutating viper

* admin: keep the rejected s3.public_endpoint value out of the log
2026-08-24 19:29:01 -07:00
641fc8b031 admin: add visual iam policy editor (#10878)
* admin: add visual iam policy editor

Add a structured, tabbed editor (Editor / JSON) for creating and editing
IAM policies in the admin dashboard, alongside the existing raw-JSON
textarea:

- policies.templ: per-statement cards for Sid, Effect, Action, and
  Resource, with unmanaged fields (Principal, NotPrincipal, NotResource,
  Condition, or anything else) preserved verbatim in a per-statement
  "advanced fields" JSON box so nothing is lost on round-trip. Switching
  tabs commits and reparses in both directions. Restored the "Use Sample
  Policy" button, now filling both the structured editor and the JSON
  tab. The "Validate" button now calls the existing but previously
  unused POST /api/object-store/policies/validate endpoint instead of
  doing JS-only checks.
- Progressive Resource ARN autocomplete: suggests bucket names first,
  then once "bucket/" is typed, suggests bucket/* plus the bucket's
  direct subfolders, drilling down one path segment at a time as the
  user types further "/" characters.
- New GET /api/files/list-folders endpoint (file_browser_handlers.go)
  backing the folder autocomplete: wraps the existing file browser data
  function and returns just the subdirectory names as JSON, scoped to
  paths under /buckets.
- Action-name suggestions (datalist) for the Action field, sourced from
  the existing s3_constants.S3_ACTION_* constants plus new
  s3_constants.S3TABLES_ACTION_* constants (extracted from the s3tables
  operation dispatch switch) so the suggestion list can't drift from the
  strings the engines actually understand.
- policy_handlers.go: ValidatePolicy now accepts a statement with only
  NotResource set (previously required Resource), matching
  policy_engine.validateStatement and the fact the new editor makes such
  statements reachable from the UI.
- Tests: ValidatePolicy behavior, route registration for the policy API
  and the new list-folders endpoint, list-folders path scoping, and the
  action-suggestion list's shape.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* admin: fix XSS, cache poisoning, and cap overshoot in policy editor

Address code review findings on the IAM policy editor added in the
previous commit:

- policies.templ (displayPolicyDetails): escape every interpolated
  policy value (Sid, Effect, Action, Resource, policy name, and the raw
  JSON document) before assigning to innerHTML. Policy documents can
  come from other admins or an import, so an unescaped field could
  execute script when the "View" modal renders it.
- policies.templ (policyEditorStateToDoc): reject JSON arrays in a
  statement's "advanced fields" box, not just invalid JSON. `typeof []
  === 'object'` was true, so a JSON array was assigned to the statement;
  subsequent property assignments (Sid, Effect, ...) landed on the array
  object but JSON.stringify of an array only serializes numeric indices,
  silently dropping them.
- policies.templ (loadPolicyFolderNames): on a failed folder lookup,
  remove the cache entry instead of permanently caching the empty
  fallback, so a transient network/server error doesn't block retries
  for the rest of the page's lifetime.
- file_browser_handlers.go (ListFolders): stop appending directory
  names as soon as the running count reaches maxListFoldersEntries,
  instead of only checking the cap after a full page is processed,
  so the returned list never exceeds the configured cap.

Regenerated policies_templ.go with the already-stamped templ v0.3.1001
to keep the diff scoped to this file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* admin: stop policy editor from clobbering the active tab and dropping malformed advanced fields

Address two review findings on the IAM policy editor (Issue 3, stored-XSS
in displayPolicyDetails, was already fixed by the previous commit and is
unchanged here):

- createPolicy, updatePolicy, and validatePolicyDocument always committed
  the structured editor's (possibly stale) state into the JSON textarea
  before submitting, even when the user had just edited the JSON tab
  directly. That silently discarded the user's JSON edits and
  validated/saved the old structured-editor state instead, which could
  leave broader permissions in force than intended.

  Added commitPolicyActiveTab(which), which commits whichever tab is
  currently visible into the other side instead of unconditionally
  overwriting the JSON tab from the editor: if the JSON tab is active it
  parses that JSON back into the structured editor (without touching the
  textarea itself), otherwise it serializes the structured editor into
  the textarea as before. All three call sites, plus the JSON-tab
  "show.bs.tab" handler, now use this and abort with an alert if the
  currently active tab's content can't be committed.

- policyEditorStateToDoc silently continued with an empty object when a
  statement's "advanced fields" box held invalid JSON, so switching
  tabs, validating, or saving would drop Principal/NotResource/Condition
  from that statement without telling the user. It now throws (with the
  statement number and parse error) on invalid or non-object JSON there,
  and callers surface that via showAlert and abort instead of proceeding.

Regenerated policies_templ.go with the already-stamped templ v0.3.1001.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* admin: keep unmanaged top-level policy fields across editor tab switches

policyDocToEditorState only carried Version and Statement into editor
state, so any other top-level key (e.g. Id) present in the JSON tab was
silently rewritten away as soon as the user switched to the Editor tab
and back. Capture those keys in state.otherFields and merge them back in
policyEditorStateToDoc before Version and Statement are written, so the
two tabs stay faithful to each other and the editor never rewrites text
the user typed.

Note this is editor fidelity only: the admin API's
policy_engine.PolicyDocument carries just Version and Statement, and
DocumentJSON is never populated, so such fields are still discarded by
the server once a policy is saved. Making them survive a save would
require a backend change, which is out of scope here.

Regenerated policies_templ.go with the already-stamped templ v0.3.1001.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* admin: warn before a policy save discards unsupported top-level fields

The editor round-trips unmanaged top-level keys (e.g. Id) between the
Editor and JSON tabs, but the admin API's policy_engine.PolicyDocument
carries only Version and Statement, so the server drops them on save and
the user saw no indication.

Added confirmPolicyFieldDiscard(), called from createPolicy and
updatePolicy after the active tab is committed (so the field list is
accurate whichever tab is showing). It names the fields that will be
lost and lets the user confirm or cancel. Not wired into
validatePolicyDocument, which doesn't persist anything.

Chose the warning over the alternative of persisting these fields
through the backend: policy_engine.PolicyDocument is shared by the S3
bucket-policy engine and IAM evaluation, so extending it would change
the stored document shape for every policy in the codebase - far beyond
the scope of this editor.

Regenerated policies_templ.go with the already-stamped templ v0.3.1001.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* admin: reject malformed Effect and Resource/NotResource conflicts in policy editor

Two review findings on the IAM policy editor:

- policyDocToEditorState defaulted any non-"Deny" Effect (missing,
  misspelled, wrong case) to "Allow". A statement meant to be "Deny" with
  a typo like "deny" would silently become a permissive "Allow" instead
  of being rejected. It now throws on anything but an exact "Allow" or
  "Deny", naming the offending statement and value.
  commitPolicyTextareaToEditor catches this the same way it already
  catches invalid JSON: alert the user and keep the JSON tab active
  instead of switching to the Editor tab with wrong data.

- policyEditorStateToDoc could save a statement with both Resource (from
  the structured field) and NotResource (surviving in the "advanced
  fields" extras from before the user switched to using Resource) set at
  once - a contradictory combination neither the admin's ValidatePolicy
  handler nor policy_engine's evaluator rejected. When the structured
  Resource field is non-empty it now deletes any leftover NotResource
  from extras, consistent with the file's existing rule that structured
  fields take precedence over extras. Mirrored the existing
  Principal/NotPrincipal exclusivity check in
  weed/admin/handlers/policy_handlers.go's ValidatePolicy to reject the
  same combination server-side, since create/update perform no
  validation at all. Deliberately left policy_engine.validateStatement
  (used by the S3 bucket-policy PUT handler for every bucket policy in
  the product) unchanged - extending that shared validator is a larger,
  separate change outside this admin-editor fix's scope.

Added a handler test for the new Resource+NotResource rejection.
Regenerated policies_templ.go with the already-stamped templ v0.3.1001.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* admin: add NotResource support to the visual policy editor

Since Resource and NotResource are mutually exclusive (enforced by a
previous fix), NotResource could previously only be set through the raw
JSON in a statement's "advanced fields" box. Promote it to a first-class
mode of the structured editor:

- The static "Resources" label is now a Resource/NotResource dropdown;
  the same list of values underneath is reused for either key depending
  on the selected mode, with a short form-text explaining the semantics.
- NotResource is added to POLICY_STATEMENT_KNOWN_KEYS, since it's now a
  managed field like Resource rather than something that falls through
  to extras.
- policyDocToEditorState derives resourceMode from which key is present
  on load, and throws (same handling as the existing malformed-Effect
  case: alert, keep the JSON tab active) if a hand-edited document has
  both Resource and NotResource on one statement, since that can't be
  represented by the dropdown.
- policyEditorStateToDoc writes only the key matching the selected mode,
  replacing the previous one-directional "delete NotResource whenever
  Resource is set" fix with mode-driven logic that also deletes Resource
  when NotResource is selected.
- displayPolicyDetails (the read-only View modal) now shows the actual
  NotResource values with a distinct label instead of a static
  "(NotResource used instead)" placeholder.

No backend changes: the server-side "cannot specify both" check added
previously in policy_handlers.go's ValidatePolicy already covers this.

Regenerated policies_templ.go with the already-stamped templ v0.3.1001.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb2a51LciCsyY35sqDoGNe

* admin: reject non-object policy documents; catch Principal/NotPrincipal conflicts server-side

Two review findings:

- policyDocToEditorState treated a top-level JSON value that wasn't an
  object (null, or a bare string/number/boolean) as an empty statement
  list instead of failing explicitly. If the user typed e.g. "hello" or
  42 in the JSON tab and switched to the Editor tab, their input was
  silently discarded and replaced with an empty policy - the same class
  of "guess instead of reject" bug fixed for malformed Effect and
  Resource/NotResource conflicts previously. Added an explicit check
  that throws for null/scalar input, while leaving array and object
  document shapes accepted exactly as before.

- weed/admin/handlers/policy_handlers.go's ValidatePolicy checked the
  Resource/NotResource conflict by non-empty length
  (len(...Strings()) > 0), which misses a statement where Resource is
  explicitly present but an empty list (e.g. "Resource": []) alongside a
  non-empty NotResource. Switched that check to field presence (!= nil),
  matching how policy_engine's own validateStatement already treats
  Principal/NotPrincipal exclusivity. Also added the equivalent
  Principal/NotPrincipal presence check to this handler, which had none
  before - the advanced-fields box in the visual editor lets a user set
  both today, and nothing server-side caught it. The existing
  non-empty "Resource or NotResource is required" check is left as a
  length check, since an empty array shouldn't count as "provided".

Added test cases for both conflict checks in policy_handlers_test.go.
Regenerated policies_templ.go with the already-stamped templ v0.3.1001.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb2a51LciCsyY35sqDoGNe

* admin: add Principal/NotPrincipal support to the visual policy editor (v1, AWS-only)

Adds a first, deliberately narrow structured editor for a statement's
Principal/NotPrincipal, left out when NotResource support was added:

- A Principal/NotPrincipal mode dropdown mirrors the existing
  Resource/NotResource one (same mutual-exclusivity handling: the two
  fields can't be set at once, and switching modes reuses the same
  value list).
- A simple repeatable text-value list feeds a single {"AWS": [...]}
  object on save - always the AWS type, never the "bare" (untyped)
  SeaweedFS-extension shape. Per policy_engine's allowedPrincipalKeys,
  Service/Federated/CanonicalUser also parse successfully, but nothing
  in the S3 bucket-policy evaluation path ever sets a real caller's
  principal to a service name, an OIDC provider ARN, or a canonical
  user ID, so only AWS is functionally meaningful today - out of scope
  for this v1.
- On load, only the exact {"AWS": ...} single-key shape is unwrapped
  into the structured field and removed from "extras". Anything else
  (bare string/array, a different single type key, or several type
  keys at once) is left untouched in "extras" exactly as before, with a
  visible warning under the dropdown so the user knows a
  Principal/NotPrincipal exists but isn't shown there. Saving with the
  structured field left empty never touches whatever's already in
  extras, so a preserved complex form isn't silently dropped just
  because the user didn't touch this field.
- The read-only View modal now displays Principal/NotPrincipal for any
  shape (via a small generic summarizer), not just the AWS-simple one.
- Generalized the action/resource field-to-state-key mapping (used by
  commitPolicyEditorForm and the add/remove-item click handler) into a
  shared lookup table instead of stacking another ternary, now that a
  third field (principal) exists.

Regenerated policies_templ.go with the already-stamped templ v0.3.1001.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb2a51LciCsyY35sqDoGNe

* admin: support the bare "*" wildcard Principal in the visual editor

"Principal": "*" (and NotPrincipal: "*") is the standard AWS shorthand
for "everyone" and is common in real bucket policies, but the v1
Principal/NotPrincipal editor only recognized the {"AWS": ...} object
form, leaving a bare "*" statement's principal hidden in Advanced
fields.

parseSimpleAwsPrincipal now also accepts the bare string "*" as a
simple, structurally-editable value. On save, a principal value list
containing exactly ["*"] is written back as the bare "*" string
(matching the common convention) rather than wrapped as {"AWS": "*"};
anything else still wraps under AWS as before. Updated the field's
form-text hint accordingly.

Regenerated policies_templ.go with the already-stamped templ v0.3.1001.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb2a51LciCsyY35sqDoGNe

* admin: add Principal field autocomplete backed by users + IAM roles

Adds a datalist-backed autocomplete for the policy editor's Principal/
NotPrincipal text fields, sourced from a new API listing existing
identities:

- weed/admin/dash/principal_suggestions.go: AdminServer.GetPrincipalSuggestions
  combines S3 user ARNs (via the existing GetObjectStoreUsers +
  iam.UserArn) with IAM role ARNs (via integration.NewFilerRoleStore /
  ListRoles, reusing the exact same construction already used in
  iam_manager.go - no new dependency risk introduced). Role ARNs are
  reconstructed from the role name using SeaweedFS's default
  arn:aws:iam::role/<name> convention rather than fetching each role's
  stored definition, since this only backs a suggestion list. Role
  listing failures are logged and swallowed rather than failing the
  whole request - an incomplete suggestion list is fine, blocking
  policy editing over it is not. Service accounts are deliberately not
  listed separately: a service account's ARN is identical to its parent
  user's, already covered by the user list.
- weed/admin/handlers/policy_handlers.go: GetPrincipalSuggestions handler
  exposing this as {"principals": [...]}.
- Route registered at the API root (GET /api/principals) rather than
  under policyApi's "/object-store/policies" prefix, since that
  subrouter's existing "/{name}" GET route would shadow any
  single-segment GET route registered after it (the same class of
  gotcha previously seen with "/validate").
- weed/admin/view/app/policies.templ: a shared, lazily-fetched-once
  policyPrincipalSuggestions datalist (flat list - unlike the
  progressive per-folder Resource ARN autocomplete, users/roles aren't
  hierarchical), wired into policyListRowHtml for field:"principal" and
  populated on input/focus, with "*" always offered first.

Added tests for the new ARN-construction helper and route registration.
Regenerated policies_templ.go with the already-stamped templ v0.3.1001.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb2a51LciCsyY35sqDoGNe

* admin: fix fieldset/legend styling in the structured policy editor

Bootstrap's form reset stretches <legend> to the fieldset's full width
(float: left; width: 100%), which loses the native "notch in the
border" look and makes each section's label bar as wide as the card.

Add two scoped classes: .policy-stmt-fieldset (border, rounded
corners, spacing between sections) and .policy-stmt-legend (undoes the
float/width so the legend hugs its content, with a little padding).
Applied to the three per-statement sections (Actions,
Resource/NotResource, Principal/NotPrincipal), replacing the ad hoc
"border rounded" utility classes that were doubling up with the
fieldset's own border. Also gave the "Advanced fields" <details> a
small top margin to match the new spacing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb2a51LciCsyY35sqDoGNe

* admin: suggest bucket/* alongside the bucket itself in Resource autocomplete

At the bucket-name stage of the Resource field's progressive
autocomplete, only "arn:aws:s3:::bucket" was offered. Add
"arn:aws:s3:::bucket/*" right alongside it, since granting access to
everything in a bucket is the more common case and previously required
typing a "/" first to reach the folder-level "*" suggestion.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb2a51LciCsyY35sqDoGNe

* admin: keep an unparseable policy in the JSON tab instead of wiping it

editPolicy() built the structured state inside the fetch .then, so a
policy the editor cannot model threw into the sibling .catch, which
alerted and called hide(). Showing the alert at that moment left the
modal on screen with an empty editor and the document only in the JSON
tab, and Save Changes then serialized the empty state over the policy.

Reachable two ways, since neither create path rejects these: the admin
API never validates on create, so "Effect":"allow" is stored as typed,
and policy_engine.validateStatement lets Resource and NotResource sit
in the same statement.

Hand the document to the JSON tab instead, which is what that tab is
for, and mark the state so nothing serializes the placeholder over it.

* admin: validate a policy document before saving it

Validation was wired only to the Validate button, so nothing stopped a
document the server's own validator rejects from being stored. With the
structured editor supplying the boilerplate and required dropped from
the textarea, opening the modal, typing a name and clicking Create
Policy was enough to save a statement-less policy.

Share validatePolicyJSON with the two save paths and abort on failure.

* admin: bound the folder autocomplete listing

maxListFoldersEntries caps the folders collected, but nothing capped the
entries paged through to find them, so a bucket holding only flat object
keys - no subfolders to count - was walked to the end, 200 entries per
round trip, behind one keystroke. Measured against an in-process filer:
6 entries 0.5ms, 3k entries 7.7ms, 30k entries 53ms, all of it linear in
the directory rather than in the answer.

Cap the scan as well, and let GetFileBrowser take a prefix so the segment
the user is still typing is filtered by the filer instead of by paging.
The same 30k directory now answers in 0.6ms once a prefix is typed.

* admin: clean the path before scoping list-folders to /buckets

util.CleanWindowsPath only rewrites backslashes, so "/buckets/../etc"
walked straight past the prefix check the endpoint relies on for its
scope. Nothing leaked - filer paths are literal keys, so the traversal
resolved to nothing - but the check reads as a boundary and wasn't one,
and the test asserting it didn't cover the one input that would try.

validateAndCleanFilePath in the same file already does this.

* admin: stringify policy values before escaping them

escapeHtml calls text.replace directly, and the Sid, the per-item action
and resource inputs, and the View modal's Resource/NotResource all pass
values straight out of JSON.parse. A policy carrying "Sid": 5 or
"Action": [1] threw "text.replace is not a function" and took the render
with it. escapedJoin already coerced; use it everywhere and coerce the
editor state at the point it's built.

* admin: only show the NotResource hint in NotResource mode

The hint rendered unconditionally, so it sat under a selector reading
"Resource" telling the user the statement applies to everything except
what they'd listed. Redraw the card when the selector changes so it
follows the mode.

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Chris Lu <chris.lu@gmail.com>
2026-08-22 12:33:08 -07:00
db34e8b6fd feat(admin): prefer stored S3 Content-Type metadata over key-extension MIME inference (#9286)
* feat(admin): prefer stored filer mime in file browser and properties

* feat(mime): enhance MIME type registration and improve fallback logic

Co-authored-by: Copilot <copilot@github.com>

---------

Co-authored-by: baracudaz <baracudaz@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 10:21:03 -07:00
Chris LuGitHubgemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
e6ee293c17 Add table operations test (#8241)
* Add Trino blog operations test

* Update test/s3tables/catalog_trino/trino_blog_operations_test.go

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* feat: add table bucket path helpers and filer operations

- Add table object root and table location mapping directories
- Implement ensureDirectory, upsertFile, deleteEntryIfExists helpers
- Support table location bucket mapping for S3 access

* feat: manage table bucket object roots on creation/deletion

- Create .objects directory for table buckets on creation
- Clean up table object bucket paths on deletion
- Enable S3 operations on table bucket object roots

* feat: add table location mapping for Iceberg REST

- Track table location bucket mappings when tables are created/updated/deleted
- Enable location-based routing for S3 operations on table data

* feat: route S3 operations to table bucket object roots

- Route table-s3 bucket names to mapped table paths
- Route table buckets to object root directories
- Support table location bucket mapping lookup

* feat: emit table-s3 locations from Iceberg REST

- Generate unique table-s3 bucket names with UUID suffix
- Store table metadata under table bucket paths
- Return table-s3 locations for Trino compatibility

* fix: handle missing directories in S3 list operations

- Propagate ErrNotFound from ListEntries for non-existent directories
- Treat missing directories as empty results for list operations
- Fixes Trino non-empty location checks on table creation

* test: improve Trino CSV parsing for single-value results

- Sanitize Trino output to skip jline warnings
- Handle single-value CSV results without header rows
- Strip quotes from numeric values in tests

* refactor: use bucket path helpers throughout S3 API

- Replace direct bucket path operations with helper functions
- Leverage centralized table bucket routing logic
- Improve maintainability with consistent path resolution

* fix: add table bucket cache and improve filer error handling

- Cache table bucket lookups to reduce filer overhead on repeated checks
- Use filer_pb.CreateEntry and filer_pb.UpdateEntry helpers to check resp.Error
- Fix delete order in handler_bucket_get_list_delete: delete table object before directory
- Make location mapping errors best-effort: log and continue, don't fail API
- Update table location mappings to delete stale prior bucket mappings on update
- Add 1-second sleep before timestamp time travel query to ensure timestamps are in past
- Fix CSV parsing: examine all lines, not skip first; handle single-value rows

* fix: properly handle stale metadata location mapping cleanup

- Capture oldMetadataLocation before mutation in handleUpdateTable
- Update updateTableLocationMapping to accept both old and new locations
- Use passed-in oldMetadataLocation to detect location changes
- Delete stale mapping only when location actually changes
- Pass empty string for oldLocation in handleCreateTable (new tables have no prior mapping)
- Improve logging to show old -> new location transitions

* refactor: cleanup imports and cache design

- Remove unused 'sync' import from bucket_paths.go
- Use filer_pb.UpdateEntry helper in setExtendedAttribute and deleteExtendedAttribute for consistent error handling
- Add dedicated tableBucketCache map[string]bool to BucketRegistry instead of mixing concerns with metadataCache
- Improve cache separation: table buckets cache is now separate from bucket metadata cache

* fix: improve cache invalidation and add transient error handling

Cache invalidation (critical fix):
- Add tableLocationCache to BucketRegistry for location mapping lookups
- Clear tableBucketCache and tableLocationCache in RemoveBucketMetadata
- Prevents stale cache entries when buckets are deleted/recreated

Transient error handling:
- Only cache table bucket lookups when conclusive (found or ErrNotFound)
- Skip caching on transient errors (network, permission, etc)
- Prevents marking real table buckets as non-table due to transient failures

Performance optimization:
- Cache tableLocationDir results to avoid repeated filer RPCs on hot paths
- tableLocationDir now checks cache before making expensive filer lookups
- Cache stores empty string for 'not found' to avoid redundant lookups

Code clarity:
- Add comment to deleteDirectory explaining DeleteEntry response lacks Error field

* go fmt

* fix: mirror transient error handling in tableLocationDir and optimize bucketDir

Transient error handling:
- tableLocationDir now only caches definitive results
- Mirrors isTableBucket behavior to prevent treating transient errors as permanent misses
- Improves reliability on flaky systems or during recovery

Performance optimization:
- bucketDir avoids redundant isTableBucket call via bucketRoot
- Directly use s3a.option.BucketsPath for regular buckets
- Saves one cache lookup for every non-table bucket operation

* fix: revert bucketDir optimization to preserve bucketRoot logic

The optimization to directly use BucketsPath bypassed bucketRoot's logic
and caused issues with S3 list operations on delimiter+prefix cases.

Revert to using path.Join(s3a.bucketRoot(bucket), bucket) which properly
handles all bucket types and ensures consistent path resolution across
the codebase.

The slight performance cost of an extra cache lookup is worth the correctness
and consistency benefits.

* feat: move table buckets under /buckets

Add a table-bucket marker attribute, reuse bucket metadata cache for table bucket detection, and update list/validation/UI/test paths to treat table buckets as /buckets entries.

* Fix S3 Tables code review issues

- handler_bucket_create.go: Fix bucket existence check to properly validate
  entryResp.Entry before setting s3BucketExists flag (nil Entry should not
  indicate existing bucket)
- bucket_paths.go: Add clarifying comment to bucketRoot() explaining unified
  buckets root path for all bucket types
- file_browser_data.go: Optimize by extracting table bucket check early to
  avoid redundant WithFilerClient call

* Fix list prefix delimiter handling

* Handle list errors conservatively

* Fix Trino FOR TIMESTAMP query - use past timestamp

Iceberg requires the timestamp to be strictly in the past.
Use current_timestamp - interval '1' second instead of current_timestamp.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-07 13:27:47 -08:00
Chris LuGitHubgemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2ee6e4f391 mount: refresh and evict hot dir cache (#8174)
* mount: refresh and evict hot dir cache

* mount: guard dir update window and extend TTL

* mount: reuse timestamp for cache mark

* Apply suggestion from @gemini-code-assist[bot]

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* mount: make dir cache tuning configurable

* mount: dedupe dir update notices

* mount: restore invalidate-all cache helper

* mount: keep hot dir tuning constants

* mount: centralize cache state reset

* mount: mark refresh completion time

* mount: allow disabling idle eviction

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-31 13:46:37 -08:00
Chris LuandGitHub 79722bcf30 Add s3tables shell and admin UI (#8172)
* Add shared s3tables manager

* Add s3tables shell commands

* Add s3tables admin API

* Add s3tables admin UI

* Fix admin s3tables namespace create

* Rename table buckets menu

* Centralize s3tables tag validation

* Reuse s3tables manager in admin

* Extract s3tables list limit

* Add s3tables bucket ARN helper

* Remove write middleware from s3tables APIs

* Fix bucket link and policy hint

* Fix table tag parsing and nav link

* Disable namespace table link on invalid ARN

* Improve s3tables error decode

* Return flag parse errors for s3tables tag

* Accept query params for namespace create

* Bind namespace create form data

* Read s3tables JS data from DOM

* s3tables: allow empty region ARN

* shell: pass s3tables account id

* shell: require account for table buckets

* shell: use bucket name for namespaces

* shell: use bucket name for tables

* shell: use bucket name for tags

* admin: add table buckets links in file browser

* s3api: reuse s3tables tag validation

* admin: harden s3tables UI handlers

* fix admin list table buckets

* allow admin s3tables access

* validate s3tables bucket tags

* log s3tables bucket metadata errors

* rollback table bucket on owner failure

* show s3tables bucket owner

* add s3tables iam conditions

* Add s3tables user permissions UI

* Authorize s3tables using identity actions

* Add s3tables permissions to user modal

* Disambiguate bucket scope in user permissions

* Block table bucket names that match S3 buckets

* Pretty-print IAM identity JSON

* Include tags in s3tables permission context

* admin: refactor S3 Tables inline JavaScript into a separate file

* s3tables: extend IAM policy condition operators support

* shell: use LookupEntry wrapper for s3tables bucket conflict check

* admin: handle buildBucketPermissions validation in create/update flows
2026-01-30 22:57:05 -08:00
Chris LuandGitHub 6de6061ce9 admin: add cursor-based pagination to file browser (#7891)
* adjust menu items

* admin: add cursor-based pagination to file browser

- Implement cursor-based pagination using lastFileName parameter
- Add customizable page size selector (20/50/100/200 entries)
- Add compact pagination controls in header and footer
- Remove summary cards for cleaner UI
- Make directory names clickable to return to first page
- Support forward-only navigation (Next button)
- Preserve cursor position when changing page size
- Remove sorting to align with filer's storage order approach

* Update file_browser_templ.go

* admin: remove directory icons from breadcrumbs

* Update file_browser_templ.go

* admin: address PR comments

- Fix fragile EOF check: use io.EOF instead of string comparison
- Cap page size at 200 to prevent potential DoS
- Remove unused helper functions from template
- Use safer templ script for page size selector to prevent XSS

* admin: cleanup redundant first button

* Update file_browser_templ.go

* admin: remove entry counting logic

* admin: remove unused variables in file browser data

* admin: remove unused logic for FirstFileName and HasPrevPage

* admin: remove unused TotalEntries and TotalSize fields

* Update file_browser_data.go
2025-12-27 02:12:57 -08:00
9c784cf9e2 fix: use path to handle urls in weed admin file browser (#7858)
* fix: use path instead of filepath to handle urls in weed admin file browser

* test: add comprehensive tests for file browser path handling

- Test breadcrumb generation for various path scenarios
- Test path handling with forward slashes (URL compatibility)
- Test parent path calculation for Windows compatibility
- Test file extension handling using path.Ext
- Test bucket path detection logic

These tests verify that the switch from filepath to path package works
correctly and handles URLs properly across all platforms.

* refactor: simplify fullPath construction using path.Join

Replace verbose manual path construction with path.Join which:
- Handles trailing slashes automatically
- Is more concise and readable
- Is more robust for edge cases

* fix: normalize path in ShowFileBrowser and rename generateBreadcrumbs parameter

Critical fix:
- Add util.CleanWindowsPath() normalization to path parameter in ShowFileBrowser
  handler, matching the pattern used in other file operation handlers
  (lines 273, 464)
- This ensures Windows-style backslashes are converted to forward slashes
  before processing, fixing path handling issues on Windows

Consistency improvement:
- Rename path parameter to dir in generateBreadcrumbs function
- Aligns with parameter rename in GetFileBrowser for consistent naming
  throughout the file

* test: improve coverage for Windows path handling and production code behavior

Address reviewer feedback by enhancing test quality:

1. Improved test documentation:
   - Added clear comments explaining what each test validates
   - Clarified that some tests validate expected behavior vs production code
   - Documented the Windows path normalization flow

2. Enhanced actual production code testing:
   - TestGenerateBreadcrumbs: Calls actual production function
   - TestBreadcrumbPathFormatting: Validates production output format
   - TestDirectoryNavigation: Integration-style test for complete flow

3. Added new test functions for better coverage:
   - TestPathJoinHandlesEdgeCases: Verifies path.Join behavior
   - TestWindowsPathNormalizationBehavior: Documents expected normalization
   - TestDirectoryNavigation: Complete navigation flow test

4. Improved test organization:
   - Fixed duplicate field naming issues
   - Better test names for clarity
   - More comprehensive edge case coverage

These improvements ensure the fix for issue #7628 (Windows path handling)
is properly validated across the complete flow from handler to path logic.

* test: use actual util.CleanWindowsPath function in Windows path normalization test

Address reviewer feedback by testing the actual production function:
- Import util package for CleanWindowsPath
- Call the real util.CleanWindowsPath() instead of reimplementing logic
- Ensures test validates actual implementation, not just expected behavior
- Added more test cases for edge cases (simple path, deep nesting)

This change validates that the Windows path normalization in the
ShowFileBrowser handler (handlers/file_browser_handlers.go:64)
works correctly with the actual util.CleanWindowsPath function.

* style: fix indentation in TestPathJoinHandlesEdgeCases

Align t.Errorf statement inside the if block with proper indentation.
The error message now correctly aligns with the if block body,
maintaining consistent indentation throughout the function.

* test: restore backslash validation check in TestPathJoinHandlesEdgeCases

---------

Co-authored-by: Chris Lu <chris.lu@gmail.com>
2025-12-23 10:11:23 -08:00
Chris LuandGitHub 687a6a6c1d Admin UI: Add policies (#6968)
* add policies to UI, accessing filer directly

* view, edit policies

* add back buttons for "users" page

* remove unused

* fix ui dark mode when modal is closed

* bucket view details button

* fix browser buttons

* filer action button works

* clean up masters page

* fix volume servers action buttons

* fix collections page action button

* fix properties page

* more obvious

* fix directory creation file mode

* Update file_browser_handlers.go

* directory permission
2025-07-12 01:13:11 -07:00
chrislu 6b706f9ccd rename files
*_server.go - main server files
*_management.go - business logic
*_data.go - data structures and types
*_middleware.go - middleware logic
2025-07-02 00:04:46 -07:00