0

I want to make sure that I’m on the right track with this one.

I have a site which has two backend areas, one for school and one for admin. I would like to authenticate the areas so that the school area has less DB privileges than admin. To do this my idea is to have two logins in Sql Server Management Studio School and Admin, which I can map certain roles to. Maybe the school section will have only read access and the admin read and write etc..

Please can somebody advise me as to the best way to go about implementing this. Do I need multiple connection strings (one for admin and one for school)? Can this be done using Form Authentication?

I am currently connecting to an existing database that I have created using Sql Server Management Studio and already have a means of logging in which will set the FormsAuthentication and this works well in the sense that I can add the authorize attribute to my school backend controller which stops the pages that require a logged in school from being displayed unless a school is logged in. The question is really how would I make this more specific so that only a school login is allowed to see this area and not an admin member who has logged in as they would also have set the FormsAuthentication.

I have done a lot of googling in but have not found anything specific to my problem, hence this post.

I can produce code if needed and am not asking for somebody to write it for me but a theoretical explanation of how to solve this kind of security model.

Thanks in advance for your help.

Working Solution using a Custom Role Provider

Account Controller code (Now one for both Admin and School)

[HttpPost]
public ActionResult LogOn(LogOn model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
            {
                return Redirect(returnUrl);
            }
            else
            {
                // Now it's our job to route the user to the correct place. Ask our DB Helper to tell 
                // us what kind of user they are and route accordingly.
                string sPage = "Index";
                string sController = "Home";

                var Role = DBHelperFunctions.Instance().GetRoleForUser(model.UserName);

                switch (Role.role_name)
                {
                    case LanguageSchoolsConstants.m_RoleAdministrator:
                        {
                            sController = "AuthorisedAdmin";
                        }
                        break;
                    case LanguageSchoolsConstants.m_RoleSchool:
                        {
                            sController = "AuthorisedSchool";
                        }
                        break;
                }

                return RedirectToAction(sPage, sController);
            }
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }

DB Helper Function used in above method:

public role GetRoleForUser(string sUserName)
{
    // Should only ever have one role...
    role Role = (from roles in DBModel.roles
                 join userrole in DBModel.user_role on roles.role_id equals userrole.role_id
                 join users in DBModel.users on userrole.user_id equals users.user_id
                 where users.username == sUserName
                 select roles).FirstOrDefault();

    return Role;
}

Web.config changes to allow role providor to be invoked:

<roleManager defaultProvider="RoleProvider" enabled="true" cacheRolesInCookie="true">
      <providers>
        <clear />
        <add name="RoleProvider" type="namespace.Models.Security.CustomRoleProvider" />
        <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="Entities" applicationName="/" />
        <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
      </providers>
    </roleManager>

My Role Provider

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;

namespace neamsepace.Models.Security
{
    public class CustomRoleProvider : RoleProvider
    {
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            throw new NotImplementedException();
        }

        public override string ApplicationName
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override void CreateRole(string roleName)
        {
            throw new NotImplementedException();
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            throw new NotImplementedException();
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            throw new NotImplementedException();
        }

        public override string[] GetAllRoles()
        {
            throw new NotImplementedException();
        }

        public override string[] GetRolesForUser(string username)
        {
            using (DB db = new DB())
            {
                string[] RolesForUser = null;

                user User = db.users.FirstOrDefault(u => u.username.Equals(username, StringComparison.CurrentCultureIgnoreCase) ||
                                                         u.email.Equals(username, StringComparison.CurrentCultureIgnoreCase));

                var roles = from dbroles in db.roles
                            join userroles in db.user_role on dbroles.role_id equals userroles.role_id
                            join users in db.users on userroles.user_id equals users.user_id
                            where users.user_id == User.user_id
                            select dbroles.role_name;

                if (roles != null)
                {
                    RolesForUser = roles.ToArray();
                }
                else
                {
                    RolesForUser = new string[] { };
                }

                return RolesForUser;

            }
        }

        public override string[] GetUsersInRole(string roleName)
        {
            throw new NotImplementedException();
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            using (DB db = new DB())
            {
                bool bUserInRole = false;

                user User = db.users.FirstOrDefault(u => u.username.Equals(username, StringComparison.CurrentCultureIgnoreCase) || 
                                                         u.email.Equals(username, StringComparison.CurrentCultureIgnoreCase));

                var roles = from dbroles in db.roles
                            join userroles in db.user_role on dbroles.role_id equals userroles.role_id
                            join users in db.users on userroles.user_id equals users.user_id
                            where users.user_id == User.user_id
                            select dbroles.role_name;

                if (User != null)
                {
                    bUserInRole = roles.Any(r => r.Equals(roleName, StringComparison.CurrentCultureIgnoreCase));
                }

                return bUserInRole;
            }
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            throw new NotImplementedException();
        }

        public override bool RoleExists(string roleName)
        {
            throw new NotImplementedException();
        }
    }
}

Controller that uses Authorize.

[Authorize(Roles = LanguageSchoolsConstants.m_RoleAdministrator)]
public class AuthorisedAdminController : Controller
{
    //
    // GET: /AuthorisedAdmin/

    public ActionResult Index()
    {
        return View();
    }

}

I really hope that this helps anybody please feel free to comment!

Thanks for all your help.

5
  • I see Membership.ValidateUser which means you're using a membership provider. What provider are you using? And more importantly, how do you currently manage your users (i.e. students, professors)? Commented Feb 25, 2013 at 11:50
  • I have created my own which inherits from MembershipProvidor. I have called it SchoolMembershipProvider and I will post the code above that it contains. I also have a SchoolMembershipUser which inherits from MembershipUser. I will also post the code for that above. I hope that it helps illustrate what I am doing at the moment. Thanks. Commented Feb 25, 2013 at 12:04
  • If you feel comfortable coding a custom membership provider, then I would advice you to implement a custom role provider as well. The idea is basically the same; you code it, hook it up in your web.config, and that's it. All role checks will be performed against the roles your custom provider returns. Commented Feb 25, 2013 at 12:12
  • Also re. "How do you currently manage your users" I simply have a school table and an admin table. Each contains the basic user information including username and password which I then query when a user logs in to ensure they are a valid school user for example. Commented Feb 25, 2013 at 12:13
  • 1
    Thanks I will check out how to "implement a custom role provider". I may have a question along the way but for now I will mark your question as solved. thanks for your input it is much appreciated! Commented Feb 25, 2013 at 12:16

2 Answers 2

1

You don't need multiple connection strings (unless you have multiple DBs.) You can accomplish what you need easily by decorating your actions with the [Authorize(Roles = "XYZ")] attribute, and defining roles to specify what you want to protect, except in this context role means task. For example:

public class ProfessorsController : Controller
{
    [Authorize(Roles = "Professors.List")]
    public ViewResult Index()
    {
        // TODO: List professors
    }

    [Authorize(Roles = "Professors.Create")]
    public ViewResult Create()
    {
        // TODO: Create professor
    }

    [Authorize(Roles = "Professors.Update")]
    public ViewResult Update(int id)
    {
        // TODO: Update professor
    }

    [Authorize(Roles = "Professors.Delete")]
    public ViewResult Delete(int id)
    {
        // TODO: Delete professor
    }
}

Then, for example, user Van Driessen (a professor) would be granted access to all four actions, while user Beavis (a student) would be granted access to just Professors.List.

Sign up to request clarification or add additional context in comments.

9 Comments

Thank you for your reply. Can I ask where the Professors role comes from in this context? Is it a role in the database i.e. Server Roles? Also does this mean that you don't have multiple logins? How would the connection string relate to this? Thanks and sorry if i'm being dumb.
@Yos I think you're confusing things. Server Roles are specific to SQL Server security (as in authorization to your DB.) Unless you were using different SQL logins for different users of your application (hardly the case for web apps) you can just use a single SQL login and a single connection string. Ideally, in this case your roles come from the DB, so that you can change users' access without having to recompile your app.
If you update your question with some code to see what your login action looks like, I might be able to update my answer with an example on how to hookup things up to retrieve your roles from the DB.
After looking at a very good tutorial I can see how I am going to implement the logic and hook up roles to my application (kitsula.com/Article/Custom-Role-Provider-for-MVC). I do have one question though. I don't have a user table at the moment my user information is in the relevant table, either school or admin. So filling in function such as GetRolesForUser(string username) would require me to check either school or adming table or maybe both. Also I would have to make a linker table for school: schoolRole and admin: adminRole and populate accordingly when entering data into these?
@Yos Nice tutorial. The provider model is a valid approach. Personally, I find it's not worth the hassle; I suggested it to you because you mentioned you are already using it. If you starting from scratch, I would have suggested a much simpler approach. See this answer I posted today to see an example of how to accomplish the same thing without providers.
|
1

You can store the login accessin a Session and your items inside an 'if' statement in you view as such

<% if (Convert.ToString(ViewData["access"]) == "School")

{ %>
             //your controls here  
   <% } %>

If condition is not satisfied, contents inside it will not be shown.

Hope this helps! Regards.

4 Comments

Sorry the ">" interfered with the blockquotes. Had to edit several times :|
Thank you for your comment. I may end up doing it this way, as this is how I have done it when making a CMS in php. You mean to basically set the access to school if the user logs into the the school section and admin if they log into the admin section. Then simply check the string value in each view? Could this be done in the controller? I was hoping to use the Authorize to solve it by checking database roles or logins. Thanks very much for your reply!
Yes, it could be done in the controller, but you still have to use some viewdata to pass values between controller and view. there are alot of ways to do it. theres ajax if you want async functions and if it will fit with your logic. If you found simpler and better ways, please share it with us too. Good Luck!
I will be sure to share my final solution onces it's complete. Thanks again for all your support. I will be back in the next day or two with my new code.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.