wwauth/Google.Solutions.WWAuth/View/EditConfigurationViewModel.cs (109 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.Util;
using Google.Solutions.WWAuth.Adapters;
using Google.Solutions.WWAuth.Data;
using Google.Solutions.WWAuth.Interop;
using System.ComponentModel;
using System.IO;
using System.Security.AccessControl;
using System.Windows.Forms;
namespace Google.Solutions.WWAuth.View
{
internal class EditConfigurationViewModel : ViewModelBase
{
private readonly CredentialConfigurationFile file;
private readonly IShellAdapter shellAdapter;
private string windowTitle = string.Empty;
public EditConfigurationViewModel(
CredentialConfigurationFile file,
IShellAdapter shellAdapter)
{
this.file = file.ThrowIfNull(nameof(file));
this.shellAdapter = shellAdapter.ThrowIfNull(nameof(shellAdapter));
this.WindowTitle = file.FilePath != null
? new FileInfo(file.FilePath).Name
: "New configuration";
}
//---------------------------------------------------------------------
// Observable properties.
//---------------------------------------------------------------------
public string WindowTitle
{
get => this.windowTitle;
private set
{
this.windowTitle = value;
RaisePropertyChange();
}
}
//---------------------------------------------------------------------
// Actions.
//---------------------------------------------------------------------
public DialogResult VerifyConfiguration(
IWin32Window owner)
{
using (var dialog = new VerifyConfigurationDialog(
this.file.Configuration))
{
return dialog.ShowDialog(owner);
}
}
public DialogResult VerifyConfigurationAsUser(
IWin32Window owner)
{
//
// Launch a copy of this process as a different user.
//
// By running the entire program as that user, we not
// only test if authentication works, but also ensure
// that the user is allowed to access any required
// certificates, files, etc.
//
// Create a temporary copy since the last changes might not
// have been applied yet.
//
var tempFile = this.file.Clone();
tempFile.SaveAs(Path.GetTempFileName());
var result = shellAdapter.PromptForCredentials(
owner,
out var credential);
if (result == DialogResult.OK)
{
//
// Grant the user access to the file.
//
// N.B. Passing the file contents on the command line
// would save us from changing file permissions, but
// CreateProcessWithLogon has a 1K command line limit,
// which is too short for that purpose.
//
var access = File.GetAccessControl(tempFile.FilePath);
access.AddAccessRule(new FileSystemAccessRule(
credential.UserName,
FileSystemRights.Read,
AccessControlType.Allow));
File.SetAccessControl(tempFile.FilePath, access);
//
// Launch a new process as the selected user.
//
try
{
shellAdapter.StartProcessAsUser(
Program.ExecutablePath,
new AttendedCommandLineOptions()
{
Executable = string.Empty,
Verify = tempFile.FilePath
}.ToString(),
credential);
}
catch (Win32Exception e) when (e.NativeErrorCode == NativeMethods.ERROR_DIRECTORY)
{
throw new IOException(
$"The user {credential.UserName} does not have access to the " +
$"program file {Program.ExecutablePath}.\n\n" +
"Modify the file permissions or move the program file to a " +
"different folder to ensure that the user can access and run " +
"this program.");
}
return DialogResult.OK;
}
else
{
return result;
}
}
public void LaunchGcloud()
{
//
// Create a temporary copy since the last changes might not
// have been applied yet.
//
var tempFile = Path.GetTempFileName();
this.file.Clone().SaveAs(tempFile);
this.shellAdapter.StartConsoleCommand(
$"cmd /K gcloud auth login --cred-file \"{tempFile}\"");
}
public void LaunchCommandLineEnvironment()
{
//
// Create a temporary copy since the last changes might not
// have been applied yet.
//
var tempFile = Path.GetTempFileName();
this.file.Clone().SaveAs(tempFile);
this.shellAdapter.StartConsoleCommand(
$"cmd /K \"set \"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES=1\" & " +
$"set \"GOOGLE_APPLICATION_CREDENTIALS={tempFile}\"\" & " +
$"echo Environment updated to use latest configuration as Application Default Credentials.");
}
public void LaunchExecutableCommand()
{
this.shellAdapter.StartConsoleCommand($"cmd /K {this.file.Configuration.Options} | more");
}
}
}