Custom instance db password safety (#7313)
* update parsers version * safety custom instance db migration * Refresh custom instance pwd button * nit indent * nit button accent * Prevent leave without saving
This commit is contained in:
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,32 @@
|
||||
DO $$
|
||||
DECLARE
|
||||
pwd text;
|
||||
BEGIN
|
||||
SELECT gen_random_uuid()::text INTO pwd;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'custom_instance_user') THEN
|
||||
EXECUTE format('ALTER USER custom_instance_user WITH PASSWORD %L', pwd);
|
||||
RAISE NOTICE 'Updated password for existing user custom_instance_user';
|
||||
ELSE
|
||||
EXECUTE format('CREATE USER custom_instance_user WITH PASSWORD %L', pwd);
|
||||
RAISE NOTICE 'Created new user custom_instance_user';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM global_settings WHERE name = 'custom_instance_pg_databases') THEN
|
||||
INSERT INTO global_settings (name, value)
|
||||
VALUES ('custom_instance_pg_databases', jsonb_build_object(
|
||||
'user_pwd', pwd::text,
|
||||
'databases', jsonb_build_object()
|
||||
));
|
||||
RAISE NOTICE 'Inserted new global setting for custom_instance_pg_databases';
|
||||
ELSE
|
||||
UPDATE global_settings
|
||||
SET value = jsonb_set(COALESCE(value, '{}'::jsonb), '{user_pwd}', to_jsonb(pwd::text)::jsonb)
|
||||
WHERE name = 'custom_instance_pg_databases';
|
||||
RAISE NOTICE 'Updated user_pwd in existing global setting for custom_instance_pg_databases';
|
||||
END IF;
|
||||
EXCEPTION
|
||||
WHEN others THEN
|
||||
RAISE NOTICE 'custom_instance_pg_databases migration error, skipping.';
|
||||
END
|
||||
$$;
|
||||
@@ -847,6 +847,20 @@ paths:
|
||||
schema:
|
||||
type: boolean
|
||||
|
||||
/settings/refresh_custom_instance_user_pwd:
|
||||
post:
|
||||
summary: Refreshes the password for the custom_instance_user
|
||||
operationId: refreshCustomInstanceUserPwd
|
||||
tags:
|
||||
- setting
|
||||
responses:
|
||||
"200":
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
|
||||
/settings/list_custom_instance_pg_databases:
|
||||
post:
|
||||
summary: Returns the set-up statuses of custom instance pg databases
|
||||
|
||||
@@ -73,6 +73,10 @@ pub fn global_service() -> Router {
|
||||
"/list_custom_instance_pg_databases",
|
||||
post(list_custom_instance_pg_databases),
|
||||
)
|
||||
.route(
|
||||
"/refresh_custom_instance_user_pwd",
|
||||
post(refresh_custom_instance_user_pwd),
|
||||
)
|
||||
.route(
|
||||
"/setup_custom_instance_pg_database/:name",
|
||||
post(setup_custom_instance_pg_database),
|
||||
@@ -625,6 +629,47 @@ async fn list_custom_instance_pg_databases(
|
||||
return Ok(Json(result));
|
||||
}
|
||||
|
||||
async fn refresh_custom_instance_user_pwd(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
) -> JsonResult<()> {
|
||||
require_super_admin(&db, &authed.email).await?;
|
||||
// 20251208123907_safety_custom_instance_db_user_pwd.up
|
||||
let query = r#"
|
||||
DO $$
|
||||
DECLARE
|
||||
pwd text;
|
||||
BEGIN
|
||||
SELECT gen_random_uuid()::text INTO pwd;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'custom_instance_user') THEN
|
||||
EXECUTE format('ALTER USER custom_instance_user WITH PASSWORD %L', pwd);
|
||||
RAISE NOTICE 'Updated password for existing user custom_instance_user';
|
||||
ELSE
|
||||
EXECUTE format('CREATE USER custom_instance_user WITH PASSWORD %L', pwd);
|
||||
RAISE NOTICE 'Created new user custom_instance_user';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM global_settings WHERE name = 'custom_instance_pg_databases') THEN
|
||||
INSERT INTO global_settings (name, value)
|
||||
VALUES ('custom_instance_pg_databases', jsonb_build_object(
|
||||
'user_pwd', pwd::text,
|
||||
'databases', jsonb_build_object()
|
||||
));
|
||||
RAISE NOTICE 'Inserted new global setting for custom_instance_pg_databases';
|
||||
ELSE
|
||||
UPDATE global_settings
|
||||
SET value = jsonb_set(COALESCE(value, '{}'::jsonb), '{user_pwd}', to_jsonb(pwd::text)::jsonb)
|
||||
WHERE name = 'custom_instance_pg_databases';
|
||||
RAISE NOTICE 'Updated user_pwd in existing global setting for custom_instance_pg_databases';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
"#;
|
||||
sqlx::query(query).execute(&db).await?;
|
||||
Ok(Json(()))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SetupCustomInstanceDbBody {
|
||||
tag: Option<String>,
|
||||
|
||||
24
frontend/package-lock.json
generated
24
frontend/package-lock.json
generated
@@ -79,11 +79,11 @@
|
||||
"windmill-parser-wasm-java": "1.510.1",
|
||||
"windmill-parser-wasm-nu": "1.510.1",
|
||||
"windmill-parser-wasm-php": "1.574.1",
|
||||
"windmill-parser-wasm-py": "1.538.0",
|
||||
"windmill-parser-wasm-regex": "1.576.3",
|
||||
"windmill-parser-wasm-py": "1.589.3",
|
||||
"windmill-parser-wasm-regex": "1.589.3",
|
||||
"windmill-parser-wasm-ruby": "1.526.1",
|
||||
"windmill-parser-wasm-rust": "1.558.1",
|
||||
"windmill-parser-wasm-ts": "1.565.0",
|
||||
"windmill-parser-wasm-ts": "1.589.3",
|
||||
"windmill-parser-wasm-yaml": "1.561.0",
|
||||
"windmill-sql-datatype-parser-wasm": "1.512.0",
|
||||
"windmill-utils-internal": "^1.3.1",
|
||||
@@ -13851,14 +13851,14 @@
|
||||
"integrity": "sha512-COyid6B1RYs+bpzUCInsA4HY/WZkpDLfkQ90+AqU/TVTpzYSbAC2JCbIwy0cRElBvlhI4bQ+9Wg6hSQKMpEkpA=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-py": {
|
||||
"version": "1.538.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-py/-/windmill-parser-wasm-py-1.538.0.tgz",
|
||||
"integrity": "sha512-s+bdIgT/fA5em3zYUwF8D14uA/dZh7iu0krZYZQqZUO7txN37hwSCVfovbMkIwm4zPbsJ50mU8DRLt7UpAPZIw=="
|
||||
"version": "1.589.3",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-py/-/windmill-parser-wasm-py-1.589.3.tgz",
|
||||
"integrity": "sha512-ob1auYfqDyjaliXVluSKAuhfW09TefJ1rJjfDBnV5KCumkTBYGSchrGU+2AJYiuwCtljRE5CzTPBAo6JTaSKHA=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-regex": {
|
||||
"version": "1.576.3",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.576.3.tgz",
|
||||
"integrity": "sha512-+5or2Gzi2W+DXU3cDqUB2sn3t3t1ZJ5BGpEh94r30fbm9yGxSy9QvxsURqv59ubKCjmm3oKwqApSyn+OqXSLjg=="
|
||||
"version": "1.589.3",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.589.3.tgz",
|
||||
"integrity": "sha512-yfYxHtrTuW+vqovPvu1h0t9XmC1cLzE6Quxb6fke3rxKYxQ3scO31zvIeZs2hGlvi/nzJjtCCgmZRyqD6RVQXQ=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-ruby": {
|
||||
"version": "1.526.1",
|
||||
@@ -13871,9 +13871,9 @@
|
||||
"integrity": "sha512-21S7lm1KF8zO1187rbq14hzPHII2RdM2+D44MoAh1F6VoaScj+Puq0z5B1O/hwn/95R/a9jBlL2D8jbkXtlD1A=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-ts": {
|
||||
"version": "1.565.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-ts/-/windmill-parser-wasm-ts-1.565.0.tgz",
|
||||
"integrity": "sha512-ui7dQ2kizWSG0ELpDU70Ccfk47EjC+yLI3tf6O+BdINs+YTjxA65MjShsvE3l4MnaxN8knbOLoSHkvIYCu77mg=="
|
||||
"version": "1.589.3",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-ts/-/windmill-parser-wasm-ts-1.589.3.tgz",
|
||||
"integrity": "sha512-PNyzEWo3qJxlocz1qmeh4IOxm8TzCeyAqqXFO4oyz9Lu6WAyCaSfefhZfaZG01wz3J1hJTZxymB9dDiv0VxgzQ=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-yaml": {
|
||||
"version": "1.561.0",
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
"@melt-ui/pp": "^0.3.2",
|
||||
"@melt-ui/svelte": "^0.86.2",
|
||||
"@playwright/test": "^1.34.3",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@sveltejs/adapter-static": "^3.0.6",
|
||||
"@sveltejs/kit": "^2.28.0",
|
||||
"@sveltejs/package": "^2.3.7",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.1.1",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@types/d3": "^7.4.0",
|
||||
"@types/d3-zoom": "^3.0.3",
|
||||
@@ -146,11 +146,11 @@
|
||||
"windmill-parser-wasm-java": "1.510.1",
|
||||
"windmill-parser-wasm-nu": "1.510.1",
|
||||
"windmill-parser-wasm-php": "1.574.1",
|
||||
"windmill-parser-wasm-py": "1.538.0",
|
||||
"windmill-parser-wasm-regex": "1.576.3",
|
||||
"windmill-parser-wasm-py": "1.589.3",
|
||||
"windmill-parser-wasm-regex": "1.589.3",
|
||||
"windmill-parser-wasm-ruby": "1.526.1",
|
||||
"windmill-parser-wasm-rust": "1.558.1",
|
||||
"windmill-parser-wasm-ts": "1.565.0",
|
||||
"windmill-parser-wasm-ts": "1.589.3",
|
||||
"windmill-parser-wasm-yaml": "1.561.0",
|
||||
"windmill-sql-datatype-parser-wasm": "1.512.0",
|
||||
"windmill-utils-internal": "^1.3.1",
|
||||
@@ -543,4 +543,4 @@
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.35.0",
|
||||
"fsevents": "^2.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,11 @@
|
||||
import type { ConfirmationModalHandle } from '../common/confirmationModal/asyncConfirmationModal.svelte'
|
||||
import ExploreAssetButton from '../ExploreAssetButton.svelte'
|
||||
import type DBManagerDrawer from '../DBManagerDrawer.svelte'
|
||||
import { ArrowRight } from 'lucide-svelte'
|
||||
import { ArrowRight, InfoIcon } from 'lucide-svelte'
|
||||
import type { Snippet } from 'svelte'
|
||||
import { truncate } from '$lib/utils'
|
||||
import Tooltip from '../meltComponents/Tooltip.svelte'
|
||||
import { superadmin } from '$lib/stores'
|
||||
|
||||
type Props = {
|
||||
customInstanceDbs: ResourceReturn<ListCustomInstanceDbsResponse>
|
||||
@@ -81,7 +83,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 shrink-0 flex flex-col pl-4 gap-4">
|
||||
<div class="flex-1 shrink-0 flex flex-col pl-4 gap-2">
|
||||
<div class="flex-1 overflow-y-scroll">
|
||||
{#if status?.error}
|
||||
<div transition:slide={{ duration: 200 }} class="mb-4">
|
||||
@@ -141,6 +143,20 @@
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{#if $superadmin}
|
||||
<Tooltip>
|
||||
<Button
|
||||
endIcon={{ icon: InfoIcon }}
|
||||
onClick={async () => {
|
||||
await SettingService.refreshCustomInstanceUserPwd()
|
||||
sendUserToast('custom_instance_user password refreshed')
|
||||
}}>Refresh custom_instance_user password</Button
|
||||
>
|
||||
{#snippet text()}
|
||||
Try this if there is an issue with your custom instance database password.
|
||||
{/snippet}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
<Button
|
||||
size="sm"
|
||||
variant={!status?.success ? 'accent' : 'default'}
|
||||
|
||||
@@ -150,6 +150,10 @@
|
||||
}
|
||||
return map
|
||||
})
|
||||
|
||||
export function unsavedChanges(): { savedValue: any; modifiedValue: any } {
|
||||
return { savedValue: dataTableSettings, modifiedValue: tempSettings }
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4 my-8">
|
||||
@@ -282,7 +286,7 @@
|
||||
</tbody>
|
||||
</DataTable>
|
||||
|
||||
<Button wrapperClasses="mt-4 mb-16 max-w-fit" on:click={onSave}>Save</Button>
|
||||
<Button wrapperClasses="mt-4 mb-16 max-w-fit" on:click={onSave} variant="accent">Save</Button>
|
||||
|
||||
<ConfirmationModal {...confirmationModal.props} />
|
||||
<DBManagerDrawer bind:this={dbManagerDrawer} />
|
||||
|
||||
@@ -358,6 +358,7 @@
|
||||
</DataTable>
|
||||
<Button
|
||||
wrapperClasses="mt-4 mb-16 max-w-fit"
|
||||
variant="accent"
|
||||
on:click={onSave}
|
||||
disabled={ducklakeSavedSettings.ducklakes.length === ducklakeSettings.ducklakes.length &&
|
||||
Object.values(ducklakeIsDirty).every((v) => v === false)}
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
})
|
||||
|
||||
let dataTableSettings: DataTableSettingsType = $state({ dataTables: [] })
|
||||
let dataTableSettingsComponent: DataTableSettings | undefined = $state(undefined)
|
||||
|
||||
let ducklakeSettings: DucklakeSettingsType = $state({ ducklakes: [] })
|
||||
let ducklakeSavedSettings: DucklakeSettingsType = $state(untrack(() => ducklakeSettings))
|
||||
@@ -559,6 +560,10 @@
|
||||
|
||||
// Combined function to check for unsaved changes across all tabs
|
||||
function getAllUnsavedChanges() {
|
||||
if (dataTableSettingsComponent) {
|
||||
return dataTableSettingsComponent.unsavedChanges()
|
||||
}
|
||||
|
||||
// Check AI settings
|
||||
const aiChanges = getAiSettingsInitialAndModifiedValues()
|
||||
if (aiChanges.savedValue && aiChanges.modifiedValue) {
|
||||
@@ -1127,7 +1132,7 @@
|
||||
}}
|
||||
/>
|
||||
{:else if tab == 'windmill_data_tables'}
|
||||
<DataTableSettings bind:dataTableSettings />
|
||||
<DataTableSettings bind:dataTableSettings bind:this={dataTableSettingsComponent} />
|
||||
{:else if tab == 'windmill_lfs'}
|
||||
<StorageSettings
|
||||
bind:s3ResourceSettings
|
||||
|
||||
Reference in New Issue
Block a user