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 specificSubject
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.