Security - Showing and Hiding Page Elements Based on User Role

Overview

This article describes how to hide or show page elements based on the ASP.NET role the current user is in.

Walk-Through

PageElement table

Field DescriptionExample
DescriptionFree-form textAdmin > Users > edit user button
UrlPatternWhen the current URL is LIKE this pattern, we have a match%/Admin/Users/%
SelectorjQuery selector of element(s)#uxEditButton

PageElementRole table

This table contains all combinations of PageElements and ASP.NET roles that should be able to see those page elements.

HTML Code

Page elements MUST be hidden by default, as shown below. The code won't hide page elements that the user doesn't have access to, only show page elements the user does have access to.

<div id='uxMyPageElement' style='display:none' ></div>

Page-Specific JavaScript



Code

Database Tables

{copytext|tables}
CREATE TABLE [dbo].[PageElement](
	[PageElementID] [int] IDENTITY(1,1) NOT NULL,
	[Descrip] [varchar](50) NOT NULL,
	[UrlPattern] [varchar](300) NOT NULL,
	[Selector] [varchar](300) NOT NULL,
 CONSTRAINT [PK_PageElement] PRIMARY KEY CLUSTERED 
(
	[PageElementID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

/*-----------------------------------------------------------------------------------------------*/
CREATE TABLE [dbo].[PageElementRole](
	[PageElementRoleID] [int] IDENTITY(1,1) NOT NULL,
	[PageElementID] [int] NOT NULL,
	[RoleID] [uniqueidentifier] NOT NULL,
 CONSTRAINT [PK_PageElementRole] PRIMARY KEY CLUSTERED 
(
	[PageElementRoleID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[PageElementRole]  WITH CHECK ADD CONSTRAINT [FK_PageElementRole_aspnet_Roles] 
FOREIGN KEY([RoleID]) REFERENCES [dbo].[aspnet_Roles] ([RoleId])
GO

ALTER TABLE [dbo].[PageElementRole] CHECK CONSTRAINT [FK_PageElementRole_aspnet_Roles]
GO

ALTER TABLE [dbo].[PageElementRole]  WITH CHECK ADD  CONSTRAINT [FK_PageElementRole_PageElement] 
FOREIGN KEY([PageElementID]) REFERENCES [dbo].[PageElement] ([PageElementID])
GO

ALTER TABLE [dbo].[PageElementRole] CHECK CONSTRAINT [FK_PageElementRole_PageElement]
GO

Stored Procedure

{copytext|storedproc}
create procedure [dbo].[GetPageElementsForUser]
    (
     @url       varchar(1000)
    ,@username  nvarchar(256)
    ) as
/*------------------*/  
/*  
declare
     @url       varchar(1000) = 'http://localhost:26335/Claim/Maintain/2'
    ,@username  nvarchar(256) = 'pjasinski'
*/    
/*------------------*/    
declare
    @result varchar(max)
       
;
with a as (
    select distinct 
        pe.Selector
    from 
        dbo.PageElement pe
        
        inner join dbo.PageElementRole per
            on pe.PageElementID = per.PageElementID
            
        inner join dbo.aspnet_UsersInRoles uir
            on uir.RoleId = per.RoleID
            
        inner join dbo.aspnet_Users u
            on u.UserId = uir.UserId
            and u.UserName = @username
            
    where 1=1
        and @url like pe.UrlPattern
    )
select
    @result = coalesce(@result + '|', '') + Selector
from
    a      

select 
    result = @result

Entity Data Model

When you do a Function Import of the above stored procedure, specify that it returns a collection of Scalars: string.

Data Provider Code

{copytext|dataprov}
public string GetPageElementsForUser(string url, string username)
{
    ObjectResult<string> chunks = Database.GetPageElementsForUser(url, username);
    StringBuilder sb = new StringBuilder();
    foreach (string chunk in chunks)
        sb.Append(chunk);
    return sb.ToString();
}

Data Provider Wrapper

{copytext|datawrap}
public static string GetPageElementsHtml()
{
    StringBuilder sb = new StringBuilder();
    sb.Append("<input type='hidden' id='uxPageElementsToShow' value='");
    string username = HttpContext.Current.User.Identity.Name;
    string url = HttpContext.Current.Request.Url.ToString();
    DataProvider dp = new DataProvider(Common.RequestContext);
    string e = dp.GetPageElementsForUser(url, username);
    sb.Append(e.Replace("'", "''"));
    sb.Append("'/>");
    return sb.ToString();
}

Master Page Code

{copytext|masterpage}
<%if (Request.IsAuthenticated)
{

    Response.Write(MySite.Web.Common.Security.GetPageElementsHtml()); 
                   
    %>

JavaScript Code

Be sure to call the enforceSecurityCommon() function from your document.ready function.

{copytext|javascript}
/*===============================================================================================*/
function isVisible(selector) {
    return $(selector).is(':visible');
}
/*===============================================================================================*/
function enable(selector, enabled) {
    if (!enabled) {
        $(selector).attr('disabled', 'disabled');
    }
    else {
        $(selector).removeAttr('disabled');
    }
}
/*===============================================================================================*/
function show(selector, visible) {
    if (visible)
        $(selector).show();
    else
        $(selector).hide();
}
/*===============================================================================================*/
function enforceSecurity() {
    // "override" this function in your page-specific javascript
    // DO NOT call the enforceSecurity() function from your DOCUMENT.READY function!!!
    //alert("This is the common [enforceSecurity]");
}
/*===============================================================================================*/
function enforceSecurityCommon() {
    var itemsConcat = $('#uxPageElementsToShow').val();
    if (itemsConcat != undefined) {
        var items = itemsConcat.split('|');
        for (i = 0; i < items.length; i++) {
            show(items[i], true);
        }
    }
    enforceSecurity();
}
/*===============================================================================================*/

Deployment

Comparison

The following code will compare the page element security configuration between two databases: SourceDb and TargetDb.

{copytext|compareMenus}
/*== Roles ======================================================================================*/
select
    b.*
from
    SourceDb.dbo.aspnet_Roles b
    left join TargetDb.dbo.aspnet_Roles a
        on a.RoleId = b.RoleId
where
    a.RoleId is null
    or a.ApplicationId <> b.ApplicationId
    or a.LoweredRoleName <> b.LoweredRoleName
    or a.RoleName <> b.RoleName        
    
/*===============================================================================================*/
/* Page Elements */
select
     b.*    
    ,Diff = case when a.PageElementID is null then 'Not deployed'
            else 'Field diffs: ' 
                + case when b.Descrip <> a.Descrip then 'Descrip; ' else '' end
                + case when b.Selector <> a.Selector then 'Selector; ' else '' end
                + case when b.UrlPattern <> a.UrlPattern then 'SortOrder; ' else '' end
            end
from
    TargetDb.dbo.PageElement a
    right join SourceDb.dbo.PageElement b
        on a.PageElementID = b.PageElementID
where 
    a.PageElementID is null
    --or
    --b.Descrip <> a.Descrip
    or
    b.Selector <> a.Selector
    or
    b.UrlPattern <> a.UrlPattern
/*===============================================================================================*/
/* Page Element Role */    
select
     b.*  
    ,pe.Descrip
    ,r.RoleName  
    ,Diff = case when a.PageElementRoleID is null then 'Not deployed'
            else 'Field diffs: ' 
                + case when b.PageElementID <> a.PageElementID then 'PageElementID; ' else '' end
                + case when b.RoleID <> a.RoleID then 'RoleID; ' else '' end
            end
from
    SourceDb.dbo.PageElementRole b
    left join TargetDb.dbo.PageElementRole a
        on a.PageElementRoleID = b.PageElementRoleID
    inner join SourceDb.dbo.PageElement pe
        on b.PageElementID = pe.PageElementID
    inner join SourceDb.dbo.aspnet_Roles r
        on b.RoleID = r.RoleId
where
    a.PageElementRoleID is null
    or b.PageElementID <> a.PageElementID
    or b.RoleID <> a.RoleID

Synchronization

The following code will update the page element security configuration from the SourceDb database to the TargetDb database.

{copytext|syncMenu}
use TargetDb
/*=== Roles =====================================================================================*/
insert into TargetDb.dbo.aspnet_Roles(ApplicationId, RoleId, RoleName, LoweredRoleName, Description)
select ApplicationId, RoleId, RoleName, LoweredRoleName, Description
from SourceDb.dbo.aspnet_Roles 
where RoleId not in (select RoleId from TargetDb.dbo.aspnet_Roles)

/*=== Page Elements =============================================================================*/
delete from TargetDb.dbo.PageElement
where PageElementID not in (Select PageElementID from SourceDb.dbo.PageElement)

update 
    TargetDb.dbo.PageElement
set
     Descrip = b.Descrip
    ,Selector = b.Selector
    ,UrlPattern = b.UrlPattern
from    
    TargetDb.dbo.PageElement a
    inner join SourceDb.dbo.PageElement b
        on a.PageElementID = b.PageElementID

set identity_insert TargetDb.dbo.PageElement on

insert into TargetDb.dbo.PageElement ( PageElementID, Descrip, Selector, UrlPattern)
select PageElementID, Descrip, Selector, UrlPattern
from SourceDb.dbo.PageElement
where PageElementID not in (Select PageElementID from TargetDb.dbo.PageElement)

set identity_insert TargetDb.dbo.PageElement off

/*=== Page Element Role =========================================================================*/
delete from TargetDb.dbo.PageElementRole
where PageElementRoleID not in (select PageElementRoleID from SourceDb.dbo.PageElementRole)

update
    TargetDb.dbo.PageElementRole
set
     PageElementID = b.PageElementID
    ,RoleID = b.RoleID
from
    TargetDb.dbo.PageElementRole a
    inner join SourceDb.dbo.PageElementRole b
        on a.PageElementRoleID = b.PageElementRoleID
 
 set identity_insert TargetDb.dbo.PageElementRole on
 
 insert into TargetDb.dbo.PageElementRole (PageElementRoleID, PageElementID, RoleID)
 select PageElementRoleID, PageElementID, RoleID
 from SourceDb.dbo.PageElementRole
 where PageElementRoleID not in (select PageElementRoleID from TargetDb.dbo.PageElementRole)    
 
 set identity_insert TargetDb.dbo.PageElementRole off