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

Added guard to FormsAuthentication as per issue #1755 #1778

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
67 changes: 65 additions & 2 deletions src/Nancy.Authentication.Forms.Tests/FormsAuthenticationFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ namespace Nancy.Authentication.Forms.Tests
using FakeItEasy;
using Fakes;
using Helpers;
using Nancy.Security;
using Nancy.Tests;
using Nancy.Tests.Fakes;
using Xunit;
Expand Down Expand Up @@ -162,7 +161,71 @@ public void Should_return_ok_response_when_user_logs_in_without_redirect()
result.StatusCode.ShouldEqual(HttpStatusCode.OK);
}

[Fact]
#region Throw helpful exception when the configuration is not enabled
Copy link
Contributor

Choose a reason for hiding this comment

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

please use 4 spaces instead of tabs.

Copy link
Author

Choose a reason for hiding this comment

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

Ug. Yuck. Spaces. Ok. There isn't a style-guide, is there? There seems to be a space for one, but no link.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Author

Choose a reason for hiding this comment

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

thx

Copy link
Member

Choose a reason for hiding this comment

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

Can we please get rid of the region? Thanks 😋


[Fact]
public void Should_throw_helpful_exception_message_when_user_logs_in_without_redirect_and_forms_authentication_not_enabled()
{
// Given
const string expectedMessage = "The internal FormsAuthenticationConfiguration has not been set. Ensure that FormsAuthentication has been enabled in the bootstrapper";
FormsAuthentication.Disable();
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think it's neccesairy to disable the FormsAuthentication if you don't enable it it's disabled.

Copy link
Author

Choose a reason for hiding this comment

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

But as I explained in the comment, I can't see how else to have unit tests for the feature(possibly creating a new AppDomain, but that's no fun). Everything for FormsAuthentication is static and the unit tests don't/shouldn't run in a any particular order, so once a test has enabled FormsAuthentication, it's enabled for the whole test-run. Hence the whether the test will pass depends on state created by other tests. It will fail nearly all of the time and it's not testable without some way of disabling FormsAuthentication. It still would fail if the tests can run in parallel, but the existing tests would as well.

FormsAuthentication could be redesigned not to be static- I don't think it should be static, it maintains a global state- however, that's a non-trivial breaking-change, so I suppose it wouldn't be approved.

TL;DR- Accept that it's not testable, or leave the hack in and sweep the design under the carpet.

Let me know what you'd like to do and I'll fix it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I understand but when FormsAuthentication.Enable isn't called the configuration is null. note: xUnit creates a new instance of this class for every test.

Copy link
Author

Choose a reason for hiding this comment

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

Perhaps I'm misunderstanding. xUnit can't create a new instance of FormsAuthentication, it's a static class.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes sorry my bad :)

Sent from my iPhone

On 9 jan. 2015, at 12:06, "Worthaboutapig" [email protected] wrote:

In src/Nancy.Authentication.Forms.Tests/FormsAuthenticationFixture.cs:

@@ -162,7 +161,71 @@ public void Should_return_ok_response_when_user_logs_in_without_redirect()
result.StatusCode.ShouldEqual(HttpStatusCode.OK);
}

  •    [Fact]
    
  •   #region Throw helpful exception when the configuration is not enabled
    
  •   [Fact]
    
  •   public void Should_throw_helpful_exception_message_when_user_logs_in_without_redirect_and_forms_authentication_not_enabled()
    
  •   {
    
  •       // Given
    
  •       const string expectedMessage = "The internal FormsAuthenticationConfiguration has not been set. Ensure that FormsAuthentication has been enabled in the bootstrapper";
    
  •       FormsAuthentication.Disable();
    
    Perhaps I'm misunderstanding. xUnit can't create a new instance of FormsAuthentication, it's a static class.


Reply to this email directly or view it on GitHub.


// When
var result = Record.Exception(() => FormsAuthentication.UserLoggedInResponse(userGuid));

// Then
result.ShouldBeOfType(typeof(InvalidOperationException));
Copy link
Member

Choose a reason for hiding this comment

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

Please change this to use the generic overload result.ShouldBeOfType<InvalidOperationException>();

result.Message.ShouldBeSameAs(expectedMessage);
}

[Fact]
public void Should_throw_helpful_exception_message_when_user_logs_in_with_redirect_and_forms_authentication_not_enabled()
{
// Given
const string expectedMessage = "The internal FormsAuthenticationConfiguration has not been set. Ensure that FormsAuthentication has been enabled in the bootstrapper";
FormsAuthentication.Disable();

// When
var result = Record.Exception(() => FormsAuthentication.UserLoggedInRedirectResponse(context, userGuid));

// Then
result.ShouldBeOfType(typeof(InvalidOperationException));
Copy link
Member

Choose a reason for hiding this comment

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

Please change this to use the generic overload result.ShouldBeOfType<InvalidOperationException>();

result.Message.ShouldBeSameAs(expectedMessage);
}

[Fact]
public void Should_throw_helpful_exception_message_when_user_logs_out_with_redirect_and_forms_authentication_not_enabled()
{
// Given
const string expectedMessage = "The internal FormsAuthenticationConfiguration has not been set. Ensure that FormsAuthentication has been enabled in the bootstrapper";
FormsAuthentication.Disable();

// When
var result = Record.Exception(() => FormsAuthentication.LogOutAndRedirectResponse(context, "/"));

// Then
result.ShouldBeOfType(typeof(InvalidOperationException));
Copy link
Member

Choose a reason for hiding this comment

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

Please change this to use the generic overload result.ShouldBeOfType<InvalidOperationException>();

result.Message.ShouldBeSameAs(expectedMessage);
}

[Fact]
public void Should_throw_helpful_exception_message_when_user_logs_out_without_redirect_and_forms_authentication_not_enabled()
{
// Given
const string expectedMessage = "The internal FormsAuthenticationConfiguration has not been set. Ensure that FormsAuthentication has been enabled in the bootstrapper";
FormsAuthentication.Disable();

// When
var result = Record.Exception(() => FormsAuthentication.LogOutResponse());

// Then
result.ShouldBeOfType(typeof(InvalidOperationException));
Copy link
Member

Choose a reason for hiding this comment

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

Please change this to use the generic overload result.ShouldBeOfType<InvalidOperationException>();

result.Message.ShouldBeSameAs(expectedMessage);
}

#endregion

[Fact]
public void Should_have_authentication_cookie_in_login_response_when_logging_in_with_redirect()
{
FormsAuthentication.Enable(A.Fake<IPipelines>(), this.config);
Expand Down
46 changes: 35 additions & 11 deletions src/Nancy.Authentication.Forms/FormsAuthentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,15 @@ public static string FormsAuthenticationCookieName
}
}

/// <summary>
/// <summary>
/// To support testing, necessary as everying is static, but not ideal
/// </summary>
internal static void Disable()
{
currentConfiguration = null;
}

/// <summary>
/// Enables forms authentication for the application
/// </summary>
/// <param name="pipelines">Pipelines to add handlers to (usually "this")</param>
Expand Down Expand Up @@ -110,7 +118,12 @@ public static void Enable(INancyModule module, FormsAuthenticationConfiguration
/// <returns>Nancy response with redirect.</returns>
public static Response UserLoggedInRedirectResponse(NancyContext context, Guid userIdentifier, DateTime? cookieExpiry = null, string fallbackRedirectUrl = null)
{
var redirectUrl = fallbackRedirectUrl;
if (currentConfiguration == null)
{
throw new InvalidOperationException("The internal FormsAuthenticationConfiguration has not been set. Ensure that FormsAuthentication has been enabled in the bootstrapper");
}

var redirectUrl = fallbackRedirectUrl;

if (string.IsNullOrEmpty(redirectUrl))
{
Expand Down Expand Up @@ -149,11 +162,14 @@ public static Response UserLoggedInRedirectResponse(NancyContext context, Guid u
/// <returns>Nancy response with status <see cref="HttpStatusCode.OK"/></returns>
public static Response UserLoggedInResponse(Guid userIdentifier, DateTime? cookieExpiry = null)
{
var response =
(Response)HttpStatusCode.OK;
if (currentConfiguration == null)
{
throw new InvalidOperationException("The internal FormsAuthenticationConfiguration has not been set. Ensure that FormsAuthentication has been enabled in the bootstrapper");
}

var response = (Response)HttpStatusCode.OK;

var authenticationCookie =
BuildCookie(userIdentifier, cookieExpiry, currentConfiguration);
var authenticationCookie = BuildCookie(userIdentifier, cookieExpiry, currentConfiguration);

response.AddCookie(authenticationCookie);

Expand All @@ -168,7 +184,12 @@ public static Response UserLoggedInResponse(Guid userIdentifier, DateTime? cooki
/// <returns>Nancy response</returns>
public static Response LogOutAndRedirectResponse(NancyContext context, string redirectUrl)
{
var response = context.GetRedirect(redirectUrl);
if (currentConfiguration == null)
{
throw new InvalidOperationException("The internal FormsAuthenticationConfiguration has not been set. Ensure that FormsAuthentication has been enabled in the bootstrapper");
}

var response = context.GetRedirect(redirectUrl);
var authenticationCookie = BuildLogoutCookie(currentConfiguration);
response.AddCookie(authenticationCookie);

Expand All @@ -181,11 +202,14 @@ public static Response LogOutAndRedirectResponse(NancyContext context, string re
/// <returns>Nancy response</returns>
public static Response LogOutResponse()
{
var response =
(Response)HttpStatusCode.OK;
if (currentConfiguration == null)
{
throw new InvalidOperationException("The internal FormsAuthenticationConfiguration has not been set. Ensure that FormsAuthentication has been enabled in the bootstrapper");
}

var response = (Response)HttpStatusCode.OK;

var authenticationCookie =
BuildLogoutCookie(currentConfiguration);
var authenticationCookie = BuildLogoutCookie(currentConfiguration);

response.AddCookie(authenticationCookie);

Expand Down
2 changes: 2 additions & 0 deletions src/Nancy.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=NancyStandard/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="NancyStandard"&gt;&lt;CSUseVar&gt;&lt;BehavourStyle&gt;CAN_CHANGE_TO_IMPLICIT&lt;/BehavourStyle&gt;&lt;LocalVariableStyle&gt;ALWAYS_IMPLICIT&lt;/LocalVariableStyle&gt;&lt;ForeachVariableStyle&gt;ALWAYS_IMPLICIT&lt;/ForeachVariableStyle&gt;&lt;/CSUseVar&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;CSReorderTypeMembers&gt;True&lt;/CSReorderTypeMembers&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ThisQualifier/INSTANCE_MEMBERS_QUALIFY_MEMBERS/@EntryValue">All</s:String>
Copy link
Contributor

Choose a reason for hiding this comment

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

You should probably remove this file from your PR.

Copy link
Author

Choose a reason for hiding this comment

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

Sure, sorry about that.

<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BETWEEN_USING_GROUPS/@EntryValue">1</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INITIALIZER_BRACES/@EntryValue">NEXT_LINE</s:String>
Expand All @@ -10,5 +11,6 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=MethodPropertyEvent/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="Aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters /&gt;&lt;/data&gt;</s:String>
<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue">&lt;data /&gt;</s:String></wpf:ResourceDictionary>
1 change: 1 addition & 0 deletions src/SharedAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
[assembly: AssemblyInformationalVersion("0.23.2")]

[assembly: InternalsVisibleTo("Nancy.Tests")]
[assembly: InternalsVisibleTo("Nancy.Authentication.Forms.Tests")]