-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #251 from ucdavis/swe/AddEmulation
Add user emulation and System role authorization
- Loading branch information
Showing
10 changed files
with
199 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace Finjector.Core.Models; | ||
|
||
public static class AccessCodes | ||
{ | ||
public const string SystemAccess = "SystemAccess"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace Finjector.Core.Models; | ||
|
||
public class SystemOptions | ||
{ | ||
public string[] Users { get; set; } = Array.Empty<string>(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
using Finjector.Core.Data; | ||
using Finjector.Core.Models; | ||
using Finjector.Core.Services; | ||
using Finjector.Web.Handlers; | ||
using Microsoft.AspNetCore.Authentication; | ||
using Microsoft.AspNetCore.Authentication.Cookies; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.EntityFrameworkCore; | ||
using Serilog; | ||
using System.Security.Claims; | ||
|
||
namespace Finjector.Web.Controllers; | ||
|
||
[Authorize] | ||
public class SystemController : Controller | ||
{ | ||
private readonly AppDbContext _dbContext; | ||
private readonly IIdentityService _identityService; | ||
private readonly IUserService _userService; | ||
|
||
public SystemController(AppDbContext dbContext, IIdentityService identityService, IUserService userService) | ||
{ | ||
_dbContext = dbContext; | ||
_identityService = identityService; | ||
_userService = userService; | ||
} | ||
|
||
[Authorize(Policy = AccessCodes.SystemAccess)] | ||
public async Task<IActionResult> Emulate(string id) | ||
{ | ||
var iamId = Request.HttpContext.User.FindFirstValue(IamIdClaimFallbackTransformer.ClaimType); | ||
var currentUser = await _userService.EnsureUserExists(iamId); | ||
Log.Information($"Emulation attempted for {id} by {currentUser.Name}"); | ||
var lookupVal = id.Trim(); | ||
|
||
var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.Email == lookupVal || u.Kerberos == lookupVal); | ||
if (user == null) | ||
{ | ||
// not found in db, look up user in IAM | ||
user = lookupVal.Contains("@") | ||
? await _identityService.GetByEmail(lookupVal) | ||
: await _identityService.GetByKerberos(lookupVal); | ||
|
||
if (user != null) | ||
{ | ||
// user found in IAM but not in our db | ||
// let the UserService handle creating it and setting up account ownership when applicable | ||
await _userService.EnsureUserExists(user.Iam); | ||
} | ||
else | ||
{ | ||
throw new Exception("User is null"); | ||
} | ||
} | ||
|
||
var identity = new ClaimsIdentity(new[] | ||
{ | ||
new Claim(ClaimTypes.NameIdentifier, user.Kerberos), | ||
new Claim(ClaimTypes.Name, user.Kerberos), | ||
new Claim(ClaimTypes.GivenName, user.FirstName), | ||
new Claim(ClaimTypes.Surname, user.LastName), | ||
new Claim("name", user.Name), | ||
new Claim(ClaimTypes.Email, user.Email), | ||
new Claim("ucdPersonIAMID", user.Iam), | ||
}, CookieAuthenticationDefaults.AuthenticationScheme); | ||
|
||
// kill old login | ||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); | ||
|
||
// create new login | ||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity)); | ||
|
||
return LocalRedirect("/"); | ||
} | ||
|
||
public async Task<IActionResult> EndEmulate() | ||
{ | ||
await HttpContext.SignOutAsync(); | ||
return LocalRedirect("/"); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
using Finjector.Core.Domain; | ||
using Finjector.Core.Models; | ||
using Finjector.Web.Handlers; | ||
using Microsoft.AspNetCore.Authorization; | ||
|
||
namespace Finjector.Web.Extensions; | ||
|
||
public static class AuthorizationExtensions | ||
{ | ||
public static void AddAccessPolicy(this AuthorizationOptions options, string policy) | ||
{ | ||
options.AddPolicy(policy, builder => builder.Requirements.Add(new VerifyRoleAccess(GetRoles(policy)))); | ||
} | ||
|
||
public static string[] GetRoles(string accessCode) | ||
{ | ||
return accessCode switch | ||
{ | ||
// System requirement can only be fulfilled by a system user | ||
AccessCodes.SystemAccess => new[] { Role.Codes.System }, | ||
_ => throw new ArgumentException($"{nameof(accessCode)} is not a valid {nameof(AccessCodes)} constant") | ||
}; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using Microsoft.AspNetCore.Authorization; | ||
|
||
namespace Finjector.Web.Handlers; | ||
|
||
public class VerifyRoleAccess : IAuthorizationRequirement | ||
{ | ||
public readonly string[] RoleStrings; | ||
|
||
public VerifyRoleAccess(params string[] roleStrings) | ||
{ | ||
RoleStrings = roleStrings; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using Finjector.Core.Domain; | ||
using Finjector.Core.Models; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.Extensions.Options; | ||
using System.Security.Claims; | ||
|
||
namespace Finjector.Web.Handlers; | ||
|
||
/// <summary> | ||
/// Currently only being used to verify system access, but can be expanded to verify other roles | ||
/// </summary> | ||
public class VerifyRoleAccessHandler : AuthorizationHandler<VerifyRoleAccess> | ||
{ | ||
private readonly IHttpContextAccessor _httpContext; | ||
private readonly SystemOptions _systemOptions; | ||
|
||
public VerifyRoleAccessHandler(IHttpContextAccessor httpContext, IOptions<SystemOptions> systemOptions) | ||
{ | ||
_httpContext = httpContext; | ||
_systemOptions = systemOptions.Value; | ||
} | ||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, VerifyRoleAccess requirement) | ||
{ | ||
var kerbId = context.User.Claims.SingleOrDefault(a => a.Type == ClaimTypes.NameIdentifier)?.Value; | ||
|
||
|
||
if (string.IsNullOrWhiteSpace(kerbId)) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
if (requirement.RoleStrings.Contains(Role.Codes.System) && _systemOptions.Users.Contains(kerbId)) | ||
{ | ||
context.Succeed(requirement); | ||
} | ||
|
||
return Task.CompletedTask; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,5 +32,8 @@ | |
"TokenEndpoint": "[External]", | ||
"ScopeApp": "Finjector", | ||
"ScopeEnv": "Production" | ||
}, | ||
"System": { | ||
"Users": [] | ||
} | ||
} |