wwauth/Google.Solutions.WWAuth/View/VerifyConfigurationViewModel.cs (283 lines of code) (raw):

// // Copyright 2022 Google LLC // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // using Google.Apis.Auth.OAuth2.Responses; using Google.Apis.Logging; using Google.Apis.Util; using Google.Solutions.WWAuth.Adapters; using Google.Solutions.WWAuth.Data; using Google.Solutions.WWAuth.Properties; using Google.Solutions.WWAuth.Util; using System; using System.Drawing; using System.Security.Principal; using System.Threading; using System.Threading.Tasks; namespace Google.Solutions.WWAuth.View { internal class VerifyConfigurationViewModel : ViewModelBase { private string windowTitle = string.Empty; private Image acquireTokenStatusImage = null; private Image exchangeTokenStatusImage = null; private Image impersonateStatusImage = null; private Image resultImage = null; private string resultText = string.Empty; private bool isResultPanelVisible = false; private bool isOkButtonEnabled = false; private bool isCancelButtonEnabled = false; private bool isLogsButtonEnabled = false; private bool isShowExternalTokenDetailsLinkEnabled = false; private bool isShowStsTokenDetailsLinkEnabled = false; private bool isShowServiceAccountTokenDetailsLinkEnabled = false; public ISubjectToken ExternalToken { get; private set; } public ISubjectToken ServiceAccountToken { get; private set; } public ISubjectToken StsToken { get; private set; } public string Logs => string.Join("\r\n", this.logger.LogEntries); private readonly MemoryLogger logger; private readonly ITokenAdapter tokenAdapter; private readonly IStsAdapter stsAdapter; private readonly IServiceAccountAdapter serviceAccountAdapter; public VerifyConfigurationViewModel( ITokenAdapter tokenAdapter, IStsAdapter stsAdapter, IServiceAccountAdapter serviceAccountAdapter, MemoryLogger logger) { this.tokenAdapter = tokenAdapter.ThrowIfNull(nameof(tokenAdapter)); this.stsAdapter = stsAdapter.ThrowIfNull(nameof(stsAdapter)); this.serviceAccountAdapter = serviceAccountAdapter.ThrowIfNull(nameof(serviceAccountAdapter)); this.logger = logger.ThrowIfNull(nameof(logger)); } //--------------------------------------------------------------------- // Observable properties. //--------------------------------------------------------------------- public string WindowTitle { get => this.windowTitle; private set { this.windowTitle = value; RaisePropertyChange(); } } public Image AcquireTokenStatusImage { get => this.acquireTokenStatusImage; private set { this.acquireTokenStatusImage = value; RaisePropertyChange(); } } public Image ExchangeTokenStatusImage { get => this.exchangeTokenStatusImage; private set { this.exchangeTokenStatusImage = value; RaisePropertyChange(); } } public Image ImpersonateStatusImage { get => this.impersonateStatusImage; private set { this.impersonateStatusImage = value; RaisePropertyChange(); } } public bool IsImpersonationStatusVisible => this.serviceAccountAdapter.IsEnabled; public Image ResultImage { get => this.resultImage; private set { this.resultImage = value; RaisePropertyChange(); } } public string ResultText { get => this.resultText; private set { this.resultText = value; RaisePropertyChange(); } } public bool IsResultPanelVisible { get => this.isResultPanelVisible; private set { this.isResultPanelVisible = value; RaisePropertyChange(); } } public bool IsOkButtonEnabled { get => this.isOkButtonEnabled; private set { this.isOkButtonEnabled = value; RaisePropertyChange(); } } public bool IsCancelButtonEnabled { get => this.isCancelButtonEnabled; private set { this.isCancelButtonEnabled = value; RaisePropertyChange(); } } public bool IsLogsButtonEnabled { get => this.isLogsButtonEnabled; private set { this.isLogsButtonEnabled = value; RaisePropertyChange(); } } public bool IsShowExternalTokenDetailsLinkEnabled { get => this.isShowExternalTokenDetailsLinkEnabled; private set { this.isShowExternalTokenDetailsLinkEnabled = value; RaisePropertyChange(); } } public bool IsShowStsTokenDetailsLinkEnabled { get => this.isShowStsTokenDetailsLinkEnabled; private set { this.isShowStsTokenDetailsLinkEnabled = value; RaisePropertyChange(); } } public bool IsShowServiceAccountTokenDetailsLinkEnabled { get => this.isShowServiceAccountTokenDetailsLinkEnabled; private set { this.isShowServiceAccountTokenDetailsLinkEnabled = value; RaisePropertyChange(); } } //--------------------------------------------------------------------- // Action. //--------------------------------------------------------------------- public async Task PerformTestAsync( CancellationToken cancellationToken) { this.AcquireTokenStatusImage = Resources.Wait_16; this.ExchangeTokenStatusImage = Resources.Wait_16; this.ImpersonateStatusImage = Resources.Wait_16; this.IsCancelButtonEnabled = true; this.IsOkButtonEnabled = false; var user = WindowsIdentity.GetCurrent().Name; this.WindowTitle = $"Test configuration as {user}"; try { cancellationToken.ThrowIfCancellationRequested(); // // (1) Acquire token. // ISubjectToken externalToken; try { externalToken = await this.tokenAdapter .AcquireTokenAsync( TokenAcquisitionOptions.ExtensiveValidation, cancellationToken) .ConfigureAwait(true); this.AcquireTokenStatusImage = Resources.Success_16; this.IsShowExternalTokenDetailsLinkEnabled = true; this.ExternalToken = externalToken; } catch (Exception) { this.AcquireTokenStatusImage = Resources.Error_16; this.ExchangeTokenStatusImage = null; this.ImpersonateStatusImage = null; throw; } // // (2) Exchange token. // TokenResponse stsToken; try { stsToken = await this.stsAdapter .ExchangeTokenAsync( externalToken, CredentialConfiguration.DefaultScopes, cancellationToken) .ConfigureAwait(true); this.ExchangeTokenStatusImage = Resources.Success_16; } catch (Exception) { this.ExchangeTokenStatusImage = Resources.Error_16; this.ImpersonateStatusImage = null; throw; } // // (3) Try to introspect STS token. // try { this.StsToken = await this.stsAdapter .IntrospectTokenAsync( stsToken.AccessToken, cancellationToken) .ConfigureAwait(true); this.IsShowStsTokenDetailsLinkEnabled = true; } catch { this.IsShowStsTokenDetailsLinkEnabled = false; } if (this.serviceAccountAdapter.IsEnabled) { // // (4) Impersonate. // try { if (!await this.serviceAccountAdapter .ExistsAsync(cancellationToken) .ConfigureAwait(true)) { throw new ArgumentException( $"Service account {serviceAccountAdapter.ServiceAccountEmail}' does not exist"); } var token = await serviceAccountAdapter .ImpersonateAsync( stsToken.AccessToken, CredentialConfiguration.DefaultScopes, cancellationToken) .ConfigureAwait(true); this.ServiceAccountToken = await serviceAccountAdapter .IntrospectTokenAsync( token.AccessToken, cancellationToken) .ConfigureAwait(true); this.IsShowServiceAccountTokenDetailsLinkEnabled = true; this.ImpersonateStatusImage = Resources.Success_16; } catch (Exception) { this.ImpersonateStatusImage = Resources.Error_16; throw; } } this.ResultImage = Resources.Success_16; this.ResultText = "Test completed successfully."; this.IsResultPanelVisible = true; } catch (OperationCanceledException) { } catch (Exception e) { this.logger.Error(e, "{0}", e.Message); this.ResultImage = Resources.Error_16; this.ResultText = e.FullMessage(); this.IsResultPanelVisible = true; this.IsLogsButtonEnabled = true; } finally { this.IsCancelButtonEnabled = false; this.IsOkButtonEnabled = true; } } } }