Skip to content

Commit 248ac35

Browse files
author
Scott Bommarito
authored
Add tool to apply organization tenant policy to a list of organizations (#6867)
1 parent 17328b8 commit 248ac35

4 files changed

Lines changed: 134 additions & 3 deletions

File tree

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Threading.Tasks;
9+
using Autofac;
10+
using Microsoft.Extensions.CommandLineUtils;
11+
using NuGet.Services.Entities;
12+
using NuGetGallery;
13+
using NuGetGallery.Security;
14+
15+
namespace GalleryTools.Commands
16+
{
17+
public static class ApplyTenantPolicyCommand
18+
{
19+
private const string UserListOption = "--path";
20+
private const string TenantIdOption = "--tenant";
21+
22+
public static void Configure(CommandLineApplication config)
23+
{
24+
config.Description = "Apply organization tenant policy";
25+
config.HelpOption("-? | -h | --help");
26+
27+
var userListOption = config.Option(
28+
$"-p | {UserListOption}",
29+
"A path to a list of organizations, one username per line.",
30+
CommandOptionType.SingleValue);
31+
32+
var tenantIdOption = config.Option(
33+
$"-t | {TenantIdOption}",
34+
"The tenant ID to restrict membership of each organization on the list to.",
35+
CommandOptionType.SingleValue);
36+
37+
config.OnExecute(() =>
38+
{
39+
return ExecuteAsync(userListOption, tenantIdOption).GetAwaiter().GetResult();
40+
});
41+
}
42+
43+
private static async Task<int> ExecuteAsync(
44+
CommandOption userListOption,
45+
CommandOption tenantIdOption)
46+
{
47+
if (!userListOption.HasValue())
48+
{
49+
Console.WriteLine($"The '{UserListOption}' parameter is required.");
50+
return 1;
51+
}
52+
53+
if (!tenantIdOption.HasValue())
54+
{
55+
Console.WriteLine($"The '{TenantIdOption}' parameter is required.");
56+
return 1;
57+
}
58+
59+
var builder = new ContainerBuilder();
60+
builder.RegisterAssemblyModules(typeof(DefaultDependenciesModule).Assembly);
61+
var container = builder.Build();
62+
var securityPolicyService = container.Resolve<SecurityPolicyService>();
63+
var userService = container.Resolve<UserService>();
64+
65+
var userListPath = userListOption.Value();
66+
Console.WriteLine($"Reading user list from {userListPath}");
67+
var usernames = ReadUserList(userListPath);
68+
Console.WriteLine($"Finished reading user list. Found {usernames.Count()} users");
69+
70+
var tenantId = tenantIdOption.Value();
71+
Console.WriteLine($"Restricting organization membership to tenant ID {tenantId}");
72+
73+
foreach (var username in usernames)
74+
{
75+
var user = userService.FindByUsername(username);
76+
if (user == null)
77+
{
78+
Console.WriteLine($"Could not find user with name {username}... skipping");
79+
continue;
80+
}
81+
82+
var organization = user as Organization;
83+
if (organization == null)
84+
{
85+
Console.WriteLine($"User with name {username} is not an organization...skipping");
86+
continue;
87+
}
88+
89+
Console.WriteLine($"Applying AAD tenant policy to organization with name {username}");
90+
try
91+
{
92+
var tenantPolicy = RequireOrganizationTenantPolicy.Create(tenantId);
93+
if (await securityPolicyService.SubscribeAsync(organization, tenantPolicy))
94+
{
95+
Console.WriteLine($"Successfully applied AAD tenant policy to organization with name {username}");
96+
}
97+
else
98+
{
99+
Console.WriteLine($"Organization with name {username} already has AAD tenant policy.");
100+
}
101+
}
102+
catch (Exception e)
103+
{
104+
Console.WriteLine($"Failed to apply AAD tenant policy to organization with name {username}: {e}");
105+
}
106+
}
107+
108+
return 0;
109+
}
110+
111+
private static IEnumerable<string> ReadUserList(string path)
112+
{
113+
var usernames = new HashSet<string>();
114+
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
115+
using (var reader = new StreamReader(fileStream))
116+
{
117+
string line;
118+
while ((line = reader.ReadLine()) != null)
119+
{
120+
if (usernames.Add(line))
121+
{
122+
// Each username in the list should be distinct, but we should also respect the ordering of the file.
123+
yield return line;
124+
}
125+
}
126+
}
127+
}
128+
}
129+
}

src/GalleryTools/Commands/ReflowCommand.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ public static class ReflowCommand
1818
{
1919
private const int DefaultBatch = 1;
2020
private const int DefaultSleepMultiplier = 60;
21-
private const string PackageListShortOption = "-p";
21+
private const string PackageListOption = "--path";
2222

2323
public static void Configure(CommandLineApplication config)
2424
{
2525
config.Description = "Bulk reflow many packages";
2626
config.HelpOption("-? | -h | --help");
2727

2828
var packageListOption = config.Option(
29-
$"{PackageListShortOption} | --path",
29+
$"-p | {PackageListOption}",
3030
"A path to a list of packages, one ID and version per line with a space in between.",
3131
CommandOptionType.SingleValue);
3232

@@ -53,7 +53,7 @@ private static async Task<int> ExecuteAsync(
5353
{
5454
if (!packageListOption.HasValue())
5555
{
56-
Console.WriteLine($"The '{PackageListShortOption}' parameter is required.");
56+
Console.WriteLine($"The '{PackageListOption}' parameter is required.");
5757
return 1;
5858
}
5959

src/GalleryTools/GalleryTools.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<ItemGroup>
4646
<Compile Include="Commands\BackfillRepositoryMetadataCommand.cs" />
4747
<Compile Include="Commands\HashCommand.cs" />
48+
<Compile Include="Commands\ApplyTenantPolicyCommand.cs" />
4849
<Compile Include="Commands\ReflowCommand.cs" />
4950
<Compile Include="Commands\UpdateIsLatestCommand.cs" />
5051
<Compile Include="Commands\VerifyApiKeyCommand.cs" />

src/GalleryTools/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public static int Main(params string[] args)
1616
commandLineApplication.HelpOption("-? | -h | --help");
1717
commandLineApplication.Command("hash", HashCommand.Configure);
1818
commandLineApplication.Command("reflow", ReflowCommand.Configure);
19+
commandLineApplication.Command("applyTenantPolicy", ApplyTenantPolicyCommand.Configure);
1920
commandLineApplication.Command("fillrepodata", BackfillRepositoryMetadataCommand.Configure);
2021
commandLineApplication.Command("verifyapikey", VerifyApiKeyCommand.Configure);
2122
commandLineApplication.Command("updateIsLatest", UpdateIsLatestCommand.Configure);

0 commit comments

Comments
 (0)