In-Secure Featuritis
Monday, June 11th, 2007I wonder sometimes what was going through the brain of my predecessor here at work. I met her, worked for her for about a month, and when applying for her job meant taking instruction from her (while she worked somewhere else) as well as taking a deep pay cut, I said no thanks and walked away. Now I’m doing the job at close to what I was making before, and she was on retainer to me for a month, during which I called her twice.
Because reading code is the way to understand.
You understand, or learn, two things when you read someone’s code. The first, at least hopefully, you understand what the code does — and sometimes why. The second is perhaps the deepest insight into another’s thought processes that I’ve ever seen — in effect you learn how they think.
You can learn if someone can think by giving them a puzzle, something solvable say, or a broad design question, and see where they go with it. But if you want to see how they break a problem down, and piece it together step by step, have them write a program to do it.
I don’t know what to make of my predecessor’s thought processes.
It’s obvious she knew little about best coding practices, and thus was largely self taught, with a poor teacher (mainly the example of the cold fusion code we bought). Today I realized that one of the biggest problems with her code was being used to make another feature work.
Let me describe the problem (this is where it starts getting ultrageeky). The app in question is a web-based application, written in cold fusion for the management of “work tickets”. It’s essentially a process application: someone creates a work ticket, it get assigned to a particular person who performs the job, notes what he or she did on the application, and marks their part done. This bumps it to their boss, who closes the work ticket, and it goes to historical work ticket land.
This is a pretty simple application, there’s a bunch of hard-to-edit supporting tables, tasks, results, assignments, and workgroups, and stuff, but the core is really just a queue-based process application.
There are two ways a ticket can be entered: by someone in an agency, who logs in with just the name of their agency (so we hope they enter real contact info), or by one of our employees. In the former case, it goes in an “incoming” queue for the appropriate workgroup, and is assigned to one of that workgroup’s members. The workgroup member enters some tasks, marks it complete and so on.
In the latter case, the workgroup member enters the ticket (which is appropriate for some cases where they see the problem and fix it, or they follow a set route every day, or something). After entering the ticket it is still technically unassigned, but they are sent to a screen that would allow them to add time. They do, and that action “assigns” the ticket to them. (Their boss could still edit the ticket, and assign someone else to it, but that rarely happens).
Now if I was designing this application, the workgroup members wouldn’t be able to see tickets that weren’t assigned to them — trying to open one up would result in an error, so I would assign tickets they entered to them automatically. This means that someone could assign themselves any ticket they wanted by opening it up in a browser. This can be done by editing the URL (which isn’t necessarily a bad thing, it’s view data, so a GET type string is appropriate). No check is made to see if they can edit it or do anything with it, but they could add work to it, effectively assigning it to themselves.
Now, no one in their right mind is going to go around assigning themselves more work, but they could screw with each other, across groups, if they knew about this. It’s just stupid, and if my predecessor had used a decent security model she’d never have been able to do it this way.
This is particularly bad because she decided to model “assignment” as being equivalent to “has done work on”. That is, it’s assigned to you if you have entered time applied against the work ticket. This is an ok definition for route 2, where the one who does the work is the one who enters the ticket (so the work is done already). But it’s a horrible definition for the other route: where the ticket comes from outside the organization.
Because it means that in order to implement the “assign” action, the application has to insert a dummy record in the table where applied time is — a 0 hours, no-task, completed in 1900 record which just “assigns” the task to the user.
The thing that really gets me is that this is a completely non-obvious way to implement this. There’s a table of workers, there’s a table of work requests, just create a table of assignments, which is just worker+work request. Easy.
Based on the (incredibly poor) naming of the tables, I suspect that the table which stores the time worked/task done information was originally just the assignment table. But they repurposed it to contain all the other info, forcing this bizarre conflagration. Table should have one purpose, really. They should be one thing — sometimes that thing is a noun-like thing, “employees” or “work requests” and sometimes it’s a relationship “employees assigned to work requests”.
Because once you change “employees assigned to work requests” to “employees assigned to work requests and the tasks they did on the work requests”, you’ve changed your key from (employee, work_request) to (employee,work_request) + some other stuff.
They told me that they used to be able to enter one task/work request. The key in that table is almost at the end (but not quite) (and my predecessor always added fields to the end of a row). So I bet this is why it’s like it is.
I just don’t fathom why anyone would make the decisions that way. It’s crazy to me.
And, to be honest, this is jut one of the things — and perhaps a minor one — that is wrong with this application.
