Skip to content

fix(permissions): decouple env editing and domain service list from "Create Services" permission#4055

Open
nizepart wants to merge 1 commit intoDokploy:canaryfrom
nizepart:fix/permission-create-services-not-required-for-env-and-domains
Open

fix(permissions): decouple env editing and domain service list from "Create Services" permission#4055
nizepart wants to merge 1 commit intoDokploy:canaryfrom
nizepart:fix/permission-create-services-not-required-for-env-and-domains

Conversation

@nizepart
Copy link
Copy Markdown

@nizepart nizepart commented Mar 23, 2026

Summary

Fixes #4052

The "Create Services" (canCreateServices) permission was incorrectly required for two unrelated operations:

  • Domain service dropdown: compose.loadServices checked service: ["create"] for a read-only operation (parsing YAML to list service names). Changed to service: ["read"].
  • Environment editing on DB/Compose: The ShowEnvironment component used the generic update mutation (gated by service: ["create"]) instead of the dedicated saveEnvironment endpoints (correctly gated by envVars: ["write"]).

Changes

File Change
apps/dokploy/server/api/routers/compose.ts loadServices: service: ["create"]service: ["read"]; added saveEnvironment endpoint with envVars: ["write"]
packages/server/src/db/schema/compose.ts Omit env from apiUpdateCompose; add apiSaveEnvironmentVariablesCompose schema
packages/server/src/db/schema/{postgres,mysql,mariadb,redis,mongo}.ts Omit env from update schemas to force usage of saveEnvironment
apps/dokploy/components/.../show-enviroment.tsx Use saveEnvironment mutations with per-service typed payloads

Greptile Summary

This PR correctly decouples two read/write operations from the canCreateServices (service: ["create"]) permission by routing them through their appropriate permission gates.

What changed:

  • compose.loadServices now requires service: ["read"] instead of service: ["create"] — appropriate since parsing YAML to list service names is a read-only operation
  • A new saveEnvironment tRPC procedure is added to the compose router, gated by envVars: ["write"], exactly matching the pre-existing pattern on postgres, redis, mysql, mariadb, and mongo routers
  • env is omitted from all six apiUpdate* schemas (compose, postgres, redis, mysql, mariadb, mongo), enforcing that env changes go exclusively through the dedicated saveEnvironment endpoint
  • ShowEnvironment now dispatches per-service saveEnvironment mutations with typed payloads instead of the catch-all update mutation

Review notes:

  • The import endpoint in compose.ts still calls updateCompose() directly with an env field. This is intentional and safe — the internal updateCompose(id, data: Partial<Compose>) function is not constrained by apiUpdateCompose and is protected by service: ["create"] permission, which is correct for template imports.
  • The apiSaveEnvironmentVariablesCompose schema mirrors the exact .pick().required() pattern used by all sibling service schemas.
  • No other UI components that call .update on these services pass env in their payloads, so there is no regression risk from omitting env from the update schemas.

Confidence Score: 5/5

  • This PR is safe to merge — it tightens permissions correctly and introduces no regressions.
  • The changes are minimal, targeted, and consistent with the existing permission patterns across all other service routers. The new saveEnvironment endpoint for compose follows the exact same structure as the already-shipped endpoints for postgres/redis/mysql/mariadb/mongo. The schema omissions for env in the update validators are safe because no other frontend components pass env through those update mutations. The fix for loadServices is a straightforward and clearly correct permission downgrade.
  • No files require special attention.

Reviews (1): Last reviewed commit: "fix(permissions): decouple env editing a..." | Re-trigger Greptile

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

…Create Services" permission

The "Create Services" (`canCreateServices`) permission was incorrectly
required for two unrelated operations:

1. Loading compose service names in the domain form dropdown
   (`compose.loadServices` checked `service: ["create"]` for a read-only
   operation). Changed to `service: ["read"]`.

2. Editing environment variables on database and compose services. The
   `ShowEnvironment` component used the generic `update` mutation (which
   checks `service: ["create"]`) instead of the dedicated
   `saveEnvironment` endpoints (which correctly check `envVars: ["write"]`).

Changes:
- compose.loadServices: service: ["create"] → service: ["read"]
- Omit `env` from update schemas for postgres, mysql, mariadb, redis,
  mongo, and compose to prevent env updates through the update endpoint
- Add `saveEnvironment` endpoint to compose router (matching the pattern
  already used by all 5 database routers)
- Update ShowEnvironment component to use `saveEnvironment` mutations
  with correctly typed per-service payloads

Made-with: Cursor
@nizepart nizepart requested a review from Siumauricio as a code owner March 23, 2026 18:10
@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Mar 23, 2026
@dosubot
Copy link
Copy Markdown

dosubot bot commented Mar 23, 2026

Related Documentation

1 document(s) may need updating based on files changed in this PR:

Dokploy's Space

README /dokploy/blob/canary/apps/api/README.md
View Suggested Changes
@@ -173,6 +173,41 @@
 }
 ```
 
+### compose.loadServices
+
+Lists service names from a Docker Compose file (used for domain service dropdown).
+
+**Query Parameters:**
+- `composeId` (required) - The ID of the compose service
+- `type` (required) - The compose file type
+
+**Permissions:**
+Requires `service: ["read"]` permission (read-only operation).
+
+**Response:**
+Returns an array of service names parsed from the compose file.
+
+### compose.saveEnvironment
+
+Update environment variables for a compose service.
+
+**Parameters:**
+- `composeId` (required) - The ID of the compose service
+- `env` (required) - Environment variables string
+
+**Permissions:**
+Requires `envVars: ["write"]` permission.
+
+**Example Input:**
+```json
+{
+  "composeId": "string",
+  "env": "KEY1=value1\nKEY2=value2"
+}
+```
+
+**Note:** The `compose.update` endpoint does not accept the `env` field. Use this dedicated endpoint for updating environment variables.
+
 ### environment.search
 
 Search environments by name and description.
@@ -293,6 +328,32 @@
 - `dockerImage` (optional string) - Specifies a custom Docker image for the database service. This allows users to use specific versions or custom-built images instead of the default image for the database type. Available for all five database services (PostgreSQL, MySQL, MariaDB, MongoDB, and Redis).
 
 Additional service-specific parameters are available depending on the database type. The `dockerImage` field provides enhanced configuration flexibility for advanced use cases such as version pinning or using specialized database distributions.
+
+**Important:** The `.update` endpoints **do not accept** the `env` field for updating environment variables. Environment variables must be updated through the separate `saveEnvironment` endpoints described below.
+
+### Database Service Environment Variable Endpoints
+
+Each database service provides a dedicated endpoint for updating environment variables:
+- **postgres.saveEnvironment**
+- **mysql.saveEnvironment**
+- **mariadb.saveEnvironment**
+- **mongo.saveEnvironment**
+- **redis.saveEnvironment**
+
+**Parameters:**
+- Service ID (required) - The respective ID field (`postgresId`, `mysqlId`, `mariadbId`, `mongoId`, or `redisId`)
+- `env` (required) - Environment variables string
+
+**Permissions:**
+These endpoints require `envVars: ["write"]` permission.
+
+**Example Input:**
+```json
+{
+  "postgresId": "string",
+  "env": "KEY1=value1\nKEY2=value2"
+}
+```
 
 ## Whitelabeling Endpoints
 

[Accept] [Decline]

Note: You must be authenticated to accept/decline updates.

How did I do? Any feedback?  Join Discord

@Bagautdino
Copy link
Copy Markdown

LGTM 👍 This is a necessary fix - ran into this exact permission issue ourselves. Would love to see this land in stable. @nizepart appreciate you!

Copy link
Copy Markdown

@mahdirajaee mahdirajaee left a comment

Choose a reason for hiding this comment

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

This is a well-scoped permission fix. Changing compose.loadServices from service: ["create"] to service: ["read"] is clearly correct — listing service names from parsed YAML is a read-only operation and should never have required create permission. The new dedicated saveEnvironment endpoint for compose follows the exact pattern already established by postgres, redis, mysql, mariadb, and mongo routers, which makes this consistent and easy to audit.

The approach of omitting env from all apiUpdate* schemas is a good defensive measure — it ensures env modifications can only go through the saveEnvironment path with envVars: ["write"] permission, eliminating any bypass via the generic update mutation. I verified that no other frontend components pass env through those update mutations, so this is a safe change with no regression risk.

One small thing worth noting: the ShowEnvironment component's as const assertion on the payloadMap and the cast on mutateAsync could be slightly cleaner with a generic helper, but that's purely stylistic and not a blocker. The permission model is correct and backward compatible — users who previously had both permissions will see no change, while users who only had env write access will now correctly be able to edit environment variables.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Create Services" permission incorrectly required for domain service selection and environment editing

3 participants