Domanda

I need to implement some kind of RBAC for a WPF app that I'm writing at the moment. Since v2.0 ASP.NET has had the Security, Membership and Role Management infrastructure (as described here for example) and while I could use that it still feels that to use it in this context would be a bit hacky. I'll welcome feedback from anyone who has used it and had success in a similar context.

I've also considered using AD LDS, read the TechNet articles and looked at some of the MSDN code samples but I'm wondering if there's any component (for .NET) out there that removes some of the inherent complexity behind creating the database, setting it up for deployment and the on-going maintenance. Free or commercial is okay in this instance.

Other questions on SO mention Client Application Services but this entails adding IIS into the mix, which, while not beyond the bounds of possibility, was something I didn't envisage at the outset of the project.

What are the best practices in this case? The app is a typical n-tier type affair, which talks to a remote SQL Server database so the roles could be stored there if needs be

È stato utile?

Soluzione

You could have a look at the P&P guidance / code for ideas (or you could use their block perhaps). http://msdn.microsoft.com/en-us/library/ff953196(v=pandp.50).aspx

I implemented my own back-end store in SQLServer. Its not that hard, tables like User,UserRole,SecurityItem,SecurityItemUser,SecurityItemRole. I authenticate the user's windows login against AD, but only store their windows login name in the database (eg the key for the User table).

It is a good idea to abstract things away via interfaces / provider model. That way if your app changes in the future, it won't require much refactoring.

I built a 2 tier app (WPF -> SQLServer) that grew a lot, and management have decided for security they now want a 3 tier app (WCF middle tier). I am working on this now, and it is a real pain because I coupled my authorization code too closely with the client app. It is apparent now that the authorization should be happening in the service tier, but will require a lot of work.

In terms of how to identify a particular 'securable', I came up with a nice trick that saves a lot of work. Although, ironically this is part of the problem I now have trying to re-engineer it for 3 tiers. The trick is to use the fully qualified name of the class as a unique identifier for a securable, then you can use some simple code each time you check :

_secUtil.PromptSecurityCheck(_secUtil.GetFullyQualifiedObjectName(this, "Save"))

Here is some other code to give you an idea how I did it (using P&P framework).

public class SecurityUtil : ISecurityUtil
{
    public string DatabaseUserName { get { return LocalUserManager.GetUserName(); } }

    public bool PromptSecurityCheck(string securityContext)
    {
        bool ret = IsAuthorized(securityContext);

        if (!ret)
        {
            MessageBox.Show(string.Format("You are not authorised to perform the action '{0}'.", securityContext), Settings.Default.AppTitle,
                                        MessageBoxButton.OK, MessageBoxImage.Error);
        }

        return ret;
    }

    public bool IsAuthorized(string securityContext)
    {
        IAuthorizationProvider ruleProvider = AuthorizationFactory.GetAuthorizationProvider("MyAuthorizationProvider");

        //bool ret = ruleProvider.Authorize(LocalUserManager.GetThreadPrinciple(), securityContext);
        bool ret = ruleProvider.Authorize(LocalUserManager.GetCurrentPrinciple(), securityContext);            
        return ret;
    }

    public string GetFullyQualifiedName(object element)
    {
        return element.GetType().FullName;
    }

    public string GetFullyQualifiedObjectName(object hostControl, string objectName)
    {
        return GetFullyQualifiedName(hostControl) + "." + objectName;
    }
}

[ConfigurationElementType(typeof(CustomAuthorizationProviderData))]
public class MyAuthorizationProvider : AuthorizationProvider
{
    public SitesAuthorizationProvider(NameValueCollection configurationItems)
    {
    }

    public override bool Authorize(IPrincipal principal, string context)
    {

        bool ret = false;

        if (principal.Identity.IsAuthenticated)
        {
            // check the security item key, otherwise check the screen uri
            ret = LocalCacheManager.GetUserSecurityItemsCache(LocalUserManager.UserId, false).Exists(
                si => si.SecurityItemKey.Equals(context, StringComparison.InvariantCultureIgnoreCase));

            if (!ret)
            {
                // check if this item matches a screen uri
                ret = LocalCacheManager.GetUserSecurityItemsCache(LocalUserManager.UserId, false).Exists(
                si => si.Uri.Equals(context, StringComparison.InvariantCultureIgnoreCase));
            }
        }

        return ret;

    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top