Skip to content

Commit

Permalink
feat: add TryLogin(string, SecureString) methods
Browse files Browse the repository at this point in the history
This commit does all the following

- Add TryLogin(string, SecureString) methods so that the password can be
  passed as a SecureString
- Added credential manager classes for Linux, macOS and Windows
  - macOS: KeyChain
  - Linux: libsecret (depends on libsecret-tools cli)
  - Windows: Credential Manager
- Create dotnet tool `metasys-secrets` which is cross platform and can
  be used on macOS, Linux and Windows to add credentials to the default
  credential manager
- Added steps in CI build to test on Windows and macOS as well
  • Loading branch information
michaelgwelch committed Aug 1, 2024
1 parent 3d2c305 commit 54016ea
Show file tree
Hide file tree
Showing 20 changed files with 1,395 additions and 39 deletions.
33 changes: 26 additions & 7 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,31 @@ name: CI
on: [push]

jobs:
build:

build-linux:
runs-on: ubuntu-latest


steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
- run: dotnet build MetasysServices --configuration Release
- run: dotnet test MetasysServicesLinux.sln

build-macos:
runs-on: macos-latest

steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: "6.0.x"
- run: dotnet build MetasysServices --configuration Release
- run: dotnet test MetasysServicesLinux.sln

build-windows:
runs-on: windows-latest

steps:
- uses: actions/checkout@master
- uses: actions/setup-dotnet@v1
- run: dotnet build MetasysServices --configuration Release
- run: dotnet test
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
- run: dotnet build MetasysServices --configuration Release
- run: dotnet test MetasysServices.sln
1 change: 1 addition & 0 deletions MetasysSecrets/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nupkg/
28 changes: 28 additions & 0 deletions MetasysSecrets/MetasysSecrets.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\MetasysServices\MetasysServices.csproj" />
<None Include="./README.md" Pack="true" PackagePath="/" />
</ItemGroup>

<PropertyGroup>
<PackageId>JohnsonControls.MetasysSecrets</PackageId>
<Version>1.0.0-alpha2</Version>
<Authors>Johnson Controls PLC</Authors>
<Description>A cross platform cli tool for adding Metasys passwords to your operating system credential manager</Description>
<PackageTags>secrets;secret;cli;metasys</PackageTags>
<PackageReleaseNotes>Initial Release for macOS, Linux and Windows. The Linux version has a dependency on libsecret-tools</PackageReleaseNotes>
<RepositoryUrl>https://github.com/jci-metasys/basic-services-dotnet/tree/master/MetasysSecrets/</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<OutputType>Exe</OutputType>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackAsTool>true</PackAsTool>
<ToolCommandName>metasys-secrets</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
</PropertyGroup>

</Project>
92 changes: 92 additions & 0 deletions MetasysSecrets/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// See https://aka.ms/new-console-template for more information
using System.Runtime.InteropServices;
using System.Security;
using JohnsonControls.Metasys.BasicServices;

if (args.Length != 3 || !(args[0] is "add" or "lookup" or "delete"))
{
WriteUsage();
return;
}


switch (args[0])
{
case "add":
var password = GetPassword();
SecretStore.AddOrReplacePassword(args[1], args[2], password);
break;
case "lookup":
if (SecretStore.TryGetPassword(args[1], args[2], out SecureString securePassword))
{
Console.WriteLine(ConvertToPlainText(securePassword));
}
break;
case "delete":
SecretStore.DeletePassword(args[1], args[2]);
break;
default:
break;
}

string ConvertToPlainText(SecureString secureString)
{
IntPtr unmanagedString = IntPtr.Zero;
try
{
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
return Marshal.PtrToStringUni(unmanagedString) ?? "";
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
}
}

void WriteUsage()
{
Console.WriteLine("Usage:");
Console.WriteLine(" metasys-secret add {host} {username}");
Console.WriteLine(" metasys-secret lookup {host} {username}");
Console.WriteLine(" metasys-secret delete {host} {username}");
return;
}

SecureString GetPassword()
{
SecureString password = new SecureString();
if (Console.IsInputRedirected)
{
var input = Console.ReadLine();
input?.ToCharArray().ToList().ForEach(password.AppendChar);
return password;
}
Console.Write("Enter your password: ");

while (true)
{

ConsoleKeyInfo key = Console.ReadKey(intercept: true);
if (key.Key == ConsoleKey.Enter)
{
break;
}
else if (key.Key == ConsoleKey.Backspace)
{
if (password.Length > 0)
{
password.RemoveAt(password.Length - 1);
// backup, write a space, and back up again
Console.Write("\b \b");
}
}
else
{
password.AppendChar(key.KeyChar);
Console.Write("*");
}
}
Console.WriteLine();
password.MakeReadOnly();
return password;
}
99 changes: 99 additions & 0 deletions MetasysSecrets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Metasys Secrets

A cli for adding metasys passwords to your operating system credential manager.
These credentials can then be retrieved by your applications using
[SecretStore](../MetasysServices/Credentials/Secrets.cs) and the password can be
securely passed to `MetasysClient.TryLogin` method of
[basic-services-dotnet](../README.md).

## Installation

You'll need a modern version of
[dotnet](https://dotnet.microsoft.com/en-us/download). Both .Net 6.0 and .Net
8.0 are supported.

```bash
dotnet tool install --global JohnsonControls.MetasysSecrets
```

## Usage

There are three subcommands `add`, `lookup` and `delete` as shown below. Each
takes a `hostName` and `userName` for arguments.

```bash
metasys-secrets add <hostName> <userName>
metasys-secrets lookup <hostName> <userName>
metasys-secrets delete <hostName> <userName>
```

### Examples

In these examples we'll assume that the `hostName` is
`my-ads-server.company.com` and the `userName` is `api-service-account`.

### Save a Password

To save the password

```bash
> metasys-secrets add my-ads-server.company.com api-service-account
Enter your password: *******
```

Notice you are prompted for your password. If you really want to do it all on
one line you can do it like this.

```bash
echo "thepassword" | metasys-secrets add my-ads-server.company.com api-service-account
```

> [!Warning]\
> Be careful with this approach as your password is now shown in plain text and will
> be stored in your shell history. It's not recommended for production environments.
### Delete a Password

To delete a password from your credential manager you can do

```bash
metasys-secrets delete <hostName> <userName>
```

### Display a Password

To read a password from the credential manager you can do

```bash
metasys-secrets lookup <hostName> <userName>
```

> [!Warning]\
> Be careful with this approach as your password is now shown in plain text.It's
> not recommended for production environments. You may choose to use the GUI tool
> for your operating system instead.
## Credential Managers

The credential manager that is used depends on the operating system you are
using.

### Windows

On Windows the passwords are saved in the Windows Credential Manager.

### macOS

On macOS the passwords are saved in the macOS Keychain

### Linux

On Linux the passwords are saved using `libsecret` and it relies on the linux
command line tool `secret-tool`. To install that tool depends on the
distribution you are using.

On Debian/Ubuntu you can install it like this

```bash
sudo apt install libsecret-tools
```
Loading

0 comments on commit 54016ea

Please sign in to comment.