Authorize only email from specific domain asp.net MVC - model-view-controller

I'm trying to limit access to one controller to users from one specific domain. I thought the following would work :
[Authorize(Users = "*#domain.com")]
But it doesn't. Is there a way to do this out of the box or I need to develop my own attribute?
Thanks

I finally created an attribute that inherits from AuthorizeAttribute.
public class AuthorizeEmailDomainAttribute : AuthorizeAttribute
{
// Custom property
public string EmailDomain { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
return false;
}
bool isGoodDomain= httpContext.User.Identity.Name.Contains("#" + EmailDomain);
return isGoodDomain;
}
}
Have a nice day

Related

Conditional validation based on request route with asp.net core 2.2 and FluentValidation

So Basically i wrote a validator for my class with FluentValidation and also a filter to do the validation task for me in my webAPI project, so far it's OK but assume that my User class has firstname,lastname,email,password properties
and i have two routes (one for register and the other one for login)
and as you might have noticed required properties are different on these route.
Thus,should I really need to write individual validation for each and every action i have?because this makes a lot of code code duplication and it's hard to change.is there any way to just add required condition based on the request coming with single validation class?
Any suggestion???
A better practice would be to use a factory pattern for your validations and use a an action filter to short circuit bad requests. You could validate any action argument(Headers, Request Bodies, etc..) with something like this.
public class TestValidationAttribute : Attribute, IActionFilter
{
private string _requestModelName;
public TestValidationAttribute(string requestModelName)
{
_requestModelName = requestModelName;
}
public void OnActionExecuting(ActionExecutingContext context)
{
// using Microsoft.Extensions.DependencyInjection;
var services = context.HttpContext.RequestServices;
var accessor = services.GetService<IHttpContextAccessor>();
var factory = services.GetService<ITestValidatorFactory>();
var tokens = accessor.HttpContext.GetRouteData().DataTokens;
if (!tokens.TryGetValue("RouteName", out var routeNameObj))
{
throw new Exception($"Action doesn't have a named route.");
}
var routeName = routeNameObj.ToString();
var validator = factory.Create(routeName);
if (!context.ActionArguments.TryGetValue(_requestModelName, out var model))
{
throw new Exception($"Action doesn't have argument named {_requestModelName}.");
}
TestModel test;
try
{
test = (TestModel) model;
}
catch (InvalidCastException)
{
throw new Exception($"Action argument can't be casted to {nameof(TestModel)}.");
}
var validation = validator.Validate(test);
if (!validation.Successful)
{
context.Result = new BadRequestObjectResult(validation.ResponseModel);
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
public class TestController : Controller
{
[HttpPost]
[Route("Test/{id}", Name = "TestGet")]
[TestValidation("model")]
public IActionResult Test(TestModel model)
{
return Ok();
}
}
public class ValidationResult
{
public bool Successful { get; }
public ResponseModel ResponseModel { get; }
}
public class TestModel
{
}
public interface ITestValidator
{
ValidationResult Validate(TestModel model);
}
public interface ITestValidatorFactory
{
ITestValidator Create(string routeName);
}

Web API - Access Custom Attribute Properties inside ActionFilterAttribute OnActionExecuting

I need to access a property inside a custom DataAnnotation attribute. How can I access this attribute in order to set the response value? The attribute is added to the model property.
public class BirthDateAttribute : ValidationAttribute
{
public string ErrorCode { get; set; }
....
}
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
List<Errors> errors = new List<Errors>();
// Set error message and errorCode
foreach (var key in keys)
{
if (!actionContext.ModelState.IsValidField(key))
{
error.Add(new HttpResponseError
{
Code = ???????????,
Message = actionContext.ModelState[key].Errors.FirstOrDefault().ErrorMessage
});
}
}
// Return to client
actionContext.Response = actionContext.Request.CreateResponse(
HttpStatusCode.BadRequest, errors);
}
}
}
Assuming that the custom attribute is applied to the controller, you can try following in the OnActionExecuting event. This similar thing works with MVC controller but should work with API controller too.
var att = actionContext.ControllerContext.GetType().GetCustomAttributes(typeof(BirthDateAttribute), false)[0] as BirthDateAttribute;
string errorCode = att.ErrorCode;
As mentioned by OP, if this is on a class (Model), it should be pretty starightforward because the type is already known. Replace the Model class.
var att = <<ModalClass>>.GetCustomAttributes(typeof(BirthDateAttribute), false)[0] as BirthDateAttribute;
string errorCode = att.ErrorCode;

Implement OAuth with ASP.NET WebAPI

Hello Friends I would like to ask you that can any one have an example to integrate oAuth in ASP.Net web api with out integrating any packages or Entity framework??i search it a lot but find a various way using nuget packages and other packages but i need the way using simple third party calls because i need this authorization in .net as well as java api's. Can any one help me out in this.
Thanks in advance...
Yes you can do this, i implemented this in my web api using oAuth in web api 2 project.
First, have an asp.net project with oauth is configured since we will take cooy some files into web api project.
Here is the steps:
1) In the web api, add a new class file called "IdentityConfig.cs".
This class will have: ApplicationUser, ApplicationUserManager, ApplicationSignInManager and ApplicationDbContext classes.
2) Make sure that these classes above is under your api namespace so it is accessible through all your controllers.
// Configure the application user manager which is used in this api.
public class ApplicationUser : IdentityUser
{
#region custom properties
public string Name { get; set; }
public int? ZipCode { get; set; }
public long? CountryId { get; set; }
public bool IsDeleted { get; set; }
public bool EmailConfirmed { get; set; }
public DateTime CreatedDate { get; set; }
public long UserId { get; set; }
#endregion
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = true,
RequireUppercase = false,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
//manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this api.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DBCONNECTIONKEY", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Note: DBCONNECTIONKEY is the key for the connection string in web.config
3) Add Startup.cs file to the root of your web api. copy the logic from the existing one you have in asp.net. feel free to tweak the configuration context properties as needed in the web api project.
4) Use objects from these classes to sign in users, and manager application user objects as you have in asp.net web app.
That's all :)
Hope this helps.

Retrieve a complex object from ActionParameters

I am working on an MVC project where controller actions deal with Assets. Different controllers take in the assetId parameter in different way: Some controllers simply get int assetId, other int id, and other using a complex object AssetDTO dto (which contains a property that holds the assetId)
I am writing an ActionFilter that is added to the action method and is provided with the actionParameter name where I can get the asset value.
Action Method:
[AssetIdFilter("assetId")]
public ActionResult Index(int assetId)
{
...
}
The attribute is defined as:
public class AssetIdFilterAttribute : ActionFilterAttribute
{
public string _assetIdParameterKey { get; set; }
public AssetIdFilterAttribute (string assetIdParameterKey)
{
_assetIdParameterKey = assetIdParameterKey;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int assetId;
if (Int32.TryParse(filterContext.ActionParameters[_assetIdParameterKey].ToString(), out assetId))
{
......
}
}
This works as expected, but will only work when the assetId is provided as a primitive. I am not sure what to do when the assetId is provided within a complex object into the action method.
Will I need to parse each object differently depending on the type? I am hoping I can specify some kind of dot-notation in the AssetIdFilter to tell it where the assetId is located: dto.assetId
Any way I can use dynamics? or reflection?? ect.???
and here dynamic comes to the rescue.you can change the actionFilterAttribute to be :
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
dynamic assetIdHolder = filterContext.ActionParameters[_assetIdParameterKey];
if (assetIdHolder.GetType().IsPrimitive)
{
//do whatever with assetIdHolder
}
else
{
//do whatever with assetIdHolder.assetId
}
}
cheers!
Well, yes, you answered your question. One way would be to use dot notation:
//simple case:
[AssetId("id")]
public ActionResult Index(string id) {
//code here
}
//complex case:
[AssetId("idObj", AssetIdProperty = "SubObj.id")]
public ActionResult index(IdObject idObj) {
//code here
}
And AssetIdAttribute is as follows:
public class AssetIdAttribute : ActionFilterAttribute
{
public string _assetIdParameterKey { get; set; }
public string AssetIdProperty { get; set; }
public AssetIdFilterAttribute(string assetIdParameterKey)
{
_assetIdParameterKey = assetIdParameterKey;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int assetId;
var param = filterContext.ActionParameters[_assetIdParameterKey];
int.TryParse(GetPropertyValue(param, this.AssetIdProperty).ToString(), out assetId);
//you code continues here.
}
private static string GetPropertyValue(object souce, string property)
{
var propNames = string.IsNullOrWhiteSpace(property) || !property.Contains('.') ? new string[] { } : property.Split('.');
var result = souce;
foreach (var prop in propNames)
{
result = result.GetType().GetProperty(prop).GetValue(result);
}
return result.ToString();
}
}
The code does not have null checks when calling ToString and when calling GetProperty though. Also, it does not check the success of TryParse. Please apply these corrections when used.
Maybe this code could be written using dynamic, but at the end dynamic usage is compiled into object using reflection (something like what I have done here), thus no big difference to me.
Also, maybe it would be more clear to have a parameter like "idObj.SubObj.id", but that again depends on the preference, and the code will become a little bit more complex.

Implementing Unique Contraint with ValidateEntity gives "The given key was not present in the dictionary" error

While in search of trying to implement unique key validations for my db using EF CodeFirst/Mvc3 I came upon this post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx which gave an example on how to do it by using IValidateObject for my object model:
public class Category : IValidatableObject
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var testContext = (TestContext)validationContext.Items["Context"];
if (testContext.Categories.Any(
c => c.CategoryName == CategoryName && c.CategoryID != CategoryID))
{
yield return new ValidationResult("A category with the same name already exists!", new[] { "CategoryName" });
}
yield break;
}
}
and overriding DbEntityValidationResult ValidateEntity:
public class TestContext : DbContext
{
public DbSet<Test.Models.Category> Categories { get; set; }
protected override DbEntityValidationResult ValidateEntity( DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var myItems = new Dictionary<object, object>();
myItems.Add("Context", this);
return base.ValidateEntity(entityEntry, myItems);
}
}
And the action on the controller
[HttpPost]
public ActionResult Create(Category category)
{
if (ModelState.IsValid) {
categoryRepository.InsertOrUpdate(category);
categoryRepository.Save();
return RedirectToAction("Index");
} else {
return View();
}
}
But I get the error: "The given key was not present in the dictionary." for the line
var testContext = (TestContext)validationContext.Items["Context"];
It seems like Validate on the object is getting called which accesses "Context" before its set in the override ValidateEntity code.
At first I thought it could have been ModelState.Isvalid triggering validate too early but it wasn't.
Anyone know what I'm missing here or what I'm doing wrong? Thanks in advance.
Model.IsValid definitely triggers it too early and perhaps something else. IValidatableObject is global interface used by both MVC and EF but your method in DbContext is called only when you call SaveChanges on the context so any usage of IValidatableObject prior to calling SaveChanges will result in the exception. You must use another approach if you want to validate your entity this way. For example store context in HttpContext.Items - you can create custom action filter and instantiate and store the context before the operation call and dispose it after operation call - hopefully it will cover all problems.
I was facing the same problem... Then after a lot of Googling I finally found this:
Exercise 3: Using IValidatableObject Custom Validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
MusicStoreEntities storeDB = new MusicStoreEntities();
if (storeDB.Albums.Any(
a => a.Title.Trim().ToUpper() == this.Title.Trim().ToUpper() &&
a.ArtistId == (int)this.ArtistId))
{
yield return new ValidationResult("Existing Album", new string[] { "Title" });
}
}
As you see in their example, they instantiate a new Context and as such there's no need for validationContext.Items["Context"];. Doing so we won't get this error anymore.

Resources