@c8y/ngx-components 1024.2.4 → 1024.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/fesm2022/c8y-ngx-components-bookmarks.mjs +33 -23
  2. package/fesm2022/c8y-ngx-components-bookmarks.mjs.map +1 -1
  3. package/fesm2022/c8y-ngx-components-context-dashboard.mjs +2 -2
  4. package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
  5. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs +3 -2
  6. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs.map +1 -1
  7. package/fesm2022/c8y-ngx-components-device-profile.mjs +27 -9
  8. package/fesm2022/c8y-ngx-components-device-profile.mjs.map +1 -1
  9. package/fesm2022/c8y-ngx-components-protocol-opcua.mjs +9 -11
  10. package/fesm2022/c8y-ngx-components-protocol-opcua.mjs.map +1 -1
  11. package/fesm2022/c8y-ngx-components-remote-access-data.mjs +43 -0
  12. package/fesm2022/c8y-ngx-components-remote-access-data.mjs.map +1 -1
  13. package/fesm2022/c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.mjs +17 -3
  14. package/fesm2022/c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.mjs.map +1 -1
  15. package/fesm2022/c8y-ngx-components-remote-access-terminal-viewer.mjs +179 -41
  16. package/fesm2022/c8y-ngx-components-remote-access-terminal-viewer.mjs.map +1 -1
  17. package/fesm2022/c8y-ngx-components-repository-firmware.mjs +5 -4
  18. package/fesm2022/c8y-ngx-components-repository-firmware.mjs.map +1 -1
  19. package/fesm2022/c8y-ngx-components-repository-software.mjs +10 -4
  20. package/fesm2022/c8y-ngx-components-repository-software.mjs.map +1 -1
  21. package/fesm2022/c8y-ngx-components-trusted-certificates.mjs +5 -4
  22. package/fesm2022/c8y-ngx-components-trusted-certificates.mjs.map +1 -1
  23. package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs +6 -5
  24. package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs.map +1 -1
  25. package/fesm2022/c8y-ngx-components.mjs +9 -4
  26. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  27. package/locales/de.po +61 -5
  28. package/locales/es.po +61 -5
  29. package/locales/fr.po +61 -5
  30. package/locales/ja_JP.po +60 -6
  31. package/locales/ko.po +61 -5
  32. package/locales/locales.pot +60 -4
  33. package/locales/nl.po +61 -5
  34. package/locales/pl.po +61 -5
  35. package/locales/pt_BR.po +61 -5
  36. package/locales/zh_CN.po +61 -5
  37. package/locales/zh_TW.po +61 -5
  38. package/package.json +1 -1
  39. package/types/c8y-ngx-components-bookmarks.d.ts +7 -7
  40. package/types/c8y-ngx-components-bookmarks.d.ts.map +1 -1
  41. package/types/c8y-ngx-components-device-profile.d.ts +5 -2
  42. package/types/c8y-ngx-components-device-profile.d.ts.map +1 -1
  43. package/types/c8y-ngx-components-protocol-opcua.d.ts.map +1 -1
  44. package/types/c8y-ngx-components-remote-access-data.d.ts +19 -0
  45. package/types/c8y-ngx-components-remote-access-data.d.ts.map +1 -1
  46. package/types/c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.d.ts +10 -3
  47. package/types/c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.d.ts.map +1 -1
  48. package/types/c8y-ngx-components-remote-access-terminal-viewer.d.ts +20 -2
  49. package/types/c8y-ngx-components-remote-access-terminal-viewer.d.ts.map +1 -1
  50. package/types/c8y-ngx-components-repository-firmware.d.ts.map +1 -1
  51. package/types/c8y-ngx-components-repository-software.d.ts +2 -0
  52. package/types/c8y-ngx-components-repository-software.d.ts.map +1 -1
  53. package/types/c8y-ngx-components-trusted-certificates.d.ts.map +1 -1
  54. package/types/c8y-ngx-components-widgets-implementations-asset-table.d.ts.map +1 -1
  55. package/types/c8y-ngx-components.d.ts +5 -1
  56. package/types/c8y-ngx-components.d.ts.map +1 -1
@@ -168,6 +168,49 @@ class RemoteAccessService {
168
168
  }
169
169
  throw new Error(`Failed to update configuration for device ${configuration.attrs.deviceId}`);
170
170
  }
171
+ /**
172
+ * Persists a confirmed server host key onto a configuration (DM-6421 first-use flow). The stored
173
+ * value is an OpenSSH authorized_keys-style string (`<keyType> <base64Blob>`).
174
+ *
175
+ * Uses the dedicated host-key endpoint so only the host key is sent — the untouched credential
176
+ * secrets (password, private key, certificate) are never round-tripped through the client.
177
+ */
178
+ async updateHostKey(deviceId, configurationId, hostKey) {
179
+ const response = await this.fetchClient.fetch(`${this.baseUrl}/devices/${deviceId}/configurations/${configurationId}/hostkey`, {
180
+ method: 'PUT',
181
+ headers: {
182
+ 'Content-Type': 'application/json'
183
+ },
184
+ body: JSON.stringify({ hostKey })
185
+ });
186
+ if (response.ok) {
187
+ return response.json();
188
+ }
189
+ throw new Error(`Failed to update host key for configuration ${configurationId}`);
190
+ }
191
+ /**
192
+ * Whether the backend auto-trusts the server host key on first connect for the current tenant. When
193
+ * enabled, the backend silently stores the key instead of prompting for confirmation.
194
+ *
195
+ * Delegates to the remote-access service rather than reading the `hostkey-autosave` tenant option
196
+ * directly: the effective value is resolved server-side from the tenant option, owner inheritance
197
+ * (a subtenant with no option set inherits the owner's) and the manifest default — none of which the
198
+ * tenant-options API exposes to the client. Defaults to `false` (secure by default) when the status
199
+ * cannot be read.
200
+ */
201
+ async isHostKeyAutosaveEnabled() {
202
+ try {
203
+ const response = await this.fetchClient.fetch(`${this.baseUrl}/settings/hostkey-autosave`);
204
+ if (!response.ok) {
205
+ return false;
206
+ }
207
+ const status = await response.json();
208
+ return status?.enabled === true;
209
+ }
210
+ catch {
211
+ return false;
212
+ }
213
+ }
171
214
  /**
172
215
  * Generates a SSH key pair for a given hostname.
173
216
  */
@@ -1 +1 @@
1
- {"version":3,"file":"c8y-ngx-components-remote-access-data.mjs","sources":["../../remote-access/data/remote-access.service.ts","../../remote-access/data/c8y-ngx-components-remote-access-data.ts"],"sourcesContent":["import { DOCUMENT, inject, Injectable } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivateFn } from '@angular/router';\nimport { FetchClient, IManagedObject } from '@c8y/client';\nimport {\n ContextRouteService,\n Permissions,\n ServiceRegistry,\n ViewContext\n} from '@c8y/ngx-components';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { Observable, defer, shareReplay } from 'rxjs';\nimport { RemoteAccessProtocolProvider } from './remote-access-protocol-provider';\nimport { uniqWith, intersectionWith, isEqual } from 'lodash';\n\nexport interface RemoteAccessConfiguration {\n id: string;\n name: string;\n hostname: string;\n port: number;\n protocol: string;\n attrs?: any;\n credentials?: any;\n credentialsType?: string;\n}\n\nexport const CREDENTIALS_TYPES = {\n NONE: {\n name: 'NONE',\n value: 'NONE',\n label: gettext('No password')\n },\n USER_PASS: {\n name: 'USER_PASS',\n value: 'USER_PASS',\n label: gettext('Username and password')\n },\n PASS_ONLY: {\n name: 'PASS_ONLY',\n value: 'PASS_ONLY',\n label: gettext('Password only')\n },\n KEY_PAIR: {\n name: 'KEY_PAIR',\n value: 'KEY_PAIR',\n label: gettext('Public/private keys')\n },\n CERTIFICATE: {\n name: 'CERTIFICATE',\n value: 'CERTIFICATE',\n label: gettext('Certificate')\n }\n} as const;\n\nexport const canActivateRemoteAccess: CanActivateFn = (route: ActivatedRouteSnapshot) => {\n const permissions = inject(Permissions);\n const remoteAccessService = inject(RemoteAccessService);\n const contextRouteService = inject(ContextRouteService);\n if (!permissions.hasRole(Permissions.ROLE_REMOTE_ACCESS_ADMIN)) {\n return false;\n }\n\n const contextDetails = contextRouteService.getContextData(route);\n if (contextDetails.context !== ViewContext.Device) {\n return false;\n }\n\n const device = contextDetails.contextData as IManagedObject;\n if (!device || !Array.isArray(device.c8y_SupportedOperations)) {\n return false;\n }\n const supportedOperations: string[] = device.c8y_SupportedOperations;\n if (!supportedOperations.includes('c8y_RemoteAccessConnect')) {\n return false;\n }\n return remoteAccessService.isAvailable$();\n};\n\n@Injectable({\n providedIn: 'root'\n})\nexport class RemoteAccessService {\n private cachedIsAvailable$: Observable<boolean>;\n readonly baseUrl = '/service/remoteaccess';\n private document = inject(DOCUMENT);\n constructor(\n private fetchClient: FetchClient,\n private serviceRegistry: ServiceRegistry\n ) {}\n\n /**\n * Verifies if the remote access service is available by sending a HEAD request to is's health endpoint.\n * @returns cached Observable that emits true if the service is available, false otherwise.\n */\n isAvailable$(): Observable<boolean> {\n if (!this.cachedIsAvailable$) {\n this.cachedIsAvailable$ = defer(() => this.healthEndpointAvailable()).pipe(shareReplay(1));\n }\n\n return this.cachedIsAvailable$;\n }\n\n /**\n * misses the leading ? for the query params\n */\n getAuthQueryParamsForWebsocketConnection() {\n const { headers } = this.fetchClient.getFetchOptions();\n const params = new URLSearchParams();\n\n if (headers) {\n const xsrfToken = headers['X-XSRF-TOKEN'];\n const auth = headers['Authorization'];\n\n if (xsrfToken) {\n params.append('XSRF-TOKEN', xsrfToken);\n }\n if (auth) {\n params.append('token', auth.replace('Bearer ', '').replace('Basic ', ''));\n }\n }\n\n const paramsString = params.toString();\n return paramsString;\n }\n\n /**\n * Returns the URI for the websocket connection to the remote access service.\n */\n getWebSocketUri<K extends string, I extends string>(deviceId: K, configurationId: I) {\n const authQueryParams = this.getAuthQueryParamsForWebsocketConnection();\n const protocol = this.document.location.protocol === 'http:' ? 'ws' : 'wss';\n const pathName =\n `${protocol}://${this.document.location.host}${this.baseUrl}/client/${deviceId}/configurations/${configurationId}` as const;\n return authQueryParams ? (`${pathName}?${authQueryParams}` as const) : pathName;\n }\n\n /**\n * Retrieves all configurations for a given device.\n */\n async listConfigurations(deviceId: string): Promise<RemoteAccessConfiguration[]> {\n const response = await this.fetchClient.fetch(\n `${this.baseUrl}/devices/${deviceId}/configurations`\n );\n if (response.ok) {\n return response.json();\n }\n\n throw new Error(`Failed to fetch configurations for device ${deviceId}`);\n }\n\n /**\n * Deletes a configuration for a given device.\n */\n async deleteConfiguration(deviceId: string, configurationId: string) {\n const response = await this.fetchClient.fetch(\n `${this.baseUrl}/devices/${deviceId}/configurations/${configurationId}`,\n { method: 'DELETE' }\n );\n\n if (response.ok) {\n return;\n }\n\n throw new Error(`Failed to delete configuration for device ${deviceId}`);\n }\n\n /**\n * Retrieves all available remote access protocol providers.\n */\n getProtocolProviders() {\n return this.serviceRegistry.get('remoteAccessProtocolHook');\n }\n\n /**\n * Retrieves all supported protocol providers for a given device.\n * Based on the declarations in the fragment c8y_RemoteAccessSupportedProtocols of the device managed object.\n */\n getSupportedProtocolProvidersFor(device: IManagedObject): RemoteAccessProtocolProvider[] {\n const { c8y_RemoteAccessSupportedProtocols = [] } = device;\n const uniqueInput = uniqWith(\n c8y_RemoteAccessSupportedProtocols.map(p => p.toUpperCase()),\n isEqual\n );\n const supportedProviders = intersectionWith(\n this.getProtocolProviders(),\n uniqueInput,\n ({ protocolName }, protocol) => protocolName?.toUpperCase() === protocol\n );\n return supportedProviders?.length > 0 ? supportedProviders : this.getProtocolProviders();\n }\n\n /**\n * Creates a new configuration for a given device.\n */\n async addConfiguration(\n deviceId: string,\n configuration: Omit<RemoteAccessConfiguration, 'id'>\n ): Promise<RemoteAccessConfiguration> {\n const response = await this.fetchClient.fetch(\n `${this.baseUrl}/devices/${deviceId}/configurations`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(configuration)\n }\n );\n\n if (response.ok) {\n return response.json();\n }\n\n throw new Error(`Failed to add configuration for device ${configuration.attrs.deviceId}`);\n }\n\n /**\n * Updates a configuration for a given device.\n */\n async updateConfiguration(\n deviceId: string,\n configuration: RemoteAccessConfiguration\n ): Promise<RemoteAccessConfiguration> {\n const response = await this.fetchClient.fetch(\n `${this.baseUrl}/devices/${deviceId}/configurations/${configuration.id}`,\n {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(configuration)\n }\n );\n\n if (response.ok) {\n return response.json();\n }\n\n throw new Error(`Failed to update configuration for device ${configuration.attrs.deviceId}`);\n }\n\n /**\n * Generates a SSH key pair for a given hostname.\n */\n async generateKeyPair(hostname: string): Promise<{ publicKey: string; privateKey: string }> {\n const response = await this.fetchClient.fetch(`${this.baseUrl}/keypair/generate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ hostname })\n });\n\n if (response.ok) {\n return response.json();\n }\n\n throw new Error(`Failed to generate key pair for ${hostname}`);\n }\n\n private async healthEndpointAvailable(): Promise<boolean> {\n try {\n const response = await this.fetchClient.fetch(`${this.baseUrl}/health`, {\n method: 'HEAD',\n headers: {\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.ok) {\n return !!response.ok;\n }\n } catch {\n return false;\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"c8y-ngx-components-remote-access-data.mjs","sources":["../../remote-access/data/remote-access.service.ts","../../remote-access/data/c8y-ngx-components-remote-access-data.ts"],"sourcesContent":["import { DOCUMENT, inject, Injectable } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivateFn } from '@angular/router';\nimport { FetchClient, IManagedObject } from '@c8y/client';\nimport {\n ContextRouteService,\n Permissions,\n ServiceRegistry,\n ViewContext\n} from '@c8y/ngx-components';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { Observable, defer, shareReplay } from 'rxjs';\nimport { RemoteAccessProtocolProvider } from './remote-access-protocol-provider';\nimport { uniqWith, intersectionWith, isEqual } from 'lodash';\n\nexport interface RemoteAccessConfiguration {\n id: string;\n name: string;\n hostname: string;\n port: number;\n protocol: string;\n attrs?: any;\n credentials?: any;\n credentialsType?: string;\n}\n\nexport const CREDENTIALS_TYPES = {\n NONE: {\n name: 'NONE',\n value: 'NONE',\n label: gettext('No password')\n },\n USER_PASS: {\n name: 'USER_PASS',\n value: 'USER_PASS',\n label: gettext('Username and password')\n },\n PASS_ONLY: {\n name: 'PASS_ONLY',\n value: 'PASS_ONLY',\n label: gettext('Password only')\n },\n KEY_PAIR: {\n name: 'KEY_PAIR',\n value: 'KEY_PAIR',\n label: gettext('Public/private keys')\n },\n CERTIFICATE: {\n name: 'CERTIFICATE',\n value: 'CERTIFICATE',\n label: gettext('Certificate')\n }\n} as const;\n\nexport const canActivateRemoteAccess: CanActivateFn = (route: ActivatedRouteSnapshot) => {\n const permissions = inject(Permissions);\n const remoteAccessService = inject(RemoteAccessService);\n const contextRouteService = inject(ContextRouteService);\n if (!permissions.hasRole(Permissions.ROLE_REMOTE_ACCESS_ADMIN)) {\n return false;\n }\n\n const contextDetails = contextRouteService.getContextData(route);\n if (contextDetails.context !== ViewContext.Device) {\n return false;\n }\n\n const device = contextDetails.contextData as IManagedObject;\n if (!device || !Array.isArray(device.c8y_SupportedOperations)) {\n return false;\n }\n const supportedOperations: string[] = device.c8y_SupportedOperations;\n if (!supportedOperations.includes('c8y_RemoteAccessConnect')) {\n return false;\n }\n return remoteAccessService.isAvailable$();\n};\n\n@Injectable({\n providedIn: 'root'\n})\nexport class RemoteAccessService {\n private cachedIsAvailable$: Observable<boolean>;\n readonly baseUrl = '/service/remoteaccess';\n private document = inject(DOCUMENT);\n constructor(\n private fetchClient: FetchClient,\n private serviceRegistry: ServiceRegistry\n ) {}\n\n /**\n * Verifies if the remote access service is available by sending a HEAD request to is's health endpoint.\n * @returns cached Observable that emits true if the service is available, false otherwise.\n */\n isAvailable$(): Observable<boolean> {\n if (!this.cachedIsAvailable$) {\n this.cachedIsAvailable$ = defer(() => this.healthEndpointAvailable()).pipe(shareReplay(1));\n }\n\n return this.cachedIsAvailable$;\n }\n\n /**\n * misses the leading ? for the query params\n */\n getAuthQueryParamsForWebsocketConnection() {\n const { headers } = this.fetchClient.getFetchOptions();\n const params = new URLSearchParams();\n\n if (headers) {\n const xsrfToken = headers['X-XSRF-TOKEN'];\n const auth = headers['Authorization'];\n\n if (xsrfToken) {\n params.append('XSRF-TOKEN', xsrfToken);\n }\n if (auth) {\n params.append('token', auth.replace('Bearer ', '').replace('Basic ', ''));\n }\n }\n\n const paramsString = params.toString();\n return paramsString;\n }\n\n /**\n * Returns the URI for the websocket connection to the remote access service.\n */\n getWebSocketUri<K extends string, I extends string>(deviceId: K, configurationId: I) {\n const authQueryParams = this.getAuthQueryParamsForWebsocketConnection();\n const protocol = this.document.location.protocol === 'http:' ? 'ws' : 'wss';\n const pathName =\n `${protocol}://${this.document.location.host}${this.baseUrl}/client/${deviceId}/configurations/${configurationId}` as const;\n return authQueryParams ? (`${pathName}?${authQueryParams}` as const) : pathName;\n }\n\n /**\n * Retrieves all configurations for a given device.\n */\n async listConfigurations(deviceId: string): Promise<RemoteAccessConfiguration[]> {\n const response = await this.fetchClient.fetch(\n `${this.baseUrl}/devices/${deviceId}/configurations`\n );\n if (response.ok) {\n return response.json();\n }\n\n throw new Error(`Failed to fetch configurations for device ${deviceId}`);\n }\n\n /**\n * Deletes a configuration for a given device.\n */\n async deleteConfiguration(deviceId: string, configurationId: string) {\n const response = await this.fetchClient.fetch(\n `${this.baseUrl}/devices/${deviceId}/configurations/${configurationId}`,\n { method: 'DELETE' }\n );\n\n if (response.ok) {\n return;\n }\n\n throw new Error(`Failed to delete configuration for device ${deviceId}`);\n }\n\n /**\n * Retrieves all available remote access protocol providers.\n */\n getProtocolProviders() {\n return this.serviceRegistry.get('remoteAccessProtocolHook');\n }\n\n /**\n * Retrieves all supported protocol providers for a given device.\n * Based on the declarations in the fragment c8y_RemoteAccessSupportedProtocols of the device managed object.\n */\n getSupportedProtocolProvidersFor(device: IManagedObject): RemoteAccessProtocolProvider[] {\n const { c8y_RemoteAccessSupportedProtocols = [] } = device;\n const uniqueInput = uniqWith(\n c8y_RemoteAccessSupportedProtocols.map(p => p.toUpperCase()),\n isEqual\n );\n const supportedProviders = intersectionWith(\n this.getProtocolProviders(),\n uniqueInput,\n ({ protocolName }, protocol) => protocolName?.toUpperCase() === protocol\n );\n return supportedProviders?.length > 0 ? supportedProviders : this.getProtocolProviders();\n }\n\n /**\n * Creates a new configuration for a given device.\n */\n async addConfiguration(\n deviceId: string,\n configuration: Omit<RemoteAccessConfiguration, 'id'>\n ): Promise<RemoteAccessConfiguration> {\n const response = await this.fetchClient.fetch(\n `${this.baseUrl}/devices/${deviceId}/configurations`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(configuration)\n }\n );\n\n if (response.ok) {\n return response.json();\n }\n\n throw new Error(`Failed to add configuration for device ${configuration.attrs.deviceId}`);\n }\n\n /**\n * Updates a configuration for a given device.\n */\n async updateConfiguration(\n deviceId: string,\n configuration: RemoteAccessConfiguration\n ): Promise<RemoteAccessConfiguration> {\n const response = await this.fetchClient.fetch(\n `${this.baseUrl}/devices/${deviceId}/configurations/${configuration.id}`,\n {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(configuration)\n }\n );\n\n if (response.ok) {\n return response.json();\n }\n\n throw new Error(`Failed to update configuration for device ${configuration.attrs.deviceId}`);\n }\n\n /**\n * Persists a confirmed server host key onto a configuration (DM-6421 first-use flow). The stored\n * value is an OpenSSH authorized_keys-style string (`<keyType> <base64Blob>`).\n *\n * Uses the dedicated host-key endpoint so only the host key is sent — the untouched credential\n * secrets (password, private key, certificate) are never round-tripped through the client.\n */\n async updateHostKey(\n deviceId: string,\n configurationId: string,\n hostKey: string\n ): Promise<RemoteAccessConfiguration> {\n const response = await this.fetchClient.fetch(\n `${this.baseUrl}/devices/${deviceId}/configurations/${configurationId}/hostkey`,\n {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ hostKey })\n }\n );\n\n if (response.ok) {\n return response.json();\n }\n\n throw new Error(`Failed to update host key for configuration ${configurationId}`);\n }\n\n /**\n * Whether the backend auto-trusts the server host key on first connect for the current tenant. When\n * enabled, the backend silently stores the key instead of prompting for confirmation.\n *\n * Delegates to the remote-access service rather than reading the `hostkey-autosave` tenant option\n * directly: the effective value is resolved server-side from the tenant option, owner inheritance\n * (a subtenant with no option set inherits the owner's) and the manifest default — none of which the\n * tenant-options API exposes to the client. Defaults to `false` (secure by default) when the status\n * cannot be read.\n */\n async isHostKeyAutosaveEnabled(): Promise<boolean> {\n try {\n const response = await this.fetchClient.fetch(`${this.baseUrl}/settings/hostkey-autosave`);\n if (!response.ok) {\n return false;\n }\n const status = await response.json();\n return status?.enabled === true;\n } catch {\n return false;\n }\n }\n\n /**\n * Generates a SSH key pair for a given hostname.\n */\n async generateKeyPair(hostname: string): Promise<{ publicKey: string; privateKey: string }> {\n const response = await this.fetchClient.fetch(`${this.baseUrl}/keypair/generate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ hostname })\n });\n\n if (response.ok) {\n return response.json();\n }\n\n throw new Error(`Failed to generate key pair for ${hostname}`);\n }\n\n private async healthEndpointAvailable(): Promise<boolean> {\n try {\n const response = await this.fetchClient.fetch(`${this.baseUrl}/health`, {\n method: 'HEAD',\n headers: {\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.ok) {\n return !!response.ok;\n }\n } catch {\n return false;\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import * as i4 from '@angular/common';
2
2
  import { NgForOf, NgClass } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Component } from '@angular/core';
4
+ import { signal, Component } from '@angular/core';
5
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
6
  import * as i1 from '@angular/forms';
6
7
  import { Validators } from '@angular/forms';
7
8
  import * as i3 from '@c8y/ngx-components';
@@ -25,11 +26,21 @@ class RemoteAccessSshEndpointModalComponent {
25
26
  ];
26
27
  this.title = gettext('Remote access endpoint');
27
28
  this.generating = false;
29
+ /**
30
+ * Whether the tenant auto-saves the server host key on first connect. When true, an empty host key
31
+ * field means the key is trusted automatically without confirmation — surfaced as a warning.
32
+ */
33
+ this.hostKeyAutosaveEnabled = signal(false, ...(ngDevMode ? [{ debugName: "hostKeyAutosaveEnabled" }] : /* istanbul ignore next */ []));
34
+ /** Whether the host key field is currently empty; the autosave warning only applies when it is. */
35
+ this.hostKeyEmpty = signal(true, ...(ngDevMode ? [{ debugName: "hostKeyEmpty" }] : /* istanbul ignore next */ []));
28
36
  this.result = new Promise((resolve, reject) => {
29
37
  this._resolve = resolve;
30
38
  this._reject = reject;
31
39
  });
32
40
  this.form = this.initForm();
41
+ this.form.controls.hostKey.valueChanges
42
+ .pipe(takeUntilDestroyed())
43
+ .subscribe(value => this.hostKeyEmpty.set(!(value ?? '').trim()));
33
44
  }
34
45
  ngOnInit() {
35
46
  if (this.currentValue) {
@@ -50,6 +61,9 @@ class RemoteAccessSshEndpointModalComponent {
50
61
  const patchedValue = { ...rest, ...attributesToAdd };
51
62
  this.form.patchValue(patchedValue);
52
63
  }
64
+ void this.remoteAccess
65
+ .isHostKeyAutosaveEnabled()
66
+ .then(enabled => this.hostKeyAutosaveEnabled.set(enabled));
53
67
  }
54
68
  initForm() {
55
69
  return this.formBuilder.group({
@@ -96,11 +110,11 @@ class RemoteAccessSshEndpointModalComponent {
96
110
  this._resolve(this.form.value);
97
111
  }
98
112
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RemoteAccessSshEndpointModalComponent, deps: [{ token: i1.FormBuilder }, { token: i2.RemoteAccessService }], target: i0.ɵɵFactoryTarget.Component }); }
99
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.17", type: RemoteAccessSshEndpointModalComponent, isStandalone: true, selector: "c8y-remote-access-ssh-endpoint-modal", ngImport: i0, template: "<ng-template #keyPairPopoverContent>\n <small translate>Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA, ECDSA, ED25519.</small>\n</ng-template>\n\n<ng-template #certificatePopoverContent>\n <small translate>Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA.</small>\n</ng-template>\n\n<c8y-modal\n [title]=\"title\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [disabled]=\"form.invalid\"\n [labels]=\"{ cancel: 'Cancel', ok: 'Save' }\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'laptop'\"></span>\n </ng-container>\n <div\n class=\"p-24\"\n [formGroup]=\"form\"\n >\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n name=\"name\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'My remote access endpoint' }\"\n formControlName=\"name\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--name'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"protocol\"\n translate\n >\n Protocol\n </label>\n <input\n class=\"form-control\"\n id=\"protocol\"\n name=\"protocol\"\n type=\"text\"\n readonly\n formControlName=\"protocol\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"hostname\"\n translate\n >\n Host\n </label>\n <input\n class=\"form-control\"\n id=\"hostname\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '127.0.0.1' } }}\"\n name=\"hostname\"\n type=\"text\"\n formControlName=\"hostname\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--hostname'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"port\"\n translate\n >\n Port\n </label>\n <input\n class=\"form-control\"\n id=\"port\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '22' } }}\"\n name=\"port\"\n type=\"number\"\n formControlName=\"port\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--port'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <div>\n <c8y-form-group>\n <label\n for=\"credentialsType\"\n translate\n >\n Sign-in method\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"credentialsType\"\n formControlName=\"credentialsType\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--credentials-type'\"\n >\n <option\n *ngFor=\"let type of credentialTypes\"\n [value]=\"type.value\"\n >\n {{ type.label | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n\n <div\n class=\"tight-grid\"\n >\n <div class=\"col-md-6\">\n <c8y-form-group>\n <label\n for=\"username\"\n translate\n >\n Username\n </label>\n <input\n class=\"form-control\"\n id=\"username\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_username' } }}\"\n name=\"username\"\n type=\"username\"\n autocomplete=\"off\"\n formControlName=\"username\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"col-md-6\" *ngIf=\"form.value.credentialsType === 'USER_PASS'\">\n <c8y-form-group>\n <label\n for=\"password\"\n translate\n >\n Password\n </label>\n <input\n class=\"form-control\"\n id=\"password\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_password' } }}\"\n name=\"password\"\n type=\"password\"\n autocomplete=\"off\"\n formControlName=\"password\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'KEY_PAIR'\">\n <div class=\"form-group\" ng-if=\"!vm.keyPairAvailable() || vm.changingKeyPair\">\n <button\n class=\"btn btn-default p-l-16\"\n type=\"button\"\n [title]=\"'Generate public and private keys' | translate\"\n [ngClass]=\"{ 'btn-pending': generating }\"\n ng-disabled=\"vm.generating\"\n (click)=\"generateKeys()\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--generate-keys'\"\n >\n <i [c8yIcon]=\"'key'\"></i>\n {{ 'Generate public and private keys' | translate }}\n </button>\n </div>\n\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"publicKey\">\n {{ 'Public key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n <button\n class=\"m-l-16 btn-link interact\"\n type=\"button\"\n [title]=\"'Download key' | translate\"\n (click)=\"downloadPublicKey()\"\n *ngIf=\"form.value.publicKey\"\n >\n <i [c8yIcon]=\"'download'\"></i>\n {{ 'Download key' | translate }}\n </button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"publicKey\"\n formControlName=\"publicKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'CERTIFICATE'\">\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"certificate\">\n {{ 'Certificate' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"certificate\"\n formControlName=\"certificate\"\n [keyType]=\"'certificate'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n </div>\n\n <div>\n <c8y-form-group>\n <label\n for=\"hostKey\"\n >\n {{ 'Host key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"hostKey\"\n formControlName=\"hostKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </div>\n</c8y-modal>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i3.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: i3.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage", "additionalMessages"] }, { kind: "directive", type: i3.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: SshKeyInputComponent, selector: "c8y-ssh-key-input", inputs: ["keyType"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i5.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
113
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: RemoteAccessSshEndpointModalComponent, isStandalone: true, selector: "c8y-remote-access-ssh-endpoint-modal", ngImport: i0, template: "<!-- eslint-disable @angular-eslint/template/prefer-control-flow -- pre-existing legacy *ngIf/*ngFor in this modal; not migrating as part of the DM-6421 host-key change -->\n<ng-template #keyPairPopoverContent>\n <small translate\n >Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA, ECDSA,\n ED25519.</small\n >\n</ng-template>\n\n<ng-template #certificatePopoverContent>\n <small translate\n >Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA.</small\n >\n</ng-template>\n\n<c8y-modal\n [title]=\"title\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [disabled]=\"form.invalid\"\n [labels]=\"{ cancel: 'Cancel', ok: 'Save' }\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'laptop'\"></span>\n </ng-container>\n <div\n class=\"p-24\"\n [formGroup]=\"form\"\n >\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n name=\"name\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'My remote access endpoint' }\"\n formControlName=\"name\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--name'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"protocol\"\n translate\n >\n Protocol\n </label>\n <input\n class=\"form-control\"\n id=\"protocol\"\n name=\"protocol\"\n type=\"text\"\n readonly\n formControlName=\"protocol\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"hostname\"\n translate\n >\n Host\n </label>\n <input\n class=\"form-control\"\n id=\"hostname\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '127.0.0.1' } }}\"\n name=\"hostname\"\n type=\"text\"\n formControlName=\"hostname\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--hostname'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"port\"\n translate\n >\n Port\n </label>\n <input\n class=\"form-control\"\n id=\"port\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '22' } }}\"\n name=\"port\"\n type=\"number\"\n formControlName=\"port\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--port'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <div>\n <c8y-form-group>\n <label\n for=\"credentialsType\"\n translate\n >\n Sign-in method\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"credentialsType\"\n formControlName=\"credentialsType\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--credentials-type'\"\n >\n <option\n *ngFor=\"let type of credentialTypes\"\n [value]=\"type.value\"\n >\n {{ type.label | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n\n <div class=\"tight-grid\">\n <div class=\"col-md-6\">\n <c8y-form-group>\n <label\n for=\"username\"\n translate\n >\n Username\n </label>\n <input\n class=\"form-control\"\n id=\"username\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_username' } }}\"\n name=\"username\"\n type=\"username\"\n autocomplete=\"off\"\n formControlName=\"username\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n <div\n class=\"col-md-6\"\n *ngIf=\"form.value.credentialsType === 'USER_PASS'\"\n >\n <c8y-form-group>\n <label\n for=\"password\"\n translate\n >\n Password\n </label>\n <input\n class=\"form-control\"\n id=\"password\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_password' } }}\"\n name=\"password\"\n type=\"password\"\n autocomplete=\"off\"\n formControlName=\"password\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'KEY_PAIR'\">\n <div\n class=\"form-group\"\n ng-if=\"!vm.keyPairAvailable() || vm.changingKeyPair\"\n >\n <button\n class=\"btn btn-default p-l-16\"\n [title]=\"'Generate public and private keys' | translate\"\n type=\"button\"\n [ngClass]=\"{ 'btn-pending': generating }\"\n ng-disabled=\"vm.generating\"\n (click)=\"generateKeys()\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--generate-keys'\"\n >\n <i [c8yIcon]=\"'key'\"></i>\n {{ 'Generate public and private keys' | translate }}\n </button>\n </div>\n\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"publicKey\">\n {{ 'Public key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n <button\n class=\"m-l-16 btn-link interact\"\n [title]=\"'Download key' | translate\"\n type=\"button\"\n (click)=\"downloadPublicKey()\"\n *ngIf=\"form.value.publicKey\"\n >\n <i [c8yIcon]=\"'download'\"></i>\n {{ 'Download key' | translate }}\n </button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"publicKey\"\n formControlName=\"publicKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'CERTIFICATE'\">\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"certificate\">\n {{ 'Certificate' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"certificate\"\n formControlName=\"certificate\"\n [keyType]=\"'certificate'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n </div>\n\n <div>\n <c8y-form-group>\n <label for=\"hostKey\">\n {{ 'Host key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"hostKey\"\n formControlName=\"hostKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n <c8y-messages></c8y-messages>\n @if (hostKeyAutosaveEnabled() && hostKeyEmpty()) {\n <div class=\"alert alert-warning m-t-8\">\n <strong>\n {{\n 'If left empty, the server host key is trusted automatically on the first connection.'\n | translate\n }}\n </strong>\n </div>\n }\n </c8y-form-group>\n </div>\n </div>\n </div>\n</c8y-modal>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i3.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: i3.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage", "additionalMessages"] }, { kind: "directive", type: i3.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: SshKeyInputComponent, selector: "c8y-ssh-key-input", inputs: ["keyType"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i5.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); }
100
114
  }
101
115
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RemoteAccessSshEndpointModalComponent, decorators: [{
102
116
  type: Component,
103
- args: [{ selector: 'c8y-remote-access-ssh-endpoint-modal', standalone: true, imports: [CoreModule, NgForOf, NgClass, SshKeyInputComponent, PopoverModule], template: "<ng-template #keyPairPopoverContent>\n <small translate>Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA, ECDSA, ED25519.</small>\n</ng-template>\n\n<ng-template #certificatePopoverContent>\n <small translate>Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA.</small>\n</ng-template>\n\n<c8y-modal\n [title]=\"title\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [disabled]=\"form.invalid\"\n [labels]=\"{ cancel: 'Cancel', ok: 'Save' }\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'laptop'\"></span>\n </ng-container>\n <div\n class=\"p-24\"\n [formGroup]=\"form\"\n >\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n name=\"name\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'My remote access endpoint' }\"\n formControlName=\"name\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--name'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"protocol\"\n translate\n >\n Protocol\n </label>\n <input\n class=\"form-control\"\n id=\"protocol\"\n name=\"protocol\"\n type=\"text\"\n readonly\n formControlName=\"protocol\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"hostname\"\n translate\n >\n Host\n </label>\n <input\n class=\"form-control\"\n id=\"hostname\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '127.0.0.1' } }}\"\n name=\"hostname\"\n type=\"text\"\n formControlName=\"hostname\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--hostname'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"port\"\n translate\n >\n Port\n </label>\n <input\n class=\"form-control\"\n id=\"port\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '22' } }}\"\n name=\"port\"\n type=\"number\"\n formControlName=\"port\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--port'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <div>\n <c8y-form-group>\n <label\n for=\"credentialsType\"\n translate\n >\n Sign-in method\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"credentialsType\"\n formControlName=\"credentialsType\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--credentials-type'\"\n >\n <option\n *ngFor=\"let type of credentialTypes\"\n [value]=\"type.value\"\n >\n {{ type.label | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n\n <div\n class=\"tight-grid\"\n >\n <div class=\"col-md-6\">\n <c8y-form-group>\n <label\n for=\"username\"\n translate\n >\n Username\n </label>\n <input\n class=\"form-control\"\n id=\"username\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_username' } }}\"\n name=\"username\"\n type=\"username\"\n autocomplete=\"off\"\n formControlName=\"username\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"col-md-6\" *ngIf=\"form.value.credentialsType === 'USER_PASS'\">\n <c8y-form-group>\n <label\n for=\"password\"\n translate\n >\n Password\n </label>\n <input\n class=\"form-control\"\n id=\"password\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_password' } }}\"\n name=\"password\"\n type=\"password\"\n autocomplete=\"off\"\n formControlName=\"password\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'KEY_PAIR'\">\n <div class=\"form-group\" ng-if=\"!vm.keyPairAvailable() || vm.changingKeyPair\">\n <button\n class=\"btn btn-default p-l-16\"\n type=\"button\"\n [title]=\"'Generate public and private keys' | translate\"\n [ngClass]=\"{ 'btn-pending': generating }\"\n ng-disabled=\"vm.generating\"\n (click)=\"generateKeys()\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--generate-keys'\"\n >\n <i [c8yIcon]=\"'key'\"></i>\n {{ 'Generate public and private keys' | translate }}\n </button>\n </div>\n\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"publicKey\">\n {{ 'Public key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n <button\n class=\"m-l-16 btn-link interact\"\n type=\"button\"\n [title]=\"'Download key' | translate\"\n (click)=\"downloadPublicKey()\"\n *ngIf=\"form.value.publicKey\"\n >\n <i [c8yIcon]=\"'download'\"></i>\n {{ 'Download key' | translate }}\n </button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"publicKey\"\n formControlName=\"publicKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'CERTIFICATE'\">\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"certificate\">\n {{ 'Certificate' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"certificate\"\n formControlName=\"certificate\"\n [keyType]=\"'certificate'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n </div>\n\n <div>\n <c8y-form-group>\n <label\n for=\"hostKey\"\n >\n {{ 'Host key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"hostKey\"\n formControlName=\"hostKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </div>\n</c8y-modal>\n" }]
117
+ args: [{ selector: 'c8y-remote-access-ssh-endpoint-modal', standalone: true, imports: [CoreModule, NgForOf, NgClass, SshKeyInputComponent, PopoverModule], template: "<!-- eslint-disable @angular-eslint/template/prefer-control-flow -- pre-existing legacy *ngIf/*ngFor in this modal; not migrating as part of the DM-6421 host-key change -->\n<ng-template #keyPairPopoverContent>\n <small translate\n >Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA, ECDSA,\n ED25519.</small\n >\n</ng-template>\n\n<ng-template #certificatePopoverContent>\n <small translate\n >Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA.</small\n >\n</ng-template>\n\n<c8y-modal\n [title]=\"title\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [disabled]=\"form.invalid\"\n [labels]=\"{ cancel: 'Cancel', ok: 'Save' }\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'laptop'\"></span>\n </ng-container>\n <div\n class=\"p-24\"\n [formGroup]=\"form\"\n >\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n name=\"name\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'My remote access endpoint' }\"\n formControlName=\"name\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--name'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"protocol\"\n translate\n >\n Protocol\n </label>\n <input\n class=\"form-control\"\n id=\"protocol\"\n name=\"protocol\"\n type=\"text\"\n readonly\n formControlName=\"protocol\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"hostname\"\n translate\n >\n Host\n </label>\n <input\n class=\"form-control\"\n id=\"hostname\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '127.0.0.1' } }}\"\n name=\"hostname\"\n type=\"text\"\n formControlName=\"hostname\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--hostname'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"port\"\n translate\n >\n Port\n </label>\n <input\n class=\"form-control\"\n id=\"port\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '22' } }}\"\n name=\"port\"\n type=\"number\"\n formControlName=\"port\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--port'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <div>\n <c8y-form-group>\n <label\n for=\"credentialsType\"\n translate\n >\n Sign-in method\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"credentialsType\"\n formControlName=\"credentialsType\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--credentials-type'\"\n >\n <option\n *ngFor=\"let type of credentialTypes\"\n [value]=\"type.value\"\n >\n {{ type.label | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n\n <div class=\"tight-grid\">\n <div class=\"col-md-6\">\n <c8y-form-group>\n <label\n for=\"username\"\n translate\n >\n Username\n </label>\n <input\n class=\"form-control\"\n id=\"username\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_username' } }}\"\n name=\"username\"\n type=\"username\"\n autocomplete=\"off\"\n formControlName=\"username\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n <div\n class=\"col-md-6\"\n *ngIf=\"form.value.credentialsType === 'USER_PASS'\"\n >\n <c8y-form-group>\n <label\n for=\"password\"\n translate\n >\n Password\n </label>\n <input\n class=\"form-control\"\n id=\"password\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_password' } }}\"\n name=\"password\"\n type=\"password\"\n autocomplete=\"off\"\n formControlName=\"password\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'KEY_PAIR'\">\n <div\n class=\"form-group\"\n ng-if=\"!vm.keyPairAvailable() || vm.changingKeyPair\"\n >\n <button\n class=\"btn btn-default p-l-16\"\n [title]=\"'Generate public and private keys' | translate\"\n type=\"button\"\n [ngClass]=\"{ 'btn-pending': generating }\"\n ng-disabled=\"vm.generating\"\n (click)=\"generateKeys()\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--generate-keys'\"\n >\n <i [c8yIcon]=\"'key'\"></i>\n {{ 'Generate public and private keys' | translate }}\n </button>\n </div>\n\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"publicKey\">\n {{ 'Public key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n <button\n class=\"m-l-16 btn-link interact\"\n [title]=\"'Download key' | translate\"\n type=\"button\"\n (click)=\"downloadPublicKey()\"\n *ngIf=\"form.value.publicKey\"\n >\n <i [c8yIcon]=\"'download'\"></i>\n {{ 'Download key' | translate }}\n </button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"publicKey\"\n formControlName=\"publicKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'CERTIFICATE'\">\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"certificate\">\n {{ 'Certificate' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"certificate\"\n formControlName=\"certificate\"\n [keyType]=\"'certificate'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n </div>\n\n <div>\n <c8y-form-group>\n <label for=\"hostKey\">\n {{ 'Host key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"hostKey\"\n formControlName=\"hostKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n <c8y-messages></c8y-messages>\n @if (hostKeyAutosaveEnabled() && hostKeyEmpty()) {\n <div class=\"alert alert-warning m-t-8\">\n <strong>\n {{\n 'If left empty, the server host key is trusted automatically on the first connection.'\n | translate\n }}\n </strong>\n </div>\n }\n </c8y-form-group>\n </div>\n </div>\n </div>\n</c8y-modal>\n" }]
104
118
  }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.RemoteAccessService }] });
105
119
 
106
120
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.mjs","sources":["../../remote-access/ssh/remote-access-ssh-endpoint-modal/remote-access-ssh-endpoint-modal.component.ts","../../remote-access/ssh/remote-access-ssh-endpoint-modal/remote-access-ssh-endpoint-modal.component.html","../../remote-access/ssh/remote-access-ssh-endpoint-modal/c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.ts"],"sourcesContent":["import { NgClass, NgForOf } from '@angular/common';\nimport { Component, OnInit } from '@angular/core';\nimport { FormBuilder, Validators } from '@angular/forms';\nimport { CoreModule } from '@c8y/ngx-components';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n CREDENTIALS_TYPES,\n RemoteAccessConfiguration,\n RemoteAccessService\n} from '@c8y/ngx-components/remote-access/data';\nimport { SshKeyInputComponent } from '@c8y/ngx-components/remote-access/shared';\nimport { PopoverModule } from 'ngx-bootstrap/popover';\nimport { saveAs } from 'file-saver';\n\n@Component({\n selector: 'c8y-remote-access-ssh-endpoint-modal',\n templateUrl: './remote-access-ssh-endpoint-modal.component.html',\n standalone: true,\n imports: [CoreModule, NgForOf, NgClass, SshKeyInputComponent, PopoverModule]\n})\nexport class RemoteAccessSshEndpointModalComponent implements OnInit {\n credentialTypes = [\n CREDENTIALS_TYPES.USER_PASS,\n CREDENTIALS_TYPES.CERTIFICATE,\n CREDENTIALS_TYPES.KEY_PAIR\n ];\n title = gettext('Remote access endpoint');\n form: ReturnType<typeof this.initForm>;\n generating = false;\n\n currentValue: Partial<RemoteAccessConfiguration>;\n\n result = new Promise<ReturnType<typeof this.initForm>['value']>((resolve, reject) => {\n this._resolve = resolve;\n this._reject = reject;\n });\n\n private _resolve: (value: ReturnType<typeof this.initForm>['value']) => void;\n private _reject: (reason?: any) => void;\n\n constructor(\n private formBuilder: FormBuilder,\n private remoteAccess: RemoteAccessService\n ) {\n this.form = this.initForm();\n }\n\n ngOnInit() {\n if (this.currentValue) {\n const { credentials, ...rest } = this.currentValue;\n const credentialsType = credentials?.type || CREDENTIALS_TYPES.USER_PASS.value;\n const attributesToAdd = { credentialsType };\n if (credentials) {\n const { username, password, publicKey, privateKey, certificate, hostKey } = credentials;\n Object.assign(attributesToAdd, {\n username,\n password,\n publicKey,\n privateKey,\n certificate,\n hostKey\n });\n }\n const patchedValue = { ...rest, ...attributesToAdd };\n this.form.patchValue(patchedValue);\n }\n }\n\n initForm() {\n return this.formBuilder.group({\n id: ['', []],\n name: ['', [Validators.required, Validators.minLength(1)]],\n hostname: ['127.0.0.1', [Validators.required, Validators.minLength(1)]],\n port: [22, [Validators.required, Validators.min(1), Validators.max(65535)]],\n protocol: ['SSH', [Validators.required, Validators.minLength(1)]],\n username: [undefined as string, [Validators.required, Validators.minLength(1)]],\n password: [undefined as string, []],\n credentialsType: [\n CREDENTIALS_TYPES.USER_PASS.value as 'USER_PASS' | 'KEY_PAIR' | 'CERTIFICATE',\n [Validators.required]\n ],\n hostKey: [undefined as string, []],\n publicKey: [undefined as string, []],\n privateKey: [undefined as string, []],\n certificate: [undefined as string, []]\n });\n }\n\n downloadPublicKey() {\n const publicKey = this.form.value.publicKey;\n const blob = new Blob([publicKey], { type: 'text/plain' });\n saveAs(blob, `key.pub`);\n }\n\n async generateKeys() {\n this.generating = true;\n try {\n const pair = await this.remoteAccess.generateKeyPair(this.form.value.hostname || undefined);\n this.form.patchValue({\n publicKey: pair.publicKey,\n privateKey: pair.privateKey\n });\n } catch (e) {\n console.log(e);\n }\n this.generating = false;\n }\n\n cancel() {\n this._reject();\n }\n\n save() {\n this._resolve(this.form.value);\n }\n}\n","<ng-template #keyPairPopoverContent>\n <small translate>Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA, ECDSA, ED25519.</small>\n</ng-template>\n\n<ng-template #certificatePopoverContent>\n <small translate>Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA.</small>\n</ng-template>\n\n<c8y-modal\n [title]=\"title\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [disabled]=\"form.invalid\"\n [labels]=\"{ cancel: 'Cancel', ok: 'Save' }\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'laptop'\"></span>\n </ng-container>\n <div\n class=\"p-24\"\n [formGroup]=\"form\"\n >\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n name=\"name\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'My remote access endpoint' }\"\n formControlName=\"name\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--name'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"protocol\"\n translate\n >\n Protocol\n </label>\n <input\n class=\"form-control\"\n id=\"protocol\"\n name=\"protocol\"\n type=\"text\"\n readonly\n formControlName=\"protocol\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"hostname\"\n translate\n >\n Host\n </label>\n <input\n class=\"form-control\"\n id=\"hostname\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '127.0.0.1' } }}\"\n name=\"hostname\"\n type=\"text\"\n formControlName=\"hostname\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--hostname'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"port\"\n translate\n >\n Port\n </label>\n <input\n class=\"form-control\"\n id=\"port\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '22' } }}\"\n name=\"port\"\n type=\"number\"\n formControlName=\"port\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--port'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <div>\n <c8y-form-group>\n <label\n for=\"credentialsType\"\n translate\n >\n Sign-in method\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"credentialsType\"\n formControlName=\"credentialsType\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--credentials-type'\"\n >\n <option\n *ngFor=\"let type of credentialTypes\"\n [value]=\"type.value\"\n >\n {{ type.label | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n\n <div\n class=\"tight-grid\"\n >\n <div class=\"col-md-6\">\n <c8y-form-group>\n <label\n for=\"username\"\n translate\n >\n Username\n </label>\n <input\n class=\"form-control\"\n id=\"username\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_username' } }}\"\n name=\"username\"\n type=\"username\"\n autocomplete=\"off\"\n formControlName=\"username\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"col-md-6\" *ngIf=\"form.value.credentialsType === 'USER_PASS'\">\n <c8y-form-group>\n <label\n for=\"password\"\n translate\n >\n Password\n </label>\n <input\n class=\"form-control\"\n id=\"password\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_password' } }}\"\n name=\"password\"\n type=\"password\"\n autocomplete=\"off\"\n formControlName=\"password\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'KEY_PAIR'\">\n <div class=\"form-group\" ng-if=\"!vm.keyPairAvailable() || vm.changingKeyPair\">\n <button\n class=\"btn btn-default p-l-16\"\n type=\"button\"\n [title]=\"'Generate public and private keys' | translate\"\n [ngClass]=\"{ 'btn-pending': generating }\"\n ng-disabled=\"vm.generating\"\n (click)=\"generateKeys()\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--generate-keys'\"\n >\n <i [c8yIcon]=\"'key'\"></i>\n {{ 'Generate public and private keys' | translate }}\n </button>\n </div>\n\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"publicKey\">\n {{ 'Public key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n <button\n class=\"m-l-16 btn-link interact\"\n type=\"button\"\n [title]=\"'Download key' | translate\"\n (click)=\"downloadPublicKey()\"\n *ngIf=\"form.value.publicKey\"\n >\n <i [c8yIcon]=\"'download'\"></i>\n {{ 'Download key' | translate }}\n </button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"publicKey\"\n formControlName=\"publicKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'CERTIFICATE'\">\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"certificate\">\n {{ 'Certificate' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"certificate\"\n formControlName=\"certificate\"\n [keyType]=\"'certificate'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n </div>\n\n <div>\n <c8y-form-group>\n <label\n for=\"hostKey\"\n >\n {{ 'Host key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"hostKey\"\n formControlName=\"hostKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </div>\n</c8y-modal>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.mjs","sources":["../../remote-access/ssh/remote-access-ssh-endpoint-modal/remote-access-ssh-endpoint-modal.component.ts","../../remote-access/ssh/remote-access-ssh-endpoint-modal/remote-access-ssh-endpoint-modal.component.html","../../remote-access/ssh/remote-access-ssh-endpoint-modal/c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.ts"],"sourcesContent":["import { NgClass, NgForOf } from '@angular/common';\nimport { Component, OnInit, signal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FormBuilder, Validators } from '@angular/forms';\nimport { CoreModule } from '@c8y/ngx-components';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n CREDENTIALS_TYPES,\n RemoteAccessConfiguration,\n RemoteAccessService\n} from '@c8y/ngx-components/remote-access/data';\nimport { SshKeyInputComponent } from '@c8y/ngx-components/remote-access/shared';\nimport { PopoverModule } from 'ngx-bootstrap/popover';\nimport { saveAs } from 'file-saver';\n\n@Component({\n selector: 'c8y-remote-access-ssh-endpoint-modal',\n templateUrl: './remote-access-ssh-endpoint-modal.component.html',\n standalone: true,\n imports: [CoreModule, NgForOf, NgClass, SshKeyInputComponent, PopoverModule]\n})\nexport class RemoteAccessSshEndpointModalComponent implements OnInit {\n credentialTypes = [\n CREDENTIALS_TYPES.USER_PASS,\n CREDENTIALS_TYPES.CERTIFICATE,\n CREDENTIALS_TYPES.KEY_PAIR\n ];\n title = gettext('Remote access endpoint');\n form: ReturnType<typeof this.initForm>;\n generating = false;\n /**\n * Whether the tenant auto-saves the server host key on first connect. When true, an empty host key\n * field means the key is trusted automatically without confirmation — surfaced as a warning.\n */\n readonly hostKeyAutosaveEnabled = signal(false);\n /** Whether the host key field is currently empty; the autosave warning only applies when it is. */\n readonly hostKeyEmpty = signal(true);\n\n currentValue: Partial<RemoteAccessConfiguration>;\n\n result = new Promise<ReturnType<typeof this.initForm>['value']>((resolve, reject) => {\n this._resolve = resolve;\n this._reject = reject;\n });\n\n private _resolve: (value: ReturnType<typeof this.initForm>['value']) => void;\n private _reject: (reason?: any) => void;\n\n constructor(\n private formBuilder: FormBuilder,\n private remoteAccess: RemoteAccessService\n ) {\n this.form = this.initForm();\n this.form.controls.hostKey.valueChanges\n .pipe(takeUntilDestroyed())\n .subscribe(value => this.hostKeyEmpty.set(!(value ?? '').trim()));\n }\n\n ngOnInit() {\n if (this.currentValue) {\n const { credentials, ...rest } = this.currentValue;\n const credentialsType = credentials?.type || CREDENTIALS_TYPES.USER_PASS.value;\n const attributesToAdd = { credentialsType };\n if (credentials) {\n const { username, password, publicKey, privateKey, certificate, hostKey } = credentials;\n Object.assign(attributesToAdd, {\n username,\n password,\n publicKey,\n privateKey,\n certificate,\n hostKey\n });\n }\n const patchedValue = { ...rest, ...attributesToAdd };\n this.form.patchValue(patchedValue);\n }\n void this.remoteAccess\n .isHostKeyAutosaveEnabled()\n .then(enabled => this.hostKeyAutosaveEnabled.set(enabled));\n }\n\n initForm() {\n return this.formBuilder.group({\n id: ['', []],\n name: ['', [Validators.required, Validators.minLength(1)]],\n hostname: ['127.0.0.1', [Validators.required, Validators.minLength(1)]],\n port: [22, [Validators.required, Validators.min(1), Validators.max(65535)]],\n protocol: ['SSH', [Validators.required, Validators.minLength(1)]],\n username: [undefined as string, [Validators.required, Validators.minLength(1)]],\n password: [undefined as string, []],\n credentialsType: [\n CREDENTIALS_TYPES.USER_PASS.value as 'USER_PASS' | 'KEY_PAIR' | 'CERTIFICATE',\n [Validators.required]\n ],\n hostKey: [undefined as string, []],\n publicKey: [undefined as string, []],\n privateKey: [undefined as string, []],\n certificate: [undefined as string, []]\n });\n }\n\n downloadPublicKey() {\n const publicKey = this.form.value.publicKey;\n const blob = new Blob([publicKey], { type: 'text/plain' });\n saveAs(blob, `key.pub`);\n }\n\n async generateKeys() {\n this.generating = true;\n try {\n const pair = await this.remoteAccess.generateKeyPair(this.form.value.hostname || undefined);\n this.form.patchValue({\n publicKey: pair.publicKey,\n privateKey: pair.privateKey\n });\n } catch (e) {\n console.log(e);\n }\n this.generating = false;\n }\n\n cancel() {\n this._reject();\n }\n\n save() {\n this._resolve(this.form.value);\n }\n}\n","<!-- eslint-disable @angular-eslint/template/prefer-control-flow -- pre-existing legacy *ngIf/*ngFor in this modal; not migrating as part of the DM-6421 host-key change -->\n<ng-template #keyPairPopoverContent>\n <small translate\n >Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA, ECDSA,\n ED25519.</small\n >\n</ng-template>\n\n<ng-template #certificatePopoverContent>\n <small translate\n >Supported formats: OpenSSHv1, OpenSSHv2, PEM, SSH2.<br />Supported algorithms: RSA, DSA.</small\n >\n</ng-template>\n\n<c8y-modal\n [title]=\"title\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [disabled]=\"form.invalid\"\n [labels]=\"{ cancel: 'Cancel', ok: 'Save' }\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'laptop'\"></span>\n </ng-container>\n <div\n class=\"p-24\"\n [formGroup]=\"form\"\n >\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n name=\"name\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'My remote access endpoint' }\"\n formControlName=\"name\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--name'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"protocol\"\n translate\n >\n Protocol\n </label>\n <input\n class=\"form-control\"\n id=\"protocol\"\n name=\"protocol\"\n type=\"text\"\n readonly\n formControlName=\"protocol\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"hostname\"\n translate\n >\n Host\n </label>\n <input\n class=\"form-control\"\n id=\"hostname\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '127.0.0.1' } }}\"\n name=\"hostname\"\n type=\"text\"\n formControlName=\"hostname\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--hostname'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"port\"\n translate\n >\n Port\n </label>\n <input\n class=\"form-control\"\n id=\"port\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: '22' } }}\"\n name=\"port\"\n type=\"number\"\n formControlName=\"port\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--port'\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n\n <div>\n <c8y-form-group>\n <label\n for=\"credentialsType\"\n translate\n >\n Sign-in method\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"credentialsType\"\n formControlName=\"credentialsType\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--credentials-type'\"\n >\n <option\n *ngFor=\"let type of credentialTypes\"\n [value]=\"type.value\"\n >\n {{ type.label | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n\n <div class=\"tight-grid\">\n <div class=\"col-md-6\">\n <c8y-form-group>\n <label\n for=\"username\"\n translate\n >\n Username\n </label>\n <input\n class=\"form-control\"\n id=\"username\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_username' } }}\"\n name=\"username\"\n type=\"username\"\n autocomplete=\"off\"\n formControlName=\"username\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n <div\n class=\"col-md-6\"\n *ngIf=\"form.value.credentialsType === 'USER_PASS'\"\n >\n <c8y-form-group>\n <label\n for=\"password\"\n translate\n >\n Password\n </label>\n <input\n class=\"form-control\"\n id=\"password\"\n placeholder=\"{{'e.g. {{ example }}' | translate : { example: 'my_password' } }}\"\n name=\"password\"\n type=\"password\"\n autocomplete=\"off\"\n formControlName=\"password\"\n />\n <c8y-messages></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'KEY_PAIR'\">\n <div\n class=\"form-group\"\n ng-if=\"!vm.keyPairAvailable() || vm.changingKeyPair\"\n >\n <button\n class=\"btn btn-default p-l-16\"\n [title]=\"'Generate public and private keys' | translate\"\n type=\"button\"\n [ngClass]=\"{ 'btn-pending': generating }\"\n ng-disabled=\"vm.generating\"\n (click)=\"generateKeys()\"\n [attr.data-cy]=\"'remoteAccessEndpointModal--generate-keys'\"\n >\n <i [c8yIcon]=\"'key'\"></i>\n {{ 'Generate public and private keys' | translate }}\n </button>\n </div>\n\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"publicKey\">\n {{ 'Public key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n <button\n class=\"m-l-16 btn-link interact\"\n [title]=\"'Download key' | translate\"\n type=\"button\"\n (click)=\"downloadPublicKey()\"\n *ngIf=\"form.value.publicKey\"\n >\n <i [c8yIcon]=\"'download'\"></i>\n {{ 'Download key' | translate }}\n </button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"publicKey\"\n formControlName=\"publicKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n </div>\n\n <div *ngIf=\"form.value.credentialsType === 'CERTIFICATE'\">\n <c8y-form-group>\n <label for=\"privateKey\">\n {{ 'Private key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"privateKey\"\n formControlName=\"privateKey\"\n [keyType]=\"'private'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n\n <c8y-form-group>\n <label for=\"certificate\">\n {{ 'Certificate' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"certificatePopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"certificate\"\n formControlName=\"certificate\"\n [keyType]=\"'certificate'\"\n ></c8y-ssh-key-input>\n </div>\n </c8y-form-group>\n </div>\n\n <div>\n <c8y-form-group>\n <label for=\"hostKey\">\n {{ 'Host key' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"keyPairPopoverContent\"\n type=\"button\"\n [container]=\"'body'\"\n [placement]=\"'right'\"\n [triggers]=\"'focus'\"\n ></button>\n </label>\n <div>\n <c8y-ssh-key-input\n id=\"hostKey\"\n formControlName=\"hostKey\"\n [keyType]=\"'public'\"\n ></c8y-ssh-key-input>\n </div>\n <c8y-messages></c8y-messages>\n @if (hostKeyAutosaveEnabled() && hostKeyEmpty()) {\n <div class=\"alert alert-warning m-t-8\">\n <strong>\n {{\n 'If left empty, the server host key is trusted automatically on the first connection.'\n | translate\n }}\n </strong>\n </div>\n }\n </c8y-form-group>\n </div>\n </div>\n </div>\n</c8y-modal>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}