StorageCache usage and best practices

Mar 28, 2010 at 12:47 AM

I am using the CheckAccessHelper generated by your library.

I would like to use StorageCache.  It would be nice if the CheckAccessHelper had a provider model so we could continue to use the same code, just change the provider to regular storage, WCF, storagecache, etc.   The 'CheckAccess' set of methods (and whatever else is common) could be in their own interface to allow this?  Or in the worst case, generate a CheckAccessHelper for each type.

 

Questions:

1)  The StorageCache does not appear to have a GetDbUser() method?

2)  For #1 are we supposed to call storageCacheObject.storage.GetDBUser()?  I am guessing the call to GetDBUser() is not cached?

3)  For #2 if that is all correct, we should cache the GetDBUser() result in session and use that in StorageCache calls?

4)  How does permissions cache fit into this scenario?  PermissionsCache is just one users permission and I could cache that instead of using StorageCache?  The timing graphs you show indicate it takes over 6x as long to build a PermissionCache as it does the StorageCache?  How can that be - permission cache is just for one user and storage cache is for all users?

5)  How does your asp.net RoleProvider fit into all of this?  If I have a StorageCache already loaded, would like RoleProvider to use it?  I guess this would be possible if there were common interfaces between the different storage providers you offer?  Looking through your code, it appears you cache much of this in the IAzManApplication?  Seems a bit redundant with having a StorageCache object (may have been written before the StorageCache even existed?) - plus there is no way to 'reset' the cache? 

6)  Seems to me we could just let the roleprovider could provide everything we need?  Put a 'checkaccess' method on it?  This way the provider could take care of all the caching and storage and everything (it is already doing this anyway)?  One could then do a ((NetSqlAzManRoleProvider).Roles).CheckAccess(role/task/operation, attributes[], context[]).   The provider already knows application/store/etc.

 

I am just trying to figure out what best practices are on this.  Thanks for your product.

Coordinator
Mar 30, 2010 at 8:08 AM

Hi,

1) StorageCache is designed only to get Authorized Item for a given User; different user … different StorageCache, so GetDbUsers() or GetWindowsUsers() has not sense.

2) Same as 1)

3) Same as 1)

4) PermissionCache is an earlier version of StorageCache and it can store authorized item of a given user but only for a given Application. StorageCache instead can be used for all Stores and all Applications.

5) NetSqlAzManRoleProvider is Role Provider for ASP.NET and cannot be used with StorageCache and/or PermissionCache. Just different purpose.

6) Yes you can, but CheckAccess method is not part of the IRoleProvider interface … so same as 5) … it is intended for a different purpose.

Thanks you too.

Regards,

Andrea.

__________________________________
Andrea Ferendeles
NetSqlAzMan Project Coordinator  
E-mail aferende@hotmail.com Web http://netsqlazman.codeplex.com

Apr 2, 2010 at 5:44 AM

1)  StorageCache appears to get the rights for ANY given user.  So, imagine I want to get the rights for a user.  I have to construct the StorageCache (if it does not exist).  I then have to construct a IAzManStorage so I can call GetDbUser() on it - just to pass into StorageCache.   That means it would be _nice_ if storagecache either cached user/ssid mappings OR just let me call getdbuser() - which could just be a passthrough to a created IAzManStorage.   Or...am I missing something?

4)  Again, I will ask the same question.  If PermissionCache is just for one user/one application/one store - why would your docs say it takes 6x longer to build it (which is a subset of storagecache) than the full storage cache?   If that is true, why would you EVER use permissioncache?

5)  RoleProvider's purpose it to provide role management AND authentication.  Most of the calls into role provider are for role authentication.  It would therefore make sense if it could use a storagecache to make these operations fast.  In my typical asp.net request there are 5+ calls into IsInRole().  ???

6)  I know that is not part of IRoleProvider interface - neither are the public properties you expose of Storage, Store or Application.  So, I still think it would be useful to provide checkaccess on that provider and then access those directly.   Why must I create an instance of CheckAccessHelper() or create storage, worry with connection strings, application, store, etc - when the role provider already has all that set up!  Are you just in disagreement with the suggestions I make or you just do not want to enhance the product any further?  I am a bit confused.

Thanks for the foundations of a great product - we can make it better though....

Apr 9, 2010 at 3:36 PM

Hey Wayne,

1) You can call the GetDBUser w/o explicitly constructing an IAzManStorage object as follows: StorageCache.Storage.GetDBUser(). I would however prefer if the user/SSID-s were cached...

 

Zen

 

Apr 19, 2010 at 1:38 AM

UncleZen,

   I have created a simple wrapper around the GetDBUser that caches them.   Really belong directly in the library and could really be static methods.   I currently have these in my RoleProvider so that lookups do not have to happen for every web request.  

   Andrea - interested in making this part of the application or store objects (note data context on each request -so this is thread safe)?

 

Here is the main code:

        Dictionary<string, byte[]> dbUsers = new Dictionary<string, byte[]>();
        private IAzManDBUser GetCachedDbUser(string userName)
        {
            byte[] sid;
            if (dbUsers.TryGetValue(userName, out sid))
            {
                return new SqlAzManDBUser(new SqlAzManSID(sid, true), userName);
            }
            IAzManDBUser result = InternalGetDbUserFromDatabase(null, userName);
            lock (this)
            {
                dbUsers.Add(userName, result.CustomSid.BinaryValue);
            }

            return result;
        }

        private IAzManDBUser GetCachedDbUser(IAzManSid customSid)
        {
            string userName;
            if (TryGetKey(customSid.BinaryValue, out userName))
            {
                return new SqlAzManDBUser(customSid, userName);
            }
            IAzManDBUser result = InternalGetDbUserFromDatabase(customSid, null);
            lock (this)
            {
                dbUsers.Add(userName, result.CustomSid.BinaryValue);
            }

            return result;
        }

        private bool TryGetKey(byte[] value, out string key)
        {
            foreach (var entry in dbUsers)
            {

                if (!entry.Value.Equals(value))
                {
                    continue;
                }

                key = entry.Key;
                return true;

            }

            key = string.Empty;
            return false;
        }


        private IAzManDBUser InternalGetDbUserFromDatabase(IAzManSid customSid, string userName)
        {
            NetSqlAzManStorageDataContext dataContext = new NetSqlAzManStorageDataContext(this.connectionStringName);
            IQueryable<GetDBUsersResult> dtDBUsers;
            if (customSid != null)
                dtDBUsers = dataContext.GetDBUsers(this.storeName, this.ApplicationName, customSid.BinaryValue, null);
            else
                dtDBUsers = dataContext.GetDBUsers(this.storeName, this.ApplicationName, null, userName);

            IAzManDBUser result;
            if (dtDBUsers.Count() == 0)
            {
                throw SqlAzManException.DBUserNotFoundException(customSid.StringValue, null);
            }
            else
            {
                result = new SqlAzManDBUser(new SqlAzManSID(dtDBUsers.First().DBUserSid.ToArray(), true), dtDBUsers.First().DBUserName);
            }
            return result;
        }

        private IAzManDBUser GetDBUser(IAzManSid customSid)
        {
            return GetCachedDbUser(customSid);
        }
        /// <summary>
        /// Finds the DB user.
        /// </summary>
        /// <param name="userName">The custom sid.</param>
        /// <returns></returns>
        private IAzManDBUser GetDBUser(string userName)
        {
            return GetCachedDbUser(userName);
        }

 

 

May 6, 2010 at 1:31 PM

Thanks Wayne!!! I'll be using it in my code.

 

Zen