do we not need HasPermissions(object, action) instead

Apr 14, 2010 at 5:46 PM

Dear Developer --

You have a very nice code set here.

I am wondering, however...

...do we not need HasPermissions(object, action) instead?

For example, do we not need to be able to check if the current-user has permission to perform target-action on target-entity-type?

What do you think?

Please advise.

Thank you.

-- Mark Kamoski

 

Coordinator
Apr 17, 2010 at 5:47 AM

Hi mkamoski,

I think I understand what you want, but please can you give me a sample code ?

Apr 19, 2010 at 12:34 PM
malbaladejo wrote:

Hi mkamoski,

I think I understand what you want, but please can you give me a sample code ?

Mal --

The general design for an permissions helper that can handle object-level permissions might look something like this...

 

 

//----------

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

namespace Test.General.Security
{
    /// <summary>
    /// This class exposes a consumer-friendly list of actions to which permissions may be applied.
    /// </summary>
    /// <remarks>
    /// Note that this class probably needs to be manually created and maintained.
    /// Note that this class probably will only need a maximum of about 10, at the most.
    /// </remarks>
    public static class PermissionAction
    {
        public enum Name
        {
            Create,
            Delete, 
            Retrieve,
            Update
        }
    }
}

//----------

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

namespace Test.General.Security
{
    /// <summary>
    /// This class exposes a consumer-friendly helper for checking permissions.
    /// </summary>
    /// <remarks>
    /// As of 20100419, this is a non-functioning shell for illustration purposes only.
    /// Note that "persistant storage" may have all of the access control list and it may be cached, 
    /// which means it may not be necessary to go to the database each time in some cases-- it depends on the design.
    /// </remarks>
    public static class PermissionHelper
    {
        public static bool HasPermissions(
            System.Security.Principal.IPrincipal targetUser, 
            Test.General.Security.PermissionObject.Name targetObject,
            Test.General.Security.PermissionAction.Name targetAction)
        {
            bool hasPermissionsFlag = false;

            if ((targetUser == null) ||
                (targetUser.Identity == null) ||
                (!targetUser.Identity.IsAuthenticated) ||
                (targetUser.Identity.Name == null) ||
                (targetUser.Identity.Name.Trim().Length <= 0))
            {
                hasPermissionsFlag = false;
            }
            else
            {
                Guid myObjectID = PermissionHelper.GetObjectID(targetObject);
                Guid myActionID = PermissionHelper.GetObjectID(targetObject);
                Guid myUserID = PermissionHelper.GetUserID(targetUser.Identity.Name);
                Guid myGroupID = PermissionHelper.GetGroupID(myUserID);
                hasPermissionsFlag = PermissionHelper.RowExists(myGroupID, myObjectID, myActionID);
            }

            return hasPermissionsFlag;
        }

        private static Guid GetActionID(Test.General.Security.PermissionAction.Name targetActionName)
        {
            Guid myID = Guid.Empty;

            //TODO. 20100419. Get the ID from the persistant storage.

            return myID;
        }

        /// <summary>
        /// This will get the ID of the object-type, not the ID of an object instance.
        /// </summary>
        /// <param name="targetObjectName">This is the object name to use.</param>
        /// <returns>This is the ID of the object-type.</returns>
        private static Guid GetObjectID(Test.General.Security.PermissionObject.Name targetObjectName)
        {
            Guid myID = Guid.Empty;

            //TODO. 20100419. Get the ID from the persistant storage.

            return myID;
        }

        /// <summary>
        /// This will get the UserID based on the UserName.
        /// </summary>
        /// <param name="targetUserName">This is the user name to check.</param>
        /// <returns>This is the ID of the user.</returns>
        /// <remarks>
        /// This presupposes that the username is a natural key.
        /// In some cases, one may already have the UserID, where UserID is stored in "Identity.Name".
        /// </remarks>
        private static Guid GetUserID(string targetUserName)
        {
            Guid myID = Guid.Empty;

            //TODO. 20100419. Get the ID from the persistant storage.

            return myID;
        }

        /// <summary>
        /// This will get the group to which the user belongs.
        /// </summary>
        /// <param name="targetUserID">This is the user to check.</param>
        /// <returns>This is the group ID.</returns>
        private static Guid GetGroupID(Guid targetUserID)
        {
            Guid myID = Guid.Empty;

            //TODO. 20100419. Get the ID from the persistant storage.

            return myID;
        }

        /// <summary>
        /// This will check to see if the given row exists.
        /// </summary>
        /// <param name="targetGroupID">This is the ID of the group that we are checking.</param>
        /// <param name="targetObjectID">This is the ID of the object-type that we are checking, not an instance ID.</param>
        /// <param name="targetActionID">This is the ID of the action-type that we are checking, not an instance ID.</param>
        /// <returns>This is a flag indicating existence (true) or not (false).</returns>
        private static bool RowExists(Guid targetGroupID, Guid targetObjectID, Guid targetActionID)
        {
            bool isExisting = false;

            //TODO. 20100419. See if the row exists in the persistant storage.

            return isExisting;
        }
    }
}

//----------

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

namespace Test.General.Security
{
    /// <summary>
    /// This class exposes a consumer-friendly list of objects to which permissions may be applied.
    /// </summary>
    /// <remarks>
    /// Note that this class should be generated from database table and view names via T4 or similar.
    /// </remarks>
    public static class PermissionObject
    {
        public enum Name
        {
            AddressEntity,
            PersonEntity,
            CompanyEntity
        }
    }
}

 

 

<%@ page language="C#" autoeventwireup="true" codefile="PermissionHelperTest01.aspx.cs" inherits="Test.General.Security.PermissionHelperTest01" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>PermissionHelperTest01</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <p>
            This is the code-infront showing how to use a sample design for a object-level permissions helper.
        </p>
        <p>
            <asp:button id="CreateButton" runat="server" text="Create" onclick="CreateButton_Click" />
        </p>
    </div>
    </form>
</body>
</html>

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Test.General.Security
{
    /// <summary>
    /// This is the code-behind showing how to use a sample design for a object-level permissions helper.
    /// </summary>
    public partial class PermissionHelperTest01 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            //This is a test call-site, showing one way to design a permission helper.
            if (Security.PermissionHelper.HasPermissions(this.User, PermissionObject.Name.AddressEntity, PermissionAction.Name.Create))
            {
                this.CreateButton.Enabled = true;
            }
            else
            {
                this.CreateButton.Enabled = true;
            }
        }

        protected void CreateButton_Click(object sender, EventArgs e)
        {
            //Do some work.
        }
    }
}

 

HTH.

Thank you.

-- Mark Kamoski

 

Apr 19, 2010 at 12:51 PM

Mal --

Of course, it really just makes a difference as to how granular and fine-grained one wants to get with a permissions checker.

Your current design would, certainly work, even with object level permissions-- it would just mean that would would need a lot of permission-names as those permissions would need to express both the object-type and the action-type.

That may be fine and it may even be less complex, so one would have something like the following...

//...

if (Permissions.UserHasPermission("AddressObjectCreate"))
{
    //...
}

if (Permissions.UserHasPermission("AddressObjectUpdate"))
{
    //...
}

if (Permissions.UserHasPermission("AddressObjectDelete"))
{
    //...
}

//...

...and all those tokens (AddressObjectCreate, AddressObjectUpdate, AddressObjectDelete, ...) could be generated from a Linq schema or similar.

Maybe.

I am just suggested, above, another way to do it-- but I am not sure which is better in the long run.

HTH.

Thank you.

-- Mark Kamoski