Is it possible to have a ActionFilterAttribute parameters like this ?
[DataType(DataType.Password)]
For example I need to create something like this
[ValidateSession(Session.Required)]
public class ValidateSession : ActionFilterAttribute
{
enum Session
{
Required,
NotRequired
}
private Session _session { get; set; }
public ValidateSession(Session session)
{
this._session = session;
}
}
You can add parameters to custom ActionFilter. For more information see
this.
Related
I have a custom action filter that takes in a property but I need the property to come from my appsettings.json file. I pass my configuration into the controller, but when I try to pass in the "_config.GetValue< string >("myString")" the "_config" is red underlined with the message:
An object reference is required for the non-static field, method, or property 'MyController._config'
Action Filter
public class MyActionFilter : ActionFilterAttribute
{
public string Property1 { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
...
}
}
Controller
[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
private readonly IConfiguration _config;
public MyController(IConfiguration config) {
_config = config;
}
[Authorize]
[HttpPost(Constants.ActionName.MyMethod, Name = nameof(MyMethod))]
[MyActionFilter(Property1 = _config.GetValue<string>("myString"))] // Breaks here!
public ActionResult<string> MyMethod()
{
...
}
}
How can I do this? Or at least, how can I avoid hardcoding a value for my action filter properties?
Your current approach does not work because constructor parameters and properties of attributes are evaluated at compile time.
You could use the service locator pattern in your filter like so:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var config = filterContext.HttpContext.RequestServices.GetService<IConfiguration>();
string Property1 = config.GetValue<string>("myString");
}
However, this approach is debatable because the service locator pattern is considered an anti-pattern.
Another approach is to use a ServiceFilter. First, create a constructor for the action filter:
public MyActionFilter(string property1)
{
Property1 = property1;
}
Second, change the action filter to a service filter in the controller:
[ServiceFilter(typeof(MyActionFilter))]
public ActionResult<string> MyMethod()
{
...
}
Third, register the action filter:
builder.Services.AddScoped(p => new MyActionFilter(p.GetService<IConfiguration>().GetValue<string>("myString")));
Here is a great blogpost about the topic: Dependency Injection in action filters in ASP.NET Core.
I have an attribute for my web api authentication:
public class ApiAuthenticationAttribute : ActionFilterAttribute
{
private IApiAuthentication AuthenticationProvider { get; set; }
public ApiAuthorizeAttribute(Type apiAuthorizationType)
{
AuthenticationProvider = (IApiAuthentication)Activator.CreateInstance(apiAuthorizationType);
}
....
}
In this attribute i have the IApiAuthentication type that will do authentication logic.
For passing a concrete type of IApiAuthentication to the attribute I pass in the Type like this:
[ApiAuthentication(typeof(ApiAuthentication))]
I want to know is there a better way to do this?
I want to model service like this
public class FooService
{
public GetById(ISecurityContext context, id)
{
//checking context has right to view
//calling Foo repository to getById
}
public Add(ISecurityContext context,Foo fooEntity)
{
//checking context has right to add
//calling Foo repository to add
}
}
In the above methods i want to pass different type of SecurityContext
So what i have did is
Public Interface ISecurityContext
{
}
UsernamePasswordContext : ISecurityContext
{
public string Username { get; set; }
public string Password { get;set; }
}
SessionContext : ISecurityContext
{
public string SessionId {get ; set;}
}
So In my Account Service i have a method
public class AccountService
{
public Account GetAccountFromSecurityContext(ISecurityContext context)
{
if(context is UsernamePasswordContext)
return GetAccountByUsernamePassword(context.Username,context.Password);
else if (context is SessionContext)
return GetAccountBySessionId(context.SessionId);
// more else if for different type of context
}
}
In the above code i didnt liked so many if else
So i tried introducing polymorphism
So in my ISecurityContext interface i added a GetAccount method which all sub class will implement
Public Interface ISecurityContext
{
Account GetAccount();
}
UsernamePasswordContext : ISecurityContext
{
public string Username { get; set; }
public string Password { get;set; }
public Account GetAccount()
{
//call account service
GetAccountByUsernamePassword(this.Username,this.Password);
}
}
and my account service will become like this
public class AccountService
{
public Account GetAccountFromSecurityContext(ISecurityContext context)
{
context.GetAccount();
}
}
But the problem here is that i am calling a service/repository from my UsernamePasswordContext POCO which voilates DDD
So what are other ways i can model this scenario.
I think you're not far off from the solution. In this case, I would inject a factory into your AccountService that would take on the responsibility of the if..then..else. Then, the factory could use one of many possible solutions.
One change I would make right off is I would make your AccountService implement an interface which should make it easier to inject later. Assuming you're using some IOC container, you shouldn't have to worry too much about dependencies because you're letting the container handle all that.
Here are the pieces you already had, with some minor ajustments:
public class Account
{
//some account information and behavior
}
public interface ISecurityContext
{
}
public class UsernamePasswordContext : ISecurityContext
{
public string Username { get; set; }
public string Password { get; set; }
}
public class SessionContext : ISecurityContext
{
public string SessionId { get; set; }
}
Here's your account service along with it's implementation:
public interface IAccountService
{
Account GetAccountFromSecurityContext(ISecurityContext securityContext);
}
public class AccountService : IAccountService
{
readonly IAccountFactory _accountFactory;
public AccountService(IAccountFactory accountFactory)
{
_accountFactory = accountFactory;
}
public Account GetAccountFromSecurityContext(ISecurityContext securityContext)
{
Account account = _accountFactory.Create(securityContext);
return account;
}
}
So, you can see here that I've injected an IAccountFactory that will handle the actual creation (retrieval, whatever) of the Account object. All we care about at this point is that the account gets created/retrieved... we don't care about how.
There are a few ways you can implement a factory like this. One way is to use a type of strategy pattern where you have a list of widgets that know how to resolve an account. Then you just pick the widget (strategy) that matches and execute it. Something similar to this would be a factory that uses an IOC or service locator to resolve a type that has been registered previously in application configuration.
In the way of an example, here's one possible implementation of IAccountFactory using CommonServiceLocator:
public interface IAccountFactory
{
Account Create(ISecurityContext securityContext);
}
public class ServiceLocatorAccountFactory : IAccountFactory
{
readonly IServiceLocator _serviceLocator;
public ServiceLocatorAccountFactory(IServiceLocator serviceLocator)
{
_serviceLocator = serviceLocator;
}
public Account Create(ISecurityContext securityContext)
{
var resolverType = typeof (IAccountResolver<>).MakeGenericType(securityContext.GetType());
dynamic resolver = _serviceLocator.GetInstance(resolverType);
return resolver.Resolve(securityContext);
}
}
My factory here goes out to the service locator context and grabs whatever resolver matches our security context. Here are a couple examples of possible resolvers:
public interface IAccountResolver<in TSecurityContext> where TSecurityContext : ISecurityContext
{
Account Resolve(TSecurityContext securityContext);
}
public class UsernamePasswordAccountResolver : IAccountResolver<UsernamePasswordContext>
{
readonly IRepository _repository;
public UsernamePasswordAccountResolver(IRepository repository)
{
_repository = repository;
}
public Account Resolve(UsernamePasswordContext securityContext)
{
var account = _repository.GetByUsernameAndPassword(securityContext.Username,
securityContext.Password);
return account;
}
}
public class SessionAccountResolver : IAccountResolver<SessionContext>
{
public Account Resolve(SessionContext securityContext)
{
//get the account using the session information
return someAccount;
}
}
The only thing left is to register the resolvers in your IOC container so that they can be found when the service locator tries to resolve them in the factory.
I have an object that contains all login data, that's in my controller (it was programmed before switching to MVC3).
I'm trying to add authorization to the site, so so far I have:
public LoginObject MyLoginObject
{
get;
set;
}
[CustomAuthorization()]
public ActionResult Index()
{
return View();
}
and
public class CustomAuthorization : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return true;
//should be return myLoginObject.IsLoggedIn;
}
}
Is there anyway to pass MyLoginObject into the AuthorizeAttribute class? If not could I at least pass in a boolean from the object that specifies if the user is authorized or not?
Edit: My solution based on Zonnenberg's advice.
public class LoginObject : IPrincipal // Now extends IPrincipal
{
... //old code
private class IdentityImpl : IIdentity
{
public string AuthenticationType
{
get;
set;
}
public bool IsAuthenticated
{
get;
set;
}
public string Name
{
get;
set;
}
}
public IIdentity Identity
{
get { return new IdentityImpl { AuthenticationType = "Custom Authentication", IsAuthenticated = this.IsLoggedIn, Name = this.Id}; }
}
}
Then I moved the instantiation of loginobject into CustomAuthorization
public override void OnAuthorization(AuthorizationContext filterContext)
{
// ... Set up LoginObject
filterContext.RequestContext.HttpContext.User = myLoginObject;
base.OnAuthorization(filterContext);
}
So now logging in, is done inside the authorization, and I can call User to access the login from the controller.
You can check wheter the user is logged in by using httpContext.User.Identity.IsAuthenticated.
To store more information you could use the httpContext.User object. You can write your own implementation of IPrincipal and IIdentity to store all kinds of login information.
Other option is to store login info in the Session.
How is your LoginObject instantiated?
If it's instantiated via a service or repository (ex. MyLoginObject = loginService.GetLogin() then you can move this call into the CustomAuthorization attribute.
If the logic is within the controller itself then this should be refactored into a service or repository depending on you solution architecture so that you can do the above.
I was wanting to use an attribute in some of my class methods that would make sure that the user is an authorized before using the method that they called.
I was wanting to do something like
[Authorized()]
public void updateSomething()
{
//TODO:
}
I here is my attribute class
class AuthorizedAttribute : Attribute
{
public bool IsAuthorized { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string UserEmail { get; set; }
public AuthorizedAttribute()
{
//This is not the actual implementation
this.IsAuthorized = false;
}
public AuthorizedAttribute(string userEmail, string userPassword)
{
this.UserEmail = userEmail;
this.Password = userPassword;
this.UserName = string.Empty;
BusinessLogic bc = new BusinessLogic();
if (bc.VerifyCredentials(userEmail, userPassword))
{
this.IsAuthorized = true;
}
else
{
this.IsAuthorized = false;
}
}
}
Could someone point me in the right direction? Some link would be great as well.
Thank you.
I think the fundemental mistake you have made here is to look at passing the credentials to the attribute. What the attribute should do is force an action to occur before the function you have called will take place.
So your attribute must be checked for by the request processing pipeline. i.e. when the function updateSomething() is called the calling assembly should be looking for the attribute which will then force an authorisation to occur using the current HttpContext and the User.Identity.
I have experience with the MVC AuthorizeAttribute and which can be extended by deriving from this attribute and adding authentication logic to it.
public class TestAuthAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
return ResultOfBusinsessLogic;
}
}
this can then be used on any controller action.
I hope this points you in the right direction.
Have you looked at the built-in AuthorizeAttribute?
If you are using Forms authentication / roles this is already built in - check out the PrincipalPermission attribute.
Sample usage:
[PrincipalPermission(SecurityAction.Demand, Role = "Admin")]