Don’t You Have to LOGIN first?
In my previous post, Avoiding Iteration Zero, I suggested starting with “the one obvious thing that needs to be done? (Hint: it’s not ‘login.’)” As Jon Kern has recently mentioned, this same topic has come up elsewhere. I was also in that list discussion.
Jon is, of course, right in a narrow sense. You can start with login, if you want. You can also start with an Iteration Zero. (Or, an Iteration Minus One, as I’ve seen one organization do when their list of pre-planning outgrew one iteration.) I’ve observed that you can generally get better software, faster if you start somewhere else.
There are some very good reasons for this. For one thing, it’s unlikely that you’ll find much business value in delivering a system that allows people to login, but do nothing else. Unless you’re writing a login package for others to integrate into their code, something else is the central idea of the system. Surely there are some things about that central idea that we don’t yet know in detail. By contrast, “login” is fairly widely known and understood (even if sometimes implemented poorly). Even if we decide we can’t deliver the system without “login,” we can learn important things earlier if we work on the central idea first. (And I can imagine, in a pinch, having a usable system where access was controlled by putting the computer in a secure room where only those with authority could touch it.)
Learning important information sooner is one of the subtle but powerful benefits of working in an Agile fashion. It can help the business make better decisions about priority, or even about the direction of the effort. That’s one of the tenets of the Lean Startup focus, but it works for established organizations, too. There’s almost always something new about what we’re doing, or we wouldn’t be spending money doing it.
This pattern of accelerating learning is powerful for businesses that use it. But it’s also powerful for programmers working on the code. As we build our application, we can learn about better ways to structure the code, and we can use that information when we’re writing further code. I’ve found this to allow me to do a better job when developing code in an iterative-incremental fashion than when creating an up-front design and then following it.
Let’s look in more detail at Jon’s suggested beginning:
For example, to start, you can simply check that the response has “Login Succeeded” ( or “Login Failed” for testing that a bogus login attempt does indeed fail). …simply:Scenario: Successful Login When I login as admin Then I should be logged in Scenario: Failed Login When I login as asdf56ghasdkfh Then I should be not logged in
And your steps would hide the logic for filling in the login form and checking for success:Given /^I login as "([^"]*)"$/ do |login| @login_name = login visit login_path fill_in "login", :with => login fill_in "password", :with => "password" click_button "login_button" end Then /^I should be logged in$/ do response.should contain "Login Succeeded" end Then /^I should not be logged in$/ do response.should contain "Login Failed. Please try again." end
Given this simple start, where will we put our access control logic? Most likely in our Controller of MVC or Presenter of MVP. Then we’ll decide to which page we direct the user. In effect, we’re protecting the “Login Succeeded” page from the unauthenticated. Is that page the asset whose access we want to control?
I once worked with a client who had an application that allowed authorized users to access the documents to which they were authorized. It was not an Agile shop, and they were not working in short iterations using small stories. They had, however, been delivering new functionality in each release for a number of years. And they were very thorough programmers, carefully checking that they were not delivering bugs. Having started with the idea of access control, they had ended up with their authorization check in the Action class of their Struts app. Over time, there was a need for further authorization concerning some documents. Only an authorized user could see any document, but some documents were only available to a subset of users. The Action class checked that the user was logged in, retrieved the list of documents, and then filtered out the ones that particular user was not allowed to see.
Do you see any problem with this scheme?
It works well enough, but it causes some maintenance headaches. Even though there was not a link to the unauthorized documents, there’s nothing to stop a savvy user from guessing the URL and retrieving it anyway–so there had to be an authorization check there, too. And there were multiple lists of documents, so each one had to make this check. Sometimes when the authorization logic changed, one of the Action classes would be overlooked. Extracting this authorization filtering code to a method would reduce much of this duplication and make that particular mistake less likely, but some lists had different filtering rules. Also, the Action classes would retain the duplication of the order of steps that each one had to apply in order to properly protect the assets. (That’s a more subtle flavor of duplication that many overlook.) And there was a requirement to show some documents in the list to people who were not yet authorized to retrieve them.
It was pretty complicated and error-prone. Enforcing the security at the outer layer, instead of directly around the protected assets, resulted in more places and more variability in the implementation. I’m sure that you and I and Jon Kern would not end up with such an error-prone design. Jon is an excellent software designer–much better than I am. He can explain why you’d want to design in a particular way and describe the reasons why. I, on the other hand, have trained myself to be sensitive to duplication. Test-driving code with an aversion to duplication would lead me to a different design.
I helped these programmers implement a new feature–one that provided government-mandated access to certain documents even if the user wasn’t logged in at all. Since this required a change to all of these access controls, it provided the impetus to change the design. While adding this new requirement using Test Driven Design, I also refactored code to remove the duplication. As I did so, I pushed the access check lower in the code, passing along a “AuthenticationToken” object that could be treated as a black box by intervening layers. Ultimately the access check was made in the database queries themselves, insuring by simple inspection that no path in the user interface could lead to a condition that allowed unauthorized access.
You’re probably a good programmer, too. I’m sure you wouldn’t make a mistake. Linda Rising recently told me that 80% of people think they’re above average, so you must be good. As it happens, the programmers who built this system were pretty good, but they weren’t experienced in the technology. This J2EE system was the first Java code they’d ever written. These programmers generally succeeded by being very careful to check all the paths. Only very occasionally did they miss one and allow a bug to escape to User Integration Testing.
It’s my contention that by implementing the primary functionality first, in this case the listing and retrieval of documents, then we’ll be more likely to naturally put the access control directly around the assets that matter. In other situations, there may be other considerations for the access control that would be different from the application I described. In any case, if our primary story is
When I do whatever my application does Then I get the result of doing so
in all of the varieties of “do,” then the proper place for access control will be obvious for more than 80% of us when we get to the story
Given I am not an authorized user When I do whatever my application does Then I am denied the result of doing so
While it is certainly true that we can get to the desired design anyway, as both Jon’s blog and my story above illustrate, it’s easier, more direct, and more likely that we’ll do so if we start with our primary business functionality before the LOGIN story.
It’s not a law of the universe, but it’s a good heuristic: The Login story is not the place to begin.