Best way to do property level authorization in ServiceStack? - c#

I'm currently developing a SPA in Angular, and so I've created a REST service using ServiceStack. I am also using ServiceStack's default authentication and authorization solution, which allows me to decorate services with the Authenticate attribute, and also allows me to authorize roles.
However, since my application has users, and users own resources, I need a way to restrict non-authorized users from performing certain actions. Furthermore, I would like to be able to create a single service for each discrete entity which can properly figure out what is safe to write to the database and what is safe to return to the user depending on their level of authorization.
So as an example, let's say I've created a service to handle operations on a Group entity. One of the actions I allow on a Group is to get the details for it:
Route: api/groups/{Id}
Response: Name, Description, CoverImageUrl, Members
However, depending on who the user is, I wish to restrict what data is returned:
Not authenticated: Name, CoverImageUrl
Authenticated: Name, CoverImageUrl, Decription
Member of requested group: Full access
Admin of website: Full access
So one simple approach to doing this is to create 3 different response DTOs, one for each type of response. Then in the service itself I can check who the user is, check on their relation to the resource, and return the appropriate response. The problem with this approach is that I would be repeating myself a lot, and would be creating DTOs that are simply subsets of the "master" DTO.
For me, the ideal solution would be some way to decorate each property on the DTO with attributes like:
[CanRead("Admin", "Owner", "Member")]
[CanWrite("Admin", "Owner")]
Then somewhere during the request, it would limit what is written to the database based on who the user is and would only serialize the subset of the "master" DTO that the user is permitted to read.
Does anyone know how I can attain my ideal solution within ServiceStack, or perhaps something even better?

The direct approach is the easiest, but you could also take advantage of custom filters attributes.
[Route("/groups/{Id}"]
public class UpdateGroup
{
public int Id { get; set; }
public string Name { get; set; }
public string CoverImageUrl { get; set; }
public string Description { get; set; }
}
[RequiresAnyRole("Admin", "FullAccess")]
[Route("/admin/groups/{Id}"]
public class AdminUpdateGroup
{
public int Id { get; set; }
public string Name { get; set; }
public string CoverImageUrl { get; set; }
public string Description { get; set; }
//... other admin properties
}
Service implementation:
public object Any(UpdateGroup request)
{
var session = base.SessionAs<AuthUserSession>();
if (session.IsAuthenticated) {
//.. update Name, CoverImageUrl, Description
}
else {
//.. only update Name, CoverImageUrl
}
}
public object Any(AdminUpdateGroup request)
{
//... Full Access
}

What ended up being the most pragmatic solution for me was actually pretty simple. The basic idea is that whichever service requires row-level authorization should implement a GetUserRole method, which in my case returns the user's most permissive role.
protected string GetUserRole(Domain.Group entity)
{
var session = SessionAs<AuthUserSession>();
var username = session.UserName;
if (session.Roles.Contains("Admin"))
{
return "Admin";
}
if (entity.Id == default(int) || entity.Leader.Username.Equals(username))
{
return "Leader";
}
// More logic here...
return session.IsAuthenticated ? "User" : "Anonymous";
}
Then I can use the user's role to figure out what to let them write:
var entityToWriteTo = ... // code that gets your entity
var userRole = GetUserRole(entityToWriteTo);
if (new[] {"Admin"}.Contains(userRole))
{
// write to admin-only entity properties
}
if (new[] {"Admin", "Leader"}.Contains(userRole))
{
// write to admin or leader entity properties
}
// Etc.
And the same logic applies for reads: You populate a DTO with properties set conditionally based on their role. Later on when you return the DTO back to the client, any properties that you haven't set either won't be serialized or will be serialized with a null value.
Ultimately, this solution allows you to use a single service for a resource instead of creating multiple services each with their own request DTO. There are, of course, refactorings you can do that makes this solution more streamlined. For example, you can isolate all of your reads and writes to one part of your code which will keep the services themselves free of role checks and things like that.

Related

Validation in a layered application ASP.NET Core

I'm designing a layered web application with an MVC, Service and Repository layer, however I'm having trouble knowing where to put validation logic that allows me to take advantage of .NET Core built in form validation (eg ModelStateDictionary), while following the DRY principle.
The first and most obvious approach is to use a ViewModel that has the appropriate data annotations:
public class VendorViewModel
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Phone { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Address { get; set; }
public DateTime? VerifiedAt { get; set; }
}
Then my controller action would look like this
public async Task<IActionResult> Create([FromForm] VendorViewModel model)
{
await AuthorizePolicyAsync(AuthorizationPolicyTypes.Vendor.Create);
if (!ModelState.IsValid) //Validation problems, so re-display the form.
return View(model);
await _vendorservice.CreateVendorAsync(model.Name,model.Phone,model.Email,model.Address,null);
return RedirectToAction(nameof(Index));
}
This works fine, however there are a couple problems:
This only supports basic validation such as checking character length, etc. In the particular example above, I want to validate that model.Address is a valid address according to google maps and also contains a city that the application is aware of, which means this kind of validation should be moved to the service layer to keep the Controller "thin".
The service layer is now missing any validation logic, and assumes that it is always being passed valid data. This seems wrong to me since it seems like the service layer should be responsible for keeping the system in a consistent valid state. A solution to this would be to also add validation logic to the service layer, but that seems to violate the DRY principle in my opinion.
The second approach would be to move all of the validation logic to the service layer and move all my data annotations to the actual domain object Vendor. This way each operation could validate the model based on the data annotations, and also apply any more complex logic such as validating the address with google maps as previously mentioned. However, I'm not sure how I can validate an annotated object in the same manner that a MVC Controller does and pass back a dictionary to the controller. This functionality seems to be specific to MVC and would introduce a dependency on MVC in my service layer which is undesirable.
Is there anyway I can elegantly move validation logic to the service layer while
taking advantage of data annotations and MVC's built in ModelStateDictionary? How do I get the list of errors back to the controller? Do I throw an exception and catch it in the controller if any validation errors occur?
I have seen several questions asking a similar question, but I'm not satisfied with any of the answers. Other answers seem to involve writing validation logic manually and not taking advantage of data annotations. Is this what I should resort to?
You can create your own custom validation attributes in addition to what are available out of the box such as Required,Range,StringLength,etc.
I will provide an example below :
public class ValidateAddressAttribute : Attribute, IModelValidator
{
public bool IsRequired => true;
public string ErrorMessage { get; set; } = "Address is not valid";
public IEnumerable<ModelValidationResult>Validate(ModelValidationContext context)
{
List<ModelValidationResult> validationResults = new List<ModelValidationResult>();
string address = context.Model as string;
if(!IsAddressValid(address))
{
validationResults.Add(new ModelValidationResult("", ErrorMessage));
}
return validationResults;
}
private bool IsAddressValid(string address)
{
bool isAddressValid;
//set isAddressValid to true or false based on your validation logic
return isAddressValid;
}
}
You can now apply this attribute on your address property as follows :
[Required]
[ValidateAddress(ErrorMessage="Invalid Address")]
public string Address { get; set; }

Customising ApplicationUser asp.net-identity

I'm trying to add a list of objects the the standard ApplicationUser object in MVC.
I've been looking at dozens of questions all about this but they all seem to be adding a single object, rather than a list.
What I'm trying to do is record all historic passwords a user has used, so I've added a table called AspNetUserPreviousPassword.
In my code I've added this line to the ApplicationUserclass:
public virtual DbSet<AspNetUserPreviousPassword> PreviousUserPasswords { get; set; }
and within the AspNetUserPreviousPassword object I've added the following:
public string ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
I've created the following extensions class:
public static class IdentityExtensions which has appeared in the User.Identity intellisense - so far so good.
{
public static string GetPreviousUserPasswords(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("PreviousUserPasswords");
// Test for null to avoid issues during local testing
return (claim != null) ? claim.Value : string.Empty;
}
}
When I went to edit the GenerateUserIdentityAsync function, to insert the custom claims, I began to think my approach was incorrect as you can only add strings, e.g.
userIdentity.AddClaim(new Claim("PreviousUserPasswords", "This must be a string"));
Despite only being able to add a string here, I wanted to test my previous passwords were being read from the database so I added this code to test:
string previousPasswords = "";
await this.PreviousUserPasswords.ForEachAsync(p => previousPasswords += p.PasswordHash);
this.PreviousUserPasswords is always NULL.
My questions:
1) Why is this.PreviousUserPasswords always NULL?
2) Is my approach even correct - can I add a list of objects to ApplicationUser or should I be doing this another way?
Here is the article I used to get preventing the use of previous "X" passwords in my implementation of Asp.Net Identity. I tweaked it to suit my needs but this got me everything I needed to get on the right track. Give it a read.
http://www.c-sharpcorner.com/UploadFile/4b0136/how-to-customize-password-policy-in-Asp-Net-identity/

How to avoid identical actions that do different things in RESTful Web API 2?

I'm in the process of designing a RESTful Web API and have bumped into the following problem: I need a controller to retrieve collections (called Sections) of a hierarchical structure as well as to retrieve a single part (a single Section). If I need a collection I have to refer to the ID of the root Section which gives me a subtree of the whole structure. So I went ahead and defined a SectionsController like this:
public class SectionsController : ApiController
{
// GET api/sections/5
// Gets a subtree.
public IEnumerable<Section> Get(int rootId)
{
...
}
// GET api/sections/5
// Gets a single section.
public Section Get(int sectionId)
{
...
}
Which obviously doesn't work as the signatures are identical. What is the recommended way to go about this?
If you want to follow standard REST patterns you should introduce a slightly different API:
public class SectionsController : ApiController
{
// GET api/section
public IEnumerable<Section> GetAll()
{
...
}
// GET api/section/5
public Section Get(int sectionId)
{
...
}
Normally you should use singular resources and provide identifier only for a specific one. You can't have same URLs, even with different controllers.
Reading this post on SO regarding image transfer and following the link provided I realized there is a very simple solution to this problem that respects REST and at the same time doesn't require additional controllers.
Just return a collection of the subtree IDs within the object requested for a particular ID, i.e.
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public int[] DescendantIds { get; set; }
}
So with a single call to
api/section/5
I get all the details for the section with ID 5 as well as the IDs of the sections below. Yes, there's some overhead involved, so you have to decide for yourself if this solution is for you.

Dynamic Validation in Web API

I am looking to validate a particular request depending on values in a database. It's a complex scenario, but I will try to simplify it in an example.
Say I have the following model:
public class CustomerModel
{
public int AgencyId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
When a POST request comes in, I need to make a call to get certain requirements for the AgencyId being passed.
var requirements = _repository.GetRequirementsForAgency(model.AgencyId);
The information I would get back from the database would tell me which properties are required, which may be different for each agency. For instance, one agency might require Name and Age where as another one might only require Name. The requirements object would look something like this:
public class Requirement
{
public string PropertyName { get; set; }
public bool IsRequired { get; set; }
}
So, my question is what would be the best way to validate this model before it gets submitted to the database? Ideally, I would like to give the Agency the ability to change these requirements, therefore, I would like to avoid hard coding validation if possible.
My first thought was to call a list of requirements and then do a foreach over each requirement searching by PropertyName and then checking to see if there was a value or not, but I wasn't sure if this was the best way.
I then looked into Data Annotations, but did not find a way to add attributes at run time.
You can use Fluent Validation library and implement custom validator
public class CustomerModelValidator : AbstractValidator<CustomerModel>
{
private readonly IRepository _repository;
public RegisterModelValidator(IRepository repository)
{
this._repository= repository;
RuleFor(x => x.AgencyId).GreaterThan(0).WithMessage("Invalid AgencyId");
RuleFor(x => x.Age).GreaterThan(0).WithMessage("Invalid Age");
Custom(c =>
{
var requirements = _repository.GetRequirementsForAgency(model.AgencyId);
\\validate each property according to requirements object.
\\if (Validation fails for some property)
return new ValidationFailure("property", "message");
\\else
return null;
});
}
}
If you use dependency injection in your project (which i strongly advice), you will have to inject relevant IRepository into an attribute. Otherwise you can just create/use a specific repository in your attribute.
A really nice thing is when you properly register your validator you will be able to validate you model with default if (ModelState.IsValid) check

Change DataMember property on WCF web service depending on input/output?

Scenario: An entity from data model is passed into a WCF Web Service with various information, saved into a database and then returned back with the object fully populated with additional information.
public class Request
{
public virtual Guid RequestID { get; set; }
public virtual string RequestType { get; set; }
public virtual System.DateTime CreatedDate { get; set; }
//More properties here populated from DB
}
[OperationContract]
Request CreateRequest(Request input);
In this example, the RequestID and CreatedDate are populated only when the record is inserted into the database, and therefore should not be visible during the initial request. They should be visible when the object is returned however.
The current approach that we are going with is to create two classes (RequestInput, RequestOutput) in our web service implementation project which inherit from the entity.
We will then add [DataMember] attributes on various properties that are required and [IgnoreDataMember] on those that should be ignored.
Is this the correct approach?
I wouldn't say it is a correct or incorrect way. But it is more usual to use names something along the line of
[DataContract]
Request{...}
and
[DataContract]
Response{...}
the Request and Response should ideally be decoupled from the model representation you are using in the client and the server - ie you have a facade or adaptor that maps them to your model from your service code.
this is along the lines of how I would do it - but this is very subjective dependant on size of entities etc - you may want to involve an auto-mapper somehow.
// higher level code
var entity = new Entity { properties we know before call };
// pass down to service layer
var response = service.CreateRequest(new Request { Prop1 = entity.Prop1... } );
entity.RequestID = response.RequestId;
entity.CreatedDate = response.CreatedDate;

Categories