Skip to content

Commit e3c6d81

Browse files
Register MAR as default trusted repository
1 parent 73b90e3 commit e3c6d81

7 files changed

Lines changed: 196 additions & 6 deletions

File tree

src/code/RegisterPSResourceRepository.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo)
323323
return null;
324324
}
325325

326-
if (repo["Name"].ToString().Equals("PSGallery"))
326+
if (repo["Name"].ToString().Equals("PSGallery", StringComparison.OrdinalIgnoreCase))
327327
{
328328
WriteError(new ErrorRecord(
329329
new PSInvalidOperationException("Cannot register PSGallery with -Name parameter. Try: Register-PSResourceRepository -PSGallery"),
@@ -334,6 +334,17 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo)
334334
return null;
335335
}
336336

337+
if (repo["Name"].ToString().Equals("MAR", StringComparison.OrdinalIgnoreCase))
338+
{
339+
WriteError(new ErrorRecord(
340+
new PSInvalidOperationException("Cannot register MAR with -Name parameter. The MAR repository is automatically registered. Try: Reset-PSResourceRepository to restore default repositories."),
341+
"MARProvidedAsNameRepoPSet",
342+
ErrorCategory.InvalidArgument,
343+
this));
344+
345+
return null;
346+
}
347+
337348
if (!repo.ContainsKey("Uri") || repo["Uri"] == null || String.IsNullOrEmpty(repo["Uri"].ToString()))
338349
{
339350
WriteError(new ErrorRecord(

src/code/RepositorySettings.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ internal static class RepositorySettings
2626
// The repository store file's location is currently only at '%LOCALAPPDATA%\PSResourceGet' for the user account.
2727
private const string PSGalleryRepoName = "PSGallery";
2828
private const string PSGalleryRepoUri = "https://www.powershellgallery.com/api/v2";
29+
private const string MARRepoName = "MAR";
30+
private const string MARRepoUri = "https://mcr.microsoft.com";
2931
private const int DefaultPriority = 50;
32+
private const int MARDefaultPriority = 40;
3033
private const bool DefaultTrusted = false;
34+
private const bool MARDefaultTrusted = true;
3135
private const string RepositoryFileName = "PSResourceRepository.xml";
3236
private static readonly string RepositoryPath = Path.Combine(Environment.GetFolderPath(
3337
Environment.SpecialFolder.LocalApplicationData), "PSResourceGet");
@@ -63,6 +67,10 @@ public static void CheckRepositoryStore()
6367
// Add PSGallery to the newly created store
6468
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
6569
Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.V2, force: false);
70+
71+
// Add MAR to the newly created store
72+
Uri marUri = new Uri(MARRepoUri);
73+
Add(MARRepoName, marUri, MARDefaultPriority, MARDefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.ContainerRegistry, force: false);
6674
}
6775

6876
// Open file (which should exist now), if cannot/is corrupted then throw error
@@ -85,6 +93,12 @@ public static PSRepositoryInfo AddRepository(string repoName, Uri repoUri, int r
8593
return null;
8694
}
8795

96+
if (repoName.Equals("MAR", StringComparison.OrdinalIgnoreCase))
97+
{
98+
errorMsg = "Cannot register MAR with -Name parameter. The MAR repository is automatically registered. Try: Reset-PSResourceRepository to restore default repositories.";
99+
return null;
100+
}
101+
88102
return AddToRepositoryStore(repoName, repoUri, repoPriority, repoTrusted, apiVersion, repoCredentialInfo, repoCredentialProvider, force, cmdletPassedIn, out errorMsg);
89103
}
90104

@@ -187,6 +201,20 @@ public static PSRepositoryInfo UpdateRepositoryStore(string repoName, Uri repoUr
187201
return null;
188202
}
189203

204+
// check MAR Uri is not trying to be set
205+
if (repoName.Equals("MAR", StringComparison.OrdinalIgnoreCase) && repoUri != null)
206+
{
207+
errorMsg = "The MAR repository has a predefined Uri. Setting the -Uri parameter for this repository is not allowed. Please run 'Reset-PSResourceRepository' to restore default repositories.";
208+
return null;
209+
}
210+
211+
// check MAR CredentialInfo is not trying to be set
212+
if (repoName.Equals("MAR", StringComparison.OrdinalIgnoreCase) && repoCredentialInfo != null)
213+
{
214+
errorMsg = "Setting the -CredentialInfo parameter for MAR is not allowed. Run 'Reset-PSResourceRepository' to restore default repositories.";
215+
return null;
216+
}
217+
190218
// determine trusted value to pass in (true/false if set, null otherwise, hence the nullable bool variable)
191219
bool? _trustedNullable = isSet ? new bool?(repoTrusted) : new bool?();
192220

@@ -908,6 +936,10 @@ public static PSRepositoryInfo Reset(out string errorMsg)
908936
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
909937
PSRepositoryInfo psGalleryRepo = Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.V2, force: false);
910938

939+
// Add MAR to the newly created store
940+
Uri marUri = new Uri(MARRepoUri);
941+
Add(MARRepoName, marUri, MARDefaultPriority, MARDefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.ContainerRegistry, force: false);
942+
911943
// Clean up backup file on success
912944
if (!string.IsNullOrEmpty(backupFilePath) && File.Exists(backupFilePath))
913945
{

src/code/ResetPSResourceRepository.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Microsoft.PowerShell.PSResourceGet.Cmdlets
1010
/// <summary>
1111
/// The Reset-PSResourceRepository cmdlet resets the repository store by creating a new PSRepositories.xml file.
1212
/// This is useful when the repository store becomes corrupted.
13-
/// It will create a new repository store with only the PSGallery repository registered.
13+
/// It will create a new repository store with PSGallery and MAR repositories registered.
1414
/// </summary>
1515
[Cmdlet(VerbsCommon.Reset,
1616
"PSResourceRepository",
@@ -39,8 +39,8 @@ protected override void ProcessRecord()
3939
"PSResourceRepository.xml");
4040

4141
WriteVerbose($"Resetting repository store at: {repositoryStorePath}");
42-
43-
if (!ShouldProcess(repositoryStorePath, "Reset repository store and create new PSRepositories.xml file with PSGallery registered"))
42+
43+
if (!ShouldProcess(repositoryStorePath, "Reset repository store and create new PSRepositories.xml file with PSGallery and MAR registered"))
4444
{
4545
return;
4646
}
@@ -57,7 +57,7 @@ protected override void ProcessRecord()
5757
return;
5858
}
5959

60-
WriteVerbose("Repository store reset successfully. PSGallery has been registered.");
60+
WriteVerbose("Repository store reset successfully. PSGallery and MAR have been registered.");
6161

6262
if (PassThru)
6363
{

src/code/SetPSResourceRepository.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,11 @@ public SwitchParameter Trusted
110110
public object GetDynamicParameters()
111111
{
112112
PSRepositoryInfo repository = RepositorySettings.Read(new[] { Name }, out string[] _).FirstOrDefault();
113-
// Dynamic parameter '-CredentialProvider' should not appear for PSGallery, or any container registry repository.
113+
// Dynamic parameter '-CredentialProvider' should not appear for PSGallery, MAR, or any container registry repository.
114114
// It should also not appear when using the 'Repositories' parameter set.
115115
if (repository is not null &&
116116
(repository.Name.Equals("PSGallery", StringComparison.OrdinalIgnoreCase) ||
117+
repository.Name.Equals("MAR", StringComparison.OrdinalIgnoreCase) ||
117118
ParameterSetName.Equals(RepositoriesParameterSet) ||
118119
repository.IsContainerRegistry()))
119120
{

test/PSGetTestUtils.psm1

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ $script:IsCoreCLR = $PSVersionTable.ContainsKey('PSEdition') -and $PSVersionTabl
1919
$script:PSGalleryName = 'PSGallery'
2020
$script:PSGalleryLocation = 'https://www.powershellgallery.com/api/v2'
2121

22+
$script:MARName = 'MAR'
23+
$script:MARLocation = 'https://mcr.microsoft.com'
24+
2225
$script:NuGetGalleryName = 'NuGetGallery'
2326
$script:NuGetGalleryLocation = 'https://api.nuget.org/v3/index.json'
2427

@@ -153,6 +156,16 @@ function Get-PSGalleryName
153156
function Get-PSGalleryLocation {
154157
return $script:PSGalleryLocation
155158
}
159+
160+
function Get-MARName
161+
{
162+
return $script:MARName
163+
}
164+
165+
function Get-MARLocation {
166+
return $script:MARLocation
167+
}
168+
156169
function Get-NewTestDirs {
157170
Param(
158171
[string[]]
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
$modPath = "$psscriptroot/../PSGetTestUtils.psm1"
5+
Write-Verbose -Verbose -Message "PSGetTestUtils path: $modPath"
6+
Import-Module $modPath -Force -Verbose
7+
8+
Describe "Test MAR Repository Registration" -tags 'CI' {
9+
BeforeEach {
10+
$MARName = Get-MARName
11+
$MARUri = Get-MARLocation
12+
$PSGalleryName = Get-PSGalleryName
13+
$PSGalleryUri = Get-PSGalleryLocation
14+
Get-NewPSResourceRepositoryFile
15+
}
16+
AfterEach {
17+
Get-RevertPSResourceRepositoryFile
18+
}
19+
20+
Context "MAR is auto-registered with expected values" {
21+
It "MAR repository should be present with expected default values" {
22+
$res = Get-PSResourceRepository -Name $MARName
23+
$res | Should -Not -BeNullOrEmpty
24+
$res.Name | Should -Be $MARName
25+
$res.Uri | Should -Be "$MARUri/"
26+
$res.Trusted | Should -Be True
27+
$res.Priority | Should -Be 40
28+
$res.ApiVersion | Should -Be 'ContainerRegistry'
29+
}
30+
31+
It "MAR repository should have lower priority number (higher priority) than PSGallery" {
32+
$mar = Get-PSResourceRepository -Name $MARName
33+
$psGallery = Get-PSResourceRepository -Name $PSGalleryName
34+
$mar.Priority | Should -BeLessThan $psGallery.Priority
35+
}
36+
}
37+
38+
Context "MAR name protection" {
39+
It "should not allow registering MAR with -Name parameter" {
40+
{ Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
41+
}
42+
43+
It "should not allow registering MAR (case insensitive) with -Name parameter" {
44+
{ Register-PSResourceRepository -Name "mar" -Uri "https://mcr.microsoft.com" -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
45+
}
46+
47+
It "should not allow registering MAR with -Name parameter in hashtable" {
48+
Unregister-PSResourceRepository -Name $MARName
49+
$hashtable = @{Name = "MAR"; Uri = "https://mcr.microsoft.com"}
50+
Register-PSResourceRepository -Repository $hashtable -ErrorVariable err -ErrorAction SilentlyContinue
51+
$err.Count | Should -BeGreaterThan 0
52+
$err[0].FullyQualifiedErrorId | Should -BeExactly "MARProvidedAsNameRepoPSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
53+
}
54+
55+
It "should not allow setting Uri for MAR repository" {
56+
{ Set-PSResourceRepository -Name $MARName -Uri "https://example.com" -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.SetPSResourceRepository"
57+
}
58+
59+
It "should not allow setting CredentialInfo for MAR repository" {
60+
$randomSecret = [System.IO.Path]::GetRandomFileName()
61+
$credentialInfo = New-Object Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo ("testvault", $randomSecret)
62+
{ Set-PSResourceRepository -Name $MARName -CredentialInfo $credentialInfo -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.SetPSResourceRepository"
63+
}
64+
65+
It "should allow setting Trusted for MAR repository" {
66+
Set-PSResourceRepository -Name $MARName -Trusted:$false
67+
$res = Get-PSResourceRepository -Name $MARName
68+
$res.Trusted | Should -Be False
69+
}
70+
71+
It "should allow setting Priority for MAR repository" {
72+
Set-PSResourceRepository -Name $MARName -Priority 10
73+
$res = Get-PSResourceRepository -Name $MARName
74+
$res.Priority | Should -Be 10
75+
}
76+
}
77+
78+
Context "Reset repository store includes MAR" {
79+
It "Reset-PSResourceRepository should register MAR alongside PSGallery" {
80+
Reset-PSResourceRepository -Force
81+
$res = Get-PSResourceRepository -Name $MARName
82+
$res | Should -Not -BeNullOrEmpty
83+
$res.Name | Should -Be $MARName
84+
$res.Uri | Should -Be "$MARUri/"
85+
$res.Trusted | Should -Be True
86+
$res.Priority | Should -Be 40
87+
$res.ApiVersion | Should -Be 'ContainerRegistry'
88+
89+
$psGallery = Get-PSResourceRepository -Name $PSGalleryName
90+
$psGallery | Should -Not -BeNullOrEmpty
91+
}
92+
93+
It "Reset-PSResourceRepository should restore MAR after unregistration" {
94+
Unregister-PSResourceRepository -Name $MARName
95+
$res = Get-PSResourceRepository -Name $MARName -ErrorAction SilentlyContinue
96+
$res | Should -BeNullOrEmpty
97+
98+
Reset-PSResourceRepository -Force
99+
$res = Get-PSResourceRepository -Name $MARName
100+
$res | Should -Not -BeNullOrEmpty
101+
$res.Name | Should -Be $MARName
102+
$res.Uri | Should -Be "$MARUri/"
103+
$res.Trusted | Should -Be True
104+
$res.Priority | Should -Be 40
105+
}
106+
107+
It "Reset-PSResourceRepository should restore both PSGallery and MAR" {
108+
Unregister-PSResourceRepository -Name $MARName
109+
Unregister-PSResourceRepository -Name $PSGalleryName
110+
Reset-PSResourceRepository -Force
111+
112+
$mar = Get-PSResourceRepository -Name $MARName
113+
$mar | Should -Not -BeNullOrEmpty
114+
$mar.Priority | Should -Be 40
115+
$mar.Trusted | Should -Be True
116+
$mar.ApiVersion | Should -Be 'ContainerRegistry'
117+
118+
$psGallery = Get-PSResourceRepository -Name $PSGalleryName
119+
$psGallery | Should -Not -BeNullOrEmpty
120+
$psGallery.Priority | Should -Be 50
121+
}
122+
}
123+
124+
Context "MAR first due to higher priority" {
125+
It "Find-PSResource Az.Accounts module from MAR" {
126+
$res = Find-PSResource -Name "Az.Accounts"
127+
$res | Should -Not -BeNullOrEmpty
128+
$res.Name | Should -Be "Az.Accounts"
129+
$res.Repository | Should -Be $MARName
130+
}
131+
}
132+
}

test/testRepositories.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<configuration>
33
<Repository Name="PSGallery" Uri="https://www.powershellgallery.com/api/v2" Priority="50" Trusted="false" APIVersion="v2" />
4+
<Repository Name="MAR" Uri="https://mcr.microsoft.com" Priority="40" Trusted="true" APIVersion="ContainerRegistry" />
45
<Repository Name="NuGetGallery" Uri="https://api.nuget.org/v3/index.json" Priority="70" Trusted="true" APIVersion="v3" />
56
</configuration>

0 commit comments

Comments
 (0)