Friday, July 18, 2014

concatenating URL variables in jQuery's .ajax and keeping user identities in C#'s cache

Here, I flirt with the idea of making a user's credentials accessible to both MVC Controller and ApiController types in an ASP.NET MVC Web API application while using a session-specific magic string to differentiate users. I don't know yet if this is a bad idea or not. Please feel free to put your hand up if this sucks. Anyhow, I have something minimalistic afloat. I start off by just making the default ASP.NET MVC Web API application which Visual Studio 2013 makes. I then dress up the Index.cshtml view as suggested here, however, some of what I originally had in Index.cshtml is bad and, moreover, it would need to be changed up further even if it wasn't flawed to begin with. I better version of Index.cshtml is:

<button id="touchme">touch me</button>
<div id="response"></div>
@section scripts
{
   <script language="javascript">
      $('#touchme').bind('click', function () {
         $.ajax({
            type: "POST",
            url: "/api/values?magicstring=@ViewBag.SessionKeyForIdentity",
            dataType: "json",
            success: function (result) {
               $("#response").html(result);
            }
         });
      });
   </script>
}

 
 

The "url" value in the .ajax implementation has a variable appended to it named magicstring which will populate the magicstring variable here:

using System;
using System.Security.Principal;
using System.Threading;
using System.Web.Http;
using Security.Utilities;
namespace Security.Controllers
{
   public class ValuesController : ApiController
   {
      public string Post(string magicstring)
      {
         try
         {
            IdentityUtility.RefreshIdentity(magicstring);
            IPrincipal principal = Thread.CurrentPrincipal;
            if (String.IsNullOrEmpty(principal.Identity.Name))
            {
               return "The current user is anonymous.";
            } else {
               return "The current user is: " + principal.Identity.Name + "!";
            }
         }
         catch(Exception exception)
         {
            return exception.Message;
         }
      }
   }
}

 
 

If the method signature were to read (string magicstring, string othermagicstring) then the .ajax implementation could feed in the two variables like this:

url: "/api/values?magicstring=@ViewBag.SessionKeyForIdentity&othermagicstring=foo",

 
 

If you are wondering what @ViewBag.SessionKeyForIdentity is, here is my other controller:

using System;
using System.Security.Principal;
using System.Web.Mvc;
using Security.Utilities;
namespace Security.Controllers
{
   public class HomeController : Controller
   {
      public const string _sessionKeyForIdentity = "sessionkeyforidentity";
      
      public ActionResult Index(string id)
      {
         if (Session[_sessionKeyForIdentity] == null)
         {
            string randomString = RandomStringUtility.GetRandomString();
            Session[_sessionKeyForIdentity] = randomString;
         }
         ViewBag.SessionKeyForIdentity = Session[_sessionKeyForIdentity] as String;
         if (id != null)
         {
            GenericIdentity genericIdentity = new GenericIdentity(id);
            GenericPrincipal genericPrincipal = new GenericPrincipal(genericIdentity, new
                  string[] { "User" });
            IdentityUtility.SetIdentity(genericPrincipal, ViewBag.SessionKeyForIdentity);
         }
         return View();
      }
   }
}

 
 

The above allows for corny mechanics for either setting the user's identity or not doing so. Do you see how the two controllers now share a session-specific magic string? I have three static utilities to round out my code. You really only need to care about the first one though. Here it is:

using System;
using System.Runtime.Caching;
using System.Security.Principal;
using System.Threading;
namespace Security.Utilities
{
   public static class IdentityUtility
   {
      private static ObjectCache _objectCache{ get { return MemoryCache.Default; } }
      
      public static void RefreshIdentity(string sessionKeyForIdentity)
      {
         if (_objectCache[sessionKeyForIdentity] != null)
         {
            IPrincipal principal = _objectCache[sessionKeyForIdentity] as IPrincipal;
            Thread.CurrentPrincipal = principal;
         }
      }
      
      public static void SetIdentity(IPrincipal principal, string sessionKeyForIdentity)
      {
         if (_objectCache[sessionKeyForIdentity] != null)
         {
            _objectCache.Remove(sessionKeyForIdentity);
         }
         CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
         DateTime twentyMinutesIntoTheFuture = TimeUtility.GetTime().AddMinutes(20);
         cacheItemPolicy.AbsoluteExpiration = twentyMinutesIntoTheFuture;
         _objectCache.Add(new CacheItem(sessionKeyForIdentity, principal),
               cacheItemPolicy);
         Thread.CurrentPrincipal = principal;
      }
   }
}

 
 

Another, less exotic utility follows. This is not how I really recommending walling of the external dependency of timekeeping, but at least I have it marginally isolated.

using System;
namespace Security.Utilities
{
   public static class TimeUtility
   {
      public static DateTime GetTime()
      {
         return DateTime.UtcNow;
      }
   }
}

 
 

Here is my last utility. This is also goofy like the file before it. These last two files aren't really what I was trying to show off in this blog posting.

using System;
namespace Security.Utilities
{
   public static class RandomStringUtility
   {
      public static string GetRandomString()
      {
         return Guid.NewGuid().ToString().Replace("-", "") +
               Guid.NewGuid().ToString().Replace("-", "");
      }
   }
}

No comments:

Post a Comment