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

Commit 11ac0fe

Browse files
jetic83khellang
andcommitted
Added Module extension RequiresRoles(string[] roles) and ... (#2982)
Added Module Security extensions RequiresRoles(string[] roles) and RequiresAnyRole(string[] roles) Co-authored-by: Peter Schueffler <[email protected]> Co-authored-by: Kristian Hellang <[email protected]>
1 parent 680823d commit 11ac0fe

File tree

4 files changed

+247
-1
lines changed

4 files changed

+247
-1
lines changed

src/Nancy/Security/ClaimsPrincipalExtensions.cs

+22
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,27 @@ public static bool HasValidClaims(this ClaimsPrincipal user, Func<IEnumerable<Cl
5656
&& user.Claims != null
5757
&& isValid(user.Claims);
5858
}
59+
60+
/// <summary>
61+
/// Tests if the user is in all of the required roles.
62+
/// </summary>
63+
/// <param name="user">User to be verified</param>
64+
/// <param name="requiredRoles">Roles the user needs to be in</param>
65+
/// <returns>True if the user is in all of the required roles, false otherwise</returns>
66+
public static bool IsInRoles(this ClaimsPrincipal user, params string[] requiredRoles)
67+
{
68+
return user != null && requiredRoles != null && requiredRoles.All(user.IsInRole);
69+
}
70+
71+
/// <summary>
72+
/// Tests if the user is in at least one of the required roles.
73+
/// </summary>
74+
/// <param name="user">User to be verified</param>
75+
/// <param name="requiredRoles">Roles the user needs to be in at least one of</param>
76+
/// <returns>True if the user is in at least one of the required roles, false otherwise</returns>
77+
public static bool IsInAnyRole(this ClaimsPrincipal user, params string[] requiredRoles)
78+
{
79+
return user != null && requiredRoles != null && requiredRoles.Any(user.IsInRole);
80+
}
5981
}
6082
}

src/Nancy/Security/ModuleSecurity.cs

+22
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,28 @@ public static void RequiresAnyClaim(this INancyModule module, params Predicate<C
4242
module.AddBeforeHookOrExecute(SecurityHooks.RequiresAuthentication(), "Requires Authentication");
4343
module.AddBeforeHookOrExecute(SecurityHooks.RequiresAnyClaim(requiredClaims), "Requires Any Claim");
4444
}
45+
46+
/// <summary>
47+
/// This module requires authentication and certain roles to be present.
48+
/// </summary>
49+
/// <param name="module">Module to enable</param>
50+
/// <param name="requiredRoles">Role(s) required</param>
51+
public static void RequiresRoles(this INancyModule module, params string[] requiredRoles)
52+
{
53+
module.AddBeforeHookOrExecute(SecurityHooks.RequiresAuthentication(), "Requires Authentication");
54+
module.AddBeforeHookOrExecute(SecurityHooks.RequiresRoles(requiredRoles), "Requires Roles");
55+
}
56+
57+
/// <summary>
58+
/// This module requires authentication and any one of certain roles to be present.
59+
/// </summary>
60+
/// <param name="module">Module to enable</param>
61+
/// <param name="requiredRoles">Role(s) at least one of which is required</param>
62+
public static void RequiresAnyRole(this INancyModule module, params string[] requiredRoles)
63+
{
64+
module.AddBeforeHookOrExecute(SecurityHooks.RequiresAuthentication(), "Requires Authentication");
65+
module.AddBeforeHookOrExecute(SecurityHooks.RequiresAnyRole(requiredRoles), "Requires Any Role");
66+
}
4567

4668
/// <summary>
4769
/// This module requires https.

src/Nancy/Security/SecurityHooks.cs

+29-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,34 @@ public static Func<NancyContext, Response> RequiresValidatedClaims(Func<IEnumera
6363
return ForbiddenIfNot(ctx => ctx.CurrentUser.HasValidClaims(isValid));
6464
}
6565

66+
/// <summary>
67+
/// Creates a hook to be used in a pipeline before a route handler to ensure
68+
/// that the request was made by an authenticated user being in all of
69+
/// the required roles.
70+
/// </summary>
71+
/// <param name="roles">Roles the authenticated user needs to be in</param>
72+
/// <returns>Hook that returns an Unauthorized response if the user is not
73+
/// authenticated or is not in all of the required roles, null
74+
/// otherwise</returns>
75+
public static Func<NancyContext, Response> RequiresRoles(params string[] roles)
76+
{
77+
return ForbiddenIfNot(ctx => ctx.CurrentUser.IsInRoles(roles));
78+
}
79+
80+
/// <summary>
81+
/// Creates a hook to be used in a pipeline before a route handler to ensure
82+
/// that the request was made by an authenticated user being in at least one of
83+
/// the required roles.
84+
/// </summary>
85+
/// <param name="roles">Roles the authenticated user needs to be in at least one of</param>
86+
/// <returns>Hook that returns an Unauthorized response if the user is not
87+
/// authenticated or is not in at least one of the required roles, null
88+
/// otherwise</returns>
89+
public static Func<NancyContext, Response> RequiresAnyRole(params string[] roles)
90+
{
91+
return ForbiddenIfNot(ctx => ctx.CurrentUser.IsInAnyRole(roles));
92+
}
93+
6694
/// <summary>
6795
/// Creates a hook to be used in a pipeline before a route handler to ensure that
6896
/// the request satisfies a specific test.
@@ -140,4 +168,4 @@ public static Func<NancyContext, Response> RequiresHttps(bool redirect, int? htt
140168
};
141169
}
142170
}
143-
}
171+
}

test/Nancy.Tests/Unit/Security/ClaimsPrincipalExtensionsFixture.cs

+174
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,180 @@ public void Should_call_validation_with_users_claims()
280280
validatedClaims.ShouldEqualSequence(user.Claims);
281281
}
282282

283+
[Fact]
284+
public void Should_return_false_for_required_role_if_the_roles_are_null()
285+
{
286+
// Given
287+
ClaimsPrincipal user = GetFakeUser("Fake");
288+
var requiredRole = "not-present-role";
289+
290+
// When
291+
var result = user.IsInRole(requiredRole);
292+
293+
// Then
294+
result.ShouldBeFalse();
295+
}
296+
297+
[Fact]
298+
public void Should_return_false_for_required_role_if_the_user_does_not_have_role()
299+
{
300+
// Given
301+
ClaimsPrincipal user = GetFakeUser("Fake", new Claim(ClaimTypes.Role, string.Empty));
302+
var requiredRole = "not-present-role";
303+
304+
// When
305+
var result = user.IsInRole(requiredRole);
306+
307+
// Then
308+
result.ShouldBeFalse();
309+
}
310+
311+
[Fact]
312+
public void Should_return_true_for_required_role_if_the_user_does_have_role()
313+
{
314+
// Given
315+
ClaimsPrincipal user = GetFakeUser("Fake", new Claim(ClaimTypes.Role, "present-role"));
316+
var requiredRole = "present-role";
317+
318+
// When
319+
var result = user.IsInRole(requiredRole);
320+
321+
// Then
322+
result.ShouldBeTrue();
323+
}
324+
325+
[Fact]
326+
public void Should_return_false_for_required_roles_if_the_user_is_null()
327+
{
328+
// Given
329+
ClaimsPrincipal user = null;
330+
var requiredRoles = new string[] { "not-present-role1", "not-present-role2" };
331+
332+
// When
333+
var result = user.IsInRoles(requiredRoles);
334+
335+
// Then
336+
result.ShouldBeFalse();
337+
}
338+
339+
[Fact]
340+
public void Should_return_false_for_required_roles_if_the_roles_are_null()
341+
{
342+
// Given
343+
ClaimsPrincipal user = GetFakeUser("Fake");
344+
string[] requiredRoles = null;
345+
346+
// When
347+
var result = user.IsInRoles(requiredRoles);
348+
349+
// Then
350+
result.ShouldBeFalse();
351+
}
352+
353+
[Fact]
354+
public void Should_return_false_for_required_roles_if_the_user_does_not_have_all_roles()
355+
{
356+
// Given
357+
ClaimsPrincipal user = GetFakeUser("Fake",
358+
new Claim(ClaimTypes.Role, "present-role1"),
359+
new Claim(ClaimTypes.Role, "present-role2"),
360+
new Claim(ClaimTypes.Role, "present-role3"));
361+
var requiredRoles = new string[]
362+
{
363+
"present-role1",
364+
"not-present-role1"
365+
};
366+
367+
// When
368+
var result = user.IsInRoles(requiredRoles);
369+
370+
// Then
371+
result.ShouldBeFalse();
372+
}
373+
374+
[Fact]
375+
public void Should_return_true_for_required_roles_if_the_user_does_have_all_roles()
376+
{
377+
// Given
378+
ClaimsPrincipal user = GetFakeUser("Fake",
379+
new Claim(ClaimTypes.Role, "present-role1"),
380+
new Claim(ClaimTypes.Role, "present-role2"),
381+
new Claim(ClaimTypes.Role, "present-role3"));
382+
var requiredRoles = new string[]
383+
{
384+
"present-role1",
385+
"present-role2"
386+
};
387+
388+
// When
389+
var result = user.IsInRoles(requiredRoles);
390+
391+
// Then
392+
result.ShouldBeTrue();
393+
}
394+
395+
[Fact]
396+
public void Should_return_false_for_any_required_role_if_the_user_is_null()
397+
{
398+
// Given
399+
ClaimsPrincipal user = null;
400+
var requiredRoles = new string[] { "not-present-role1", "not-present-role2" };
401+
402+
// When
403+
var result = user.IsInAnyRole(requiredRoles);
404+
405+
// Then
406+
result.ShouldBeFalse();
407+
}
408+
409+
[Fact]
410+
public void Should_return_false_for_any_required_role_if_the_roles_are_null()
411+
{
412+
// Given
413+
ClaimsPrincipal user = GetFakeUser("Fake");
414+
string[] requiredRoles = null;
415+
416+
// When
417+
var result = user.IsInAnyRole(requiredRoles);
418+
419+
// Then
420+
result.ShouldBeFalse();
421+
}
422+
423+
[Fact]
424+
public void Should_return_false_for_any_required_role_if_the_user_does_not_have_any_role()
425+
{
426+
// Given
427+
ClaimsPrincipal user = GetFakeUser("Fake",
428+
new Claim(ClaimTypes.Role, "present-role1"),
429+
new Claim(ClaimTypes.Role, "present-role2"),
430+
new Claim(ClaimTypes.Role, "present-role3"));
431+
var requiredRoles = new string[] { "not-present-role1", "not-present-role2" };
432+
433+
// When
434+
var result = user.IsInAnyRole(requiredRoles);
435+
436+
// Then
437+
result.ShouldBeFalse();
438+
}
439+
440+
[Fact]
441+
public void Should_return_true_for_any_required_role_if_the_user_does_have_any_of_role()
442+
{
443+
// Given
444+
ClaimsPrincipal user = GetFakeUser("Fake",
445+
new Claim(ClaimTypes.Role, "present-role1"),
446+
new Claim(ClaimTypes.Role, "present-role2"),
447+
new Claim(ClaimTypes.Role, "present-role3"));
448+
var requiredRoles = new string[] { "present-role1", "not-present-role1" };
449+
450+
// When
451+
var result = user.IsInAnyRole(requiredRoles);
452+
453+
// Then
454+
result.ShouldBeTrue();
455+
}
456+
283457
private static ClaimsPrincipal GetFakeUser(string userName, params Claim[] claims)
284458
{
285459
var claimsList = (claims ?? Enumerable.Empty<Claim>()).ToList();

0 commit comments

Comments
 (0)