added auth handlers, added role seed, separate username/email, implemented some web endpoints
This commit is contained in:
parent
1f94b624d2
commit
0ad552c334
@ -32,6 +32,8 @@ namespace Selector.Model
|
|||||||
.HasOne(w => w.User)
|
.HasOne(w => w.User)
|
||||||
.WithMany(u => u.Watchers)
|
.WithMany(u => u.Watchers)
|
||||||
.HasForeignKey(w => w.UserId);
|
.HasForeignKey(w => w.UserId);
|
||||||
|
|
||||||
|
SeedData.Seed(modelBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,4 +19,33 @@ namespace Selector.Model
|
|||||||
|
|
||||||
public List<Watcher> Watchers { get; set; }
|
public List<Watcher> Watchers { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ApplicationUserDTO
|
||||||
|
{
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string PhoneNumber { get; set; }
|
||||||
|
|
||||||
|
public bool SpotifyIsLinked { get; set; }
|
||||||
|
public DateTime SpotifyLastRefresh { get; set; }
|
||||||
|
public int SpotifyTokenExpiry { get; set; }
|
||||||
|
public string SpotifyAccessToken { get; set; }
|
||||||
|
public string SpotifyRefreshToken { get; set; }
|
||||||
|
|
||||||
|
public string LastFmUsername { get; set; }
|
||||||
|
|
||||||
|
public static explicit operator ApplicationUserDTO(ApplicationUser user) => new() {
|
||||||
|
UserName = user.UserName,
|
||||||
|
Email = user.Email,
|
||||||
|
PhoneNumber = user.PhoneNumber,
|
||||||
|
|
||||||
|
SpotifyIsLinked = user.SpotifyIsLinked,
|
||||||
|
SpotifyLastRefresh = user.SpotifyLastRefresh,
|
||||||
|
SpotifyTokenExpiry = user.SpotifyTokenExpiry,
|
||||||
|
SpotifyAccessToken = user.SpotifyAccessToken,
|
||||||
|
SpotifyRefreshToken = user.SpotifyRefreshToken,
|
||||||
|
|
||||||
|
LastFmUsername = user.LastFmUsername
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
36
Selector.Model/Authorisation/Constants.cs
Normal file
36
Selector.Model/Authorisation/Constants.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||||
|
|
||||||
|
namespace Selector.Model.Authorisation
|
||||||
|
{
|
||||||
|
public static class WatcherOperations
|
||||||
|
{
|
||||||
|
public static OperationAuthorizationRequirement Create = new() { Name = Constants.CreateOpName };
|
||||||
|
public static OperationAuthorizationRequirement Read = new() { Name = Constants.ReadOpName };
|
||||||
|
public static OperationAuthorizationRequirement Update = new() { Name = Constants.UpdateOpName };
|
||||||
|
public static OperationAuthorizationRequirement Delete = new() { Name = Constants.DeleteOpName };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UserOperations
|
||||||
|
{
|
||||||
|
public static OperationAuthorizationRequirement Create = new() { Name = Constants.CreateOpName };
|
||||||
|
public static OperationAuthorizationRequirement Read = new() { Name = Constants.ReadOpName };
|
||||||
|
public static OperationAuthorizationRequirement Update = new() { Name = Constants.UpdateOpName };
|
||||||
|
public static OperationAuthorizationRequirement Delete = new() { Name = Constants.DeleteOpName };
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Constants
|
||||||
|
{
|
||||||
|
public const string CreateOpName = "Create";
|
||||||
|
public const string ReadOpName = "Read";
|
||||||
|
public const string UpdateOpName = "Update";
|
||||||
|
public const string DeleteOpName = "Delete";
|
||||||
|
|
||||||
|
public const string AdminRole = "Admin";
|
||||||
|
}
|
||||||
|
}
|
34
Selector.Model/Authorisation/User/UserIsAdminAuthHandler.cs
Normal file
34
Selector.Model/Authorisation/User/UserIsAdminAuthHandler.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace Selector.Model.Authorisation
|
||||||
|
{
|
||||||
|
public class UserIsAdminAuthHandler
|
||||||
|
: AuthorizationHandler<OperationAuthorizationRequirement, ApplicationUser>
|
||||||
|
{
|
||||||
|
protected override Task HandleRequirementAsync(
|
||||||
|
AuthorizationHandlerContext context,
|
||||||
|
OperationAuthorizationRequirement requirement,
|
||||||
|
ApplicationUser resource
|
||||||
|
) {
|
||||||
|
if (context.User == null || resource == null)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.User.IsInRole(Constants.AdminRole))
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
Selector.Model/Authorisation/User/UserIsSelfAuthHandler.cs
Normal file
48
Selector.Model/Authorisation/User/UserIsSelfAuthHandler.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace Selector.Model.Authorisation
|
||||||
|
{
|
||||||
|
public class UserIsSelfAuthHandler
|
||||||
|
: AuthorizationHandler<OperationAuthorizationRequirement, ApplicationUser>
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> userManager;
|
||||||
|
|
||||||
|
public UserIsSelfAuthHandler(UserManager<ApplicationUser> userManager)
|
||||||
|
{
|
||||||
|
this.userManager = userManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task HandleRequirementAsync(
|
||||||
|
AuthorizationHandlerContext context,
|
||||||
|
OperationAuthorizationRequirement requirement,
|
||||||
|
ApplicationUser resource
|
||||||
|
) {
|
||||||
|
if (context.User == null || resource == null)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requirement.Name != Constants.ReadOpName &&
|
||||||
|
requirement.Name != Constants.UpdateOpName &&
|
||||||
|
requirement.Name != Constants.DeleteOpName)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.Id == userManager.GetUserId(context.User))
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace Selector.Model.Authorisation
|
||||||
|
{
|
||||||
|
public class WatcherIsAdminAuthHandler
|
||||||
|
: AuthorizationHandler<OperationAuthorizationRequirement, Watcher>
|
||||||
|
{
|
||||||
|
protected override Task HandleRequirementAsync(
|
||||||
|
AuthorizationHandlerContext context,
|
||||||
|
OperationAuthorizationRequirement requirement,
|
||||||
|
Watcher resource
|
||||||
|
) {
|
||||||
|
if (context.User == null || resource == null)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.User.IsInRole(Constants.AdminRole))
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace Selector.Model.Authorisation
|
||||||
|
{
|
||||||
|
public class WatcherIsOwnerAuthHandler
|
||||||
|
: AuthorizationHandler<OperationAuthorizationRequirement, Watcher>
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> userManager;
|
||||||
|
|
||||||
|
public WatcherIsOwnerAuthHandler(UserManager<ApplicationUser> userManager)
|
||||||
|
{
|
||||||
|
this.userManager = userManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task HandleRequirementAsync(
|
||||||
|
AuthorizationHandlerContext context,
|
||||||
|
OperationAuthorizationRequirement requirement,
|
||||||
|
Watcher resource
|
||||||
|
) {
|
||||||
|
if (context.User == null || resource == null)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requirement.Name != Constants.CreateOpName &&
|
||||||
|
requirement.Name != Constants.ReadOpName &&
|
||||||
|
requirement.Name != Constants.UpdateOpName &&
|
||||||
|
requirement.Name != Constants.DeleteOpName)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.UserId == userManager.GetUserId(context.User))
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
Selector.Model/SeedData.cs
Normal file
33
Selector.Model/SeedData.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
using Selector.Model.Authorisation;
|
||||||
|
|
||||||
|
namespace Selector.Model
|
||||||
|
{
|
||||||
|
public static class SeedData
|
||||||
|
{
|
||||||
|
public static void Seed(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<IdentityRole>().HasData(
|
||||||
|
GetRole(Constants.AdminRole, "00c64c0a-3387-4933-9575-83443fa9092b")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityRole GetRole(string name, string id)
|
||||||
|
{
|
||||||
|
return new IdentityRole
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
NormalizedName = name.ToUpperInvariant(),
|
||||||
|
Id = id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,9 @@ namespace Selector.Model
|
|||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
public string UserId { get; set; }
|
public string UserId { get; set; }
|
||||||
|
[Required]
|
||||||
public ApplicationUser User { get; set; }
|
public ApplicationUser User { get; set; }
|
||||||
|
|
||||||
public WatcherType Type { get; set; }
|
public WatcherType Type { get; set; }
|
||||||
|
@ -14,9 +14,9 @@
|
|||||||
<hr />
|
<hr />
|
||||||
<div asp-validation-summary="All" class="text-danger"></div>
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="Input.Email"></label>
|
<label asp-for="Input.Username"></label>
|
||||||
<input asp-for="Input.Email" class="form-control" />
|
<input asp-for="Input.Username" class="form-control" />
|
||||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
<span asp-validation-for="Input.Username" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="Input.Password"></label>
|
<label asp-for="Input.Password"></label>
|
||||||
|
@ -45,8 +45,7 @@ namespace Selector.Web.Areas.Identity.Pages.Account
|
|||||||
public class InputModel
|
public class InputModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[EmailAddress]
|
public string Username { get; set; }
|
||||||
public string Email { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[DataType(DataType.Password)]
|
[DataType(DataType.Password)]
|
||||||
@ -83,7 +82,7 @@ namespace Selector.Web.Areas.Identity.Pages.Account
|
|||||||
{
|
{
|
||||||
// This doesn't count login failures towards account lockout
|
// This doesn't count login failures towards account lockout
|
||||||
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
|
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
|
||||||
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
|
var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberMe, lockoutOnFailure: false);
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("User logged in.");
|
_logger.LogInformation("User logged in.");
|
||||||
|
@ -12,6 +12,11 @@
|
|||||||
<h4>Create a new account.</h4>
|
<h4>Create a new account.</h4>
|
||||||
<hr />
|
<hr />
|
||||||
<div asp-validation-summary="All" class="text-danger"></div>
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.Username"></label>
|
||||||
|
<input asp-for="Input.Username" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.Username" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="Input.Email"></label>
|
<label asp-for="Input.Email"></label>
|
||||||
<input asp-for="Input.Email" class="form-control" />
|
<input asp-for="Input.Email" class="form-control" />
|
||||||
|
@ -47,6 +47,10 @@ namespace Selector.Web.Areas.Identity.Pages.Account
|
|||||||
|
|
||||||
public class InputModel
|
public class InputModel
|
||||||
{
|
{
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "Username")]
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[EmailAddress]
|
[EmailAddress]
|
||||||
[Display(Name = "Email")]
|
[Display(Name = "Email")]
|
||||||
@ -76,7 +80,7 @@ namespace Selector.Web.Areas.Identity.Pages.Account
|
|||||||
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||||
if (ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email };
|
var user = new ApplicationUser { UserName = Input.Username, Email = Input.Email };
|
||||||
var result = await _userManager.CreateAsync(user, Input.Password);
|
var result = await _userManager.CreateAsync(user, Input.Password);
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
|
34
Selector.Web/Controller/BaseAuthController.cs
Normal file
34
Selector.Web/Controller/BaseAuthController.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
using Selector.Model;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Selector.Web.Controller {
|
||||||
|
|
||||||
|
public class BaseAuthController: Microsoft.AspNetCore.Mvc.Controller
|
||||||
|
{
|
||||||
|
protected ApplicationDbContext Context { get; }
|
||||||
|
protected IAuthorizationService AuthorizationService { get; }
|
||||||
|
protected UserManager<ApplicationUser> UserManager { get; }
|
||||||
|
protected ILogger<BaseAuthController> Logger { get; }
|
||||||
|
|
||||||
|
public BaseAuthController(
|
||||||
|
ApplicationDbContext context,
|
||||||
|
IAuthorizationService auth,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
ILogger<BaseAuthController> logger
|
||||||
|
) {
|
||||||
|
Context = context;
|
||||||
|
AuthorizationService = auth;
|
||||||
|
UserManager = userManager;
|
||||||
|
Logger = logger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,46 +4,91 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Selector.Model;
|
|
||||||
|
|
||||||
namespace Selector.Web.Controller {
|
using Selector.Model;
|
||||||
|
using Selector.Model.Authorisation;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Selector.Web.Controller
|
||||||
|
{
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class UsersController {
|
public class UsersController : BaseAuthController
|
||||||
|
|
||||||
private readonly ApplicationDbContext db;
|
|
||||||
|
|
||||||
public UsersController(ApplicationDbContext context)
|
|
||||||
{
|
{
|
||||||
db = context;
|
public UsersController(
|
||||||
}
|
ApplicationDbContext context,
|
||||||
|
IAuthorizationService auth,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
ILogger<UsersController> logger
|
||||||
|
) : base(context, auth, userManager, logger) { }
|
||||||
|
|
||||||
[HttpGet()]
|
[HttpGet]
|
||||||
public async Task<ActionResult<IEnumerable<ApplicationUser>>> Get(string username)
|
[Authorize(Roles = Constants.AdminRole)]
|
||||||
|
public async Task<ActionResult<IEnumerable<ApplicationUserDTO>>> Get()
|
||||||
{
|
{
|
||||||
// TODO: Authorise
|
// TODO: Authorise
|
||||||
return await db.Users.ToListAsync();
|
return await Context.Users.AsNoTracking().Select(u => (ApplicationUserDTO)u).ToListAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class UserController {
|
public class UserController : BaseAuthController
|
||||||
|
|
||||||
private readonly ApplicationDbContext db;
|
|
||||||
|
|
||||||
public UserController(ApplicationDbContext context)
|
|
||||||
{
|
{
|
||||||
db = context;
|
public UserController(
|
||||||
|
ApplicationDbContext context,
|
||||||
|
IAuthorizationService auth,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
ILogger<UserController> logger
|
||||||
|
) : base(context, auth, userManager, logger) { }
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<ApplicationUserDTO>> Get()
|
||||||
|
{
|
||||||
|
var userId = UserManager.GetUserId(User);
|
||||||
|
var user = await Context.Users.AsNoTracking().FirstOrDefaultAsync(u => u.Id == userId);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
Logger.LogWarning($"No user found for [{userId}], even though the 'me' route was used");
|
||||||
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{username}")]
|
var isAuthed = await AuthorizationService.AuthorizeAsync(User, user, UserOperations.Read);
|
||||||
public async Task<ActionResult<ApplicationUser>> Get(string username)
|
|
||||||
|
if (!isAuthed.Succeeded)
|
||||||
{
|
{
|
||||||
// TODO: Implement
|
Logger.LogWarning($"User [{user.UserName}] not authorised to view themselves?");
|
||||||
return await db.Users.SingleAsync();
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ApplicationUserDTO)user;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<ActionResult<ApplicationUserDTO>> GetById(string id)
|
||||||
|
{
|
||||||
|
var usernameUpper = id.ToUpperInvariant();
|
||||||
|
|
||||||
|
var user = await Context.Users.AsNoTracking().FirstOrDefaultAsync(u => u.Id == id)
|
||||||
|
?? await Context.Users.AsNoTracking().FirstOrDefaultAsync(u => u.NormalizedUserName == usernameUpper);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var isAuthed = await AuthorizationService.AuthorizeAsync(User, user, UserOperations.Read);
|
||||||
|
|
||||||
|
if (!isAuthed.Succeeded)
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ApplicationUserDTO)user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,46 +4,72 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
using Selector.Model;
|
using Selector.Model;
|
||||||
|
using Selector.Model.Authorisation;
|
||||||
|
|
||||||
namespace Selector.Web.Controller {
|
namespace Selector.Web.Controller {
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class WatchersController {
|
public class WatchersController : BaseAuthController
|
||||||
|
|
||||||
private readonly ApplicationDbContext db;
|
|
||||||
|
|
||||||
public WatchersController(ApplicationDbContext context)
|
|
||||||
{
|
{
|
||||||
db = context;
|
public WatchersController(
|
||||||
}
|
ApplicationDbContext context,
|
||||||
|
IAuthorizationService auth,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
ILogger<WatchersController> logger
|
||||||
|
) : base(context, auth, userManager, logger) { }
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<IEnumerable<Watcher>>> Get()
|
public async Task<ActionResult<IEnumerable<Watcher>>> Get()
|
||||||
{
|
{
|
||||||
// TODO: Authorise
|
var isAuthed = User.IsInRole(Constants.AdminRole);
|
||||||
return await db.Watcher.ToListAsync();
|
|
||||||
|
if(isAuthed)
|
||||||
|
{
|
||||||
|
return await Context.Watcher.AsNoTracking().ToListAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var userId = UserManager.GetUserId(User);
|
||||||
|
return await Context.Watcher.AsNoTracking().Where(w => w.UserId == userId).ToListAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class WatcherController {
|
public class WatcherController : BaseAuthController
|
||||||
|
|
||||||
private readonly ApplicationDbContext db;
|
|
||||||
|
|
||||||
public WatcherController(ApplicationDbContext context)
|
|
||||||
{
|
{
|
||||||
db = context;
|
public WatcherController(
|
||||||
}
|
ApplicationDbContext context,
|
||||||
|
IAuthorizationService auth,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
ILogger<WatcherController> logger
|
||||||
|
) : base(context, auth, userManager, logger) { }
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<ActionResult<Watcher>> Get(int id)
|
public async Task<ActionResult<Watcher>> Get(int id)
|
||||||
{
|
{
|
||||||
// TODO: Implement
|
var watcher = await Context.Watcher.AsNoTracking().FirstOrDefaultAsync(w => w.Id == id);
|
||||||
return await db.Watcher.FirstAsync();
|
|
||||||
|
if(watcher is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var isAuthed = await AuthorizationService.AuthorizeAsync(User, watcher, WatcherOperations.Read);
|
||||||
|
|
||||||
|
if(!isAuthed.Succeeded)
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
return watcher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="display-4">Welcome</h1>
|
<h1 class="display-4">Welcome</h1>
|
||||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
|
||||||
<partial name="_LoginPartial" />
|
<partial name="_LoginPartial" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
using Selector.Model;
|
using Selector.Model;
|
||||||
|
using Selector.Model.Authorisation;
|
||||||
|
|
||||||
namespace Selector.Web
|
namespace Selector.Web
|
||||||
{
|
{
|
||||||
@ -80,6 +81,12 @@ namespace Selector.Web
|
|||||||
.RequireAuthenticatedUser()
|
.RequireAuthenticatedUser()
|
||||||
.Build();
|
.Build();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.AddScoped<IAuthorizationHandler, WatcherIsOwnerAuthHandler>();
|
||||||
|
services.AddSingleton<IAuthorizationHandler, WatcherIsAdminAuthHandler>();
|
||||||
|
|
||||||
|
services.AddScoped<IAuthorizationHandler, UserIsSelfAuthHandler>();
|
||||||
|
services.AddSingleton<IAuthorizationHandler, UserIsAdminAuthHandler>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
Loading…
Reference in New Issue
Block a user