mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-31 23:55:23 +00:00
feat: move vault ssh signing into a plugin (C5)
This commit is contained in:
1 parent
b027717c46
commit
6e56c90aac
198 files changed
+19717
-3445
No files matched your search
@@ -267,7 +267,7 @@ http {
|
|||||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ ^/vault(/.*)?$ {
|
location = /vault/oidc/callback {
|
||||||
proxy_pass http://127.0.0.1:30001;
|
proxy_pass http://127.0.0.1:30001;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
|
|||||||
+1
-1
@@ -247,7 +247,7 @@ http {
|
|||||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ ^/vault(/.*)?$ {
|
location = /vault/oidc/callback {
|
||||||
proxy_pass http://127.0.0.1:30001;
|
proxy_pass http://127.0.0.1:30001;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- vault_profiles and vault_tokens move to the vault plugin's adopted tables
|
||||||
|
-- at activation, and ssh_data.vault_profile_id is copied into the vault
|
||||||
|
-- plugin's host settings by a boot migration after this runs. Dropping any
|
||||||
|
-- of them here would lose the data first.
|
||||||
|
SELECT 1;
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -379,6 +379,13 @@
|
|||||||
"when": 1790295071302,
|
"when": 1790295071302,
|
||||||
"tag": "0053_magical_groot",
|
"tag": "0053_magical_groot",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 54,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1790298804983,
|
||||||
|
"tag": "0054_wakeful_misty_knight",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- vault_profiles and vault_tokens move to the vault plugin's adopted tables
|
||||||
|
-- at activation, and ssh_data.vault_profile_id is copied into the vault
|
||||||
|
-- plugin's host settings by a boot migration after this runs. Dropping any
|
||||||
|
-- of them here would lose the data first.
|
||||||
|
SELECT 1;
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -386,6 +386,13 @@
|
|||||||
"when": 1790295070561,
|
"when": 1790295070561,
|
||||||
"tag": "0054_cloudy_galactus",
|
"tag": "0054_cloudy_galactus",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 55,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1790298804216,
|
||||||
|
"tag": "0055_jazzy_quasimodo",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- vault_profiles and vault_tokens move to the vault plugin's adopted tables
|
||||||
|
-- at activation, and ssh_data.vault_profile_id is copied into the vault
|
||||||
|
-- plugin's host settings by a boot migration after this runs. Dropping any
|
||||||
|
-- of them here would lose the data first.
|
||||||
|
SELECT 1;
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -358,6 +358,13 @@
|
|||||||
"when": 1790295069807,
|
"when": 1790295069807,
|
||||||
"tag": "0050_clear_white_queen",
|
"tag": "0050_clear_white_queen",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 51,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1790298803438,
|
||||||
|
"tag": "0051_conscious_blur",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Generated
+11
@@ -7060,6 +7060,10 @@
|
|||||||
"resolved": "plugins/tunnels",
|
"resolved": "plugins/tunnels",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@termix-plugin/vault": {
|
||||||
|
"resolved": "plugins/vault",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@termix-plugin/wake-on-lan": {
|
"node_modules/@termix-plugin/wake-on-lan": {
|
||||||
"resolved": "plugins/wake-on-lan",
|
"resolved": "plugins/wake-on-lan",
|
||||||
"link": true
|
"link": true
|
||||||
@@ -18772,6 +18776,13 @@
|
|||||||
"@termix/plugin-sdk": "*"
|
"@termix/plugin-sdk": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"plugins/vault": {
|
||||||
|
"name": "@termix-plugin/vault",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@termix/plugin-sdk": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"plugins/wake-on-lan": {
|
"plugins/wake-on-lan": {
|
||||||
"name": "@termix-plugin/wake-on-lan",
|
"name": "@termix-plugin/wake-on-lan",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ artifacts in lockstep: the `schema.ts` declaration, the `index.ts` DDL, and the
|
|||||||
regenerated `schema.pg.ts`/`schema.mysql.ts` plus drizzle migrations.
|
regenerated `schema.pg.ts`/`schema.mysql.ts` plus drizzle migrations.
|
||||||
|
|
||||||
**Sync entities are registered, not hardcoded.** `ctx.sync.registerEntity`
|
**Sync entities are registered, not hardcoded.** `ctx.sync.registerEntity`
|
||||||
adds an entity to remote sync; core registers its own ten the same way in
|
adds an entity to remote sync; core registers its own the same way in
|
||||||
`database/routes/sync-entities.ts`. The wire names are unchanged, because
|
`database/routes/sync-entities.ts`. The wire names are unchanged, because
|
||||||
existing tombstones and rows on the far side match on those strings. The
|
existing tombstones and rows on the far side match on those strings. The
|
||||||
Electron client asks the server for the ordered list and falls back to its
|
Electron client asks the server for the ordered list and falls back to its
|
||||||
@@ -1008,12 +1008,10 @@ auto-session and trusted devices, plus the base SSH auth types password, key,
|
|||||||
stored credential, agent and none. Every other login method, second factor and
|
stored credential, agent and none. Every other login method, second factor and
|
||||||
SSH auth method is a plugin through `ctx.auth`.
|
SSH auth method is a plugin through `ctx.auth`.
|
||||||
|
|
||||||
In 2.9.0, Vault still lives in core. It registers through the same
|
**C5** moved Vault, the last SSH auth method core still registered, into
|
||||||
interface from `src/backend/auth/legacy-providers.ts` with
|
its plugin and deleted `src/backend/auth/legacy-providers.ts`. **C4** had
|
||||||
`pluginId: "core"`. Its Phase C step moves its block out of that file; D1
|
already deleted `src/ui/auth/legacy-auth-ui.tsx`. Core registers only the
|
||||||
deletes it. **C4** moved Step-CA out and deleted
|
base types and branches on no plugin type name.
|
||||||
`src/ui/auth/legacy-auth-ui.tsx`, which had no other editor left.
|
|
||||||
Nothing else in core branches on those type names.
|
|
||||||
|
|
||||||
**C1** moved TOTP and passkeys out:
|
**C1** moved TOTP and passkeys out:
|
||||||
|
|
||||||
@@ -1137,7 +1135,7 @@ Nothing else in core branches on those type names.
|
|||||||
`requires_browser_sign_in` and continue on `connect-browser-sign-in`.
|
`requires_browser_sign_in` and continue on `connect-browser-sign-in`.
|
||||||
- `@termix/plugin-sdk/ssh-certs` (`applyCertificateAuth`) replaces core's
|
- `@termix/plugin-sdk/ssh-certs` (`applyCertificateAuth`) replaces core's
|
||||||
`opkssh-cert-auth.ts`: opkssh, step-ca, core key auth with a CA
|
`opkssh-cert-auth.ts`: opkssh, step-ca, core key auth with a CA
|
||||||
certificate, and the Vault provider still in core use it.
|
certificate, and vault use it.
|
||||||
|
|
||||||
**C4** moved Step-CA out:
|
**C4** moved Step-CA out:
|
||||||
|
|
||||||
@@ -1159,6 +1157,39 @@ Nothing else in core branches on those type names.
|
|||||||
- The sign-in dialog is a `terminal.overlay` contribution speaking
|
- The sign-in dialog is a `terminal.overlay` contribution speaking
|
||||||
`stepca_*`.
|
`stepca_*`.
|
||||||
|
|
||||||
|
**C5** moved HashiCorp Vault out:
|
||||||
|
|
||||||
|
- `vault` registers the `vault` SSH auth provider. It adopts
|
||||||
|
`vault_profiles` and `vault_tokens` as `p_vault_profiles` and
|
||||||
|
`p_vault_tokens`. Profiles keep their ids and sync ids; cached
|
||||||
|
certificates are sealed with `ctx.secrets.seal`, and the ones 2.8 wrote
|
||||||
|
with the user's data key do not unseal, so they are dropped and cost one
|
||||||
|
sign-in. A fresh install has no foreign key from a token to its profile
|
||||||
|
(the SDK has no builder for one to a plugin's own table), so deleting a
|
||||||
|
profile deletes its tokens in code.
|
||||||
|
- A host's profile is the `profileId` host setting (hidden, edited in the
|
||||||
|
plugin's `registerSshAuthEditor` editor, which also holds the profile
|
||||||
|
manager where the host editor always had it).
|
||||||
|
`vault-settings-migration.ts` copies `ssh_data.vault_profile_id` into it;
|
||||||
|
the column is out of `schema.ts` and its drizzle drop is a no-op.
|
||||||
|
- Vault is reached through `ctx.fetch`, with the profile's own Vault host
|
||||||
|
passed as `allowPrivateHosts`, since Vault servers are usually internal.
|
||||||
|
- Profile routes are `/plugin-api/vault/profiles`, gated on `vault.use`;
|
||||||
|
sharing a profile needs `vault.share` (admins by default), which replaces
|
||||||
|
2.8's admin check. Only the owner edits or deletes one.
|
||||||
|
- The sync entity keeps its wire name `vaultProfiles` and order 20. Hosts no
|
||||||
|
longer reference it, so core's host entity lost its `vaultProfileId`
|
||||||
|
reference and `sync-references.ts` has no Vault case.
|
||||||
|
- The callback is `/plugin-api/vault/oidc/callback` (public). The boot
|
||||||
|
migration decides `legacyCallback` once: on when the 2.8 database used
|
||||||
|
Vault, so Termix keeps sending `<base>/vault/oidc/callback`, the URI Vault
|
||||||
|
roles allow, which core answers with a 307 to the plugin
|
||||||
|
(`registerVaultCompatRoutes` in `host-compat-routes.ts`; nginx proxies
|
||||||
|
only that path now).
|
||||||
|
- The sign-in dialog is a `terminal.overlay` contribution. It keeps the 2.8
|
||||||
|
message names (`vault_auth_url`, `vault_completed`, `vault_error`) for
|
||||||
|
Termix-Mobile.
|
||||||
|
|
||||||
#### One SSH connect pipeline
|
#### One SSH connect pipeline
|
||||||
|
|
||||||
Every SSH connection, core's and plugins', goes through
|
Every SSH connection, core's and plugins', goes through
|
||||||
@@ -2298,8 +2329,7 @@ upgrade on all three engines, and the checks in step 15 are green.
|
|||||||
(`ctx.disposables.add`). Nothing at module scope. `deactivate` is usually
|
(`ctx.disposables.add`). Nothing at module scope. `deactivate` is usually
|
||||||
empty.
|
empty.
|
||||||
4. **When the SDK does not have it, add it to the SDK.** Never work around a
|
4. **When the SDK does not have it, add it to the SDK.** Never work around a
|
||||||
gap with a core import, and never add to
|
gap with a core import. A new ctx member is:
|
||||||
`src/backend/auth/legacy-providers.ts`. A new ctx member is:
|
|
||||||
1. the type and a doc comment in `packages/plugin-sdk/src/backend.ts`;
|
1. the type and a doc comment in `packages/plugin-sdk/src/backend.ts`;
|
||||||
2. the implementation in `src/backend/plugins/ctx.ts`, wrapped in `guarded()`
|
2. the implementation in `src/backend/plugins/ctx.ts`, wrapped in `guarded()`
|
||||||
when it is privileged, so it checks the capability and writes an audit
|
when it is privileged, so it checks the capability and writes an audit
|
||||||
@@ -2540,7 +2570,7 @@ Then, with the app running:
|
|||||||
The bundled plugins predate the SDK, apart from workspaces (A9), snippets
|
The bundled plugins predate the SDK, apart from workspaces (A9), snippets
|
||||||
(B2), remote-desktop (B14), docker (B15), host-metrics (B16), automations
|
(B2), remote-desktop (B14), docker (B15), host-metrics (B16), automations
|
||||||
(B17), ai (B18), homepage (B19), totp and webauthn (C1), sso and ldap (C2),
|
(B17), ai (B18), homepage (B19), totp and webauthn (C1), sso and ldap (C2),
|
||||||
opkssh and warpgate (C3), step-ca (C4),
|
opkssh and warpgate (C3), step-ca (C4), vault (C5),
|
||||||
which import nothing from core. The others still reach core by relative
|
which import nothing from core. The others still reach core by relative
|
||||||
path (`../../../../src/backend/...`), which an esbuild plugin,
|
path (`../../../../src/backend/...`), which an esbuild plugin,
|
||||||
`packages/plugin-sdk/cli/lib/legacy-core-imports.mjs`, keeps out of the bundle
|
`packages/plugin-sdk/cli/lib/legacy-core-imports.mjs`, keeps out of the bundle
|
||||||
|
|||||||
@@ -529,6 +529,31 @@ build`'s esbuild step has no static-asset-copy pipeline the way core's Vite
|
|||||||
rejects (it then downloads the pinned version instead). Pass
|
rejects (it then downloads the pinned version instead). Pass
|
||||||
`OPKSSH_VERSION` and the matching `OPKSSH_SHA256` as `ENV` in the final
|
`OPKSSH_VERSION` and the matching `OPKSSH_SHA256` as `ENV` in the final
|
||||||
stage. Owner: D0.
|
stage. Owner: D0.
|
||||||
|
- **C5 (vault):** the Termix docs still describe Vault profiles with the
|
||||||
|
`/vault/oidc/callback` redirect URI. They need the plugin settings page,
|
||||||
|
the new redirect URI (`/plugin-api/vault/oidc/callback`) and the
|
||||||
|
`legacyCallback` setting. Owner: D0 (docs repo).
|
||||||
|
- **C5 (vault):** a host's Vault profile is the plugin's `profileId` host
|
||||||
|
setting now, and host-scope plugin settings do not travel over remote sync
|
||||||
|
(the host entity used to carry `vaultProfileSyncId`). Profiles still sync
|
||||||
|
as `vaultProfiles`; the host on the other side has to pick its profile
|
||||||
|
again. Needs a generic way to sync host-scope plugin settings, the same gap
|
||||||
|
the B7 export/import line describes. Owner: D1.
|
||||||
|
- **C5 (vault), for D2:** `vaultProfileId` was an owner-private auth field a
|
||||||
|
shared-host recipient with edit access could not change. As a host setting
|
||||||
|
it is written through `/plugins/vault/settings/host/:hostId`, which only
|
||||||
|
checks edit access, so such a recipient can now point the host at another
|
||||||
|
profile. Same question as the B14 line on per-field sharing levels.
|
||||||
|
Owner: D2.
|
||||||
|
- **C5 (vault):** `ssh_data.vault_profile_id` stays in the database,
|
||||||
|
unused, after `vault-settings-migration.ts` copies it (kept so a downgrade
|
||||||
|
works, and the pg/mysql foreign key on it now points at
|
||||||
|
`p_vault_profiles`). Drop it in 3.0.0. Owner: 3.0.0.
|
||||||
|
- **Pre-existing, found in C5:** `npm run test -w plugins/ssh-terminal`
|
||||||
|
fails "ssh-terminal activate > serves the terminal socket": it expects
|
||||||
|
`mock.wsRoutes` to equal `[{ path, raw }]`, but `FakeWsRoute` now also
|
||||||
|
records `handler` and `options`. Use `toMatchObject` or
|
||||||
|
`expect.objectContaining`. Owner: D0.
|
||||||
|
|
||||||
## Manual checks after 2.9.0
|
## Manual checks after 2.9.0
|
||||||
|
|
||||||
@@ -624,3 +649,11 @@ build`'s esbuild step has no static-asset-copy pipeline the way core's Vite
|
|||||||
and the host says it needs it. A Warpgate host's toggle is on in the host
|
and the host says it needs it. A Warpgate host's toggle is on in the host
|
||||||
editor's Plugins group after the upgrade, and the terminal, file manager
|
editor's Plugins group after the upgrade, and the terminal, file manager
|
||||||
and docker each show the sign-in dialog and connect after it.
|
and docker each show the sign-in dialog and connect after it.
|
||||||
|
- Vault: on a 2.8 database every Vault profile is there and each Vault host
|
||||||
|
still has its profile selected in the host editor; the admin page shows the
|
||||||
|
old redirect URI; a terminal connect opens Vault's sign-in, signs a
|
||||||
|
certificate and connects, and a second connect skips the browser until the
|
||||||
|
certificate expires; create, edit, share and delete a profile from the host
|
||||||
|
editor (sharing only offered with "Share Vault profiles"); switch to the new
|
||||||
|
redirect URI after allowing it in the Vault role; disable the vault plugin
|
||||||
|
and a Vault host says it needs it, then enable it again.
|
||||||
@@ -584,7 +584,6 @@ export async function activate(ctx: PluginContext) {
|
|||||||
let resolvedSocks5Username = socks5Username;
|
let resolvedSocks5Username = socks5Username;
|
||||||
let resolvedSocks5Password = socks5Password;
|
let resolvedSocks5Password = socks5Password;
|
||||||
let resolvedSocks5ProxyChain = socks5ProxyChain;
|
let resolvedSocks5ProxyChain = socks5ProxyChain;
|
||||||
let resolvedVaultProfileId: number | null = null;
|
|
||||||
|
|
||||||
const resolveFrom = async () => {
|
const resolveFrom = async () => {
|
||||||
const resolvedHost = hostSyncId
|
const resolvedHost = hostSyncId
|
||||||
@@ -607,8 +606,6 @@ export async function activate(ctx: PluginContext) {
|
|||||||
resolvedTerminalConfig = resolvedHost.terminalConfig as unknown as
|
resolvedTerminalConfig = resolvedHost.terminalConfig as unknown as
|
||||||
Record<string, unknown> | undefined;
|
Record<string, unknown> | undefined;
|
||||||
resolvedScpLegacy = resolvedHost.scpLegacy ?? false;
|
resolvedScpLegacy = resolvedHost.scpLegacy ?? false;
|
||||||
resolvedVaultProfileId =
|
|
||||||
(resolvedHost.vaultProfile as { id?: number } | undefined)?.id ?? null;
|
|
||||||
if (resolvedHost.useSocks5) {
|
if (resolvedHost.useSocks5) {
|
||||||
resolvedUseSocks5 = resolvedHost.useSocks5;
|
resolvedUseSocks5 = resolvedHost.useSocks5;
|
||||||
resolvedSocks5Host = resolvedHost.socks5Host;
|
resolvedSocks5Host = resolvedHost.socks5Host;
|
||||||
@@ -717,9 +714,6 @@ export async function activate(ctx: PluginContext) {
|
|||||||
keyPassword: resolvedCredentials.keyPassword,
|
keyPassword: resolvedCredentials.keyPassword,
|
||||||
certPublicKey: resolvedCredentials.certPublicKey,
|
certPublicKey: resolvedCredentials.certPublicKey,
|
||||||
terminalConfig: resolvedTerminalConfig ?? null,
|
terminalConfig: resolvedTerminalConfig ?? null,
|
||||||
vaultProfile: resolvedVaultProfileId
|
|
||||||
? { id: resolvedVaultProfileId }
|
|
||||||
: null,
|
|
||||||
jumpHosts: resolvedJumpHosts,
|
jumpHosts: resolvedJumpHosts,
|
||||||
useSocks5: resolvedUseSocks5,
|
useSocks5: resolvedUseSocks5,
|
||||||
socks5Host: resolvedSocks5Host,
|
socks5Host: resolvedSocks5Host,
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} window",
|
"tmuxWindowCount": "{{count}} window",
|
||||||
"tmuxWindows": "Windows",
|
"tmuxWindows": "Windows",
|
||||||
"unknownError": "Unknown error occurred",
|
"unknownError": "Unknown error occurred",
|
||||||
"vaultAuthDescription": "A window has opened to sign in to HashiCorp Vault. Complete the sign-in there; this connection will continue automatically.",
|
|
||||||
"vaultAuthFailed": "Vault authentication failed. Please try again.",
|
|
||||||
"vaultAuthTitle": "Vault sign-in required",
|
|
||||||
"vaultReopen": "Reopen sign-in window",
|
|
||||||
"websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.",
|
"websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.",
|
||||||
"websocketError": "WebSocket connection error"
|
"websocketError": "WebSocket connection error"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} venster",
|
"tmuxWindowCount": "{{count}} venster",
|
||||||
"tmuxWindows": "Vensters",
|
"tmuxWindows": "Vensters",
|
||||||
"unknownError": "Onbekende fout het voorgekom",
|
"unknownError": "Onbekende fout het voorgekom",
|
||||||
"vaultAuthDescription": "'n Venster het oopgemaak om by HashiCorp Vault aan te meld. Voltooi die aanmelding daar; hierdie verbinding sal outomaties voortgaan.",
|
|
||||||
"vaultAuthFailed": "Kluisverifikasie het misluk. Probeer asseblief weer.",
|
|
||||||
"vaultAuthTitle": "Kluisaanmelding vereis",
|
|
||||||
"vaultReopen": "Maak aanmeldvenster weer oop",
|
|
||||||
"websocketAbnormalClose": "Verbinding het onverwags gesluit. Dit kan wees as gevolg van 'n omgekeerde proxy- of SSL-konfigurasieprobleem. Gaan asseblief bedienerlogboeke na.",
|
"websocketAbnormalClose": "Verbinding het onverwags gesluit. Dit kan wees as gevolg van 'n omgekeerde proxy- of SSL-konfigurasieprobleem. Gaan asseblief bedienerlogboeke na.",
|
||||||
"websocketError": "WebSocket-verbindingsfout"
|
"websocketError": "WebSocket-verbindingsfout"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} نافذة",
|
"tmuxWindowCount": "{{count}} نافذة",
|
||||||
"tmuxWindows": "النوافذ",
|
"tmuxWindows": "النوافذ",
|
||||||
"unknownError": "حدث خطأ غير معروف",
|
"unknownError": "حدث خطأ غير معروف",
|
||||||
"vaultAuthDescription": "تم فتح نافذة لتسجيل الدخول إلى HashiCorp Vault. أكمل تسجيل الدخول هناك؛ سيستمر هذا الاتصال تلقائيًا.",
|
|
||||||
"vaultAuthFailed": "فشلت مصادقة Vault. يرجى المحاولة مرة أخرى.",
|
|
||||||
"vaultAuthTitle": "مطلوب تسجيل الدخول إلى Vault",
|
|
||||||
"vaultReopen": "أعد فتح نافذة تسجيل الدخول",
|
|
||||||
"websocketAbnormalClose": "أُغلق الاتصال بشكل غير متوقع. قد يكون السبب مشكلة في الوكيل العكسي أو إعدادات SSL. يرجى مراجعة سجلات الخادم.",
|
"websocketAbnormalClose": "أُغلق الاتصال بشكل غير متوقع. قد يكون السبب مشكلة في الوكيل العكسي أو إعدادات SSL. يرجى مراجعة سجلات الخادم.",
|
||||||
"websocketError": "خطأ في اتصال WebSocket"
|
"websocketError": "خطأ في اتصال WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} прозорец",
|
"tmuxWindowCount": "{{count}} прозорец",
|
||||||
"tmuxWindows": "Прозорци",
|
"tmuxWindows": "Прозорци",
|
||||||
"unknownError": "Възникна неизвестна грешка",
|
"unknownError": "Възникна неизвестна грешка",
|
||||||
"vaultAuthDescription": "Отвори се прозорец за влизане в HashiCorp Vault. Завършете влизането там; тази връзка ще продължи автоматично.",
|
|
||||||
"vaultAuthFailed": "Удостоверяването на трезора не бе успешно. Моля, опитайте отново.",
|
|
||||||
"vaultAuthTitle": "Изисква се влизане в трезора",
|
|
||||||
"vaultReopen": "Отваряне отново на прозореца за влизане",
|
|
||||||
"websocketAbnormalClose": "Връзката се прекъсна неочаквано. Това може да се дължи на проблем с обратен прокси или SSL конфигурация. Моля, проверете лог файловете на сървъра.",
|
"websocketAbnormalClose": "Връзката се прекъсна неочаквано. Това може да се дължи на проблем с обратен прокси или SSL конфигурация. Моля, проверете лог файловете на сървъра.",
|
||||||
"websocketError": "Грешка при свързване с WebSocket"
|
"websocketError": "Грешка при свързване с WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} জানালা",
|
"tmuxWindowCount": "{{count}} জানালা",
|
||||||
"tmuxWindows": "উইন্ডোজ",
|
"tmuxWindows": "উইন্ডোজ",
|
||||||
"unknownError": "অজানা ত্রুটি ঘটেছে",
|
"unknownError": "অজানা ত্রুটি ঘটেছে",
|
||||||
"vaultAuthDescription": "হ্যাশিকর্প ভল্টে সাইন ইন করার জন্য একটি উইন্ডো খুলেছে। সেখানে সাইন-ইন সম্পন্ন করুন; এই সংযোগটি স্বয়ংক্রিয়ভাবে চলতে থাকবে।",
|
|
||||||
"vaultAuthFailed": "ভল্ট প্রমাণীকরণ ব্যর্থ হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।",
|
|
||||||
"vaultAuthTitle": "ভল্টে সাইন-ইন করা আবশ্যক",
|
|
||||||
"vaultReopen": "সাইন-ইন উইন্ডো পুনরায় খুলুন",
|
|
||||||
"websocketAbnormalClose": "সংযোগটি অপ্রত্যাশিতভাবে বন্ধ হয়ে গেছে। এটি রিভার্স প্রক্সি বা SSL কনফিগারেশন সংক্রান্ত সমস্যার কারণে হতে পারে। অনুগ্রহ করে সার্ভার লগ পরীক্ষা করুন।",
|
"websocketAbnormalClose": "সংযোগটি অপ্রত্যাশিতভাবে বন্ধ হয়ে গেছে। এটি রিভার্স প্রক্সি বা SSL কনফিগারেশন সংক্রান্ত সমস্যার কারণে হতে পারে। অনুগ্রহ করে সার্ভার লগ পরীক্ষা করুন।",
|
||||||
"websocketError": "ওয়েবসকেট সংযোগ ত্রুটি"
|
"websocketError": "ওয়েবসকেট সংযোগ ত্রুটি"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "finestra {{count}}",
|
"tmuxWindowCount": "finestra {{count}}",
|
||||||
"tmuxWindows": "Finestres",
|
"tmuxWindows": "Finestres",
|
||||||
"unknownError": "S'ha produït un error desconegut",
|
"unknownError": "S'ha produït un error desconegut",
|
||||||
"vaultAuthDescription": "S'ha obert una finestra per iniciar la sessió a HashiCorp Vault. Completeu l'inici de sessió allà; aquesta connexió continuarà automàticament.",
|
|
||||||
"vaultAuthFailed": "L'autenticació de la caixa forta ha fallat. Torna-ho a provar.",
|
|
||||||
"vaultAuthTitle": "Cal iniciar sessió a la caixa forta",
|
|
||||||
"vaultReopen": "Torna a obrir la finestra d'inici de sessió",
|
|
||||||
"websocketAbnormalClose": "La connexió s'ha tancat inesperadament. Això pot ser degut a un problema de configuració del proxy invers o de l'SSL. Si us plau, reviseu els registres del servidor.",
|
"websocketAbnormalClose": "La connexió s'ha tancat inesperadament. Això pot ser degut a un problema de configuració del proxy invers o de l'SSL. Si us plau, reviseu els registres del servidor.",
|
||||||
"websocketError": "Error de connexió de WebSocket"
|
"websocketError": "Error de connexió de WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} okno",
|
"tmuxWindowCount": "{{count}} okno",
|
||||||
"tmuxWindows": "Okna",
|
"tmuxWindows": "Okna",
|
||||||
"unknownError": "Došlo k neznámé chybě",
|
"unknownError": "Došlo k neznámé chybě",
|
||||||
"vaultAuthDescription": "Otevřelo se okno pro přihlášení do HashiCorp Vault. Dokončete přihlášení tam; toto připojení bude automaticky pokračovat.",
|
|
||||||
"vaultAuthFailed": "Ověření v trezoru se nezdařilo. Zkuste to prosím znovu.",
|
|
||||||
"vaultAuthTitle": "Vyžaduje se přihlášení do trezoru",
|
|
||||||
"vaultReopen": "Znovu otevřít přihlašovací okno",
|
|
||||||
"websocketAbnormalClose": "Spojení se neočekávaně ukončilo. Může to být způsobeno problémem s reverzní proxy nebo konfigurací SSL. Zkontrolujte prosím protokoly serveru.",
|
"websocketAbnormalClose": "Spojení se neočekávaně ukončilo. Může to být způsobeno problémem s reverzní proxy nebo konfigurací SSL. Zkontrolujte prosím protokoly serveru.",
|
||||||
"websocketError": "Chyba připojení k WebSocketu"
|
"websocketError": "Chyba připojení k WebSocketu"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} vindue",
|
"tmuxWindowCount": "{{count}} vindue",
|
||||||
"tmuxWindows": "Vinduer",
|
"tmuxWindows": "Vinduer",
|
||||||
"unknownError": "Der opstod en ukendt fejl",
|
"unknownError": "Der opstod en ukendt fejl",
|
||||||
"vaultAuthDescription": "Et vindue er åbnet, hvor du kan logge ind på HashiCorp Vault. Fuldfør loginprocessen der; forbindelsen fortsætter automatisk.",
|
|
||||||
"vaultAuthFailed": "Godkendelse af arkiv mislykkedes. Prøv igen.",
|
|
||||||
"vaultAuthTitle": "Login til Vault kræves",
|
|
||||||
"vaultReopen": "Genåbn loginvinduet",
|
|
||||||
"websocketAbnormalClose": "Forbindelsen blev lukket uventet. Dette kan skyldes et problem med reverse proxy eller SSL-konfigurationen. Tjek venligst serverlogfilerne.",
|
"websocketAbnormalClose": "Forbindelsen blev lukket uventet. Dette kan skyldes et problem med reverse proxy eller SSL-konfigurationen. Tjek venligst serverlogfilerne.",
|
||||||
"websocketError": "WebSocket-forbindelsesfejl"
|
"websocketError": "WebSocket-forbindelsesfejl"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} Fenster",
|
"tmuxWindowCount": "{{count}} Fenster",
|
||||||
"tmuxWindows": "Fenster",
|
"tmuxWindows": "Fenster",
|
||||||
"unknownError": "Unbekannter Fehler aufgetreten",
|
"unknownError": "Unbekannter Fehler aufgetreten",
|
||||||
"vaultAuthDescription": "Ein Fenster zur Anmeldung bei HashiCorp Vault wurde geöffnet. Schließen Sie die Anmeldung dort ab; diese Verbindung wird automatisch fortgesetzt.",
|
|
||||||
"vaultAuthFailed": "Vault-Authentifizierung fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
|
||||||
"vaultAuthTitle": "Vault-Anmeldung erforderlich",
|
|
||||||
"vaultReopen": "Anmeldefenster erneut öffnen",
|
|
||||||
"websocketAbnormalClose": "Verbindung unerwartet geschlossen. Dies kann auf ein Problem mit dem Reverse-Proxy oder der SSL-Konfiguration zurückzuführen sein. Bitte überprüfen Sie die Server-Logs.",
|
"websocketAbnormalClose": "Verbindung unerwartet geschlossen. Dies kann auf ein Problem mit dem Reverse-Proxy oder der SSL-Konfiguration zurückzuführen sein. Bitte überprüfen Sie die Server-Logs.",
|
||||||
"websocketError": "WebSocket-Verbindungsfehler"
|
"websocketError": "WebSocket-Verbindungsfehler"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "παράθυρο {{count}}",
|
"tmuxWindowCount": "παράθυρο {{count}}",
|
||||||
"tmuxWindows": "Παράθυρα",
|
"tmuxWindows": "Παράθυρα",
|
||||||
"unknownError": "Παρουσιάστηκε άγνωστο σφάλμα",
|
"unknownError": "Παρουσιάστηκε άγνωστο σφάλμα",
|
||||||
"vaultAuthDescription": "Έχει ανοίξει ένα παράθυρο για να συνδεθείτε στο HashiCorp Vault. Ολοκληρώστε τη σύνδεση εκεί. Αυτή η σύνδεση θα συνεχιστεί αυτόματα.",
|
|
||||||
"vaultAuthFailed": "Ο έλεγχος ταυτότητας του vault απέτυχε. Δοκιμάστε ξανά.",
|
|
||||||
"vaultAuthTitle": "Απαιτείται σύνδεση στο Vault",
|
|
||||||
"vaultReopen": "Επανάνοιγμα παραθύρου σύνδεσης",
|
|
||||||
"websocketAbnormalClose": "Η σύνδεση έκλεισε απροσδόκητα. Αυτό μπορεί να οφείλεται σε πρόβλημα αντίστροφης μεσολάβησης ή ρύθμισης παραμέτρων SSL. Ελέγξτε τα αρχεία καταγραφής διακομιστή.",
|
"websocketAbnormalClose": "Η σύνδεση έκλεισε απροσδόκητα. Αυτό μπορεί να οφείλεται σε πρόβλημα αντίστροφης μεσολάβησης ή ρύθμισης παραμέτρων SSL. Ελέγξτε τα αρχεία καταγραφής διακομιστή.",
|
||||||
"websocketError": "Σφάλμα σύνδεσης WebSocket"
|
"websocketError": "Σφάλμα σύνδεσης WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} ventana",
|
"tmuxWindowCount": "{{count}} ventana",
|
||||||
"tmuxWindows": "Ventanas",
|
"tmuxWindows": "Ventanas",
|
||||||
"unknownError": "Se ha producido un error desconocido.",
|
"unknownError": "Se ha producido un error desconocido.",
|
||||||
"vaultAuthDescription": "Se ha abierto una ventana para iniciar sesión en HashiCorp Vault. Completa el inicio de sesión allí; esta conexión continuará automáticamente.",
|
|
||||||
"vaultAuthFailed": "La autenticación de Vault falló. Inténtalo de nuevo.",
|
|
||||||
"vaultAuthTitle": "Inicio de sesión en Vault requerido",
|
|
||||||
"vaultReopen": "Reabrir ventana de inicio de sesión",
|
|
||||||
"websocketAbnormalClose": "La conexión se cerró de forma inesperada. Esto puede deberse a un problema con el proxy inverso o la configuración SSL. Revisa los registros del servidor.",
|
"websocketAbnormalClose": "La conexión se cerró de forma inesperada. Esto puede deberse a un problema con el proxy inverso o la configuración SSL. Revisa los registros del servidor.",
|
||||||
"websocketError": "Error de conexión WebSocket"
|
"websocketError": "Error de conexión WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} ikkuna",
|
"tmuxWindowCount": "{{count}} ikkuna",
|
||||||
"tmuxWindows": "Ikkunat",
|
"tmuxWindows": "Ikkunat",
|
||||||
"unknownError": "Tuntematon virhe tapahtui",
|
"unknownError": "Tuntematon virhe tapahtui",
|
||||||
"vaultAuthDescription": "Ikkuna HashiCorp-holviin kirjautumista varten on avautunut. Kirjaudu sisään siinä. Yhteys jatkuu automaattisesti.",
|
|
||||||
"vaultAuthFailed": "Holvin todennus epäonnistui. Yritä uudelleen.",
|
|
||||||
"vaultAuthTitle": "Holviin kirjautuminen vaaditaan",
|
|
||||||
"vaultReopen": "Avaa kirjautumisikkuna uudelleen",
|
|
||||||
"websocketAbnormalClose": "Yhteys katkesi odottamatta. Tämä voi johtua käänteisestä välityspalvelimesta tai SSL-määritysongelmasta. Tarkista palvelimen lokit.",
|
"websocketAbnormalClose": "Yhteys katkesi odottamatta. Tämä voi johtua käänteisestä välityspalvelimesta tai SSL-määritysongelmasta. Tarkista palvelimen lokit.",
|
||||||
"websocketError": "WebSocket-yhteysvirhe"
|
"websocketError": "WebSocket-yhteysvirhe"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} fenêtre(s)",
|
"tmuxWindowCount": "{{count}} fenêtre(s)",
|
||||||
"tmuxWindows": "Fenêtres",
|
"tmuxWindows": "Fenêtres",
|
||||||
"unknownError": "Une erreur inconnue s'est produite",
|
"unknownError": "Une erreur inconnue s'est produite",
|
||||||
"vaultAuthDescription": "Une fenêtre s'est ouverte pour vous connecter à HashiCorp Vault. Terminez la connexion dans cette fenêtre ; cette connexion continuera automatiquement.",
|
|
||||||
"vaultAuthFailed": "Échec de l'authentification Vault. Veuillez réessayer.",
|
|
||||||
"vaultAuthTitle": "Connexion à Vault requise",
|
|
||||||
"vaultReopen": "Rouvrir la fenêtre de connexion",
|
|
||||||
"websocketAbnormalClose": "Connexion fermée de manière inattendue. Cela peut être dû à un problème de proxy inverse ou de configuration SSL. Veuillez vérifier les journaux du serveur.",
|
"websocketAbnormalClose": "Connexion fermée de manière inattendue. Cela peut être dû à un problème de proxy inverse ou de configuration SSL. Veuillez vérifier les journaux du serveur.",
|
||||||
"websocketError": "Erreur de connexion WebSocket"
|
"websocketError": "Erreur de connexion WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "חלון {{count}}",
|
"tmuxWindowCount": "חלון {{count}}",
|
||||||
"tmuxWindows": "חלונות",
|
"tmuxWindows": "חלונות",
|
||||||
"unknownError": "אירעה שגיאה לא ידועה",
|
"unknownError": "אירעה שגיאה לא ידועה",
|
||||||
"vaultAuthDescription": "נפתח חלון לכניסה לכספת HashiCorp. השלם את תהליך הכניסה שם; חיבור זה ימשיך אוטומטית.",
|
|
||||||
"vaultAuthFailed": "אימות הכספת נכשל. אנא נסה שוב.",
|
|
||||||
"vaultAuthTitle": "נדרשת כניסה לכספת",
|
|
||||||
"vaultReopen": "פתח מחדש את חלון הכניסה",
|
|
||||||
"websocketAbnormalClose": "החיבור נסגר באופן בלתי צפוי. ייתכן שהדבר נובע מבעיית פרוקסי הפוך או מבעיית תצורת SSL. אנא בדוק את יומני השרת.",
|
"websocketAbnormalClose": "החיבור נסגר באופן בלתי צפוי. ייתכן שהדבר נובע מבעיית פרוקסי הפוך או מבעיית תצורת SSL. אנא בדוק את יומני השרת.",
|
||||||
"websocketError": "שגיאת חיבור WebSocket"
|
"websocketError": "שגיאת חיבור WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} विंडो",
|
"tmuxWindowCount": "{{count}} विंडो",
|
||||||
"tmuxWindows": "विंडो",
|
"tmuxWindows": "विंडो",
|
||||||
"unknownError": "अज्ञात त्रुटि हुई",
|
"unknownError": "अज्ञात त्रुटि हुई",
|
||||||
"vaultAuthDescription": "HashiCorp Vault में साइन इन करने के लिए एक विंडो खुल गई है। वहाँ साइन-इन पूरा करें; यह कनेक्शन स्वचालित रूप से जारी रहेगा।",
|
|
||||||
"vaultAuthFailed": "Vault प्रमाणीकरण विफल। कृपया पुनः प्रयास करें।",
|
|
||||||
"vaultAuthTitle": "Vault साइन-इन आवश्यक है",
|
|
||||||
"vaultReopen": "साइन-इन विंडो फिर से खोलें",
|
|
||||||
"websocketAbnormalClose": "कनेक्शन अप्रत्याशित रूप से बंद हो गया। यह रिवर्स प्रॉक्सी या SSL कॉन्फ़िगरेशन समस्या के कारण हो सकता है। कृपया सर्वर लॉग जाँचें।",
|
"websocketAbnormalClose": "कनेक्शन अप्रत्याशित रूप से बंद हो गया। यह रिवर्स प्रॉक्सी या SSL कॉन्फ़िगरेशन समस्या के कारण हो सकता है। कृपया सर्वर लॉग जाँचें।",
|
||||||
"websocketError": "WebSocket कनेक्शन त्रुटि"
|
"websocketError": "WebSocket कनेक्शन त्रुटि"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} ablak",
|
"tmuxWindowCount": "{{count}} ablak",
|
||||||
"tmuxWindows": "Ablakok",
|
"tmuxWindows": "Ablakok",
|
||||||
"unknownError": "Ismeretlen hiba történt",
|
"unknownError": "Ismeretlen hiba történt",
|
||||||
"vaultAuthDescription": "Megnyílt egy ablak a HashiCorp Vaultba való bejelentkezéshez. Fejezze be a bejelentkezést ott; a kapcsolat automatikusan folytatódik.",
|
|
||||||
"vaultAuthFailed": "A trezor hitelesítése sikertelen. Próbálja újra.",
|
|
||||||
"vaultAuthTitle": "Bejelentkezés szükséges a széfbe",
|
|
||||||
"vaultReopen": "Bejelentkezési ablak újranyitása",
|
|
||||||
"websocketAbnormalClose": "A kapcsolat váratlanul megszakadt. Ennek oka lehet fordított proxy vagy SSL konfigurációs probléma. Kérjük, ellenőrizze a szervernaplókat.",
|
"websocketAbnormalClose": "A kapcsolat váratlanul megszakadt. Ennek oka lehet fordított proxy vagy SSL konfigurációs probléma. Kérjük, ellenőrizze a szervernaplókat.",
|
||||||
"websocketError": "WebSocket csatlakozási hiba"
|
"websocketError": "WebSocket csatlakozási hiba"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} jendela",
|
"tmuxWindowCount": "{{count}} jendela",
|
||||||
"tmuxWindows": "Windows",
|
"tmuxWindows": "Windows",
|
||||||
"unknownError": "Terjadi kesalahan yang tidak diketahui.",
|
"unknownError": "Terjadi kesalahan yang tidak diketahui.",
|
||||||
"vaultAuthDescription": "Jendela telah terbuka untuk masuk ke HashiCorp Vault. Selesaikan proses masuk di sana; koneksi ini akan berlanjut secara otomatis.",
|
|
||||||
"vaultAuthFailed": "Autentikasi Vault gagal. Silakan coba lagi.",
|
|
||||||
"vaultAuthTitle": "Login ke Vault diperlukan.",
|
|
||||||
"vaultReopen": "Buka kembali jendela masuk",
|
|
||||||
"websocketAbnormalClose": "Koneksi terputus secara tidak terduga. Ini mungkin disebabkan oleh masalah konfigurasi reverse proxy atau SSL. Silakan periksa log server.",
|
"websocketAbnormalClose": "Koneksi terputus secara tidak terduga. Ini mungkin disebabkan oleh masalah konfigurasi reverse proxy atau SSL. Silakan periksa log server.",
|
||||||
"websocketError": "Kesalahan koneksi WebSocket"
|
"websocketError": "Kesalahan koneksi WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} finestra",
|
"tmuxWindowCount": "{{count}} finestra",
|
||||||
"tmuxWindows": "Finestre",
|
"tmuxWindows": "Finestre",
|
||||||
"unknownError": "Si è verificato un errore sconosciuto",
|
"unknownError": "Si è verificato un errore sconosciuto",
|
||||||
"vaultAuthDescription": "Una finestra si è aperta per accedere a HashiCorp Vault. Completa l'accesso lì; la connessione continuerà automaticamente.",
|
|
||||||
"vaultAuthFailed": "Autenticazione Vault fallita. Riprova.",
|
|
||||||
"vaultAuthTitle": "Accesso a Vault richiesto",
|
|
||||||
"vaultReopen": "Riapri la finestra di accesso",
|
|
||||||
"websocketAbnormalClose": "La connessione si è chiusa in modo imprevisto. Potrebbe trattarsi di un problema di reverse proxy o configurazione SSL. Controlla i log del server.",
|
"websocketAbnormalClose": "La connessione si è chiusa in modo imprevisto. Potrebbe trattarsi di un problema di reverse proxy o configurazione SSL. Controlla i log del server.",
|
||||||
"websocketError": "Errore di connessione WebSocket"
|
"websocketError": "Errore di connessione WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} ウィンドウ",
|
"tmuxWindowCount": "{{count}} ウィンドウ",
|
||||||
"tmuxWindows": "ウィンドウ",
|
"tmuxWindows": "ウィンドウ",
|
||||||
"unknownError": "不明なエラーが発生しました",
|
"unknownError": "不明なエラーが発生しました",
|
||||||
"vaultAuthDescription": "HashiCorp Vaultへのサインイン用のウィンドウが開きました。そこでサインインを完了すると、この接続は自動的に続行されます。",
|
|
||||||
"vaultAuthFailed": "Vault 認証に失敗しました。もう一度お試しください。",
|
|
||||||
"vaultAuthTitle": "Vault サインインが必要です",
|
|
||||||
"vaultReopen": "サインインウィンドウを再表示",
|
|
||||||
"websocketAbnormalClose": "接続が予期せず切断されました。リバースプロキシまたはSSLの設定に問題がある可能性があります。サーバーログを確認してください。",
|
"websocketAbnormalClose": "接続が予期せず切断されました。リバースプロキシまたはSSLの設定に問題がある可能性があります。サーバーログを確認してください。",
|
||||||
"websocketError": "WebSocket接続エラー"
|
"websocketError": "WebSocket接続エラー"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}}개 윈도우",
|
"tmuxWindowCount": "{{count}}개 윈도우",
|
||||||
"tmuxWindows": "윈도우",
|
"tmuxWindows": "윈도우",
|
||||||
"unknownError": "알 수 없는 오류가 발생했습니다.",
|
"unknownError": "알 수 없는 오류가 발생했습니다.",
|
||||||
"vaultAuthDescription": "HashiCorp Vault 로그인을 위한 창이 열렸습니다. 해당 창에서 로그인을 완료하면 연결이 자동으로 계속됩니다.",
|
|
||||||
"vaultAuthFailed": "Vault 인증에 실패했습니다. 다시 시도하세요.",
|
|
||||||
"vaultAuthTitle": "Vault 로그인 필요",
|
|
||||||
"vaultReopen": "로그인 창 다시 열기",
|
|
||||||
"websocketAbnormalClose": "연결이 예기치 않게 종료되었습니다. 리버스 프록시 또는 SSL 구성 문제일 수 있습니다. 서버 로그를 확인하세요.",
|
"websocketAbnormalClose": "연결이 예기치 않게 종료되었습니다. 리버스 프록시 또는 SSL 구성 문제일 수 있습니다. 서버 로그를 확인하세요.",
|
||||||
"websocketError": "WebSocket 연결 오류"
|
"websocketError": "WebSocket 연결 오류"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} venster",
|
"tmuxWindowCount": "{{count}} venster",
|
||||||
"tmuxWindows": "Windows",
|
"tmuxWindows": "Windows",
|
||||||
"unknownError": "Er is een onbekende fout opgetreden.",
|
"unknownError": "Er is een onbekende fout opgetreden.",
|
||||||
"vaultAuthDescription": "Er is een venster geopend om in te loggen bij HashiCorp Vault. Voltooi de aanmelding daar; deze verbinding wordt automatisch tot stand gebracht.",
|
|
||||||
"vaultAuthFailed": "Authenticatie voor de kluis is mislukt. Probeer het opnieuw.",
|
|
||||||
"vaultAuthTitle": "Aanmelden bij Vault is vereist.",
|
|
||||||
"vaultReopen": "Heropen het aanmeldvenster",
|
|
||||||
"websocketAbnormalClose": "De verbinding is onverwacht verbroken. Dit kan te wijten zijn aan een probleem met de reverse proxy of de SSL-configuratie. Controleer de serverlogboeken.",
|
"websocketAbnormalClose": "De verbinding is onverwacht verbroken. Dit kan te wijten zijn aan een probleem met de reverse proxy of de SSL-configuratie. Controleer de serverlogboeken.",
|
||||||
"websocketError": "WebSocket-verbindingsfout"
|
"websocketError": "WebSocket-verbindingsfout"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} vindu",
|
"tmuxWindowCount": "{{count}} vindu",
|
||||||
"tmuxWindows": "Vinduer",
|
"tmuxWindows": "Vinduer",
|
||||||
"unknownError": "Ukjent feil oppstod",
|
"unknownError": "Ukjent feil oppstod",
|
||||||
"vaultAuthDescription": "Et vindu har åpnet seg for å logge på HashiCorp Vault. Fullfør påloggingen der; denne tilkoblingen vil fortsette automatisk.",
|
|
||||||
"vaultAuthFailed": "Hvelv-autentisering mislyktes. Prøv på nytt.",
|
|
||||||
"vaultAuthTitle": "Pålogging til arkivet kreves",
|
|
||||||
"vaultReopen": "Åpne påloggingsvinduet på nytt",
|
|
||||||
"websocketAbnormalClose": "Tilkoblingen ble avsluttet uventet. Dette kan skyldes et problem med omvendt proxy eller SSL-konfigurasjon. Sjekk serverloggene.",
|
"websocketAbnormalClose": "Tilkoblingen ble avsluttet uventet. Dette kan skyldes et problem med omvendt proxy eller SSL-konfigurasjon. Sjekk serverloggene.",
|
||||||
"websocketError": "WebSocket-tilkoblingsfeil"
|
"websocketError": "WebSocket-tilkoblingsfeil"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} okno",
|
"tmuxWindowCount": "{{count}} okno",
|
||||||
"tmuxWindows": "Okna",
|
"tmuxWindows": "Okna",
|
||||||
"unknownError": "Wystąpił nieznany błąd",
|
"unknownError": "Wystąpił nieznany błąd",
|
||||||
"vaultAuthDescription": "Otworzyło się okno logowania do HashiCorp Vault. Dokończ logowanie, a połączenie zostanie nawiązane automatycznie.",
|
|
||||||
"vaultAuthFailed": "Uwierzytelnienie w Vault nie powiodło się. Spróbuj ponownie.",
|
|
||||||
"vaultAuthTitle": "Wymagane logowanie do Vault",
|
|
||||||
"vaultReopen": "Otwórz ponownie okno logowania",
|
|
||||||
"websocketAbnormalClose": "Połączenie zostało nieoczekiwanie zamknięte. Przyczyną może być reverse proxy lub problem z konfiguracją SSL. Sprawdź logi serwera.",
|
"websocketAbnormalClose": "Połączenie zostało nieoczekiwanie zamknięte. Przyczyną może być reverse proxy lub problem z konfiguracją SSL. Sprawdź logi serwera.",
|
||||||
"websocketError": "Błąd połączenia WebSocket"
|
"websocketError": "Błąd połączenia WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} janela",
|
"tmuxWindowCount": "{{count}} janela",
|
||||||
"tmuxWindows": "Windows",
|
"tmuxWindows": "Windows",
|
||||||
"unknownError": "Ocorreu um erro desconhecido.",
|
"unknownError": "Ocorreu um erro desconhecido.",
|
||||||
"vaultAuthDescription": "Uma janela foi aberta para fazer login no HashiCorp Vault. Conclua o login; esta conexão será estabelecida automaticamente.",
|
|
||||||
"vaultAuthFailed": "A autenticação do cofre falhou. Tente novamente.",
|
|
||||||
"vaultAuthTitle": "É necessário fazer login no cofre.",
|
|
||||||
"vaultReopen": "Reabrir janela de login",
|
|
||||||
"websocketAbnormalClose": "A conexão foi encerrada inesperadamente. Isso pode ter ocorrido devido a um problema de configuração de proxy reverso ou SSL. Verifique os registros do servidor.",
|
"websocketAbnormalClose": "A conexão foi encerrada inesperadamente. Isso pode ter ocorrido devido a um problema de configuração de proxy reverso ou SSL. Verifique os registros do servidor.",
|
||||||
"websocketError": "Erro de conexão WebSocket"
|
"websocketError": "Erro de conexão WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} janela",
|
"tmuxWindowCount": "{{count}} janela",
|
||||||
"tmuxWindows": "Janelas",
|
"tmuxWindows": "Janelas",
|
||||||
"unknownError": "Ocorreu um erro desconhecido.",
|
"unknownError": "Ocorreu um erro desconhecido.",
|
||||||
"vaultAuthDescription": "Foi aberta uma janela para iniciar sessão no HashiCorp Vault. Conclua o início de sessão aí; esta ligação continuará automaticamente.",
|
|
||||||
"vaultAuthFailed": "A autenticação no Vault falhou. Tente novamente.",
|
|
||||||
"vaultAuthTitle": "Início de sessão no Vault necessário",
|
|
||||||
"vaultReopen": "Reabrir janela de início de sessão",
|
|
||||||
"websocketAbnormalClose": "A ligação foi fechada inesperadamente. Isto pode dever-se a um problema de configuração do proxy reverso ou SSL. Verifique os registos do servidor.",
|
"websocketAbnormalClose": "A ligação foi fechada inesperadamente. Isto pode dever-se a um problema de configuração do proxy reverso ou SSL. Verifique os registos do servidor.",
|
||||||
"websocketError": "Erro de ligação WebSocket"
|
"websocketError": "Erro de ligação WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "fereastră {{count}}",
|
"tmuxWindowCount": "fereastră {{count}}",
|
||||||
"tmuxWindows": "Ferestre",
|
"tmuxWindows": "Ferestre",
|
||||||
"unknownError": "A apărut o eroare necunoscută",
|
"unknownError": "A apărut o eroare necunoscută",
|
||||||
"vaultAuthDescription": "S-a deschis o fereastră pentru a vă conecta la HashiCorp Vault. Finalizați conectarea acolo; această conexiune va continua automat.",
|
|
||||||
"vaultAuthFailed": "Autentificarea în seif a eșuat. Vă rugăm să încercați din nou.",
|
|
||||||
"vaultAuthTitle": "Este necesară conectarea la seif",
|
|
||||||
"vaultReopen": "Redeschideți fereastra de conectare",
|
|
||||||
"websocketAbnormalClose": "Conexiunea s-a închis pe neașteptate. Acest lucru se poate datora unei probleme de configurare a proxy-ului invers sau a SSL. Vă rugăm să verificați jurnalele serverului.",
|
"websocketAbnormalClose": "Conexiunea s-a închis pe neașteptate. Acest lucru se poate datora unei probleme de configurare a proxy-ului invers sau a SSL. Vă rugăm să verificați jurnalele serverului.",
|
||||||
"websocketError": "Eroare de conexiune WebSocket"
|
"websocketError": "Eroare de conexiune WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} окно",
|
"tmuxWindowCount": "{{count}} окно",
|
||||||
"tmuxWindows": "Окна",
|
"tmuxWindows": "Окна",
|
||||||
"unknownError": "Произошла неизвестная ошибка",
|
"unknownError": "Произошла неизвестная ошибка",
|
||||||
"vaultAuthDescription": "Открыто окно для входа в HashiCorp Vault. Завершите вход там; подключение продолжится автоматически.",
|
|
||||||
"vaultAuthFailed": "Аутентификация Vault не удалась. Попробуйте снова.",
|
|
||||||
"vaultAuthTitle": "Требуется вход в Vault",
|
|
||||||
"vaultReopen": "Повторно открыть окно входа",
|
|
||||||
"websocketAbnormalClose": "Соединение неожиданно закрыто. Возможно, проблема с обратным прокси или конфигурацией SSL. Проверьте журналы сервера.",
|
"websocketAbnormalClose": "Соединение неожиданно закрыто. Возможно, проблема с обратным прокси или конфигурацией SSL. Проверьте журналы сервера.",
|
||||||
"websocketError": "Ошибка подключения WebSocket"
|
"websocketError": "Ошибка подключения WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} прозор",
|
"tmuxWindowCount": "{{count}} прозор",
|
||||||
"tmuxWindows": "Прозори",
|
"tmuxWindows": "Прозори",
|
||||||
"unknownError": "Дошло је до непознате грешке",
|
"unknownError": "Дошло је до непознате грешке",
|
||||||
"vaultAuthDescription": "Отворио се прозор за пријављивање на HashiCorp Vault. Завршите пријављивање тамо; ова веза ће се аутоматски наставити.",
|
|
||||||
"vaultAuthFailed": "Аутентификација трезора није успела. Молимо покушајте поново.",
|
|
||||||
"vaultAuthTitle": "Потребно је пријављивање у трезор",
|
|
||||||
"vaultReopen": "Поново отвори прозор за пријављивање",
|
|
||||||
"websocketAbnormalClose": "Веза је неочекивано прекинута. Ово може бити због проблема са обрнутим проксијем или SSL конфигурацијом. Молимо вас да проверите логове сервера.",
|
"websocketAbnormalClose": "Веза је неочекивано прекинута. Ово може бити због проблема са обрнутим проксијем или SSL конфигурацијом. Молимо вас да проверите логове сервера.",
|
||||||
"websocketError": "Грешка у повезивању са WebSocket-ом"
|
"websocketError": "Грешка у повезивању са WebSocket-ом"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} fönster",
|
"tmuxWindowCount": "{{count}} fönster",
|
||||||
"tmuxWindows": "Fönster",
|
"tmuxWindows": "Fönster",
|
||||||
"unknownError": "Okänt fel inträffade",
|
"unknownError": "Okänt fel inträffade",
|
||||||
"vaultAuthDescription": "Ett fönster har öppnats för att logga in på HashiCorp Vault. Slutför inloggningen där; anslutningen fortsätter automatiskt.",
|
|
||||||
"vaultAuthFailed": "Valvautentisering misslyckades. Försök igen.",
|
|
||||||
"vaultAuthTitle": "Inloggning till arkivet krävs",
|
|
||||||
"vaultReopen": "Öppna inloggningsfönstret igen",
|
|
||||||
"websocketAbnormalClose": "Anslutningen stängdes oväntat. Detta kan bero på ett problem med omvänd proxy eller SSL-konfiguration. Kontrollera serverloggarna.",
|
"websocketAbnormalClose": "Anslutningen stängdes oväntat. Detta kan bero på ett problem med omvänd proxy eller SSL-konfiguration. Kontrollera serverloggarna.",
|
||||||
"websocketError": "WebSocket-anslutningsfel"
|
"websocketError": "WebSocket-anslutningsfel"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} หน้าต่าง",
|
"tmuxWindowCount": "{{count}} หน้าต่าง",
|
||||||
"tmuxWindows": "วินโดวส์",
|
"tmuxWindows": "วินโดวส์",
|
||||||
"unknownError": "เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ",
|
"unknownError": "เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ",
|
||||||
"vaultAuthDescription": "หน้าต่างสำหรับเข้าสู่ระบบ HashiCorp Vault ได้เปิดขึ้นแล้ว โปรดทำการเข้าสู่ระบบให้เสร็จสมบูรณ์ การเชื่อมต่อนี้จะดำเนินการต่อโดยอัตโนมัติ",
|
|
||||||
"vaultAuthFailed": "การตรวจสอบสิทธิ์ Vault ล้มเหลว โปรดลองอีกครั้ง",
|
|
||||||
"vaultAuthTitle": "ต้องลงชื่อเข้าใช้ Vault",
|
|
||||||
"vaultReopen": "เปิดหน้าต่างเข้าสู่ระบบอีกครั้ง",
|
|
||||||
"websocketAbnormalClose": "การเชื่อมต่อถูกตัดโดยไม่คาดคิด อาจเกิดจากปัญหาเกี่ยวกับรีเวิร์สพร็อกซีหรือการกำหนดค่า SSL โปรดตรวจสอบบันทึกของเซิร์ฟเวอร์",
|
"websocketAbnormalClose": "การเชื่อมต่อถูกตัดโดยไม่คาดคิด อาจเกิดจากปัญหาเกี่ยวกับรีเวิร์สพร็อกซีหรือการกำหนดค่า SSL โปรดตรวจสอบบันทึกของเซิร์ฟเวอร์",
|
||||||
"websocketError": "ข้อผิดพลาดในการเชื่อมต่อ WebSocket"
|
"websocketError": "ข้อผิดพลาดในการเชื่อมต่อ WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} pencere",
|
"tmuxWindowCount": "{{count}} pencere",
|
||||||
"tmuxWindows": "Pencereler",
|
"tmuxWindows": "Pencereler",
|
||||||
"unknownError": "Bilinmeyen bir hata oluştu",
|
"unknownError": "Bilinmeyen bir hata oluştu",
|
||||||
"vaultAuthDescription": "HashiCorp Vault'ta oturum açmak için bir pencere açıldı. Orada oturum açmayı tamamlayın; bu bağlantı otomatik olarak devam edecek.",
|
|
||||||
"vaultAuthFailed": "Vault kimlik doğrulaması başarısız oldu. Lütfen tekrar deneyin.",
|
|
||||||
"vaultAuthTitle": "Vault Oturum Açması Gerekli",
|
|
||||||
"vaultReopen": "Oturum açma penceresini yeniden aç",
|
|
||||||
"websocketAbnormalClose": "Bağlantı beklenmedik şekilde kapandı. Bu, ters proxy veya SSL yapılandırma sorunundan kaynaklanabilir. Lütfen sunucu günlüklerini kontrol edin.",
|
"websocketAbnormalClose": "Bağlantı beklenmedik şekilde kapandı. Bu, ters proxy veya SSL yapılandırma sorunundan kaynaklanabilir. Lütfen sunucu günlüklerini kontrol edin.",
|
||||||
"websocketError": "WebSocket bağlantı hatası"
|
"websocketError": "WebSocket bağlantı hatası"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "вікно {{count}}",
|
"tmuxWindowCount": "вікно {{count}}",
|
||||||
"tmuxWindows": "Вікна",
|
"tmuxWindows": "Вікна",
|
||||||
"unknownError": "Сталася невідома помилка",
|
"unknownError": "Сталася невідома помилка",
|
||||||
"vaultAuthDescription": "Відкрилося вікно для входу в HashiCorp Vault. Завершіть вхід там; це з’єднання буде продовжено автоматично.",
|
|
||||||
"vaultAuthFailed": "Не вдалося виконати автентифікацію сховища. Спробуйте ще раз.",
|
|
||||||
"vaultAuthTitle": "Потрібен вхід до Сейфа",
|
|
||||||
"vaultReopen": "Знову відкрити вікно входу",
|
|
||||||
"websocketAbnormalClose": "З’єднання несподівано завершилося. Можливо, це пов’язано зі зворотним проксі-сервером або проблемою конфігурації SSL. Перевірте журнали сервера.",
|
"websocketAbnormalClose": "З’єднання несподівано завершилося. Можливо, це пов’язано зі зворотним проксі-сервером або проблемою конфігурації SSL. Перевірте журнали сервера.",
|
||||||
"websocketError": "Помилка підключення WebSocket"
|
"websocketError": "Помилка підключення WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} cửa sổ",
|
"tmuxWindowCount": "{{count}} cửa sổ",
|
||||||
"tmuxWindows": "Cửa sổ",
|
"tmuxWindows": "Cửa sổ",
|
||||||
"unknownError": "Lỗi không xác định đã xảy ra",
|
"unknownError": "Lỗi không xác định đã xảy ra",
|
||||||
"vaultAuthDescription": "Một cửa sổ đã mở để đăng nhập vào HashiCorp Vault. Hoàn tất đăng nhập ở đó; kết nối này sẽ tự động tiếp tục.",
|
|
||||||
"vaultAuthFailed": "Xác thực Vault thất bại. Vui lòng thử lại.",
|
|
||||||
"vaultAuthTitle": "Yêu cầu đăng nhập Vault",
|
|
||||||
"vaultReopen": "Mở lại cửa sổ đăng nhập",
|
|
||||||
"websocketAbnormalClose": "Kết nối đóng đột ngột. Có thể do sự cố cấu hình reverse proxy hoặc SSL. Vui lòng kiểm tra nhật ký máy chủ.",
|
"websocketAbnormalClose": "Kết nối đóng đột ngột. Có thể do sự cố cấu hình reverse proxy hoặc SSL. Vui lòng kiểm tra nhật ký máy chủ.",
|
||||||
"websocketError": "Lỗi kết nối WebSocket"
|
"websocketError": "Lỗi kết nối WebSocket"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} 个窗口",
|
"tmuxWindowCount": "{{count}} 个窗口",
|
||||||
"tmuxWindows": "窗口",
|
"tmuxWindows": "窗口",
|
||||||
"unknownError": "发生未知错误",
|
"unknownError": "发生未知错误",
|
||||||
"vaultAuthDescription": "已打开窗口以登录 HashiCorp Vault。在该处完成登录;此连接将自动继续。",
|
|
||||||
"vaultAuthFailed": "Vault 认证失败,请重试。",
|
|
||||||
"vaultAuthTitle": "需要 Vault 登录",
|
|
||||||
"vaultReopen": "重新打开登录窗口",
|
|
||||||
"websocketAbnormalClose": "连接意外关闭。这可能是因为反向代理或SSL配置问题。请检查服务器日志。",
|
"websocketAbnormalClose": "连接意外关闭。这可能是因为反向代理或SSL配置问题。请检查服务器日志。",
|
||||||
"websocketError": "WebSocket 连接错误"
|
"websocketError": "WebSocket 连接错误"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,10 +74,6 @@
|
|||||||
"tmuxWindowCount": "{{count}} 個視窗",
|
"tmuxWindowCount": "{{count}} 個視窗",
|
||||||
"tmuxWindows": "視窗",
|
"tmuxWindows": "視窗",
|
||||||
"unknownError": "發生未知錯誤",
|
"unknownError": "發生未知錯誤",
|
||||||
"vaultAuthDescription": "已開啟視窗以登入 HashiCorp Vault。在該處完成登入;此連線將自動繼續。",
|
|
||||||
"vaultAuthFailed": "Vault 驗證失敗。請再試一次。",
|
|
||||||
"vaultAuthTitle": "需要 Vault 登入",
|
|
||||||
"vaultReopen": "重新開啟登入視窗",
|
|
||||||
"websocketAbnormalClose": "連線意外關閉。這可能是由於反向代理或 SSL 設定問題所導致。請檢查伺服器記錄。",
|
"websocketAbnormalClose": "連線意外關閉。這可能是由於反向代理或 SSL 設定問題所導致。請檢查伺服器記錄。",
|
||||||
"websocketError": "WebSocket 連線錯誤"
|
"websocketError": "WebSocket 連線錯誤"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1141,7 +1141,7 @@ export function createTerminalSocket(deps: TerminalSocketDeps) {
|
|||||||
/**
|
/**
|
||||||
* Starts the browser sign-in behind an "<interaction>_auth_required"
|
* Starts the browser sign-in behind an "<interaction>_auth_required"
|
||||||
* message. The provider for the host's auth type wins; otherwise the first
|
* message. The provider for the host's auth type wins; otherwise the first
|
||||||
* provider that owns this interaction (Vault attaches to any auth type).
|
* provider that owns this interaction.
|
||||||
*/
|
*/
|
||||||
async function startAuthInteraction(
|
async function startAuthInteraction(
|
||||||
interaction: string,
|
interaction: string,
|
||||||
|
|||||||
@@ -342,15 +342,6 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
const currentHostIdRef = useRef<number | null>(null);
|
const currentHostIdRef = useRef<number | null>(null);
|
||||||
const currentHostConfigRef = useRef<TerminalHostConfig | null>(null);
|
const currentHostConfigRef = useRef<TerminalHostConfig | null>(null);
|
||||||
|
|
||||||
// Vault SSH signer interactive OIDC flow
|
|
||||||
const [vaultDialog, setVaultDialog] = useState<{
|
|
||||||
stage: "waiting" | "error";
|
|
||||||
error?: string;
|
|
||||||
} | null>(null);
|
|
||||||
const vaultFailedRef = useRef(false);
|
|
||||||
const vaultPopupRef = useRef<Window | null>(null);
|
|
||||||
const vaultTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
||||||
|
|
||||||
const [hostKeyVerification, setHostKeyVerification] = useState<{
|
const [hostKeyVerification, setHostKeyVerification] = useState<{
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
scenario: "new" | "changed";
|
scenario: "new" | "changed";
|
||||||
@@ -1909,7 +1900,6 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
updateConnectionError(errorMessage);
|
updateConnectionError(errorMessage);
|
||||||
setIsConnecting(false);
|
setIsConnecting(false);
|
||||||
} else if (msg.type === "connected") {
|
} else if (msg.type === "connected") {
|
||||||
vaultFailedRef.current = false;
|
|
||||||
wasConnectedRef.current = true;
|
wasConnectedRef.current = true;
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
setIsConnecting(false);
|
setIsConnecting(false);
|
||||||
@@ -2104,86 +2094,6 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
webSocketRef.current.close();
|
webSocketRef.current.close();
|
||||||
}
|
}
|
||||||
}, 300000);
|
}, 300000);
|
||||||
} else if (msg.type === "vault_auth_required") {
|
|
||||||
if (connectionTimeoutRef.current) {
|
|
||||||
clearTimeout(connectionTimeoutRef.current);
|
|
||||||
connectionTimeoutRef.current = null;
|
|
||||||
}
|
|
||||||
if (vaultFailedRef.current) {
|
|
||||||
setVaultDialog({
|
|
||||||
stage: "error",
|
|
||||||
error: t("terminal.vaultAuthFailed"),
|
|
||||||
});
|
|
||||||
updateConnectionError(t("terminal.vaultAuthFailed"));
|
|
||||||
addLog({
|
|
||||||
type: "error",
|
|
||||||
stage: "auth",
|
|
||||||
message: t("terminal.vaultAuthFailed"),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
vaultFailedRef.current = true;
|
|
||||||
webSocketRef.current?.send(
|
|
||||||
JSON.stringify({
|
|
||||||
type: "vault_start_auth",
|
|
||||||
data: { hostId: msg.hostId },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (msg.type === "vault_auth_url") {
|
|
||||||
if (connectionErrorRef.current) return;
|
|
||||||
try {
|
|
||||||
vaultPopupRef.current = window.open(
|
|
||||||
msg.url,
|
|
||||||
"termix-vault-oidc",
|
|
||||||
"width=540,height=720",
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
vaultPopupRef.current = null;
|
|
||||||
}
|
|
||||||
setVaultDialog({ stage: "waiting" });
|
|
||||||
if (vaultTimeoutRef.current) clearTimeout(vaultTimeoutRef.current);
|
|
||||||
vaultTimeoutRef.current = setTimeout(() => {
|
|
||||||
setVaultDialog(null);
|
|
||||||
webSocketRef.current?.close();
|
|
||||||
}, 300000);
|
|
||||||
} else if (msg.type === "vault_completed") {
|
|
||||||
if (vaultTimeoutRef.current) {
|
|
||||||
clearTimeout(vaultTimeoutRef.current);
|
|
||||||
vaultTimeoutRef.current = null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
vaultPopupRef.current?.close();
|
|
||||||
} catch {
|
|
||||||
// popup may already be closed
|
|
||||||
}
|
|
||||||
setVaultDialog(null);
|
|
||||||
if (webSocketRef.current && terminal) {
|
|
||||||
webSocketRef.current.send(
|
|
||||||
JSON.stringify({
|
|
||||||
type: "vault_auth_completed",
|
|
||||||
data: {
|
|
||||||
hostId: currentHostIdRef.current,
|
|
||||||
cols: terminal.cols || 80,
|
|
||||||
rows: terminal.rows || 24,
|
|
||||||
hostConfig: currentHostConfigRef.current,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (msg.type === "vault_error") {
|
|
||||||
if (connectionErrorRef.current) return;
|
|
||||||
vaultFailedRef.current = true;
|
|
||||||
if (vaultTimeoutRef.current) {
|
|
||||||
clearTimeout(vaultTimeoutRef.current);
|
|
||||||
vaultTimeoutRef.current = null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
vaultPopupRef.current?.close();
|
|
||||||
} catch {
|
|
||||||
// popup may already be closed
|
|
||||||
}
|
|
||||||
setVaultDialog({ stage: "error", error: msg.error });
|
|
||||||
setIsConnecting(false);
|
|
||||||
} else if (msg.type === "keyboard_interactive_available") {
|
} else if (msg.type === "keyboard_interactive_available") {
|
||||||
setKeyboardInteractiveDetected(true);
|
setKeyboardInteractiveDetected(true);
|
||||||
setIsConnecting(false);
|
setIsConnecting(false);
|
||||||
@@ -2240,7 +2150,6 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
}
|
}
|
||||||
} else if (msg.type === "sessionAttached") {
|
} else if (msg.type === "sessionAttached") {
|
||||||
isAttachingSessionRef.current = false;
|
isAttachingSessionRef.current = false;
|
||||||
vaultFailedRef.current = false;
|
|
||||||
wasConnectedRef.current = true;
|
wasConnectedRef.current = true;
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
setIsConnecting(false);
|
setIsConnecting(false);
|
||||||
@@ -3884,70 +3793,6 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
props={overlayProps as unknown as Record<string, unknown>}
|
props={overlayProps as unknown as Record<string, unknown>}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{vaultDialog && (
|
|
||||||
<div className="absolute inset-0 z-50 flex items-center justify-center bg-black/60">
|
|
||||||
<div className="w-[420px] max-w-[90%] border border-border bg-background p-5 shadow-lg">
|
|
||||||
<h3 className="text-sm font-bold mb-2 text-foreground">
|
|
||||||
{vaultDialog.stage === "error"
|
|
||||||
? t("terminal.vaultAuthFailed")
|
|
||||||
: t("terminal.vaultAuthTitle")}
|
|
||||||
</h3>
|
|
||||||
{vaultDialog.stage === "error" ? (
|
|
||||||
<p className="text-xs text-destructive mb-4 break-words">
|
|
||||||
{vaultDialog.error || t("terminal.vaultAuthFailed")}
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<p className="text-xs text-muted-foreground mb-4">
|
|
||||||
{t("terminal.vaultAuthDescription")}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<div className="flex justify-end gap-2">
|
|
||||||
{vaultDialog.stage === "waiting" && (
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => {
|
|
||||||
try {
|
|
||||||
vaultPopupRef.current?.focus();
|
|
||||||
} catch {
|
|
||||||
// popup may be gone
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("terminal.vaultReopen")}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => {
|
|
||||||
if (vaultTimeoutRef.current) {
|
|
||||||
clearTimeout(vaultTimeoutRef.current);
|
|
||||||
vaultTimeoutRef.current = null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
vaultPopupRef.current?.close();
|
|
||||||
} catch {
|
|
||||||
// popup may already be closed
|
|
||||||
}
|
|
||||||
webSocketRef.current?.send(
|
|
||||||
JSON.stringify({
|
|
||||||
type: "vault_cancel",
|
|
||||||
data: { hostId: currentHostIdRef.current },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
setVaultDialog(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{vaultDialog.stage === "error"
|
|
||||||
? t("common.close")
|
|
||||||
: t("hosts.cancelBtn")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{hostKeyVerification?.isOpen && (
|
{hostKeyVerification?.isOpen && (
|
||||||
<HostKeyVerificationDialog
|
<HostKeyVerificationDialog
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
Bundled with Termix 2.9.0.
|
||||||
|
|
||||||
|
- Vault SSH signing moved out of core. The callback is now
|
||||||
|
`/plugin-api/vault/oidc/callback`; the 2.8 URI `/vault/oidc/callback`
|
||||||
|
still works and upgraded installs keep sending it until an admin turns
|
||||||
|
off "Use the old redirect URI".
|
||||||
|
- Profiles keep their ids and sync ids. A host's profile is now a host
|
||||||
|
setting of this plugin.
|
||||||
|
- Signed certificates are stored encrypted with the installation key.
|
||||||
|
Certificates cached by 2.8 are not carried over; the next connection asks
|
||||||
|
for one sign-in.
|
||||||
|
- Sharing a profile needs the "Share Vault profiles" permission, which
|
||||||
|
admins have by default.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# HashiCorp Vault
|
||||||
|
|
||||||
|
Adds the `vault` SSH auth type: Termix signs you in to Vault with OIDC in the
|
||||||
|
browser, has Vault's SSH secrets engine sign a fresh key, and keeps the
|
||||||
|
short-lived certificate until it expires.
|
||||||
|
|
||||||
|
Termix talks to Vault's HTTP API directly (`auth/<mount>/oidc/auth_url`,
|
||||||
|
`auth/<mount>/oidc/callback`, `<ssh mount>/sign/<role>`). No Vault token,
|
||||||
|
AppRole secret or long-lived key is ever stored.
|
||||||
|
|
||||||
|
## Profiles
|
||||||
|
|
||||||
|
A profile holds the connection settings only: the Vault address and
|
||||||
|
namespace, the OIDC mount and role, the SSH secrets mount and signer role,
|
||||||
|
the valid principals and the key type. Pick one in the host editor after
|
||||||
|
choosing "Vault" as the authentication type, and manage profiles from the
|
||||||
|
same place. Users with the "Share Vault profiles" permission can share a
|
||||||
|
profile with everyone.
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
Admin settings, under Settings > Plugins > HashiCorp Vault:
|
||||||
|
|
||||||
|
- **Redirect URI**: add it to `allowed_redirect_uris` in each Vault OIDC
|
||||||
|
role: `<base URL>/plugin-api/vault/oidc/callback`, or
|
||||||
|
`<base URL>/vault/oidc/callback` while "Use the old redirect URI" is on.
|
||||||
|
|
||||||
|
An install upgraded from 2.8 that used Vault keeps the old redirect URI
|
||||||
|
turned on.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault Signer Profile",
|
||||||
|
"selectProfile": "Select a Vault profile...",
|
||||||
|
"hint": "Settings come from the shared profile; you'll sign in to Vault via OIDC when you connect. No secrets are stored.",
|
||||||
|
"docs": "View docs",
|
||||||
|
"sharedProfile": "{{name}} (shared)"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "New profile",
|
||||||
|
"manage": "Manage Vault profiles",
|
||||||
|
"name": "Friendly Name",
|
||||||
|
"vaultAddr": "Vault Address",
|
||||||
|
"namespace": "Namespace",
|
||||||
|
"oidcMount": "OIDC Auth Mount",
|
||||||
|
"oidcRole": "OIDC Role",
|
||||||
|
"sshMount": "SSH Secrets Mount",
|
||||||
|
"sshRole": "SSH Signer Role",
|
||||||
|
"validPrincipals": "Valid Principals",
|
||||||
|
"keyType": "Ephemeral Key Type",
|
||||||
|
"shareWithAll": "Share with all users",
|
||||||
|
"create": "Create profile",
|
||||||
|
"created": "Vault profile created",
|
||||||
|
"saved": "Vault profile saved",
|
||||||
|
"deleted": "Vault profile deleted",
|
||||||
|
"saveFailed": "Failed to save Vault profile",
|
||||||
|
"deleteFailed": "Failed to delete Vault profile",
|
||||||
|
"save": "Save profile",
|
||||||
|
"validationError": "Name, Vault address and SSH signer role are required",
|
||||||
|
"none": "No Vault profiles yet.",
|
||||||
|
"sharedBadge": "shared",
|
||||||
|
"edit": "Edit",
|
||||||
|
"delete": "Delete"
|
||||||
|
},
|
||||||
|
"hosts": {
|
||||||
|
"filterAuthVault": "Vault"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Vault sign-in required",
|
||||||
|
"description": "A window has opened to sign in to HashiCorp Vault. Complete the sign-in there; this connection will continue automatically.",
|
||||||
|
"failed": "Vault authentication failed. Please try again.",
|
||||||
|
"reopen": "Reopen sign-in window"
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"use": {
|
||||||
|
"title": "Use Vault SSH signing",
|
||||||
|
"description": "Create Vault signer profiles and connect to hosts that use Vault."
|
||||||
|
},
|
||||||
|
"share": {
|
||||||
|
"title": "Share Vault profiles",
|
||||||
|
"description": "Mark a Vault signer profile as shared so every user can pick it."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"redirectUri": {
|
||||||
|
"label": "Redirect URI",
|
||||||
|
"description": "Add this URL to allowed_redirect_uris in each Vault OIDC role that Termix signs in with."
|
||||||
|
},
|
||||||
|
"legacyCallback": {
|
||||||
|
"label": "Use the old redirect URI",
|
||||||
|
"description": "Keep sending /vault/oidc/callback, the redirect URI used before 2.9. Turn this off once your Vault roles allow the new one."
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"profileId": {
|
||||||
|
"label": "Vault signer profile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Kluis Ondertekenaar Profiel",
|
||||||
|
"selectProfile": "Kies 'n kluisprofiel...",
|
||||||
|
"hint": "Instellings kom van die gedeelde profiel; jy sal by Vault aanmeld via OIDC wanneer jy koppel. Geen geheime word gestoor nie.",
|
||||||
|
"docs": "Bekyk dokumente"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Nuwe profiel",
|
||||||
|
"manage": "Bestuur Vault-profiele",
|
||||||
|
"name": "Vriendelike Naam",
|
||||||
|
"vaultAddr": "Kluisadres",
|
||||||
|
"namespace": "Naamruimte",
|
||||||
|
"oidcMount": "OIDC-magtigingsmontering",
|
||||||
|
"oidcRole": "OIDC-rol",
|
||||||
|
"sshMount": "SSH-geheime berg",
|
||||||
|
"sshRole": "SSH-ondertekenaarrol",
|
||||||
|
"validPrincipals": "Geldige Prinsipale",
|
||||||
|
"keyType": "Efemere Sleuteltipe",
|
||||||
|
"shareWithAll": "Deel met alle gebruikers",
|
||||||
|
"create": "Skep profiel",
|
||||||
|
"created": "Kluisprofiel geskep",
|
||||||
|
"saved": "Kluisprofiel gestoor",
|
||||||
|
"deleted": "Kluisprofiel uitgevee",
|
||||||
|
"saveFailed": "Kon nie kluisprofiel stoor nie",
|
||||||
|
"deleteFailed": "Kon nie kluisprofiel uitvee nie",
|
||||||
|
"save": "Stoor profiel",
|
||||||
|
"validationError": "Naam, kluisadres en SSH-ondertekenrol word vereis",
|
||||||
|
"none": "Geen Vault-profiele nog nie.",
|
||||||
|
"sharedBadge": "gedeel"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Kluisaanmelding vereis",
|
||||||
|
"description": "'n Venster het oopgemaak om by HashiCorp Vault aan te meld. Voltooi die aanmelding daar; hierdie verbinding sal outomaties voortgaan.",
|
||||||
|
"failed": "Kluisverifikasie het misluk. Probeer asseblief weer.",
|
||||||
|
"reopen": "Maak aanmeldvenster weer oop"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "ملف توقيع Vault",
|
||||||
|
"selectProfile": "اختيار ملف توقيع Vault...",
|
||||||
|
"hint": "تأتي الإعدادات من الملف المشترك؛ ستقوم بتسجيل الدخول إلى Vault عبر OIDC عند الاتصال. لا يتم تخزين أي أسرار.",
|
||||||
|
"docs": "عرض المستندات"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "ملف جديد",
|
||||||
|
"manage": "إدارة ملفات Vault",
|
||||||
|
"name": "الاسم المألوف",
|
||||||
|
"vaultAddr": "عنوان Vault",
|
||||||
|
"namespace": "مساحة الاسم",
|
||||||
|
"oidcMount": "تثبيت مصادقة OIDC",
|
||||||
|
"oidcRole": "دور OIDC",
|
||||||
|
"sshMount": "تثبيت أسرار SSH",
|
||||||
|
"sshRole": "دور موقع SSH",
|
||||||
|
"validPrincipals": "المستخدمون المسموح بهم",
|
||||||
|
"keyType": "نوع المفتاح المؤقت",
|
||||||
|
"shareWithAll": "مشاركة مع جميع المستخدمين",
|
||||||
|
"create": "إنشاء ملف تعريف",
|
||||||
|
"created": "تم إنشاء ملف تعريف Vault",
|
||||||
|
"saved": "تم حفظ ملف تعريف Vault",
|
||||||
|
"deleted": "تم حذف ملف تعريف Vault",
|
||||||
|
"saveFailed": "فشل حفظ ملف تعريف Vault",
|
||||||
|
"deleteFailed": "فشل حذف ملف تعريف Vault",
|
||||||
|
"save": "حفظ ملف التعريف",
|
||||||
|
"validationError": "الاسم وعنوان Vault ودور الموقّع SSH مطلوب",
|
||||||
|
"none": "لا توجد ملفات تعريف Vault بعد.",
|
||||||
|
"sharedBadge": "مشترك"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "مطلوب تسجيل الدخول إلى Vault",
|
||||||
|
"description": "تم فتح نافذة لتسجيل الدخول إلى HashiCorp Vault. أكمل تسجيل الدخول هناك؛ سيستمر هذا الاتصال تلقائيًا.",
|
||||||
|
"failed": "فشلت مصادقة Vault. يرجى المحاولة مرة أخرى.",
|
||||||
|
"reopen": "أعد فتح نافذة تسجيل الدخول"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Профил на подписващ в трезора",
|
||||||
|
"selectProfile": "Изберете профил в трезора...",
|
||||||
|
"hint": "Настройките идват от споделения профил; ще влезете в Vault чрез OIDC, когато се свържете. Не се съхраняват тайни.",
|
||||||
|
"docs": "Преглед на документи"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Нов профил",
|
||||||
|
"manage": "Управление на профили в хранилището",
|
||||||
|
"name": "Приятелско име",
|
||||||
|
"vaultAddr": "Адрес на трезора",
|
||||||
|
"namespace": "Именно пространство",
|
||||||
|
"oidcMount": "OIDC Authentication Mount",
|
||||||
|
"oidcRole": "Роля на OIDC",
|
||||||
|
"sshMount": "SSH Secrets Mount",
|
||||||
|
"sshRole": "Роля на SSH подписващ",
|
||||||
|
"validPrincipals": "Валидни принципали",
|
||||||
|
"keyType": "Тип ефимерен ключ",
|
||||||
|
"shareWithAll": "Споделяне с всички потребители",
|
||||||
|
"create": "Създаване на профил",
|
||||||
|
"created": "Профилът на трезора е създаден",
|
||||||
|
"saved": "Профилът на трезора е запазен",
|
||||||
|
"deleted": "Профилът в трезора е изтрит",
|
||||||
|
"saveFailed": "Запазването на профила в Трезора не бе успешно",
|
||||||
|
"deleteFailed": "Изтриването на профила в Vault не бе успешно",
|
||||||
|
"save": "Запазване на профил",
|
||||||
|
"validationError": "Име, адрес на трезора и роля на SSH подписващ са задължителни",
|
||||||
|
"none": "Все още няма профили в Трезора.",
|
||||||
|
"sharedBadge": "споделено"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Изисква се влизане в трезора",
|
||||||
|
"description": "Отвори се прозорец за влизане в HashiCorp Vault. Завършете влизането там; тази връзка ще продължи автоматично.",
|
||||||
|
"failed": "Удостоверяването на трезора не бе успешно. Моля, опитайте отново.",
|
||||||
|
"reopen": "Отваряне отново на прозореца за влизане"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "ভল্ট স্বাক্ষরকারীর প্রোফাইল",
|
||||||
|
"selectProfile": "একটি ভল্ট প্রোফাইল নির্বাচন করুন...",
|
||||||
|
"hint": "সেটিংস শেয়ার করা প্রোফাইল থেকে আসে; সংযোগ করার সময় আপনি OIDC-এর মাধ্যমে ভল্টে সাইন ইন করবেন। কোনো গোপনীয় তথ্য সংরক্ষিত থাকে না।",
|
||||||
|
"docs": "নথি দেখুন"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "নতুন প্রোফাইল",
|
||||||
|
"manage": "ভল্ট প্রোফাইলগুলি পরিচালনা করুন",
|
||||||
|
"name": "বন্ধুত্বপূর্ণ নাম",
|
||||||
|
"vaultAddr": "ভল্ট ঠিকানা",
|
||||||
|
"namespace": "নেমস্পেস",
|
||||||
|
"oidcMount": "OIDC অথোরাইজেশন মাউন্ট",
|
||||||
|
"oidcRole": "OIDC ভূমিকা",
|
||||||
|
"sshMount": "SSH সিক্রেটস মাউন্ট",
|
||||||
|
"sshRole": "SSH স্বাক্ষরকারীর ভূমিকা",
|
||||||
|
"validPrincipals": "বৈধ অধ্যক্ষরা",
|
||||||
|
"keyType": "ক্ষণস্থায়ী কী টাইপ",
|
||||||
|
"shareWithAll": "সকল ব্যবহারকারীর সাথে শেয়ার করুন",
|
||||||
|
"create": "প্রোফাইল তৈরি করুন",
|
||||||
|
"created": "ভল্ট প্রোফাইল তৈরি করা হয়েছে",
|
||||||
|
"saved": "ভল্ট প্রোফাইল সংরক্ষিত হয়েছে",
|
||||||
|
"deleted": "ভল্ট প্রোফাইল মুছে ফেলা হয়েছে",
|
||||||
|
"saveFailed": "ভল্ট প্রোফাইল সংরক্ষণ করতে ব্যর্থ হয়েছে",
|
||||||
|
"deleteFailed": "ভল্ট প্রোফাইল মুছে ফেলা ব্যর্থ হয়েছে",
|
||||||
|
"save": "প্রোফাইল সংরক্ষণ করুন",
|
||||||
|
"validationError": "নাম, ভল্ট অ্যাড্রেস এবং SSH সাইনার রোল প্রয়োজন।",
|
||||||
|
"none": "এখনো কোনো ভল্ট প্রোফাইল তৈরি হয়নি।",
|
||||||
|
"sharedBadge": "শেয়ার করা"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "ভল্টে সাইন-ইন করা আবশ্যক",
|
||||||
|
"description": "হ্যাশিকর্প ভল্টে সাইন ইন করার জন্য একটি উইন্ডো খুলেছে। সেখানে সাইন-ইন সম্পন্ন করুন; এই সংযোগটি স্বয়ংক্রিয়ভাবে চলতে থাকবে।",
|
||||||
|
"failed": "ভল্ট প্রমাণীকরণ ব্যর্থ হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।",
|
||||||
|
"reopen": "সাইন-ইন উইন্ডো পুনরায় খুলুন"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Perfil del signant de la caixa forta",
|
||||||
|
"selectProfile": "Seleccioneu un perfil de Vault...",
|
||||||
|
"hint": "La configuració prové del perfil compartit; iniciaràs la sessió a Vault mitjançant OIDC quan et connectis. No s'emmagatzemen secrets.",
|
||||||
|
"docs": "Veure documents"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Nou perfil",
|
||||||
|
"manage": "Gestiona els perfils de Vault",
|
||||||
|
"name": "Nom amigable",
|
||||||
|
"vaultAddr": "Adreça de la caixa forta",
|
||||||
|
"namespace": "Espai de noms",
|
||||||
|
"oidcMount": "Muntatge d'autenticació OIDC",
|
||||||
|
"oidcRole": "Rol de l'OIDC",
|
||||||
|
"sshMount": "Muntanya de Secrets SSH",
|
||||||
|
"sshRole": "Rol de signant SSH",
|
||||||
|
"validPrincipals": "Principals vàlids",
|
||||||
|
"keyType": "Tipus de clau efímera",
|
||||||
|
"shareWithAll": "Comparteix amb tots els usuaris",
|
||||||
|
"create": "Crea un perfil",
|
||||||
|
"created": "Perfil de la caixa forta creat",
|
||||||
|
"saved": "Perfil de la caixa forta desat",
|
||||||
|
"deleted": "Perfil de la caixa forta suprimit",
|
||||||
|
"saveFailed": "No s'ha pogut desar el perfil de Vault",
|
||||||
|
"deleteFailed": "No s'ha pogut suprimir el perfil de Vault",
|
||||||
|
"save": "Desa el perfil",
|
||||||
|
"validationError": "El nom, l'adreça de la caixa forta i el rol de signant SSH són obligatoris.",
|
||||||
|
"none": "Encara no hi ha perfils de Vault.",
|
||||||
|
"sharedBadge": "compartit"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Cal iniciar sessió a la caixa forta",
|
||||||
|
"description": "S'ha obert una finestra per iniciar la sessió a HashiCorp Vault. Completeu l'inici de sessió allà; aquesta connexió continuarà automàticament.",
|
||||||
|
"failed": "L'autenticació de la caixa forta ha fallat. Torna-ho a provar.",
|
||||||
|
"reopen": "Torna a obrir la finestra d'inici de sessió"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Profil podepisujícího trezoru",
|
||||||
|
"selectProfile": "Vyberte profil úložiště...",
|
||||||
|
"hint": "Nastavení pocházejí ze sdíleného profilu; po připojení se do úložiště přihlásíte přes OIDC. Žádné tajné kódy se neukládají.",
|
||||||
|
"docs": "Zobrazit dokumenty"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Nový profil",
|
||||||
|
"manage": "Správa profilů úložiště",
|
||||||
|
"name": "Přátelský název",
|
||||||
|
"vaultAddr": "Adresa trezoru",
|
||||||
|
"namespace": "Jmenný prostor",
|
||||||
|
"oidcMount": "Připojení autorizace OIDC",
|
||||||
|
"oidcRole": "Role OIDC",
|
||||||
|
"sshMount": "SSH tajemství Mount",
|
||||||
|
"sshRole": "Role SSH podepisujícího",
|
||||||
|
"validPrincipals": "Platné principy",
|
||||||
|
"keyType": "Typ dočasného klíče",
|
||||||
|
"shareWithAll": "Sdílet se všemi uživateli",
|
||||||
|
"create": "Vytvořit profil",
|
||||||
|
"created": "Profil úložiště vytvořen",
|
||||||
|
"saved": "Profil trezoru uložen",
|
||||||
|
"deleted": "Profil trezoru smazán",
|
||||||
|
"saveFailed": "Uložení profilu úložiště se nezdařilo.",
|
||||||
|
"deleteFailed": "Profil v úložišti se nepodařilo smazat.",
|
||||||
|
"save": "Uložit profil",
|
||||||
|
"validationError": "Jméno, adresa trezoru a role SSH signatáře jsou povinné.",
|
||||||
|
"none": "Zatím žádné profily v úložišti Vault.",
|
||||||
|
"sharedBadge": "sdílené"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Vyžaduje se přihlášení do trezoru",
|
||||||
|
"description": "Otevřelo se okno pro přihlášení do HashiCorp Vault. Dokončete přihlášení tam; toto připojení bude automaticky pokračovat.",
|
||||||
|
"failed": "Ověření v trezoru se nezdařilo. Zkuste to prosím znovu.",
|
||||||
|
"reopen": "Znovu otevřít přihlašovací okno"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Profil for underskriver af arkiv",
|
||||||
|
"selectProfile": "Vælg en Vault-profil...",
|
||||||
|
"hint": "Indstillingerne kommer fra den delte profil; du logger ind på Vault via OIDC, når du opretter forbindelse. Der gemmes ingen hemmeligheder.",
|
||||||
|
"docs": "Se dokumenter"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Ny profil",
|
||||||
|
"manage": "Administrer Vault-profiler",
|
||||||
|
"name": "Venligt navn",
|
||||||
|
"vaultAddr": "Adresse i boksen",
|
||||||
|
"namespace": "Navnerum",
|
||||||
|
"oidcMount": "OIDC-godkendelsesmontering",
|
||||||
|
"oidcRole": "OIDC-rolle",
|
||||||
|
"sshMount": "SSH-hemmeligheder monteres",
|
||||||
|
"sshRole": "SSH-underskriverrolle",
|
||||||
|
"validPrincipals": "Gyldige principper",
|
||||||
|
"keyType": "Kortvarig nøgletype",
|
||||||
|
"shareWithAll": "Del med alle brugere",
|
||||||
|
"create": "Opret profil",
|
||||||
|
"created": "Vault-profil oprettet",
|
||||||
|
"saved": "Vault-profil gemt",
|
||||||
|
"deleted": "Vault-profil slettet",
|
||||||
|
"saveFailed": "Kunne ikke gemme Vault-profilen",
|
||||||
|
"deleteFailed": "Kunne ikke slette Vault-profilen",
|
||||||
|
"save": "Gem profil",
|
||||||
|
"validationError": "Navn, Vault-adresse og SSH-underskriverrolle er påkrævet",
|
||||||
|
"none": "Ingen Vault-profiler endnu.",
|
||||||
|
"sharedBadge": "delt"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Login til Vault kræves",
|
||||||
|
"description": "Et vindue er åbnet, hvor du kan logge ind på HashiCorp Vault. Fuldfør loginprocessen der; forbindelsen fortsætter automatisk.",
|
||||||
|
"failed": "Godkendelse af arkiv mislykkedes. Prøv igen.",
|
||||||
|
"reopen": "Genåbn loginvinduet"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault-Signer-Profil",
|
||||||
|
"selectProfile": "Vault-Profil auswählen...",
|
||||||
|
"hint": "Die Einstellungen stammen aus dem freigegebenen Profil; Sie melden sich beim Verbinden per OIDC bei Vault an. Es werden keine Geheimnisse gespeichert.",
|
||||||
|
"docs": "Dokumentation anzeigen"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Neues Profil",
|
||||||
|
"manage": "Vault-Profile verwalten",
|
||||||
|
"name": "Anzeigename",
|
||||||
|
"vaultAddr": "Vault-Adresse",
|
||||||
|
"namespace": "Namespace",
|
||||||
|
"oidcMount": "OIDC-Auth-Mount",
|
||||||
|
"oidcRole": "OIDC-Rolle",
|
||||||
|
"sshMount": "SSH-Secrets-Mount",
|
||||||
|
"sshRole": "SSH-Signer-Rolle",
|
||||||
|
"validPrincipals": "Gültige Principals",
|
||||||
|
"keyType": "Ephemerer Schlüsseltyp",
|
||||||
|
"shareWithAll": "Mit allen Benutzern teilen",
|
||||||
|
"create": "Profil erstellen",
|
||||||
|
"created": "Vault-Profil erstellt",
|
||||||
|
"saved": "Vault-Profil gespeichert",
|
||||||
|
"deleted": "Vault-Profil gelöscht",
|
||||||
|
"saveFailed": "Fehler beim Speichern des Vault-Profils",
|
||||||
|
"deleteFailed": "Fehler beim Löschen des Vault-Profils",
|
||||||
|
"save": "Profil speichern",
|
||||||
|
"validationError": "Name, Vault-Adresse und SSH-Signer-Rolle sind erforderlich",
|
||||||
|
"none": "Noch keine Vault-Profile.",
|
||||||
|
"sharedBadge": "geteilt"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Vault-Anmeldung erforderlich",
|
||||||
|
"description": "Ein Fenster zur Anmeldung bei HashiCorp Vault wurde geöffnet. Schließen Sie die Anmeldung dort ab; diese Verbindung wird automatisch fortgesetzt.",
|
||||||
|
"failed": "Vault-Authentifizierung fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
||||||
|
"reopen": "Anmeldefenster erneut öffnen"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Προφίλ Υπογράφοντος Vault",
|
||||||
|
"selectProfile": "Επιλέξτε ένα προφίλ Vault...",
|
||||||
|
"hint": "Οι ρυθμίσεις προέρχονται από το κοινόχρηστο προφίλ. Θα συνδεθείτε στο Vault μέσω OIDC όταν συνδεθείτε. Δεν αποθηκεύονται μυστικά.",
|
||||||
|
"docs": "Προβολή εγγράφων"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Νέο προφίλ",
|
||||||
|
"manage": "Διαχείριση προφίλ Vault",
|
||||||
|
"name": "Φιλικό όνομα",
|
||||||
|
"vaultAddr": "Διεύθυνση θησαυροφυλακίου",
|
||||||
|
"namespace": "Χώρος ονομάτων",
|
||||||
|
"oidcMount": "Μοντάζ ελέγχου ταυτότητας OIDC",
|
||||||
|
"oidcRole": "Ρόλος OIDC",
|
||||||
|
"sshMount": "SSH Secrets Mount",
|
||||||
|
"sshRole": "Ρόλος Υπογράφοντος SSH",
|
||||||
|
"validPrincipals": "Έγκυρες Αρχές",
|
||||||
|
"keyType": "Εφήμερος Τύπος Κλειδιού",
|
||||||
|
"shareWithAll": "Κοινοποίηση με όλους τους χρήστες",
|
||||||
|
"create": "Δημιουργία προφίλ",
|
||||||
|
"created": "Δημιουργήθηκε προφίλ Vault",
|
||||||
|
"saved": "Το προφίλ του Vault αποθηκεύτηκε",
|
||||||
|
"deleted": "Το προφίλ Vault διαγράφηκε",
|
||||||
|
"saveFailed": "Αποτυχία αποθήκευσης προφίλ Vault",
|
||||||
|
"deleteFailed": "Αποτυχία διαγραφής προφίλ Vault",
|
||||||
|
"save": "Αποθήκευση προφίλ",
|
||||||
|
"validationError": "Απαιτούνται όνομα, διεύθυνση vault και ρόλος υπογράφοντος SSH",
|
||||||
|
"none": "Δεν υπάρχουν ακόμη προφίλ Vault.",
|
||||||
|
"sharedBadge": "κοινόχρηστο"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Απαιτείται σύνδεση στο Vault",
|
||||||
|
"description": "Έχει ανοίξει ένα παράθυρο για να συνδεθείτε στο HashiCorp Vault. Ολοκληρώστε τη σύνδεση εκεί. Αυτή η σύνδεση θα συνεχιστεί αυτόματα.",
|
||||||
|
"failed": "Ο έλεγχος ταυτότητας του vault απέτυχε. Δοκιμάστε ξανά.",
|
||||||
|
"reopen": "Επανάνοιγμα παραθύρου σύνδεσης"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Perfil de firmante de Vault",
|
||||||
|
"selectProfile": "Seleccionar un perfil de Vault...",
|
||||||
|
"hint": "La configuración proviene del perfil compartido; se iniciará sesión en Vault a través de OIDC al conectarse. No se almacenan secretos.",
|
||||||
|
"docs": "Ver documentación"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Nuevo perfil",
|
||||||
|
"manage": "Administrar perfiles de Vault",
|
||||||
|
"name": "Nombre descriptivo",
|
||||||
|
"vaultAddr": "Dirección de Vault",
|
||||||
|
"namespace": "Espacio de nombres",
|
||||||
|
"oidcMount": "Montaje de autenticación OIDC",
|
||||||
|
"oidcRole": "Rol OIDC",
|
||||||
|
"sshMount": "Montaje de secretos SSH",
|
||||||
|
"sshRole": "Rol de firmante SSH",
|
||||||
|
"validPrincipals": "Principals válidos",
|
||||||
|
"keyType": "Tipo de clave efímera",
|
||||||
|
"shareWithAll": "Compartir con todos los usuarios",
|
||||||
|
"create": "Crear perfil",
|
||||||
|
"created": "Perfil de Vault creado",
|
||||||
|
"saved": "Perfil de Vault guardado",
|
||||||
|
"deleted": "Perfil de Vault eliminado",
|
||||||
|
"saveFailed": "Error al guardar el perfil de Vault",
|
||||||
|
"deleteFailed": "Error al eliminar el perfil de Vault",
|
||||||
|
"save": "Guardar perfil",
|
||||||
|
"validationError": "Se requieren el nombre, la dirección de Vault y el rol de firmante SSH",
|
||||||
|
"none": "Aún no hay perfiles de Vault.",
|
||||||
|
"sharedBadge": "compartido"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Inicio de sesión en Vault requerido",
|
||||||
|
"description": "Se ha abierto una ventana para iniciar sesión en HashiCorp Vault. Completa el inicio de sesión allí; esta conexión continuará automáticamente.",
|
||||||
|
"failed": "La autenticación de Vault falló. Inténtalo de nuevo.",
|
||||||
|
"reopen": "Reabrir ventana de inicio de sesión"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Varaston allekirjoittajan profiili",
|
||||||
|
"selectProfile": "Valitse holviprofiili...",
|
||||||
|
"hint": "Asetukset tulevat jaetusta profiilista; kirjaudut Holviin OIDC:n kautta, kun muodostat yhteyden. Salaisuuksia ei tallenneta.",
|
||||||
|
"docs": "Näytä dokumentit"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Uusi profiili",
|
||||||
|
"manage": "Hallinnoi holviprofiileja",
|
||||||
|
"name": "Ystävällinen nimi",
|
||||||
|
"vaultAddr": "Holvin osoite",
|
||||||
|
"namespace": "Nimiavaruus",
|
||||||
|
"oidcMount": "OIDC-todennusliitos",
|
||||||
|
"oidcRole": "OIDC-rooli",
|
||||||
|
"sshMount": "SSH-salaisuuksien kiinnitys",
|
||||||
|
"sshRole": "SSH-allekirjoittajan rooli",
|
||||||
|
"validPrincipals": "Voimassa olevat pääasialliset",
|
||||||
|
"keyType": "Lyhytaikainen avaintyyppi",
|
||||||
|
"shareWithAll": "Jaa kaikkien käyttäjien kanssa",
|
||||||
|
"create": "Luo profiili",
|
||||||
|
"created": "Holvin profiili luotu",
|
||||||
|
"saved": "Holvin profiili tallennettu",
|
||||||
|
"deleted": "Holvin profiili poistettu",
|
||||||
|
"saveFailed": "Holvin profiilin tallentaminen epäonnistui",
|
||||||
|
"deleteFailed": "Holviprofiilin poistaminen epäonnistui",
|
||||||
|
"save": "Tallenna profiili",
|
||||||
|
"validationError": "Nimi, holvin osoite ja SSH-allekirjoittajan rooli vaaditaan",
|
||||||
|
"none": "Ei vielä holviprofiileja.",
|
||||||
|
"sharedBadge": "jaettu"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Holviin kirjautuminen vaaditaan",
|
||||||
|
"description": "Ikkuna HashiCorp-holviin kirjautumista varten on avautunut. Kirjaudu sisään siinä. Yhteys jatkuu automaattisesti.",
|
||||||
|
"failed": "Holvin todennus epäonnistui. Yritä uudelleen.",
|
||||||
|
"reopen": "Avaa kirjautumisikkuna uudelleen"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Profil de signature Vault",
|
||||||
|
"selectProfile": "Sélectionner un profil Vault...",
|
||||||
|
"hint": "Les paramètres proviennent du profil partagé ; vous vous connecterez à Vault via OIDC lors de la connexion. Aucun secret n'est stocké.",
|
||||||
|
"docs": "Voir la documentation"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Nouveau profil",
|
||||||
|
"manage": "Gérer les profils Vault",
|
||||||
|
"name": "Nom convivial",
|
||||||
|
"vaultAddr": "Adresse Vault",
|
||||||
|
"namespace": "Espace de noms",
|
||||||
|
"oidcMount": "Point de montage d'authentification OIDC",
|
||||||
|
"oidcRole": "Rôle OIDC",
|
||||||
|
"sshMount": "Point de montage des secrets SSH",
|
||||||
|
"sshRole": "Rôle de signature SSH",
|
||||||
|
"validPrincipals": "Principaux valides",
|
||||||
|
"keyType": "Type de clé éphémère",
|
||||||
|
"shareWithAll": "Partager avec tous les utilisateurs",
|
||||||
|
"create": "Créer un profil",
|
||||||
|
"created": "Profil Vault créé",
|
||||||
|
"saved": "Profil Vault enregistré",
|
||||||
|
"deleted": "Profil Vault supprimé",
|
||||||
|
"saveFailed": "Échec de l'enregistrement du profil Vault",
|
||||||
|
"deleteFailed": "Échec de la suppression du profil Vault",
|
||||||
|
"save": "Enregistrer le profil",
|
||||||
|
"validationError": "Le nom, l'adresse Vault et le rôle de signataire SSH sont requis",
|
||||||
|
"none": "Aucun profil Vault pour le moment",
|
||||||
|
"sharedBadge": "partagé"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Connexion à Vault requise",
|
||||||
|
"description": "Une fenêtre s'est ouverte pour vous connecter à HashiCorp Vault. Terminez la connexion dans cette fenêtre ; cette connexion continuera automatiquement.",
|
||||||
|
"failed": "Échec de l'authentification Vault. Veuillez réessayer.",
|
||||||
|
"reopen": "Rouvrir la fenêtre de connexion"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "פרופיל חותם בכספת",
|
||||||
|
"selectProfile": "בחר פרופיל כספת...",
|
||||||
|
"hint": "ההגדרות מגיעות מהפרופיל המשותף; תיכנס לכספת דרך OIDC כשתתחבר. לא נשמרים סודות.",
|
||||||
|
"docs": "הצג מסמכים"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "פרופיל חדש",
|
||||||
|
"manage": "ניהול פרופילי כספת",
|
||||||
|
"name": "שם ידידותי",
|
||||||
|
"vaultAddr": "כתובת הכספת",
|
||||||
|
"namespace": "מרחב השמות",
|
||||||
|
"oidcMount": "אישור OIDC",
|
||||||
|
"oidcRole": "תפקיד OIDC",
|
||||||
|
"sshMount": "סודות SSH מאוורר",
|
||||||
|
"sshRole": "תפקיד חותם SSH",
|
||||||
|
"validPrincipals": "עקרונות תקפים",
|
||||||
|
"keyType": "סוג מפתח זמני",
|
||||||
|
"shareWithAll": "שתף עם כל המשתמשים",
|
||||||
|
"create": "צור פרופיל",
|
||||||
|
"created": "פרופיל הכספת נוצר",
|
||||||
|
"saved": "פרופיל הכספת נשמר",
|
||||||
|
"deleted": "פרופיל הכספת נמחק",
|
||||||
|
"saveFailed": "שמירת פרופיל הכספת נכשלה",
|
||||||
|
"deleteFailed": "נכשלה מחיקת פרופיל הכספת",
|
||||||
|
"save": "שמור פרופיל",
|
||||||
|
"validationError": "שם, כתובת כספת ותפקיד חתימה ב-SSH נדרשים",
|
||||||
|
"none": "אין עדיין פרופילים בכספת.",
|
||||||
|
"sharedBadge": "מְשׁוּתָף"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "נדרשת כניסה לכספת",
|
||||||
|
"description": "נפתח חלון לכניסה לכספת HashiCorp. השלם את תהליך הכניסה שם; חיבור זה ימשיך אוטומטית.",
|
||||||
|
"failed": "אימות הכספת נכשל. אנא נסה שוב.",
|
||||||
|
"reopen": "פתח מחדש את חלון הכניסה"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault साइनर प्रोफ़ाइल",
|
||||||
|
"selectProfile": "एक Vault प्रोफ़ाइल चुनें...",
|
||||||
|
"hint": "सेटिंग साझा प्रोफ़ाइल से आती हैं; कनेक्ट करते समय आप OIDC के माध्यम से Vault में साइन इन करेंगे। कोई सीक्रेट संग्रहीत नहीं किए जाते।",
|
||||||
|
"docs": "दस्तावेज़ देखें"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "नई प्रोफ़ाइल",
|
||||||
|
"manage": "Vault प्रोफ़ाइल प्रबंधित करें",
|
||||||
|
"name": "अनुकूल नाम",
|
||||||
|
"vaultAddr": "Vault पता",
|
||||||
|
"namespace": "नेमस्पेस",
|
||||||
|
"oidcMount": "OIDC ऑथ माउंट",
|
||||||
|
"oidcRole": "OIDC रोल",
|
||||||
|
"sshMount": "SSH सीक्रेट माउंट",
|
||||||
|
"sshRole": "SSH साइनर रोल",
|
||||||
|
"validPrincipals": "मान्य प्रिंसिपल",
|
||||||
|
"keyType": "एफेमरल कुंजी प्रकार",
|
||||||
|
"shareWithAll": "सभी उपयोगकर्ताओं के साथ साझा करें",
|
||||||
|
"create": "प्रोफ़ाइल बनाएँ",
|
||||||
|
"created": "Vault प्रोफ़ाइल बनाई गई",
|
||||||
|
"saved": "Vault प्रोफ़ाइल सहेजी गई",
|
||||||
|
"deleted": "Vault प्रोफ़ाइल हटाई गई",
|
||||||
|
"saveFailed": "Vault प्रोफ़ाइल सहेजने में विफल",
|
||||||
|
"deleteFailed": "Vault प्रोफ़ाइल हटाने में विफल",
|
||||||
|
"save": "प्रोफ़ाइल सहेजें",
|
||||||
|
"validationError": "नाम, Vault पता और SSH हस्ताक्षरकर्ता भूमिका आवश्यक हैं",
|
||||||
|
"none": "अभी तक कोई Vault प्रोफ़ाइल नहीं है।",
|
||||||
|
"sharedBadge": "साझा"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Vault साइन-इन आवश्यक है",
|
||||||
|
"description": "HashiCorp Vault में साइन इन करने के लिए एक विंडो खुल गई है। वहाँ साइन-इन पूरा करें; यह कनेक्शन स्वचालित रूप से जारी रहेगा।",
|
||||||
|
"failed": "Vault प्रमाणीकरण विफल। कृपया पुनः प्रयास करें।",
|
||||||
|
"reopen": "साइन-इन विंडो फिर से खोलें"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault aláírói profil",
|
||||||
|
"selectProfile": "Válasszon ki egy Vault profilt...",
|
||||||
|
"hint": "A beállítások a megosztott profilból származnak; a csatlakozáskor OIDC-n keresztül fog bejelentkezni a Vaultba. A rendszer nem tárol titkos adatokat.",
|
||||||
|
"docs": "Dokumentumok megtekintése"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Új profil",
|
||||||
|
"manage": "Vault profilok kezelése",
|
||||||
|
"name": "Barátságos név",
|
||||||
|
"vaultAddr": "Trezor címe",
|
||||||
|
"namespace": "Névtér",
|
||||||
|
"oidcMount": "OIDC hitelesítési csatolás",
|
||||||
|
"oidcRole": "OIDC szerepkör",
|
||||||
|
"sshMount": "SSH titkok felhúzása",
|
||||||
|
"sshRole": "SSH aláíró szerepkör",
|
||||||
|
"validPrincipals": "Érvényes megbízók",
|
||||||
|
"keyType": "Efemer kulcstípus",
|
||||||
|
"shareWithAll": "Megosztás az összes felhasználóval",
|
||||||
|
"create": "Profil létrehozása",
|
||||||
|
"created": "Létrehozva a trezor profilja",
|
||||||
|
"saved": "A trezor profilja mentve",
|
||||||
|
"deleted": "Tárolóprofil törölve",
|
||||||
|
"saveFailed": "Nem sikerült menteni a Vault profilját",
|
||||||
|
"deleteFailed": "Nem sikerült törölni a Vault profilját",
|
||||||
|
"save": "Profil mentése",
|
||||||
|
"validationError": "Név, trezor címe és SSH aláírói szerepkör megadása kötelező.",
|
||||||
|
"none": "Még nincsenek Vault-profilok.",
|
||||||
|
"sharedBadge": "megosztott"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Bejelentkezés szükséges a széfbe",
|
||||||
|
"description": "Megnyílt egy ablak a HashiCorp Vaultba való bejelentkezéshez. Fejezze be a bejelentkezést ott; a kapcsolat automatikusan folytatódik.",
|
||||||
|
"failed": "A trezor hitelesítése sikertelen. Próbálja újra.",
|
||||||
|
"reopen": "Bejelentkezési ablak újranyitása"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Profil Penandatangan Vault",
|
||||||
|
"selectProfile": "Pilih profil Vault...",
|
||||||
|
"hint": "Pengaturan berasal dari profil bersama; Anda akan masuk ke Vault melalui OIDC saat terhubung. Tidak ada rahasia yang disimpan.",
|
||||||
|
"docs": "Lihat dokumen"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Profil baru",
|
||||||
|
"manage": "Kelola profil Vault",
|
||||||
|
"name": "Nama yang Ramah",
|
||||||
|
"vaultAddr": "Alamat Brankas",
|
||||||
|
"namespace": "Ruang nama",
|
||||||
|
"oidcMount": "OIDC Otorisasi Pemasangan",
|
||||||
|
"oidcRole": "Peran OIDC",
|
||||||
|
"sshMount": "Pemasangan Rahasia SSH",
|
||||||
|
"sshRole": "Peran Penandatangan SSH",
|
||||||
|
"validPrincipals": "Prinsip-prinsip yang Sah",
|
||||||
|
"keyType": "Jenis Kunci Sementara",
|
||||||
|
"shareWithAll": "Bagikan dengan semua pengguna",
|
||||||
|
"create": "Buat profil",
|
||||||
|
"created": "Profil Vault telah dibuat.",
|
||||||
|
"saved": "Profil Vault tersimpan.",
|
||||||
|
"deleted": "Profil Vault dihapus",
|
||||||
|
"saveFailed": "Gagal menyimpan profil Vault",
|
||||||
|
"deleteFailed": "Gagal menghapus profil Vault",
|
||||||
|
"save": "Simpan profil",
|
||||||
|
"validationError": "Nama, alamat Vault, dan peran penandatangan SSH wajib diisi.",
|
||||||
|
"none": "Belum ada profil Vault.",
|
||||||
|
"sharedBadge": "dibagikan"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Login ke Vault diperlukan.",
|
||||||
|
"description": "Jendela telah terbuka untuk masuk ke HashiCorp Vault. Selesaikan proses masuk di sana; koneksi ini akan berlanjut secara otomatis.",
|
||||||
|
"failed": "Autentikasi Vault gagal. Silakan coba lagi.",
|
||||||
|
"reopen": "Buka kembali jendela masuk"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Profilo firmatario Vault",
|
||||||
|
"selectProfile": "Seleziona un profilo Vault...",
|
||||||
|
"hint": "Le impostazioni provengono dal profilo condiviso; effettuerai l'accesso a Vault tramite OIDC quando ti connetti. Nessun segreto è memorizzato.",
|
||||||
|
"docs": "Visualizza documentazione"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Nuovo profilo",
|
||||||
|
"manage": "Gestisci profili Vault",
|
||||||
|
"name": "Nome descrittivo",
|
||||||
|
"vaultAddr": "Indirizzo Vault",
|
||||||
|
"namespace": "Namespace",
|
||||||
|
"oidcMount": "Mount di autenticazione OIDC",
|
||||||
|
"oidcRole": "Ruolo OIDC",
|
||||||
|
"sshMount": "Mount dei segreti SSH",
|
||||||
|
"sshRole": "Ruolo firmatario SSH",
|
||||||
|
"validPrincipals": "Principal validi",
|
||||||
|
"keyType": "Tipo di chiave effimera",
|
||||||
|
"shareWithAll": "Condividi con tutti gli utenti",
|
||||||
|
"create": "Crea profilo",
|
||||||
|
"created": "Profilo Vault creato",
|
||||||
|
"saved": "Profilo Vault salvato",
|
||||||
|
"deleted": "Profilo Vault eliminato",
|
||||||
|
"saveFailed": "Salvataggio profilo Vault non riuscito",
|
||||||
|
"deleteFailed": "Eliminazione profilo Vault non riuscita",
|
||||||
|
"save": "Salva profilo",
|
||||||
|
"validationError": "Nome, indirizzo Vault e ruolo di firma SSH sono obbligatori",
|
||||||
|
"none": "Nessun profilo Vault ancora.",
|
||||||
|
"sharedBadge": "condiviso"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Accesso a Vault richiesto",
|
||||||
|
"description": "Una finestra si è aperta per accedere a HashiCorp Vault. Completa l'accesso lì; la connessione continuerà automaticamente.",
|
||||||
|
"failed": "Autenticazione Vault fallita. Riprova.",
|
||||||
|
"reopen": "Riapri la finestra di accesso"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault 署名者プロファイル",
|
||||||
|
"selectProfile": "Vault プロファイルを選択...",
|
||||||
|
"hint": "設定は共有プロファイルから取得され、接続時に OIDC で Vault にサインインします。シークレットは保存されません。",
|
||||||
|
"docs": "ドキュメントを表示"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "新規プロファイル",
|
||||||
|
"manage": "Vault プロファイルの管理",
|
||||||
|
"name": "表示名",
|
||||||
|
"vaultAddr": "Vault アドレス",
|
||||||
|
"namespace": "名前空間",
|
||||||
|
"oidcMount": "OIDC 認証マウント",
|
||||||
|
"oidcRole": "OIDC ロール",
|
||||||
|
"sshMount": "SSH シークレットマウント",
|
||||||
|
"sshRole": "SSH 署名者ロール",
|
||||||
|
"validPrincipals": "有効なプリンシパル",
|
||||||
|
"keyType": "エフェメラルキータイプ",
|
||||||
|
"shareWithAll": "すべてのユーザーと共有",
|
||||||
|
"create": "プロファイルを作成",
|
||||||
|
"created": "Vaultプロファイルが作成されました",
|
||||||
|
"saved": "Vaultプロファイルが保存されました",
|
||||||
|
"deleted": "Vaultプロファイルが削除されました",
|
||||||
|
"saveFailed": "Vaultプロファイルの保存に失敗しました",
|
||||||
|
"deleteFailed": "Vaultプロファイルの削除に失敗しました",
|
||||||
|
"save": "プロファイルを保存",
|
||||||
|
"validationError": "名前、Vaultアドレス、SSH署名ロールは必須です",
|
||||||
|
"none": "まだVaultプロファイルがありません。",
|
||||||
|
"sharedBadge": "共有"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Vault サインインが必要です",
|
||||||
|
"description": "HashiCorp Vaultへのサインイン用のウィンドウが開きました。そこでサインインを完了すると、この接続は自動的に続行されます。",
|
||||||
|
"failed": "Vault 認証に失敗しました。もう一度お試しください。",
|
||||||
|
"reopen": "サインインウィンドウを再表示"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault 서명자 프로필",
|
||||||
|
"selectProfile": "Vault 프로필 선택...",
|
||||||
|
"hint": "설정은 공유 프로필에서 가져옵니다. 연결 시 OIDC를 통해 Vault에 로그인하게 됩니다. 비밀은 저장되지 않습니다.",
|
||||||
|
"docs": "문서 보기"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "새 프로필",
|
||||||
|
"manage": "Vault 프로필 관리",
|
||||||
|
"name": "별칭",
|
||||||
|
"vaultAddr": "Vault 주소",
|
||||||
|
"namespace": "네임스페이스",
|
||||||
|
"oidcMount": "OIDC 인증 마운트",
|
||||||
|
"oidcRole": "OIDC 역할",
|
||||||
|
"sshMount": "SSH 시크릿 마운트",
|
||||||
|
"sshRole": "SSH 서명자 역할",
|
||||||
|
"validPrincipals": "유효한 프린시펄",
|
||||||
|
"keyType": "임시 키 유형",
|
||||||
|
"shareWithAll": "모든 사용자와 공유",
|
||||||
|
"create": "프로필 생성",
|
||||||
|
"created": "Vault 프로필 생성됨",
|
||||||
|
"saved": "Vault 프로필 저장됨",
|
||||||
|
"deleted": "Vault 프로필 삭제됨",
|
||||||
|
"saveFailed": "Vault 프로필 저장 실패",
|
||||||
|
"deleteFailed": "Vault 프로필 삭제 실패",
|
||||||
|
"save": "프로필 저장",
|
||||||
|
"validationError": "이름, Vault 주소 및 SSH 서명자 역할이 필요합니다.",
|
||||||
|
"none": "아직 Vault 프로필이 없습니다.",
|
||||||
|
"sharedBadge": "공유"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Vault 로그인 필요",
|
||||||
|
"description": "HashiCorp Vault 로그인을 위한 창이 열렸습니다. 해당 창에서 로그인을 완료하면 연결이 자동으로 계속됩니다.",
|
||||||
|
"failed": "Vault 인증에 실패했습니다. 다시 시도하세요.",
|
||||||
|
"reopen": "로그인 창 다시 열기"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault-ondertekenaarprofiel",
|
||||||
|
"selectProfile": "Selecteer een Vault-profiel...",
|
||||||
|
"hint": "De instellingen zijn afkomstig van het gedeelde profiel; u meldt zich bij Vault aan via OIDC wanneer u verbinding maakt. Er worden geen geheimen opgeslagen.",
|
||||||
|
"docs": "Documenten bekijken"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Nieuw profiel",
|
||||||
|
"manage": "Beheer Vault-profielen",
|
||||||
|
"name": "Vriendelijke naam",
|
||||||
|
"vaultAddr": "Kluisadres",
|
||||||
|
"namespace": "Naamruimte",
|
||||||
|
"oidcMount": "OIDC-authenticatiekoppeling",
|
||||||
|
"oidcRole": "OIDC-rol",
|
||||||
|
"sshMount": "SSH-geheimen Mount",
|
||||||
|
"sshRole": "SSH-ondertekenaarrol",
|
||||||
|
"validPrincipals": "Geldige principes",
|
||||||
|
"keyType": "Tijdelijk sleuteltype",
|
||||||
|
"shareWithAll": "Delen met alle gebruikers",
|
||||||
|
"create": "Profiel aanmaken",
|
||||||
|
"created": "Vault-profiel aangemaakt",
|
||||||
|
"saved": "Vault-profiel opgeslagen",
|
||||||
|
"deleted": "Kluisprofiel verwijderd",
|
||||||
|
"saveFailed": "Het opslaan van het Vault-profiel is mislukt.",
|
||||||
|
"deleteFailed": "Het verwijderen van het Vault-profiel is mislukt.",
|
||||||
|
"save": "Profiel opslaan",
|
||||||
|
"validationError": "Naam, kluisadres en SSH-ondertekeningsrol zijn vereist.",
|
||||||
|
"none": "Nog geen Vault-profielen beschikbaar.",
|
||||||
|
"sharedBadge": "gedeeld"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Aanmelden bij Vault is vereist.",
|
||||||
|
"description": "Er is een venster geopend om in te loggen bij HashiCorp Vault. Voltooi de aanmelding daar; deze verbinding wordt automatisch tot stand gebracht.",
|
||||||
|
"failed": "Authenticatie voor de kluis is mislukt. Probeer het opnieuw.",
|
||||||
|
"reopen": "Heropen het aanmeldvenster"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Profil for hvelvsigner",
|
||||||
|
"selectProfile": "Velg en Vault-profil...",
|
||||||
|
"hint": "Innstillingene kommer fra den delte profilen. Du logger deg på Vault via OIDC når du kobler til. Ingen hemmeligheter lagres.",
|
||||||
|
"docs": "Vis dokumenter"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Ny profil",
|
||||||
|
"manage": "Administrer Vault-profiler",
|
||||||
|
"name": "Vennlig navn",
|
||||||
|
"vaultAddr": "Hvelvadresse",
|
||||||
|
"namespace": "Navnerom",
|
||||||
|
"oidcMount": "OIDC-autentiseringsmontering",
|
||||||
|
"oidcRole": "OIDC-rolle",
|
||||||
|
"sshMount": "SSH-hemmeligheter monteres",
|
||||||
|
"sshRole": "SSH-signatørrolle",
|
||||||
|
"validPrincipals": "Gyldige prinsipaler",
|
||||||
|
"keyType": "Kortvarig nøkkeltype",
|
||||||
|
"shareWithAll": "Del med alle brukere",
|
||||||
|
"create": "Opprett profil",
|
||||||
|
"created": "Vault-profil opprettet",
|
||||||
|
"saved": "Vault-profilen er lagret",
|
||||||
|
"deleted": "Vault-profilen er slettet",
|
||||||
|
"saveFailed": "Kunne ikke lagre Vault-profilen",
|
||||||
|
"deleteFailed": "Kunne ikke slette Vault-profilen",
|
||||||
|
"save": "Lagre profil",
|
||||||
|
"validationError": "Navn, hvelvadresse og SSH-signerrolle er obligatorisk",
|
||||||
|
"none": "Ingen Vault-profiler ennå.",
|
||||||
|
"sharedBadge": "delt"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Pålogging til arkivet kreves",
|
||||||
|
"description": "Et vindu har åpnet seg for å logge på HashiCorp Vault. Fullfør påloggingen der; denne tilkoblingen vil fortsette automatisk.",
|
||||||
|
"failed": "Hvelv-autentisering mislyktes. Prøv på nytt.",
|
||||||
|
"reopen": "Åpne påloggingsvinduet på nytt"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Profil podpisujący Vault",
|
||||||
|
"selectProfile": "Wybierz profil Vault...",
|
||||||
|
"hint": "Ustawienia pochodzą z profilu współdzielonego. Po połączeniu zalogujesz się do Vault przez OIDC. Nie są przechowywane żadne sekrety.",
|
||||||
|
"docs": "Zobacz dokumentację"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Nowy profil",
|
||||||
|
"manage": "Zarządzaj profilami Vault",
|
||||||
|
"name": "Przyjazna nazwa",
|
||||||
|
"vaultAddr": "Adres Vault",
|
||||||
|
"namespace": "Przestrzeń nazw",
|
||||||
|
"oidcMount": "Mount uwierzytelniania OIDC",
|
||||||
|
"oidcRole": "Rola OIDC",
|
||||||
|
"sshMount": "Mount sekretów SSH",
|
||||||
|
"sshRole": "Rola podpisująca SSH",
|
||||||
|
"validPrincipals": "Dozwoleni użytkownicy (principals)",
|
||||||
|
"keyType": "Typ klucza efemerycznego",
|
||||||
|
"shareWithAll": "Udostępnij wszystkim użytkownikom",
|
||||||
|
"create": "Utwórz profil",
|
||||||
|
"created": "Utworzono profil Vault",
|
||||||
|
"saved": "Zapisano profil Vault",
|
||||||
|
"deleted": "Usunięto profil Vault",
|
||||||
|
"saveFailed": "Nie udało się zapisać profilu Vault",
|
||||||
|
"deleteFailed": "Nie udało się usunąć profilu Vault",
|
||||||
|
"save": "Zapisz profil",
|
||||||
|
"validationError": "Wymagane są nazwa, adres Vault i rola podpisująca SSH",
|
||||||
|
"none": "Brak profili Vault.",
|
||||||
|
"sharedBadge": "współdzielony"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Wymagane logowanie do Vault",
|
||||||
|
"description": "Otworzyło się okno logowania do HashiCorp Vault. Dokończ logowanie, a połączenie zostanie nawiązane automatycznie.",
|
||||||
|
"failed": "Uwierzytelnienie w Vault nie powiodło się. Spróbuj ponownie.",
|
||||||
|
"reopen": "Otwórz ponownie okno logowania"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Perfil do assinante do Vault",
|
||||||
|
"selectProfile": "Selecione um perfil do Vault...",
|
||||||
|
"hint": "As configurações são provenientes do perfil compartilhado; você fará login no Vault via OIDC ao se conectar. Nenhum segredo é armazenado.",
|
||||||
|
"docs": "Ver documentos"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Novo perfil",
|
||||||
|
"manage": "Gerenciar perfis do Vault",
|
||||||
|
"name": "Nome amigável",
|
||||||
|
"vaultAddr": "Endereço do cofre",
|
||||||
|
"namespace": "Espaço de nomes",
|
||||||
|
"oidcMount": "Montagem de autenticação OIDC",
|
||||||
|
"oidcRole": "Função OIDC",
|
||||||
|
"sshMount": "Montagem de segredos SSH",
|
||||||
|
"sshRole": "Função de assinatura SSH",
|
||||||
|
"validPrincipals": "Principais válidos",
|
||||||
|
"keyType": "Tipo de chave efêmera",
|
||||||
|
"shareWithAll": "Compartilhe com todos os usuários",
|
||||||
|
"create": "Criar perfil",
|
||||||
|
"created": "Perfil do Vault criado",
|
||||||
|
"saved": "Perfil do Vault salvo",
|
||||||
|
"deleted": "Perfil do Vault excluído",
|
||||||
|
"saveFailed": "Falha ao salvar o perfil do Vault",
|
||||||
|
"deleteFailed": "Falha ao excluir o perfil do Vault",
|
||||||
|
"save": "Salvar perfil",
|
||||||
|
"validationError": "Nome, endereço do Vault e função de assinatura SSH são obrigatórios.",
|
||||||
|
"none": "Ainda não há perfis no Vault.",
|
||||||
|
"sharedBadge": "compartilhado"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "É necessário fazer login no cofre.",
|
||||||
|
"description": "Uma janela foi aberta para fazer login no HashiCorp Vault. Conclua o login; esta conexão será estabelecida automaticamente.",
|
||||||
|
"failed": "A autenticação do cofre falhou. Tente novamente.",
|
||||||
|
"reopen": "Reabrir janela de login"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Perfil de assinatura Vault",
|
||||||
|
"selectProfile": "Selecionar um perfil Vault...",
|
||||||
|
"hint": "As definições provêm do perfil partilhado; ao ligar, iniciará sessão no Vault via OIDC. Nenhum segredo é armazenado.",
|
||||||
|
"docs": "Ver documentação"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Novo perfil",
|
||||||
|
"manage": "Gerir perfis Vault",
|
||||||
|
"name": "Nome Amigável",
|
||||||
|
"vaultAddr": "Endereço Vault",
|
||||||
|
"namespace": "Namespace",
|
||||||
|
"oidcMount": "Mount de autenticação OIDC",
|
||||||
|
"oidcRole": "Função OIDC",
|
||||||
|
"sshMount": "Mount de segredos SSH",
|
||||||
|
"sshRole": "Função de assinatura SSH",
|
||||||
|
"validPrincipals": "Principais válidos",
|
||||||
|
"keyType": "Tipo de chave efémera",
|
||||||
|
"shareWithAll": "Partilhar com todos os utilizadores",
|
||||||
|
"create": "Criar perfil",
|
||||||
|
"created": "Perfil Vault criado",
|
||||||
|
"saved": "Perfil Vault guardado",
|
||||||
|
"deleted": "Perfil Vault eliminado",
|
||||||
|
"saveFailed": "Falha ao guardar perfil Vault",
|
||||||
|
"deleteFailed": "Falha ao eliminar perfil Vault",
|
||||||
|
"save": "Guardar perfil",
|
||||||
|
"validationError": "Nome, endereço Vault e função de signatário SSH são obrigatórios",
|
||||||
|
"none": "Ainda sem perfis Vault.",
|
||||||
|
"sharedBadge": "partilhado"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Início de sessão no Vault necessário",
|
||||||
|
"description": "Foi aberta uma janela para iniciar sessão no HashiCorp Vault. Conclua o início de sessão aí; esta ligação continuará automaticamente.",
|
||||||
|
"failed": "A autenticação no Vault falhou. Tente novamente.",
|
||||||
|
"reopen": "Reabrir janela de início de sessão"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Profilul semnatarului din seif",
|
||||||
|
"selectProfile": "Selectați un profil Vault...",
|
||||||
|
"hint": "Setările provin din profilul partajat; te vei conecta la Vault prin OIDC când te conectezi. Nu sunt stocate secrete.",
|
||||||
|
"docs": "Vizualizați documentele"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Profil nou",
|
||||||
|
"manage": "Gestionați profilurile din seif",
|
||||||
|
"name": "Nume prietenos",
|
||||||
|
"vaultAddr": "Adresă seif",
|
||||||
|
"namespace": "Spațiu de nume",
|
||||||
|
"oidcMount": "Montare autentificare OIDC",
|
||||||
|
"oidcRole": "Rolul OIDC",
|
||||||
|
"sshMount": "Secretele SSH Mount",
|
||||||
|
"sshRole": "Rolul semnatarului SSH",
|
||||||
|
"validPrincipals": "Principali validi",
|
||||||
|
"keyType": "Tip de cheie efemeră",
|
||||||
|
"shareWithAll": "Partajați cu toți utilizatorii",
|
||||||
|
"create": "Creați un profil",
|
||||||
|
"created": "Profilul seifului a fost creat",
|
||||||
|
"saved": "Profilul seifului a fost salvat",
|
||||||
|
"deleted": "Profilul seifului a fost șters",
|
||||||
|
"saveFailed": "Nu s-a putut salva profilul din Seif",
|
||||||
|
"deleteFailed": "Nu s-a putut șterge profilul Vault",
|
||||||
|
"save": "Salvați profilul",
|
||||||
|
"validationError": "Numele, adresa seifului și rolul semnatarului SSH sunt obligatorii.",
|
||||||
|
"none": "Încă nu există profiluri Vault.",
|
||||||
|
"sharedBadge": "partajat"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Este necesară conectarea la seif",
|
||||||
|
"description": "S-a deschis o fereastră pentru a vă conecta la HashiCorp Vault. Finalizați conectarea acolo; această conexiune va continua automat.",
|
||||||
|
"failed": "Autentificarea în seif a eșuat. Vă rugăm să încercați din nou.",
|
||||||
|
"reopen": "Redeschideți fereastra de conectare"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Профиль подписчика Vault",
|
||||||
|
"selectProfile": "Выберите профиль Vault...",
|
||||||
|
"hint": "Настройки берутся из общего профиля; при подключении вы войдёте в Vault через OIDC. Секреты не хранятся.",
|
||||||
|
"docs": "Документация"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Новый профиль",
|
||||||
|
"manage": "Управление профилями Vault",
|
||||||
|
"name": "Дружественное имя",
|
||||||
|
"vaultAddr": "Адрес Vault",
|
||||||
|
"namespace": "Пространство имён",
|
||||||
|
"oidcMount": "Монтирование аутентификации OIDC",
|
||||||
|
"oidcRole": "Роль OIDC",
|
||||||
|
"sshMount": "Монтирование секретов SSH",
|
||||||
|
"sshRole": "Роль подписчика SSH",
|
||||||
|
"validPrincipals": "Допустимые принципалы",
|
||||||
|
"keyType": "Тип эфемерного ключа",
|
||||||
|
"shareWithAll": "Общий доступ для всех пользователей",
|
||||||
|
"create": "Создать профиль",
|
||||||
|
"created": "Профиль Vault создан",
|
||||||
|
"saved": "Профиль Vault сохранён",
|
||||||
|
"deleted": "Профиль Vault удалён",
|
||||||
|
"saveFailed": "Не удалось сохранить профиль Vault",
|
||||||
|
"deleteFailed": "Не удалось удалить профиль Vault",
|
||||||
|
"save": "Сохранить профиль",
|
||||||
|
"validationError": "Требуются имя, адрес Vault и роль SSH подписанта",
|
||||||
|
"none": "Пока нет профилей Vault.",
|
||||||
|
"sharedBadge": "общий"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Требуется вход в Vault",
|
||||||
|
"description": "Открыто окно для входа в HashiCorp Vault. Завершите вход там; подключение продолжится автоматически.",
|
||||||
|
"failed": "Аутентификация Vault не удалась. Попробуйте снова.",
|
||||||
|
"reopen": "Повторно открыть окно входа"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Профил потписника трезора",
|
||||||
|
"selectProfile": "Изаберите профил трезора...",
|
||||||
|
"hint": "Подешавања долазе из дељеног профила; пријавићете се у Vault преко OIDC-а када се повежете. Ниједне тајне се не чувају.",
|
||||||
|
"docs": "Прикажи документе"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Нови профил",
|
||||||
|
"manage": "Управљање профилима трезора",
|
||||||
|
"name": "Пријатељско име",
|
||||||
|
"vaultAddr": "Адреса трезора",
|
||||||
|
"namespace": "Именски простор",
|
||||||
|
"oidcMount": "OIDC аутентификација монтирања",
|
||||||
|
"oidcRole": "Улога OIDC-а",
|
||||||
|
"sshMount": "SSH тајне носач",
|
||||||
|
"sshRole": "Улога SSH потписника",
|
||||||
|
"validPrincipals": "Важећи принципали",
|
||||||
|
"keyType": "Тип ефемерног кључа",
|
||||||
|
"shareWithAll": "Подели са свим корисницима",
|
||||||
|
"create": "Направи профил",
|
||||||
|
"created": "Профил трезора је креиран",
|
||||||
|
"saved": "Профил трезора је сачуван",
|
||||||
|
"deleted": "Профил трезора је обрисан",
|
||||||
|
"saveFailed": "Чување профила у трезору није успело",
|
||||||
|
"deleteFailed": "Брисање профила у Трезору није успело",
|
||||||
|
"save": "Сачувај профил",
|
||||||
|
"validationError": "Име, адреса трезора и улога SSH потписника су обавезни",
|
||||||
|
"none": "Још нема профила у Трезору.",
|
||||||
|
"sharedBadge": "дељено"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Потребно је пријављивање у трезор",
|
||||||
|
"description": "Отворио се прозор за пријављивање на HashiCorp Vault. Завршите пријављивање тамо; ова веза ће се аутоматски наставити.",
|
||||||
|
"failed": "Аутентификација трезора није успела. Молимо покушајте поново.",
|
||||||
|
"reopen": "Поново отвори прозор за пријављивање"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Profil för valvsignerare",
|
||||||
|
"selectProfile": "Välj en Vault-profil...",
|
||||||
|
"hint": "Inställningarna kommer från den delade profilen; du loggar in på Vault via OIDC när du ansluter. Inga hemligheter lagras.",
|
||||||
|
"docs": "Visa dokument"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Ny profil",
|
||||||
|
"manage": "Hantera Vault-profiler",
|
||||||
|
"name": "Vänligt namn",
|
||||||
|
"vaultAddr": "Valvadress",
|
||||||
|
"namespace": "Namnrymd",
|
||||||
|
"oidcMount": "OIDC-autentiseringsmontering",
|
||||||
|
"oidcRole": "OIDC-roll",
|
||||||
|
"sshMount": "SSH-hemligheter monteras",
|
||||||
|
"sshRole": "SSH-signerarroll",
|
||||||
|
"validPrincipals": "Giltiga principer",
|
||||||
|
"keyType": "Tillfällig nyckeltyp",
|
||||||
|
"shareWithAll": "Dela med alla användare",
|
||||||
|
"create": "Skapa profil",
|
||||||
|
"created": "Arkivprofil skapad",
|
||||||
|
"saved": "Arkivprofilen har sparats",
|
||||||
|
"deleted": "Arkivprofil raderad",
|
||||||
|
"saveFailed": "Kunde inte spara arkivprofilen",
|
||||||
|
"deleteFailed": "Misslyckades med att ta bort Arkiv-profilen",
|
||||||
|
"save": "Spara profil",
|
||||||
|
"validationError": "Namn, valvadress och SSH-signerarroll krävs",
|
||||||
|
"none": "Inga Vault-profiler ännu.",
|
||||||
|
"sharedBadge": "delad"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Inloggning till arkivet krävs",
|
||||||
|
"description": "Ett fönster har öppnats för att logga in på HashiCorp Vault. Slutför inloggningen där; anslutningen fortsätter automatiskt.",
|
||||||
|
"failed": "Valvautentisering misslyckades. Försök igen.",
|
||||||
|
"reopen": "Öppna inloggningsfönstret igen"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "โปรไฟล์ผู้ลงนาม Vault",
|
||||||
|
"selectProfile": "เลือกโปรไฟล์ Vault...",
|
||||||
|
"hint": "การตั้งค่าต่างๆ มาจากโปรไฟล์ที่ใช้ร่วมกัน คุณจะเข้าสู่ระบบ Vault ผ่าน OIDC เมื่อเชื่อมต่อ ไม่มีข้อมูลลับใดๆ ถูกจัดเก็บไว้",
|
||||||
|
"docs": "ดูเอกสาร"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "โปรไฟล์ใหม่",
|
||||||
|
"manage": "จัดการโปรไฟล์ Vault",
|
||||||
|
"name": "ชื่อที่เป็นมิตร",
|
||||||
|
"vaultAddr": "ที่อยู่ห้องนิรภัย",
|
||||||
|
"namespace": "เนมสเปซ",
|
||||||
|
"oidcMount": "OIDC Auth Mount",
|
||||||
|
"oidcRole": "บทบาทของ OIDC",
|
||||||
|
"sshMount": "SSH Secrets Mount",
|
||||||
|
"sshRole": "บทบาทผู้ลงนาม SSH",
|
||||||
|
"validPrincipals": "หลักการที่ถูกต้อง",
|
||||||
|
"keyType": "ประเภทคีย์ชั่วคราว",
|
||||||
|
"shareWithAll": "แชร์กับผู้ใช้ทั้งหมด",
|
||||||
|
"create": "สร้างโปรไฟล์",
|
||||||
|
"created": "สร้างโปรไฟล์ Vault แล้ว",
|
||||||
|
"saved": "บันทึกโปรไฟล์ Vault แล้ว",
|
||||||
|
"deleted": "โปรไฟล์ Vault ถูกลบแล้ว",
|
||||||
|
"saveFailed": "ไม่สามารถบันทึกโปรไฟล์ Vault ได้",
|
||||||
|
"deleteFailed": "ไม่สามารถลบโปรไฟล์ Vault ได้",
|
||||||
|
"save": "บันทึกโปรไฟล์",
|
||||||
|
"validationError": "จำเป็นต้องระบุชื่อ ที่อยู่ Vault และบทบาทผู้ลงนาม SSH",
|
||||||
|
"none": "ยังไม่มีโปรไฟล์ Vault ใดๆ",
|
||||||
|
"sharedBadge": "แชร์"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "ต้องลงชื่อเข้าใช้ Vault",
|
||||||
|
"description": "หน้าต่างสำหรับเข้าสู่ระบบ HashiCorp Vault ได้เปิดขึ้นแล้ว โปรดทำการเข้าสู่ระบบให้เสร็จสมบูรณ์ การเชื่อมต่อนี้จะดำเนินการต่อโดยอัตโนมัติ",
|
||||||
|
"failed": "การตรวจสอบสิทธิ์ Vault ล้มเหลว โปรดลองอีกครั้ง",
|
||||||
|
"reopen": "เปิดหน้าต่างเข้าสู่ระบบอีกครั้ง"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault İmzalayıcı Profili",
|
||||||
|
"selectProfile": "Bir Vault profili seçin...",
|
||||||
|
"hint": "Ayarlar paylaşılan profilden gelir; bağlandığınızda OIDC ile Vault'ta oturum açarsınız. Hiçbir gizli bilgi saklanmaz.",
|
||||||
|
"docs": "Dokümanları görüntüle"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Yeni profil",
|
||||||
|
"manage": "Vault Profillerini Yönet",
|
||||||
|
"name": "Gösterim Adı",
|
||||||
|
"vaultAddr": "Vault Adresi",
|
||||||
|
"namespace": "Ad Alanı",
|
||||||
|
"oidcMount": "OIDC Kimlik Doğrulama Bağlama",
|
||||||
|
"oidcRole": "OIDC Rolü",
|
||||||
|
"sshMount": "SSH Gizli Dizin Bağlama",
|
||||||
|
"sshRole": "SSH İmzalayıcı Rolü",
|
||||||
|
"validPrincipals": "Geçerli Asıllar",
|
||||||
|
"keyType": "Geçici Anahtar Türü",
|
||||||
|
"shareWithAll": "Tüm Kullanıcılarla Paylaş",
|
||||||
|
"create": "Profil oluştur",
|
||||||
|
"created": "Vault profili oluşturuldu",
|
||||||
|
"saved": "Vault profili kaydedildi",
|
||||||
|
"deleted": "Vault profili silindi",
|
||||||
|
"saveFailed": "Vault profili kaydedilemedi",
|
||||||
|
"deleteFailed": "Vault profili silinemedi",
|
||||||
|
"save": "Profili kaydet",
|
||||||
|
"validationError": "Ad, Vault adresi ve SSH imzalama rolü gereklidir",
|
||||||
|
"none": "Henüz Vault profili yok.",
|
||||||
|
"sharedBadge": "paylaşılan"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Vault Oturum Açması Gerekli",
|
||||||
|
"description": "HashiCorp Vault'ta oturum açmak için bir pencere açıldı. Orada oturum açmayı tamamlayın; bu bağlantı otomatik olarak devam edecek.",
|
||||||
|
"failed": "Vault kimlik doğrulaması başarısız oldu. Lütfen tekrar deneyin.",
|
||||||
|
"reopen": "Oturum açma penceresini yeniden aç"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Профіль підписанта сховища",
|
||||||
|
"selectProfile": "Виберіть профіль Сховища...",
|
||||||
|
"hint": "Налаштування надходять зі спільного профілю; ви входите у Сейф через OIDC під час підключення. Секрети не зберігаються.",
|
||||||
|
"docs": "Переглянути документи"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Новий профіль",
|
||||||
|
"manage": "Керування профілями Сховища",
|
||||||
|
"name": "Зрозуміле ім'я",
|
||||||
|
"vaultAddr": "Адреса сховища",
|
||||||
|
"namespace": "Простір імен",
|
||||||
|
"oidcMount": "Монтування автентифікації OIDC",
|
||||||
|
"oidcRole": "Роль OIDC",
|
||||||
|
"sshMount": "SSH-секрети кріплення",
|
||||||
|
"sshRole": "Роль підписувача SSH",
|
||||||
|
"validPrincipals": "Дійсні принципали",
|
||||||
|
"keyType": "Тип тимчасового ключа",
|
||||||
|
"shareWithAll": "Поділитися з усіма користувачами",
|
||||||
|
"create": "Створити профіль",
|
||||||
|
"created": "Профіль сховища створено",
|
||||||
|
"saved": "Профіль сховища збережено",
|
||||||
|
"deleted": "Профіль сховища видалено",
|
||||||
|
"saveFailed": "Не вдалося зберегти профіль Сейфа",
|
||||||
|
"deleteFailed": "Не вдалося видалити профіль Сейфа",
|
||||||
|
"save": "Зберегти профіль",
|
||||||
|
"validationError": "Ім'я, адреса сховища та роль підписувача SSH обов'язкові",
|
||||||
|
"none": "Поки що немає профілів Сейфа.",
|
||||||
|
"sharedBadge": "спільний"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Потрібен вхід до Сейфа",
|
||||||
|
"description": "Відкрилося вікно для входу в HashiCorp Vault. Завершіть вхід там; це з’єднання буде продовжено автоматично.",
|
||||||
|
"failed": "Не вдалося виконати автентифікацію сховища. Спробуйте ще раз.",
|
||||||
|
"reopen": "Знову відкрити вікно входу"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Hồ sơ người ký Vault",
|
||||||
|
"selectProfile": "Chọn hồ sơ Vault...",
|
||||||
|
"hint": "Cài đặt từ hồ sơ dùng chung; bạn sẽ đăng nhập Vault qua OIDC khi kết nối. Không lưu bí mật.",
|
||||||
|
"docs": "Xem tài liệu"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "Hồ sơ mới",
|
||||||
|
"manage": "Quản lý hồ sơ Vault",
|
||||||
|
"name": "Tên hiển thị",
|
||||||
|
"vaultAddr": "Địa chỉ Vault",
|
||||||
|
"namespace": "Không gian tên",
|
||||||
|
"oidcMount": "Điểm gắn xác thực OIDC",
|
||||||
|
"oidcRole": "Vai trò OIDC",
|
||||||
|
"sshMount": "Điểm gắn bí mật SSH",
|
||||||
|
"sshRole": "Vai trò ký SSH",
|
||||||
|
"validPrincipals": "Các danh tính hợp lệ",
|
||||||
|
"keyType": "Loại khóa tạm thời",
|
||||||
|
"shareWithAll": "Chia sẻ với tất cả người dùng",
|
||||||
|
"create": "Tạo hồ sơ",
|
||||||
|
"created": "Đã tạo hồ sơ Vault",
|
||||||
|
"saved": "Đã lưu hồ sơ Vault",
|
||||||
|
"deleted": "Đã xóa hồ sơ Vault",
|
||||||
|
"saveFailed": "Không thể lưu hồ sơ Vault",
|
||||||
|
"deleteFailed": "Không thể xóa hồ sơ Vault",
|
||||||
|
"save": "Lưu hồ sơ",
|
||||||
|
"validationError": "Tên, địa chỉ Vault và vai trò ký SSH là bắt buộc",
|
||||||
|
"none": "Chưa có hồ sơ Vault nào.",
|
||||||
|
"sharedBadge": "được chia sẻ"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "Yêu cầu đăng nhập Vault",
|
||||||
|
"description": "Một cửa sổ đã mở để đăng nhập vào HashiCorp Vault. Hoàn tất đăng nhập ở đó; kết nối này sẽ tự động tiếp tục.",
|
||||||
|
"failed": "Xác thực Vault thất bại. Vui lòng thử lại.",
|
||||||
|
"reopen": "Mở lại cửa sổ đăng nhập"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault 签名者配置文件",
|
||||||
|
"selectProfile": "选择 Vault 配置文件...",
|
||||||
|
"hint": "设置来自共享配置文件;连接时将使用 OIDC 登录 Vault。不会存储任何机密。",
|
||||||
|
"docs": "查看文档"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "新建配置文件",
|
||||||
|
"manage": "管理 Vault 配置文件",
|
||||||
|
"name": "友好名称",
|
||||||
|
"vaultAddr": "Vault 地址",
|
||||||
|
"namespace": "命名空间",
|
||||||
|
"oidcMount": "OIDC 认证挂载点",
|
||||||
|
"oidcRole": "OIDC 角色",
|
||||||
|
"sshMount": "SSH 机密挂载点",
|
||||||
|
"sshRole": "SSH 签名者角色",
|
||||||
|
"validPrincipals": "有效主体",
|
||||||
|
"keyType": "临时密钥类型",
|
||||||
|
"shareWithAll": "与所有用户共享",
|
||||||
|
"create": "创建配置文件",
|
||||||
|
"created": "Vault 配置文件已创建",
|
||||||
|
"saved": "Vault 配置文件已保存",
|
||||||
|
"deleted": "Vault 配置文件已删除",
|
||||||
|
"saveFailed": "保存 Vault 配置文件失败",
|
||||||
|
"deleteFailed": "删除 Vault 配置文件失败",
|
||||||
|
"save": "保存配置文件",
|
||||||
|
"validationError": "名称、Vault 地址和 SSH 签名者角色为必填项",
|
||||||
|
"none": "暂无 Vault 配置文件。",
|
||||||
|
"sharedBadge": "共享"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "需要 Vault 登录",
|
||||||
|
"description": "已打开窗口以登录 HashiCorp Vault。在该处完成登录;此连接将自动继续。",
|
||||||
|
"failed": "Vault 认证失败,请重试。",
|
||||||
|
"reopen": "重新打开登录窗口"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"editor": {
|
||||||
|
"profile": "Vault 簽署者設定檔",
|
||||||
|
"selectProfile": "選取 Vault 設定檔...",
|
||||||
|
"hint": "設定來自共用設定檔;連線時您將透過 OIDC 登入 Vault。不會儲存任何機密。",
|
||||||
|
"docs": "檢視文件"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"new": "新增設定檔",
|
||||||
|
"manage": "管理 Vault 設定檔",
|
||||||
|
"name": "易記名稱",
|
||||||
|
"vaultAddr": "Vault 位址",
|
||||||
|
"namespace": "命名空間",
|
||||||
|
"oidcMount": "OIDC 驗證掛載",
|
||||||
|
"oidcRole": "OIDC 角色",
|
||||||
|
"sshMount": "SSH 機密掛載",
|
||||||
|
"sshRole": "SSH 簽署者角色",
|
||||||
|
"validPrincipals": "有效主體",
|
||||||
|
"keyType": "暫時金鑰類型",
|
||||||
|
"shareWithAll": "與所有使用者共用",
|
||||||
|
"create": "建立設定檔",
|
||||||
|
"created": "Vault 設定檔已建立",
|
||||||
|
"saved": "Vault 設定檔已儲存",
|
||||||
|
"deleted": "Vault 設定檔已刪除",
|
||||||
|
"saveFailed": "無法儲存 Vault 設定檔",
|
||||||
|
"deleteFailed": "無法刪除 Vault 設定檔",
|
||||||
|
"save": "儲存設定檔",
|
||||||
|
"validationError": "名稱、Vault 位址和 SSH 簽署者角色為必填",
|
||||||
|
"none": "尚無 Vault 設定檔。",
|
||||||
|
"sharedBadge": "已共用"
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"title": "需要 Vault 登入",
|
||||||
|
"description": "已開啟視窗以登入 HashiCorp Vault。在該處完成登入;此連線將自動繼續。",
|
||||||
|
"failed": "Vault 驗證失敗。請再試一次。",
|
||||||
|
"reopen": "重新開啟登入視窗"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"id": "vault",
|
||||||
|
"name": "HashiCorp Vault",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Sign in to hosts with short-lived SSH certificates from a HashiCorp Vault SSH secrets engine, after an OIDC sign-in to Vault in the browser.",
|
||||||
|
"author": {
|
||||||
|
"name": "Termix"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "https://github.com/Termix-SSH/Termix",
|
||||||
|
"category": "Access & Security",
|
||||||
|
"icon": "KeyRound",
|
||||||
|
"engine": {
|
||||||
|
"termix": ">=2.9.0",
|
||||||
|
"api": "1"
|
||||||
|
},
|
||||||
|
"capabilities": [
|
||||||
|
"db:own",
|
||||||
|
"secrets:own",
|
||||||
|
"network:outbound",
|
||||||
|
"network:serve",
|
||||||
|
"auth:provide",
|
||||||
|
"ui:surface"
|
||||||
|
],
|
||||||
|
"contributes": {
|
||||||
|
"auth": {
|
||||||
|
"sshAuthTypes": ["vault"]
|
||||||
|
},
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"name": "use",
|
||||||
|
"titleKey": "permissions.use.title",
|
||||||
|
"descriptionKey": "permissions.use.description",
|
||||||
|
"defaultRoles": ["admin", "user"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "share",
|
||||||
|
"titleKey": "permissions.share.title",
|
||||||
|
"descriptionKey": "permissions.share.description",
|
||||||
|
"defaultRoles": ["admin"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"admin": [
|
||||||
|
{
|
||||||
|
"key": "redirectUri",
|
||||||
|
"type": "custom",
|
||||||
|
"component": "redirectUri"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "legacyCallback",
|
||||||
|
"type": "boolean",
|
||||||
|
"labelKey": "settings.legacyCallback.label",
|
||||||
|
"descriptionKey": "settings.legacyCallback.description",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"host": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"key": "profileId",
|
||||||
|
"type": "number",
|
||||||
|
"labelKey": "settings.host.profileId.label",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"locales": "locales",
|
||||||
|
"platforms": ["linux", "win32", "darwin"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
-- vault 0001: adopt_vault_tables
|
||||||
|
-- Generated by termix-plugin migrations. Review before committing.
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `vault_profiles` (
|
||||||
|
`id` int AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`user_id` varchar(255) NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`description` text,
|
||||||
|
`folder` text,
|
||||||
|
`tags` text,
|
||||||
|
`vault_addr` text NOT NULL,
|
||||||
|
`vault_namespace` text,
|
||||||
|
`oidc_mount` text,
|
||||||
|
`oidc_role` text,
|
||||||
|
`ssh_mount` text,
|
||||||
|
`ssh_role` text NOT NULL,
|
||||||
|
`valid_principals` text,
|
||||||
|
`key_type` text,
|
||||||
|
`shared` boolean NOT NULL DEFAULT false,
|
||||||
|
`sync_id` varchar(255) UNIQUE,
|
||||||
|
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
RENAME TABLE `vault_profiles` TO `p_vault_profiles`;
|
||||||
|
CREATE TABLE IF NOT EXISTS `vault_tokens` (
|
||||||
|
`id` int AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`user_id` varchar(255) NOT NULL,
|
||||||
|
`profile_id` int NOT NULL,
|
||||||
|
`ssh_cert` text NOT NULL,
|
||||||
|
`private_key` text NOT NULL,
|
||||||
|
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
`expires_at` varchar(255) NOT NULL,
|
||||||
|
`last_used` text,
|
||||||
|
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
RENAME TABLE `vault_tokens` TO `p_vault_tokens`;
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
-- vault 0001: adopt_vault_tables
|
||||||
|
-- Generated by termix-plugin migrations. Review before committing.
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "vault_profiles" (
|
||||||
|
"id" serial PRIMARY KEY,
|
||||||
|
"user_id" varchar(255) NOT NULL,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"description" text,
|
||||||
|
"folder" text,
|
||||||
|
"tags" text,
|
||||||
|
"vault_addr" text NOT NULL,
|
||||||
|
"vault_namespace" text,
|
||||||
|
"oidc_mount" text,
|
||||||
|
"oidc_role" text,
|
||||||
|
"ssh_mount" text,
|
||||||
|
"ssh_role" text NOT NULL,
|
||||||
|
"valid_principals" text,
|
||||||
|
"key_type" text,
|
||||||
|
"shared" boolean NOT NULL DEFAULT false,
|
||||||
|
"sync_id" varchar(255) UNIQUE,
|
||||||
|
"created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
ALTER TABLE "vault_profiles" RENAME TO "p_vault_profiles";
|
||||||
|
CREATE TABLE IF NOT EXISTS "vault_tokens" (
|
||||||
|
"id" serial PRIMARY KEY,
|
||||||
|
"user_id" varchar(255) NOT NULL,
|
||||||
|
"profile_id" integer NOT NULL,
|
||||||
|
"ssh_cert" text NOT NULL,
|
||||||
|
"private_key" text NOT NULL,
|
||||||
|
"created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"expires_at" varchar(255) NOT NULL,
|
||||||
|
"last_used" text,
|
||||||
|
FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS "idx_vault_tokens_user_profile" ON "vault_tokens" ("user_id", "profile_id");
|
||||||
|
ALTER TABLE "vault_tokens" RENAME TO "p_vault_tokens";
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"tables": {
|
||||||
|
"profiles": {
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"type": "id",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"type": "refUser",
|
||||||
|
"length": 255,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"folder": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"vaultAddr": {
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"vaultNamespace": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"oidcMount": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"oidcRole": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"sshMount": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"sshRole": {
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"validPrincipals": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"keyType": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"shared": {
|
||||||
|
"type": "boolean",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
"syncId": {
|
||||||
|
"type": "varchar",
|
||||||
|
"length": 255,
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultNow": true
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultNow": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": [],
|
||||||
|
"adopts": "vault_profiles"
|
||||||
|
},
|
||||||
|
"tokens": {
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"type": "id",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"type": "refUser",
|
||||||
|
"length": 255,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"profileId": {
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"sshCert": {
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"privateKey": {
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultNow": true
|
||||||
|
},
|
||||||
|
"expiresAt": {
|
||||||
|
"type": "varchar",
|
||||||
|
"length": 255,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"lastUsed": {
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"name": "idx_vault_tokens_user_profile",
|
||||||
|
"columns": ["userId", "profileId"],
|
||||||
|
"unique": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"adopts": "vault_tokens"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
-- vault 0001: adopt_vault_tables
|
||||||
|
-- Generated by termix-plugin migrations. Review before committing.
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "vault_profiles" (
|
||||||
|
"id" integer PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"user_id" text NOT NULL,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"description" text,
|
||||||
|
"folder" text,
|
||||||
|
"tags" text,
|
||||||
|
"vault_addr" text NOT NULL,
|
||||||
|
"vault_namespace" text,
|
||||||
|
"oidc_mount" text,
|
||||||
|
"oidc_role" text,
|
||||||
|
"ssh_mount" text,
|
||||||
|
"ssh_role" text NOT NULL,
|
||||||
|
"valid_principals" text,
|
||||||
|
"key_type" text,
|
||||||
|
"shared" integer NOT NULL DEFAULT 0,
|
||||||
|
"sync_id" text UNIQUE,
|
||||||
|
"created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
ALTER TABLE "vault_profiles" RENAME TO "p_vault_profiles";
|
||||||
|
CREATE TABLE IF NOT EXISTS "vault_tokens" (
|
||||||
|
"id" integer PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"user_id" text NOT NULL,
|
||||||
|
"profile_id" integer NOT NULL,
|
||||||
|
"ssh_cert" text NOT NULL,
|
||||||
|
"private_key" text NOT NULL,
|
||||||
|
"created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"expires_at" text NOT NULL,
|
||||||
|
"last_used" text,
|
||||||
|
FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS "idx_vault_tokens_user_profile" ON "vault_tokens" ("user_id", "profile_id");
|
||||||
|
ALTER TABLE "vault_tokens" RENAME TO "p_vault_tokens";
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "@termix-plugin/vault",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "termix-plugin build",
|
||||||
|
"test": "termix-plugin test",
|
||||||
|
"typecheck": "tsc -p tsconfig.json",
|
||||||
|
"validate": "termix-plugin validate"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@termix/plugin-sdk": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
import type { PluginContext } from "@termix/plugin-sdk/backend";
|
||||||
|
import type { TokenStore } from "./token-store.js";
|
||||||
|
import {
|
||||||
|
completeVaultOidc,
|
||||||
|
generateEphemeralKeyPair,
|
||||||
|
signWithVault,
|
||||||
|
startVaultOidc,
|
||||||
|
type VaultProfileConfig,
|
||||||
|
} from "./vault-client.js";
|
||||||
|
|
||||||
|
/** The redirect URI 2.8 had Vault roles allow. */
|
||||||
|
export const LEGACY_CALLBACK_PATH = "/vault/oidc/callback";
|
||||||
|
export const CALLBACK_PATH = "/plugin-api/vault/oidc/callback";
|
||||||
|
|
||||||
|
const AUTH_TIMEOUT_MS = 5 * 60 * 1000;
|
||||||
|
|
||||||
|
/** The terminal socket, as far as a sign-in needs it. */
|
||||||
|
export interface SignInSocket {
|
||||||
|
send: (data: string) => void;
|
||||||
|
on?: (event: "close", listener: () => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StartRequest {
|
||||||
|
userId: string;
|
||||||
|
hostId: number;
|
||||||
|
profile: VaultProfileConfig;
|
||||||
|
socket: SignInSocket;
|
||||||
|
requestOrigin: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CallbackResult {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Session {
|
||||||
|
state: string;
|
||||||
|
userId: string;
|
||||||
|
hostId: number;
|
||||||
|
profile: VaultProfileConfig;
|
||||||
|
clientNonce: string;
|
||||||
|
privateKey: string;
|
||||||
|
publicKey: string;
|
||||||
|
socket: SignInSocket;
|
||||||
|
timeout: ReturnType<typeof setTimeout>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function send(socket: SignInSocket, message: Record<string, unknown>): void {
|
||||||
|
try {
|
||||||
|
socket.send(JSON.stringify(message));
|
||||||
|
} catch {
|
||||||
|
// socket already gone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorMessage(error: unknown): string {
|
||||||
|
return error instanceof Error ? error.message : String(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AuthSessions = ReturnType<typeof createAuthSessions>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vault OIDC sign-ins in flight, keyed by the state Vault put in its auth
|
||||||
|
* URL so the unauthenticated browser callback finds its way back. Created
|
||||||
|
* per activation; closeAll runs on deactivate.
|
||||||
|
*/
|
||||||
|
export function createAuthSessions(ctx: PluginContext, tokens: TokenStore) {
|
||||||
|
const sessions = new Map<string, Session>();
|
||||||
|
|
||||||
|
function end(session: Session): void {
|
||||||
|
clearTimeout(session.timeout);
|
||||||
|
sessions.delete(session.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callbackPath(): Promise<string> {
|
||||||
|
const legacy = await ctx.settings.get<boolean>("legacyCallback");
|
||||||
|
return legacy ? LEGACY_CALLBACK_PATH : CALLBACK_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
/** Asks Vault for an auth URL and sends it to the terminal. */
|
||||||
|
async start(request: StartRequest): Promise<void> {
|
||||||
|
try {
|
||||||
|
const keyPair = generateEphemeralKeyPair(request.profile.keyType);
|
||||||
|
const redirectUri = `${request.requestOrigin}${await callbackPath()}`;
|
||||||
|
const { authUrl, state, clientNonce } = await startVaultOidc(
|
||||||
|
ctx.fetch,
|
||||||
|
request.profile,
|
||||||
|
redirectUri,
|
||||||
|
);
|
||||||
|
|
||||||
|
const existing = sessions.get(state);
|
||||||
|
if (existing) end(existing);
|
||||||
|
|
||||||
|
const session: Session = {
|
||||||
|
state,
|
||||||
|
userId: request.userId,
|
||||||
|
hostId: request.hostId,
|
||||||
|
profile: request.profile,
|
||||||
|
clientNonce,
|
||||||
|
privateKey: keyPair.privateKey,
|
||||||
|
publicKey: keyPair.publicKey,
|
||||||
|
socket: request.socket,
|
||||||
|
timeout: setTimeout(() => {
|
||||||
|
const current = sessions.get(state);
|
||||||
|
if (current) end(current);
|
||||||
|
}, AUTH_TIMEOUT_MS),
|
||||||
|
};
|
||||||
|
sessions.set(state, session);
|
||||||
|
request.socket.on?.("close", () => {
|
||||||
|
const current = sessions.get(state);
|
||||||
|
if (current) end(current);
|
||||||
|
});
|
||||||
|
|
||||||
|
send(request.socket, {
|
||||||
|
type: "vault_auth_url",
|
||||||
|
hostId: request.hostId,
|
||||||
|
requestId: state,
|
||||||
|
url: authUrl,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const message = errorMessage(error);
|
||||||
|
ctx.log.error(`Failed to start Vault authentication: ${message}`);
|
||||||
|
send(request.socket, {
|
||||||
|
type: "vault_error",
|
||||||
|
hostId: request.hostId,
|
||||||
|
error: message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identity provider's redirect, via Vault: exchanges the code for a
|
||||||
|
* Vault token, has Vault sign the ephemeral key and caches the result.
|
||||||
|
*/
|
||||||
|
async complete(state: string, code: string): Promise<CallbackResult> {
|
||||||
|
const session = sessions.get(state);
|
||||||
|
if (!session) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
message: "This sign-in request is no longer active.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
end(session);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = await completeVaultOidc(ctx.fetch, session.profile, {
|
||||||
|
state,
|
||||||
|
code,
|
||||||
|
clientNonce: session.clientNonce,
|
||||||
|
});
|
||||||
|
const signed = await signWithVault(
|
||||||
|
ctx.fetch,
|
||||||
|
session.profile,
|
||||||
|
token,
|
||||||
|
session.publicKey,
|
||||||
|
);
|
||||||
|
const expiresAt = await tokens.save(
|
||||||
|
session.userId,
|
||||||
|
session.profile.id,
|
||||||
|
session.privateKey,
|
||||||
|
signed,
|
||||||
|
);
|
||||||
|
ctx.log.info(
|
||||||
|
`Vault signed a certificate for host ${session.hostId} with profile ${session.profile.id}`,
|
||||||
|
);
|
||||||
|
send(session.socket, {
|
||||||
|
type: "vault_completed",
|
||||||
|
hostId: session.hostId,
|
||||||
|
requestId: state,
|
||||||
|
expiresAt,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
message: "You can close this window and return to Termix.",
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
const message = errorMessage(error);
|
||||||
|
ctx.log.error(`Vault sign-in failed: ${message}`);
|
||||||
|
send(session.socket, {
|
||||||
|
type: "vault_error",
|
||||||
|
hostId: session.hostId,
|
||||||
|
requestId: state,
|
||||||
|
error: message,
|
||||||
|
});
|
||||||
|
return { ok: false, message };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Cancels by request id, or every sign-in the user has for a host. */
|
||||||
|
cancel(userId: string, hostId?: number, requestId?: string): void {
|
||||||
|
for (const session of [...sessions.values()]) {
|
||||||
|
if (session.userId !== userId) continue;
|
||||||
|
if (
|
||||||
|
requestId ? session.state === requestId : session.hostId === hostId
|
||||||
|
) {
|
||||||
|
end(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
closeAll(): void {
|
||||||
|
for (const session of [...sessions.values()]) end(session);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import type { Router } from "express";
|
||||||
|
import type { PluginContext } from "@termix/plugin-sdk/backend";
|
||||||
|
import { profiles as profilesTable, tokens as tokensTable } from "./tables.js";
|
||||||
|
import { createProfileStore, SYNC_ENTITY } from "./profile-store.js";
|
||||||
|
import { createTokenStore } from "./token-store.js";
|
||||||
|
import { createAuthSessions } from "./auth-session.js";
|
||||||
|
import { createVaultProvider } from "./provider.js";
|
||||||
|
import { PUBLIC_PATHS, registerRoutes } from "./routes.js";
|
||||||
|
|
||||||
|
export async function activate(ctx: PluginContext) {
|
||||||
|
const profilesDb = await ctx.db.define(profilesTable);
|
||||||
|
const tokensDb = await ctx.db.define(tokensTable);
|
||||||
|
const profiles = createProfileStore(ctx, profilesDb, tokensDb);
|
||||||
|
const tokens = createTokenStore(ctx, tokensDb);
|
||||||
|
|
||||||
|
const sessions = createAuthSessions(ctx, tokens);
|
||||||
|
ctx.disposables.add(() => sessions.closeAll());
|
||||||
|
|
||||||
|
ctx.auth.registerSshAuthProvider(
|
||||||
|
createVaultProvider(ctx, profiles, tokens, sessions),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Same wire name as 2.8, where core synced it. Hosts point at a profile
|
||||||
|
// through the profileId host setting, not a column, so nothing references it.
|
||||||
|
ctx.sync.registerEntity({
|
||||||
|
type: SYNC_ENTITY,
|
||||||
|
table: profilesDb,
|
||||||
|
order: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
registerRoutes(
|
||||||
|
ctx,
|
||||||
|
ctx.http.router<Router>({ public: PUBLIC_PATHS }),
|
||||||
|
profiles,
|
||||||
|
sessions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deactivate() {
|
||||||
|
// Everything above was registered through ctx and is disposed by core.
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import { randomUUID } from "node:crypto";
|
||||||
|
import { desc, eq, or } from "drizzle-orm";
|
||||||
|
import type { PluginContext } from "@termix/plugin-sdk/backend";
|
||||||
|
import type { VaultProfileConfig } from "./vault-client.js";
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
// ctx.db.define hands the table back untyped and the drizzle handle is the
|
||||||
|
// server's own, so both are typed at this module's edge.
|
||||||
|
type Table = any;
|
||||||
|
type Drizzle = any;
|
||||||
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
export const SYNC_ENTITY = "vaultProfiles";
|
||||||
|
|
||||||
|
export interface ProfileRow {
|
||||||
|
id: number;
|
||||||
|
userId: string;
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
folder: string | null;
|
||||||
|
tags: string | null;
|
||||||
|
vaultAddr: string;
|
||||||
|
vaultNamespace: string | null;
|
||||||
|
oidcMount: string | null;
|
||||||
|
oidcRole: string | null;
|
||||||
|
sshMount: string | null;
|
||||||
|
sshRole: string;
|
||||||
|
validPrincipals: string | null;
|
||||||
|
keyType: string | null;
|
||||||
|
shared: boolean | number;
|
||||||
|
syncId: string | null;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProfileInput = Partial<
|
||||||
|
Omit<ProfileRow, "id" | "userId" | "syncId" | "createdAt" | "updatedAt">
|
||||||
|
>;
|
||||||
|
|
||||||
|
export function toConfig(row: ProfileRow): VaultProfileConfig {
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
vaultAddr: row.vaultAddr,
|
||||||
|
vaultNamespace: row.vaultNamespace ?? null,
|
||||||
|
oidcMount: row.oidcMount ?? null,
|
||||||
|
oidcRole: row.oidcRole ?? null,
|
||||||
|
sshMount: row.sshMount ?? null,
|
||||||
|
sshRole: row.sshRole,
|
||||||
|
validPrincipals: row.validPrincipals ?? null,
|
||||||
|
keyType: row.keyType ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProfileStore = ReturnType<typeof createProfileStore>;
|
||||||
|
|
||||||
|
export function createProfileStore(
|
||||||
|
ctx: PluginContext,
|
||||||
|
table: Table,
|
||||||
|
tokensTable: Table,
|
||||||
|
) {
|
||||||
|
const client = () => ctx.db.client<Drizzle>();
|
||||||
|
|
||||||
|
async function findById(id: number): Promise<ProfileRow | null> {
|
||||||
|
const rows = await (
|
||||||
|
await client()
|
||||||
|
)
|
||||||
|
.select()
|
||||||
|
.from(table)
|
||||||
|
.where(eq(table.id, id))
|
||||||
|
.limit(1);
|
||||||
|
return rows[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findBySyncId(syncId: string): Promise<ProfileRow | null> {
|
||||||
|
const rows = await (
|
||||||
|
await client()
|
||||||
|
)
|
||||||
|
.select()
|
||||||
|
.from(table)
|
||||||
|
.where(eq(table.syncId, syncId))
|
||||||
|
.limit(1);
|
||||||
|
return rows[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
findById,
|
||||||
|
|
||||||
|
/** The user's own profiles plus every shared one. */
|
||||||
|
async listVisible(userId: string): Promise<ProfileRow[]> {
|
||||||
|
return (await client())
|
||||||
|
.select()
|
||||||
|
.from(table)
|
||||||
|
.where(or(eq(table.userId, userId), eq(table.shared, true)))
|
||||||
|
.orderBy(desc(table.updatedAt));
|
||||||
|
},
|
||||||
|
|
||||||
|
async create(
|
||||||
|
userId: string,
|
||||||
|
input: ProfileInput & {
|
||||||
|
name: string;
|
||||||
|
vaultAddr: string;
|
||||||
|
sshRole: string;
|
||||||
|
},
|
||||||
|
): Promise<ProfileRow> {
|
||||||
|
const syncId = randomUUID();
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
await (await client()).insert(table).values({
|
||||||
|
...input,
|
||||||
|
userId,
|
||||||
|
shared: input.shared ? true : false,
|
||||||
|
syncId,
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
});
|
||||||
|
await ctx.db.persist();
|
||||||
|
return (await findBySyncId(syncId)) as ProfileRow;
|
||||||
|
},
|
||||||
|
|
||||||
|
async update(id: number, input: ProfileInput): Promise<ProfileRow | null> {
|
||||||
|
await (
|
||||||
|
await client()
|
||||||
|
)
|
||||||
|
.update(table)
|
||||||
|
.set({ ...input, updatedAt: new Date().toISOString() })
|
||||||
|
.where(eq(table.id, id));
|
||||||
|
await ctx.db.persist();
|
||||||
|
return findById(id);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Deletes the profile, its cached certificates and records a tombstone. */
|
||||||
|
async remove(row: ProfileRow): Promise<void> {
|
||||||
|
const db = await client();
|
||||||
|
await db.delete(tokensTable).where(eq(tokensTable.profileId, row.id));
|
||||||
|
await db.delete(table).where(eq(table.id, row.id));
|
||||||
|
await ctx.db.persist();
|
||||||
|
await ctx.sync.recordTombstone(row.userId, SYNC_ENTITY, row.syncId);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import type {
|
||||||
|
PluginContext,
|
||||||
|
PluginSshAuthProvider,
|
||||||
|
PluginSshHost,
|
||||||
|
} from "@termix/plugin-sdk/backend";
|
||||||
|
import { applyCertificateAuth } from "@termix/plugin-sdk/ssh-certs";
|
||||||
|
import type { AuthSessions, SignInSocket } from "./auth-session.js";
|
||||||
|
import {
|
||||||
|
toConfig,
|
||||||
|
type ProfileRow,
|
||||||
|
type ProfileStore,
|
||||||
|
} from "./profile-store.js";
|
||||||
|
import type { TokenStore } from "./token-store.js";
|
||||||
|
|
||||||
|
export const AUTH_TYPE = "vault";
|
||||||
|
|
||||||
|
const REQUIRED_MESSAGE =
|
||||||
|
"Vault SSH signer authentication required. Please open a Terminal connection first.";
|
||||||
|
|
||||||
|
const AUTH_FAILED_PATTERN = /All configured authentication methods failed/i;
|
||||||
|
|
||||||
|
/** The profile a host points at, from its host settings. */
|
||||||
|
export async function profileForHost(
|
||||||
|
ctx: PluginContext,
|
||||||
|
profiles: ProfileStore,
|
||||||
|
hostId: number,
|
||||||
|
): Promise<ProfileRow | null> {
|
||||||
|
const raw = await ctx.settings.getHost<number | string>(hostId, "profileId");
|
||||||
|
const id = Number(raw);
|
||||||
|
if (!Number.isInteger(id) || id <= 0) return null;
|
||||||
|
return profiles.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hosts with auth type "vault" connect with a certificate Vault signed for
|
||||||
|
* the host's profile. Without one, the terminal is asked for a browser
|
||||||
|
* sign-in ("vault_auth_required"), which startInteraction runs.
|
||||||
|
*/
|
||||||
|
export function createVaultProvider(
|
||||||
|
ctx: PluginContext,
|
||||||
|
profiles: ProfileStore,
|
||||||
|
tokens: TokenStore,
|
||||||
|
sessions: AuthSessions,
|
||||||
|
): PluginSshAuthProvider {
|
||||||
|
return {
|
||||||
|
type: AUTH_TYPE,
|
||||||
|
labelKey: "hosts.filterAuthVault",
|
||||||
|
needsUserInteraction: true,
|
||||||
|
supportsBackground: false,
|
||||||
|
interaction: AUTH_TYPE,
|
||||||
|
prepare: async (config, host: PluginSshHost, env) => {
|
||||||
|
const profile = await profileForHost(ctx, profiles, host.id);
|
||||||
|
if (!profile) {
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
code: "failed",
|
||||||
|
message: "Host has no Vault signer profile configured",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const cert = await tokens.get(env.userId, profile.id);
|
||||||
|
if (!cert) {
|
||||||
|
env.log(
|
||||||
|
"info",
|
||||||
|
"No valid Vault certificate found, requesting authentication",
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
status: "interaction-required",
|
||||||
|
interaction: AUTH_TYPE,
|
||||||
|
message: REQUIRED_MESSAGE,
|
||||||
|
flag: "requiresVaultAuth",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await applyCertificateAuth(
|
||||||
|
config as never,
|
||||||
|
env.client as never,
|
||||||
|
{ privateKey: cert.privateKey, certificate: cert.sshCert },
|
||||||
|
host.username,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
code: "failed",
|
||||||
|
message:
|
||||||
|
"Vault SSH signer authentication failed: " +
|
||||||
|
(error instanceof Error ? error.message : String(error)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
env.log("info", "Using cached Vault-signed certificate");
|
||||||
|
return { status: "ready" };
|
||||||
|
},
|
||||||
|
onAuthFailed: (host, env, context) => {
|
||||||
|
if (!AUTH_FAILED_PATTERN.test(context.error.message)) return undefined;
|
||||||
|
ctx.log.warn("Vault certificate authentication failed, forgetting it");
|
||||||
|
void profileForHost(ctx, profiles, host.id)
|
||||||
|
.then((profile) =>
|
||||||
|
profile ? tokens.remove(env.userId, profile.id) : undefined,
|
||||||
|
)
|
||||||
|
.catch(() => undefined);
|
||||||
|
return {
|
||||||
|
status: "interaction-required",
|
||||||
|
interaction: AUTH_TYPE,
|
||||||
|
message:
|
||||||
|
"Vault authentication failed or expired. Please authenticate again.",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
startInteraction: async (request) => {
|
||||||
|
const profile = await profileForHost(ctx, profiles, request.hostId);
|
||||||
|
if (!profile) {
|
||||||
|
throw new Error("No Vault signer profile configured for this host");
|
||||||
|
}
|
||||||
|
await sessions.start({
|
||||||
|
userId: request.userId,
|
||||||
|
hostId: request.hostId,
|
||||||
|
profile: toConfig(profile),
|
||||||
|
socket: request.socket as SignInSocket,
|
||||||
|
requestOrigin: request.requestOrigin,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
cancelInteraction: (request) => {
|
||||||
|
sessions.cancel(request.userId, request.hostId, request.requestId);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,337 @@
|
|||||||
|
import type { Request, Response, Router } from "express";
|
||||||
|
import type { PluginContext } from "@termix/plugin-sdk/backend";
|
||||||
|
import type { AuthSessions } from "./auth-session.js";
|
||||||
|
import type {
|
||||||
|
ProfileInput,
|
||||||
|
ProfileRow,
|
||||||
|
ProfileStore,
|
||||||
|
} from "./profile-store.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vault sends the browser back here without a Termix session: the sign-in is
|
||||||
|
* matched by the state Vault issued.
|
||||||
|
*/
|
||||||
|
export const PUBLIC_PATHS = ["/oidc/callback"];
|
||||||
|
|
||||||
|
export function escapeHtml(value: string): string {
|
||||||
|
return value
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resultPage(ok: boolean, message: string): string {
|
||||||
|
return `<!doctype html><html><head><meta charset="utf-8"><title>Termix</title>
|
||||||
|
<style>body{font-family:system-ui,sans-serif;background:#111;color:#eee;display:flex;align-items:center;justify-content:center;height:100vh;margin:0}
|
||||||
|
main{max-width:28rem;padding:2rem;border:1px solid #333;background:#181818}h1{font-size:1.1rem;margin:0 0 .5rem}p{margin:0;color:#aaa}</style></head>
|
||||||
|
<body><main><h1>${ok ? "Vault sign-in complete" : "Vault sign-in failed"}</h1><p>${escapeHtml(message)}</p></main>
|
||||||
|
${ok ? "<script>setTimeout(function(){window.close()},1500)</script>" : ""}</body></html>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNonEmptyString(value: unknown): value is string {
|
||||||
|
return typeof value === "string" && value.trim().length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function optional(value: unknown): string | null {
|
||||||
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatProfile(row: ProfileRow, userId: string) {
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
name: row.name,
|
||||||
|
description: row.description,
|
||||||
|
folder: row.folder,
|
||||||
|
tags: row.tags ? row.tags.split(",").filter(Boolean) : [],
|
||||||
|
vaultAddr: row.vaultAddr,
|
||||||
|
vaultNamespace: row.vaultNamespace,
|
||||||
|
oidcMount: row.oidcMount,
|
||||||
|
oidcRole: row.oidcRole,
|
||||||
|
sshMount: row.sshMount,
|
||||||
|
sshRole: row.sshRole,
|
||||||
|
validPrincipals: row.validPrincipals,
|
||||||
|
keyType: row.keyType,
|
||||||
|
shared: !!row.shared,
|
||||||
|
owned: row.userId === userId,
|
||||||
|
createdAt: row.createdAt,
|
||||||
|
updatedAt: row.updatedAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseId(req: Request): number | null {
|
||||||
|
const id = Number.parseInt(String(req.params.id), 10);
|
||||||
|
return Number.isFinite(id) ? id : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerRoutes(
|
||||||
|
ctx: PluginContext,
|
||||||
|
router: Router,
|
||||||
|
profiles: ProfileStore,
|
||||||
|
sessions: AuthSessions,
|
||||||
|
): void {
|
||||||
|
const userId = () => ctx.currentActor() as string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /plugin-api/vault/oidc/callback:
|
||||||
|
* get:
|
||||||
|
* summary: Vault OIDC callback
|
||||||
|
* description: Public. The redirect URI a Vault OIDC role allows. Matches the sign-in by the state Vault issued, has Vault sign the ephemeral key and tells the terminal to reconnect. The 2.8 URI /vault/oidc/callback redirects here.
|
||||||
|
* tags: [Vault]
|
||||||
|
* parameters:
|
||||||
|
* - in: query
|
||||||
|
* name: state
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* - in: query
|
||||||
|
* name: code
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* - in: query
|
||||||
|
* name: error
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Signed in; an HTML page that closes itself.
|
||||||
|
* 400:
|
||||||
|
* description: The sign-in failed or is no longer active.
|
||||||
|
*/
|
||||||
|
router.get("/oidc/callback", async (req: Request, res: Response) => {
|
||||||
|
const state = typeof req.query.state === "string" ? req.query.state : "";
|
||||||
|
const code = typeof req.query.code === "string" ? req.query.code : "";
|
||||||
|
const error = typeof req.query.error === "string" ? req.query.error : "";
|
||||||
|
|
||||||
|
const reply = (ok: boolean, message: string) =>
|
||||||
|
res
|
||||||
|
.status(ok ? 200 : 400)
|
||||||
|
.type("html")
|
||||||
|
.send(resultPage(ok, message));
|
||||||
|
|
||||||
|
if (error) return reply(false, `Vault returned: ${error}`);
|
||||||
|
if (!state || !code) return reply(false, "Missing state or code.");
|
||||||
|
const result = await sessions.complete(state, code);
|
||||||
|
return reply(result.ok, result.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.use("/profiles", ctx.rbac.require("use") as never);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /plugin-api/vault/profiles:
|
||||||
|
* get:
|
||||||
|
* summary: List Vault profiles
|
||||||
|
* description: The caller's own Vault signer profiles and every shared one. Needs vault.use.
|
||||||
|
* tags: [Vault]
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Array of Vault profile objects.
|
||||||
|
* 403:
|
||||||
|
* description: Missing vault.use.
|
||||||
|
*/
|
||||||
|
router.get("/profiles", async (_req: Request, res: Response) => {
|
||||||
|
const rows = await profiles.listVisible(userId());
|
||||||
|
res.json(rows.map((row) => formatProfile(row, userId())));
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /plugin-api/vault/profiles:
|
||||||
|
* post:
|
||||||
|
* summary: Create a Vault profile
|
||||||
|
* description: Creates a Vault signer profile owned by the caller. Needs vault.use, and vault.share to mark it shared.
|
||||||
|
* tags: [Vault]
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* required: [name, vaultAddr, sshRole]
|
||||||
|
* properties:
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* vaultAddr:
|
||||||
|
* type: string
|
||||||
|
* vaultNamespace:
|
||||||
|
* type: string
|
||||||
|
* oidcMount:
|
||||||
|
* type: string
|
||||||
|
* oidcRole:
|
||||||
|
* type: string
|
||||||
|
* sshMount:
|
||||||
|
* type: string
|
||||||
|
* sshRole:
|
||||||
|
* type: string
|
||||||
|
* validPrincipals:
|
||||||
|
* type: string
|
||||||
|
* keyType:
|
||||||
|
* type: string
|
||||||
|
* shared:
|
||||||
|
* type: boolean
|
||||||
|
* responses:
|
||||||
|
* 201:
|
||||||
|
* description: The created profile.
|
||||||
|
* 400:
|
||||||
|
* description: Missing required fields.
|
||||||
|
* 403:
|
||||||
|
* description: Missing vault.use, or vault.share for a shared profile.
|
||||||
|
*/
|
||||||
|
router.post("/profiles", async (req: Request, res: Response) => {
|
||||||
|
const body = (req.body ?? {}) as Record<string, unknown>;
|
||||||
|
if (
|
||||||
|
!isNonEmptyString(body.name) ||
|
||||||
|
!isNonEmptyString(body.vaultAddr) ||
|
||||||
|
!isNonEmptyString(body.sshRole)
|
||||||
|
) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: "name, vaultAddr and sshRole are required" });
|
||||||
|
}
|
||||||
|
const shared = !!body.shared;
|
||||||
|
if (shared && !(await ctx.rbac.has("share"))) {
|
||||||
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "You are not allowed to share Vault profiles" });
|
||||||
|
}
|
||||||
|
const row = await profiles.create(userId(), {
|
||||||
|
name: body.name.trim(),
|
||||||
|
description: optional(body.description),
|
||||||
|
folder: optional(body.folder),
|
||||||
|
tags: Array.isArray(body.tags)
|
||||||
|
? body.tags.join(",")
|
||||||
|
: typeof body.tags === "string"
|
||||||
|
? body.tags
|
||||||
|
: "",
|
||||||
|
vaultAddr: body.vaultAddr.trim(),
|
||||||
|
vaultNamespace: optional(body.vaultNamespace),
|
||||||
|
oidcMount: optional(body.oidcMount),
|
||||||
|
oidcRole: optional(body.oidcRole),
|
||||||
|
sshMount: optional(body.sshMount),
|
||||||
|
sshRole: body.sshRole.trim(),
|
||||||
|
validPrincipals: optional(body.validPrincipals),
|
||||||
|
keyType: optional(body.keyType),
|
||||||
|
shared,
|
||||||
|
});
|
||||||
|
res.status(201).json(formatProfile(row, userId()));
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /plugin-api/vault/profiles/{id}:
|
||||||
|
* put:
|
||||||
|
* summary: Update a Vault profile
|
||||||
|
* description: Only the owner may edit a profile. Needs vault.use, and vault.share to mark it shared.
|
||||||
|
* tags: [Vault]
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* requestBody:
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The updated profile.
|
||||||
|
* 400:
|
||||||
|
* description: Invalid profile id.
|
||||||
|
* 403:
|
||||||
|
* description: Not the owner, or sharing without vault.share.
|
||||||
|
* 404:
|
||||||
|
* description: Profile not found.
|
||||||
|
*/
|
||||||
|
router.put("/profiles/:id", async (req: Request, res: Response) => {
|
||||||
|
const id = parseId(req);
|
||||||
|
if (id === null)
|
||||||
|
return res.status(400).json({ error: "Invalid profile id" });
|
||||||
|
const existing = await profiles.findById(id);
|
||||||
|
if (!existing) return res.status(404).json({ error: "Profile not found" });
|
||||||
|
if (existing.userId !== userId()) {
|
||||||
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "Only the owner can edit this profile" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = (req.body ?? {}) as Record<string, unknown>;
|
||||||
|
const fields: ProfileInput = {};
|
||||||
|
if (isNonEmptyString(body.name)) fields.name = body.name.trim();
|
||||||
|
if (body.description !== undefined)
|
||||||
|
fields.description = optional(body.description);
|
||||||
|
if (body.folder !== undefined) fields.folder = optional(body.folder);
|
||||||
|
if (body.tags !== undefined) {
|
||||||
|
fields.tags = Array.isArray(body.tags)
|
||||||
|
? body.tags.join(",")
|
||||||
|
: typeof body.tags === "string"
|
||||||
|
? body.tags
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
if (isNonEmptyString(body.vaultAddr))
|
||||||
|
fields.vaultAddr = body.vaultAddr.trim();
|
||||||
|
if (body.vaultNamespace !== undefined)
|
||||||
|
fields.vaultNamespace = optional(body.vaultNamespace);
|
||||||
|
if (body.oidcMount !== undefined)
|
||||||
|
fields.oidcMount = optional(body.oidcMount);
|
||||||
|
if (body.oidcRole !== undefined) fields.oidcRole = optional(body.oidcRole);
|
||||||
|
if (body.sshMount !== undefined) fields.sshMount = optional(body.sshMount);
|
||||||
|
if (isNonEmptyString(body.sshRole)) fields.sshRole = body.sshRole.trim();
|
||||||
|
if (body.validPrincipals !== undefined)
|
||||||
|
fields.validPrincipals = optional(body.validPrincipals);
|
||||||
|
if (body.keyType !== undefined) fields.keyType = optional(body.keyType);
|
||||||
|
if (body.shared !== undefined) {
|
||||||
|
if (body.shared && !(await ctx.rbac.has("share"))) {
|
||||||
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "You are not allowed to share Vault profiles" });
|
||||||
|
}
|
||||||
|
fields.shared = !!body.shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await profiles.update(id, fields);
|
||||||
|
if (!updated) return res.status(404).json({ error: "Profile not found" });
|
||||||
|
res.json(formatProfile(updated, userId()));
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /plugin-api/vault/profiles/{id}:
|
||||||
|
* delete:
|
||||||
|
* summary: Delete a Vault profile
|
||||||
|
* description: Deletes a profile and every certificate cached for it. Only the owner may delete it. Needs vault.use.
|
||||||
|
* tags: [Vault]
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Deleted.
|
||||||
|
* 400:
|
||||||
|
* description: Invalid profile id.
|
||||||
|
* 403:
|
||||||
|
* description: Not the owner.
|
||||||
|
* 404:
|
||||||
|
* description: Profile not found.
|
||||||
|
*/
|
||||||
|
router.delete("/profiles/:id", async (req: Request, res: Response) => {
|
||||||
|
const id = parseId(req);
|
||||||
|
if (id === null)
|
||||||
|
return res.status(400).json({ error: "Invalid profile id" });
|
||||||
|
const existing = await profiles.findById(id);
|
||||||
|
if (!existing) return res.status(404).json({ error: "Profile not found" });
|
||||||
|
if (existing.userId !== userId()) {
|
||||||
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({ error: "Only the owner can delete this profile" });
|
||||||
|
}
|
||||||
|
await profiles.remove(existing);
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
Loaded 100 of 198 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user