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 race conditions in pull metrics tracking and flushing
Replace non-atomic operations with atomic Redis operations to prevent
data loss when concurrent pulls occur during flush operations.
* fixing tests
* updating tests
* added uuid to the rename factors to ensure unique key at concurrent requests
---------
Co-authored-by: shudeshp <shudeshp@redhat.com>
* chore: update ci to use new large ubuntu 24.04 runner
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-Authored-By: Dave O'Connor <doconnor@redhat.com>
* fix: add libfreetype6-dev for Ubuntu 24.04 compatibility
The reportlab package requires FreeType development headers to build.
On Ubuntu 24.04, this dependency is not pulled in transitively and
must be explicitly installed. This fixes the "cannot find ft2build.h"
build error.
Added libfreetype6-dev to all jobs that install system dependencies
in CI.yaml and CI-nightly.yaml workflows.
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-Authored-By: Dave O'Connor <doconnor@redhat.com>
* chore: set the TEST_DATETIME to a static value
this caused an issue in xdist when generating test names
Signed-off-by: Brady Pratt <bpratt@redhat.com>
* chore: cache pip packages in CI
Signed-off-by: Brady Pratt <bpratt@redhat.com>
* chore: run registry tests with -n auto
Signed-off-by: Brady Pratt <bpratt@redhat.com>
* chore: run psql with -n auto
Signed-off-by: Brady Pratt <bpratt@redhat.com>
* chore: add file locking to prevent parallel test db init race condition
When running pytest -n auto with multiple workers, both workers would
simultaneously execute populate_database(), causing duplicate key
violations on shared tables like imagestoragelocation:
Worker 1: Check if User "devtable" exists → No → Start populating
Worker 2: Check if User "devtable" exists → No → Start populating
Both: INSERT INTO imagestoragelocation (name) VALUES ('local_eu')
Result: IntegrityError - duplicate key violation
Solution: Wrap init_db_path fixture with FileLock to ensure only one
worker initializes the database at a time. The lock file is created
in pytest's shared temp directory, coordinating across all workers.
- First worker acquires lock and populates database
- Subsequent workers wait at lock, then see database is already
populated (via User.get() check in populate_database())
- Works for both PostgreSQL and MySQL
- 300-second timeout prevents deadlocks
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: run mysql with -n auto
Signed-off-by: Brady Pratt <bpratt@redhat.com>
---------
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Dave O'Connor <doconnor@redhat.com>
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>
This fixes an issue where global readonly superusers were blocked from
accessing organization quota limit endpoints when FEATURE_SUPERUSERS_FULL_ACCESS
was set to false.
Fixed endpoints in endpoints/api/namespacequota.py:
- OrganizationQuotaLimitList.get() - List quota limits
- OrganizationQuotaLimit.get() - Get individual quota limit
Both endpoints now use the consistent permission pattern:
permission.can() OR
allow_if_global_readonly_superuser() OR
allow_if_superuser_with_full_access()
Added comprehensive tests in test_global_readonly_superuser.py:
- test_global_readonly_superuser_can_access_quota_limit_list
- test_global_readonly_superuser_can_access_individual_quota_limit
- test_regular_superuser_cannot_access_quota_limits_without_full_access
Test implementation uses autouse fixture to ensure FEATURE_SUPERUSERS_FULL_ACCESS
is disabled for all tests in the class, following the pattern from
TestOrganizationLogsAccessWithoutFullAccess.
Tests verify:
1. Global readonly superusers CAN access quota limits for auditing,
regardless of FEATURE_SUPERUSERS_FULL_ACCESS setting
2. Regular superusers are still blocked when FEATURE_SUPERUSERS_FULL_ACCESS
is false (correct security behavior)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
React-markdown v10.x no longer reliably passes the inline prop to
custom code components. Changed detection to check for newlines in
code content instead, which correctly distinguishes inline code from
code blocks.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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>
* fix: allow global readonly superusers to access all organization data without FULL_ACCESS (PROJQUAY-9798)
This is a comprehensive fix for multiple endpoints where global readonly superusers
were incorrectly blocked from accessing organization data when
FEATURE_SUPERUSERS_FULL_ACCESS was set to false.
Fixed endpoints in endpoints/api/logs.py:
- OrgLogs.get() - Organization audit logs
- OrgAggregateLogs.get() - Aggregated organization logs
- ExportOrgLogs.post() - Export organization logs
Fixed endpoints in endpoints/api/team.py:
- TeamMemberList.get() - Team member list
- TeamPermissions.get() - Team repository permissions
Fixed endpoints in endpoints/api/organization.py:
- OrganizationMemberList.get() - Organization member list
- OrganizationMember.get() - Individual member details
- OrganizationApplications.get() - OAuth application list
- OrganizationApplication.get() - Individual application details
Fixed endpoints in endpoints/api/prototype.py:
- PermissionPrototypeList.get() - Default permission prototypes
All endpoints now use consistent permission logic:
permission.can() OR
allow_if_global_readonly_superuser() OR
allow_if_superuser_with_full_access()
Added comprehensive tests verifying:
1. Global readonly superusers CAN access all data for auditing, regardless
of FEATURE_SUPERUSERS_FULL_ACCESS setting
2. Regular superusers are still blocked when FEATURE_SUPERUSERS_FULL_ACCESS
is false (correct behavior)
* fix(test): ensure owners team exists for testorglogs org in test setup
Addresses review feedback from PR #4549 comment #2539202868.
The test was attempting to access the 'owners' team in 'testorglogs'
org, but the fixture only created the organization without creating
any teams. This could cause the test to receive a 404 (team not found)
instead of 403 (permission denied), making it pass for the wrong reason.
Also simplified the test logic to only expect 403 since the team now
exists in the fixtures, ensuring the test validates permission blocking
rather than missing resources.
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>
This fixes a bug where global readonly superusers were incorrectly blocked
from accessing organization logs when FEATURE_SUPERUSERS_FULL_ACCESS was
set to false.
Changes:
- Updated OrgLogs.get() to allow global readonly superusers
- Updated OrgAggregateLogs.get() to allow global readonly superusers
- Updated ExportOrgLogs.post() to allow global readonly superusers
- Added comprehensive tests verifying the fix
The fix ensures that:
1. Global readonly superusers can ALWAYS access organization logs for
auditing purposes, regardless of FEATURE_SUPERUSERS_FULL_ACCESS setting
2. Regular superusers are still blocked from accessing organization logs
when FEATURE_SUPERUSERS_FULL_ACCESS is false (correct behavior)
All three endpoints now use consistent permission logic:
permission.can() OR
allow_if_global_readonly_superuser() OR
allow_if_superuser_with_full_access()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* Fix: Add lazy Redis connection with retry logic for pull metrics
- Implement lazy initialization to prevent startup failures when Redis unavailable
- Add retry logic (3 attempts, 1s delay) for automatic reconnection
- Add health checks before each Redis operation
- Improve error logging from DEBUG to WARNING level
- Fix silent failures after pod restart when Redis not immediately available
This fixes the issue where pull statistics tracking was permanently broken
after registry component restart if Redis wasn't available at startup.
---------
Co-authored-by: shudeshp <shudeshp@redhat.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>
add a claude code command to analyze a jira issue and create a plan from
it. This should be used in plan mode!
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Converted all remaining prop-types usage to TypeScript interfaces:
- SystemStatusBanner: Added BannerContentProps interface
- DateTimePicker: Added DateTimePickerProps interface
prop-types remains as transitive dependency but is no longer directly
imported or used in the codebase.
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
pullstats: updated bulk upsert function to track correct pull count and timestamp in case of race condition
Co-authored-by: shudeshp <shudeshp@redhat.com>
fix(api): implement proper superuser permission model and fix access controls
Fixes multiple issues with superuser functionality and implements a comprehensive
permission model for FEATURE_SUPERUSERS_FULL_ACCESS:
**Permission Model:**
- Global Readonly Superusers (auditors): Always have read access to all content,
independent of FEATURE_SUPERUSERS_FULL_ACCESS setting
- Regular Superusers: Can access /v1/superuser endpoints and their own content.
Require FEATURE_SUPERUSERS_FULL_ACCESS=true for cross-namespace read access
- Full Access Superusers: Regular superusers with FULL_ACCESS enabled, can
perform CRUD on content they don't own
- Write operations: Only allowed for full access superusers (global readonly
superusers never get write access)
**Key Fixes:**
1. Fixed superuser panel endpoints returning 403 when FULL_ACCESS was disabled.
Basic panel operations (user list, logs, org list, messages) now work with
just FEATURE_SUPER_USERS enabled.
2. Updated decorators to properly differentiate between basic superuser
operations and permission bypass operations.
3. Implemented license bypass: Superusers with FULL_ACCESS now bypass
license/quota limits when creating or modifying private repositories.
4. Fixed 18 permission checks across 7 files to properly implement cross-namespace
access controls for different superuser types.
**Changes:**
- endpoints/api/__init__.py: Fixed allow_if_superuser(), require_repo_permission, and decorators
- endpoints/api/superuser.py: Updated SuperUserAppTokens permission check
- endpoints/api/organization.py: Updated 4 GET endpoints to require FULL_ACCESS
- endpoints/api/namespacequota.py: Updated 2 GET endpoints to require FULL_ACCESS
- endpoints/api/team.py: Updated 2 GET endpoints to require FULL_ACCESS
- endpoints/api/prototype.py: Updated 1 GET endpoint to require FULL_ACCESS
- endpoints/api/policy.py: Updated auto-prune policy endpoints
- endpoints/api/robot.py: Updated robot endpoints
- endpoints/api/build.py: Updated repository build logs
- endpoints/api/repository.py: Added license bypass for superusers with FULL_ACCESS
- endpoints/api/repository_models_pre_oci.py: Updated repository visibility query
- endpoints/api/logs.py: Fixed log access to require FULL_ACCESS for permission bypass
- endpoints/api/test/test_superuser_full_access.py: Added comprehensive test suite
- endpoints/api/test/test_appspecifictoken.py: Updated test mocking and added 403 test
- test/test_api_usage.py: Updated test expectations for license bypass behavior
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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>
Superusers could not add/update/delete quota limits for user namespaces
because the React UI was calling non-existent /api/v1/superuser/users/
endpoints instead of the correct /api/v1/organization/ endpoints.
The backend only provides /api/v1/organization/{namespace}/quota/*
endpoints which work for both organizations AND user namespaces. The
Angular UI correctly used these endpoints for all cases, but the React
UI incorrectly tried to use separate /api/v1/superuser/users/ endpoints
for user namespaces, resulting in HTTP 405 Method Not Allowed errors.
Changes:
- QuotaResource.ts: Removed isUser conditional logic from createQuotaLimit,
updateQuotaLimit, and deleteQuotaLimit functions - now always use
/api/v1/organization/ endpoints
- UseQuotaManagement.ts: Updated hooks to remove isUser parameter from
quota limit function calls
🤖 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 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>
* chore: set up CTRF cypress reporting
generate and upload cypress test results to PRs using CTRF (common test
report format)
Signed-off-by: Brady Pratt <bpratt@redhat.com>
* chore: set up multi workflow ctrf pr reporting
and simplify the uploading into 2 stages
Signed-off-by: Brady Pratt <bpratt@redhat.com>
---------
Signed-off-by: Brady Pratt <bpratt@redhat.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>
Migrates UI state management from Recoil atoms to a centralized
UIContext using pure React Context API. This is part of the broader
effort to simplify state management and reduce dependencies.
Changes:
- Create UIContext with sidebar and alert state management
- Migrate SidebarState: isSidebarOpen with localStorage persistence
- Migrate AlertState: alerts array with add/remove/clear operations
- Move AlertVariant enum and AlertDetails interface to UIContext
- Remove UseAlerts hook (now redundant - consumers use useUI directly)
- Update Alerts component to use removeAlert from context
- Update QuayHeader and QuaySidebar to use useUI hook
- Update 128 files to import types/hooks from UIContext
- Delete AlertState.ts, SidebarState.ts, and UseAlerts.ts
Benefits:
- Zero runtime logic changes for consumers
- Centralized UI state in single context
- Reduced Recoil surface area (2 fewer atoms)
- Simpler architecture (removed unnecessary hook wrapper)
- Future-proof for additional UI state (theme, plugin mode)
- Pure React with no external dependencies for UI state
Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
to help guide dev and coderabbit reviews. Claude users can symlink the
file or mention the file to have it loaded
Signed-off-by: Brady Pratt <bpratt@redhat.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>