Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Credentials Table #1616

Merged
merged 18 commits into from
Oct 4, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
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
34 changes: 34 additions & 0 deletions src/NuGetGallery.Core/Entities/Credential.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;

namespace NuGetGallery
{
public class Credential : IEntity
{
public Credential() { }

public Credential(string type, string value)
{
Type = type;
Value = value;
}

public int Key { get; set; }

[Required]
public int UserKey { get; set; }

[Required]
[StringLength(maximumLength: 64)]
public string Type { get; set; }

[Required]
[StringLength(maximumLength: 256)]
public string Value { get; set; }

public virtual User User { get; set; }
}
}
6 changes: 6 additions & 0 deletions src/NuGetGallery.Core/Entities/EntitiesContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ public void DeleteOnCommit<T>(T entity) where T : class
#pragma warning disable 618 // TODO: remove Package.Authors completely once prodution services definitely no longer need it
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Credential>()
.HasKey(c => c.Key)
.HasRequired(c => c.User)
.WithMany(u => u.Credentials)
.HasForeignKey(c => c.UserKey);

modelBuilder.Entity<PackageLicenseReport>()
.HasKey(r => r.Key)
.HasMany(r => r.Licenses)
Expand Down
4 changes: 3 additions & 1 deletion src/NuGetGallery.Core/Entities/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public User() : this(null)

public User(string username)
{
Credentials = new List<Credential>();
Username = username;
}

Expand Down Expand Up @@ -61,6 +62,7 @@ public string LastSavedEmailAddress
return UnconfirmedEmailAddress ?? EmailAddress;
}
}
public virtual ICollection<Credential> Credentials { get; set; }

public void ConfirmEmailAddress()
{
Expand Down Expand Up @@ -94,4 +96,4 @@ public void UpdateEmailAddress(string newEmailAddress, Func<string> generateToke
EmailConfirmationToken = generateToken();
}
}
}
}
1 change: 1 addition & 0 deletions src/NuGetGallery.Core/NuGetGallery.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<Compile Include="..\CommonAssemblyInfo.cs">
<Link>Properties\CommonAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Entities\Credential.cs" />
<Compile Include="Entities\CuratedFeed.cs" />
<Compile Include="Entities\CuratedPackage.cs" />
<Compile Include="Entities\EmailMessage.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using Dapper;
using Microsoft.SqlServer.Server;

namespace NuGetGallery.Operations.Infrastructure
{
public class TableValuedParameter : SqlMapper.IDynamicParameters
{
public string Name { get; private set; }
public string TableType { get; private set; }
public DataTable TableValue { get; private set; }

public TableValuedParameter(string name, string tableType, DataTable tableValue)
{
Name = name;
TableType = tableType;
TableValue = tableValue;
}

public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
var sqlCommand = (SqlCommand)command;
var param = new SqlParameter(Name, TableValue)
{
TypeName = TableType,
SqlDbType = SqlDbType.Structured,
Direction = ParameterDirection.Input
};
sqlCommand.Parameters.Add(param);
}
}
}
5 changes: 5 additions & 0 deletions src/NuGetGallery.Operations/NuGetGallery.Operations.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\AnglicanGeek.DbExecutor.0.1.2\lib\net40\AnglicanGeek.DbExecutor.dll</HintPath>
</Reference>
<Reference Include="Dapper">
<HintPath>..\..\packages\Dapper.1.13\lib\net45\Dapper.dll</HintPath>
</Reference>
<Reference Include="EntityFramework">
<HintPath>..\..\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll</HintPath>
</Reference>
Expand Down Expand Up @@ -151,6 +154,7 @@
<Compile Include="Infrastructure\LogLevelConverter.cs" />
<Compile Include="Infrastructure\SnazzyConsoleTarget.cs" />
<Compile Include="Infrastructure\SqlDbExecutorFactory.cs" />
<Compile Include="Infrastructure\TableValuedParameter.cs" />
<Compile Include="LoggerExtensions.cs" />
<Compile Include="Model\AuditEnvironment.cs" />
<Compile Include="Model\AuditRecord.cs" />
Expand Down Expand Up @@ -212,6 +216,7 @@
<Compile Include="Tasks\DataManagement\DeleteDuplicatePackageVersionsTask.cs" />
<Compile Include="Tasks\DataManagement\NormalizePackageFilesTask.cs" />
<Compile Include="Tasks\DataManagement\NormalizePackageVersionsTask.cs" />
<Compile Include="Tasks\DataManagement\PopulateCredentialsTask.cs" />
<Compile Include="Tasks\ExecuteAggregateStatisticsTask.cs" />
<Compile Include="Tasks\CopyExternalPackagesTask.cs" />
<Compile Include="Tasks\CreateWarehouseReportsTask.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,8 @@ FROM Packages p

DataTable output;
int count = 0;
try
WithTableType(c, "Temp_NormalizePackageVersionsInputType", "PackageKey int, NormalizedVersion nvarchar(64)", () =>
{
// Create a table-type for the query
db.Execute(@"
IF EXISTS (
SELECT *
FROM sys.types
WHERE is_table_type = 1
AND name = 'Temp_NormalizePackageVersionsInputType'
)
BEGIN
DROP TYPE Temp_NormalizePackageVersionsInputType
END
CREATE TYPE Temp_NormalizePackageVersionsInputType AS TABLE(PackageKey int, NormalizedVersion nvarchar(64))");

// Build a table to hold the new data
var updateTable = new DataTable();
updateTable.Columns.Add(new DataColumn("PackageKey", typeof(int)));
Expand All @@ -83,7 +70,7 @@ DROP TYPE Temp_NormalizePackageVersionsInputType
Log.Trace("Updating Database...");
var reader = cmd.ExecuteReader();
Log.Trace("Database Update Complete");

// Load the results into a datatable and render them
output = new DataTable();
output.Load(reader);
Expand All @@ -98,18 +85,7 @@ DROP TYPE Temp_NormalizePackageVersionsInputType
}
}
Log.Info("Updated {0} packages", count);
}
finally
{
// Clean up the type
db.Execute(@"
IF EXISTS (
SELECT *
FROM sys.types
WHERE is_table_type = 1
AND name = 'Temp_NormalizePackageVersionsInputType'
) DROP TYPE Temp_NormalizePackageVersionsInputType");
}
});
});
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using NuGetGallery.Operations.Infrastructure;

namespace NuGetGallery.Operations.Tasks.DataManagement
{
[Command("populatecredentials", "Populates the Credentials table for users who are missing data", AltName = "pc")]
public class PopulateCredentialsTask : DatabaseTask
{
private const string WhatIfQuery = "BEGIN TRAN\r\n" + Query + "\r\nROLLBACK TRAN";
private const string CommitQuery = "BEGIN TRAN\r\n" + Query + "\r\nCOMMIT TRAN";

private const string Query = @"
DECLARE @results TABLE(
Action nchar(10),
UserKey int,
Type nvarchar(64),
Value nvarchar(256)
)

MERGE INTO Credentials dest
USING @creds src
ON src.UserKey = dest.UserKey AND src.Type = dest.Type
WHEN NOT MATCHED THEN
INSERT(UserKey, Type, Value)
VALUES(src.UserKey, src.Type, src.Value)
OUTPUT
$action AS 'Action',
inserted.UserKey,
inserted.Type,
inserted.Value
INTO @results;

SELECT COUNT(*) FROM @results
";

private Dictionary<string, string> _hashAlgorithmToCredType = new Dictionary<string, string>() {
{Constants.PBKDF2HashAlgorithmId, CredentialTypes.Password.Pbkdf2},
{Constants.Sha1HashAlgorithmId, CredentialTypes.Password.Sha1}
};

public override void ExecuteCommand()
{
WithConnection(c =>
{
// Get user credentials
var users = c.Query("SELECT [Key], HashedPassword, PasswordHashAlgorithm, ApiKey FROM Users");

// Build a table
var dt = new DataTable();
dt.Columns.Add("UserKey", typeof(int));
dt.Columns.Add("Type", typeof(string));
dt.Columns.Add("Value", typeof(string));
foreach (var user in users)
{
var row = dt.NewRow();
row.SetField("UserKey", (int)user.Key);
row.SetField("Type", CredentialTypes.ApiKeyV1);
row.SetField("Value", ((Guid)user.ApiKey).ToString().ToLowerInvariant());
dt.Rows.Add(row);


string passwordCredType;
if (!_hashAlgorithmToCredType.TryGetValue(user.PasswordHashAlgorithm, out passwordCredType))
{
Log.Error("Unknown Hash Algorithm: {0}", user.PasswordHashAlgorithm);
}
else
{
row = dt.NewRow();
row.SetField("UserKey", (int)user.Key);
row.SetField("Type", passwordCredType);
row.SetField("Value", (string)user.HashedPassword);
dt.Rows.Add(row);
}
}

WithTableType(c, "Temp_PopulateCredentialsInputType", "UserKey int, Type nvarchar(64), Value nvarchar(256)", () =>
{
// Update the DB
var updatedRowCount = c.Execute(
WhatIf ? WhatIfQuery : CommitQuery,
new TableValuedParameter("@creds", "Temp_PopulateCredentialsInputType", dt));

Log.Info("Inserted {0} credential records", updatedRowCount);
});
});
}
}
}
37 changes: 37 additions & 0 deletions src/NuGetGallery.Operations/Tasks/TaskBases.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.WindowsAzure.Storage.Blob;
using NuGetGallery.Operations.Common;
using NuGetGallery.Infrastructure;
using Dapper;

namespace NuGetGallery.Operations
{
Expand Down Expand Up @@ -135,6 +136,42 @@ protected bool WithMasterConnection(Func<SqlConnection, SqlExecutor, bool> act)
}
}

protected void WithTableType(SqlConnection connection, string name, string definition, Action act)
{
try
{
// Create the table-valued parameter type
connection.Execute(String.Format(@"
IF EXISTS (
SELECT *
FROM sys.types
WHERE is_table_type = 1
AND name = '{0}'
)
BEGIN
DROP TYPE {0}
END
CREATE TYPE {0} AS TABLE ({1})", name, definition));

act();
}
finally
{
// Clean up the table-valued parameter type
connection.Execute(String.Format(@"
IF EXISTS (
SELECT *
FROM sys.types
WHERE is_table_type = 1
AND name = '{0}'
)
BEGIN
DROP TYPE {0}
END", name));
}

}

protected SqlConnection OpenConnection()
{
var c = new SqlConnection(ConnectionString.ConnectionString);
Expand Down
1 change: 1 addition & 0 deletions src/NuGetGallery.Operations/packages.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AnglicanGeek.DbExecutor" version="0.1.2" targetFramework="net45" />
<package id="Dapper" version="1.13" targetFramework="net45" />
<package id="EntityFramework" version="5.0.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="2.0.20715.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.20710.0" targetFramework="net45" />
Expand Down
4 changes: 4 additions & 0 deletions src/NuGetGallery/App_Start/ContainerBindings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ public override void Load()
.To<EntityRepository<PackageStatistics>>()
.InRequestScope();

Bind<IEntityRepository<Credential>>()
.To<EntityRepository<Credential>>()
.InRequestScope();

Bind<ICuratedFeedService>()
.To<CuratedFeedService>()
.InRequestScope();
Expand Down
15 changes: 13 additions & 2 deletions src/NuGetGallery/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public static class Constants

public static readonly string ReturnUrlViewDataKey = "ReturnUrl";

public const string UrlValidationRegEx = @"(https?):\/\/[^ ""]+$";
public const string UrlValidationErrorMessage = "This doesn't appear to be a valid HTTP/HTTPS URL";

public static class ContentNames
{
public static readonly string FrontPageAnnouncement = "FrontPage-Announcement";
Expand All @@ -42,8 +45,16 @@ public static class ContentNames
public static readonly string TermsOfUse = "Terms-Of-Use";
public static readonly string PrivacyPolicy = "Privacy-Policy";
}
}

public const string UrlValidationRegEx = @"(https?):\/\/[^ ""]+$";
public const string UrlValidationErrorMessage = "This doesn't appear to be a valid HTTP/HTTPS URL";
public static class CredentialTypes
{
public static class Password
{
public static readonly string Prefix = "password.";
public static readonly string Pbkdf2 = Prefix + "pbkdf2";
public static readonly string Sha1 = Prefix + "sha1";
}
public static readonly string ApiKeyV1 = "apikey.v1";
}
}
Loading