diff --git a/README.md b/README.md index effb08a49f..7e2d7fcf10 100644 --- a/README.md +++ b/README.md @@ -386,6 +386,7 @@ it being synced automatically everyday. | SMTP_USERNAME | None | username for the smtp server to send invite emails | Server | | SMTP_PASSWORD | None | password for the smtp server to send invite emails | Server | | SMTP_TLS_IMPLICIT | false | https://docs.rs/mail-send/latest/mail_send/struct.SmtpClientBuilder.html#method.implicit_tlsemails | Server | +| CREATE_WORKSPACE_REQUIRE_SUPERADMIN | false | If true, only superadmin can create workspaces | Server | ## Run a local dev setup diff --git a/backend/src/main.rs b/backend/src/main.rs index 9bcb179cc2..643ddf59b2 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -187,6 +187,7 @@ Windmill Community Edition {GIT_VERSION} "SMTP_USERNAME", "SMTP_PORT", "SMTP_TLS_IMPLICIT", + "CREATE_WORKSPACE_REQUIRE_SUPERADMIN", ]); tracing::info!("Loading OAuth providers...: {:#?}", *OAUTH_CLIENTS); diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 0f87fa6196..633ae36b7a 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -2171,7 +2171,6 @@ paths: schema: {} language: type: string - enum: [deno, python3, go, bash, postgresql] summary: type: string required: @@ -2201,7 +2200,8 @@ paths: schema: type: string - name: first_parent_hash - description: mask to filter scripts whom first direct parent has exact hash + description: + mask to filter scripts whom first direct parent has exact hash in: query schema: type: string @@ -2369,7 +2369,8 @@ paths: /workers/custom_tags: get: - summary: get all instance custom tags (tags are used to dispatch jobs to + summary: + get all instance custom tags (tags are used to dispatch jobs to different worker groups) operationId: getCustomTags tags: @@ -2420,7 +2421,8 @@ paths: /w/{workspace}/scripts/delete/h/{hash}: post: - summary: delete script by hash (erase content but keep hash, require admin) + summary: + delete script by hash (erase content but keep hash, require admin) operationId: deleteScriptByHash tags: - script @@ -2612,14 +2614,16 @@ paths: type: string format: date-time - name: scheduled_in_secs - description: schedule the script to execute in the number of seconds starting now + description: + schedule the script to execute in the number of seconds starting now in: query schema: type: integer - $ref: "#/components/parameters/ParentJob" - $ref: "#/components/parameters/NewJobId" - name: invisible_to_owner - description: make the run invisible to the the script owner (default false) + description: + make the run invisible to the the script owner (default false) in: query schema: type: boolean @@ -3498,7 +3502,8 @@ paths: type: string format: date-time - name: scheduled_in_secs - description: schedule the script to execute in the number of seconds starting now + description: + schedule the script to execute in the number of seconds starting now in: query schema: type: integer @@ -3506,7 +3511,8 @@ paths: - $ref: "#/components/parameters/NewJobId" - $ref: "#/components/parameters/IncludeHeader" - name: invisible_to_owner - description: make the run invisible to the the flow owner (default false) + description: + make the run invisible to the the flow owner (default false) in: query schema: type: boolean @@ -3544,7 +3550,8 @@ paths: type: string format: date-time - name: scheduled_in_secs - description: schedule the script to execute in the number of seconds starting now + description: + schedule the script to execute in the number of seconds starting now in: query schema: type: integer @@ -3552,7 +3559,8 @@ paths: - $ref: "#/components/parameters/NewJobId" - $ref: "#/components/parameters/IncludeHeader" - name: invisible_to_owner - description: make the run invisible to the the script owner (default false) + description: + make the run invisible to the the script owner (default false) in: query schema: type: boolean @@ -3583,7 +3591,8 @@ paths: - $ref: "#/components/parameters/WorkspaceId" - $ref: "#/components/parameters/IncludeHeader" - name: invisible_to_owner - description: make the run invisible to the the script owner (default false) + description: + make the run invisible to the the script owner (default false) in: query schema: type: boolean @@ -3616,7 +3625,8 @@ paths: - $ref: "#/components/parameters/WorkspaceId" - $ref: "#/components/parameters/IncludeHeader" - name: invisible_to_owner - description: make the run invisible to the the script owner (default false) + description: + make the run invisible to the the script owner (default false) in: query schema: type: boolean @@ -3987,7 +3997,8 @@ paths: /w/{workspace}/jobs/resume_urls/{id}/{resume_id}: get: - summary: get resume urls given a job_id, resume_id and a nonce to resume a flow + summary: + get resume urls given a job_id, resume_id and a nonce to resume a flow operationId: getResumeUrls tags: - job @@ -4459,7 +4470,8 @@ paths: - $ref: "#/components/parameters/WorkspaceId" - name: only_member_of in: query - description: only list the groups the user is member of (default false) + description: + only list the groups the user is member of (default false) schema: type: boolean responses: @@ -4647,7 +4659,8 @@ paths: - $ref: "#/components/parameters/WorkspaceId" - name: only_member_of in: query - description: only list the folders the user is member of (default false) + description: + only list the folders the user is member of (default false) schema: type: boolean responses: @@ -5285,7 +5298,8 @@ components: type: integer PerPage: name: per_page - description: number of items to return for a given page (default 30, max 100) + description: + number of items to return for a given page (default 30, max 100) in: query schema: type: integer @@ -5401,7 +5415,8 @@ components: type: boolean ArgsFilter: name: args - description: filter on jobs containing those args as a json subset (@> in postgres) + description: + filter on jobs containing those args as a json subset (@> in postgres) in: query schema: type: string @@ -5413,7 +5428,8 @@ components: type: string ResultFilter: name: result - description: filter on jobs containing those result as a json subset (@> in postgres) + description: + filter on jobs containing those result as a json subset (@> in postgres) in: query schema: type: string diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index cb94d0e356..28ddca5fc3 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -698,15 +698,22 @@ async fn check_name_conflict<'c>(tx: &mut Transaction<'c, Postgres>, w_id: &str) return Ok(()); } +lazy_static::lazy_static! { + + pub static ref CREATE_WORKSPACE_REQUIRE_SUPERADMIN: bool = std::env::var("CREATE_WORKSPACE_REQUIRE_SUPERADMIN").is_ok_and(|x| x.parse::().unwrap_or(false)); + +} + async fn create_workspace( authed: Authed, Extension(db): Extension, Json(nw): Json, ) -> Result { - if &nw.username == "bot" { - return Err(Error::BadRequest("bot is a reserved username".to_string())); + + let mut tx: Transaction<'_, Postgres> = db.begin().await?; + if *CREATE_WORKSPACE_REQUIRE_SUPERADMIN { + require_super_admin(&mut tx, &authed.email).await?; } - let mut tx = db.begin().await?; check_name_conflict(&mut tx, &nw.id).await?; sqlx::query!( "INSERT INTO workspace