Thursday, November 29, 2012

Client and Server Side Form Validation Pt. 3

So, with a nice looking form, and some decent JavaScript processing my form works very nicely. However, I need to make sure that the server receives good data even if the JavaScript was turned off or not in play. All that form niceness is to help the user, now it is time to help the server.

Currently, my thinking on this isn't very solid. I'm asking the server to do a lot of work to double check the form and then determining if it should pass on the information to be processed or if the form needs to be displayed again.

This is not too bad if the form is small and has a predetermined set of fields. However, many of these forms are dynamically generated, so the processing has to be dynamically processed as well. This is where I try to take advantage of frameworks and partials to keep my display code in modular chunks- breaking out the form from the rest of the page.

Basically, I try to cfparam all the form fields and then validate the results and report them with the form. So if JavaScript does its job on the original form, great. If not, it still is taken care of before it hits the server. Regardless of the amount of work the server or I have to do, it is probably for the best.

Thursday, November 8, 2012

Client and Server Side Form Validation Pt. 2

So continuing from oh, so long ago. I have moved from the previous basic form and posting and blended in Bootstrap, jQuery.Validation and a lot of Googling/research.

Styling up a nice, easy to read and flow form with Bootstrap's form code is straight forward and pleasant. Even if radio button groups can be tricky.

Next, I needed to bring in some jQuery validation. Pretty much the go to is the jQuery Validation plug-in from bassistance.de. It seems to have the most work and there were many tutorials and references in the Bootstrap issues discussion about getting it to work.

My starting point to getting from Ben Nadel's basic form upgraded is this little code sample called jQuery Validate Demo from aLittleCode.com. This will punch up your forms nicely. But the forms I work with extensively use radio groups (evaluation ratings of 1-5, sometimes with N/A and some need to be required).

From here it takes a little fixing and JavaScript to include radio buttons to put the error message in a decent place. I got my notes from these two posts: Mihir Chitnis and this discussion (starting with the last half of kwilliams's post) on the Bootstrap issues.

I'd like to see Bootstrap fold in jQuery Validation to their JavaScript library or for a solid plug in to be developed. But with some tweaking I think I got a code base to work for my code set up.

Lets see if I can find some place to put code...


Form HTML
<form action="act_form.cfm" method="post" class="form-horizontal well" id="contact-form">
<fieldset>
<legend>A Form</legend>
<div class="control-group">
<label for="name" class="control-label">Name:</label>
<div class="controls"><input type="text" name="name" id="name" class="required" minlength="2" /><span class="help-inline"></span></div>
</div>
<div class="control-group">
<label for="email" class="control-label">Email:</label>
<div class="controls"><input type="text" name="email" id="email" class="required email" minlength="2" /><span class="help-inline"></span></div>
</div>
<div class="control-group">
<label for="birthday" class="control-label">Birthday:</label>
<div class="controls"><input type="text" name="birthday" id="birthday" class="required date" /><span class="help-inline"></span></div>
</div>
<div class="control-group">
<div class="controls">
<label class="radio">
<input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" class="required" messages="Please select one.">Option one is this and that-be sure to include why it's great
</label>
<label class="radio">
<input type="radio" name="optionsRadios" id="optionsRadios2" value="option2">Option two can be something else and selecting it will deselect option one
</label>
</div>
</div>
<div class="form-actions">
<p id="errors">
<!-- Errors go here. -->
</p>
<input type="submit" value="Submit" class="submit btn btn-primary" />
</div>
</fieldset>
</form>
view raw gistfile1.html hosted with ❤ by GitHub
Form JavaScript
<script src="/_includes/jquery/jquery-1.8.2.min.js"></script>
<script src="js/jquery.validate.js"></script>
<!-- <script src="js/bootstrap.js"></script> -->
<script type="text/javascript">
$(document).ready(function(){
$("#contact-form").validate({
messages: {
optionsRadios: "Please select one of the options."
},
errorPlacement: function(error, element) {
var isInputAppend = ($(element).parent('div.input-append').length > 0);
var isRadio = ($(element).attr('type') == 'radio');
if(isRadio){
afterElement = $(element).closest('div.controls').children("label").filter(":last");
error.insertAfter(afterElement);
}else if (isInputAppend) {
appendElement = $(element).next('span.add-on');
error.insertAfter(appendElement);
}else {
error.insertAfter(element);
}
},
showErrors: function(errorMap, errorList) {
$("#errors").html("Your form contains " + this.numberOfInvalids() + " errors, see details above.");
this.defaultShowErrors();
},
highlight: function(label) {
$(label).closest('.control-group').addClass('error');
},
success: function(label) {
label
.html(' <i class="icon-ok"></i>').addClass('valid')
.closest('.control-group').addClass('success');
},
errorElement: 'span'
});
}); // end document.ready
</script>
view raw gistfile1.html hosted with ❤ by GitHub

Thursday, August 2, 2012

Client and Server Side Form Validation


Something that has bothered me for a while is how to combine Client and Server Side form validation as painlessly as possible (as a developer) while still maintaining a pleasant user experience. Its not just about being able to fall back when Javascript is not available, but having secure input to the database. I like to consider the Ajax-y/jQuery to be mostly in service of making data entry more efficient.

To that end here is my form design thoughts.

  1. Writing clear and concise labels and instructions (too many instructions means that my form has business logic problems)
  2. Scripting input events to alert the operator to problems as they enter data and as they submit
  3. Checking and sanitizing the data on the server before making changes to the database
    • If the form is deemed valid, proceed to processing
    • If there are problems, re-post the form and repopulate the data with error messages
From the reading I've done, there will always be some amount of duplication of code. This is the nature of web applications. This can be mitigated by having reusable code, both as JavaScript libraries and having modular server side code. How to do this will be the subject of this experiment.

My starting point is the article by Ben Nadel from December 2011 called How Client-Side Validation Is Changing The Shape Of Server-Side Validation. Part of the issues will be integrating the code into our style of FuseBox, finding a decent jQuery plug-in, I may have to buckle down and figure out how CFCs really work, figure out how to use Ajax and standard posts with the same code (probably related to CFCs) and lastly how to deal with dynamically generated forms.

Tall order. I should have done this years ago.

Thursday, July 26, 2012

Temporary Break

So, I've hit sort of a slow down here. A few things will keep me away from posting on a schedule.

  1. Seasonal family activities
  2. Seasonal work load
  3. Temporary hold on the Expired Passwords script
This time of year is very busy with family, (uninteresting) work back-log, and deployment of my script is on hold until various stakeholders can get together to talk about deployment.

I'm not ready for this to become a link-blog, so I'll return with new code when I get a chance to explore new scripts and ideas- hopefully mid August.

Friday, July 6, 2012

Put it All Together

After beating myself up over the last post, I rolled up my sleeves and got working logic code and it is sending me notifications of who's account password is expiring. I'm still having trouble with getting useable information out of the LDAP hash. I'm probably doing it wrong, so I've asked for a code review and maybe we'll get to the bottom of how Ruby is doing this. I keep getting values in the format of:
["gcolasurdo"] in stead of just gcolasurdo
when I try to get the cn, mail or other LDAP key field.

First, an outline of my code's logic (based on the previous ColdFusion script):
  1. Loop over the relevant OUs (organization units in the LDAP)
  2. Query the LDAP
  3. If there is an expiration date and mail then...
  4. Find the expiration date and calculate the difference from today (script will run daily)
  5. If the daydiff is on a warning interval...
  6. If the OU is for the College of Nursing or everyone else, prepare personalized email text
  7. Then send an email with the details of the impending password expiration
Two questions that came up from the CF code. Should I:
  1. Set a timeout at some point so as to not choke the mail relay
  2. Log the results
One independent question I had:
  1. Should we do anything about already expired passwords (I'm sure the business reason was already set, but as the programmer, I kinda want to make sure)
This is all working locally, so we will need to start in on the deployment process.

Ok, here is the code I'm using:

# Basic init of arrays
# Systems and Administration will have to make sure the base
# organizations are accurate and up to date.
base_list = [
"ou=Employee,ou=SOM,o=hsc",
"ou=Employee,ou=Library,o=hsc",
"ou=Affiliate,ou=Library,o=hsc",
"ou=Employee,ou=COP,o=hsc",
"ou=Employee,ou=CRTC,ou=SOM,o=hsc",
"ou=Employee,ou=Administration,o=hsc",
"ou=Employee,ou=CON,o=hsc",
"ou=Employee,ou=Services,o=hsc",
"ou=Student,ou=COP,o=hsc",
"ou=Student,ou=SOM,o=hsc",
"ou=Student,ou=CON,o=hsc",
"ou=CON,ou=Trans,o=HSC",
"ou=HSLIC,ou=Trans,o=HSC"
]
# Days until expiration to warn on
warning_days = [30,14,7,3,2,1]
# CON has its own email text to send, use this array to find them
con_mail_exception = [
"ou=Employee,ou=CON,o=hsc",
"ou=Student,ou=CON,o=hsc"
]
# Actual look up and email code!
# Start main search by going through each organizational group
base_list.each do |area|
puts "Searching #{area}"
conn = LDAP::SSLConn.new(host, port)
conn.bind(username, password)
conn.perror("bind")
begin
conn.search(area, scope, filter, attrs) { |entry|
if entry.vals('passwordExpirationTime').nil? || entry.vals('mail').nil?
#puts "none"
else
pswrd_expire = entry.to_hash()
# Today's date and the entry's expiaration date split from string to date
today = Time.now
expire_date = Time.new(
pswrd_expire['passwordExpirationTime'].to_s.slice(2,4),
pswrd_expire['passwordExpirationTime'].to_s.slice(6,2),
pswrd_expire['passwordExpirationTime'].to_s.slice(8,2)
)
# Calculate the remaining days diff/24 hours/60 minues/60 seconds + 1 day
days_remaining = (((expire_date - today)/24/60/60)+1).to_i
# For use in the body text
if days_remaining == 1
plural_text = "day"
else
plural_text = "days"
end
if con_mail_exception.include?(area)
mail_body = "Your HSC NetID password for the account #{pswrd_expire['cn']} will expire in #{days_remaining} #{plural_text}, on #{expire_date.strftime('%m/%d/%Y')}."
# Add in College of Nursing specific text!
else
mail_body ="Your HSC NetID password for the account #{pswrd_expire['cn']} will expire in #{days_remaining} #{plural_text}, on #{expire_date.strftime('%m/%d/%Y')}."
end
puts "#{pswrd_expire['sn']} (#{pswrd_expire['mail']})on #{expire_date.strftime('%m/%d/%Y')} in #{days_remaining} #{plural_text}"
# Add in remaining HSC specific text!
# Look to see if the days_remaining is on one of the warning intervals and send mail
if warning_days.include?(days_remaining)
Pony.mail(
:to => 'me@unm.edu',
:via => :smtp,
:via_options => {
:address => 'relay.health.unm.edu',
:port => '25',
:openssl_verify_mode => 'none'
},
:subject => 'Your HSC NetID Password Expires Soon',
:body => mail_body,
:from => '"HSC Support" <hscsupport@salud.unm.edu>'
)
end
end
}
rescue LDAP::ResultError
conn.perror("search")
exit
end
conn.perror("search")
conn.unbind
end
view raw loop.rb hosted with ❤ by GitHub

Friday, June 29, 2012

S is for Stalled

I was hoping to not have to do this so early on. It's the biggest problem in writing a blog; maintaining consistency and finding a voice. But here I am.

I've run into a block and I don't have any code to talk about this time.

Fortunately, I am learning something from this.

My difficulty with Ruby, and by extension Rails, is that it seems easy to get an application up and do fun things like utilize Ajax, query an LDAP or send email. The part I'm having trouble with is manipulating results.

It seems funny to stumble on the simplest of things, but when it comes down to it, that is the whole reason for an app: to manipulate and present data.

I'm just not getting the the pointers and hashes. I'm not getting the syntax and conventions. I'm not getting the methods that can be used on an object. It is going to take some reading and re-reading to get it. And beyond re-reading, I need to make some code samples and play with the results. Maybe I can show that next time.

Friday, June 22, 2012

Sending Email

Next up for my little scripting project is to send some email from our IT's mail relay.

There are a several nice Ruby gems that can make sending mail easy to do. Two are Pony and Mail. Pony is super simple, so I went with that for this script. Mail has a lot of features for importing body text, attachments and other great stuff. I will probably use it in a web application.

I would post some demo code, but it's basically right there in the GitHub Read Me.

I did run into some trouble. I got an error connecting to my IT department's relay.
/Users/garthcolasurdo/.rvm/rubies/ruby-1.9.2-p318/lib/ruby/1.9.1/openssl/ssl-internal.rb:121:in `post_connection_check': hostname was not match with the server certificate (OpenSSL::SSL::SSLError)
Stack Overflow asked and answered. By adding:
:openssl_verify_mode => 'none'
to my
:via_options => {...}
I'm receiving email from my department's relay.

Friday, June 15, 2012

Connecting to LDAP with Ruby

Looking at my tasks, the first order of business is to get connected to our LDAP server through a Ruby script. I'll start with a few Google searches and see what kind of LDAP gems I can use and if there are any examples or tutorials.
Here are a few results:
For whatever reason, I chose ruby-ldap as my gem to install and ran with its tutorials and examples. Net-LDAP also seemed like a good choice.

I ran a little script just to get started.

require 'rubygems'
require 'ldap'
puts "Start Search"
conn = LDAP::SSLConn.new( 'my.ldap.host', xxx )
conn.bind(dn,password) {
conn.perror("bind")
}
view raw lookup.rb hosted with ❤ by GitHub
But I'm not able to connect. Our LDAP is on SSL with a self signed certificate and I need to import it to my environment. I'm jumping ahead here a bit as I got that script from Christian Hofstadtler's post that helped me troubleshoot the issue. Since I'm developing on OS X, these instructions from Apple got my TLS_CACERT in /etc/openldap/ldap.conf configured correctly.

Now I'm getting data from our LDAP and I need to start filtering and refining. My basic script looks like this:

require 'rubygems'
require 'ldap'
puts "Start Search"
host = "my.ldap.host"
port = xxx
username = "managerdn"
password = "password"
base = "o=hsc"
scope = LDAP::LDAP_SCOPE_SUBTREE
filter = "(&(objectclass=person)(!(loginDisabled=TRUE)))"
attrs = ['dn', 'cd', 'givenName', 'sn', 'mail', 'title', 'ou', 'loginDisabled', 'passwordExpirationTime', 'loginGraceRemaining']
conn = LDAP::SSLConn.new(host, port)
conn.bind(username, password)
conn.perror("bind")
begin
conn.search(base, scope, filter, attrs) { |entry|
puts entry.to_hash
}
rescue LDAP::ResultError
conn.perror("search")
exit
end
conn.perror("search")
conn.unbind
view raw lookup.rb hosted with ❤ by GitHub

Tuesday, June 12, 2012

Move password expiration script from ColdFusion to Ruby


Today I started an interesting project migrating a ColdFusion scheduled task to Ruby. Why would we do such a thing? We have a number of problems with our CF server, primarily the CF spooler has been sending two copies of our organization's notice of password expiration. We've investigated and researched and have never come up with a satisfactory (or logical) solution for it. In addition our unit is moving away from CF and to Ruby on Rails for our application development. It would be in our interest to migrate existing business processes to the new environment.

Me, I'm struggling to get up to speed on Rails. I still maintain a lot of CF code and I have at least two current projects being actively developed in CF. I can support our other developer with html, css, testing and mock ups, but I just can't quite get on the Rails train yet.

My boss, probably at the suggestion of our other dev, assigned me to rewrite our failing expiration notification script in Ruby. This I can probably do. And I'll probably learn a lot about Ruby along the way. I'll break this down in our usual SBAR format.

Situation: Our organization expires passwords every six months. We should probably warn our users when their expiration is coming due; at 30, 14, 7, 3, 2 and 1 day. A script that runs every morning should search the LDAP looking for upcoming expirations, and at those intervals send an email.

Background: We have a working ColdFusion script and a history of utilizing our organization's LDAP. ColdFusion was quick and easy to set up, but there are problems with our server. Additionally, our unit is moving away from CF development. Developing a new script should not be difficult since the logic and assets are readily available. Hopefully, we can return responsibility of this business process to the Identity Management unit and not take so much flack for what the warnings do or do not do.

Analysis: To accomplish this project I have created the following tasks:
  1. Connect to LDAP with Ruby
  2. Report contents of LDAP in Ruby
  3. Refine LDAP results
  4. Send email to myself
  5. Build logic structure based on existing code
  6. Test locally
  7. Deploy on server
  8. Test on server
  9. Schedule as chron job
Results: Successfully find and send expiration notices daily.