Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions documentation/Get-PnPManagedAppId.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPManagedAppId.htm
# Get-PnPManagedAppId

## SYNOPSIS
Retrieve an App Id associated with a Url from either the Windows Credential Manager, the MacOS Key chain or if you use the Microsoft.PowerShell.SecretManagement module, a default vault.
Retrieve an App Id associated with a Url from the Windows Credential Manager, MacOS Key chain, Linux Secret Service, or if you use the Microsoft.PowerShell.SecretManagement module, a default vault.

## SYNTAX

Expand All @@ -19,7 +19,7 @@ Get-PnPManagedAppId -Url <String>
```

## DESCRIPTION
Returns an associated App Id from the Windows Credential Manager or Mac OS Key Chain Entry.
Returns an associated App Id from the Windows Credential Manager, Mac OS Key Chain Entry, Linux Secret Service, or a default SecretManagement vault.

## EXAMPLES

Expand Down
2 changes: 1 addition & 1 deletion documentation/Remove-PnPManagedAppId.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Remove-PnPManagedAppId -Url <String> [-Force]
```

## DESCRIPTION
Removes an App Id from the Credential Manager
Removes an App Id from the Windows Credential Manager, Mac OS Key Chain Entry, Linux Secret Service, or a default SecretManagement vault.

## EXAMPLES

Expand Down
6 changes: 3 additions & 3 deletions documentation/Set-PnPManagedAppId.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ title: Set-PnPManagedAppId
# Set-PnPManagedAppId

## SYNOPSIS
Sets/Adds an App Id for use with Connect-PnPOnline to the Windows Credential Manager or Mac OS Key Chain Entry. If you the PowerShell Module Microsoft.PowerShell.SecretsStore and Microsoft.PowerShell.SecretsManagement installed and you have defined a default vault without a password than that will be used to store the App Id.
Sets/Adds an App Id for use with Connect-PnPOnline to the Windows Credential Manager, Mac OS Key Chain, or Linux Secret Service. If you have the PowerShell Module Microsoft.PowerShell.SecretManagement installed and you have defined a default vault without a password, that will be used to store the App Id.

## SYNTAX

Expand All @@ -20,7 +20,7 @@ Set-PnPManagedAppId -Url <String> -AppId <String> [-Overwrite]
```

## DESCRIPTION
Adds an App Id entry to the Windows Credential Manager or Mac OS Key Chain Entry. PnP PowerShell will check if an App Id is available when you connect using Connect-PnPOnline -Interactive. If it finds a matching URL it will use the associated App Id. You do not need to specify the -ClientId parameter then.
Adds an App Id entry to the Windows Credential Manager, Mac OS Key Chain, Linux Secret Service, or a default SecretManagement vault. PnP PowerShell will check if an App Id is available when you connect using Connect-PnPOnline -Interactive. If it finds a matching URL it will use the associated App Id. You do not need to specify the -ClientId parameter then.

If you add a Credential with a name of "https://yourtenant.sharepoint.com" it will find a match when you connect to "https://yourtenant.sharepoint.com" but also when you connect to "https://yourtenant.sharepoint.com/sites/demo1". Of course you can specify more granular entries, allow you to automatically provide App Ids for different URLs.

Expand Down Expand Up @@ -49,7 +49,7 @@ Accept wildcard characters: False
```

### -Overwrite
Use parameter to overwrite existing Mac OS Key Chain Entry. Not required on Windows.
Use parameter to overwrite existing Mac OS Key Chain Entry. Not required on Windows or Linux.

```yaml
Type: SwitchParameter
Expand Down
2 changes: 1 addition & 1 deletion pages/articles/defaultclientid.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ To set a client id for tenant with url `https://yourtenant.sharepoint.com`, you
Set-PnPManagedAppId -Url https://yourtenant.sharepoint.com -AppId f0e2b362-8973-4fc7-a293-3c73e2677e79
```

This will add an entry to your Windows Credential Manager or the MacOS keychain if your are on MacOS. Connect-PnPOnline will use this value to match the correct client id with the url you are connecting to and it is not needed use -ClientId anymore, e.g.
This will add an entry to your Windows Credential Manager, the MacOS keychain, or the Linux Secret Service. If you have configured a default Microsoft.PowerShell.SecretManagement vault, that vault will be used instead. Connect-PnPOnline will use this value to match the correct client id with the url you are connecting to and it is not needed use -ClientId anymore, e.g.

```powershell
Connect-PnPOnline -Url https://yourtenant.sharepoint.com -Interactive
Expand Down
181 changes: 128 additions & 53 deletions src/Commands/Utilities/CredentialManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
using Microsoft.Win32.SafeHandles;
using PnP.Framework.Modernization.Cache;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;

Expand All @@ -17,6 +20,9 @@ namespace PnP.PowerShell.Commands.Utilities
{
internal static class CredentialManager
{
private const string LinuxManagedAppIdSchemaName = "pnp.powershell.managedappid";
private const string LinuxManagedAppIdSecretLabel = "PnP PowerShell managed App Id";
private const string LinuxManagedAppIdCacheDirectory = ".m365pnppowershell";


public static bool AddCredential(string name, string username, SecureString password, bool overwrite)
Expand Down Expand Up @@ -54,27 +60,27 @@ public static bool AddAppId(string name, string appid, bool overwrite)
{
name = $"PnPPSAppId:{name}";
}
if (HasSecretManagement())
{
var defaultVault = GetDefaultVault();

if (!string.IsNullOrEmpty(defaultVault))
{
AddVaultAppId(defaultVault, name, appid);
}
var defaultVault = GetDefaultVaultIfAvailable();
if (!string.IsNullOrEmpty(defaultVault))
{
AddVaultAppId(defaultVault, name, appid);
return true;
}
else

var secureAppId = new NetworkCredential(null, appid).SecurePassword;
if (OperatingSystem.IsWindows())
{
var secureAppId = new NetworkCredential(null, appid).SecurePassword;
if (OperatingSystem.IsWindows())
{

WriteWindowsCredentialManagerEntry(name, null, secureAppId);
}
else if (OperatingSystem.IsMacOS())
{
WriteMacOSKeyChainEntry(name, appid);
}
WriteWindowsCredentialManagerEntry(name, null, secureAppId);
}
else if (OperatingSystem.IsMacOS())
{
WriteMacOSKeyChainEntry(name, appid);
}
else if (OperatingSystem.IsLinux())
{
WriteLinuxAppIdEntry(name, appid);
}
return true;
}
Expand Down Expand Up @@ -122,34 +128,32 @@ public static string GetAppId(string name)
name = $"PnPPSAppId:{name}";
}
// check if Microsoft.PowerShell.SecretManagement is available
if (HasSecretManagement())
var defaultVault = GetDefaultVaultIfAvailable();
if (!string.IsNullOrEmpty(defaultVault))
{
var defaultVault = GetDefaultVault();
return GetVaultAppId(defaultVault, name);
}

if (!string.IsNullOrEmpty(defaultVault))
if (OperatingSystem.IsWindows())
{
var cred = ReadWindowsCredentialManagerEntry(name);
if (cred != null)
{
return GetVaultAppId(defaultVault, name);
return SecureStringToString(cred.Password);
}
}
else
if (OperatingSystem.IsMacOS())
{
if (OperatingSystem.IsWindows())
{
var cred = ReadWindowsCredentialManagerEntry(name);
if (cred != null)
{
return SecureStringToString(cred.Password);
}
}
if (OperatingSystem.IsMacOS())
var cred = ReadMacOSKeyChainEntry(name);
if (cred != null)
{
var cred = ReadMacOSKeyChainEntry(name);
if (cred != null)
{
return SecureStringToString(cred.Password).Trim('"');
}
return SecureStringToString(cred.Password).Trim('"');
}
}
if (OperatingSystem.IsLinux())
{
return ReadLinuxAppIdEntry(name);
}
return null;
}

Expand Down Expand Up @@ -198,27 +202,26 @@ public static bool RemoveAppid(string name)
}
bool success = false;

if (HasSecretManagement())
var defaultVault = GetDefaultVaultIfAvailable();
if (!string.IsNullOrEmpty(defaultVault))
{
var defaultVault = GetDefaultVault();
RemoveVaultCredential(defaultVault, name);
return true;
}

if (!string.IsNullOrEmpty(defaultVault))
{
RemoveVaultCredential(defaultVault, name);
return true;
}
if (OperatingSystem.IsWindows())
{
success = DeleteWindowsCredentialManagerEntry(name);
}
else
if (OperatingSystem.IsMacOS())
{
if (OperatingSystem.IsWindows())
{
success = DeleteWindowsCredentialManagerEntry(name);
}
if (OperatingSystem.IsMacOS())
{
success = DeleteMacOSKeyChainEntry(name);
return success;
}
success = DeleteMacOSKeyChainEntry(name);
return success;
}
if (OperatingSystem.IsLinux())
{
success = DeleteLinuxAppIdEntry(name);
return success;
}
return success;
}
Expand Down Expand Up @@ -248,6 +251,16 @@ private static bool HasSecretManagement()
}
return false;
}

private static string GetDefaultVaultIfAvailable()
{
if (HasSecretManagement())
{
return GetDefaultVault();
}
return null;
}

private static string GetDefaultVault()
{
var defaultVaultName = "";
Expand Down Expand Up @@ -514,6 +527,68 @@ private static bool DeleteMacOSKeyChainEntry(string name)
// return success;
}

private static Storage CreateLinuxManagedAppIdStorage(string name)
{
var cacheDir = Path.Combine(MsalCacheHelper.UserRootDirectory, LinuxManagedAppIdCacheDirectory);
var cacheFileName = $"pnp.managedappid.{GetSha256Hash(name)}.cache";

var properties = new StorageCreationPropertiesBuilder(cacheFileName, cacheDir)
.WithLinuxKeyring(
schemaName: LinuxManagedAppIdSchemaName,
collection: MsalCacheHelper.LinuxKeyRingDefaultCollection,
secretLabel: LinuxManagedAppIdSecretLabel,
attribute1: new KeyValuePair<string, string>("Product", "PnPPowerShell"),
attribute2: new KeyValuePair<string, string>("Name", name))
.Build();

return Storage.Create(properties);
}

private static void WriteLinuxAppIdEntry(string name, string appId)
{
var storage = CreateLinuxManagedAppIdStorage(name);
storage.VerifyPersistence();
storage.WriteData(Encoding.UTF8.GetBytes(appId));
}
Comment on lines +547 to +559
Comment on lines +547 to +559

private static string ReadLinuxAppIdEntry(string name)
{
try
{
var data = CreateLinuxManagedAppIdStorage(name).ReadData();
return data == null || data.Length == 0 ? null : Encoding.UTF8.GetString(data);
}
catch (MsalCachePersistenceException)
{
return null;
}
}

private static bool DeleteLinuxAppIdEntry(string name)
{
try
{
var storage = CreateLinuxManagedAppIdStorage(name);
var data = storage.ReadData();
if (data == null || data.Length == 0)
{
return false;
}

storage.Clear(false);
return true;
}
catch (MsalCachePersistenceException)
{
return false;
}
}

private static string GetSha256Hash(string value)
{
return Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(value))).ToLowerInvariant();
}

public static string SecureStringToString(SecureString value)
{
IntPtr valuePtr = IntPtr.Zero;
Expand Down
Loading