+1-888-365-2779
Try Now
More in this section
Categories
Bloggers
Blogs RSS feed

Roles Selector and hiding control based on user's roles

by Radoslav Georgiev
In the following blog post I will sample the creation of a custom WebUITypeEditor which will select roles from the default roles provider and return them as a string array. Then I will use this selector in a control derived from Generic Content Control in order to make it "secured" - it will hide its contents to users which do not belong to at least one of the selected roles. Before we start with the implementation I recommend that you first take a look at the following blog post and KB article in order to get a better idea how to build WebUITypeEditors and create controls deriving from Generic Content Control:
Creating a custom WebUITypeEditor

We will first start with the implementation of the RolesSelector. This clash should inherit WebUITypeEditor. What it will do is to populate the roles within the default role provider into a list box and allow users to select from them. Then it returns the selected roles in the form of a string array. First Lets Create the template for this control. We will place it in ~/Sitefinity/Admin/ControlTemplates/Selectors/RolesSelector.ascx, bellow is the sample markup:
<%@ Control Language="C#" %>
<p><h2>Select roles which will see the control's content.</h2></p>
<asp:Label AssociatedControlID="RolesSource" runat="server" ID="label1" Text="Roles to select from:"></asp:Label><br />
<telerik:RadListBox
    runat="server" ID="RolesSource"
    Height="200px" Width="200px"
    AllowTransfer="true" TransferToID="RolesDestination">
</telerik:RadListBox>
<telerik:RadListBox
    runat="server" ID="RolesDestination"
    Height="200px" Width="200px" />

Now lets create our actual control. We will bind the first list box control to all roles coming from default role provider. If the view state of the control contains information of previously selected roles will will insert them in the second list box which will contain already selected roles. Sample code bellow:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Telerik.Cms.Web.UI;
using Telerik.Web.UI;
using System.Web.UI;
using Telerik.Framework.Web;
using Telerik.Security;
 
namespace Sitefinity.Samples.WebControls
{
    class RolesSelector : WebUITypeEditor<string []>
    {
        public override string[] Value
        {
            get
            {
                IList<string> roles = (new string[]{}).ToList();
                foreach (RadListBoxItem item in RolesDestination.Items)
                {
                   roles.Add(item.Text.ToString());
                }
                return roles.ToArray();
            }
            set
            {
                this.ViewState["selectedRoles"] = value;
            }
        }
 
        private string layoutTemplatePath = "~/Sitefinity/Admin/ControlTemplates/Selectors/RolesSelector.ascx";
 
        public string Template
        {
            get
            {
                object o = this.ViewState["Template"];
                if (o == null)
                    return this.layoutTemplatePath;
                return (string)o;
            }
            set
            {
                this.ViewState["Template"] = value;
            }
        }
        protected RadListBox RolesSource
        {
            get
            {
                return this.Controls[0].FindControl("RolesSource") as RadListBox;
            }
        }
 
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            userManger = new UserManager();
            this.template = ControlUtils.GetTemplate<SelectorTemplate>(Template);
            this.template.InstantiateIn(this);
            RolesSource.DataSource = userManger.GetAllRoles();
            RolesSource.DataBind();
            if (this.ViewState["selectedRoles"] != null)
            {
                IList<string> selectedRoles = (IList<string>)this.ViewState["selectedRoles"];
                RolesDestination.DataSource = selectedRoles;
                RolesDestination.ItemDataBound += new RadListBoxItemEventHandler(RolesDestination_ItemDataBound);
                RolesDestination.DataBind();
            }
        }
 
        void RolesDestination_ItemDataBound(object sender, RadListBoxItemEventArgs e)
        {
            RadListBoxItem item = RolesSource.FindItemByText(e.Item.Text);
            if (item != null)
            {
                RolesSource.Delete(item);
            }
        }
 
        protected RadListBox RolesDestination
        {
            get
            {
                return this.Controls[0].FindControl("RolesDestination") as RadListBox;
            }
        }
        public class SelectorTemplate : ITemplate
        {
            #region ITemplate Members
 
            public void InstantiateIn(Control container)
            {
                //throw new NotImplementedException();
            }
 
            #endregion
        }
        private ITemplate template;
        private UserManager userManger;
    }
}

The next step is to create the control which is going to use our RolesSelector. As in the KB article linked above we will create a custom control which inherits from Generic Content Control. Additionally we have to expose a property for setting selected roles and add some more logic to the Render method override. First lets add the property and get a reference of the div containing the actual generic content control:
/// <summary>
/// Gets or sets the roles which are allowed to see content of the cotnrol. We are going to use the RolesSelector for WebEditor
/// </summary>
[Browsable(true)]
[WebEditor("Sitefinity.Samples.WebControls.RolesSelector, Sitefinity.Samples"),TypeConverter(typeof(Telerik.Framework.Utilities.StringArrayConverter))]
public string[] Roles
{
    get
    {
        object obj = this.roles;
        if (obj == null)
            return new string[]{};
        return (string[])obj;
    }
    set
    {
        this.roles = value;
    }
}
 
/// <summary>
/// The div containing the generic content control
/// </summary>
[Browsable(false)]
public HtmlGenericControl ContentWrapper
{
    get { return this.Container.GetControl<HtmlGenericControl>("contentWrapper", true); }
}

In the method overriding Render we will check if the selected roles property is null and if not check against the current user's roles. If user does not belong to at least one of selected roles the user will not be able to see the content of the control:
protected override void Render(HtmlTextWriter writer)
{
 
    StringWriter content = null;
    HtmlTextWriter contentWriter = null;
 
    try
    {
        // if in normal mode, output the full content of the control
        if (!this.DesignMode)
        {
            // first, we need to "extract"
            content = new StringWriter();
            contentWriter = new HtmlTextWriter(content);
            base.Render(contentWriter);
 
            this.ContentPlaceholder.Text = content.ToString();
            //check selected roles and set behavior accordingly
            if (this.roles != null)
            {
                UserManager userManager = new UserManager();
                MembershipUser currentUser = userManager.GetUser();
                if (currentUser != null)
                {
                    bool userInRole = false;
                    foreach (string role in this.roles)
                    {
                        userInRole = userManager.IsUserInRole(role);
                        if (userInRole == true)
                            break;
                    }
                    if (userInRole == false)
                        ContentWrapper.Visible = false;
                }
                else
                {
                    ContentWrapper.Visible = false;
                }
            }
            // we need to do this manually, as generic content control renders its
            // content directly and does not inherit CompositeControl,
            // so it will not render out template correctly (not at all, actually)
            foreach (Control child in this.Controls)
            {
                child.RenderControl(writer);
            }
            if (!this.Controls.Contains(this.Container))
            {
                this.Container.RenderControl(writer);
            }
        }
        else
        {
            // if in edit (design) mode, output the content of the genreric content control
            // THIS IS REQUIRED
            base.Render(writer);
        }
    }
    finally
    {
        if (content != null)
        {
            content.Dispose();
        }
        if (contentWriter != null)
        {
            contentWriter.Dispose();
        }
    }
    
}

You should note that overriding the render method is required when you are dealing with control inheriting from Generic Content Control. If you are going to create a custom control which inherits from SimpleControl for example you need to override CreateChildControls() and place the logic for hiding content there. If the user does not belong to selected roles you can simply clear the control collection - nothing will be displayed.

Sample project can be downloaded from this link: RolesSelector. The roles selector and custom generic content control are packed in a code library. After you add this to your solution you fix the assembly references in the Sitefinity.Samples project. Then add the Sitefinity.Samples project as a project reference in your Sitefinity website and build the Sitefinity.Samples project. Finally add the custom control to your toolbox, include this in your web.config:
<toolboxControls>
  <clear />
  ...
  <add name="Custom Generic Content" section="Sitefinity Samples" type="Sitefinity.Samples.WebControls.GCWrapper, Sitefinity.Samples" />
</toolboxControls>

5 comments

Leave a comment
  1. KingKong Feb 23, 2010
    Thanks Rado. Very good post. I copied this logic to several other controls and this works without problems. Now my controls are secured.
  2. Sravrandidra Masculajundra Feb 23, 2010
    thank you for your post
  3. Felipe May 03, 2010
    Download link is broken
  4. yohannes May 26, 2011
    Hi Support,

    I am having a problem here. I have copied this but didn't seem to work for me. The error I have is that, I couldn't access the controls in the template.  The following line is throwing error saying that index is out of range.

    return

     

     

    this.Controls[0].FindControl("RolesSource") as RadListBox;

    Do I need to provide implementation for the method:

     

     

    public

     

     

    void InstantiateIn(Control container)

     

     

    {

     

     

     

     

    //throw new NotImplementedException();

     

     

     

     

     

     

     

     

    }
    Any help on this will be appreciated    

    Thanks

    Yohannes

  5. yohannes May 26, 2011
    Hi Support,

    I am having a problem here. I have copied this but didn't seem to work for me. The error I have is that, I couldn't access the controls in the template.  The following line is throwing error saying that index is out of range.

    return

     

     

    this.Controls[0].FindControl("RolesSource") as RadListBox;

    Do I need to provide implementation for the method:

     

     

    public

     

     

    void InstantiateIn(Control container)

     

     

    {

     

     

     

     

    //throw new NotImplementedException();

     

     

     

     

     

     

     

     

    }
    Any help on this will be appreciated    

    Thanks

    Yohannes

    Leave a comment