views:

1009

answers:

5

I have an ASP.NET 2.0 [no ajax...yet] web site that will be deployed in compiled form on multiple customer sites. Typically the site will be intranet only. Some customers trust all of their people and don't care about limiting access to the site and/or page functions, others trust no one and want only certain people and/or groups to be able to view certain pages, click certain buttons, et al.

i could do some home-grown solution, possibly drive the access permissions from a database table, but before i go down that road i thought i'd ask in SO: what is a good solution for this situation? preferably one that can be controlled completedly in the web.config file and/or database, since rebuilding the web site is not possible (for the client, and i don't want to have to do it for them over and over). Active Directory integration would be a bonus, but not a requirement (unless that's just easier).

as a starting point, i'm thinking that each page/function point in the site be given an identity and associated with a permission group...

EDIT: web.config authorization section to allow/deny access by role and user is good, but that is only half of the problem - the other half is controlling access to the individual methods (buttons, whatever) on each page. For example, some users can view whatchamacallits while others are allowed to edit, create, delete, or disable/enable them. All of these buttons/links/actions are on the view page...

[ideally i would make the disabled buttons invisible, but that is not important here]

EDIT: some good suggestions so far, but no complete solution yet - still leaning towards a database-driven solution...

  • security permission demand attributes will throw exceptions when buttons are clicked, which is not a friendly thing to do; i'd much rather hide buttons that the user is not allowed to use
  • the LoginView control is also interesting, but would require replicating most of the page content several times (once for each role) and may not handle the case where a user is in more than one role - i cannot assume that the roles are hierarchical since they will be defined by the customer

EDIT: platform is Win2K/XP, Sql Server 2005, ASP.NET 2.0, not using AJAX

+1  A: 

I prefer to grant access rights to AD groups rather than specific users. I find it's much more flexible.

I don't know much about your application, but you might want to look at the authorization tag in the web.config file:

<authorization>
    <!--  
        <deny users="?" />
        <allow     users="[comma separated list of users]"
                   roles="[comma separated list of roles]"/>
        <deny      users="[comma separated list of users]"
                   roles="[comma separated list of roles]"/>
    -->
</authorization>

You can separate web.config files each directory within your web application, and you can nest directories. Each web.config file can have it's own authorization section. If you put different pages in each directory you can effectively tightly manage security by allowing a specific role in each web.config, and denying everything else. Then you can manage members of each role in active directory. I've found this to be an affective solution because it makes good use of Microsoft's Active Directory and ASP.NET security framework without writing your own custom stuff, and if you use roles, it's possible to offload the management of role membership to someone who doesn't ever have to touch the web.config file they just need to know how to use the AD management console.

Jeremy
an interesting approach, but that only solves half of the problem - some users may be able to view a certain page, but not click some of the buttons on that page...
Steven A. Lowe
accepted as "the answer" so the Stats page will quit pestering me
Steven A. Lowe
+1  A: 

While I've never used this before in practice and cannot argue its merits, I know that .NET has role based code security which allows you to declaratively lock methods down by role or user. For example:

[PrincipalPermissionAttribute(SecurityAction.Demand, Name = "MyUser", Role = "User")]
public static void PrivateInfo()
{   
    //Print secret data.
    Console.WriteLine("\n\nYou have access to the private data!");
}

Role based security is covered in more detail here. I don't know that it will help you much though considering it will require a recompile to change it; however slapping labels on methods is faster than building logic to show/hide buttons or do security validation in code.

Additionally, you'll want to read up on Integrated Windows authentication to gain the Active Directory possibility.

ddc0660
thanks, but the security demand attribute will just throw an exception when an unauthorized user clicks a button, which is not a very 'friendly' thing to do...
Steven A. Lowe
An excellent point...
ddc0660
+1  A: 

It sounds like you could use the LoginView control, which can show panels of controls to only certain users or roles. Roles are most flexible- if no security is required, put all users in all roles.

Use in combination with standard web.config security (integrated windows with active directory, or forms authentication (the asp 2 Sql server schema or your own).

<asp:LoginView id="LoginView1" runat="server">
                    <RoleGroups>
                        <asp:RoleGroup Roles="Admin">
                            <ContentTemplate>
                                <asp:LoginName id="LoginName2" runat="Server"></asp:LoginName>, you
                                are logged in as an administrator.
                            </ContentTemplate>
                        </asp:RoleGroup>
                        <asp:RoleGroup Roles="User">
                            <ContentTemplate>
                                <asp:Button id="Button1" runat="Server" OnClick="AllUserClick">
                            </ContentTemplate>
                        </asp:RoleGroup>
                    </RoleGroups>
                </asp:LoginView>
martin
another interesting approach, thanks - but using this would mean that i would have to replicate most of the html for each page several times, and would not handle the case where a user may be in multiple roles
Steven A. Lowe
A: 

i think i'm going to have to combine AD authorization with 'features and permissions' tables in the database in order to get the fine-grained control that we need -

  • use the web.config file to allow only authorized users (via AD groups) to visit the web site
  • make a 'features' table listing each page and feature that can be affected, e.g. page 1 edit button, page 2 delete button, page 3 detail grid, etc.
  • make a 'permissions' table specfying a feature and an AD group that is allowed to use the feature
  • alter the site pages to check feature-permissions on page-load (or prerender, as appropriate) to disable/hide forbidden features as appropriate

examples:

  • Administrators can use all features of the site
  • Developers can use all features of the site
  • Managers can view all pages, but can only add and edit information, no deletions
  • Supervisors can view summaries for all departments, but see and edit details only for their own department (there is an AD group for each department and dept-supervisor)
  • Staff can view details only for their department
  • etc.

The final solution reduced the notion of 'feature' to a binary can-use or cannot-use decision, and added a 'permissive/not-permissive' flag to each feature. This allows features that most everyone can use to be defined as 'permissive', and then the permissions table only has to record the groups that are denied permission to use that feature. For a feature defined as not-permissive, by default no one can use the feature and you have to create permission table entries for the groups that are allowed to use the feature. This seems to give a best-of-both-worlds solution in that it reduces the number of permission records required for each feature.

Steven A. Lowe
+2  A: 

I think what you need to do here is implement a set of permissions query methods in either your business objects or your controller. Examples: CanRead(), CanEdit(), CanDelete()

When the page renders, it needs to query the business object and determine the users authorized capabilities and enable or disable functionality based on this information. The business object can, in turn, use Roles or additional database queries to determine the active user's permissions.

I can't think of a way to declaratively define these permissions centrally. They need to be distributed into the implementation of the functions. If you want do improve the design, however, you could use dependency injection to insert authorizers into your business objects and thus keep the implementations separate.

There's some code that uses this model in Rocky Lhotka's book. The new version isn't in Google yet.

JoshRivers
i started out with this approach, but soon ran into problems as not all of the permissions are based on entity access; some are simply behavioral and fairly arbitrary.
Steven A. Lowe
see edits to my answer; binary AD group authorization plus Feature permission seems to solve all of the requirements
Steven A. Lowe