UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

477 lines (475 loc) 22.5 kB
import { from, of, Subject, throwError } from 'rxjs'; import { filter, map, mergeMap, take } from 'rxjs/operators'; import { ErrorExtended } from '../data/error-extended'; import { Logging } from '../diagnostics/logging'; import { RpcCredSSPOperationResultMember, RpcCredSSPOperationType, RpcCredSspResponseKey } from '../rpc/credssp/rpc-credssp-model'; import { RpcCredSspRequestClient } from '../rpc/credssp/rpc-credssp-request-client'; import { GatewayInstallationType } from '../shared/gateway-inventory/gateway-inventory'; import { GatewayInventoryCache } from '../shared/gateway-inventory/gateway-inventory-cache'; /** * CredSPP Manager class. Handles detecting and configuring CredSSP on a set of servers. */ export class CredSSPManager { rpc; strings = MsftSme.getStrings().MsftSmeShell.Core.CredSSPManager; watcher; gatewayInventoryCache; /** * Initializes a new instance of the Authorization Manager class. * * @param rpc The rpc to forward auth requests to a parent window */ constructor(rpc) { this.rpc = rpc; this.watcher = new Subject(); } initialize(appContext) { // When in Shell do not register. if (!this.rpc.isShell) { this.rpc.register(RpcCredSspResponseKey.command, this.onRpcResponse.bind(this)); } this.gatewayInventoryCache = new GatewayInventoryCache(appContext); } /** * New Enable CredSSP on the passed in server. * * @param serverName This server on which CredSSP should be enabled * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored */ wsmanEnableManagedServer(serverName, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (MsftSme.isNullOrWhiteSpace(serverName)) { throw new Error('A server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.EnableManagedServer, serverNames: [serverName], notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.Succeeded); } /** * New Disable CredSSP for the passed in server. * * @param serverName This server on which CredSSP should be disabled * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored */ wsmanDisableManagedServer(serverName, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (MsftSme.isNullOrWhiteSpace(serverName)) { throw new Error('A server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.DisableManagedServer, serverNames: [serverName], notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.Succeeded); } /** * New Enable CredSSP client role for the gateway and delegate to the list of servers. * * @param serverNames This list of servers where CredSSP should be enabled. * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored */ wsmanEnableClientRole(serverNames, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (!serverNames || serverNames.length < 1) { throw new Error('At least one server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.EnableClientRole, serverNames: serverNames, notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.Succeeded); } /** * New Disable CredSSP client role for the gateway and remove all delegated servers. * * @param serverNames This list of servers where CredSSP should be disabled * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored */ wsmanDisableClientRole(serverNames, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (!serverNames || serverNames.length < 1) { throw new Error('At least one server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.DisableClientRole, serverNames, notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.Succeeded); } /** * @deprecated * Use tryGatewayLocalPowershellConfig instead which will only enable CredSSP when the gateway is making * a double hop to a remote node. This method will enable CredSSP every time, even in cases where * it is not needed. * * New Enable the server as a CredSSP server, and enable the gateway as a CredSSP client of the server. * * @param serverName The server where CredSSP delegation should be enabled * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored */ wsmanEnableDelegation(serverName, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (MsftSme.isNullOrWhiteSpace(serverName)) { throw new Error('The server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.EnableDelegation, serverNames: [serverName], notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.Succeeded); } /** * Check to see if given servers contain the gateway machine. If not, enable CredSSP on index 0 of serverNames, * otherwise, do nothing. * * Note: Will only check if local runspace can be used if msft.sme.shell.localRunspace experiment key is set * or gateway is running as WAC in Portal. Otherwise will fall back to calling {@link wsmanEnableDelegation}. * This is because old installs of WAC do not have the necessary shell RPC endpoint or PowerShell API functionality. * * @param serverNames String array of server names to check for a match with the gateway server name. * If not found, CredSSP will be enabled between the gateway machine and the server name at index 0. * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored. * @returns GatewayLocalPowerShellConfig object if success. * @throws If call is made from shell, serverNames is empty or contains empty values, or unable to enable CredSSP. */ tryGatewayLocalPowerShellConfig(serverNames, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (serverNames.length === 0 || serverNames.some(serverName => MsftSme.isNullOrWhiteSpace(serverName))) { throw new Error('The server name(s) must be provided.'); } if (MsftSme.isExperimentEnabled('localRunspace', true)) { return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.TryGatewayLocalPowerShellConfig, serverNames: serverNames, notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.Data); } return this.gatewayInventoryCache.query({}).pipe(take(1), mergeMap(inventory => { if (inventory.instance.installationType === GatewayInstallationType.AzureVmExtension) { return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.TryGatewayLocalPowerShellConfig, serverNames: serverNames, notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.Data); } // Fallback to using the old, deprecated wsmanEnableDelegation method if user is running an old version of WAC. // Uses index 0 because if serverNames does not contain the gateway machine, the given name(s) should all be able to // delegate credentials (by receiving the) in a double hop scenario. return this.wsmanEnableDelegation(serverNames[0], verbose).pipe(map(response => { if (!response) { throw new Error(this.strings.TryGatewayLocalPowerShellConfigNotConfirmed.error); } const data = { configuredServerConnectionString: serverNames[0], powerShellOptions: { authenticationMechanism: 'Credssp' } }; return data; })); })); } /** * Test WSMan CredSSP connection from gateway to server(s) * * @param serverNames the servers to test connection to from gateway * @param credentials explicit credentials(username and password) to be used to WSMan CredSSP test * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored * @returns true if we can safely connect to all server without isssues otherwise returns false */ testCredSSP(serverNames, credentials, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (!serverNames.length || serverNames.some(serverName => MsftSme.isNullOrWhiteSpace(serverName))) { throw new Error('The server name(s) must be provided.'); } if (!credentials || !credentials.username || !credentials.password) { throw new Error('Username and password must be provided in the options.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.TestCredSSP, serverNames: serverNames, credentials, notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.Succeeded); } /** * Get the CredSSP client role configuration of the gateway, including: * 1. Client role of gateway to delegate fresh credentials * 2. Which servers can be delegated fresh credentials * * @param serverNames The list of servers to check credential delegation status * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored * @returns ClientRoleConfiguration object of the gateway client role configuration */ wsmanGetClientConfigurationOnGateway(serverNames, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (!serverNames || serverNames.length < 1) { throw new Error('At least one server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.ConfirmClientConfiguration, serverNames, notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.ConfigurationStatus).pipe(map(response => response.client)); } /** * Get the CredSSP server role configuration of the server. * * @param serverName The server to get the CredSSP configuration * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored * @returns ServerRoleConfiguration object of the managed server server role configuration */ wsmanGetManagedServerConfiguration(serverNames, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (!serverNames || serverNames.length < 1) { throw new Error('At least one server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.ConfirmManagedServerConfiguration, serverNames, notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.ConfigurationStatus).pipe(map(response => response.servers)); } /** * Get the CredSSP delegation configuration, including: * 1. Client role of gateway to delegate fresh credentials * 2. Which servers can be delegated fresh credentials * 3. Server roles of each servers * * @param serverNames The list of servers to check credential delegation from gateway and to check server role status * @param verbose (Optional) Specify whether a solution (if any) should be returned if CredSSP errored * @returns ConfigurationData object of the client and server role configuation */ wsmanGetDelegationConfiguration(serverNames, verbose = false) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (!serverNames || serverNames.length < 1) { throw new Error('At least one server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.ConfirmDelegation, serverNames, notificationTitle: null, notificationId: null }, verbose, RpcCredSSPOperationResultMember.ConfigurationStatus); } /** * @deprecated * Notification message wouldn't be displayed from this call. Display the notification by own code, * and use wsmanEnableManagedServer instead. * * Enable CredSSP on the passed in server. * * @param alertTitle Title for notifications raised by this servers. Should be contextual to the scenario that is using this service * @param serverName This server on which CredSSP should be enabled * @param alertId Optional notification Id */ enableManagedServer(alertTitle, serverName, alertId) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (MsftSme.isNullOrWhiteSpace(serverName)) { throw new Error('A server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.EnableManagedServer, serverNames: [serverName], notificationTitle: alertTitle, notificationId: alertId }, false, RpcCredSSPOperationResultMember.Succeeded); } /** * @deprecated * Notification message wouldn't be displayed from this call. Display the notification by own code, * and use wsmanDisableManagedServer instead. * * Disable CredSSP for the passed in server. * * @param alertTitle Title for notifications raised by this servers. Should be contextual to the scenario that is using this service * @param serverName This server on which CredSSP should be disabled * @param alertId Optional notification Id */ disableManagedServer(alertTitle, serverName, alertId) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (MsftSme.isNullOrWhiteSpace(serverName)) { throw new Error('A server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.DisableManagedServer, serverNames: [serverName], notificationTitle: alertTitle, notificationId: alertId }, false, RpcCredSSPOperationResultMember.Succeeded); } /** * @deprecated * Notification message wouldn't be displayed from this call. Display the notification by own code, * and use wsmanEnableClientRole instead. * * Enable CredSSP client role for the gateway and delegate to the list of servers. * * @param alertTitle Title for notifications raised by this servers. Should be contextual to the scenario that is using this service * @param serverNames This list of servers where CredSSP should be enabled. * @param alertId Optional notification Id */ enableClientRole(alertTitle, serverNames, alertId) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (!serverNames || serverNames.length < 1) { throw new Error('At least one server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.EnableClientRole, serverNames: serverNames, notificationTitle: alertTitle, notificationId: alertId }, false, RpcCredSSPOperationResultMember.Succeeded); } /** * @deprecated * Notification message wouldn't be displayed from this call. Display the notification by own code, * and use wsmanDisableClientRole instead. * * Disable CredSSP client role for the gateway and remove all delegated servers. * * @param alertTitle Title for notifications raised by this servers. Should be contextual to the scenario that is using this service * @param serverNames This list of servers where CredSSP should be disabled. * @param alertId Optional notification Id */ disableClientRole(alertTitle, serverNames, alertId) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (!serverNames || serverNames.length < 1) { throw new Error('At least one server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.DisableClientRole, serverNames, notificationTitle: alertTitle, notificationId: alertId }, false, RpcCredSSPOperationResultMember.Succeeded); } /** * @deprecated * Notification message wouldn't be displayed from this call. Display the notification by own code, * and use wsmanEnableDelegation instead. * * Enable the server as a CredSSP server, and enable the gateway as a CredSSP client of the server. * * @param alertTitle Title for notifications raised by this servers. Should be contextual to the scenario that is using this service * @param serverName The sever where CredSSP delegation should be enabled. * @param alertId Optional notification Id */ enableDelegation(alertTitle, serverName, alertId) { if (this.rpc.isShell) { throw new Error('Not supported on the shell environment.'); } if (MsftSme.isNullOrWhiteSpace(serverName)) { throw new Error('The server name must be provided.'); } return this.sendRequest({ requestId: MsftSme.getUniqueId(), operation: RpcCredSSPOperationType.EnableDelegation, serverNames: [serverName], notificationTitle: alertTitle, notificationId: alertId }, false, RpcCredSSPOperationResultMember.Succeeded); } /** * @deprecated * This method is obsolete! * * Disable the server as a CredSSP server, and disable the gateway as a CredSSP client of the server. * * @param alertTitle Title for notifications raised by this servers. Should be contextual to the scenario that is using this service * @param serverName The sever where CredSSP delegation should be disabled. * @param alertId Optional notification Id */ disableDelegation() { return of(true); } /** * The RPC request to the CredSSPManagerShellService. * @param request The requested CredSSP manager operation * @param verbose Specify whether a solution (if any) should be returned if CredSSP errored * @param returnProperty The property of the RPC result object to return */ sendRequest(request, verbose, returnProperty) { Logging.logDebug('CredSSPManager', 'Sending request to CredSSPManagerService. Request:{0}'.format(JSON.stringify(request))); return from(RpcCredSspRequestClient.credSspRequest(this.rpc, request)) .pipe(mergeMap(() => this.watcher), filter(result => result.requestId === request.requestId), take(1), mergeMap(result => { // shell reports 'error' property originated from the CredSSP operation. // the client using this API should get this error message to display to user. if (result.error) { if (verbose) { const error = new ErrorExtended(result.error); error.extendedSource = `${request.operation}-credSSPError`; error.extended = { solutionMessage: result.solutionMessage }; return throwError(() => error); } else { return throwError(() => new Error(result.error)); } } return of(result[returnProperty]); })); } /** * Process the RPC response from the CredSSPManagerShellService. * @param data The requested CredSSP manager operation result. */ onRpcResponse(data) { Logging.logDebug('CredSSPManager', 'Processing response from CredSSPManagerService Response:{0}'.format(JSON.stringify(data))); this.watcher.next(data); return Promise.resolve(); } } //# sourceMappingURL=credssp-manager.js.map