Skip to content

[Frontend] Create API-only users that only have access to customer-defined Fleet API endpoints #43281

Merged
juan-fdz-hawa merged 36 commits intomainfrom
nulmete/42879-api-only-user-management-ui
Apr 20, 2026
Merged

[Frontend] Create API-only users that only have access to customer-defined Fleet API endpoints #43281
juan-fdz-hawa merged 36 commits intomainfrom
nulmete/42879-api-only-user-management-ui

Conversation

@nulmete
Copy link
Copy Markdown
Member

@nulmete nulmete commented Apr 8, 2026

Related issue: Resolves #42879

Checklist for submitter

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

Tested with latest backend changes from: #43440

Regular user create/edit

regularuser.mov

API-only user create/edit

apionly_1.mov

API-only user form validation

Screen.Recording.2026-04-15.at.3.48.37.PM.mov

Summary by CodeRabbit

  • New Features

    • Full UI for API-only user management: create/edit flows, fleet/role assignment, selectable API endpoint permissions, and one-time API key display.
    • New reusable components: API user form, endpoint selector, API access section, and API key presentation.
  • Improvements

    • Admin workflow switched from in-page modals to dedicated pages and streamlined action dropdown navigation.
    • Layout and styling refinements for user management, team lists, and dropdown behaviors.
  • Minor

    • Actions dropdown button label is now customizable.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 8, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.84%. Comparing base (c783ac7) to head (8732cfa).
⚠️ Report is 34 commits behind head on main.

Files with missing lines Patch % Lines
...components/SelectedTeamsForm/SelectedTeamsForm.tsx 66.66% 1 Missing ⚠️
frontend/router/paths.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #43281      +/-   ##
==========================================
- Coverage   66.91%   66.84%   -0.08%     
==========================================
  Files        2600     2589      -11     
  Lines      208710   207177    -1533     
  Branches     9302     9172     -130     
==========================================
- Hits       139666   138481    -1185     
+ Misses      56327    56095     -232     
+ Partials    12717    12601     -116     
Flag Coverage Δ
frontend 54.76% <60.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@frontend/pages/admin/UserManagementPage/components/ApiUserForm/ApiUserForm.tsx`:
- Line 245: The submit button is being disabled based on combinedErrors (which
merges local formErrors and server/prop ancestorErrors) so server-provided
ancestorErrors can keep the button disabled after the user fixes local inputs;
change the disabled logic to only consider the component-controlled local
validation state (formErrors) or otherwise exclude ancestorErrors from the
disabling condition: update the disabled prop to use
Object.keys(formErrors).length > 0 (or compute a new localOnlyErrors from
formErrors) while still displaying combinedErrors to the user; reference
combinedErrors, formErrors, and ancestorErrors to locate and adjust the check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 685c3bac-fe82-42b3-a64e-4d09de160b43

📥 Commits

Reviewing files that changed from the base of the PR and between f3d3052 and 9aedefa.

📒 Files selected for processing (1)
  • frontend/pages/admin/UserManagementPage/components/ApiUserForm/ApiUserForm.tsx

Comment thread frontend/pages/admin/UserManagementPage/components/ApiUserForm/ApiUserForm.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
frontend/pages/admin/UserManagementPage/_styles.scss (1)

199-247: Consider extracting repeated values for maintainability.

Minor observations:

  1. The top: 44px on line 202 is a magic number likely tied to the search input height. If that input's height changes, this will need manual adjustment.
  2. The box-shadow value 0px 4px 10px rgba(52, 59, 96, 0.15) is duplicated on lines 209 and 229.

These could be extracted to variables or a mixin for easier maintenance, but not critical for this PR.

♻️ Optional: Extract duplicated shadow to a variable
+// At top of file or in variables partial
+$dropdown-shadow: 0px 4px 10px rgba(52, 59, 96, 0.15);

 &__search-dropdown {
   // ...
   .table-container {
-    box-shadow: 0px 4px 10px rgba(52, 59, 96, 0.15);
+    box-shadow: $dropdown-shadow;
     // ...
   }

   .empty-search {
     // ...
-    box-shadow: 0px 4px 10px rgba(52, 59, 96, 0.15);
+    box-shadow: $dropdown-shadow;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/pages/admin/UserManagementPage/_styles.scss` around lines 199 - 247,
The CSS in the &__search-dropdown block uses a magic number top: 44px and
repeats the box-shadow value in .table-container and .empty-search; extract
these into SCSS variables or a mixin (e.g., $search-input-height or
$search-dropdown-top and $card-box-shadow) and replace the hardcoded top: 44px
and both box-shadow declarations in the &__search-dropdown > .table-container
and &__search-dropdown > .empty-search to reference the new variables/mixin
(locate the selectors &__search-dropdown, .table-container, and .empty-search in
the diff).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@frontend/pages/admin/UserManagementPage/_styles.scss`:
- Around line 199-247: The CSS in the &__search-dropdown block uses a magic
number top: 44px and repeats the box-shadow value in .table-container and
.empty-search; extract these into SCSS variables or a mixin (e.g.,
$search-input-height or $search-dropdown-top and $card-box-shadow) and replace
the hardcoded top: 44px and both box-shadow declarations in the
&__search-dropdown > .table-container and &__search-dropdown > .empty-search to
reference the new variables/mixin (locate the selectors &__search-dropdown,
.table-container, and .empty-search in the diff).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: df89b295-a85e-4aee-9012-29f265ed7ea8

📥 Commits

Reviewing files that changed from the base of the PR and between 9aedefa and db3ceb7.

📒 Files selected for processing (1)
  • frontend/pages/admin/UserManagementPage/_styles.scss

return (
<div className={baseClass}>
<div className={`${baseClass}__api-key-label`}>
<b>API key</b>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Figma shows API Key

Cell: NameCell,
},
{
title: "Protocol",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be "Method"

const searchResults: IEndpointRow[] = useMemo(() => {
if (isEmpty(searchText)) return [];
const query = searchText.toLowerCase();
return allRows.filter(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated 👍

Screenshot 2026-04-16 at 11 55 32 AM

Comment thread frontend/services/entities/users.ts Outdated
const { USERS_API_ONLY } = endpoints;

return sendRequest("POST", USERS_API_ONLY, formData).then((response) => ({
user: helpers.addGravatarUrlToResource(response.user),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calling helpers.addGravatarUrlToResource shouldn't be necessary here - API only users don't have a gravatar

Comment thread frontend/services/entities/users.ts Outdated
const path = `${USERS_API_ONLY}/${userId}`;

return sendRequest("PATCH", path, formData).then((response) =>
helpers.addGravatarUrlToResource(response.user)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See prev comment, calling addGravatarUrlToResource is not needed

@juan-fdz-hawa juan-fdz-hawa merged commit 578f352 into main Apr 20, 2026
19 checks passed
@juan-fdz-hawa juan-fdz-hawa deleted the nulmete/42879-api-only-user-management-ui branch April 20, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API-only + API-endpoints] Frontend

4 participants