4

I've got a custom IIdentity implementation:

public class FeedbkIdentity : IIdentity
{
    public int UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string Name { get; set; }

    public FeedbkIdentity() 
    {
        // Empty contructor for deserialization
    }

    public FeedbkIdentity(string name)
    {
        this.Name = name;
    }

    public string AuthenticationType
    {
        get { return "Custom"; }
    }

    public bool IsAuthenticated
    {
        get { return !string.IsNullOrEmpty(this.Name); }
    }
}

and custom IPrincipal

public class FeedbkPrincipal : IPrincipal
{
    public IIdentity Identity { get; private set; }

    public FeedbkPrincipal(FeedbkIdentity customIdentity)
    {
        this.Identity = customIdentity;
    }

    public bool IsInRole(string role)
    {
        return true;
    }
}

In global.asax.cs I'm deserializing FormsAuthenticationTicket userData and replacing

HttpContext.Current.User:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];

    if (authCookie != null)
    {
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

        JavaScriptSerializer serializer = new JavaScriptSerializer();

        FeedbkIdentity identity = serializer.Deserialize<FeedbkIdentity>(authTicket.UserData);

        FeedbkPrincipal newUser = new FeedbkPrincipal(identity);
        HttpContext.Current.User = newUser;
    }
}

Then, in Razor Views I can do:

@(((User as FeedbkPrincipal).Identity as FeedbkIdentity).FirstName) 

I'm already using Ninject to inject custom User Repository for the membership provider and I've been trying to bind IPrincipal to HttpContext.Current.User:

internal class NinjectBindings : NinjectModule
{
    public override void Load()
    {
        Bind<IUserRepository>().To<EFUserRepository>();
        Bind<IPrincipal>().ToMethod(ctx => ctx.Kernel.Get<RequestContext>().HttpContext.User).InRequestScope();
    }
}

but it doesn't work.

All I'm trying to do is to be able to access my custom IIdentity properties like this: @User.Identity.FirstName

How can I make this work?

EDIT

I was expecting that by binding IPrincipal to HttpContext.Current.User as suggested here: MVC3 + Ninject: What is the proper way to inject the User IPrincipal? I will be able to access @User.Identity.FirstName in my application.

If this is not the case, what is the purpose of binding IPrincipal to HttpContext.Current.User as shown in the linked question?

Community
  • 1
  • 1
LukeP
  • 10,422
  • 6
  • 29
  • 48
  • Define "doesn't work". What doesn't work? What does it do? How is this different from what you were expecting? Are there errors? You need to give more information. How would you react if an end user came to you and just said "It doesn't work"? Notice how everyone is saying "Assuming..." that's because you didn't actually tell us what the problem is. – Erik Funkenbusch May 09 '12 at 01:23
  • I edited the post but I mentioned what I would like to do just above the edit. I would like to be able to use @User.Identity.FirstName (which is coming from my custom IIdentity) in Razor views. – LukeP May 09 '12 at 07:48
  • you have to cast it to your custom IIdentity type. User is typed to the base interface, so a cast is needed to get your custom things. – Erik Funkenbusch May 09 '12 at 14:30
  • @MystereMan I understand. But if that's the case why would anyone want to bind IPrincipal to HttpContext.Current.User like in the question linked? – LukeP May 09 '12 at 15:07
  • some do it for testing purposes, but it's not really necessary with MVC because MVC already uses interefaces. At least in my opinion, i don't see a good reason to inject an IPrincipal. However, if you really want to.. you would make sure your custom IPricipal has it's own interface (IFeedbkPrincipal) and instead, inject that. – Erik Funkenbusch May 09 '12 at 15:12
  • @MystereMan Thanks. So I assume the answer to my question is in one of the answers below. Which one would be considered better? – LukeP May 09 '12 at 15:49
  • AFinklestein's is the better way. – Erik Funkenbusch May 09 '12 at 16:03

2 Answers2

3

I'd recommend creating your own base WebViewPage. See http://haacked.com/archive/2011/02/21/changing-base-type-of-a-razor-view.aspx

Then inside that you could do

public FeedbkPrincipal FeedbkPrincipal
{
    get
    {
        return User as FeedbkPrincipal;
    }
}

public FeedbkIdentity FeedbkIdentity
{
     get
     {
         return FeedbkPrincipal.Identity as FeedbkIdentity;
     }
}

Then it's just

@FeedbkIdentity.FirstName

within the view.

what is the purpose of binding IPrincipal to HttpContext.Current.User as shown in the linked question

Dependency injection lets you choose your components at run time. In the example given, you could do

public MyClassConstructor(IPrincipal principal) { // }

and IPrincipal would automatically be bound to the User. Note this would not let you access your specific FeedbkPrincipal properties because even though at runtime it would become of type FeedbkPrincipal, your class doesn't know that. Dependency injection isn't a solution to your current issue because you do need a way of accessing your concrete classes.

DMulligan
  • 8,993
  • 6
  • 33
  • 34
  • Many thanks. This will allow me to use my Identity class in Views. If I wanted to use them in controllers as well I can create a base controller and make all controllers to inherit the type as shown: http://stackoverflow.com/questions/7044424/how-can-i-make-accessing-my-custom-iprincipal-easier-in-asp-net-mvc. Would I be able to access those custom properties within views if I implement it on a controller level? – LukeP May 09 '12 at 18:43
  • Within a custom WebViewPage you could have a property that casts your controller to your base type. Then have a second property that does MyBaseController.MyUser to fetch your custom user properties. – DMulligan May 09 '12 at 19:49
  • This doesn't seem to work. For starters I have to have 2 separate implementations for WebViewPage and WebViewPage. Then after I implement your code it still doesn't recognise @FeedbkPrincipal in views. – LukeP May 09 '12 at 21:03
  • For WebViewPage you could just have public abstract class WebViewPage : WebViewPage { } and then put all logic in WebViewPage. If you implemented it correctly in your config file it should work. If the intellisense isn't picking it up, try closing and reopening visual studios. – DMulligan May 09 '12 at 21:15
  • Thanks. That worked. Is there no way to override @User object in views? When I try that i get into infinite loop. – LukeP May 09 '12 at 21:20
  • Great stuff. I got it all working. Docuemented the whole thing in another answer - http://stackoverflow.com/a/10524305/239819. Many thanks! – LukeP May 09 '12 at 21:26
1

Assuming that HttpContext.User gets set correctly, you could create a global filter to automatically populate your ViewBag with your FeedbkIdentity.

E.g.

Create it

public class FeedbkPrincipalIntoViewBagAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var principal = filterContext.HttpContext.User as FeedbkPrincipal;
        if (principal != null)
        {
            filterContext.Controller.ViewBag.Identity = principal.Identity as FeedbkIdentity;
        }
    }
}

Register it

protected void Application_Start()
{
    GlobalFilters.Filters.Add(new FeedbkPrincipalIntoViewBagAttribute());
}

Use it

@ViewBag.Identity.FirstName
Charlino
  • 15,802
  • 3
  • 58
  • 74