I have been through this subject before, but haven't found a neat solution yet.
Say we have an application where customers can book a course using the website, and admin staff can also book courses on customers' behalf using a backend system. I'm trying to establish a way to let HR administrators codify constraints applied to permissions like can_make_booking
, as the permission isn't just a boolean and shouldn't be hard-coded into the application.
At the moment, customers can make a booking as long as the course date is a date at least 'n' days standard notice in the future, they are booking no more than the number of places available and they are paying at least the amount due (or nil if their account is set to invoice). Managers can book using the backend application, as long as the appointment date is any time after now.
I envision something like this. Let HR administrators add permission constraints like the following:
role permission constraint
-------- ------------ ----------
customer make_booking 1
customer make_booking 2
customer make_booking 3
manager make_booking 5
Then a table of constraints,
constraint property operator value OR_parent
---------- ------------ -------- -------------------------- ---------
1 $course_date >= strtotime("+$notice days") NULL
2 $places_booked <= $places_available NULL
3 $paid >= $total NULL
4 $send_invoice == TRUE 3
5 $course_date >= strtotime("now") NULL
Chaining these constraints for the customer role would build something like the following eval
ed code (constraint #4 is paired with #3 as part of an OR sequence):
if($course_date >= strtotime("+$notice days") && $places_booked <= $places_available && ($paid >= $total || $send_invoice == TRUE)){
// make the booking
}
Each rule could be used independently at each stage, such as JavaScript and form validation, to give feedback if the booking can't be made for some reason.
However, say HR want to change the rule for customers so that they are only allowed to book 3 places at a time, and the $paid
amount must be at least the $deposit
amount? Ideally, I'd like to allow them to build these php rules dynamically, without giving them access to the hard-written code. The properties and values could be sanitized so that eval
ing code isn't a problem. I don't want to hard-code every combination of each rule as for some cases, there would be no clear way to guess an HR admin's logic in advance.
I've looked at the Zend_ACL version of assertions, but they don't seem to offer the dynamism I'm looking for. What would be a good way to implement these dynamic constraints? Any thoughts from other environments? Thanks!
Some more insight into the problem from a CUSEC presentation by Zed Shaw talking about why "the ACL is dead" - http://vimeo.com/2723800
Well, this is one of the areas that still elicit a rather big deal of discussion. As some say[who? - think it was Atwood among other people, but a link escapes me], an application that can do everything has already been made; it's called C. What you want to do borders quite nearly the 'too generalized' area, although I can see the value in not needing a programmer every time a business rule changes.
If I'd have to implement such a system, I guess I'd try and break it down into domains. You've done a relatively good job of it already with the second table. Just normalise that into separate domains that are used to compound business rules. You create a business rule which is comprised of 1 or many constraints OR-ed together. Each constraint needs a property that is restricted with an operator against a term. Terms can be tricky, since they can be anything from a property to a function, to a compound function. It's probably easiest to check your business rules to see what you need. Start with, say, properties, booleans and commonplace things like 'NOW'.
So the schema itself would, for example, be comprised of the
rules
, which contain multipleconstraints
(obvious benefit being that you could tie these to any [user group/offer/timespan/other domain] you want). These are, in turn, comprised ofproperties
, which would compare with one of theoperators
(reference table mostly so you can enter custom descriptive names for non-programmers, but you may choose to enter a custom functions in it at some point) and, of course one of theterms
. The last part being the most complex one, so you'd probably have to qualify it with an ID interm_types
so you'd know whether you're comparing to another property or a function. You can also justVARCHAR
it and create the field with PHP, which shouldn't be too difficult, given how you have all the options inproperties
and/orfunctions
.It's a very open-ended system (and there are probably better ways of going at it), so it's probably not worth doing unless you know that you'll need a high degree of dynamism in business rules.