hacky auth working

This commit is contained in:
Tom Gardham-Pallister
2017-02-24 19:52:48 +00:00
parent bd07af6926
commit 112a9c303e
19 changed files with 448 additions and 94 deletions

View File

@ -139,8 +139,7 @@ namespace Ocelot.AcceptanceTests
};
s.AddOcelotOutputCaching(settings);
s.AddOcelotFileConfiguration(configuration);
s.AddOcelot();
s.AddOcelot(configuration);
})
.ConfigureLogging(l =>
{

View File

@ -235,7 +235,7 @@ namespace Ocelot.IntegrationTests
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("scope", "admin"),
new KeyValuePair<string, string>("username", "admin"),
new KeyValuePair<string, string>("password", "admin"),
new KeyValuePair<string, string>("password", "secret"),
new KeyValuePair<string, string>("grant_type", "password")
};
var content = new FormUrlEncodedContent(formData);

View File

@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CacheManager.Core;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
@ -15,7 +18,7 @@ namespace Ocelot.ManualTest
{
public class Startup
{
private IdentityServerConfiguration _identityServerConfig;
private IIdentityServerConfiguration _identityServerConfig;
public Startup(IHostingEnvironment env)
{
@ -27,9 +30,6 @@ namespace Ocelot.ManualTest
.AddEnvironmentVariables();
Configuration = builder.Build();
var identityServerConfigProvider = new HardCodedIdentityServerConfigurationProvider();
_identityServerConfig = identityServerConfigProvider.Get();
}
public IConfigurationRoot Configuration { get; }
@ -46,15 +46,36 @@ namespace Ocelot.ManualTest
};
services.AddOcelotOutputCaching(settings);
services.AddOcelotFileConfiguration(Configuration);
services.AddOcelot(_identityServerConfig);
var username = Environment.GetEnvironmentVariable("OCELOT_USERNAME");
var hash = Environment.GetEnvironmentVariable("OCELOT_HASH");
var salt = Environment.GetEnvironmentVariable("OCELOT_SALT");
_identityServerConfig = new IdentityServerConfiguration(
"admin",
false,
SupportedTokens.Both,
"secret",
new List<string> {"admin", "openid", "offline_access"},
"Ocelot Administration",
true,
GrantTypes.ResourceOwnerPassword,
AccessTokenType.Jwt,
false,
new List<User>
{
new User("admin", username, hash, salt)
}
);
services.AddOcelot(Configuration, _identityServerConfig);
}
public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
await app.UseOcelot(_identityServerConfig);
await app.UseOcelot();
}
}
}

View File

@ -14,7 +14,8 @@
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
"Microsoft.NETCore.App": "1.1.0",
"Consul": "0.7.2.1",
"Polly": "5.0.3"
"Polly": "5.0.3",
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "1.1.0"
},
"tools": {

View File

@ -0,0 +1,33 @@
using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Shouldly;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class HashCreationTests
{
[Fact]
public void should_create_hash_and_salt()
{
var password = "secret";
var salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
var storedSalt = Convert.ToBase64String(salt);
var storedHash = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
salt: salt,
prf: KeyDerivationPrf.HMACSHA256,
iterationCount: 10000,
numBytesRequested: 256 / 8));
}
}
}

View File

@ -0,0 +1,76 @@
using Ocelot.Configuration.Authentication;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class HashMatcherTests
{
private string _password;
private string _hash;
private string _salt;
private bool _result;
private HashMatcher _hashMatcher;
public HashMatcherTests()
{
_hashMatcher = new HashMatcher();
}
[Fact]
public void should_match_hash()
{
var hash = "kE/mxd1hO9h9Sl2VhGhwJUd9xZEv4NP6qXoN39nIqM4=";
var salt = "zzWITpnDximUNKYLiUam/w==";
var password = "secret";
this.Given(x => GivenThePassword(password))
.And(x => GivenTheHash(hash))
.And(x => GivenTheSalt(salt))
.When(x => WhenIMatch())
.Then(x => ThenTheResultIs(true))
.BDDfy();
}
[Fact]
public void should_not_match_hash()
{
var hash = "kE/mxd1hO9h9Sl2VhGhwJUd9xZEv4NP6qXoN39nIqM4=";
var salt = "zzWITpnDximUNKYLiUam/w==";
var password = "secret1";
this.Given(x => GivenThePassword(password))
.And(x => GivenTheHash(hash))
.And(x => GivenTheSalt(salt))
.When(x => WhenIMatch())
.Then(x => ThenTheResultIs(false))
.BDDfy();
}
private void GivenThePassword(string password)
{
_password = password;
}
private void GivenTheHash(string hash)
{
_hash = hash;
}
private void GivenTheSalt(string salt)
{
_salt = salt;
}
private void WhenIMatch()
{
_result = _hashMatcher.Match(_password, _salt, _hash);
}
private void ThenTheResultIs(bool expected)
{
_result.ShouldBe(expected);
}
}
}

View File

@ -0,0 +1,117 @@
using Ocelot.Configuration.Authentication;
using Xunit;
using Shouldly;
using TestStack.BDDfy;
using Moq;
using IdentityServer4.Validation;
using Ocelot.Configuration.Provider;
using System.Collections.Generic;
namespace Ocelot.UnitTests.Configuration
{
public class OcelotResourceOwnerPasswordValidatorTests
{
private OcelotResourceOwnerPasswordValidator _validator;
private Mock<IHashMatcher> _matcher;
private string _userName;
private string _password;
private ResourceOwnerPasswordValidationContext _context;
private Mock<IIdentityServerConfiguration> _config;
private User _user;
public OcelotResourceOwnerPasswordValidatorTests()
{
_matcher = new Mock<IHashMatcher>();
_config = new Mock<IIdentityServerConfiguration>();
_validator = new OcelotResourceOwnerPasswordValidator(_matcher.Object, _config.Object);
}
[Fact]
public void should_return_success()
{
this.Given(x => GivenTheUserName("tom"))
.And(x => GivenThePassword("password"))
.And(x => GivenTheUserIs(new User("sub", "tom", "xxx", "xxx")))
.And(x => GivenTheMatcherReturns(true))
.When(x => WhenIValidate())
.Then(x => ThenTheUserIsValidated())
.And(x => ThenTheMatcherIsCalledCorrectly())
.BDDfy();
}
[Fact]
public void should_return_fail_when_no_user()
{
this.Given(x => GivenTheUserName("bob"))
.And(x => GivenTheUserIs(new User("sub", "tom", "xxx", "xxx")))
.And(x => GivenTheMatcherReturns(true))
.When(x => WhenIValidate())
.Then(x => ThenTheUserIsNotValidated())
.BDDfy();
}
[Fact]
public void should_return_fail_when_password_doesnt_match()
{
this.Given(x => GivenTheUserName("tom"))
.And(x => GivenThePassword("password"))
.And(x => GivenTheUserIs(new User("sub", "tom", "xxx", "xxx")))
.And(x => GivenTheMatcherReturns(false))
.When(x => WhenIValidate())
.Then(x => ThenTheUserIsNotValidated())
.And(x => ThenTheMatcherIsCalledCorrectly())
.BDDfy();
}
private void ThenTheMatcherIsCalledCorrectly()
{
_matcher
.Verify(x => x.Match(_password, _user.Salt, _user.Hash), Times.Once);
}
private void GivenThePassword(string password)
{
_password = password;
}
private void GivenTheUserIs(User user)
{
_user = user;
_config
.Setup(x => x.Users)
.Returns(new List<User>{_user});
}
private void GivenTheMatcherReturns(bool expected)
{
_matcher
.Setup(x => x.Match(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(expected);
}
private void GivenTheUserName(string userName)
{
_userName = userName;
}
private void WhenIValidate()
{
_context = new ResourceOwnerPasswordValidationContext
{
UserName = _userName,
Password = _password
};
_validator.ValidateAsync(_context).Wait();
}
private void ThenTheUserIsValidated()
{
_context.Result.IsError.ShouldBe(false);
}
private void ThenTheUserIsNotValidated()
{
_context.Result.IsError.ShouldBe(true);
}
}
}

View File

@ -24,7 +24,8 @@
"Shouldly": "2.8.2",
"TestStack.BDDfy": "4.3.2",
"Microsoft.AspNetCore.Authentication.OAuth": "1.1.0",
"Microsoft.DotNet.InternalAbstractions": "1.0.0"
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "1.1.0"
},
"runtimes": {
"win10-x64": {},