@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
477 lines (475 loc) • 22.5 kB
JavaScript
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