Commit Graph

8 Commits

Author SHA1 Message Date
Chris Lu
d1823d3784 fix(s3): include static identities in listing operations (#8903)
* fix(s3): include static identities in listing operations

Static identities loaded from -s3.config file were only stored in the
S3 API server's in-memory state. Listing operations (s3.configure shell
command, aws iam list-users) queried the credential manager which only
returned dynamic identities from the backend store.

Register static identities with the credential manager after loading
so they are included in LoadConfiguration and ListUsers results, and
filtered out before SaveConfiguration to avoid persisting them to the
dynamic store.

Fixes https://github.com/seaweedfs/seaweedfs/discussions/8896

* fix: avoid mutating caller's config and defensive copies

- SaveConfiguration: use shallow struct copy instead of mutating the
  caller's config.Identities field
- SetStaticIdentities: skip nil entries to avoid panics
- GetStaticIdentities: defensively copy PolicyNames slice to avoid
  aliasing the original

* fix: filter nil static identities and sync on config reload

- SetStaticIdentities: filter nil entries from the stored slice (not
  just from staticNames) to prevent panics in LoadConfiguration/ListUsers
- Extract updateCredentialManagerStaticIdentities helper and call it
  from both startup and the grace.OnReload handler so the credential
  manager's static snapshot stays current after config file reloads

* fix: add mutex for static identity fields and fix ListUsers for store callers

- Add sync.RWMutex to protect staticIdentities/staticNames against
  concurrent reads during config reload
- Revert CredentialManager.ListUsers to return only store users, since
  internal callers (e.g. DeletePolicy) look up each user in the store
  and fail on non-existent static entries
- Merge static usernames in the filer gRPC ListUsers handler instead,
  via the new GetStaticUsernames method
- Fix CI: TestIAMPolicyManagement/managed_policy_crud_lifecycle was
  failing because DeletePolicy iterated static users that don't exist
  in the store

* fix: show static identities in admin UI and weed shell

The admin UI and weed shell s3.configure command query the filer's
credential manager via gRPC, which is a separate instance from the S3
server's credential manager. Static identities were only registered
on the S3 server's credential manager, so they never appeared in the
filer's responses.

- Add CredentialManager.LoadS3ConfigFile to parse a static S3 config
  file and register its identities
- Add FilerOptions.s3ConfigFile so the filer can load the same static
  config that the S3 server uses
- Wire s3ConfigFile through in weed mini and weed server modes
- Merge static usernames in filer gRPC ListUsers handler
- Add CredentialManager.GetStaticUsernames helper
- Add sync.RWMutex to protect concurrent access to static identity
  fields
- Avoid importing weed/filer from weed/credential (which pulled in
  filer store init() registrations and broke test isolation)
- Add docker/compose/s3_static_users_example.json

* fix(admin): make static users read-only in admin UI

Static users loaded from the -s3.config file should not be editable
or deletable through the admin UI since they are managed via the
config file.

- Add IsStatic field to ObjectStoreUser, set from credential manager
- Hide edit, delete, and access key buttons for static users in the
  users table template
- Show a "static" badge next to static user names
- Return 403 Forbidden from UpdateUser and DeleteUser API handlers
  when the target user is a static identity

* fix(admin): show details for static users

GetObjectStoreUserDetails called credentialManager.GetUser which only
queries the dynamic store. For static users this returned
ErrUserNotFound. Fall back to GetStaticIdentity when the store lookup
fails.

* fix(admin): load static S3 identities in admin server

The admin server has its own credential manager (gRPC store) which is
a separate instance from the S3 server's and filer's. It had no static
identity data, so IsStaticIdentity returned false (edit/delete buttons
shown) and GetStaticIdentity returned nil (details page failed).

Pass the -s3.config file path through to the admin server and call
LoadS3ConfigFile on its credential manager, matching the approach
used for the filer.

* fix: use protobuf is_static field instead of passing config file path

The previous approach passed -s3.config file path to every component
(filer, admin). This is wrong because the admin server should not need
to know about S3 config files.

Instead, add an is_static field to the Identity protobuf message.
The field is set when static identities are serialized (in
GetStaticIdentities and LoadS3ConfigFile). Any gRPC client that loads
configuration via GetConfiguration automatically sees which identities
are static, without needing the config file.

- Add is_static field (tag 8) to iam_pb.Identity proto message
- Set IsStatic=true in GetStaticIdentities and LoadS3ConfigFile
- Admin GetObjectStoreUsers reads identity.IsStatic from proto
- Admin IsStaticUser helper loads config via gRPC to check the flag
- Filer GetUser gRPC handler falls back to GetStaticIdentity
- Remove s3ConfigFile from AdminOptions and NewAdminServer signature
2026-04-03 20:01:28 -07:00
Chris Lu
67a551fd62 admin UI: add anonymous user creation checkbox (#8773)
Add an "Anonymous" checkbox next to the username field in the Create User
modal. When checked, the username is set to "anonymous" and the credential
generation checkbox is disabled since anonymous users do not need keys.

The checkbox is only shown when no anonymous user exists yet. The
manage-access-keys button in the users table is hidden for the anonymous
user.
2026-03-25 21:24:10 -07:00
Chris Lu
6dab90472b admin: fix access key creation UX (#8579)
* admin: remove misleading "secret key only shown once" warning

The access key details modal already allows viewing both the access key
and secret key at any time, so the warning about the secret key only
being displayed once is incorrect and misleading.

* admin: allow specifying custom access key and secret key

Add optional access_key and secret_key fields to the create access key
API. When provided, the specified keys are used instead of generating
random ones. The UI now shows a form with optional fields when creating
a new key, with a note that leaving them blank auto-generates keys.

* admin: check access key uniqueness before creating

Access keys must be globally unique across all users since S3 auth
looks them up in a single global map. Add an explicit check using
GetUserByAccessKey before creating, so the user gets a clear error
("access key is already in use") rather than a generic store error.

* Update object_store_users_templ.go

* admin: address review feedback for access key creation

Handler:
- Use decodeJSONBody/newJSONMaxReader instead of raw json.Decode to
  enforce request size limits and handle malformed JSON properly
- Return 409 Conflict for duplicate access keys, 400 Bad Request for
  validation errors, instead of generic 500

Backend:
- Validate access key length (4-128 chars) and secret key length
  (8-128 chars) when user-provided

Frontend:
- Extract resetCreateKeyForm() helper to avoid duplicated cleanup logic
- Wire resetCreateKeyForm to accessKeysModal hidden.bs.modal event so
  form state is always cleared when modal is dismissed
- Change secret key input to type="password" with a visibility toggle

* admin: guard against nil request and handle GetUserByAccessKey errors

- Add nil check for the CreateAccessKeyRequest pointer before
  dereferencing, defaulting to an empty request (auto-generate both
  keys).
- Handle non-"not found" errors from GetUserByAccessKey explicitly
  instead of silently proceeding, so store errors (e.g. db connection
  failures) surface rather than being swallowed.

* Update object_store_users_templ.go

* admin: fix access key uniqueness check with gRPC store

GetUserByAccessKey returns a gRPC NotFound status error (not the
sentinel credential.ErrAccessKeyNotFound) when using the gRPC store,
causing the uniqueness check to fail with a spurious error.

Treat the lookup as best-effort: only reject when a user is found
(err == nil). Any error (not-found via any store, connectivity issues)
falls through to the store's own CreateAccessKey which enforces
uniqueness definitively.

* admin: fix error handling and input validation for access key creation

Backend:
- Remove access key value from the duplicate-key error message to avoid
  logging the caller-supplied identifier.

Handler:
- Handle empty POST body (io.EOF) as a valid request that auto-generates
  both keys, instead of rejecting it as malformed JSON.
- Return 404 for "not found" errors (e.g. non-existent user) instead of
  collapsing them into a 500.

Frontend:
- Add minlength/maxlength attributes matching backend constraints
  (access key 4-128, secret key 8-128).
- Call reportValidity() before submitting so invalid lengths are caught
  client-side without a round trip.

* admin: use sentinel errors and fix GetUserByAccessKey error handling

Backend (user_management.go):
- Define sentinel errors (ErrAccessKeyInUse, ErrUserNotFound,
  ErrInvalidInput) and wrap them in returned errors so callers can use
  errors.Is.
- Handle GetUserByAccessKey errors properly: check the sentinel
  credential.ErrAccessKeyNotFound first, then fall back to string
  matching for stores (gRPC) that return non-sentinel not-found errors.
  Surface unexpected errors instead of silently proceeding.

Handler (user_handlers.go):
- Replace fragile strings.Contains error matching with errors.Is
  against the new dash sentinels.

Frontend (object_store_users.templ):
- Add double-submit guard (isCreatingKey flag + button disabling) to
  prevent duplicate access key creation requests.
2026-03-09 14:03:41 -07:00
Chris Lu
8d59ef41d5 Admin UI: replace gin with mux (#8420)
* Replace admin gin router with mux

* Update layout_templ.go

* Harden admin handlers

* Add login CSRF handling

* Fix filer copy naming conflict

* address comments

* address comments
2026-02-23 19:11:17 -08:00
Chris Lu
6bc5a64a98 Add access key status management to Admin UI (#8050)
* Add access key status management to Admin UI

- Add Status field to AccessKeyInfo struct
- Implement UpdateAccessKeyStatus API endpoint
- Add status dropdown in access keys modal
- Fix modal backdrop issue by using refreshAccessKeysList helper
- Status can be toggled between Active and Inactive

* Replace magic strings with constants for access key status

- Define AccessKeyStatusActive and AccessKeyStatusInactive constants in admin_data.go
- Define STATUS_ACTIVE and STATUS_INACTIVE constants in JavaScript
- Replace all hardcoded 'Active' and 'Inactive' strings with constants
- Update error messages to use constants for consistency

* Remove duplicate manageAccessKeys function definition

* Add security improvements to access key status management

- Add status validation in UpdateAccessKeyStatus to prevent invalid values
- Fix XSS vulnerability by replacing inline onchange with data attributes
- Add delegated event listener for status select changes
- Add URL encoding to API request path segments
2026-01-17 18:18:32 -08:00
Chris Lu
e67973dc53 Support Policy Attachment for Object Store Users (#7981)
* Implement Policy Attachment support for Object Store Users

- Added policy_names field to iam.proto and regenerated protos.
- Updated S3 API and IAM integration to support direct policy evaluation for users.
- Enhanced Admin UI to allow attaching policies to users via modals.
- Renamed 'policies' to 'policy_names' to clarify that it stores identifiers.
- Fixed syntax error in user_management.go.

* Fix policy dropdown not populating

The API returns {policies: [...]} but JavaScript was treating response as direct array.
Updated loadPolicies() to correctly access data.policies property.

* Add null safety checks for policy dropdowns

Added checks to prevent "undefined" errors when:
- Policy select elements don't exist
- Policy dropdowns haven't loaded yet
- User is being edited before policies are loaded

* Fix policy dropdown by using correct JSON field name

JSON response has lowercase 'name' field but JavaScript was accessing 'Name'.
Changed policy.Name to policy.name to match the IAMPolicy JSON structure.

* Fix policy names not being saved on user update

Changed condition from len(req.PolicyNames) > 0 to req.PolicyNames != nil
to ensure policy names are always updated when present in the request,
even if it's an empty array (to allow clearing policies).

* Add debug logging for policy names update flow

Added console.log in frontend and glog in backend to trace
policy_names data through the update process.

* Temporarily disable auto-reload for debugging

Commented out window.location.reload() so console logs are visible
when updating a user.

* Add detailed debug logging and alert for policy selection

Added console.log for each step and an alert to show policy_names value
to help diagnose why it's not being included in the request.

* Regenerate templ files for object_store_users

Ran templ generate to ensure _templ.go files are up to date with
the latest .templ changes including debug logging.

* Remove debug logging and restore normal functionality

Cleaned up temporary debug code (console.log and alert statements)
and re-enabled automatic page reload after user update.

* Add step-by-step alert debugging for policy update

Added 5 alert checkpoints to trace policy data through the update flow:
1. Check if policiesSelect element exists
2. Show selected policy values
3. Show userData.policy_names
4. Show full request body
5. Confirm server response

Temporarily disabled auto-reload to see alerts.

* Add version check alert on page load

Added alert on DOMContentLoaded to verify new JavaScript is being executed
and not cached by the browser.

* Compile templates using make

Ran make to compile all template files and install the weed binary.

* Add button click detection and make handleUpdateUser global

- Added inline alert on button click to verify click is detected
- Made handleUpdateUser a window-level function to ensure it's accessible
- Added alert at start of handleUpdateUser function

* Fix handleUpdateUser scope issue - remove duplicate definition

Removed duplicate function definition that was inside DOMContentLoaded.
Now handleUpdateUser is defined only once in global scope (line 383)
making it accessible when button onclick fires.

* Remove all duplicate handleUpdateUser definitions

Now handleUpdateUser is defined only once at the very top of the script
block (line 352), before DOMContentLoaded, ensuring it's available when
the button onclick fires.

* Add function existence check and error catching

Added alerts to check if handleUpdateUser is defined and wrapped
the function call in try-catch to capture any JavaScript errors.
Also added console.log statements to verify function definition.

* Simplify handleUpdateUser to non-async for testing

Removed async/await and added early return to test if function
can be called at all. This will help identify if async is causing
the issue.

* Add cache-control headers to prevent browser caching

Added no-cache headers to ShowObjectStoreUsers handler to prevent
aggressive browser caching of inline JavaScript in the HTML page.

* Fix syntax error - make handleUpdateUser async

Changed function back to async to fix 'await is only valid in async functions' error.
The cache-control headers are working - browser is now loading new code.

* Update version check to v3 to verify cache busting

Changed version alert to 'v3 - WITH EARLY RETURN' to confirm
the new code with early return statement is being loaded.

* Remove all debug code - clean implementation

Removed all alerts, console.logs, and test code.
Implemented clean policy update functionality with proper error handling.

* Add ETag header for cache-busting and update walkthrough

* Fix policy pre-selection in Edit User modal

- Updated admin.js editUser function to pre-select policies
- Root cause: duplicate editUser in admin.js overwrote inline version
- Added policy pre-selection logic to match inline template
- Verified working in browser: policies now pre-select correctly

* Fix policy persistence in handleUpdateUser

- Added policy_names field to userData payload in handleUpdateUser
- Policies were being lost because handleUpdateUser only sent email and actions
- Now collects selected policies from editPolicies dropdown
- Verified working: policies persist correctly across updates

* Fix XSS vulnerability in access keys display

- Escape HTML in access key display using escapeHtml utility
- Replace inline onclick handlers with data attributes
- Add event delegation for delete access key buttons
- Prevents script injection via malicious access key values

* Fix additional XSS vulnerabilities in user details display

- Escape HTML in actions badges (line 626)
- Escape HTML in policy_names badges (line 636)
- Prevents script injection via malicious action or policy names

* Fix XSS vulnerability in loadPolicies function

- Replace innerHTML string concatenation with DOM API
- Use createElement and textContent for safe policy name insertion
- Prevents script injection via malicious policy names
- Apply same pattern to both create and edit select elements

* Remove debug logging from UpdateObjectStoreUser

- Removed glog.V(0) debug statements
- Clean up temporary debugging code before production

* Remove duplicate handleUpdateUser function

- Removed inline handleUpdateUser that duplicated admin.js logic
- Removed debug console.log statement
- admin.js version is now the single source of truth
- Eliminates maintenance burden of keeping two versions in sync

* Refine user management and address code review feedback

- Preserve PolicyNames in UpdateUserPolicies
- Allow clearing actions in UpdateObjectStoreUser by checking for nil
- Remove version comment from object_store_users.templ
- Refactor loadPolicies for DRYness using cloneNode while keeping DOM API security

* IAM Authorization for Static Access Keys

* verified XSS Fixes in Templates

* fix div
2026-01-06 21:53:28 -08:00
Chris Lu
b6d99f1c9e Admin: Add Service Account Management UI (#7902)
* admin: add Service Account management UI

Add admin UI for managing service accounts:

New files:
- handlers/service_account_handlers.go - HTTP handlers
- dash/service_account_management.go - CRUD operations
- view/app/service_accounts.templ - UI template

Changes:
- dash/types.go - Add ServiceAccount and related types
- handlers/admin_handlers.go - Register routes and handlers
- view/layout/layout.templ - Add sidebar navigation link

Service accounts are stored as special identities with "sa:" prefix
in their name, using ABIA access key prefix. They can be created,
listed, enabled/disabled, and deleted through the admin UI.

Features:
- Create service accounts linked to parent users
- View and manage service account status
- Delete service accounts
- Service accounts inherit parent user permissions

Note: STS configuration is read-only (configured via JSON file).
Full STS integration requires changes from PR #7901.

* admin: use dropdown for parent user selection

Change the Parent User field from text input to dropdown when
creating a service account. The dropdown is populated with all
existing Object Store users.

Changes:
- Add AvailableUsers field to ServiceAccountsData type
- Populate available users in getServiceAccountsData handler
- Update template to use <select> element with user options

* admin: show secret access key on service account creation

Display both access key and secret access key when creating a
service account, with proper AWS CLI usage instructions.

Changes:
- Add SecretAccessKey field to ServiceAccount type (only populated on creation)
- Return secret key from CreateServiceAccount
- Add credentials modal with copy-to-clipboard buttons
- Show AWS CLI usage example with actual credentials
- Modal is non-dismissible until user confirms they saved credentials

The secret key is only shown once during creation for security.
After creation, only the access key ID is visible in the list.

* admin: address code review comments for service account management

- Persist creation dates in identity actions (createdAt:timestamp)
- Replace magic number slicing with len(accessKeyPrefix)
- Add bounds checking after strings.SplitN
- Use accessKeyPrefix constant instead of hardcoded "ABIA"

Creation dates are now stored as actions (e.g., "createdAt:1735473600")
and will persist across restarts. Helper functions getCreationDate()
and setCreationDate() manage the timestamp storage.

Addresses review comments from gemini-code-assist[bot] and coderabbitai[bot]

* admin: fix XSS vulnerabilities in service account details

Replace innerHTML with template literals with safe DOM creation.
The createSADetailsContent function now uses createElement and
textContent to prevent XSS attacks from malicious service account
data (id, description, parent_user, etc.).

Also added try-catch for date parsing to prevent exceptions on
malformed input.

Addresses security review comments from coderabbitai[bot]

* admin: add context.Context to service account management methods

Addressed PR #7902 review feedback:

1. All service account management methods now accept context.Context as
   first parameter to enable cancellation, deadlines, and tracing
2. Removed all context.Background() calls
3. Updated handlers to pass c.Request.Context() from HTTP requests

Methods updated:
- GetServiceAccounts
- GetServiceAccountDetails
- CreateServiceAccount
- UpdateServiceAccount
- DeleteServiceAccount
- GetServiceAccountByAccessKey

Note: Creation date persistence was already implemented using the
createdAt:<timestamp> action pattern as suggested in the review.

* admin: fix render flow to prevent partial HTML writes

Fixed ShowServiceAccounts handler to render template to an in-memory
buffer first before writing to the response. This prevents partial HTML
writes followed by JSON error responses, which would result in invalid
mixed content.

Changes:
- Render to bytes.Buffer first
- Only write to c.Writer if render succeeds
- Use c.AbortWithStatus on error instead of attempting JSON response
- Prevents any additional headers/body writes after partial write

* admin: fix error handling, date validation, and event parameters

Addressed multiple code review issues:

1. Proper 404 vs 500 error handling:
   - Added ErrServiceAccountNotFound sentinel error
   - GetServiceAccountDetails now wraps errors with sentinel
   - Handler uses errors.Is() to distinguish not-found from internal errors
   - Returns 404 only for missing resources, 500 for other errors
   - Logs internal errors before returning 500

2. Date validation in JavaScript:
   - Validate expiration date before using it
   - Check !isNaN(date.getTime()) to ensure valid date
   - Return validation error if date is invalid
   - Prevents invalid Date construction

3. Event parameter handling:
   - copyToClipboard now accepts event parameter
   - Updated onclick attributes to pass event object
   - Prevents reliance on window.event
   - More explicit and reliable event handling

* admin: replace deprecated execCommand with Clipboard API

Replaced deprecated document.execCommand('copy') with modern
navigator.clipboard.writeText() API for better security and UX.

Changes:
- Made copyToClipboard async to support Clipboard API
- Use navigator.clipboard.writeText() as primary method
- Fallback to execCommand if Clipboard API fails (older browsers)
- Added console warning when fallback is used
- Maintains same visual feedback behavior

* admin: improve security and UX for error handling

Addressed code review feedback:

1. Security: Remove sensitive error details from API responses
   - CreateServiceAccount: Return generic error message
   - UpdateServiceAccount: Return generic error message
   - DeleteServiceAccount: Return generic error message
   - Detailed errors still logged server-side via glog.Errorf()
   - Prevents exposure of internal system details to clients

2. UX: Replace alert() with Bootstrap toast notifications
   - Implemented showToast() function using Bootstrap 5 toasts
   - Non-blocking, modern notification system
   - Auto-dismiss after 5 seconds
   - Proper HTML escaping to prevent XSS
   - Toast container positioned at top-right
   - Success (green) and error (red) variants

* admin: complete error handling improvements

Addressed remaining security review feedback:

1. GetServiceAccounts: Remove error details from response
   - Log errors server-side via glog.Errorf()
   - Return generic error message to client

2. UpdateServiceAccount & DeleteServiceAccount:
   - Wrap not-found errors with ErrServiceAccountNotFound sentinel
   - Enables proper 404 vs 500 distinction in handlers

3. Update & Delete handlers:
   - Added errors.Is() check for ErrServiceAccountNotFound
   - Return 404 for missing resources
   - Return 500 for internal errors with logging
   - Consistent with GetServiceAccountDetails behavior

All handlers now properly distinguish not-found (404) from internal
errors (500) and never expose sensitive error details to clients.

* admin: implement expiration support and improve code quality

Addressed final code review feedback:

1. Expiration Support:
   - Added expiration helper functions (getExpiration, setExpiration)
   - Implemented expiration in CreateServiceAccount
   - Implemented expiration in UpdateServiceAccount
   - Added Expiration field to ServiceAccount struct
   - Parse and validate RFC3339 expiration dates

2. Constants for Magic Strings:
   - Added StatusActive, StatusInactive constants
   - Added disabledAction, serviceAccountPrefix constants
   - Replaced all magic strings with constants throughout
   - Improves maintainability and prevents typos

3. Helper Function to Reduce Duplication:
   - Created identityToServiceAccount() helper
   - Reduces code duplication across Get/Update/Delete methods
   - Centralizes ServiceAccount struct building logic

4. Fixed time.Now() Fallback:
   - Changed from time.Now() to time.Time{} for legacy accounts
   - Prevents creation date from changing on each fetch
   - UI can display zero time as "N/A" or blank

All code quality issues addressed!

* admin: fix StatusActive reference in handler

Use dash.StatusActive to properly reference the constant from the dash package.

* admin: regenerate templ files

Regenerated all templ Go files after recent template changes.
The AWS CLI usage example already uses proper <pre><code> formatting
which preserves line breaks for better readability.

* admin: add explicit white-space CSS to AWS CLI example

Added style="white-space: pre-wrap;" to the pre tag to ensure
line breaks are preserved and displayed correctly in all browsers.
This forces the browser to respect the newlines in the code block.

* admin: fix AWS CLI example to display on separate lines

Replaced pre/code block with individual div elements for each line.
This ensures each command displays on its own line regardless of
how templ processes whitespace. Each line is now a separate div
with font-monospace styling for code appearance.

* make

* admin: filter service accounts from parent user dropdown

Service accounts should not appear as selectable parent users when
creating new service accounts. Added filter to GetObjectStoreUsers()
to skip identities with "sa:" prefix, ensuring only actual IAM users
are shown in the parent user dropdown.

* admin: address code review feedback

- Use constants for magic strings in service account management
- Add Expiration field to service account responses
- Add nil checks and context propagation
- Improve templates (date validation, async clipboard, toast notifications)

* Update service_accounts_templ.go
2025-12-29 22:57:02 -08:00
chrislu
f47c4aef5a object store users 2025-07-02 00:00:23 -07:00