I'm trying to write a custom validator that will check if an entity exists in the database, using OrmLite. The problem is that the type arguments for IRuleBuilder can no longer be inferred from usage.
I have to write the method call like this:
RuleFor(r => r.Id).Exists<DtoName, int, EntityName>()
But I want to write it like this:
Rulefor(r => r.Id).Exists<EntityName>()
This happens because IRuleBuilder has two type parameters and the method is an extension method. Is there a smart, fluent way to design this and make the function call preferably like the second version?
Here is code for my extension method and my validator:
public static class AbstractValidatorExtensions
{
public static IRuleBuilderOptions<T, TProperty> Exists<T, TProperty, U>(this IRuleBuilder<T, TProperty> ruleBuilder)
{
return ruleBuilder.SetValidator(new EntityExistsValidator<U>());
}
}
public class EntityExistsValidator<T> : PropertyValidator
{
public EntityExistsValidator() : base("Entity does not exist") {}
protected override bool IsValid(PropertyValidatorContext context)
{
return HostContext.Resolve<Repository>()
.Exists<T>((int)context.PropertyValue);
}
}
My experience with FluentValidation is that you’re trying to push more and more logic into validators. I would not do this as it adds too much complexity. My rule of thumb is to validate discrete property values only. Example: I would just use FluentValidation to check if property int Id is 0 or greater than 0. The check if the entity already exists I would move to another service (often called “the business logic”).
You'll need to a Custom Validator for custom validation to access dependencies, something like:
RuleFor(x => x.Id)
.Must(id =>
{
using (var db = HostContext.AppHost.GetDbConnection(base.Request))
{
return !db.Exists<EntityName>(x => x.Id == id);
}
})
.WithErrorCode("AlreadyExists")
.WithMessage("...");
I'd also consider just doing validation that use dependencies in your Services instead:
if (Db.Exists<EntityName>(x => x.Id == request.Id))
throw new ArgumentException("Already Exists", nameof(request.Id));
Related
We are using fluentvalidation (with service stack) to validate our request DTO's. We have recently extended our framework to accept "PATCH" requests, which means we now have a requirement to apply validation ONLY when the patch contained the field being validated.
We have done this using an extension method such as this:
RuleFor(dto => dto.FirstName).Length(1,30)).WhenFieldInPatch((MyRequest dto)=>dto.FirstName);
RuleFor(dto => dto.MiddleName).Length(1,30)).WhenFieldInPatch((MyRequest dto)=>dto.MiddleName);
RuleFor(dto => dto.LastName).Length(1,30)).WhenFieldInPatch((MyRequest dto)=>dto.LastName);
This means we can run the same validation for a POST/PUT or a PATCH.
I have been looking for a way of hooking in to the fluent validation framework in such as way that we do not need to duplicate the .WhenFieldInPatch() rule on EVERY line in our validations, but have not yet found a nice way to do this.
I have tried the following:
Creating a helper method (in a in a base class) to intercept the initial "RuleFor" which adds the .When() clause up front, but the this does not work as fluent validation requires the .When() to be last
Intercepting the calls in PreValidation, but I can only intercept based on the whole class, and not on a rule by rule basis
Adding an extension method to apply to the end of every rule (as per example), but I cannot access the initial expression in order to check whether the field should be mapped - so I need to pass it in again.
Am I missing something, or am I attempting the impossible?
Thanks
When I need to share Fluent Validation Logic I'd use extension methods, here's an example of shared Extension methods for TechStacks, e.g:
public static class ValidatorUtils
{
public static bool IsValidUrl(string arg) => Uri.TryCreate(arg, UriKind.Absolute, out _);
public static string InvalidUrlMessage = "Invalid URL";
public static IRuleBuilderOptions<T, string> OptionalUrl<T>(
this IRuleBuilderInitial<T, string> propertyRule)
{
return propertyRule
.Length(0, UrlMaxLength)
.Must(IsValidUrl)
.When(x => !string.IsNullOrEmpty(x as string))
.WithMessage(InvalidUrlMessage);
}
}
And some examples where they're shared:
public class CreatePostValidator : AbstractValidator<CreatePost>
{
public CreatePostValidator()
{
RuleSet(ApplyTo.Post, () =>
{
RuleFor(x => x.Url).OptionalUrl();
});
}
}
public class UpdatePostValidator : AbstractValidator<UpdatePost>
{
public UpdatePostValidator()
{
RuleSet(ApplyTo.Put, () =>
{
RuleFor(x => x.Url).OptionalUrl();
});
}
}
I'm trying to apply ubiquitous language to my domain objects.
I want to convert a Data Transfer Object coming from a client into the domain object. The Aggregate's Constructor only accepts the required fields, and the rest of parameters should be passed using aggregate's API even when the Aggregate is being created(by say CreateAggregate command).
But the DTO to Aggregate mapping code becomes a bit messy:
if(DTO.RegistrantType == 0){
registrantType = RegistrantType.Person()
}
elseif(DTO.RegistrantType == 1){
registrantType = RegistrantType.Company()
}
//.....
//.....
var aggregate = new Aggregate(
title,
weight,
registrantType,
route,
callNumber,
)
//look at this one:
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
//..........
//..........
One thing I should mention is that this problem doesn't seem a domain specific problem.
How can I reduce these If-Else statements without letting my domain internals leakage, and with being sure that the aggregate(not a mapping tool) doesn't accept values that can invalide it's business rules, and with having the ubiquitous language applied?
Please don't tell me I can use AoutoMapper to do the trick. Please read the last part carefully.'
Thank you.
A typical answer would be to convert the DTO (which is effectively a message) into a Command, where the command has all of the arguments expressed as domain specific value types.
void doX(DTO dto) {
Command command = toCommand(dto)
doX(command)
}
void doX(Command command) {
// ...
aggregate.Route(command.connectionType)
}
It's fairly common for the toCommand logic use something like a Builder pattern to improve the readability of the code.
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
In cases like this one, the strategy pattern can help
ConnectionTypeFactory f = getConnectionFactory(DTO.connectionType)
ConnectionType connectionType = f.create(DTO)
Once that you recognize that ConnectionTypeFactory is a thing, you can think about building lookup tables to choose the right one.
Map<ConnectionType, ConnectionTypeFactory> lookup = /* ... */
ConnectionTypeFactory f = lookup(DTO.connectionType);
if (null == f) {
f = defaultConnectionFactory;
}
So why don't you use more inheritance
for example
class CompanyRegistration : Registration {
}
class PersonRegistraiton : Registration {
}
then you can use inheritance instead of your if/else scenario's
public class Aggregate {
public Aggregate (CompanyRegistration) {
registantType = RegistrantType.Company();
}
public Aggregate (PersonRegistration p) {
registrantType = RegistrantType.Person();
}
}
you can apply simmilar logic for say a setRoute method or any other large if/else situations.
Also, i know you don't want to hear it, you can write your own mapper (inside the aggegate) that maps and validates it's business logic
for example this idea comes from fluentmapper
var mapper = new FluentMapper.ThatMaps<Aggregate>().From<DTO>()
.ThatSets(x => x.title).When(x => x != null).From(x => x.title)
It isn't too hard to write your own mapper that allow this kind of rules and validates your properties. And i think it will improve readability
I am not sure if ServiceStack has a mechanism to prevent "JavaScript/HTML Injection" on Entities (Request Entities) properties.
Also as per my understanding entity's properties of type string is prone to JavaScript/HTML injection
If there is no in built mechanism please suggest me a better option.
One of the option which i see is use to validate may be using Fluent Validation or any other validating library
Use validation:
Yes you should be using Fluent Validation or another validation mechanism to sanitise all the values that are passed as a request to your ServiceStack service.
Why ServiceStack shouldn't sanitise for you:
ServiceStack won't do this for you, after all sending HTML and/or JavaScript in a request to the service may be perfectly legitimate, (i.e. where your service is a content manager for a blog), and it's wrong to assume the request is an injection attack.
ServiceStack isn't constricted to only being consumed by web applications, so it's up to the service to decide which values are appropriate.
It should be noted that ServiceStack does prevent SQL injection by escaping all parameters.
Encode HTML entities:
If you are concerned about HTML injection, then you should consider encoding HTML entities, then any unsafe values that are returned won't affect your result. You can do this easily using this request filter, and marking up your DTO with an attribute [EncodeHtml].
GlobalRequestFilters.Add((req,res,dto) => {
var dtoType = dto.GetType();
var filteredProperties = dtoType.GetPublicProperties().Where(p => p.PropertyType == typeof(string) && p.HasAttribute<EncodeHtmlAttribute>() && p.CanWrite);
foreach(var property in filteredProperties)
property.SetValue(dto, HttpUtility.HtmlEncode(property.GetValue(dto, null)), null);
});
On your DTO add the [EncodeHtml] attribute to the properties you want to protect.
[Route("/test", "GET")]
public class Test
{
public string UnsafeMessage { get; set; }
[EncodeHtml]
public string SafeMessage { get; set; }
}
The attribute declaration is simply:
public class EncodeHtmlAttribute : Attribute {}
Then when you send a request such as:
/test?unsafeMessage=<b>I am evil</b>&safeMessage=<b>I am good</b>
The result will be
UnsafeMessage: "<b>I am evil</b>"
SafeMessage: "<b>I am good</b>"
I hope this helps.
As per your suggestion if you want to throw an exception on any DTOs that may contain HTML then you could use a more general check which prevents any HTML in any strings on the DTO by checking against a regular expression, but I'd do this sparingly.
GlobalRequestFilters.Add((req,res,dto) => {
var dtoType = dto.GetType();
if(!dtoType.HasAttribute<PreventHtmlAttribute>())
return;
var filteredProperties = dtoType.GetPublicProperties().Where(p => p.PropertyType == typeof(string));
foreach(var property in filteredProperties){
var value = property.GetValue(dto, null) as string;
if(value != null && Regex.Match(value, #"<[^>]*>", RegexOptions.IgnoreCase).Success)
throw new HttpError(System.Net.HttpStatusCode.BadRequest, "400", "HTML is not permitted in the request");
}
});
Then use this attribute:
public class PreventHtmlAttribute : Attribute {}
On the DTO:
[PreventHtml]
[Route("/test", "GET")]
public class Test
{
...
}
I have the need to extend Service/IService to allow me to register additional resources like other DB connections and custom classes that each individual service may need to get a handle to.
Is the proper way to do this to subclass Service? Also, it is not clear to me if I have another (say) IDbConnection how Funq figures out which Property to inject the value into.
If you have multiple services with the same type you need to register them in funq with a name. Unfortunatly I don't think funq can autowire the properties correctly so you need to manually resolve them.
container.Register<DataContext>("Security", x => new SecurityDataContext());
container.Register<DataContext>("Customers", x => new CustomersDataContext());
container.Register<DataContext>("Reporting", x => new ReportingDataContext());
container.Register<IReportRepository>(x => new ReportRepositoryImpl(x.ResolveNamed<DataContext>("Reporting")));
An alternative approach would be to create a unique interface (even if it has no members) for each type and then use that in funq. This would allow autowiring
container.Register<ISecurityDataContext>(x => new SecurityDataContext());
container.Register<ICustomersDataContext>(x => new CustomersDataContext());
container.Register<IReportingDataContext>(x => new ReportingDataContext());
// this could just be autowired
container.Register<IReportRepository>(x => new ReportRepositoryImpl(x.Resolve<IReportingDataContext>()));
If you still really need to extend Service you can just use standard inheritance in c#
public abstract class BaseService : Service
{
// custom things go here
public string Example() {
return "Hello World";
}
}
public class ReportsService : BaseService
{
public string Get(ListReports request) {
return Example();
}
}
You can configure other DB connections easily without extending the Service , but by just wiring them in the configure method in the AppHost.cs file.
I want to use a Voter to only allow owners to edit a project object in my application.
I have a route /project/42/edit that invokes my action ProjectController.editAction(Project $project). I use a type hint (Project $project) to automatically invoke the ParamConverter to convert the ID 42 from the URI into a project object. This works nicely for the controller action, however it seems to be invoked too late for the voter. Its vote() method gets called with the request as 2nd parameter, not my project.
Is there a way to pass the project to the voter without having to retrieve it from the database again?
UPDATE: I learned that I have to manually call isGranted() on the security context in the edit method. This is very similar in approach to this answer.
Here is my Voter:
namespace FUxCon2013\ProjectsBundle\Security;
use FUxCon2013\ProjectsBundle\Entity\Project;
use Symfony\Component\BrowserKit\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class OwnerVoter implements VoterInterface
{
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function supportsAttribute($attribute)
{
return $attribute == 'MAY_EDIT';
}
public function supportsClass($class)
{
// your voter supports all type of token classes, so return true
return true;
}
function vote(TokenInterface $token, $object, array $attributes)
{
if (!in_array('MAY_EDIT', $attributes)) {
return self::ACCESS_ABSTAIN;
}
if (!($object instanceof Project)) {
return self::ACCESS_ABSTAIN;
}
$user = $token->getUser();
$securityContext = $this->container->get('security.context');
return $securityContext->isGranted('IS_AUTHENTICATED_FULLY')
&& $user->getId() == $object->getUser()->getId()
? self::ACCESS_GRANTED
: self::ACCESS_DENIED;
}
}
I register this in configure.yml so that it gets the service container as parameter:
services:
fuxcon2013.security.owner_voter:
class: FUxCon2013\ProjectsBundle\Security\OwnerVoter
public: false
arguments: [ #service_container ]
tags:
- { name: security.voter }
The last block is to configure the access decision manager in security.yml to unanimous:
security:
access_decision_manager:
# strategy can be: affirmative, unanimous or consensus
strategy: unanimous
allow_if_all_abstain: true
Please have a look at this answer i have written yesterday.
You can easily adapt it to your needs by checking for the owner of your object.
The current object doesn't get passed in the voter if you use the role security handler.
I had to extend the latter to get the former.
Don't hesitate to comment for details.