Saturday, October 08, 2011

NemID on Rails

NemID on Rails
For nylig har jeg set et tweet eller to fra folk der gerne vil kunne banke et NemID-login op på en hjemmeside i et Ruby on Rails-projekt. Derfor tænkte jeg at en lille guide til at få NemID-login op at køre i Ruby on Rails kunne være til nytte ude i verden.


Jeg vil holde mig til at få NemID til at fungere i JRuby. Skal man have det til at fungere i flere Ruby-implementationer, kræver det en del arbejde (mere herom nedenfor).


Jeg vil ikke forklare hvad NemID i bund og grund er for noget. Det har DanID gjort fint på deres egen side, https://www.nemid.nu/.

Der findes en såkaldt Tjenesteudbyderpakke med noget eksempel-kildekode til Java og .NET, så hvis man har mod på at forstå det, vil nedenstående måske virke trivielt.

Formatering
Tilsyneladende er Blogger et sindssygt dårligt sted at skrive kodeeksempler. Mange linjer bliver ombrudt så det bliver meningsforstyrrende, og HTML-tags i tekst er den meget forvirret over.

Undskyld.

Scope
NemID består faktisk af to løsninger: En til bankerne, og en til det offentlige. Vi vil fokusere på den offentlige løsning, da private firmaer kan få lov at gøre brug af denne løsning. Derfor er det sandsynligvis denne løsning du er interesseret i.

DanID har lavet nogle fine udkast til design af login-sider. I vores eksempel vil vi dog nøjes med en kedelig, hvid stil. Pointen med dette blog-indlæg er ikke at "style" en rigtig login-side, kun at få login til at fungere.

NemID giver mulighed både for login og signering af dokumenter. Vi holder os til login. Hvis du er interesseret i en tilsvarende guide til signering, så giv lyd i en kommentar nedenfor.

Desuden tilbyder DanID nogle services til, på baggrund af certifikatoplysningerne ifm. et login, at slå et tilhørende cpr-nummer op. Det kræver ekstra aftaler med DanID, og vi vil heller ikke komme ind på det.

Forberedelse
Før du kan følge skridtene her på siden til noget, er du nødt til at få dig en såkaldt tjenesteudbyder-aftale med DanID. Ud over at det giver mulighed for at benytte NemID på din egen hjemmeside, giver det dig adgang til DanID's testmiljøer. Og før du har adgang til disse testmiljøer, vil ikke engang test-projektet her på siden kunne køre hos dig, idet DanID bruger IP-blokering på testmiljøerne.

Når du indgår aftale med DanID, får du et certifikat registreret hos DanID. Dette certifikat skal bruges ifm. opsætning af applet'en, således at denne starter op med dit firmanavn.

DanID vil også fortælle dig hvordan du opretter testbrugere i testmiljøet.

OOAPI
OOAPI (Open Oces API) er et open source-library som findes både til Java og .NET. Det indeholder funktionalitet til at læse certifikater, lave gyldighedscheck på certifikater osv. Desuden kender dette library til de ekstra certifikat-felter som NemID benytter sig af.

Hvis man vil bruge NemID uden for JRuby eller IronRuby, er man nødt til at gen-implementere dele af OOAPI. Det er helt sikkert en hyggelig, fin opgave, men i dag holder vi os til blot at bruge OOAPI i Java-udgaven.

Selve OOAPI er blot en jar-fil, og den hentes fra DanID's hjemmeside for tjenesteudbydere. Denne side er dog ret svær at finde, så for nemheds skyld får du her et direkte link:

https://www.nets-danid.dk/produkter/for_tjenesteudbydere/nemid_tjenesteudbyder/nemid_tjenesteudbyder_support/tjenesteudbyderpakken/

Hent ooapi-1.81.2-with-dependencies.jar.

Tjenesteudbyder-eksemplet
Vi skal desuden, for at få vores legetøjseksempel til at fungere, have et keystore med et legetøjs-certifikat som i DanID's testmiljøer er registreret under tjenesteudbyderen "www.nemid.nu".

Hent derfor tuexample-source.zip fra ovenstående side og snup filen applet-parameter-signing-keystore-cvr30808460-uid1263281782319.jks.

Faktisk har du i tuexample-source.zip al information der skal til for at gennemføre alle scenarier med NemID: Login for privatpersoner, login for medarbejdere, signering osv. Og det er ikke alt for kompleks kode. Men det kan du altid vende tilbage til hvis du får behov for det.

Oprettelse af Rails-projektet
Først skal du sikre at du har installeret og bruger JRuby. Bruger du RVM, er det blot at skrive

rvm install jruby
rvm use jruby

fra kommandolinjen.

Lav en mappe hvori du har ooapi-1.81.2-with-dependencies.jar og applet-parameter-signing-keystore-cvr30808460-uid1263281782319.jks liggende. Kør dernæst følgende fra kommandolinjen:

export CLASSPATH=$JAVA_STUFF/:/$JAVA_STUFF/ooapi-1.81.2-with-dependencies.jar 

(...hvor altså $JAVA_STUFF skal pege på den nævnte mappe med de to filer...)

Så lad os oprette et Rails-projekt:

rails new NemIdOnRails
cd NemIdOnRails


Environments
BouncyCastle skal sættes som Security Provider i JVM'en, så følgende skal tilføjes config/application.rb:

# Registrer BouncyCastle som Security Provider
java.security.Security.addProvider org.bouncycastle.jce.provider.BouncyCastleProvider.new

OOAPI skal have at vide hvilke miljøer det skal stole på. Her bruger vi DanID's "ekstern test". Tilføj følgende i bunden af config/environments/development.rb:

org.openoces.ooapi.environment.Environments.setEnvironments(org.openoces.ooapi.environment.Environments::Environment.value_of('OCESII_DANID_ENV_EXTERNALTEST'))

Desuden skal vi have styr på hvor det førnævnte keystore ligger, hvordan vi får fat i indholdet, og hvilken URL vi bruger for at tilgå vores testmiljø. Så sæt yderligere følgende ind i config/environments/development.rb:


ENV['key_store_url'] = 'applet-parameter-signing-keystore-cvr30808460-uid1263281782319.jks'
ENV['key_store_password'] = 'Test1234'
ENV['key_store_alias'] = 'alias'
ENV['key_password'] = 'Test1234'
ENV['server_url_prefix'] = 'https://syst2ext.danid.dk'

Når du kører i produktion, vil du bruge et andet keystore, og sikkert også nogle andre passwords... og server_url_prefix får du fra DanID.


Controllers, views, routes...
Vi vil lave en meget, meget simpel applikation med to controllers: Sessions og SecretPages. Sessions bruges til login, og SecretPages beskytter sin ene side, således at man skal være logget ind for at se den.

Vi beslutter at man er logget ind hvis den aktuelle session har en "pid"-attribut. PID er et felt i et personligt NemID-certifikat som unikt identificerer en person. Altså lidt som et cpr-nummer. Man kan derfor oplagt bruge PID'en som nøgle i en brugerdatabase.

Så lad os generere de to controllers:



rails g controller Sessions
rails g controller SecretPages


Og så skal vi lige opdatere config/routes.rb lidt:

resources :sessions
match 'hemmelig', :to => 'secret_pages#index'

Visning af applet'en
Vores Sessions-controllers new-action skal vise applet'en. Der er noget arbejde både i controlleren og i vores view.

For at undgå "replay attacks" finder vi på en tilfældig streng som brugeren via sit login signerer, og som vi så efterfølgende, når login er foretaget, checker i det svar som applet'en giver. Det er alt hvad vi gør i vores controller:


def new
  session[:challenge] = SecureRandom.hex(10)
end


Selve view'et, app/views/sessions/new.html.erb, indeholder lidt kode, men er ikke vildt kompleks:


<%
signer = org.openoces.ooapi.web.Signer.new(ENV['key_store_url'], ENV['key_store_password'], ENV['key_store_alias'], ENV['key_password'])


generator = org.openoces.ooapi.web.OcesAppletElementGenerator.new signer
generator.server_url_prefix = ENV['server_url_prefix']


generator.challenge = session[:challenge]
generator.log_level = 'debug' # INFO/DEBUG/ERROR
%>

<h1>Log ind</h1>
<%=raw generator.generate_logon_applet_element sessions_path %>

Her bruger vi så alle de værdier vi har sat i development.rb tidligere. Desuden bruger vi OOAPI til at generere selve applet-tag'et til siden. I den sidste linje beder vi desuden applet'en om at POST'e tilbage til "sessions_path".

Prøv det! Kør "rails s" fra kommandolinjen, og gå ind på http://localhost:3000/sessions/new.

Check af svaret
Som sagt har vi bedt applet'en om at POST'e sit svar ind på "sessions_path", dvs. vi rammer vores create-metode i SessionsController.rb. Der skal heldigvis ikke så meget til at checke resultatet fra applet'en... lad os starte med følgende:

def create
  result = org.openoces.securitypackage.SignHandler.base64Decode(request[:result])
  if result == 'ok'
    handle_ok
  elsif result == 'cancel'
    redirect_to :cancelled
  else
    redirect_to :unknown_action
  end
end


Så det er ret nemt at se om brugeren rent faktisk ønskede at gennemføre et login, eller om vedkommende fortrød først. Men det er jo ikke nok at brugeren bare har trykket "OK" - vi forventer også et gyldigt certifikat, og oven i købet et personligt certifikat (i modsætning til fx et virksomhedscertifikat), og vi ønsker som tidligere nævnt at huske PID'en fra certifikatet:


def handle_ok
  certificate_and_status = get_certificate_and_status 
  if certificate_and_status.certificate_status() != org.openoces.ooapi.certificate.CertificateStatus.value_of('VALID')
    redirect_to :invalid_certificate
  elsif !is_poces(certificate_and_status)
    redirect_to :wrong_certificate_type
  else
    session[:pid] = certificate_and_status.certificate.pid
    redirect_to '/hemmelig'
  end
end

Også her er der et par løse ender, nemlig hvordan vi haler certifikatet og status ud af request'et, og hvordan vi checker at der er tale om et personligt certifikat. Først certifikatet og status:

def get_certificate_and_status
  signature, challenge, service_provider = request[:signature], session[:challenge], 'www.nemid.nu'
  org.openoces.securitypackage.LogonHandler.validateAndExtractCertificateAndStatus(org.openoces.securitypackage.SignHandler.base64Decode(signature), challenge, service_provider)
end

Så det hele ordnes af OOAPI. Bemærk brugen af 'www.nemid.nu'. Her vil du, når aftalen med DanID er på plads, indsætte det navn som DanID har registreret dig under. Desuden giver vi vores tilfældige streng fra tidligere med, så OOAPI checker at brugeren har signeret dette.

Det er nemt at checke at vi har fået fat i et personligt certifikat:

def is_poces(certificate_and_status)
  certificate_and_status.certificate.kind_of? org.openoces.ooapi.certificate.PocesCertificate
end

Men en lille ting mere... Det er jo applet'en der kalder tilbage til vores controller, og det sker jo så uden Rails' indbyggede CSRF-beskyttelse. Så ovenstående kode vil bare give en fejl når brugeren logger ind. Men da vi har vores tilfældige streng og checker denne, er vi sikre på at alt er sikkert alligevel. Så lad os slå CSRF-beskyttelse fra på vores create-metode ved at skrive følgende øverst i vores SessionsController:

protect_from_forgery :except => :create

Åh ja... og vi vil jo også gerne kunne logge ud igen, dvs. nedlægge vores session:

def destroy
  session[:pid] = nil
  redirect_to :action => :new
end

I ovenstående kode har vi et par ekstra sider vi redirigerer til når noget er gået galt. Det efterlades som en opgave til læseren at implementere dette :-)


Den hemmelige side
Tjah, der er vel ikke så meget at sige om SecretPagesController. Den beskytter sin index-metode:

def index

  redirect_to :controller => :sessions, :action => :new unless session[:pid]
end

I det tilhørende view (app/views/secret_pages/index.html.erb) vil vi gerne se vores PID, samt have mulighed for at logge ud igen:



<h1>Tillykke!</h1>

Det lykkedes dig at logge ind! Din PID er <%= session[:pid] %>.


<%= link_to 'Log ud igen', session_path(1), :method => :delete %>


Det var så det
Mere er der ikke i det. Men som sagt, for overhovedet at kunne starte på denne lille trin-for-trin-guide skal du have en aftale i stand med DanID først.

Som det sig hør og bør, så har jeg lavet et projekt på GitHub med al koden, så du kan se om jeg har snydt: https://github.com/olefriis/NemID-Rails-login

Held og lykke!

Monday, June 13, 2011

Programming with the Stars at GOTO Copenhagen 2011

At GOTO Copenhagen 2011, we had a track called Programming with the Stars. It gave me a unique chance watching five software professionals along with an audience member solve the same (simple) problem in different programming languages, using different programming paradigms. It would be a shame not to reflect a little on what I learned that day.

Overview
In every time slot, a speaker was asked to solve the Tennis Kata along with a volunteer from the audience. The big idea was to start discussions on how to write code, how to apply different programming paradigms, and to show off the different languages and techniques to the audience.

First, Ulf Wiger did the Erlang presentation. There was even media coverage on this presentation. Ulf was very relaxed and cool and showed a lot of nifty language features. Alongside his text editor, he had a console where he poked around in the running code, showing how it worked. The result can be seen here.

Then, Patrick Linskey did a solution in Java. The result can be seen here. Apart from doing a solution in Java, Patrick was keen to check code into Git along the way, and we had a unit test showing that the code worked. I ended up as his partner, since the Java audience was a bit shy. It was great fun.

Next up was Mark Seeman in C#. Mark is a hard-core TDD master, and his partner clearly had no TDD experience. This was an extremely entertaining learning experience to everybody, as Mark was struggling to keep his partner from writing code before test and keeping things simple. The result can be seen here.

Talking entertainment, Sam Aaron was up next for the Ruby session, and he brought with him a monome. He had programmed it so that when he ran the tests (or rather, RSpec specifications), it would flash the right colors and perform adequate sounds based on the test outcomes. The result can be seen here. Frank Vilhelmsen took some great pictures of the event.

Finishing off, Christophe Grand showed us how to do the solution in Clojure (with Sam Aaron as partner, in fact). Just as Ulf, Christophe tried out the solution in a console (the so-called REPL) while writing the code. The result can be seen here.

Programming styles
Of course it's unfair to use the results of the different time slots as representations of the programming styles used in the different programming camps, since the track was more about process and learning than the actual result. Unfair or not, though, let's dive in...

I've started to appreciate Erlang. I used to think that it could only be used for telecommunication hubs and other stuff that was irrelevant to me, but Ulf showed lots of nifty stuff. Pattern matching works well in Erlang, and record manipulation is concise. However, it still feels a bit old-fashioned to me, e.g. you cannot reference fields in records in other modules by name until you specifically load a record definition. Ulf explained that there was a reason for this (very loose coupling, so you can upgrade parts of your application).

The Java solution is different from all the other solutions in that it models players and a game, and we have a definition of a finished game, a winner and all. This is probably overkill for the simple Tennis Kata game, but is typical Java. Why do we Java programmers over-engineer like this? Java is very verbose, so we probably wouldn't have had the time to write all this code without a proper IDE.

The C# code ended up very simple, since Mark was struggling to get the process straight. He used AutoFixture as a testing tool, something I'd really like to try out soon. We didn't get to see much of it, but it seems like a nice testing productivity tool. My verbosity comments for Java mostly apply for C# as well.

Ruby... I love it. The code, the specs, it all looks and feels natural to me. Compared to the two functional language results, though, I do see that it's a little verbose. However, I think it's OK to be a little bit verbose, as long as the verbosity serves readability.

I have to admit that I still haven't learned to appreciate Clojure. The technology is really cool (memory transactions, persistent data structures, etc.), but the LISP style is a problem for me. The following Clojure code:

(defn result
  "Returns a string representation of the game."
  [[score-a score-b]]
  (cond 
    (= score-a score-b) (if (>= score-a 3) 
                          "Deuce"
                          (str (points score-a) " all"))
    (and (> score-a 3) (= score-a (inc score-b))) "Advantage A"
    (and (> score-b 3) (= score-b (inc score-a))) "Advantage B"
    (and (> score-a 3) (> score-a (inc score-b))) "Game A"
    (and (> score-b 3) (> score-b (inc score-a))) "Game B"
    :else (str (points score-a) " " (points score-b))))

can be written (almost) as concise in Java/C#/Ruby:

String result(int scoreA, int scoreB) {
  if (scoreA == scoreB) {
    return scoreA >= 3 ? "Deuce" : scoreA + " all";
  }
  if (scoreA > 3 && scoreA == scoreB + 1) return "Advantage A";
  if (scoreB > 3 && scoreB == scoreA + 1) return "Advantage B";
  if (scoreA > 3 && scoreA > scoreB + 1) return "Game A";
  if (scoreB > 3 && scoreB > scoreA + 1) return "Game B";
  return points(scoreA) + " " + points(scoreB);
}

I don't know... what you like the most probably depends on what you're used to.

To test or not to test?
Both the Java and C# examples were made strictly TDD, and I think it worked really well. Having the tests drive the design enables the audience to follow exactly what's going on. At the end of the session, everybody knew that the solution worked.

It seems like test-driven development is having a hard time in the functional language fragment. Just try looking up "testing" in Joe Armstrong's Erlang book, and you'll see what I mean. It's easy to start up a prompt and mess around with your code and obtain confidence that way, so perhaps that's all a functional programmer needs? I'm a bit curious about the effect this has on long-term projects with many team members, though.

The really interesting case was the Ruby session. Sam and his partner started poking around with no tests, just to get some basic design fleshed out. That worked well. However, they held back doing the tests for too long, mainly because Sam's partner and part of the audience didn't feel it was necessary to start testing. However, when finally they started doing RSpec tests (with the monome!), they went from having no clear idea about their state to knowing exactly what worked and what needed to be done next. An amazing transformation!

Different cultures
Whoever turns up for a specific session depends on lots of things on a conference, but it was interesting to reflect on the audience for the different sessions.

The C# audience seemed pretty young and enthusiastic. The Java audience was the biggest, but also the most shy: nobody volunteered as Patrick's partner, and there were very few comments from the audience during the session. The Clojure session was packed with other speakers from the conference.

In conclusion
Being part of the organization team, I was supposed to stay at the track the whole day, and that turned out to be very rewarding. Seeing people with experience in the 5 different languages and paradigms solve the same problem gave a much better feeling of the languages than reading 5 different introductory articles.

Sunday, January 23, 2011

So, TDD is hard? Start decoupling!

As a lot of other TDD people, I try to push TDD onto my colleagues. It seldom works, and I think that's a shame. Therefore, lately I've begun to think about why a lot of other people find TDD painful, unproductive, and plain irritating when my own experience is the direct opposite.

I'd like to start a discussion about doing TDD and other agile practices in the real world, with real-world code examples, and this is the first blog post in this series.

The thing I want to point out in this post is that most of the time, people holding a grudge against TDD don't practice enough decoupling. Let's take an example of some code (practically) from the real world that I stumbled upon the other day. I understand those who wouldn't like to test this.

public ModelAndView createUser(long applicationId, String comment) {
  Application application = applicationDao.get(applicationId);
  application(OrderState.GRANTED);
  if (comment != null && !comment.isEmpty()) {
    application.setProcessingComment(comment);
  }
  applicationDao.save(application);
  Address address = application.getContactAddress();
  SocialSecurityNumber socialSecurityNumber = order.getSocialSecurityNumber();
  if (userDao.findUser(socialSecurityNumber) == null) {
    userDao.createUser(socialSecurityNumber, address);
  }
  if (application.isAdministrator()) {
    HashSet roles = new HashSet<role>();
    roles.add(new AdministratorRole(timeService.getTime(), null));
    roleDao.addRoles(socialSecurityNumber, roles);
  }
  return new ModelAndView("users/create", "message", "User created");
}

So, how would we test this? Well, we need to create a mock for an ApplicationDao, UserDao, and RoleDao. And we need to mock these method calls:

applicationDao.get(...), returning an Application
applicationDao.save(...)
userDao.findUser(...), returning a User
userDao.createUser(...)
timeService.getTime(), returing a Date
roleDao.addRoles(...)

But even worse, our logic needs to know a lot about different objects:

  • How to change an application so it gets to the "granted" state.
  • How to make sure that a user exists in the database.
  • How to promote a user to administrator.
  • How to construct an AdministratorRole.

This directly affects the cyclomatic complexity, so we need a huge amount of test cases to cover all possible paths through the code.

I understand why people find TDD frustrating when writing this kind of code.

But let's refactor it just a little bit:

public ModelAndView createUser(long applicationId, String comment) {
  Application application = applicationDao.get(applicationId);
  applicationDao.grant(application, comment);
  Address address = application.getContactAddress();
  SocialSecurityNumber socialSecurityNumber = order.getSocialSecurityNumber();
  userDao.ensureUserExists(socialSecurityNumber, address);
  if (application.isAdministrator()) {
    roleDao.makeAdministrator(socialSecurityNumber);
  }
  return new ModelAndView("users/create", "message", "User created");
}

In the real world, we shouldn't stop here - the method could easily be split up further. However, for this blog post, we'll stop with the refactoring now.

Anyway, with that code, we'll need to mock these method calls:

applicationDao.get(...), returning an Application
applicationDao.grant(...)
userDao.ensureUserExists(...)
roleDao.makeAdministrator(...)

Small change? Well, the code no longer needs to know how to change the state of the application, how to make sure a user exists in the database, or how to promote a user to administrator. As a result, the cyclomatic complexity of the new code is much lower, so just two tests will cover the positive cases. Of course, we've introduced new methods in the other services which need to be tested in their respective test suites, but each of them should be pretty straight-forward to test.

And in fact, we've done only two things in the refactoring:

  • Tell, don't ask. Instead of fishing out values and passing them back and forth, we've defined new methods that just "do the job", without us having to figure out how to meddle with a lot of object in the same method.
  • Flesh out responsability. In our method, we don't want to know that in order to promote a user to administrator, we need to give the user a certain role. That's what RoleDao knows, and there's no need to spread that knowledge to all our classes.

These simple tools are great when you're trying to decouple your components, and incidentally, you end up with more testable code.

And even better, if you remember these tools when you're test-driving your code, you'll start off writing nice, decoupled code.

Don't be afraid of writing a lot of small classes with their own, very limited responsability. Each of them will be easy to test, and your code base will be more readable.