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