Wednesday, 15 July 2009

Securing Dynamic Data Preview 4 Refresh – Part 3

Continuing from the previous article Securing Dynamic Data Preview 4 Refresh – Part 2  we will proceed to complete the series by finishing off the hyperlinks so you have the option of them all showing as disabled or all being hidden.

The reasoning behind this extra bit to the series (if you can call two articles a series) is I don’t like

  • Dead hyperlinks when you mouse over you get the hyperlink action i.e. underline.
  • I like consystancy i.e. some links hidden and some shown as dead I want it all to be the same.

So to complete this we will add a new web user control called DynamicHyperLink.ascx which will wrap the asp:DynamicHyperlink control.

%@ Control 
    Language="C#" 
    AutoEventWireup="true" 
    CodeBehind="DynamicHyperLink.ascx.cs" 
    Inherits="DD_EF_SecuringDynamicData.DynamicData.DynamicHyperLink" %>

<asp:DynamicHyperLink 
    ID="DynamicHyperLink1" 
    runat="server" 
    onprerender="DynamicHyperLink1_PreRender">
</asp:DynamicHyperLink>
<asp:Literal ID="Literal1" runat="server">&nbsp;</asp:Literal>

Listing 1 - DynamicHyperLink.ascx

public partial class DynamicHyperLink : System.Web.UI.UserControl
{
    public String Action { get; set; }
    public String Text { get; set; }
    public String CssClass { get; set; }
    public Boolean ShowText { get; set; }
    public String DisabledClass { get; set; }

    protected void Page_Init(object sender, EventArgs e)
    {
        DynamicHyperLink1.Action = Action;
        DynamicHyperLink1.Text = Text;
        DynamicHyperLink1.CssClass = CssClass;
    }

    /// <summary>
    /// Handles the PreRender event of the DynamicHyperLink1 control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">
    /// The <see cref="System.EventArgs"/> 
    /// instance containing the event data.
    /// </param>
    protected void DynamicHyperLink1_PreRender(object sender, EventArgs e)
    {
        // the DynamicHyperLink is considered disabled if it has no URL
        if (String.IsNullOrEmpty(DynamicHyperLink1.NavigateUrl))
        {
            DynamicHyperLink1.Visible = false;
            Literal1.Visible = false;
            if (ShowText)
            {
                Literal1.Visible = true;
                Literal1.Text = String.Format(
                    "<span class=\"{0}\">{1}</span>&nbsp;", 
                    DisabledClass, 
                    Text);
            }
        }
    }
}

Listing 2 - DynamicHyperLink.ascx.cs

With the DynamicHyperLink web user control we simply have some properties to allow us to pass in the main properties of the asp:DynamicHyperLink Action, Text and CssClass. The last two properties ShowText and DisbledClass are used:

  • ShowText – to show the Text property in a span when the DynamicHyerLink is hidden.
  • DisbledClass – is the css class to use when ShowText is true.

So this is the way it works:

When the DynamicHyperLink’s NavigateUrl is and empty string or null then the DynamicHyperLink is in the disabled state. In the disabled state if ShowText is false then no control is displayed when in a disabled state, and when ShowText is true as span is displayed surrounding the Text property and having the DisabledCalss css as the span’s class.

<%@ Control 
    Language="C#" 
    AutoEventWireup="true" 
    CodeBehind="DeleteLink.ascx.cs" 
    Inherits="DD_EF_SecuringDynamicData.DeleteLink" %>
<asp:LinkButton 
    ID="LinkButton1" 
    runat="server" 
    CommandName="Delete" Text="Delete"
    OnClientClick='return confirm("Are you sure you want to delete this item?");' />
<asp:Literal ID="Literal1" runat="server">&nbsp;</asp:Literal>

Listing 3 – DeleteHyperLink.ascx

public partial class DeleteHyperLink : System.Web.UI.UserControl
{
    public Boolean ShowText { get; set; }
    public String CssClass { get; set; }
    public String DisabledClass { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        LinkButton1.CssClass = CssClass;

        // get restrictions for the current
        // users access to this table
        var table = DynamicDataRouteHandler.GetRequestMetaTable(Context);
        var usersRoles = Roles.GetRolesForUser();
        var tableRestrictions = table.Attributes.OfType<SecureTableAttribute>();
        if (tableRestrictions.Count() == 0)
            return;

        foreach (var tp in tableRestrictions)
        {
            // the LinkButton is considered disabled if delete is denied.
            if (tp.HasAnyRole(usersRoles) &&
                (tp.Restriction & TableDeny.Delete) == TableDeny.Delete)
            {
                LinkButton1.Visible = false;
                LinkButton1.OnClientClick = null;
                LinkButton1.Enabled = false;
                Literal1.Visible = false;
                if (ShowText)
                {
                    Literal1.Visible = true;
                    Literal1.Text = String.Format(
"<span class=\"{0}\">Delete</span>&nbsp;",
DisabledClass); } } } } }

Listing 4 – DeleteHyperLink.ascx.cs

This is pretty similar to the DynamicHyperLink.ascx the main difference being how the LinkButton is determined to be disabled, this is done by the foreach loop checking each restriction on the current table and checking to see if it is TableDeny.Delete and if so setting the disabled state.

Download

Note: This has an ASPNETDB database which requires SQL Express 2008 and a connection to Northwind you will need to edit the connection string for Northwind.

Happy coding HappyWizard

19 comments:

trialot said...

Missing Text....

It seems that something misses in the text, being the securecolumattribute class.

The corresponding file is in the download, but not in the explanation.

Please add the class and corresponding explanation.

trialot said...

Question?

In the Dynamic Data Preview 4 there is a Futures Sample, containing custom classes, tables etc.

These CustomMetaModel, CustomMetaTable (etc.) classes are more elaborate then their SecureMeta* counterparts, as they are proposed by you.

This is somewhat confusing, in different ways.

First of all, seems to me that inclusion of CustomMeta* classes should be desirable and that the reference to the Meta* classes in the SecureMeta* classes should be changed to CustomMeta*.

Not sure though, which changes are adequate. Any suggestions?

Second, the CustomMeta* classes are more elaborate and contain more definitions.

I suppose they can be simply copied (with minor alterations) to the SecureMeta*.

Any suggestions?

Third and most important: it seems to me that we want customization with security (with security being part of customization).

It then seems to be desirable to have SecureMeta* classes that extend the CustomMeta* classes.

If i am not mistaken, this would be a solution to the second point and requiring a step as mentioned in the first point.

But confusion exists: can you have a look and add a part 4 of securing dynamic data?

Thanx in advance.....

Stephen J. Naughton said...

Hi trialotm, first I think you will find the missing bit in part 1.

second, this is just an example and I like to keep them as simple as possible.

And yes you are correct about the custom meta classes inheritance would let you do that.

There is more you can do with the custom metadata classes, one thing I'm looking at is allowing you to pass in a delegate for row generation. I have also asked that some of the other methods of Table and Column be made virtual so they can be extended i.e. IsReadOnly which could add the logic to determin by security if the column was read only.

Steve :D

trialot said...

I am not sure what you want to achieve with the extension part of your answer.

If I am not mistaken, you want a gridview or formview (the ones used in DynData) that has the attribute IsReadOnly in both the parent control (e.g. asp:gridview/formview) as the child controls (in this case, only itemtemplates or variants of that).

If that is your intention, than you have the problem that this intervenes with the DefaultMode in the formview.

Simple managed code can be used to make formviews read-only for specific (security) roles. No need to extend or no need to use attributes there.

The gridview is something different though. No attribute such as DefaultMode is present.

Then again, you probably want to prevent (based upon security rules and roles) the edit, delete and insert hyperlink and the possibility for inline editing.

Managed code can again be used to disenable and remove visibility of the linkbuttons.

The only thing that then remains is the read-only property for specific columns, but that can be easily achieved by managed code (when using templates for the columns).

Seems to me that you desire an extension that is not directly necessary. I can be mistaken.

As a final consideration: is the securityattribute meaningful, taking into consideration that everything can be achieved by managed code?

Yes, it is, as a general method. The managed code approach should be used for custom page templates or in addition, in order to tie up and intensify security.

Furthermore, the security part of dynamicdata contains the delicious routehandler, also resulting in a better security.

In short, no need for the read-only extension.

By the way, any quick idea how the prettydynamicdataroute compares to the secure dynamicdataroutehandler?

I presume the same idea applies as in my previous post.....

As a final point: if you want me to, i am happy to provide you with some (untested) code, containing the combination of security and custom classes, including the prettydynamicdataroute and securedynamicdataroutehandler.....

trialot said...

Euhm, as another comment.....i have changed the secure dynamicdataroute classes to incorporate both the restrictions on tables as on actions.

Maybe you can have a look at it and post the corresponding changes to this topic.

Shall i mail ?

Stephen J. Naughton said...

Hi Trialot, as I explained before I'm just trying to give a simple sample that does security, I don;t waht to make to too complicated otherwise it will confuse the issue.

However if the IsReadOnly method of the Column class becomes virtual the I can return true in cases where there is restricted access this would then simplyfy the code. and when this change appears then I will look at extending the solution to incorporate it.

Steve :D
P.S. just drop me an e-mail is you want to chat more in depth on this and I can add you to messenger :D

jeff Denmark said...

Great stuff, but its's hard following along for us lowley VB types..

it's not the regular loops and logic that stumps us, but the multitude of new declarations with inheritance, metadata, etc..

do you know if this was translated to VB by anyone ?

Stephen J. Naughton said...

Sorry about that, I switched to C# about 4-5 years ago and find it hard to switch back now :(

and I've not heard of anyone doing a port.

Steve :)

primate said...

Hi Steve

I managed to understand and get everything to work except for the final two hyperlink controls (DynamicHyperLink.ascx & DeleteHyperLink.ascx). Please could you provide an example of how to use them?

Many thanks, Simon

Stephen J. Naughton said...

Have you run the sample?

primate said...

Thanks for the snappy response Steve.

I've been using the sample as a reference and to fill in some of the gaps. Especially the SecureColumnAttribute code. Problem is that the hyperlink controls don't appear to be implemented in the sample and I can't figure out how they are supposed to be used.

Everything else works great.

Thanks, Simon

primate said...

Thanks for the snappy response Steve.

I've been using the sample as a reference and to fill in some of the gaps. Especially the SecureColumnAttribute code. Problem is that the hyperlink controls don't appear to be implemented in the sample and I can't figure out how they are supposed to be used.

Everything else works great.

Thanks, Simon

primate said...

Hi Steve

The hyperlink controls don't appear to be implemented in the sample. If they are I can't find them.

Thanks, Simon

Anonymous said...

Hi Steve

The hyperlink controls don't appear to be implemented in the sample. If they are I can't find them.

Thanks, Simon

Anonymous said...

Hi Stev,
I'm new in programing ASP.NET
I use DD whith EF NET 4, I downloaded SecuringDynamicData - 2010-06-13a.zip security works without problem, but as I try to use ListDetail.aspx it does not work, the only change is that :


routes.Add(New DynamicDataRoute("{table}/ListDetails.aspx") With { _
.Action = PageAction.List, _
.RouteHandler = New SecureDynamicDataRouteHandler(), _
.Model = DefaultModel, _
.ViewName = "ListDetails"})

routes.Add(New DynamicDataRoute("{table}/ListDetails.aspx") With { _
.Action = PageAction.Details, _
.RouteHandler = New SecureDynamicDataRouteHandler(), _
.Model = DefaultModel, _
.ViewName = "ListDetails"})


Sorry the code is in VB.
Thank you for your help.

Stephen J. Naughton said...

Hi VB not a problem :) I don't believe I tested it with ListDetails sorry. I'll try to have a look today.

Steve.

Anonymous said...

Thank you Steve

Anonymous said...

Hi Steve,
have you tested the "SecuringDynamicData" whith listDetails ?

Evaldas said...

nevermind about my previous question.