Skip to content

Commit

Permalink
Tool to hash active api key credentials (#5416)
Browse files Browse the repository at this point in the history
* Tool to hash active api key credentials

* Added batching

* PR comments

* Bug fix

* PR comments
  • Loading branch information
skofman1 committed Feb 7, 2018
1 parent cb23f82 commit bfee0dc
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 2 deletions.
16 changes: 14 additions & 2 deletions NuGetGallery.sln
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio 15
VisualStudioVersion = 15.0.27323.2
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{96E4AFF8-D3A1-4102-ADCF-05F186F916A9}"
ProjectSection(SolutionItems) = preProject
Expand All @@ -24,6 +24,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0. Shared", "0. Shared", "{
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGet.Services.Search.Client", "src\NuGet.Services.Search.Client\NuGet.Services.Search.Client.csproj", "{6931C2EE-E081-4518-9798-D34D83B35BF6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4. Tools", "4. Tools", "{2204C510-A559-4ED7-9590-FDC09093575B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GalleryTools", "src\GalleryTools\GalleryTools.csproj", "{C5849063-8CDC-4561-BA5C-7D97BD905DC3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -50,6 +54,10 @@ Global
{6931C2EE-E081-4518-9798-D34D83B35BF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6931C2EE-E081-4518-9798-D34D83B35BF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6931C2EE-E081-4518-9798-D34D83B35BF6}.Release|Any CPU.Build.0 = Release|Any CPU
{C5849063-8CDC-4561-BA5C-7D97BD905DC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5849063-8CDC-4561-BA5C-7D97BD905DC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5849063-8CDC-4561-BA5C-7D97BD905DC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5849063-8CDC-4561-BA5C-7D97BD905DC3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -60,5 +68,9 @@ Global
{097B2CDD-9623-4C34-93C2-D373D51F5B4E} = {155100FF-524B-4CAF-93C6-A57478B3DBAD}
{8AC9E39E-366C-47E5-80AE-38E71CD31386} = {39E54EC3-CBAA-453A-BE64-748FE1559A58}
{6931C2EE-E081-4518-9798-D34D83B35BF6} = {05998089-58F5-4A84-8C11-C5C6244A6F89}
{C5849063-8CDC-4561-BA5C-7D97BD905DC3} = {2204C510-A559-4ED7-9590-FDC09093575B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {064A3BDE-5203-4AD6-A6C9-5CF08301EC8F}
EndGlobalSection
EndGlobal
88 changes: 88 additions & 0 deletions src/GalleryTools/App.config
@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
</startup>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb"/>
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer"/>
</providers>
</entityFramework>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.5.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="EntityFramework" publicKeyToken="b77a5c561934e089" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="MonAgentListener" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-33.1.0.0" newVersion="33.1.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.6.5.0" newVersion="5.6.5.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.6.5.0" newVersion="5.6.5.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.6.5.0" newVersion="5.6.5.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.13.4.878" newVersion="3.13.4.878"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Clients.ActiveDirectory" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.13.4.878" newVersion="3.13.4.878"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.6.5.0" newVersion="5.6.5.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
74 changes: 74 additions & 0 deletions src/GalleryTools/GalleryTools.csproj
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C5849063-8CDC-4561-BA5C-7D97BD905DC3}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>GalleryTools</RootNamespace>
<AssemblyName>GalleryTools</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
</Reference>
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.CommandLineUtils, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Extensions.CommandLineUtils.1.1.1\lib\net451\Microsoft.Extensions.CommandLineUtils.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NuGetGallery.Core\NuGetGallery.Core.csproj">
<Project>{097b2cdd-9623-4c34-93c2-d373d51f5b4e}</Project>
<Name>NuGetGallery.Core</Name>
</ProjectReference>
<ProjectReference Include="..\NuGetGallery\NuGetGallery.csproj">
<Project>{1dacf781-5cd0-4123-8bac-cd385d864be5}</Project>
<Name>NuGetGallery</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
116 changes: 116 additions & 0 deletions src/GalleryTools/Program.cs
@@ -0,0 +1,116 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.Extensions.CommandLineUtils;
using NuGetGallery;
using NuGetGallery.Infrastructure.Authentication;

namespace GalleryTools
{
class Program
{
const int BatchSize = 100;

public static void Main(params string[] args)
{
var commandLineApplication = new CommandLineApplication();
commandLineApplication.HelpOption("-? | -h | --help");

var hash = commandLineApplication.Command("hash", config =>
{
config.Description = "Hash V1/V2 ApiKeys in Gallery DB";
CommandOption connectionString = config.Option("-db", "DB connection string. User should have write permission.", CommandOptionType.SingleValue);
CommandOption whatIf = config.Option("-w | --whatif", "Don't apply changes to DB.", CommandOptionType.NoValue);
config.HelpOption("-? | -h | --help");
config.OnExecute(() =>
{
Hash(connectionString.Value(), whatIf.HasValue()).GetAwaiter().GetResult();
return 0;
});
});

try
{
commandLineApplication.Execute(args);
}
catch (Exception e)
{
Console.WriteLine($"Error: {e}");
}
}

private static async Task Hash(string connectionString, bool whatIf)
{
using (var context = new EntitiesContext(connectionString, readOnly: whatIf))
{
var allCredentials = (IQueryable<Credential>)context.Set<Credential>();

// Get all V1/V2 credentials that are active:
// V1 credentials that have no expiration date, but were used in the last year
// V1/V2 credentials that have a future expiration date

var validLastUsed = DateTime.UtcNow - TimeSpan.FromDays(365);
Expression<Func<Credential, bool>> predicate = x => (x.Type == CredentialTypes.ApiKey.V1 || x.Type == CredentialTypes.ApiKey.V2) &&
((x.Expires == null && x.LastUsed != null && x.LastUsed > validLastUsed) ||
(x.Expires != null && x.Expires > DateTime.UtcNow));

var activeCredentialsCount = allCredentials.Count(predicate);

Console.WriteLine($"Found {activeCredentialsCount} active V1/V2 ApiKeys.");

IList<Credential> batch;
int batchNumber = 1;
bool failures = false;

do
{
batch = allCredentials.Where(predicate).Take(BatchSize).ToList();

if (batch.Count > 0)
{
Console.WriteLine($"Hashing batch {batchNumber}/{Math.Ceiling((double)activeCredentialsCount / (double)BatchSize)}. Batch size: {batch.Count}...");

foreach (var v1v2ApiKey in batch)
{
ApiKeyV3 hashedApiKey = null;

try
{
hashedApiKey = ApiKeyV3.CreateFromV1V2ApiKey(v1v2ApiKey.Value);

v1v2ApiKey.Type = CredentialTypes.ApiKey.V3;
v1v2ApiKey.Value = hashedApiKey.HashedApiKey;
}
catch (ArgumentException e)
{
failures = true;
Console.WriteLine($"Failed to hash key: {v1v2ApiKey.Key} Type: {v1v2ApiKey.Type} Old value: {v1v2ApiKey.Value}. Reason: {e}");
}
}

if (!failures && !whatIf)
{
var stopwatch = Stopwatch.StartNew();
Console.WriteLine($"Saving batch {batchNumber} to DB...");
await context.SaveChangesAsync();
Console.WriteLine($"Saved changes to DB. Took: {stopwatch.Elapsed.TotalSeconds} seconds");
}
else
{
Console.WriteLine("Skipping DB save.");
}

batchNumber++;
}
}
while (batch.Count > 0);
}
}
}
}
33 changes: 33 additions & 0 deletions src/GalleryTools/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GalleryTools")]
[assembly: AssemblyCompany(".NET Foundation")]
[assembly: AssemblyProduct("NuGet Services")]
[assembly: AssemblyCopyright("\x00a9 .NET Foundation. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]


#if !PORTABLE
[assembly: ComVisible(false)]
#endif

#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif

[assembly: CLSCompliant(false)]
[assembly: NeutralResourcesLanguage("en-us")]

[assembly: AssemblyMetadata("RepositoryUrl", "https://www.github.com/NuGet/NuGetGallery")]
5 changes: 5 additions & 0 deletions src/GalleryTools/packages.config
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EntityFramework" version="6.2.0" targetFramework="net46" />
<package id="Microsoft.Extensions.CommandLineUtils" version="1.1.1" targetFramework="net46" />
</packages>

0 comments on commit bfee0dc

Please sign in to comment.