Variable in Authorization Attribute .net in base Controller - c#

In my application, I'm trying to build an authorization - privilege based authentication.
For better understanding here's how my privileges are named and stored in my database : "GET_USERS" , "GET_USER", "DELETE_USER" ...
What I want to do is to specify the privilege name in the authorization attribute from my base controller, but the problem is that the authorization attribute only allows constant parameters.
Here's my base Controller :
public class BaseController<T> : ControllerBase where T : class
{
protected readonly ConcreetDataContext _context;
protected readonly IBaseRepository<T> _repo;
protected readonly INotificationRepository _notificationRepo;
protected readonly IHubContext<NotificationHub> _hubContext;
protected readonly DbSet<T> _dbSet;
public BaseController(IHubContext<NotificationHub> hubContext,
ConcreetDataContext context,
IBaseRepository<T> repo,
INotificationRepository notifRepo)
{
_context = context;
_hubContext = hubContext;
_repo = repo;
_dbSet = _context.Set<T>();
_notificationRepo = notifRepo;
}
// GET: api/[items]
[HttpGet]
// HERE's THE ISSUE
[PrivilegeAuthorize("GET_"+typeof(T).Name.toUpper()] // this is not allowed
public async Task<ActionResult<IEnumerable<T>>> Get([FromQuery] GlobalParams globalParams)
{
Type t = typeof(T);
Console.WriteLine(t.Name.ToUpper());
var classes = await PagedList<T>.CreateAsync(_repo.Get(globalParams),globalParams.PageNumber,globalParams.PageSize);
Response.AddPagination(classes.CurrentPage, classes.PageSize, classes.TotalCount, classes.TotalPages);
return Ok(classes);
}
// GET: api/[items]/5
[HttpGet("{id}")]
public virtual async Task<ActionResult<T>> Get(int id)
{
var item = await this._repo.GetByID(id);
if (item == null)
{
return NotFound();
}
return item;
}
// PUT: api/[items]/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
public async Task<IActionResult> Put(int id, T item)
{
// if (id != item.ID)
// {
// return BadRequest();
// }
try
{
await this._repo.Update(item);
// Creating the notification
await this._notificationRepo.CreateNotification("Update",typeof(T).ToString(),"Updated "+typeof(T).ToString()+" with ID : "+id);
}
catch (DbUpdateConcurrencyException)
{
if (!(await Exists(id)))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Classes
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async virtual Task<ActionResult<T>> Post(T item)
{
await this._repo.Insert(item);
await this._notificationRepo.CreateNotification("Create",typeof(T).ToString(),"Created "+typeof(T).ToString());
return CreatedAtAction("Get", item);
}
// DELETE: api/Classes/5
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var item = await _dbSet.FindAsync(id);
if (item == null)
{
return NotFound();
}
_dbSet.Remove(item);
await _context.SaveChangesAsync();
var notification = await this._notificationRepo.CreateNotification("Delete",typeof(T).ToString(),"Deleted "+typeof(T).ToString());
// Invoking BroadCastToUserFunction
var useID = Request.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value;
await _hubContext.Clients.User(useID).SendAsync("simo",notification);
return NoContent();
}
private async Task<bool> Exists(int id)
{
var item = await this._repo.GetByID(id);
if(item != null)
return true;
return false;
}
[HttpGet("count")]
public async Task<ActionResult<int>> Count([FromQuery] GlobalParams globalParams)
{
return await this._repo.Count(globalParams);
}
}
Thank you in advance !

As Lasse V. Karlsen pointed out in comments: "base class is compiled once, it is not compiled for each Variant, so the compiler will have to figure out what to pass as a string there once".
So I went with the following solution for the moment :
[PrivilegeAuthorize("GET_USERS")]
public override Task<ActionResult<IEnumerable<City>>> Get([FromQuery] GlobalParams globalParams)
{
return base.Get(globalParams);
}
I should override each method where I want to apply this authorization attribute.

Related

How can I use unit of work pattern with generic repository pattern in my ASP.NET Core Web API project?

There are many examples on the internet about the use of unit of work and generic repository together, but I couldn't find exactly how to apply the unit of work repository pattern to my own project. Because everyone did it differently.
Actually, I set up the unit of work structure, but I don't quite understand how I can use it in my own project? How can I apply the unit of work repository I made to my own project? Can you help me with this? Can you tell me if I have a mistake? This is how I saw the unit of work repository on the internet and implemented it.
First of all, if I just talk about my project, my project is an ASP.NET Core Web API project and basically a 3-layer structure:
API layer. I have controllers in the API layer.
Second is the business layer. The business layer only serves to communicate with the data access layer.
The third layer is the data access layer, in this layer I do the database operations, such as adding, deleting, updating.
I'm doing these with the generic repository. As an example, I am sharing some of my code below.
I just shared category as an example, but I have more than one class like category.
API layer - CategoriesController:
[Route("api/[controller]")]
[ApiController]
public class categoriesController : ControllerBase
{
private ICategoryService category_service;
DatabaseContext c = new DatabaseContext();
public categoriesController(ICategoryService category_service)
{
this.category_service = category_service;
}
[HttpGet("getall")]
public async Task<IActionResult> Get()
{
return Ok(await category_service.TGetAll());
}
[HttpGet("getbyid/{id}")]
public async Task<IActionResult> GetByIdCategory(int id)
{
var category = await category_service.TGetById(id);
if (category != null)
{
return Ok(category); // 200 ok
}
else
{
return NotFound(); //404 not found
}
}
[HttpPost("add")]
public async Task<IActionResult> Add(Category category)
{
var result = category_service.TAdd(category);
if (result != null)
{
return Ok(result);
}
return BadRequest(result);
}
}
Business layer - CategoryManager:
public class CategoryManager:ICategoryService
{
ICategoryDal _categoryDal;
public CategoryManager(ICategoryDal _cateogoryDal)
{
this._categoryDal = _cateogoryDal;
}
public async Task<List<Category>> TGetAll()
{
return await _categoryDal.GetListAll();
}
public async Task<Category> TGetById(int id)
{
return await _categoryDal.GetByID(id);
}
public async Task TAdd(Category entity)
{
await _categoryDal.Insert(entity);
}
public async Task TDelete(Category entity)
{
await _categoryDal.Delete(entity);
}
public async Task TUpdate(Category entity)
{
await _categoryDal.Update(entity);
}
}
Data Access layer - CategoryRepository:
public class CategoryRepository : GenericRepository<Category>, ICategoryDal
{
}
GenericRepository:
public class GenericRepository<T> : IGenericDal<T> where T : class
{
protected DatabaseContext dbContext;
public GenericRepository(DatabaseContext context)
{
dbContext = context;
}
public async Task Delete(T t)
{
dbContext.Remove(t);
await dbContext.SaveChangesAsync();
}
public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
{
return dbContext.Set<T>()
.Where(expression)
.AsNoTracking();
}
public async Task<T> GetByID(int id)
{
return await dbContext.Set<T>().FindAsync(id);
}
public async Task<List<T>> GetListAll()
{
return await dbContext.Set<T>().ToListAsync();
}
public async Task<List<T>> GetListAll(Expression<Func<T, bool>> filter)
{
return await dbContext.Set<T>()
.Where(filter)
.ToListAsync();
}
public async Task Insert(T t)
{
await dbContext.AddAsync(t);
await dbContext.SaveChangesAsync();
}
public async Task Update(T t)
{
var updatedEntity = dbContext.Entry(t);
updatedEntity.State = EntityState.Modified;
dbContext.SaveChanges();
}
}
UnitOfWork:
public class UnitOfWorkRepository : IUnitOfWork
{
private readonly DatabaseContext _dbContext;
private IDbContextTransaction _transaction;
private bool _disposed;
public UnitOfWorkRepository(DatabaseContext dbContext)
{
_dbContext = dbContext;
}
public bool BeginNewTransaction()
{
try
{
_transaction = _dbContext.Database.BeginTransaction();
return true;
}
catch (Exception ex)
{
return false;
}
}
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
_dbContext.Dispose();
}
}
this._disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public IGenericDal<T> GetRepository<T>() where T : class
{
return new GenericRepository<T>(_dbContext);
}
public bool RollBackTransaction()
{
try
{
_transaction.Rollback();
_transaction = null;
return true;
}
catch (Exception ex)
{
return false;
}
}
public int SaveChanges()
{
var transaction = _transaction != null ? _transaction : _dbContext.Database.BeginTransaction();
using (transaction)
{
try
{
// Context boş ise hata fırlatıyoruz
if (_dbContext == null)
{
throw new ArgumentException("Context is null");
}
// SaveChanges metodundan dönen int result ı yakalayarak geri dönüyoruz.
int result = _dbContext.SaveChanges();
// Sorun yok ise kuyruktaki tüm işlemleri commit ederek bitiriyoruz.
transaction.Commit();
return result;
}
catch (Exception ex)
{
// Hata ile karşılaşılır ise işlemler geri alınıyor
transaction.Rollback();
throw new Exception("Error on SaveChanges ", ex);
}
}
}
}
IUnitOfWork:
public interface IUnitOfWork : IDisposable
{
bool BeginNewTransaction();
bool RollBackTransaction();
IGenericDal<T> GetRepository<T>() where T : class;
int SaveChanges();
}
You can check the source in the link.
https://www.linkedin.com/pulse/repository-unit-work-patterns-net-core-dimitar-iliev/

MongoDB / Asp.Net Core remove method error with "filter"

I am working on a Url Shortener in Asp.Net Core and using MongoDB.
I currently have a working Get method and a working Create method.
I ran into an issue with my Delete method and this is the message I get:
Argument 1: cannot convert from
'MongoDB.Driver.FilterDefinition)', candidates
are: System.Threading.Tasks.Task
DeleteOneAsync(MongoDB.Driver.FilterDefinition,
System.Threading.CancellationToken)(in interface
IMongoCollection)
System.Threading.Tasks.Task
DeleteOneAsync(this
MongoDB.Driver.IMongoCollection,
System.Linq.Expressions.Expression>,
System.Threading.CancellationToken) (in class
IMongoCollectionExtensions)
The error has something to do with this ".DeleteOneAsync(filter);" in my 'ShortUrlRepository' class:
public async Task<ShortUrl> Delete(ShortUrl su)
{
var filter = Builders<Url>.Filter.Eq("Su", su);
return await _db.Urls.DeleteOneAsync(filter);
}
My ShortUrlsController class seems to be working just fine but I will post it in case:
namespace ShortenUrls.Controllers
{
[Route("api/codes")]
public class ShortUrlsController : Controller
{
private readonly ShortUrlRepository _repo;
//private readonly IShortUrlService _service;
public ShortUrlsController(ShortUrlRepository repo /*IShortUrlService service*/)
{
_repo = repo;
//_service = service;
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
var su = await _repo.GetAsync(id);
if (su == null)
return NotFound();
return Ok(su);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] ShortUrl su)
{
await _repo.CreateAsync(su);
return Ok(su);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete (ShortUrl su)
{
try
{
if (su == null)
return new NotFoundResult();
await _repo.Delete(su);
return Ok("Deleted Successfully");
}
catch (Exception ex)
{
return BadRequest(ex.ToString());
}
}
}
}
I have tried other remove methods but have gotten similar errors so maybe I am missing something?
If anyone can offer some suggestions I would greatly appreciate it as I am new to Asp.Net Core and I am having very little success finding a solution to this error. If I can provide anymore information please let me know.
Creating the variable 'r' and returning it solved the 'Argument 1 error':
public async Task<bool> Delete(ObjectId id)
{
var filter = Builders<ShortUrl>.Filter.Eq(x => x.Id, id);
var r = await _db.Urls.DeleteOneAsync(filter);
return r.DeletedCount > 0;
}
I made other changes that were unrelated to this error but were necessary to make the Delete method work properly. Here are the changes I had to make to my 'ShortUrlsController' class:
[HttpDelete("{id}")]
public async Task<IActionResult> Delete (string id)
{
return (await _repo.Delete(ObjectId.Parse(id)))
? (IActionResult) Ok("Deleted Successfully")
: NotFound();
}

Cannot find HTTP resource on OData REST API

Hi I have an OData API that I'm trying to run locally but whenever it is being run I cannot load any of the resources. When I run the API, I get this screen with the information on the entities.
enter image description here
Whenever I try to navigate to one using a URL such as http://localhost:51578/Managers, I get an error saying No HTTP resource was found that matches the request URI 'http://localhost:51578/Managers'. Here is the code I have for the Manager Controller.
public class ManagerController : ODataController
{
DatabaseContext db = new DatabaseContext();
private bool ManagerExists(Guid key)
{
return db.Managers.Any(p => p.Id == key);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
[EnableQuery]
public SingleResult<Manager> Get([FromODataUri] Guid key)
{
IQueryable<Manager> result = db.Managers.Where(p => p.Id == key);
return SingleResult.Create(result);
}
[HttpPost]
public async Task<IHttpActionResult> Post(Manager manager)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Managers.Add(manager);
await db.SaveChangesAsync();
return Created(manager);
}
[HttpPatch]
public async Task<IHttpActionResult> Patch([FromODataUri] Guid key, Delta<Manager> manager)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var entity = await db.Managers.FindAsync(key);
if (entity == null)
{
return NotFound();
}
manager.Patch(entity);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ManagerExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(entity);
}
[HttpPut]
public async Task<IHttpActionResult> Put([FromODataUri] Guid key, Manager update)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (key != update.Id)
{
return BadRequest();
}
db.Entry(update).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ManagerExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(update);
}
[HttpDelete]
public async Task<IHttpActionResult> Delete([FromODataUri] Guid key)
{
var manager = await db.Managers.FindAsync(key);
if (manager == null)
{
return NotFound();
}
db.Managers.Remove(manager);
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}
}
Make sure that your WebApiConfig.cs file has the following:
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel(),
defaultHandler: HttpClientFactory.CreatePipeline(innerHandler: new HttpControllerDispatcher(config),
handlers: new[] { new ODataNullValueMessageHandler() }));
This will allow your route to be http://localhost:51578/Managers since we set "routePrefix" to null. If route prefix had something else like "v4" your url would look like http://localhost:51578/v4/Managers.

ASP.NET Boilerplate: Get and GetAll is POST instead of GET method

I'm using the ASP.NET Boilerplate framework to do a small project and I used Swagger UI Integration to check the API within the project. I noticed that the Get and GetAll are still using POST method while the others are in their respective HTTP verb for example Create is POST, Edit is UPDATE, etc... I read the documentation and tried what they suggested in this [Documentation]
(https://aspnetboilerplate.com/Pages/Documents/Dynamic-Web-API#http-verbs)
Edit 1: Code for UserAppServiceClass
[AbpAuthorize(PermissionNames.Pages_Users)]
public class UserAppService : AsyncCrudAppService<User, UserDto, long, PagedResultRequestDto, CreateUserDto, UpdateUserDto>, IUserAppService
{
private readonly UserManager _userManager;
private readonly RoleManager _roleManager;
private readonly IRepository<Role> _roleRepository;
public UserAppService(
IRepository<User, long> repository,
UserManager userManager,
IRepository<Role> roleRepository,
RoleManager roleManager)
: base(repository)
{
_userManager = userManager;
_roleRepository = roleRepository;
_roleManager = roleManager;
}
[HttpGet]
public override async Task<UserDto> Get(EntityDto<long> input)
{
var user = await base.Get(input);
var userRoles = await _userManager.GetRolesAsync(user.Id);
user.Roles = userRoles.Select(ur => ur).ToArray();
return user;
}
[HttpPost]
public override async Task<UserDto> Create(CreateUserDto input)
{
CheckCreatePermission();
var user = ObjectMapper.Map<User>(input);
user.TenantId = AbpSession.TenantId;
user.Password = new PasswordHasher().HashPassword(input.Password);
user.IsEmailConfirmed = true;
//Assign roles
user.Roles = new Collection<UserRole>();
foreach (var roleName in input.RoleNames)
{
var role = await _roleManager.GetRoleByNameAsync(roleName);
user.Roles.Add(new UserRole(AbpSession.TenantId, user.Id, role.Id));
}
CheckErrors(await _userManager.CreateAsync(user));
return MapToEntityDto(user);
}
[HttpPut]
public override async Task<UserDto> Update(UpdateUserDto input)
{
CheckUpdatePermission();
var user = await _userManager.GetUserByIdAsync(input.Id);
MapToEntity(input, user);
CheckErrors(await _userManager.UpdateAsync(user));
if (input.RoleNames != null)
{
CheckErrors(await _userManager.SetRoles(user, input.RoleNames));
}
return await Get(input);
}
[HttpDelete]
public override async Task Delete(EntityDto<long> input)
{
var user = await _userManager.GetUserByIdAsync(input.Id);
await _userManager.DeleteAsync(user);
}
[HttpGet]
public async Task<ListResultDto<RoleDto>> GetRoles()
{
var roles = await _roleRepository.GetAllListAsync();
return new ListResultDto<RoleDto>(ObjectMapper.Map<List<RoleDto>>(roles));
}
protected override User MapToEntity(CreateUserDto createInput)
{
var user = ObjectMapper.Map<User>(createInput);
return user;
}
protected override void MapToEntity(UpdateUserDto input, User user)
{
ObjectMapper.Map(input, user);
}
protected override IQueryable<User> CreateFilteredQuery(PagedResultRequestDto input)
{
return Repository.GetAllIncluding(x => x.Roles);
}
protected override async Task<User> GetEntityByIdAsync(long id)
{
var user = Repository.GetAllIncluding(x => x.Roles).FirstOrDefault(x => x.Id == id);
return await Task.FromResult(user);
}
protected override IQueryable<User> ApplySorting(IQueryable<User> query, PagedResultRequestDto input)
{
return query.OrderBy(r => r.UserName);
}
protected virtual void CheckErrors(IdentityResult identityResult)
{
identityResult.CheckErrors(LocalizationManager);
}
}
Edit 2:
Your Get method has complex input argument: Get(EntityDto<long> input). HTTP GET verb can't pass it to server, that is why ASP.NET Boilerplate has no choice, it decide to assign POST verb instead. Only parameters like /Get?name=Max&age=22 are allowed for GET - at this case method's signature will be like this: Get(string name, int age).
So you should leave all of it without changes or change signature to flattern variant, enumerating properties of EntityDto<long> as comma separated arguments.
You have to write Http Verb attributes to the interface. Not to the method implementations!
public interface IUserAppService: IApplicationService
{
[HttpGet]
GetUserOutput Get(GetInput input);
[HttpGet]
GetAllOutput GetAll(GetAllInput input);
}
Do it for IAsyncCrudAppService
PS: For the methods come from base, you can new them in your interface.

Not updating object on database ASP.net core 2.0

I am having a little issue that I cannot fully comprehend.
On my controller I have:
[HttpPut("{id}")]
public async Task<IActionResult> PutExercise([FromRoute] int id, [FromBody] Exercise exercise)
{
logger.LogInformation("Updating item {ID}", id);
if (!ModelState.IsValid)
return BadRequest(ModelState);
if (id != exercise.ExerciseId)
return BadRequest();
var baseExercise = await repository.GetExercise(id);
if (baseExercise == null)
return NotFound(id);
baseExercise = exercise;
await unitOfWork.CompleteAsync();
return NoContent();
}
but it is NOT updating on the database with the values provided on the PUT body request. Although on memory they are changed, the object is not pushed... But if I set a breakpoint on await unitOfWork.CompleteAsync(); and then modify baseExercise with the debugger, it does update. Why is this happening?
public class UnitOfWork : IUnitOfWork
{
private readonly FitaholicContext context;
public UnitOfWork(FitaholicContext context)
{
this.context = context;
}
public Task CompleteAsync()
{
return context.SaveChangesAsync();
}
}
--
public async Task<Exercise> GetExercise(int id, bool includeRelated = false)
{
if (!includeRelated)
return await context.Exercise.SingleOrDefaultAsync(m => m.ExerciseId == id);
return await context.Exercise
.Include(e => e.Equipments)
.ThenInclude(ee => ee.Equipment)
.Include(e => e.TypeRecord)
.SingleOrDefaultAsync(m => m.ExerciseId == id);
}
You should using async/await in the function:
public async Task CompleteAsync()
{
await context.SaveChangesAsync();
}
You are missing the link between the Repository and the UnitOfWork. In order to get the pattern working, and then persist your data, you should only access the repository's methods through the unit of work, i.e. the UoW should be just a wrapper for all the repositories related to one database, with the extra purpose of maintaining your data consistency.
In the controller your first retrieve baseExercise via repository.GetExercise(id), then you're trying to persist the data with unitOfWork.CompleteAsync(). The problem is that you didn't instruct the uniOfWork to care about the repository behaviour.
That said, it should be a quick fix.
First, set the first part of the link in the UnitOfWork:
public class UnitOfWork : IUnitOfWork
{
private readonly FitaholicContext context;
public UnitOfWork(FitaholicContext context)
{
this.context = context;
}
private Repository _repository;
public Repository repository =>
this._repository ?? (this._repository =
new Repository(this.context));
public Task CompleteAsync()
{
return context.SaveChangesAsync();
}
}
Then the second half, in the Repository:
public class Repository
{
public Repository(FitaholicContext context)
{ }
public async Task<Exercise> GetExercise(int id, bool includeRelated = false)
{ ... }
}
Last step, update PutExercise() in the controller to reflect the logic changes:
[HttpPut("{id}")]
public async Task<IActionResult> PutExercise([FromRoute] int id, [FromBody] Exercise exercise)
{
...
var baseExercise = await this.unitOfWork.repository.GetExercise(id);
...
await this.unitOfWork.CompleteAsync();
return NoContent();
}
If you need to dig a bit deeper into the Repository-UnitOfWork pattern, check the Microsoft Docs.

Categories