Remove reCAPTCHA integration from the password recovery flow
as the feature has been deprecated.
Changes:
- Delete ReCaptcha component
- Remove recaptcha token handling from Signin page
- Simplify UsePasswordRecovery hook
- Remove react-google-recaptcha dependencies
- Clean up test fixtures and CSS
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
The formatSize() function used a falsy check which treated 0 as invalid,
returning "N/A" instead of formatting it. Now 0 displays as "0.00 KiB"
matching the legacy UI behavior.
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
when using OIDC authentication and the user has no password set, display
an info alert with a "Set password" button to guide users through setting
up their CLI password
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(web): enable user self-service email changes when FEATURE_MAILING enabled (PROJQUAY-9879)
This commit fixes the issue where users received 401 Unauthorized errors
when attempting to update their email address in the new React UI when
FEATURE_MAILING is enabled.
Root cause: ChangeEmailModal was using the superuser-only endpoint
/api/v1/superuser/users/{username}, which regular users cannot access.
Changes:
- Added useChangeEmail hook in UseCurrentUser.ts that calls the correct
user self-service endpoint /api/v1/user/ for email updates
- Modified ChangeEmailModal to support dual modes (superuser vs user)
with isSuperuserMode prop for backward compatibility
- Updated GeneralSettings to display email as clickable link when
FEATURE_MAILING is enabled, opening the modal for email changes
- Pre-fill modal with current email address for better UX
- Added validation to prevent submitting the same email address
- Added 8 comprehensive Cypress e2e tests covering email change flows
The fix implements the proper email verification workflow where users
receive a verification email and must confirm before the change is applied.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fixing tests
* resolving coderabbit suggestion
---------
Co-authored-by: Claude <noreply@anthropic.com>
Global readonly superusers could click Create Message and Service Key
buttons which then failed with 403 errors. These buttons are now disabled
using the existing useSuperuserPermissions hook's canModify flag.
Co-authored-by: Claude <noreply@anthropic.com>
The settings page showed "Organization" labels and helper text even
when viewing a user namespace. Changed to conditionally display
"Username" for users and "Organization" for organizations.
Co-authored-by: Claude <noreply@anthropic.com>
Normal users couldn't see their own namespace quota in the Organizations
list Size column. The backend already returns quota_report in /api/v1/user/
but the frontend wasn't using it. Added fallback to use current user's
quota_report when superuser data isn't available.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Previously, all errors on the Organizations page showed "Org deletion failed"
as the modal title, even for unrelated operations like registry size
calculation. This was confusing for Global Readonly Superusers who saw
"Org deletion failed" when trying to calculate registry size.
Changes:
- Separated error states in OrganizationsList.tsx (deletionErr, registryCalcErr)
- Added separate ErrorModal for registry calculation with correct title
- Fixed RepositoriesList.tsx ErrorModal title to "Repository deletion failed"
- Added Cypress test to verify correct error modal title
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
The UI was showing the "Enable OIDC Team Sync" button even when
FEATURE_TEAM_SYNCING was disabled in the config. Added check for
config?.features?.TEAM_SYNCING before displaying the team sync button.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
fix(ui): ensure logout redirects to signin on network error (PROJQUAY-9792)
When the logout API call fails due to network error, the UI now properly
redirects to the signin page and clears the client-side session. Previously,
the user would be stuck on the current page with an error modal.
Changes:
- Move redirect and cleanup to finally block in logout handler
- Add optional chaining to user.username to prevent undefined errors
- Remove unused addDisplayError import
- Add comprehensive Cypress e2e tests for logout functionality
The finally block ensures client-side logout always succeeds, even when
the server is unreachable, improving security and user experience.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Stripe and StatusPage scripts were hardcoded in index.html, causing
85-second delays in air-gapped/restricted networks as browsers waited
for connection timeouts.
Created useExternalScripts hook to dynamically load scripts only when
BILLING feature is enabled. Scripts load asynchronously to prevent
blocking page render. On-premise deployments (BILLING=false) no longer
make external requests.
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
Previously only organizations and the logged-in user showed avatars.
Now all users and superusers display avatars by passing avatar data
from the API response through component props.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Organization and user quota settings pages are view-only, but were
displaying an empty "Add Limit" form row with disabled controls.
This creates visual clutter and implies users can add limits when
they cannot.
Conditionally render the "Add Limit" card only when !isReadOnly to
match the behavior of the old Angular UI. Update Cypress tests to
verify the form does not exist (not just disabled) in view-only mode.
fix(web): prevent redirect to signin after OIDC username confirmation (PROJQUAY-9835)
When users authenticated via OIDC and confirmed their username, they were
incorrectly redirected back to the signin page instead of the home page.
This occurred because the OAuth flow stored the signin page URL in localStorage
as the redirect target. After username confirmation, the app would read this
stored URL and redirect back to signin, creating a loop.
The fix checks if the stored redirect URL contains '/signin' and navigates to
the home page instead.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* fix(web): validate quota value input accepts only numbers (PROJQUAY-9837)
Changed Storage Quota input from type="text" to type="number" to prevent
non-numeric characters from being entered. Also enhanced validation to
catch edge cases where parseFloat could incorrectly parse mixed values
like "300xxxx" as 300.
Co-authored-by: Claude <noreply@anthropic.com>
* chore: move quota test seeding
locally the test goes from 8 minutes to 55 seconds :)
Signed-off-by: Brady Pratt <bpratt@redhat.com>
---------
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
* ui: redirect new UI super user for fresh login when authentication type is OIDC (PROJQUAY-9748)
* test(ui): Fix Cypress tests for fresh login OIDC flow (PROJQUAY-9748)
- Fixed incorrect API endpoint (/api/v1/superuser/logs* instead of /api/v1/superuserlogs**)
- Fixed incorrect route (/usage-logs instead of /superuser/usagelogs)
- Added proper authentication setup using cy.loginByCSRF
- Used fixtures (config.json, superuser.json) following existing test patterns
- Simplified tests to 3 critical cases: OIDC redirect, query param preservation, Database modal
---------
Co-authored-by: harishsurf <hgovinda@redhat.com>
Implement RFC 6749 Section 10.12 compliant state parameter to prevent
CSRF attacks in OAuth token generation flow.
Changes:
- Generate cryptographically secure state using crypto.randomUUID()
- Store state in sessionStorage before OAuth redirect
- Parent window validates state from popup via postMessage
- Read state from query params (backend echoes it back)
- Display security error for invalid/missing state
- Add Cypress tests for state validation and CSRF protection
Security Impact:
- Prevents CSRF token theft and session fixation attacks
- Complies with OAuth 2.0 security best practices
- React UI only; Angular UI remains unchanged
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ui): Enable organization/user visibility for read-only superusers (PROJQUAY-6882)
Users listed under GLOBAL_READONLY_SUPER_USERS can now see all
organizations and users in the UI, matching regular superuser visibility
with read-only restrictions on actions.
- Update UseCurrentUser to include global_readonly_super_user in isSuperUser check
- Add Cypress tests for read-only superuser visibility and action restrictions
- Settings column actions correctly hidden via existing canModify permission
* fix(ui): Add global_readonly_super_user field to API responses (PROJQUAY-6882)
- Add global_readonly_super_user field to user API response in endpoints/api/user.py
- Allow read-only superusers to view organization teams in endpoints/api/organization.py
- Allow read-only superusers to view robot permissions in endpoints/api/robot.py
* fix(ui): Prevent read-only superusers from deleting orgs/users
Security fix: Read-only superusers should not be able to delete
orgs or users they don't own, even though they can view them.
* Fix inline import + incorrect assert + add codecov tests
---------
Co-authored-by: Claude <noreply@anthropic.com>
The superuser build logs feature was calling only
/api/v1/superuser/<build_uuid>/build which returns build metadata but
NOT logs. Logs are available from a separate
/api/v1/superuser/<build_uuid>/logs endpoint that must be called
separately.
Updated fetchBuildLogsSuperuser() to fetch both endpoints in parallel
using Promise.all and merge the results. This matches the behavior of
the old AngularJS UI which called both endpoints separately.
Updated Cypress tests to mock both API endpoints.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
remove status code formatting from getErrorMessage to prevent exposing
the status code to users - reducing the noise of the error messages
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Superusers viewing another user's account now only see the Repositories
tab, matching the old UI behavior. Settings, Robot accounts, External
logins, and Logs tabs are now only visible when viewing your own account.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
Regular users viewing their own quota need to use /api/v1/user/quota
endpoint, not the superuser endpoint which returns 403 permission
denied. Added viewMode parameter ('self'|'organization'|'superuser')
to properly route quota requests to the correct backend endpoint.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
The new UI was collecting a password from the superuser during user
creation, but the backend API ignores this and generates a random
32-character password instead. The UI never displayed this generated
password, causing users to be unable to log in.
This fix removes the password input fields from the Create User modal
and displays the backend-generated password in a ClipboardCopy component
after successful creation, matching the behavior of the old Angular UI.
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
The bulk delete functionality incorrectly called the organization delete
API for both users and organizations, causing failures when attempting to
delete users via the Actions dropdown. This separates the deletion logic
to call the appropriate API endpoint based on entity type (isUser flag).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(web): hide Create User button for external authentication in superuser panel (PROJQUAY-9736)
When Quay is configured with external authentication (LDAP, OIDC, etc.),
the Create User button is now hidden in the superuser Organizations page
and replaced with an informational alert explaining that users can only
be created in the external authentication system. This matches the
behavior of the old AngularJS UI and prevents the confusing error that
occurred when users clicked the button.
Changes:
- Conditionally render Create User button only for Database auth
- Add PatternFly Alert for external auth with user-friendly message
- Add Cypress tests covering Database, LDAP, OIDC, and AppToken auth types
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Brady Pratt <bpratt@redhat.com>
* chore: drop proptypes use in orgs list
Signed-off-by: Brady Pratt <bpratt@redhat.com>
---------
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
When AUTHENTICATION_TYPE is LDAP or other external auth (not Database),
the superuser user management panel now correctly hides "Change E-mail Address"
and "Change Password" options from the user actions menu. These options are only
shown for Database authentication since external auth users are managed in the
external system.
Also hides "Send Recovery E-mail" option for external auth as it only works
with Database authentication.
Adds Cypress tests to verify correct behavior for LDAP, OIDC, and Database
authentication types.
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
When logging in with LDAP authentication, the new React UI was skipping the
username confirmation page and going directly to the Organizations page. This
fix ensures that users with the confirm_username prompt are redirected to the
/updateuser page after successful login.
Changes:
- Modified Signin.tsx to check for user prompts after successful login
- Added redirect to /updateuser if prompts exist
- Enhanced UpdateUser.tsx to honor quay.redirectAfterLoad for external logins
- Added Cypress e2e tests for username confirmation flow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
when user with unverified email attempts login, display "You must verify
your email address before you can sign in" instead of misleading "CSRF
token expired - please refresh" error
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
When INVITE_ONLY_USER_CREATION is enabled and a valid LDAP user attempts
to login without an existing Quay account, the frontend now displays the
proper error message from the backend: "User creation is disabled. Please
contact your administrator to gain access."
Previously, the frontend hardcoded "Invalid login credentials" regardless
of the actual error message returned by the backend, which was misleading.
Changes:
- Use backend error message instead of hardcoded string in Signin.tsx
- Add Cypress test for INVITE_ONLY_USER_CREATION error scenario
- Fix existing test to align with new behavior
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
- Rename ApplicationTokenCredentials to CredentialsModal for reusability
- Add support for both application tokens and encrypted passwords
- Fix memory leak by moving state cleanup to useEffect
- Fix error handling to clear errors on successful responses
- Add null checks for user loading state
- Update data-testid naming for better specificity
- Mock encrypted password API in Cypress tests
- Simplify Cypress selectors for better reliability
Co-authored-by: Claude <noreply@anthropic.com>
Users can now navigate directly to organizations and repositories using
shorthand URLs, matching the Angular UI behavior:
- /myorg → /organization/myorg
- /openshift/release → /repository/openshift/release
Implementation improvements:
- Dynamically derives reserved route prefixes from NavigationPath enum
- TypeScript interface for type-safe route parameters
- Comprehensive JSDoc documentation with examples
- Preserves query parameters and hash fragments during redirects
- Factory function for test mock data reusability
Test coverage:
- 11 comprehensive Cypress e2e tests (up from 6)
- Tests for organization and repository redirects
- Query parameter and hash fragment preservation
- Reserved route prefix handling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ui): superuser usage logs filter searches across all fields (PROJQUAY-9622)
The filter in the superuser usage logs page was only searching the log.kind
field, causing it to return no results when users searched for namespaces,
usernames, or other visible data.
Changes:
- Enhanced filter to search across namespace, repository, performer, IP address,
log kind, and description fields
- Removed duplicate filtering logic (filterLogs function and select option)
- Now follows codebase standard pattern used by other tables
- Filter applied once in usePaginatedSortableTable hook instead of twice
- Added comprehensive Cypress tests for filter functionality
The filter now performs case-insensitive substring matching across all
displayed fields, matching the behavior of the Angular UI.
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ui): extract text from JSX for usage logs filter search
The filter was converting React elements to "[object Object]" instead of
searchable text. Added extractTextFromReactNode() utility to recursively
extract plain text from JSX while maintaining rich formatting for display
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ui): fallback to user orgs when superuser API fails (PROJQUAY-9650)
Always show user's own organizations from /api/v1/user/ and
combine with superuser orgs when available. Prevents empty org
list when FEATURE_SUPER_USERS is enabled but superuser API
returns 403.
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: harishsurf <hgovinda@redhat.com>
* fix(ui): resolve React hooks violations in OrganizationsList
Optimize UseOrganizations with useMemo to prevent unnecessary
re-renders. Move all useState hooks before early returns in Organizations
kebab menu. Also fix PropTypes warning by ensuring showRegistrySize
is always boolean. Add cypress tests for regression
---------
Signed-off-by: harishsurf <hgovinda@redhat.com>
Application token table now shows relative time ("5 minutes ago") for Last Accessed,
Expiration, and Created columns, with full timestamp displayed in tooltip on hover.
This matches the behavior of the current Angular UI.
Uses existing formatRelativeTime and formatDate utilities from libs/utils with
PatternFly Tooltip component.
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>