Friday, September 25, 2015

Simple CSS "Sticky Footer"

This method would probably need some tweaking to work in older browsers, but for my purposes, it works pretty good:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <style>
        html {
            height: 100%;
        }
        body {
            height: 100%;
            margin: 0px;
        }
        .container {
            background-color: lightgray;
            height: 100%;
        }
        .footer {
            height: 50px;
            background-color: gray;
            margin-top: -50px;
        }
    </style>
</head>
<body>
    <div class="container">
        
    </div>
    <div class="footer">

    </div>
</body>
</html>

Thursday, September 24, 2015

Git push and pull

As I've explained in earlier posts, the command

git push origin master

is used to push changes to a remote repository.  Similarly, the following command

git pull origin master

pulls changes from the remote repository and merges these changes in with your local branch.

Tuesday, September 15, 2015

Implementing Forgot Password and Email Confirmation in ASP.NET Identity

This is one of those tasks that is not very difficult to actually accomplish but exceedingly difficult to find good instructions on how to do it.  Forgot Password is simply a page where the user can enter his or her email address if he has forgotten his/her password.  He/she then receives an email containing a link where the password can be reset.  Email Confirmation means that when a user first registers, he/she cannot log in until the email is confirmed.  How is the email confirmed?  The person is sent an email containing a link to click that will mark the address as confirmed.  This all sounds complicated, but ASP.NET Identity builds all of this for you when you create an ASP.NET web application with Individual Accounts.  However, the code in the AccountController is commented out initially.  Because email is a central part of these operations, you must first configure your email.

To configure your email, just open up the IdentityConfig.vb file.  Find the EmailService class and add code to the SendAsync method.  The code below illustrates some code that sends an email using a traditional SMTP server:

Public Class EmailService
    Implements IIdentityMessageService

    Public Function SendAsync(message As IdentityMessage) As Task Implements IIdentityMessageService.SendAsync
        ' Plug in your email service here to send an email.

        Dim SmtpServer As String

        If System.Configuration.ConfigurationManager.AppSettings("SmtpServer") Is Nothing Then
            Throw New Exception("The SmtpServer key is not present in the web.config file.")
        Else
            SmtpServer = System.Configuration.ConfigurationManager.AppSettings("SmtpServer")
        End If

        Dim FromAddress As String

        If System.Configuration.ConfigurationManager.AppSettings("ConfirmationEmailFrom") Is Nothing Then
            Throw New Exception("The ConfirmationEmailFrom key is not present in the web.config file.")
        Else
            FromAddress = System.Configuration.ConfigurationManager.AppSettings("ConfirmationEmailFrom")
        End If

        Dim MyClient As New System.Net.Mail.SmtpClient(SmtpServer)

        Dim MyMessage As New System.Net.Mail.MailMessage

        MyMessage.From = New Net.Mail.MailAddress(FromAddress)

        MyMessage.Subject = message.Subject

        MyMessage.Body = message.Body
        MyMessage.IsBodyHtml = True

        MyMessage.To.Add(message.Destination)

        MyClient.Send(MyMessage)

        Return Task.FromResult(0)

    End Function

End Class

Once this is configured, go back to your AccountController and uncomment out the appropriate lines.

reCAPTCHA 2.0

The folks at Google have released a new version of their CAPTCHA product named reCAPTCHA.  They refer to the new version as the No CAPTCHA reCAPTCHA.  It's fairly easy to implement even in an ASP.NET MVC web application as I'll show below.

First, go here and sign up.  You input the domains at which your site will be hosted.  LocalHost always "just works" so don't worry about development.  However, if you're testing at a different domain than production, be sure to put both in there.  You'll be given two keys.  The "site" key and the "secret" key.

Implementing the CAPTCHA is a two part process.

First Part - Display the CAPTCHA control for the user to "solve"

Add this to the top of your view:

<script src="https://www.google.com/recaptcha/api.js" async defer></script>

Then add this where you want the CAPTCHA box to appear:

<div class="g-recaptcha" data-sitekey="SITE-KEY-GOES-HERE" ></div>

That's all.

Second Part - Verify that the CAPTCHA was solved correctly

This is the more tricky of the two parts.

Once the user submits the form, reCAPTCHA is going to insert an additional form field named g-recaptcha-response into the form.  This will look like gobbly-gook.  This response along with the secret key needs to be sent to a Google web service.  The web service will return a JSON object that contains a success attribute.  If the success attribute is true, the user solved the CAPTCHA and you can safely assume that he or she is not a bot.

First, in your controller, add a data contract that you can deserialize the JSON response into:

<System.Runtime.Serialization.DataContract>
Private Class GoogleResults
    <System.Runtime.Serialization.DataMember(Name:="success")>
    Public Success As Boolean
End Class

Next, in your post action method within the controller use the following code:

Dim RecaptchaResponse As String = Request("g-recaptcha-response")

Dim MyClient As System.Net.WebClient = New System.Net.WebClient

Dim Reply As String = _
            MyClient.DownloadString(String.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", "SECRET-KEY-GOES-HERE", RecaptchaResponse))

Dim MySerializer As New System.Runtime.Serialization.Json.DataContractJsonSerializer(GetType(GoogleResults))

Dim MyStream = New System.IO.MemoryStream(Encoding.Unicode.GetBytes(Reply))

Dim Results As GoogleResults = CType(MySerializer.ReadObject(MyStream), GoogleResults)

If Not Results.Success Then
    ModelState.AddModelError("", "You must verify that you are not a robot.")
End If

If Not ModelState.IsValid Then
    Return View(model)
End If


Friday, July 31, 2015

Close connections to your IndexedDB databases

Recently, I was attempting to run the deleteDatabase method of the IDBFactory interface of the IndexedDB API.  I noticed that occasionally the method would just hang there like it was stuck.  I slowly began to realize that it would hang if a connection was currently open.  This led me to researching how to close connections.

When you open a connection using the open method of IDBFactory, you get an object of type IDBDatabase.  In my experience, the only thing you do with the IDBDatabase object is to create the transaction object from it.  After you create the transaction, you should then immediately close the IDBDatabase object which closes the connection you opened.  At first, I wondered if you had to wait until you were done with the transaction before closing the connection, but apparently the close method knows to wait until all transactions created from it are completed.  This is from the specification:
Wait for all transactions created using connection to complete. Once they are complete, connection is closed.
Your code should look something like what is shown below. In this case, request is the IDBOpenDBRequest object.

request.onsuccess = function() {
    var db = event.target.result;

    var tx = db.transaction(['TableName'], 'readwrite');

    db.close();
}

Monday, July 27, 2015

Left Outer Join using LINQ

Left outer joins are another example of something that's seems intuitive in SQL but appears foreign in LINQ.  Here we have two tables, TableOne and TableTwo both with a common TableOneId column.  TableOne always has a record and TableTwo has 0 to many records for each TableOne record.  The first query will return every record from both tables even if the TableOne record has no TableTwo record.  The second query will only return records from TableOne only if they have no corresponding TableTwo record.  By the way, this is VB.


'Without a where clause

Dim Query = 
    From t1 In TableOnes
    Group Join t2 In TableTwos On t1.TableOneId Equals t2.TableOneId Into gj = Group
    From grouping In gj.DefaultIfEmpty
    Select t1, grouping


'With a where clause

Dim Query = 
    From t1 In TableOnes
    Group Join t2 In TableTwos On t1.RecordId Equals t2.RecordId Into gj = Group
    From grouping In gj.DefaultIfEmpty
    Where grouping Is Nothing
    Select t1, grouping
 

Thursday, July 23, 2015

Security Auditing in WCF

It is possible to log all security successes and/or failures to the event log by just modifying your configuration file.  This can be a quick and easy way to see if any funny business is going on with your web service.  However, a better solution is to log these types of events to a database that is easier to check and query on if you're doing this on a regular basis.

I'm a fan of the Service Configuration Editor tool (In Visual Studio, right click the web.config and select Edit WCF Configuration) rather than changing the XML directly, but it's helpful to see both.

First add a Service Behavior Configuration.  It doesn't necessarily have to be named.  Then add the serviceSecurityAudit behavior to the configuration:


Now, expand the behavior configuration, and select the newly added serviceSecurityAudit:


I recommend choosing the "Application" log as the location.  Here, I have chosen to log both successes and failures at the message level.  Once this is set up, simply go to the Event Viewer and you'll see information entries for each authentication or rejection.  To turn it off, just set it to None and leave it in the web.config in case you want to turn it on again.

Here is the settings as they exist in the XML:

<behaviors>
  <servicebehaviors>
    <behavior name="">

...

      <servicesecurityaudit auditloglocation="Application" 
          messageauthenticationauditlevel="SuccessOrFailure" 
          serviceauthorizationauditlevel="None">
      </servicesecurityaudit>
    </behavior>
  </servicebehaviors>
</behaviors>