У меня есть приложение, в котором вошедшие в систему пользователи должны просматривать, создавать, редактировать или удалять только своих клиентов.
Я пытался реализовать это с помощью политик и требований с ядром asp.net.Однако после добавления атрибута авторизации в моем контроллере я получаю сообщение об ошибке «Отказано в доступе».
Я создал отдельные политики для создания и редактирования клиентов.
Вот запуск.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using ProsperCRM.Models;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using ProsperCRM.Areas.Identity.Services;
using Microsoft.AspNetCore.Identity;
namespace ProsperCRM
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//services.AddDbContext<ProsperCRMContext>(options =>
//options.UseSqlServer(Configuration.GetConnectionString("ProsperCRMContext")));
services.AddDbContext<ProsperCRMContext>(options =>
options.UseSqlite("Data Source=ProsperCRM.db"));
// add authorization - each user can view, edit and delete only their own customers. the admin can view, delete and edit all customers in addition to creating new user accounts.
services.AddAuthorization(options =>
{
options.AddPolicy("CreateAndEditCustomers", policy => policy.Requirements.Add(new CustomerAuthorizationRequirement()));
});
services.AddAuthorization(options =>
{
options.AddPolicy("CreateCustomers", policy => policy.Requirements.Add(new CustomerCreationRequirement()));
});
services.AddAuthorization(options =>
{
options.AddPolicy("CreateNewUsers", policy => policy.RequireRole("Admin"));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
Вот одно из требований со свойством UserId:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using ProsperCRM.Areas.Identity.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ProsperCRM.Areas.Identity.Services
{
public class CustomerAuthorizationRequirement : IAuthorizationRequirement
{
public string UserId { get; set; }
public CustomerAuthorizationRequirement()
{
}
}
}
Вот обработчик требования:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using ProsperCRM.Models;
namespace ProsperCRM.Areas.Identity.Services
{
public class CustomerAuthorizationHandler : AuthorizationHandler<CustomerAuthorizationRequirement, Customer>
{
private readonly UserManager<Data.ProsperCRMUser> _userManager;
public CustomerAuthorizationHandler(UserManager<Data.ProsperCRMUser> userManager)
{
_userManager = userManager;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomerAuthorizationRequirement requirement, Customer resource)
{
// var userId = _userManager.GetUserId(context.User);
requirement.UserId = _userManager.GetUserId(context.User);
if (resource.ApplicationUserId == requirement.UserId)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
И, наконец,, контроллер клиентов, куда я добавляю атрибуты:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using ProsperCRM.Areas.Identity.Data;
using ProsperCRM.Areas.Identity.Services;
using ProsperCRM.Models;
using SendGrid;
using SendGrid.Helpers.Mail;
namespace ProsperCRM.Controllers
{
public class CustomersController : Controller
{
private readonly ProsperCRMContext _context;
private readonly IConfiguration _configuration;
public CustomersController(ProsperCRMContext context, IConfiguration configuration)
{
_context = context;
_configuration = configuration;
}
// GET: Customers
[Authorize]
// [Authorize(Policy = "CreateAndEditCustomers")]
public async Task<IActionResult> Index()
{
CustomerViewModel model = new CustomerViewModel();
// only customers for logged in user
var list = await _context.Customer.ToListAsync();
// customers for all users
// var list = await _context.Customer.ToListAsync();
model.Customers = list;
return View(model);
}
[Authorize(Policy = "CreateAndEditCustomers")]
// GET: Customers/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var customer = await _context.Customer
.FirstOrDefaultAsync(m => m.ID == id);
if (customer == null)
{
return NotFound();
}
return View(customer);
}
// GET: Customers/Create
[Authorize]
public IActionResult Create()
{
return View();
}
// POST: Customers/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Policy = "CreateCustomers")]
public async Task<IActionResult> Create([Bind("ID,FirstName,LastName,PhoneNumber,Email")] Customer customer)
{
if (ModelState.IsValid)
{
// customer.ApplicationUserId = _userManager.GetUserId(User);
_context.Add(customer);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(customer);
}
// GET: Customers/Edit/5
[Authorize(Policy = "CreateAndEditCustomers")]
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var customer = await _context.Customer.FindAsync(id);
if (customer == null)
{
return NotFound();
}
/* old approach
// User can only edit their entries
var userId = _userManager.GetUserId(User);
if (customer.ApplicationUserId != userId)
{
return NotFound();
}
*/
return View(customer);
}
// POST: Customers/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Policy = "CreateAndEditCustomers")]
public async Task<IActionResult> Edit(int id, [Bind("ID,FirstName,LastName,PhoneNumber,Email")] Customer customer)
{
if (id != customer.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(customer);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomerExists(customer.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(customer);
}
// GET: Customers/Delete/5
[Authorize(Policy = "CreateAndEditCustomers")]
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var customer = await _context.Customer
.FirstOrDefaultAsync(m => m.ID == id);
if (customer == null)
{
return NotFound();
}
return View(customer);
}
// POST: Customers/Delete/5
[Authorize(Policy = "CreateAndEditCustomers")]
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var customer = await _context.Customer.FindAsync(id);
_context.Customer.Remove(customer);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool CustomerExists(int id)
{
return _context.Customer.Any(e => e.ID == id);
}
[HttpPost]
public IActionResult SendEmail(CustomerViewModel model)
{
// why returning notfound?
if (model.SelectedCustomers == null)
{
return NotFound();
}
List<EmailAddressViewModel> nameAndEmail = new List<EmailAddressViewModel>();
var customers = model.SelectedCustomers;
for (var i = 0; i < customers.Count; i++)
{
if (customers[i].IsSelected)
{
nameAndEmail.Add( new EmailAddressViewModel { Email = model.Customers[i].Email, Name = model.Customers[i].LastName } );
}
}
SendGridViewModel sendModel = new SendGridViewModel();
sendModel.To = nameAndEmail;
// ViewBag.Data = nameAndEmail;
return View(sendModel);
}
[HttpPost]
public async Task<IActionResult> SubmitEmail(SendGridViewModel model)
{
var tos = new List<EmailAddress>();
var apiKey = _configuration.GetSection("SENDGRID_API_KEY").Value;
var client = new SendGridClient(apiKey);
var from = new EmailAddress("subs22222@gmail.com", "Example User");
var subject = model.Subject;
for (int i = 0; i < model.To.Count; i++)
{
tos.Add(new EmailAddress(model.To[i].Email, model.To[i].Name));
}
// var to = new EmailAddress("star113@gmail.com", "Example User");
var plainTextContent = model.PlainTextContent;
var htmlContent = "<strong>" + model.PlainTextContent + "</strong>";
var msg = MailHelper.CreateSingleEmailToMultipleRecipients(from, tos, subject, plainTextContent, htmlContent);
var response = await client.SendEmailAsync(msg);
return RedirectToAction(nameof(Index));
}
}
}
Любая помощь будет принята с благодарностью.