Open Monograph Press  1.1
 All Classes Namespaces Functions Variables Groups Pages
AuthorizationDecisionManager.inc.php
1 <?php
26 import('lib.pkp.classes.security.authorization.PolicySet');
27 
28 define('AUTHORIZATION_NOT_APPLICABLE', 0x03);
29 
32  var $_rootPolicySet;
33 
35  var $_authorizationMessages = array();
36 
38  var $_authorizedContext = array();
39 
43  function AuthorizationDecisionManager() {
44  // Instantiate the main policy set we'll add root policies to.
45  $this->_rootPolicySet = new PolicySet(COMBINING_DENY_OVERRIDES);
46  }
47 
48 
49  //
50  // Setters and Getters
51  //
57  function setDecisionIfNoPolicyApplies($decisionIfNoPolicyApplies) {
58  $this->_rootPolicySet->setEffectIfNoPolicyApplies($decisionIfNoPolicyApplies);
59  }
60 
68  function addPolicy($policyOrPolicySet, $addToTop = false) {
69  $this->_rootPolicySet->addPolicy($policyOrPolicySet, $addToTop);
70  }
71 
76  function addAuthorizationMessage($message) {
77  $this->_authorizationMessages[] = $message;
78  }
79 
84  function getAuthorizationMessages() {
85  return $this->_authorizationMessages;
86  }
87 
94  function &getAuthorizedContextObject($assocType) {
95  if (isset($this->_authorizedContext[$assocType])) {
96  return $this->_authorizedContext[$assocType];
97  } else {
98  $nullVar = null;
99  return $nullVar;
100  }
101  }
102 
107  function &getAuthorizedContext() {
109  }
110 
111 
112  //
113  // Public methods
114  //
121  function decide() {
122  // Decide the root policy set which will recursively decide
123  // all nested policy sets and return a single decision.
124  $callOnDeny = null;
125  $decision = $this->_decidePolicySet($this->_rootPolicySet, $callOnDeny);
126  assert($decision !== AUTHORIZATION_NOT_APPLICABLE);
127 
128  // Call the "call on deny" advice
129  if ($decision === AUTHORIZATION_DENY && !is_null($callOnDeny)) {
130  assert(is_array($callOnDeny) && count($callOnDeny) == 3);
131  list($classOrObject, $method, $parameters) = $callOnDeny;
132  $methodCall = array($classOrObject, $method);
133  assert(is_callable($methodCall));
134  call_user_func_array($methodCall, $parameters);
135  }
136 
137  return $decision;
138  }
139 
140 
141  //
142  // Private helper methods
143  //
151  function _decidePolicySet(&$policySet, &$callOnDeny) {
152  // Configure the decision algorithm.
153  $combiningAlgorithm = $policySet->getCombiningAlgorithm();
154  switch($combiningAlgorithm) {
155  case COMBINING_DENY_OVERRIDES:
156  $dominantEffect = AUTHORIZATION_DENY;
157  $overriddenEffect = AUTHORIZATION_PERMIT;
158  break;
159 
160  case COMBINING_PERMIT_OVERRIDES:
161  $dominantEffect = AUTHORIZATION_PERMIT;
162  $overriddenEffect = AUTHORIZATION_DENY;
163  break;
164 
165  default:
166  assert(false);
167  }
168 
169  // Set the default decision.
170  $decision = $policySet->getEffectIfNoPolicyApplies();
171 
172  // The following flag will record when the
173  // overridden decision state is returned by
174  // at least one policy.
175  $decidedByOverriddenEffect = false;
176 
177  // Separated from below for bug #6821.
178  $context =& $this->getAuthorizedContext();
179 
180  // Go through all policies within the policy set
181  // and combine them with the configured algorithm.
182  foreach($policySet->getPolicies() as $policy) {
183  // Treat policies and policy sets differently.
184  switch (true) {
185  case is_a($policy, 'AuthorizationPolicy'):
186  // Make sure that the policy can access the latest authorized context.
187  // NB: The authorized context is set by reference. This means that it
188  // will change globally if changed by the policy which is intended
189  // behavior so that policies can access authorized objects provided
190  // by policies called earlier in the authorization process.
191  $policy->setAuthorizedContext($context);
192 
193  // Check whether the policy applies.
194  if ($policy->applies()) {
195  // If the policy applies then retrieve its effect.
196  $effect = $policy->effect();
197  } else {
198  $effect = AUTHORIZATION_NOT_APPLICABLE;
199  }
200  break;
201 
202  case is_a($policy, 'PolicySet'):
203  // We found a nested policy set.
204  $effect = $this->_decidePolicySet($policy, $callOnDeny);
205  break;
206 
207  default:
208  assert(false);
209  }
210 
211  // Try the next policy if this policy didn't apply.
212  if ($effect === AUTHORIZATION_NOT_APPLICABLE) continue;
213  assert($effect === AUTHORIZATION_PERMIT || $effect === AUTHORIZATION_DENY);
214 
215  // "Deny" decision may cause a message to the end user.
216  if (is_a($policy, 'AuthorizationPolicy') && $effect == AUTHORIZATION_DENY
217  && $policy->hasAdvice(AUTHORIZATION_ADVICE_DENY_MESSAGE)) {
218  $this->addAuthorizationMessage($policy->getAdvice(AUTHORIZATION_ADVICE_DENY_MESSAGE));
219  }
220 
221  // Process the effect.
222  if ($effect === $overriddenEffect) {
223  $decidedByOverriddenEffect = true;
224  } else {
225  // In case of a "deny overrides" we allow a "call-on-deny" advice.
226  if (is_a($policy, 'AuthorizationPolicy') && $dominantEffect == AUTHORIZATION_DENY
227  && $policy->hasAdvice(AUTHORIZATION_ADVICE_CALL_ON_DENY)) {
228  $callOnDeny = $policy->getAdvice(AUTHORIZATION_ADVICE_CALL_ON_DENY);
229  }
230 
231  // Only one dominant effect overrides all other effects
232  // so we don't even have to evaluate other policies.
233  return $dominantEffect;
234  }
235  }
236 
237  // Only return an overridden effect if at least one
238  // policy returned that effect and none returned the
239  // dominant effect.
240  if ($decidedByOverriddenEffect) $decision = $overriddenEffect;
241  return $decision;
242  }
243 }
244 
245 ?>
An ordered list of policies. Policy sets can be added to decision managers like policies. The decision manager will evaluate the contained policies in the order they were added.
setDecisionIfNoPolicyApplies($decisionIfNoPolicyApplies)
addPolicy($policyOrPolicySet, $addToTop=false)
A class that can take a list of authorization policies, apply them to the current authorization reque...