Wednesday, October 1, 2014

.NET MVC style bundle gotchas

I encountered a couple of .NET MVC bundle problems when trying to load css resources for the jHtmlArea jQuery HTML Editor.

Problem #1:  403 – Forbidden: Access is denied.

This was my first attempt to bundle the jHtmlArea style resources.  The 403 Forbidden occurred because my bundle name matched the actual folder name, so IIS was attempting to handle the request as explained in this StackOverflow post.

1 bundles.Add(new StyleBundle("~/Content/jHtmlArea").Include(
2 "~/Content/jHtmlArea/jHtmlArea.css",
3 "~/Content/jHtmlArea/jHtmlArea.Editor.css",
4 "~/Content/jHtmlArea/jHtmlArea.ColorPickerMenu.css"));

Problem #2:  Resources not found.

This was my second attempt to bundle the jHtmlArea style resources.  This time, my resources such as jHtmlArea.png (located in \Content\jHtmlArea) could not be found because the bundle attempted to load those resources from \Content.

1 bundles.Add(new StyleBundle("~/Content/jHtmlAreacss").Include(
2 "~/Content/jHtmlArea/jHtmlArea.css",
3 "~/Content/jHtmlArea/jHtmlArea.Editor.css",
4 "~/Content/jHtmlArea/jHtmlArea.ColorPickerMenu.css"));

Solution

This version solves both problems.  Apparently the root path for the bundle must match the actual root path of the resources.

1 bundles.Add(new StyleBundle("~/Content/jHtmlArea/jHtmlAreacss").Include(
2 "~/Content/jHtmlArea/jHtmlArea.css",
3 "~/Content/jHtmlArea/jHtmlArea.Editor.css",
4 "~/Content/jHtmlArea/jHtmlArea.ColorPickerMenu.css"));

Wednesday, August 20, 2014

Update ModelState for an individual Property in .NET MVC

As has been noted many times before, on postback MVC Helpers render the value stored in ModelState, not the values on the model.  Simon Ince describes this feature very well and recommends some solutions.

I have created a set of extension methods to allow easy update of an individual property value for postback if you find yourself in the need for this scenario.  These methods encapsulate the ModelState.Remove method but improve the use of this method by not requiring any magic string property names.

Here are the extension methods:

1 /// <summary>
2 /// Get the string equivalent of an instance property name using Reflection.
3 /// </summary>
4 /// <typeparam name="T">Any class type.</typeparam>
5 /// <typeparam name="TReturn">Any Property type for which a string translation is needed.</typeparam>
6 /// <param name="obj">A class instance.</param>
7 /// <param name="expression">Lambda expression describing the Property for which a string translation is needed.</param>
8 /// <returns>A string translation of a class Property member.</returns>
9 /// <remarks>http://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/</remarks>
10 public static string GetPropertyName<T, TReturn>(this T obj, Expression<Func<T, TReturn>> expression)
11 where T : class
12 {
13 MemberExpression body = (MemberExpression)expression.Body;
14 return body.Member.Name;
15 }
16
17 /// <summary>
18 /// Set the value of a Public property specified by an input string.
19 /// </summary>
20 /// <param name="input">Any object instance.</param>
21 /// <param name="propertyName">Name of a Public property for which the value is set.</param>
22 /// <param name="value">Object value to set.</param>
23 public static void SetValueByPropertyName(this object input, string propertyName, object value)
24 {
25 PropertyInfo prop = input.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
26 prop.SetValue(input, value);
27 }
28
29 /// <summary>
30 /// Updates the value of an individual property and resets the ModelState.
31 /// </summary>
32 /// <typeparam name="T">Any class type.</typeparam>
33 /// <typeparam name="TReturn"></typeparam>
34 /// <typeparam name="TReturn">Any Property type for which a string translation is needed.</typeparam>
35 /// <param name="model">A model class instance.</param>
36 /// <param name="expression">Lambda expression describing the Property for which a string translation is needed.</param>
37 /// <param name="updateValue">The new value to set on the model.</param>
38 public static void Update<T, TReturn>(this System.Web.Mvc.ModelStateDictionary modelState, T model, Expression<Func<T, TReturn>> expression, object updateValue)
39 where T : class
40 {
41 string propName = model.GetPropertyName(expression);
42 modelState.Remove(propName);
43 model.SetValueByPropertyName(propName, updateValue);
44 }
45

And here is an example implementation:


1 ModelState.Update(model, m => m.IsInitialPost, false);

Thursday, June 19, 2014

Utility for two-factor verification with Google Authenticator

There are a lot of code samples out there for this particular task.  However I had trouble finding a good one that showed how to authenticate against several time intervals, for better user experience, so here is a code sample.

Some references:

Comments:

  • This class uses the Singleton Pattern, which I prefer over static methods because the singleton can be mocked more easily.
  • The GenerateSecretKey method creates an encrypted key to store with an individual user (e.g., using a custom SimpleMembership configuration)
  • The IsTwoFactorVerificationCodeValid method called by an external class such as a custom validator
  • The GetCurrentCountersWithBeforeAndAfterIntervals method is what calculates the previous, current, and future intervals

Code sample:

1 using System;
2 using System.Linq;
3 using System.Collections.Generic;
4 using System.Security.Cryptography;
5 using System.Text;
6
7 namespace MyNamespace
8 {
9 public class GoogleAuthenticatorUtility
10 {
11 private const int NUMBER_OF_DIGITS = 6;
12 private const int NUMBER_OF_SECONDS_IN_INTERVAL = 30;
13
14 private readonly DateTime _unixEpoch;
15
16 private GoogleAuthenticatorUtility()
17 {
18 _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
19 }
20
21 private static GoogleAuthenticatorUtility _instance;
22 public static GoogleAuthenticatorUtility Instance
23 {
24 get { _instance = _instance ?? new GoogleAuthenticatorUtility(); return _instance; }
25 set { _instance = value; }
26 }
27
28
29 public virtual bool IsTwoFactorVerificationCodeValid(string userSecret, string verificationCode)
30 {
31 int[] passwords = GenerateTimeBasedPasswords(userSecret);
32 string[] allPasswords = GeneratePasswords(passwords);
33 return allPasswords.Contains(verificationCode);
34 }
35
36 public virtual string GenerateSecretKey()
37 {
38 byte[] buffer = new byte[9];
39
40 using (RandomNumberGenerator rng = RNGCryptoServiceProvider.Create())
41 {
42 rng.GetBytes(buffer);
43 }
44
45 return Convert.ToBase64String(buffer)
46 .Substring(0, 10)
47 .Replace('/', '0')
48 .Replace('+', '1');
49 }
50
51
52 private int[] GenerateTimeBasedPasswords(string secret)
53 {
54 if (string.IsNullOrEmpty(secret))
55 {
56 throw new ArgumentException("Secret must not be null or empty", "secret");
57 }
58
59 long[] counters = GetCurrentCountersWithBeforeAndAfterIntervals();
60 int[] passwords = new int[counters.Length];
61
62 for (int i = 0; i < counters.Length; i++)
63 {
64 byte[] counter = BitConverter.GetBytes(counters[i]);
65
66 if (BitConverter.IsLittleEndian)
67 {
68 Array.Reverse(counter);
69 }
70
71 byte[] key = Encoding.ASCII.GetBytes(secret);
72
73 HMACSHA1 hmac = new HMACSHA1(key, true);
74
75 byte[] hash = hmac.ComputeHash(counter);
76
77 int offset = hash[hash.Length - 1] & 0xf;
78
79 int binary =
80 ((hash[offset] & 0x7f) << 24)
81 | ((hash[offset + 1] & 0xff) << 16)
82 | ((hash[offset + 2] & 0xff) << 8)
83 | (hash[offset + 3] & 0xff);
84
85 passwords[i] = binary % (int)Math.Pow(10, NUMBER_OF_DIGITS); // 6 digits
86 }
87
88 return passwords;
89 }
90
91 private long[] GetCurrentCountersWithBeforeAndAfterIntervals()
92 {
93 var counters = new long[3];
94 double totalSeconds = (DateTime.UtcNow - _unixEpoch).TotalSeconds;
95
96 counters[0] = (long)((totalSeconds - NUMBER_OF_SECONDS_IN_INTERVAL) / NUMBER_OF_SECONDS_IN_INTERVAL);
97 counters[1] = (long)(totalSeconds / NUMBER_OF_SECONDS_IN_INTERVAL);
98 counters[2] = (long)((totalSeconds + NUMBER_OF_SECONDS_IN_INTERVAL) / NUMBER_OF_SECONDS_IN_INTERVAL);
99
100 return counters;
101 }
102
103 private string[] GeneratePasswords(int[] timeBasedPasswords)
104 {
105 var passwords = new string[timeBasedPasswords.Length];
106 for (int i = 0; i < timeBasedPasswords.Length; i++)
107 {
108 passwords[i] = timeBasedPasswords[i].ToString(new string('0', NUMBER_OF_DIGITS));
109 }
110
111 return passwords;
112 }
113 }
114 }
115

Friday, June 13, 2014

Resolve DNN error “Violation of UNIQUE KEY constraint 'IX_PortalAlias'”

Encountered this error message after installing a DNN site in my local IIS and then browsing to my site:  “Violation of UNIQUE KEY constraint 'IX_PortalAlias'. Cannot insert duplicate key in object 'dbo.PortalAlias'. The duplicate key value is (default.aspx)”.

I resolved the problem by deleting all existing rows from the table dbo.PortalAlias.

Tuesday, June 10, 2014

Resolve ASP.NET MVC 4 Ajax.BeginForm full postback

I recently spent several hours trying to resolve a problem where my view using Ajax.BeginForm kept posting back a full view instead of inserting the results of an AJAX partial postback into my existing view.  I searched for hours and could not find what was wrong:

web.config appSettings were correct:

<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />


Microsoft jQuery Unobtrusive Ajax NuGet package installed and bundled:


1 bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
2 "~/Scripts/jquery.unobtrusive*",
3 "~/Scripts/jquery.validate*"));
4

Simple test controller:


1 public class TestAjaxController : Controller
2 {
3 public ActionResult TestAjaxIndex()
4 {
5 return View();
6 }
7
8 [HttpPost]
9 public PartialViewResult GetTestAjax()
10 {
11 return PartialView("_GetTestAjax");
12 }
13 }


View (TestAjaxIndex.cshtml) created:


1 @{
2 ViewBag.Title = "TestAjaxIndex";
3 }
4
5 <h2>TEST AJAX</h2>
6
7 <div id="testAjax"></div>
8
9 @using (Ajax.BeginForm("GetTestAjax", new AjaxOptions() {
10 UpdateTargetId = "testAjax", InsertionMode = InsertionMode.Replace }))
11 {
12 <input type="submit" value="Test" />
13 }
14
15 @Scripts.Render("~/bundles/jqueryval")

Partial view (_GetTestAjax.cshtml) created:


1 <span>GOT IT</span>

Whenever I submitted \TestAjax\TestAjaxIndex it would do a full postback to \TestAjax\GetTestAjax and show my “GOT IT” span, rather than inserting my “GOT IT” span into my “testAjax” div.


It turns out the problem is that I have a _Layout.cshtml view which renders my TestAjaxIndex view inside the body using @RenderBody(), and I neglected to render my “~/bundles/jqueryval” script bundle inside of a Scripts section.  Therefore my jquery.unobtrusive-ajax.js resource was loaded AFTER my jquery-1.10.2.js resource.


Here is the corrected version of my TestAjaxIndex.cshtml which solved my problem:


1 @{
2 ViewBag.Title = "TestAjaxIndex";
3 }
4
5 <h2>TEST AJAX</h2>
6
7 <div id="testAjax"></div>
8
9 @using (Ajax.BeginForm("GetTestAjax", new AjaxOptions() {
10 UpdateTargetId = "testAjax", InsertionMode = InsertionMode.Replace }))
11 {
12 <input type="submit" value="Test" />
13 }
14
15 @*use a Scripts section to fix the problem*@
16 @section Scripts {
17 @Scripts.Render("~/bundles/jqueryval")
18 }

Thursday, January 16, 2014

Avoiding merge conflicts with git tf

Recently I began using git tf to interface with a Microsoft TFS repository for a client.  I quickly began to encounter problems when trying to merge TFS changesets into my local repository when issuing the git tf pull --rebase command.  Sometimes the issues would be simple conflicts, but other times I would encounter bizarre problems that I could not easily resolve.

My solution was to adopt a modified workflow using a local branch, e.g., “Working”.  I perform all of my code changes on the Working branch and use master only for TFS interactions.  Here is a typical workflow:

  1. Start my day
  2. git checkout master
  3. git tf pull --rebase
  4. git checkout Working
  5. git merge master -m “Merged latest changes from TFS.”
  6. Make changes to the code in my local git repository
  7. git commit -a -m “commit local into Working branch”
  8. Merge my local Working changes into master
  9. git checkout master
  10. git tf pull --rebase
  11. git merge Working --no-commit
  12. Fix any merge conflicts and then check updated code in to TFS
  13. git tf checkin
  14. git checkout Working
  15. Resume local code changes

I have not had any more bizarre, unsolvable problems since adopting this new workflow.

Saturday, December 7, 2013

Synchronize Kindle metadata between 2 Macs for 3rd party books

I try hard to stick to e-books for my developer resources for the obvious benefits of search and electronic notes/bookmarks.  I have several Macs and I use Dropbox to sync content and 3rd party e-books but I couldn't figure out how to sync my Kindle metadata for those 3rd party books.  Today I finally figured out a solution that works for Macs.

  1. Created a Kindle_3rd_Party_Content folder within my Dropbox folder.
  2. Moved all 3rd party .mobi and .mbp files to that folder.
  3. Created symbolic links to the 3rd party .mobi and .mbp files within my Kindle application's Content Folder on one of my Macs
  4. Duplicated step 3 on my other Macs

Now when I open my Kindle application on any of my Macs, Kindle recognizes the symbolic links as being within my Downloaded Items, and when I add notes/bookmarks or change my location, my changes are saved via the symbolic link to the original .mbp file stored in Dropbox.

The key to this solution is to use symbolic links instead of aliases, because the Kindle application does not recognize aliases.

The Dropbox Wiki provided some great suggestions on how to sync folders outside of the Dropbox folder.  Tom Nelson gave a nice explanation of the differences between aliases and symbolic links.