Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c9c472f
add support for credential persistence by having a soft dependency on…
Jun 18, 2021
63f3477
fix tests by comparing URL.LocalPaths
Aug 31, 2021
adf7617
Add new line
alerickson Sep 1, 2021
f4a267d
Update test/SetPSResourceRepository.Tests.ps1
alerickson Sep 1, 2021
1924def
Update .gitignore
alerickson Sep 1, 2021
f08ef0d
Update src/code/AuthenticationHelper.cs
alerickson Sep 1, 2021
d5474e4
Update src/code/RegisterPSResourceRepository.cs
alerickson Sep 1, 2021
15d8f63
Update test/testRepositoriesWithAuthentication.xml
alerickson Sep 1, 2021
4dada70
Merge branch 'master' of https://github.com/PowerShell/PowerShellGet …
Nov 19, 2021
f598313
rename Authentication parameter to CredentialInfo
Nov 22, 2021
f5a4cf0
replace type Hashtable with PSCredentialInfo for the CredentialInfo p…
Dec 31, 2021
ce16394
add save repository credential functionality and secret management er…
Jan 5, 2022
dcbbb5c
add PSRepositoryInfo to PSGet.Format.ps1xml to not display Credential…
Jan 5, 2022
5051876
Merge branch 'master' of https://github.com/PowerShell/PowerShellGet …
Jan 6, 2022
27d8a75
address outstanding merge conflicts
Jan 6, 2022
a955eff
change valid type to only PSCredential
Jan 6, 2022
1d28767
fix error id after parameter rename
Jan 6, 2022
c3d81dd
add tests for SecretManagement setup validation, fix error and warnin…
Jan 7, 2022
f7e7a1f
make style adjustments
Jan 13, 2022
37e402c
check for null values explicitly
Jan 13, 2022
343b820
check for errorid in tests
Jan 13, 2022
24e64c0
remove pester results file from gitignore, add new line
Jan 14, 2022
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
98 changes: 61 additions & 37 deletions src/PSGet.Format.ps1xml
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<Configuration>
<ViewDefinitions>
<View>
<Name>PSResourceInfo</Name>
<ViewSelectedBy>
<TypeName>Microsoft.PowerShell.PowerShellGet.UtilClasses.PSResourceInfo</TypeName>
</ViewSelectedBy>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>Name</Label></TableColumnHeader>
<TableColumnHeader><Label>Version</Label></TableColumnHeader>
<TableColumnHeader><Label>Prerelease</Label></TableColumnHeader>
<TableColumnHeader><Label>Repository</Label></TableColumnHeader>
<TableColumnHeader><Label>Description</Label></TableColumnHeader>
</TableHeaders>
<TableHeaders>
<TableColumnHeader><Label>Name</Label></TableColumnHeader>
<TableColumnHeader><Label>Version</Label></TableColumnHeader>
<TableColumnHeader><Label>Prerelease</Label></TableColumnHeader>
<TableColumnHeader><Label>Repository</Label></TableColumnHeader>
<TableColumnHeader><Label>Description</Label></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
Expand All @@ -23,42 +23,66 @@
<TableColumnItem><PropertyName>Repository</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Description</PropertyName></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>PSIncludedResourceInfoTable</Name>
<ViewSelectedBy>
<TypeName>Microsoft.PowerShell.PowerShellGet.UtilClasses.PSIncludedResourceInfo</TypeName>
<TypeName>Microsoft.PowerShell.PowerShellGet.UtilClasses.PSIncludedResourceInfo</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>Name</Label></TableColumnHeader>
<TableColumnHeader>
<Label>Version</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>Prerelease</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>ModuleName</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>Repository</Label>
</TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>Name</PropertyName></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.Version</ScriptBlock></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.PrereleaseLabel</ScriptBlock></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.Name</ScriptBlock></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.Repository</ScriptBlock></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
<TableHeaders>
<TableColumnHeader><Label>Name</Label></TableColumnHeader>
<TableColumnHeader>
<Label>Version</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>Prerelease</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>ModuleName</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>Repository</Label>
</TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>Name</PropertyName></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.Version</ScriptBlock></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.PrereleaseLabel</ScriptBlock></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.Name</ScriptBlock></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.Repository</ScriptBlock></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>PSRepositoryInfo</Name>
<ViewSelectedBy>
<TypeName>Microsoft.PowerShell.PowerShellGet.UtilClasses.PSRepositoryInfo</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>Name</Label></TableColumnHeader>
<TableColumnHeader><Label>Url</Label></TableColumnHeader>
<TableColumnHeader><Label>Trusted</Label></TableColumnHeader>
<TableColumnHeader><Label>Priority</Label></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>Name</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Url</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Trusted</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Priority</PropertyName></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
Expand Down
25 changes: 21 additions & 4 deletions src/code/FindHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Management.Automation;
using System.Net;
using System.Net.Http;
using System.Security;
using System.Threading;

using Dbg = System.Diagnostics.Debug;
Expand Down Expand Up @@ -139,7 +141,7 @@ public IEnumerable<PSResourceInfo> FindByResourceName(

// detect if Script repository needs to be added and/or Module repository needs to be skipped
Uri psGalleryScriptsUrl = new Uri("http://www.powershellgallery.com/api/v2/items/psscript/");
PSRepositoryInfo psGalleryScripts = new PSRepositoryInfo(_psGalleryScriptsRepoName, psGalleryScriptsUrl, repositoriesToSearch[i].Priority, false);
PSRepositoryInfo psGalleryScripts = new PSRepositoryInfo(_psGalleryScriptsRepoName, psGalleryScriptsUrl, repositoriesToSearch[i].Priority, trusted: false, credentialInfo: null);
if (_type == ResourceType.None)
{
_cmdletPassedIn.WriteVerbose("Null Type provided, so add PSGalleryScripts repository");
Expand All @@ -159,7 +161,7 @@ public IEnumerable<PSResourceInfo> FindByResourceName(

// detect if Script repository needs to be added and/or Module repository needs to be skipped
Uri poshTestGalleryScriptsUrl = new Uri("https://www.poshtestgallery.com/api/v2/items/psscript/");
PSRepositoryInfo poshTestGalleryScripts = new PSRepositoryInfo(_poshTestGalleryScriptsRepoName, poshTestGalleryScriptsUrl, repositoriesToSearch[i].Priority, false);
PSRepositoryInfo poshTestGalleryScripts = new PSRepositoryInfo(_poshTestGalleryScriptsRepoName, poshTestGalleryScriptsUrl, repositoriesToSearch[i].Priority, trusted: false, credentialInfo: null);
if (_type == ResourceType.None)
{
_cmdletPassedIn.WriteVerbose("Null Type provided, so add PoshTestGalleryScripts repository");
Expand All @@ -180,7 +182,8 @@ public IEnumerable<PSResourceInfo> FindByResourceName(
_cmdletPassedIn.WriteVerbose(string.Format("Searching in repository {0}", repositoriesToSearch[i].Name));
foreach (var pkg in SearchFromRepository(
repositoryName: repositoriesToSearch[i].Name,
repositoryUrl: repositoriesToSearch[i].Url))
repositoryUrl: repositoriesToSearch[i].Url,
repositoryCredentialInfo: repositoriesToSearch[i].CredentialInfo))
{
yield return pkg;
}
Expand All @@ -193,7 +196,8 @@ public IEnumerable<PSResourceInfo> FindByResourceName(

private IEnumerable<PSResourceInfo> SearchFromRepository(
string repositoryName,
Uri repositoryUrl)
Uri repositoryUrl,
PSCredentialInfo repositoryCredentialInfo)
{
PackageSearchResource resourceSearch;
PackageMetadataResource resourceMetadata;
Expand Down Expand Up @@ -229,12 +233,25 @@ private IEnumerable<PSResourceInfo> SearchFromRepository(

// HTTP, HTTPS, FTP Uri schemes (only other Uri schemes allowed by RepositorySettings.Read() API)
PackageSource source = new PackageSource(repositoryUrl.ToString());

// Explicitly passed in Credential takes precedence over repository CredentialInfo
if (_credential != null)
{
string password = new NetworkCredential(string.Empty, _credential.Password).Password;
source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), _credential.UserName, password, true, null);
_cmdletPassedIn.WriteVerbose("credential successfully set for repository: " + repositoryName);
}
else if (repositoryCredentialInfo != null)
{
PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement(
repositoryName,
repositoryCredentialInfo,
_cmdletPassedIn);

string password = new NetworkCredential(string.Empty, repoCredential.Password).Password;
source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), repoCredential.UserName, password, true, null);
_cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + repositoryName);
}

// GetCoreV3() API is able to handle V2 and V3 repository endpoints
var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider);
Expand Down
16 changes: 16 additions & 0 deletions src/code/InstallHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,9 @@ private List<PSResourceInfo> ProcessRepositories(

List<PSResourceInfo> pkgsInstalled = InstallPackage(
pkgsFromRepoToInstall,
repoName,
repo.Url.AbsoluteUri,
repo.CredentialInfo,
credential,
isLocalRepo);

Expand Down Expand Up @@ -303,7 +305,9 @@ private IEnumerable<PSResourceInfo> FilterByInstalledPkgs(IEnumerable<PSResource

private List<PSResourceInfo> InstallPackage(
IEnumerable<PSResourceInfo> pkgsToInstall, // those found to be required to be installed (includes Dependency packages as well)
string repoName,
string repoUrl,
PSCredentialInfo repoCredentialInfo,
PSCredential credential,
bool isLocalRepo)
{
Expand Down Expand Up @@ -398,11 +402,23 @@ private List<PSResourceInfo> InstallPackage(
/* Download from a non-local repository */
// Set up NuGet API resource for download
PackageSource source = new PackageSource(repoUrl);

// Explicitly passed in Credential takes precedence over repository CredentialInfo
if (credential != null)
{
string password = new NetworkCredential(string.Empty, credential.Password).Password;
source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, credential.UserName, password, true, null);
}
else if (repoCredentialInfo != null)
{
PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement(
repoName,
repoCredentialInfo,
_cmdletPassedIn);

string password = new NetworkCredential(string.Empty, repoCredential.Password).Password;
source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repoCredential.UserName, password, true, null);
}
var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider);
SourceRepository repository = new SourceRepository(source, provider);

Expand Down
107 changes: 107 additions & 0 deletions src/code/PSCredentialInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Management.Automation;

namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
{
/// <summary>
/// This class contains information for a repository's authentication credential.
/// </summary>
public sealed class PSCredentialInfo
{
#region Constructor

/// <summary>
/// Initializes a new instance of the PSCredentialInfo class with
/// vaultName and secretName of type string, and
/// (optionally) credential of type PSCredential.
/// </summary>
/// <param name="vaultName"></param>
/// <param name="secretName"></param>
/// <param name="credential"></param>
public PSCredentialInfo(string vaultName, string secretName, PSCredential credential = null)
{
VaultName = vaultName;
SecretName = secretName;
Credential = credential;
}

/// <summary>
/// Initializes a new instance of the PSCredentialInfo class with
/// vaultName and secretName of type string, and
/// (optionally) credential of type PSCredential from a PSObject.
/// </summary>
/// <param name="psObject"></param>
public PSCredentialInfo(PSObject psObject)
{
if (psObject == null)
{
throw new ArgumentNullException(nameof(psObject));
}

VaultName = (string) psObject.Properties[PSCredentialInfo.VaultNameAttribute]?.Value;
SecretName = (string) psObject.Properties[PSCredentialInfo.SecretNameAttribute]?.Value;
Credential = (PSCredential) psObject.Properties[PSCredentialInfo.CredentialAttribute]?.Value;
}

#endregion

#region Members

private string _vaultName;
/// <summary>
/// the Name of the SecretManagement Vault
/// </summary>
public string VaultName {
get
{
return _vaultName;
}

private set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException($"Invalid CredentialInfo, {PSCredentialInfo.VaultNameAttribute} must be a non-empty string");
}

_vaultName = value;
}
}

private string _secretName;
/// <summary>
/// the Name of the Secret
/// </summary>
public string SecretName {
get
{
return _secretName;
}

private set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException($"Invalid CredentialInfo, {PSCredentialInfo.SecretNameAttribute} must be a non-empty string");
}

_secretName = value;
}
}

/// <summary>
/// optional Credential object to save in a SecretManagement Vault
/// for authenticating to repositories
/// </summary>
public PSCredential Credential { get; private set; }

internal static readonly string VaultNameAttribute = nameof(VaultName);
internal static readonly string SecretNameAttribute = nameof(SecretName);
internal static readonly string CredentialAttribute = nameof(Credential);

#endregion
}
}
9 changes: 8 additions & 1 deletion src/code/PSRepositoryInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ public sealed class PSRepositoryInfo
{
#region Constructor

public PSRepositoryInfo(string name, Uri url, int priority, bool trusted)
public PSRepositoryInfo(string name, Uri url, int priority, bool trusted, PSCredentialInfo credentialInfo)
{
Name = name;
Url = url;
Priority = priority;
Trusted = trusted;
CredentialInfo = credentialInfo;
}

#endregion
Expand All @@ -37,6 +38,7 @@ public PSRepositoryInfo(string name, Uri url, int priority, bool trusted)

/// <summary>
/// whether the repository is trusted
/// </summary>
public bool Trusted { get; }

/// <summary>
Expand All @@ -45,6 +47,11 @@ public PSRepositoryInfo(string name, Uri url, int priority, bool trusted)
[ValidateRange(0, 50)]
public int Priority { get; }

/// <summary>
/// the credential information for repository authentication
/// </summary>
public PSCredentialInfo CredentialInfo { get; }

#endregion
}
}
Loading