ServiceStack Validation RuleSet for Post is not working - validation

i use ServiceStack build a web service,
this is my validator code:
public class AccountValidator : AbstractValidator<AccountModel>
{
public AccountValidator()
{
//only for post
RuleSet(ServiceStack.ApplyTo.Post, () =>
{
RuleFor(s => s.Password).Length(30);
});
}
}
Service code
public object Post(Account req) {
AccountModel model = req.ConvertTo<AccountModel>();
var result = this.Validator.Validate(model);
if (result.IsValid)
{
//is aways true
}
throw result.ToException();
}
the Account.Password is "abc" ,way the result.IsValid is true?

The result is always true because the ApplyTo.Post condition in your validator only applies to rules that are validated on the Request DTO. The ApplyTo conditions don't work for FluentValidation that is invoked manually. See the Validation Documentation.
But given that you are converting the Account into an AccountModel you could use the ValidationFeature plugin to check the validation of Account before converting it to an AccountModel.
Thus:
public class AccountValidator : AbstractValidator<Account>
{
public AccountValidator()
{
RuleSet(ApplyTo.Post, () => RuleFor(s => s.Password).Length(30));
}
}
// Account will be validated on POST
public object Post(Account req)
{
// Account is valid coming into the request
AccountModel model = req.ConvertTo<AccountModel>();
// Is it not safe to assume the model is valid, because Account is valid?
}

Related

AllowAnonymous attribute is not working in .net core api 2.2. Please consider (.NetCore) and version(2.2) before suggesting duplicate or answer

I am working in .net core api 2.2 where I am using authorization filter. I am just checking bearer token in authorization tag in header and if bearer token is already there then user action can be called. But some action I wanna exclude from authorization part. I am using AllAnonymous attribute on specific action but calling on same anonymous method the authorization filter is being called. The code of filter is given below :
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
public class ApiAuthorizeFilter : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
string token = context.HttpContext.GetToken();
if (string.IsNullOrEmpty(token))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
string realmId = context.HttpContext.GetRealm();
if (string.IsNullOrEmpty(realmId))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
}
}
As per I checked the some solution per this is not getting resolved. Please share any solution regarding .net core API version 2.2 .
You can check the AllowAnonymous attribute inside OnAuthorization method :
// Allow Anonymous skips all authorization
if (context.Filters.Any(item => item is IAllowAnonymousFilter))
{
return;
}
Base on your codes :
public class ApiAuthorizeFilter : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
// Allow Anonymous skips all authorization
if (context.Filters.Any(item => item is IAllowAnonymousFilter))
{
return;
}
string token = context.HttpContext.GetToken();
if (string.IsNullOrEmpty(token))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
string realmId = context.HttpContext.GetRealm();
if (string.IsNullOrEmpty(realmId))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
}
}
When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that
were discovered on controllers and actions. To maintain compat with 2.x,
we'll check for the presence of IAllowAnonymous in endpoint metadata.
var endpoint = context.HttpContext.GetEndpoint();
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
{
return true;
}

Custom Async Action Filter for Web API 2

I have a web api to consume the data coming from android mobile. This web api will consume the multi part file from along with the form data the web api request. I followed this article to archive.
[CustAuthAsync]
public async Task<HttpResponseMessage> SaveEHSInspectionData()
{
try
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(root);
//do stuff
var res = await Request.Content.ReadAsMultipartAsync(provider);
// DO SOME STUFF
}
catch (Exception exp)
{
}
return Request.CreateResponse(HttpStatusCode.OK, result);
}
I wanted to do the custom access validation for this web api, so implemented a filter to validate the request.
I have the filter like below
public class CustAuthAsyncAttribute : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
InternalOnExecutingAsync(actionContext);
}
}
The internal method like this
protected void InternalOnExecutingAsync(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
bool validationResult = // validation with database
if (!validationResult)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Unauthorized Request" };
}
These implementations are working fine in API Client Tools (Example: Postman) if the validation passes, allows the request to the method.
Postman Response screen shot
This is not working in mobile app, Saying the response message as Unauthorized Access. and not allowing the request to the method even the custom access validations are passed.
FYI : This method is working fine in mobile without filter
Help me to get this works in mobile app also.
Thanks in advance.
Your using the wrong type of filter to manage access. You should use an authorization filter. Besides you can't have an async method to authorize. You have to make the calling client wait for clearance. This may cause the side effects you're experiencing.
I'm not sure this has any to do with fact that it's a mobile application, however the authorization phase ir prior to the processing of the request. Verify that your are not using any other form of authorization in your project.
You should implement an authorization filter by inheriting AuthorizeAttribute and overriding IsAuthorized(HttpActionContext actionContext) method:
public class CustAuthAsync : AuthorizeAttribute
{
public CustAuthAsync()
{
///Some initialization if required. Otherwise, not necessary to declare the constructor..
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
return //database validation
}
else
{
return false;
//No need to create special unauthorized response. You should not hint the reason at this point. You can do this in the HandleUnauthorizedRequest method.
}
}
else
{
return false;//No need to create special unauthorized response.
}
}
}
You can use this attribute to decorate your controllers. You can even pass parameter in the constructor for more granular control on access management, like a required role to access de controller.

how to unit test controller when automapper is used?

here's my controller
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = Mapper.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
here's my unit test:
[Test]
public void Signup_Action_When_The_User_Model_Is_Valid_Returns_RedirectToRouteResult()
{
// Arrange
const string expectedRouteName = "~/Views/Home/Index.cshtml";
var registeredUser = new UserRegisterViewModel { Email = "newuser#test.com", Password = "123456789".Hash()};
var confirmation = new ActionConfirmation<User>
{
WasSuccessful = true,
Message = "",
Value = new User()
};
_userService.Setup(r => r.AddUser(new User())).Returns(confirmation);
_accountController = new AccountController(_userService.Object);
// Act
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result, "Should have returned a RedirectToRouteResult");
Assert.AreEqual(expectedRouteName, result.RouteName, "Route name should be {0}", expectedRouteName);
}
Unit test failed right here.
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
when I debug my unit test, I got following error message: "Missing type map configuration or unsupported mapping."
I think its because configuration is in web project, not the unit test project. what should I do to fix it?
You need to have the mapper configured, so in your test class set up, not the per-test setup, call the code to set up the mappings. Note, you'll also probably need to modify your expectation for the user service call as the arguments won't match, i.e, they are different objects. Probably you want a test that checks if the properties of the object match those of the model being passed to the method.
You should really use an interface for the mapping engine so that you can mock it rather than using AutoMapper otherwise it is an integration test not a unit test.
AutoMapper has an interface called IMappingEngine that you can inject into your controller using your IoC container like below (this example is using StructureMap).
class MyRegistry : Registry
{
public MyRegistry()
{
For<IMyRepository>().Use<MyRepository>();
For<ILogger>().Use<Logger>();
Mapper.AddProfile(new AutoMapperProfile());
For<IMappingEngine>().Use(() => Mapper.Engine);
}
}
You will then be able to use dependency injection to inject AutoMapper's mapping engine into your controller, allowing you to reference your mappings like below:
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = this.mappingEngine.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
You can read more about this here: How to inject AutoMapper IMappingEngine with StructureMap
Probably it is cool to abstract mapping into MappingEngine.
Sometimes I use following approach to IOC Automapper
In IOC builder:
builder.RegisterInstance(AutoMapperConfiguration.GetAutoMapper()).As<IMapper>();
where GetAutoMapper is:
public class AutoMapperConfiguration
{
public static IMapper GetAutoMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<OrderModelMapperProfile>();
cfg.AddProfile<OtherModelMapperProfile>();
//etc;
});
var mapper = config.CreateMapper();
return mapper;
}
}
And finally in Controller ctor
public MyController(IMapper mapper)
{
_mapper = mapper;
}

Fluent Validation - MVC 3

I'm using fluent validation in MVC 3. Is it possible to turn off fluent validation for specific post action in controller?
Thanks
Assuming you are using the AttributedValidatorFactory and you wanted to disable validation for the Index action on Home controller for POST verbs you could write a custom validator factory:
public class MyAttributedValidatorFactory : AttributedValidatorFactory
{
private readonly Func<HttpContextBase> _contextAccessor;
public MyAttributedValidatorFactory(Func<HttpContextBase> contextAccessor)
{
_contextAccessor = contextAccessor;
}
public override IValidator GetValidator(Type type)
{
var context = _contextAccessor();
var rd = context.Request.RequestContext.RouteData;
var action = rd.GetRequiredString("action");
var controller = rd.GetRequiredString("controller");
if (string.Equals("post", context.Request.HttpMethod, StringComparison.OrdinalIgnoreCase) &&
string.Equals("index", action, StringComparison.OrdinalIgnoreCase) &&
string.Equals("home", controller, StringComparison.OrdinalIgnoreCase)
)
if (type == typeof(MyViewModel))
{
return null;
}
return base.GetValidator(type);
}
}
which will be used to replace the default one in your Application_Start:
FluentValidationModelValidatorProvider.Configure(config =>
{
Func<HttpContextBase> contextAccessor =
() => new HttpContextWrapper(HttpContext.Current);
config.ValidatorFactory = new MyAttributedValidatorFactory(contextAccessor);
});
and then if you have the following action on the Home controller:
[HttpPost]
public ActionResult Index(MyViewModel model)
{
...
}
FluentValidation won't kick in.
Hay men you have miss some important point of fluentvalidation called the Validator customization.
find here http://fluentvalidation.codeplex.com/wikipage?title=mvc
Validator customization
With FluentValidation v3 you can use the CustomizeValidatorAttribute to configure how the validator will be run. For example, if you want the validator to only run for a particular ruleset then you can specify that ruleset name by attributing the parameter that is going to be validated:
public ActionResult Save([CustomizeValidator(RuleSet="MyRuleset")] Customer cust) {
// ...
}
this

Validate checkbox on the client with FluentValidation/MVC 3

I am trying to validate if a check box is checked on the client using FluentValidation. I can't figure it our for the life of me.
Can it be done using unobtrusive validation?
Let's assume that you have the following model:
[Validator(typeof(MyViewModelValidator))]
public class MyViewModel
{
public bool IsChecked { get; set; }
}
with the following validator:
public class MyViewModelValidator : AbstractValidator<MyViewModel>
{
public MyViewModelValidator()
{
RuleFor(x => x.IsChecked).Equal(true).WithMessage("Please check this checkbox");
}
}
and a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
with a corresponding view:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.IsChecked)
#Html.CheckBoxFor(x => x.IsChecked)
#Html.ValidationMessageFor(x => x.IsChecked)
<button type="submit">OK</button>
}
and in Global.asax you have registered the fluent validation model validator provider:
FluentValidationModelValidatorProvider.Configure();
So far we have server side validation up and running fine. That's good. That's always the first part that we must setup. I have seen people focusing too much on doing client side validation that they forget doing server side validation and when you disable javascript (or even worse if you stumble upon a user with bad intentions), well, bad things happen.
So far we are confident because we know that even if something gets screwed up on the client our domain is protected with server side validation.
So let's now take care for the client validation. Out of the box FluentValidation.NET supports automatic client validation for the EqualTo validator but when comparing against another property value which is the equivalent of the [Compare] data annotation.
But in our case we are comparing against a fixed value. So we don't get client side vaildation out of the box. And when we don't get something out of the box, we need to put it in the box.
So we start by defining a custom FluentValidationPropertyValidator:
public class EqualToValueFluentValidationPropertyValidator : FluentValidationPropertyValidator
{
public EqualToValueFluentValidationPropertyValidator(ModelMetadata metadata, ControllerContext controllerContext, PropertyRule rule, IPropertyValidator validator)
: base(metadata, controllerContext, rule, validator)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
if (!this.ShouldGenerateClientSideRules())
{
yield break;
}
var validator = (EqualValidator)Validator;
var errorMessage = new MessageFormatter()
.AppendPropertyName(Rule.GetDisplayName())
.AppendArgument("ValueToCompare", validator.ValueToCompare)
.BuildMessage(validator.ErrorMessageSource.GetString());
var rule = new ModelClientValidationRule();
rule.ErrorMessage = errorMessage;
rule.ValidationType = "equaltovalue";
rule.ValidationParameters["valuetocompare"] = validator.ValueToCompare;
yield return rule;
}
}
that we are going to register in Application_Start:
FluentValidationModelValidatorProvider.Configure(provider =>
{
provider.AddImplicitRequiredValidator = false;
provider.Add(typeof(EqualValidator), (metadata, context, description, validator) => new EqualToValueFluentValidationPropertyValidator(metadata, context, description, validator));
});
So far we have associated our custom FluentValidationPropertyValidator with the EqualValidator.
The last part is to write a custom adapter:
(function ($) {
$.validator.unobtrusive.adapters.add('equaltovalue', ['valuetocompare'], function (options) {
options.rules['equaltovalue'] = options.params;
if (options.message != null) {
options.messages['equaltovalue'] = options.message;
}
});
$.validator.addMethod('equaltovalue', function (value, element, params) {
if ($(element).is(':checkbox')) {
if ($(element).is(':checked')) {
return value.toLowerCase() === 'true';
} else {
return value.toLowerCase() === 'false';
}
}
return params.valuetocompare.toLowerCase() === value.toLowerCase();
});
})(jQuery);
And that's pretty much it. All that's left is to include the client scripts:
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/customadapter.js")" type="text/javascript"></script>
I like the Darin Dimitrov's answer, but if you want to do it quickly, here is my alternative way.
Create an additional property in your model, e.g.:
public bool ValidationTrue { get; set; }
and set its value to true in the model's contructor.
Use it in your view to save the value across the requests:
#Html.HiddenFor(x => x.ValidationTrue)
Now add a validation rule like this:
public class MyViewModelValidator : AbstractValidator<MyViewModel>
{
public MyViewModelValidator()
{
RuleFor(x => x.ValidationTrue)
.Equal(true); // check it for security reasons, if someone has edited it in the source of the page
RuleFor(x => x.HasToBeChecked)
.Equal(x => x.ValidationTrue) // HasToBeChecked has to have the same value as ValidationTrue (which is always true)
.WithMessage("Required");
}
}
That validation is supported by the unobtrusive validator out-of-the-box.
I am coding in ASP.NET MVC5 and Darin's code produces a javascript error on the lines that reference value.ToLowerCase() when a checkbox is involved. Another issue is that this code invalidates the client side equality comparison between two properties. It only seems to work when comparing against a literal value...That may have been his intent, but I need it to work for both situations:
Here's one possible workaround, that involves only two changes to Darin's answer:
First, I updated the javascript function with the following.
$.validator.addMethod('equaltovalue', function (value, element, params) {
if ($(element).is(':checkbox')) {
value = $(element).is(':checked') ? "true" : "false";
}
return params.valuetocompare.toLowerCase() === value.toLowerCase();
});
Secondly, I updated EqualToValueFluentValidationPropertyValidator with the following:
public class EqualToValueFluentValidationPropertyValidator : FluentValidationPropertyValidator
{
EqualValidator EqualValidator
{
get { return (EqualValidator)Validator; }
}
public EqualToValueFluentValidationPropertyValidator(ModelMetadata metadata, ControllerContext controllerContext, PropertyRule rule, IPropertyValidator validator) : base(metadata, controllerContext, rule, validator) {
ShouldValidate = false;
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
if (!ShouldGenerateClientSideRules()) yield break;
var propertyToCompare = EqualValidator.MemberToCompare as PropertyInfo;
if(propertyToCompare != null) {
// If propertyToCompare is not null then we're comparing to another property.
// If propertyToCompare is null then we're either comparing against a literal value, a field or a method call.
// We only care about property comparisons in this case.
var comparisonDisplayName =
ValidatorOptions.DisplayNameResolver(Rule.TypeToValidate, propertyToCompare, null)
?? propertyToCompare.Name.SplitPascalCase();
var formatter = new MessageFormatter()
.AppendPropertyName(Rule.GetDisplayName())
.AppendArgument("ComparisonValue", comparisonDisplayName);
string message = formatter.BuildMessage(EqualValidator.ErrorMessageSource.GetString());
yield return new ModelClientValidationEqualToRule(message, CompareAttribute.FormatPropertyForClientValidation(propertyToCompare.Name)) ;
}
else
{
var validator = (EqualValidator)Validator;
var errorMessage = new MessageFormatter()
.AppendPropertyName(Rule.GetDisplayName())
.AppendArgument("ValueToCompare", validator.ValueToCompare)
.BuildMessage(validator.ErrorMessageSource.GetString());
var rule = new ModelClientValidationRule();
rule.ErrorMessage = errorMessage;
rule.ValidationType = "equaltovalue";
rule.ValidationParameters["valuetocompare"] = validator.ValueToCompare;
yield return rule;
}
}
}
This code was copied from the EqualToFluentValidationPropertyValidator internal class in the fluentvalidation source, and I added Darin's logic after the else. This allows the client-side validation to work for property comparisons as well as value comparisons...I'm not sure if this is a great approach since you're basically overriding the built-in equality validator and it may break in future releases of fluent validation....but Darin's answer has the same issue.
There might be better ways to handle this. If somebody knows of a way to directly include the logic from the internal EqualToFluentValidationPropertyValidator class, then I'd love to hear it.
it's based on #cryss answer
RuleFor(x => x.HasToBeChecked)
.Equal(x => true)
.WithMessage("Required");
you don't need to use additional property

Resources