1

I'm in the process of creating a complex authorization module for a system which will be based on the specification pattern. Access rules are defined as followed:

  • An operationName defines to which operation the rule applies to.

  • A SubjectSpecification defines wheter the rule should apply to a specific Subject or not. E.g. the following would match any subject within the division A.

    subjectSpec = new MyAppSubjectSpecification(division: 
    Division.SOME_DIVISION)
    
  • An AccessSpecification defines the actual rule expression.

  • An AccessType defines wheter the matching rule grants or deny access.

  • A RulePriorityStrategy that defines how rules shall be inherited.

For the question, let's say that our application's Subject has the following structure {divisionId, sectionId, groupId, userId } and that the RulePriority states that the more specific the SubjectSpecification is then the higher it's priority is.

For instance, a rule targeting a specific userId would be prioritized over a rule targeting the division to which that user belongs to.

Now, here's the real challenge which I'm not sure how to solve yet. I want rules to be inherited in a way that high priority rules inherit from the lowest priority rules, while having their own condition override parts of the lowest priority rules which are conflicting. Please also note that operations are denied by default if there are no matching grant rule.

For instance, with the following rules:

- Grant OperationA to all where (A && B) || C
- Grant OperationA to division A where D
- Deny Operation to user xyz where B && E

Here I'd expect user xyz (which belongs to division A) to be allowed when C || D || (A && B && !E).

The way I was planning to use the final inherited grant and allow rules is as follow:

canPerformOperation = grantSpec.isSatisfiedBy(...) and not (denySpec.isSatisfiedBy(...))

In this case it seems that combining in the following way would work, but it does not in all scenarios (e.g. when allow must override denial).

GR = Grant rule
DN = Deny rule

allowed = (GRn || GRn+1 ... || GRn+x)) && !(DRn || DRn+1 ... || DRn+x)
allowed = ((A && B) || C) || D) && !(B && E)

I've made a bit of research and I'm sure this is standard academic knowledge of logical expressions, but I couldn't find a standard algorithm that solves the issue somehow and I'm sure coming up with a solution will take me quite a lot of time or will not be efficient.

plalx
  • 42,889
  • 6
  • 74
  • 90
  • If rule R1 supersedes another R2, that means that R2 is effectively R2 && !R1. If then there's R3 after that, then it's effectively R3 && !R2 && !R1. Obviously that makes a mess if you have a lot of rules, but at least that frees you from the ordering. You could try to simplify the results by finding the conjunctive or disjunctive normal form (CNF or DNF) of the rules. I think there's a standard algorithm for that but I forget what it's called. Also, you might want to put grant and deny on an equal footing by letting deny(A) = grant(U \ A) where U = all users and backslash = set difference. – Robert Dodier Jul 08 '17 at 18:33
  • @RobertDodier Thanks for your comment, however I think I would need a less abstract example to make sure I fully understand (step by step transform perhaps). Could you show how that applies to the ruleset of the example and how I'd use the superseding logic you explained to construct a final rule which combines all the mentionned rules in a single expression that returns if the operation would be allowed. Also, could you expand on what you mean by equal footing? – plalx Jul 09 '17 at 00:09
  • "Equal footing" = you don't have to think about whether it was a grant or deny if you just say deny = complement of grant; there's just one kind of operation (namely grant). I suspect the main difficulty here is that you've assumed that overall = (union of all grant) intersect complement(union of all deny). That's incorrect since ordering means that the effective rule when you get to rule #k is (rule #k as stated) intersect (complement of all preceding rules). The overall rule is then intersect(all effective grants). – Robert Dodier Jul 09 '17 at 20:32
  • As I was saying before you can find the conjunctive normal form of that overall rule and then you could identify which terms fail, for an input which fails the overall rule. Or you could find the disjunctive normal form and then identify the terms which pass, for an input which passes the overall rule. Note that the terms in CNF and DNF don't necessary have a simple meaning. But maybe this business about CNF & DNF is an unnecessary complication -- maybe just converting deny to equivalent grant and then constructing the effective equivalent of each grant is enough. – Robert Dodier Jul 09 '17 at 20:42

0 Answers0