wwauth/Google.Solutions.WWAuth/Program.cs (110 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.Logging;
using Google.Solutions.WWAuth.Adapters;
using Google.Solutions.WWAuth.Data;
using Google.Solutions.WWAuth.Interop;
using Google.Solutions.WWAuth.View;
using System;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Windows.Forms;
[assembly: InternalsVisibleTo("Google.Solutions.WWAuth.Test")]
namespace Google.Solutions.WWAuth
{
static class Program
{
public static readonly string ExecutablePath
= Assembly.GetExecutingAssembly().Location;
private static readonly IShellAdapter shellAdapter = new ShellAdapter();
private static void WindowsMain()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
var options = AttendedCommandLineOptions
.Parse(Environment.CommandLine)
.Validate();
if (options.Verify != null)
{
//
// Verify the given configuration.
//
using (var dialog = new VerifyConfigurationDialog(
CredentialConfigurationFile
.FromFile(options.Verify)
.Configuration))
{
dialog.ShowDialog(null);
}
}
else
{
//
// Register a file extension.
//
shellAdapter.RegisterFileExtension(
typeof(Program).Namespace,
CredentialConfigurationFile.FileExtension,
new AttendedCommandLineOptions()
{
Executable = ExecutablePath,
Edit = "%1"
}.ToString());
//
// Edit or create new file.
//
// NB. For now, we always create a workload identity
// configuration.
//
var file = string.IsNullOrEmpty(options.Edit)
? CredentialConfigurationFile.NewWorkloadIdentityConfigurationFile()
: CredentialConfigurationFile.FromFile(options.Edit);
using (var dialog = new EditConfigurationDialog(file))
{
dialog.ShowDialog(null);
}
}
}
catch (Exception e)
{
ErrorDialog.ShowError(null, "Invalid credential configuration", e);
Environment.Exit(1);
}
}
private static void ConsoleMain()
{
//
// Fetch token for current user and output a
// pluggable auth-compliant result.
//
var writer = ConsoleHandle.Out.Writer;
try
{
var options = UnattendedCommandLineOptions
.Parse(Environment.CommandLine)
.Validate();
//
// Create the right token adapter.
//
var tokenAdapter = AdapterFactory.CreateTokenAdapter(
options,
new NullLogger());
//
// Acquire a token and write the result to the console
// (where the calling application will pick it up).
//
var token = tokenAdapter
.AcquireTokenAsync(
Adapters.TokenAcquisitionOptions.None,
CancellationToken.None)
.Result;
new PluggableAuthResult(token).WriteTo(writer);
}
catch (Exception e)
{
//
// Pluggable auth expects errors to be reported
// as a result on STDOUT, not on STDERR.
//
new PluggableAuthResult(e).WriteTo(writer);
}
finally
{
writer.Flush();
}
}
[STAThread]
static void Main(string[] args)
{
//
// Enable TLS 1.2.
//
System.Net.ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11;
//
// N.B. Pluggable authentication providers must write
// to STDOUT. On Windows, that (typically) demands that
// we create a CONSOLE-subsystem EXE, because GUI-subsystem
// processes don't have an attached console, and no STDOUT.
//
// But we want the same application to also serve as a
// GUI app (for configuration and testing). That demands
// that we create a GUI-subsystem EXE, otherwise, we'll
// get an (ugly, empty) console window.
//
// To have our cake and eat it to, let Windows treat this
// EXE as a GUI-subsystem process. At launch, check if we've
// been launched with a redirected STDOUT handle. If so, then
// assume we're in "pluggable authentication mode" and behave
// like a console app.
//
// If STDOUT hasn't been redirected, then behave like a
// regular GUI app.
//
// To force the app to behave like a console app, just
// redirect output to `more` or a file:
//
// wwauth.exe <...> | more
//
// N.B. The Visual Studio debugger always launches apps
// with STDOUT redirected, hence the (otherwise redundant)
// check for /protocol.
//
if (ConsoleHandle.Out.IsRedirected &&
args.Any(a => a.Equals("/protocol", StringComparison.OrdinalIgnoreCase)))
{
//
// Run in unattended mode, acting like
// pluggable-auth console application.
//
ConsoleMain();
}
else
{
//
// Act like a Windows application
//
WindowsMain();
}
}
}
}