public async Task <HwVaultInfoFromHesDto> UpdateVaultStatus(IDevice device, HwVaultInfoFromHesDto vaultInfo, CancellationToken ct) { ct.ThrowIfCancellationRequested(); HwVaultInfoFromHesDto newVaultInfo = null; if (vaultInfo.NeedStateUpdate && _hesConnection.State == HesConnectionState.Connected) { newVaultInfo = await _hesConnection.UpdateHwVaultStatus(new HwVaultInfoFromClientDto(device), ct); await device.RefreshDeviceInfo(); } if (device.AccessLevel.IsLinkRequired) { if (_hesConnection.State == HesConnectionState.Connected) { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.StateUpdate.Error.NotAssignedToUser"]); } else { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.StateUpdate.Error.CannotLinkToUser"]); } } return(newVaultInfo ?? vaultInfo); }
public async Task UpdateAccounts(IDevice device, HwVaultInfoFromHesDto vaultInfo, bool onlyOsAccounts) { if (_hesConnection.State == HesConnectionState.Connected) { if ((vaultInfo.NeedUpdateOSAccounts && onlyOsAccounts) || (vaultInfo.NeedUpdateNonOSAccounts && !onlyOsAccounts)) { await _hesConnection.UpdateHwVaultAccounts(device.SerialNo, onlyOsAccounts); await device.RefreshDeviceInfo(); } } }
// Incoming request public async Task <HesResponse <HwVaultInfoFromHesDto> > UpdateHwVaultProperties(HwVaultInfoFromClientDto dto, bool returnInfo = false) { try { await ValidateConnectionAsync(); await _hardwareVaultService.UpdateVaultInfoAsync(dto); switch (dto.ConnectionState) { case HwVaultConnectionState.Offline: _remoteDeviceConnectionsService.OnDeviceDisconnected(dto.VaultSerialNo, GetWorkstationId()); await _employeeService.UpdateLastSeenAsync(dto.VaultSerialNo); await InvokeVaultStateChangedAsync(dto.VaultSerialNo); break; case HwVaultConnectionState.Initializing: _remoteDeviceConnectionsService.OnDeviceConnected(dto.VaultSerialNo, GetWorkstationId(), Clients.Caller); break; case HwVaultConnectionState.Finalizing: break; case HwVaultConnectionState.Online: await InvokeVaultStateChangedAsync(dto.VaultSerialNo); break; } HwVaultInfoFromHesDto info = null; if (returnInfo) { info = await GetHardwareVaultInfoAsync(dto); return(new HesResponse <HwVaultInfoFromHesDto>(info)); } return(new HesResponse <HwVaultInfoFromHesDto>(info)); } catch (HideezException ex) { _logger.LogInformation($"[{dto.VaultSerialNo}] {ex.Message}"); return(new HesResponse <HwVaultInfoFromHesDto>(ex)); } catch (Exception ex) { _logger.LogError(ex.Message); return(new HesResponse <HwVaultInfoFromHesDto>(ex)); } }
public void CacheAndUpdateVaultOwner(ref IDevice device, HwVaultInfoFromHesDto dto, CancellationToken ct) { ct.ThrowIfCancellationRequested(); if (string.IsNullOrWhiteSpace(dto.VaultMac)) { LoadLocalVaultOwner(ref device); } else { UpdateVaultOwner(ref device, dto.OwnerName, dto.OwnerEmail); CacheVaultInfoAsync(dto); } }
void CacheVaultInfoAsync(HwVaultInfoFromHesDto dto) { if (_localDeviceInfoCache != null) { Task.Run(() => { var info = new LocalDeviceInfo { Mac = dto.VaultMac, SerialNo = dto.VaultSerialNo, OwnerName = dto.OwnerName, OwnerEmail = dto.OwnerEmail, }; _localDeviceInfoCache.SaveLocalInfo(info); }).ConfigureAwait(false); } else { WriteLine("Failed to cache info: Local info cache not available"); } }
async Task MainWorkflow(string mac, bool rebondOnConnectionFail, bool tryUnlock, Action <WorkstationUnlockResult> onUnlockAttempt, CancellationToken ct) { // Ignore MainFlow requests for devices that are already connected // IsConnected-true indicates that device already finished main flow or is in progress var existingDevice = _deviceManager.Find(mac, (int)DefaultDeviceChannel.Main); if (existingDevice != null && existingDevice.IsConnected && !WorkstationHelper.IsActiveSessionLocked()) { return; } WriteLine($"Started main workflow ({mac})"); var flowId = Guid.NewGuid().ToString(); Started?.Invoke(this, flowId); bool workflowFinishedSuccessfully = false; bool deleteVaultBond = false; string errorMessage = null; IDevice device = null; try { await _ui.SendNotification(string.Empty, mac); _subp.PermissionsCheckProcessor.CheckPermissions(); // Start periodic screen activator to raise the "curtain" if (WorkstationHelper.IsActiveSessionLocked()) { _screenActivator?.ActivateScreen(); _screenActivator?.StartPeriodicScreenActivation(0); await new WaitWorkstationUnlockerConnectProc(_workstationUnlocker) .Run(SdkConfig.WorkstationUnlockerConnectTimeout, ct); } device = await _subp.VaultConnectionProcessor.ConnectVault(mac, rebondOnConnectionFail, ct); device.Disconnected += OnVaultDisconnectedDuringFlow; device.OperationCancelled += OnCancelledByVaultButton; await _subp.VaultConnectionProcessor.WaitVaultInitialization(mac, device, ct); if (device.IsBoot) { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.Error.VaultInBootloaderMode"]); } device.SetUserProperty(CustomProperties.HW_CONNECTION_STATE_PROP, HwVaultConnectionState.Initializing); HwVaultInfoFromHesDto vaultInfo = new HwVaultInfoFromHesDto(); // Initializes with default values for when HES is not connected if (_hesConnection.State == HesConnectionState.Connected) { vaultInfo = await _hesConnection.UpdateHwVaultProperties(new HwVaultInfoFromClientDto(device), true); } _subp.CacheVaultInfoProcessor.CacheAndUpdateVaultOwner(ref device, vaultInfo, ct); await _subp.LicensingProcessor.CheckLicense(device, vaultInfo, ct); vaultInfo = await _subp.StateUpdateProcessor.UpdateVaultStatus(device, vaultInfo, ct); vaultInfo = await _subp.ActivationProcessor.ActivateVault(device, vaultInfo, ct); await _subp.MasterkeyProcessor.AuthVault(device, ct); var osAccUpdateTask = _subp.AccountsUpdateProcessor.UpdateAccounts(device, vaultInfo, true); if (_workstationUnlocker.IsConnected && WorkstationHelper.IsActiveSessionLocked() && tryUnlock) { await Task.WhenAll(_subp.UserAuthorizationProcessor.AuthorizeUser(device, ct), osAccUpdateTask); _screenActivator?.StopPeriodicScreenActivation(); await _subp.UnlockProcessor.UnlockWorkstation(device, flowId, onUnlockAttempt, ct); } else { await osAccUpdateTask; } device.SetUserProperty(CustomProperties.HW_CONNECTION_STATE_PROP, HwVaultConnectionState.Finalizing); WriteLine($"Finalizing main workflow: ({device.Id})"); DeviceFinilizingMainFlow?.Invoke(this, device); await _subp.AccountsUpdateProcessor.UpdateAccounts(device, vaultInfo, false); device.SetUserProperty(CustomProperties.HW_CONNECTION_STATE_PROP, HwVaultConnectionState.Online); if (_hesConnection.State == HesConnectionState.Connected) { await _hesConnection.UpdateHwVaultProperties(new HwVaultInfoFromClientDto(device), false); } workflowFinishedSuccessfully = true; } catch (HideezException ex) { switch (ex.ErrorCode) { case HideezErrorCode.DeviceIsLocked: case HideezErrorCode.DeviceNotAssignedToUser: case HideezErrorCode.HesDeviceNotFound: case HideezErrorCode.HesDeviceCompromised: case HideezErrorCode.DeviceHasBeenWiped: // There errors require bond removal deleteVaultBond = true; errorMessage = HideezExceptionLocalization.GetErrorAsString(ex); break; case HideezErrorCode.ButtonConfirmationTimeout: case HideezErrorCode.GetPinTimeout: case HideezErrorCode.GetActivationCodeTimeout: // Silent handling WriteLine(ex); break; case HideezErrorCode.HesNotConnected: // We need to display an error message which is different from one that is usually shown for that error code. errorMessage = TranslationSource.Instance["ConnectionFlow.Error.UnexpectedlyLostNetworkConnection"]; break; default: errorMessage = HideezExceptionLocalization.GetErrorAsString(ex); break; } } catch (VaultFailedToAuthorizeException ex) { // User should never receive this error unless there is a bug in algorithm errorMessage = HideezExceptionLocalization.GetErrorAsString(ex); } catch (WorkstationUnlockFailedException ex) { // Silent handling of failed workstation unlock // The actual message will be displayed by credential provider WriteLine(ex); } catch (OperationCanceledException ex) { // Silent cancelation handling WriteLine(ex); } catch (TimeoutException ex) { // Silent timeout handling WriteLine(ex); } catch (WebSocketException ex) { // Retrieve the most inner WebSocketException to retrieve the error code if (ex.WebSocketErrorCode != 0) { errorMessage = string.Format(TranslationSource.Instance["ConnectionFlow.Error.UnexpectedNetworkError"], ex.WebSocketErrorCode); } else if (ex.ErrorCode != 0) { errorMessage = string.Format(TranslationSource.Instance["ConnectionFlow.Error.UnexpectedNetworkError"], ex.ErrorCode); } else if (ex.InnerException is Win32Exception) { errorMessage = string.Format(TranslationSource.Instance["ConnectionFlow.Error.UnexpectedNetworkError"], "native " + (ex.InnerException as Win32Exception).NativeErrorCode); } } catch (Exception ex) { errorMessage = HideezExceptionLocalization.GetErrorAsString(ex); } finally { if (device != null) { device.Disconnected -= OnVaultDisconnectedDuringFlow; device.OperationCancelled -= OnCancelledByVaultButton; } _screenActivator?.StopPeriodicScreenActivation(); } await WorkflowCleanup(errorMessage, mac, device, workflowFinishedSuccessfully, deleteVaultBond); Finished?.Invoke(this, flowId); WriteLine($"Main workflow end {mac}"); }
public async Task <HwVaultInfoFromHesDto> ActivateVault(IDevice device, HwVaultInfoFromHesDto vaultInfo, CancellationToken ct) { HwVaultInfoFromHesDto newVaultInfo = null; if (device.IsLocked && device.IsCanUnlock) { try { do { ct.ThrowIfCancellationRequested(); var code = await _ui.GetActivationCode(device.Id, 30_000, ct); // Todo: activation timeout should not be a magic number ct.ThrowIfCancellationRequested(); if (code.Length < 6) { await _ui.SendError(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.CodeToShort"], device.Mac); continue; } if (code.Length > 8) { await _ui.SendError(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.CodeToLong"], device.Mac); continue; } try { await device.UnlockDeviceCode(code); } catch (HideezException ex) when(ex.ErrorCode == HideezErrorCode.ERR_PIN_WRONG) // Entered invalid activation code { } catch (HideezException ex) when(ex.ErrorCode == HideezErrorCode.ERR_DEVICE_LOCKED_BY_CODE) // Unlock attempts == 0 { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.LockedByInvalidAttempts"]); } ct.ThrowIfCancellationRequested(); await device.RefreshDeviceInfo(); ct.ThrowIfCancellationRequested(); if (!device.IsLocked) { WriteLine($"({device.SerialNo}) unlocked with activation code"); } else if (device.UnlockAttemptsRemain > 0) { await _ui.SendNotification(TranslationSource.Instance.Format("ConnectionFlow.ActivationCode.Error.InvalidCode", device.UnlockAttemptsRemain), device.Mac); } else { // We won't reach this line, but will leave it just in case throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.LockedByInvalidAttempts"]); } }while (device.IsLocked); } catch { if (_hesConnection.State == HesConnectionState.Connected) { await _hesConnection.UpdateHwVaultStatus(new HwVaultInfoFromClientDto(device), ct); } throw; } finally { await _ui.HideActivationCodeUi(); } if (_hesConnection.State == HesConnectionState.Connected) { newVaultInfo = await _hesConnection.UpdateHwVaultStatus(new HwVaultInfoFromClientDto(device), ct); } } if (device.IsLocked) { if (_hesConnection.State == HesConnectionState.Connected) { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.VaultIsLocked"]); } else { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.VaultIsLockedNoNetwork"]); } } return(newVaultInfo ?? vaultInfo); }
public async Task CheckLicense(IDevice device, HwVaultInfoFromHesDto vaultInfo, CancellationToken ct) { if ((device.AccessLevel.IsLinkRequired || vaultInfo.NeedUpdateLicense) && _hesConnection.State == HesConnectionState.Connected) { await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.License.UpdatingLicenseMessage"], device.Mac); IList <HwVaultLicenseDto> licenses; if (device.AccessLevel.IsLinkRequired) { licenses = await _hesConnection.GetHwVaultLicenses(device.SerialNo, ct); WriteLine($"Received {licenses.Count} TOTAL licenses from HES"); } else { licenses = await _hesConnection.GetNewHwVaultLicenses(device.SerialNo, ct); WriteLine($"Received {licenses.Count} NEW licenses from HES"); } ct.ThrowIfCancellationRequested(); if (licenses.Count > 0) { for (int i = 0; i < licenses.Count; i++) { var license = licenses[i]; ct.ThrowIfCancellationRequested(); if (license.Data == null) { throw new WorkflowException(TranslationSource.Instance.Format("ConnectionFlow.License.Error.EmptyLicenseData", device.SerialNo)); } if (license.Id == null) { throw new WorkflowException(TranslationSource.Instance.Format("ConnectionFlow.License.Error.EmptyLicenseId", device.SerialNo)); } try { await device.LoadLicense(license.Data, SdkConfig.DefaultCommandTimeout); WriteLine($"Loaded license ({license.Id}) into vault ({device.SerialNo}) in available slot"); } catch (HideezException ex) when(ex.ErrorCode == HideezErrorCode.ERR_DEVICE_LOCKED_BY_CODE || ex.ErrorCode == HideezErrorCode.ERR_DEVICE_LOCKED_BY_PIN) { await device.LoadLicense(i, license.Data, SdkConfig.DefaultCommandTimeout); WriteLine($"Loaded license ({license.Id}) into vault ({device.SerialNo}) into slot {i}"); } await _hesConnection.OnHwVaultLicenseApplied(device.SerialNo, license.Id); } } await device.RefreshDeviceInfo(); ct.ThrowIfCancellationRequested(); } if (device.LicenseInfo == 0) { if (_hesConnection.State == HesConnectionState.Connected) { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.License.Error.NoLicensesAvailable"]); } else { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.License.Error.CannotDownloadLicense"]); } } }