Skip to content

Conversation

@celdrake
Copy link
Collaborator

@celdrake celdrake commented Jan 27, 2026

Adds workload privilege settings to Single Container and Quadlets applications.

Summary by CodeRabbit

  • New Features

    • Display and edit "Run as user" across app list and edit flows.
    • Added "Access & permissions" with a System Integrity Protection toggle and helper text.
    • Shows rootless user identity (default and customizable) and accepts run-as values in forms.
    • Integrity settings applied when creating or resetting app forms.
  • Localization

    • Added English translations for all new UI strings.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

Walkthrough

Adds optional runAs fields to application types and device models, exposes run-as controls in the device edit UI via a new ApplicationIntegritySettings component, propagates runAs through device-spec utilities and validation, adds translations, and displays a "Run as user" column in the applications table.

Changes

Cohort / File(s) Summary
Translations
libs/i18n/locales/en/translation.json
Added 8 English translation keys for run-as wording, access & permissions, system integrity protection (enabled/disabled), helper texts, and rootless identity.
Types / Models
libs/types/models/ApplicationProviderSpec.ts, libs/types/models/DeviceApplicationStatus.ts, libs/ui-components/src/types/deviceSpec.ts
Added optional runAs?: string to ApplicationProviderSpec and DeviceApplicationStatus; added runAs?: string to SingleContainerAppForm, QuadletImageAppForm, QuadletInlineAppForm; introduced RUN_AS_DEFAULT_USER = 'flightctl' and RUN_AS_ROOT_USER = 'root'.
Device spec utilities
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts
Large refactor and new exports: added createInitialAppForm, modular create* helpers, conversions between form/API file shapes, and propagation/detection of runAs through serialization and form helpers.
New UI component
libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationIntegritySettings.tsx
New exported component that reads/writes applications[index].runAs, renders a system integrity Switch, toggles between default/root users, and shows rootless identity info.
Form integrations & layout
libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationContainerForm.tsx, .../ApplicationImageForm.tsx, .../ApplicationInlineForm.tsx, .../ApplicationTemplates.tsx
Integrated ApplicationIntegritySettings into container and quadlet forms; wrapped Image/Inline layouts in Grid; ApplicationTemplates now initializes apps via createInitialAppForm and conditionally shows integrity UI for quadlet apps.
Validation
libs/ui-components/src/components/form/validations.ts
Added optional runAs: Yup.string() to single-container application validation schema.
Tables / Display
libs/ui-components/src/components/DetailsPage/Tables/ApplicationsTable.tsx
Added "Run as user" column showing app.runAs or RUN_AS_DEFAULT_USER.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant UI as Edit Device UI
    participant Formik as Formik State
    participant Utils as deviceSpecUtils
    participant API as API Model

    User->>UI: Toggle "System integrity" switch
    UI->>Formik: setFieldValue("applications[i].runAs", runAsValue)
    Formik->>UI: render updated fields (rootless identity shown/hidden)
    User->>UI: Submit form
    UI->>Utils: getApplicationValues(formState)
    Utils->>Utils: create*App helpers attach runAs to payload
    Utils->>API: return serialized application payload including runAs
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'EDM-2954: Allow configuring app privileges' accurately summarizes the main change: adding privilege/access configuration capabilities to applications via the new runAs property and ApplicationIntegritySettings component.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@celdrake celdrake force-pushed the EDM-2954-app-privilege-configuration branch 2 times, most recently from dc8e398 to b484007 Compare January 28, 2026 11:18
@celdrake celdrake marked this pull request as ready for review January 28, 2026 11:21
Copy link

@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: 3

🤖 Fix all issues with AI agents
In `@libs/types/models/DeviceApplicationStatus.ts`:
- Around line 27-30: Fix the JSDoc typo for the runAs property in
DeviceApplicationStatus: change "runing" to "running" in the comment above the
runAs?: string; declaration so the description reads "...this application is
running under."

In `@libs/ui-components/src/components/DetailsPage/Tables/ApplicationsTable.tsx`:
- Line 48: The UI is showing RUN_AS_DEFAULT_USER ('flightctl') when
DeviceApplicationStatus.runAs is undefined, which conflicts with the API doc
stating the agent's user (generally root) is used; update ApplicationsTable.tsx
(the Td rendering using app.runAs || RUN_AS_DEFAULT_USER) and the application
form components (Container, Image, Inline) and helper text to reflect the API
fallback (display the agent user, e.g., "agent (root)") when runAs is undefined
OR, if the runtime default truly is 'flightctl', change the RUN_AS_DEFAULT_USER
constant and related helper text so the UI and API documentation are
consistent—make the chosen single source of truth the value shown whenever
app.runAs is falsy.

In
`@libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts`:
- Around line 320-332: In toAPIApplication, the call to toInlineFiles(app.files)
is wrong because toInlineFiles converts API->form; replace it with a form->API
converter (e.g., toAPIInlineFiles) that transforms each file's base64 boolean
into the API's contentEncoding: EncodingType and preserves name/content; if such
a helper doesn't exist, implement toAPIInlineFiles(files) and use it when
building inlineData.inline, and ensure Quadlet-related runAs handling remains
unchanged.
🧹 Nitpick comments (3)
libs/ui-components/src/components/form/validations.ts (1)

707-707: Consider adding runAs validation to Quadlet app schemas for consistency.

The runAs field has been added to the validation schema for SingleContainerAppForm, but looking at deviceSpec.ts, the runAs?: string property is also present on QuadletImageAppForm (line 76) and QuadletInlineAppForm (line 84).

For consistency and to prevent potential validation issues, consider adding runAs: Yup.string() to the Quadlet validation schemas as well (around lines 743-756 and 761-771).

♻️ Suggested additions

For the image applications schema (around line 756):

             volumes: composeQuadletVolumesSchema(t),
             variables: appVariablesSchema(t),
+            runAs: Yup.string(),
           });

For the inline quadlet applications schema (around line 771):

             volumes: composeQuadletVolumesSchema(t),
             variables: appVariablesSchema(t),
+            runAs: Yup.string(),
           });
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (2)

638-647: Consider renaming toInlineFiles or adding a complementary function.

The function toInlineFiles converts from API format (contentEncoding) to form format (base64), but the name doesn't clearly indicate the conversion direction. This ambiguity contributed to its incorrect usage at line 324.

Consider either:

  1. Renaming to apiFilesToFormFiles for clarity
  2. Adding a complementary formFilesToApiFiles function for the reverse conversion

297-302: Minor inconsistency: runAs always set for Single Container but conditionally for Quadlet.

For Single Container apps (line 300), runAs is always included in the API payload with a default of RUN_AS_DEFAULT_USER. For Quadlet apps (lines 314-316, 328-330), runAs is only included if explicitly defined.

This works correctly because the create*App helpers always initialize runAs, but the conditional pattern for Quadlet could be simplified to match the Single Container pattern for consistency.

Optional: Simplify to match Single Container pattern
   if (isQuadletImageAppForm(app) || isComposeImageAppForm(app)) {
     const data: ApplicationProviderSpec = {
       image: app.image,
       appType: app.appType,
       envVars,
       volumes,
     };
     if (app.name) {
       data.name = app.name;
     }
-    if (isQuadletImageAppForm(app) && app.runAs) {
-      data.runAs = app.runAs;
+    if (isQuadletImageAppForm(app)) {
+      data.runAs = app.runAs || RUN_AS_DEFAULT_USER;
     }
     return data;
   }

@celdrake celdrake force-pushed the EDM-2954-app-privilege-configuration branch from bc86be2 to c457348 Compare January 28, 2026 15:52
Copy link

@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

🤖 Fix all issues with AI agents
In
`@libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationInlineForm.tsx`:
- Line 2: The import statement in ApplicationInlineForm.tsx includes an unused
symbol `useField`; remove `useField` from the import list so only `FieldArray`
is imported (i.e., change the import that currently reads "import { FieldArray,
useField } from 'formik';" to import only `FieldArray`) to satisfy the static
analysis and avoid unused-import warnings.
🧹 Nitpick comments (1)
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (1)

798-811: Consider defining a proper type for the intersection.

The cast at line 806 (as ApplicationProviderSpec & InlineApplicationProviderSpec & { runAs?: string }) suggests the base types don't include runAs for inline applications. While functional, this inline type extension is a maintenance risk.

Consider whether the @flightctl/types definitions should be updated to include runAs on the appropriate specs, or define a local type alias for clarity.

@celdrake celdrake force-pushed the EDM-2954-app-privilege-configuration branch 2 times, most recently from ce1c0e7 to ee46cbf Compare January 28, 2026 16:20
Copy link

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (1)

300-331: Inconsistent runAs serialization creates privilege escalation risk for legacy apps

Backend defaults omitted runAs to root (agent user), but the UI defaults to 'flightctl'. This creates a mismatch when editing existing apps:

SingleContainerApp (line 300) always serializes with the fallback: data.runAs = app.runAs || RUN_AS_DEFAULT_USER, so any edit to an app with undefined runAs (currently root on backend) will change it to 'flightctl'.

QuadletImageApp and QuadletInlineApp correctly avoid this by only serializing runAs when explicitly defined (lines 314 and 321), but form initialization (lines 709, 720, 728) still defaults all app types to 'flightctl' when loading undefined values.

Change detection (lines 354, 463, 522) also uses the fallback pattern, masking actual privilege changes.

Preserve the backend's undefined runAs for existing apps unless the user explicitly changes the setting. Only default 'flightctl' in the form for new apps.

@celdrake celdrake force-pushed the EDM-2954-app-privilege-configuration branch from ee46cbf to 18d97a5 Compare January 29, 2026 08:14
Copy link

@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

🤖 Fix all issues with AI agents
In
`@libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts`:
- Around line 798-811: The switch on app.appType (inside the map callback that
returns app specs) lacks a default and can return undefined; update that switch
to include a default branch that throws a descriptive Error for unexpected
AppType values (e.g., referencing app.appType and maybe app.id) so the callback
always returns or fails fast. Ensure you modify the same switch that calls
createContainerApp, createHelmApp, createQuadletImageApp,
createQuadletInlineApp, createComposeImageApp and createComposeInlineApp and
keep existing isImageAppProvider checks intact.
🧹 Nitpick comments (1)
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (1)

771-789: Add a default branch to guard unknown AppType values.

Future enum additions could leave app uninitialized; a default throw keeps failures explicit.

Suggested update
   switch (appType) {
     case AppType.AppTypeContainer:
       app = createContainerApp(undefined);
       break;
     case AppType.AppTypeHelm:
       app = createHelmApp(undefined);
       break;
     case AppType.AppTypeQuadlet:
       app = specType === AppSpecType.OCI_IMAGE ? createQuadletImageApp(undefined) : createQuadletInlineApp(undefined);
       break;
     case AppType.AppTypeCompose:
       app = specType === AppSpecType.OCI_IMAGE ? createComposeImageApp(undefined) : createComposeInlineApp(undefined);
       break;
+    default:
+      throw new Error(`Unsupported appType: ${appType}`);
   }

@celdrake celdrake merged commit 8b4ac25 into flightctl:main Jan 29, 2026
6 checks passed
@celdrake celdrake deleted the EDM-2954-app-privilege-configuration branch January 29, 2026 08:23
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.

2 participants