4

I'm building my site, and I want to restrict a part of my site (The admin parts) from normal public display.

  • I am using LINQ for database access.
  • I have a Service class to handle calls to the database through LINQ
  • I have the whole site running, except for the Login part.

So far I have only been able to find examples using MembershipProvider and/or RoleProviders etc. And to be honest, it does seem like too much work for what I want. All this has to do is to let you in if you type the correct password in the input fields.

Can i really not avoid the Providers?

smartcaveman
  • 41,281
  • 29
  • 127
  • 212
KristianB
  • 1,403
  • 3
  • 25
  • 46

4 Answers4

6

Since you only have a single user you don't need to create a database dependency. You can make a very simple authorization service based off of a hard coded credentials. For example,

public class AuthorizationService{
     private AuthorizationService(){}
     public static readonly AuthorizationService Instance = new AuthorizationService();

     private const string HardCodedAdminUsername = "someone";
     private const string HardCodedAdminPassword = "secret";
     private readonly string AuthorizationKey = "ADMIN_AUTHORIZATION";

     public bool Login(string username, string password, HttpSessionStateBase session){
         if(username.ToLowerInvariant().Trim()==HardCodedAdminUsername && password.ToLowerInvariant().Trim()==HardCodedAdminPassword){
              session[AuthorizationKey] = true;
              return true;
         } 
         return false;
     }

     public void Logout(HttpSessionStateBase session){
        session[AuthorizationKey] = false;
     }

     public bool IsAdmin(HttpSessionStateBase session){
         return session[AuthorizationKey] == true;
     }
}

Then you can build a custom IAuthorizationFilter like:

public class SimpleAuthFilterAttribute: FilterAttribute, IAuthorizationFilter{
     public void OnAuthorization(AuthorizationContext filterContext){
         if(!AuthorizationService.Instance.IsAdmin(filterContext.HttpContext.Session)){
              throw new UnauthorizedAccessException();
         }
     }
}

Then all you have to do is decorate the protected controller actions with the SimpleAuthFilter and you're application's login suddenly works. Yay! (Note, I wrote all this code in the StackOverflow answer window, so you may need to clean up typos, etc. before it actually works)

Also, you could refactor this to omit the username if you find that unnecessary. You will need to create a controller action for Login and Logout that make the corresponding calls to the AuthorizationService, if you want your protected controller actions to ever be accessible.

smartcaveman
  • 41,281
  • 29
  • 127
  • 212
  • Brilliant! Okay, if I then want to restrict an action like `ControlPanel` to be viewed without being logged I simply have to use the `[Authorize]` annotation? – KristianB Jun 18 '11 at 19:37
  • 1
    @KristianB, No. The [Authorize] annotation will apply the default [`AuthorizeAttribute`](http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.aspx). You will need to annotate your protected actions with `[SimpleAuthFilter]`, or whatever you decide to name the attribute class I called `SimpleAuthFilterAttribute` – smartcaveman Jun 18 '11 at 19:44
  • I've come so far as to what `HttpSessionStateBase` exactly is. And how I send the session to the method via the controller... – KristianB Jun 18 '11 at 20:36
  • 1
    @KristianB, you can access the `HttpSessionStateBase` in your controller action via `ControllerContext.HttpContext.Session` – smartcaveman Jun 18 '11 at 20:39
  • My view does not seem to call the action method, even though it's inside a BeginForm tag like this `<% using (Html.BeginForm("Login", "Admin")) { %>`. I retrieve form data like this in my `Login` action: `string name = Request.Form["Username"];` - isn't that correct? – KristianB Jun 18 '11 at 20:48
  • 1
    @KristianB, you will need to post more code in order to debug this specific issue. From what you've written, you should be able to access the form data like that, but it is considered bad practice in MVC. The recommended approach is to use a model binder. For this simple action, you could even just put the `string username` as a parameter in the controller action, if the field is called username. – smartcaveman Jun 18 '11 at 21:16
  • Thanks again - I made a class to match the fieldnames in the form. I agree that it is much cleaner code. But going from JSP to ASP.NET MVC made me realise that I'm not ridden of `<% ... %>` tags yet - sadly, it's ugly code to me, compared to regular expressions. – KristianB Jun 21 '11 at 15:24
3

Its worth building a light-weight Membership Provider with minimal implementation; GetUser, ValidateUser etc methods. YOu dont need to implement the whole thing. It just helps with authorising pages and checking User.Identity etc when needed. You also dont need the RoleProvider or ProfileProvider to do this.

Its also scalable for the future.

UPDATE

You just need to implement the core methods to valudate and get the user and insert your own validation/data access code.

Something like this....

web.config settings:

<membership defaultProvider="ApplicationMembershipProvider">
      <providers>
        <clear/>
        <add name="ApplicationMembershipProvider" type="YourNamespace.ApplicationMembershipProvider"/>
      </providers>
    </membership>

Login Code:

if (Membership.ValidateUser(username, password))
            {
                FormsAuthentication.SetAuthCookie(username, false);
}
Mark Redman
  • 24,079
  • 20
  • 92
  • 147
  • So I will have to create a simple User class, even though LINQ made that for me? And how exactly do I tell my Web.config file that this custom made MembershipProvider is what it has to use? Reason I ask so directly, is that my attempt has failed! Hehe. – KristianB Jun 18 '11 at 19:21
  • Any time you have a sub class which does not implement all members you violate Liskov. Don't implement a membership provider unless you plan on using all of the given methods. Thats just bad practice. See: http://stackoverflow.com/questions/410719/notimplementedexception-are-they-kidding-me – John Farrell Jun 18 '11 at 19:59
  • @jfar: That is not helpful at all. Technically you may need to implement all the methods and not put code in them, if you dont need them dont imeplement them. Anyway, who is Liskov, I dont feel I have violated him. – Mark Redman Jun 18 '11 at 20:19
  • +1, @KristianB, while the code I provided will work for your objective, @Mark Redman's suggestion would be considered more inline with ASP.NET's best practices. @jfar, [Check this out](http://msdn.microsoft.com/en-us/library/system.notimplementedexception.aspx) – smartcaveman Jun 18 '11 at 23:09
0

You can set the status (logged in or not) in a session variable. Set the variable to true if the user entered the correct password, then on every page you want to restrict access, check if the variable is true.

Jim
  • 1,695
  • 2
  • 23
  • 42
  • Oh yeah, that sounds like a very simple solution. Isn't it open for Session hijacking? – KristianB Jun 18 '11 at 19:17
  • I would love to know how one can do session hijacking with that? Session hijacking is about session ID being stolen or hijacked, and not about what you use the session for. I would suggest you read about session hijacking on Wikipedia, especially the "methods" part (http://en.wikipedia.org/wiki/Session_hijacking#Methods). – Jim Jun 18 '11 at 19:43
0

@KristianB a while ago I gave an answer to this SO question. I believe it may be useful since it's very straightforward to implement and at the same time it's better than hardcoding a username and a password in your code.

Good luck!

Community
  • 1
  • 1
uvita
  • 4,124
  • 1
  • 27
  • 23
  • I think you meant to link to [your other post](http://stackoverflow.com/questions/2692144/asp-mvc-keeping-track-of-logged-in-users/2692890#2692890), which actually contains code. Also, I didn't downvote, but I don't like the unnecessary dependency on cookies. – smartcaveman Jun 18 '11 at 23:11
  • Thanks alot! I used both your example and smartcaveman's - it now works with a database user, password encrypted and all :) – KristianB Jun 21 '11 at 12:38