Skip to content
This repository has been archived by the owner on Jan 24, 2021. It is now read-only.

Add CookieBasedSessionsConfiguration #1409

Merged
merged 4 commits into from Mar 16, 2014
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Nancy.Tests/Nancy.Tests.csproj
Expand Up @@ -218,6 +218,7 @@
<Compile Include="Unit\Security\DefaultCsrfTokenValidatorFixture.cs" />
<Compile Include="Unit\Security\ModuleSecurityFixture.cs" />
<Compile Include="Unit\Security\UserIdentityExtensionsFixture.cs" />
<Compile Include="Unit\Sessions\CookieBasedSessionsConfigurationFixture.cs" />
<Compile Include="Unit\Sessions\CookieBasedSessionsFixture.cs" />
<Compile Include="Unit\Cryptography\RijndaelEncryptionProviderFixture.cs" />
<Compile Include="Unit\Sessions\DefaultSessionObjectFormatterFixture.cs" />
Expand Down
@@ -0,0 +1,85 @@
namespace Nancy.Testing.Tests
{
using FakeItEasy;

using Nancy.Cryptography;
using Nancy.Session;
using Nancy.Tests;
using Xunit;

public class CookieBasesSessionsConfigurationFixture
{
private readonly CookieBasedSessionsConfiguration config;

public CookieBasesSessionsConfigurationFixture()
{
var cryptographyConfiguration = new CryptographyConfiguration(
new RijndaelEncryptionProvider(new PassphraseKeyGenerator("SuperSecretPass", new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })),
new DefaultHmacProvider(new PassphraseKeyGenerator("UberSuperSecure", new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })));

this.config = new CookieBasedSessionsConfiguration()
{
CryptographyConfiguration = cryptographyConfiguration,
Serializer = A.Fake<IObjectSerializer>()
};
}

[Fact]
public void Should_be_valid_with_all_properties_set()
{
var result = config.IsValid;

result.ShouldBeTrue();
}

[Fact]
public void Should_not_be_valid_with_empty_cookiename()
{
config.CookieName = "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nancy code base prefers this.config to config


var result = config.IsValid;

result.ShouldBeFalse();
}

[Fact]
public void Should_not_be_valid_with_null_serializer()
{
config.Serializer = null;

var result = config.IsValid;

result.ShouldBeFalse();
}

[Fact]
public void Should_not_be_valid_with_null_cryptography_configuration()
{
config.CryptographyConfiguration = null;

var result = config.IsValid;

result.ShouldBeFalse();
}

[Fact]
public void Should_not_be_valid_with_null_encryption_provider()
{
config.CryptographyConfiguration = new CryptographyConfiguration(null, config.CryptographyConfiguration.HmacProvider);

var result = config.IsValid;

result.ShouldBeFalse();
}

[Fact]
public void Should_not_be_valid_with_null_hmac_provider()
{
config.CryptographyConfiguration = new CryptographyConfiguration(config.CryptographyConfiguration.EncryptionProvider, null);

var result = config.IsValid;

result.ShouldBeFalse();
}
}
}
114 changes: 98 additions & 16 deletions src/Nancy.Tests/Unit/Sessions/CookieBasedSessionsFixture.cs
Expand Up @@ -26,19 +26,24 @@ public class CookieBasedSessionsFixture
private readonly IEncryptionProvider fakeEncryptionProvider;
private readonly CookieBasedSessions cookieStore;
private readonly IHmacProvider fakeHmacProvider;
private readonly IObjectSerializer fakeObjectSerializer;

private RijndaelEncryptionProvider rijndaelEncryptionProvider;

private DefaultHmacProvider defaultHmacProvider;

private IObjectSerializer defaultObjectSerializer;

public CookieBasedSessionsFixture()
{
this.fakeEncryptionProvider = A.Fake<IEncryptionProvider>();
this.fakeHmacProvider = A.Fake<IHmacProvider>();
this.cookieStore = new CookieBasedSessions(this.fakeEncryptionProvider, this.fakeHmacProvider, new Fakes.FakeObjectSerializer());
this.fakeObjectSerializer = new Fakes.FakeObjectSerializer();
this.cookieStore = new CookieBasedSessions(this.fakeEncryptionProvider, this.fakeHmacProvider, this.fakeObjectSerializer);

this.rijndaelEncryptionProvider = new RijndaelEncryptionProvider(new PassphraseKeyGenerator("password", new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }, 1000));
this.defaultHmacProvider = new DefaultHmacProvider(new PassphraseKeyGenerator("anotherpassword", new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }, 1000));
this.defaultObjectSerializer = new DefaultObjectSerializer();
}

[Fact]
Expand Down Expand Up @@ -76,7 +81,7 @@ public void Should_save_the_session_cookie()

response.Cookies.Count.ShouldEqual(1);
var cookie = response.Cookies.First();
cookie.Name.ShouldEqual(CookieBasedSessions.GetCookieName());
cookie.Name.ShouldEqual(this.cookieStore.CookieName);
cookie.Value.ShouldEqual("encrypted=key1=val1;key2=val2;");
cookie.Expires.ShouldBeNull();
cookie.Path.ShouldBeNull();
Expand Down Expand Up @@ -179,7 +184,7 @@ public void Should_only_not_add_response_cookie_if_it_has_not_changed()
var hooks = A.Fake<IPipelines>();
A.CallTo(() => hooks.BeforeRequest).Returns(beforePipeline);
A.CallTo(() => hooks.AfterRequest).Returns(afterPipeline);
CookieBasedSessions.Enable(hooks, new CryptographyConfiguration(this.fakeEncryptionProvider, this.fakeHmacProvider)).WithSerializer(new Fakes.FakeObjectSerializer());
CookieBasedSessions.Enable(hooks, new CryptographyConfiguration(this.fakeEncryptionProvider, this.fakeHmacProvider)).WithSerializer(this.fakeObjectSerializer);
var request = CreateRequest("encryptedkey1=value1");
A.CallTo(() => this.fakeEncryptionProvider.Decrypt("encryptedkey1=value1")).Returns("key1=value1;");
var response = A.Fake<Response>();
Expand All @@ -199,7 +204,7 @@ public void Should_add_response_cookie_if_it_has_changed()
var hooks = A.Fake<IPipelines>();
A.CallTo(() => hooks.BeforeRequest).Returns(beforePipeline);
A.CallTo(() => hooks.AfterRequest).Returns(afterPipeline);
CookieBasedSessions.Enable(hooks, new CryptographyConfiguration(this.fakeEncryptionProvider, this.fakeHmacProvider)).WithSerializer(new Fakes.FakeObjectSerializer());
CookieBasedSessions.Enable(hooks, new CryptographyConfiguration(this.fakeEncryptionProvider, this.fakeHmacProvider)).WithSerializer(this.fakeObjectSerializer);
var request = CreateRequest("encryptedkey1=value1");
A.CallTo(() => this.fakeEncryptionProvider.Decrypt("encryptedkey1=value1")).Returns("key1=value1;");
var response = A.Fake<Response>();
Expand Down Expand Up @@ -264,14 +269,14 @@ public void Should_be_able_to_save_a_complex_object_to_session()
var response = new Response();
var session = new Session(new Dictionary<string, object>());
var payload = new DefaultSessionObjectFormatterFixture.Payload(27, true, "Test string");
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, new DefaultObjectSerializer());
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, this.defaultObjectSerializer);
session["testObject"] = payload;

store.Save(session, response);

response.Cookies.Count.ShouldEqual(1);
var cookie = response.Cookies.First();
cookie.Name.ShouldEqual(CookieBasedSessions.GetCookieName());
cookie.Name.ShouldEqual(store.CookieName);
cookie.Value.ShouldNotBeNull();
cookie.Value.ShouldNotBeEmpty();
}
Expand All @@ -282,7 +287,7 @@ public void Should_be_able_to_load_an_object_previously_saved_to_session()
var response = new Response();
var session = new Session(new Dictionary<string, object>());
var payload = new DefaultSessionObjectFormatterFixture.Payload(27, true, "Test string");
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, new DefaultObjectSerializer());
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, this.defaultObjectSerializer);
session["testObject"] = payload;
store.Save(session, response);
var request = new Request("GET", "/", "http");
Expand Down Expand Up @@ -330,9 +335,9 @@ public void Should_load_valid_test_data()
{
var inputValue = ValidHmac + ValidData;
inputValue = HttpUtility.UrlEncode(inputValue);
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, new DefaultObjectSerializer());
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, this.defaultObjectSerializer);
var request = new Request("GET", "/", "http");
request.Cookies.Add(CookieBasedSessions.GetCookieName(), inputValue);
request.Cookies.Add(store.CookieName, inputValue);

var result = store.Load(request);

Expand All @@ -345,9 +350,9 @@ public void Should_return_blank_session_if_hmac_changed()
{
var inputValue = "b" + ValidHmac.Substring(1) + ValidData;
inputValue = HttpUtility.UrlEncode(inputValue);
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, new DefaultObjectSerializer());
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, this.defaultObjectSerializer);
var request = new Request("GET", "/", "http");
request.Cookies.Add(CookieBasedSessions.GetCookieName(), inputValue);
request.Cookies.Add(store.CookieName, inputValue);

var result = store.Load(request);

Expand All @@ -359,9 +364,9 @@ public void Should_return_blank_session_if_hmac_missing()
{
var inputValue = ValidData;
inputValue = HttpUtility.UrlEncode(inputValue);
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, new DefaultObjectSerializer());
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, this.defaultObjectSerializer);
var request = new Request("GET", "/", "http");
request.Cookies.Add(CookieBasedSessions.GetCookieName(), inputValue);
request.Cookies.Add(store.CookieName, inputValue);

var result = store.Load(request);

Expand All @@ -373,22 +378,99 @@ public void Should_return_blank_session_if_encrypted_data_modified()
{
var inputValue = ValidHmac + ValidData.Substring(0, ValidData.Length - 1) + "Z";
inputValue = HttpUtility.UrlEncode(inputValue);
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, new DefaultObjectSerializer());
var store = new CookieBasedSessions(this.rijndaelEncryptionProvider, this.defaultHmacProvider, this.defaultObjectSerializer);
var request = new Request("GET", "/", "http");
request.Cookies.Add(CookieBasedSessions.GetCookieName(), inputValue);
request.Cookies.Add(store.CookieName, inputValue);

var result = store.Load(request);

result.Count.ShouldEqual(0);
}

[Fact]
public void Should_use_CookieName_when_config_provides_cookiename_value()
{
//Given
var cryptoConfig = new CryptographyConfiguration(this.fakeEncryptionProvider, this.fakeHmacProvider);
var storeConfig = new CookieBasedSessionsConfiguration(cryptoConfig)
{
CookieName = "NamedCookie",
Serializer = this.fakeObjectSerializer
};
var store = new CookieBasedSessions(storeConfig);

//When
var response = new Response();
var session = new Session(new Dictionary<string, object>
{
{"key1", "val1"},
});
session["key2"] = "val2";
store.Save(session, response);

//Then
response.Cookies.ShouldHave(c => c.Name == storeConfig.CookieName);
}

[Fact]
public void Should_set_Domain_when_config_provides_domain_value()
{
//Given
var cryptoConfig = new CryptographyConfiguration(this.fakeEncryptionProvider, this.fakeHmacProvider);
var storeConfig = new CookieBasedSessionsConfiguration(cryptoConfig)
{
Domain = ".nancyfx.org",
Serializer = this.fakeObjectSerializer
};
var store = new CookieBasedSessions(storeConfig);

//When
var response = new Response();
var session = new Session(new Dictionary<string, object>
{
{"key1", "val1"},
});
session["key2"] = "val2";
store.Save(session, response);

//Then
var cookie = response.Cookies.First(c => c.Name == storeConfig.CookieName);
cookie.Domain.ShouldEqual(storeConfig.Domain);
}

[Fact]
public void Should_set_Path_when_config_provides_path_value()
{
//Given
var cryptoConfig = new CryptographyConfiguration(this.fakeEncryptionProvider, this.fakeHmacProvider);
var storeConfig = new CookieBasedSessionsConfiguration(cryptoConfig)
{
Path = "/",
Serializer = this.fakeObjectSerializer
};
var store = new CookieBasedSessions(storeConfig);

//When
var response = new Response();
var session = new Session(new Dictionary<string, object>
{
{"key1", "val1"},
});
session["key2"] = "val2";
store.Save(session, response);

//Then
var cookie = response.Cookies.First(c => c.Name == storeConfig.CookieName);
cookie.Path.ShouldEqual(storeConfig.Path);
}

private Request CreateRequest(string sessionValue, bool load = true)
{
var headers = new Dictionary<string, IEnumerable<string>>(1);

if (!string.IsNullOrEmpty(sessionValue))
{
headers.Add("cookie", new[] { CookieBasedSessions.GetCookieName()+ "=" + HttpUtility.UrlEncode(sessionValue) });
headers.Add("cookie", new[] { this.cookieStore.CookieName+ "=" + HttpUtility.UrlEncode(sessionValue) });
}

var request = new Request("GET", new Url { Path = "/", Scheme = "http", Port = 9001, BasePath = "goku.power" }, CreateRequestStream(), headers);
Expand Down
1 change: 1 addition & 0 deletions src/Nancy/Nancy.csproj
Expand Up @@ -260,6 +260,7 @@
<Compile Include="Routing\Trie\RouteResolverTrie.cs" />
<Compile Include="Routing\Trie\SegmentMatch.cs" />
<Compile Include="Routing\Trie\TrieNodeFactory.cs" />
<Compile Include="Session\CookieBasedSessionsConfiguration.cs" />
<Compile Include="StaticContent.cs" />
<Compile Include="Conventions\ViewLocationConventions.cs" />
<Compile Include="Cryptography\Base64Helpers.cs" />
Expand Down