Skip to content

Commit 9ee70f3

Browse files
committed
feat(auth): define AuthorizationService#setRootRule(.) to allow cycles [PPUC-318]
- provided for flexibility for rules that for some not so good reason depend on the service singleton itself
1 parent b84e40e commit 9ee70f3

File tree

3 files changed

+81
-5
lines changed

3 files changed

+81
-5
lines changed

assemblies/pentaho-solutions/src/main/resources/pentaho-solutions/system/pentahoObjects.spring.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,11 @@ not directly from the PentahoObjectFactory. -->
368368
<bean id="authorizationService"
369369
class="org.pentaho.platform.engine.security.authorization.core.CachingAuthorizationService">
370370
<constructor-arg ref="authorizationActionService" />
371-
<constructor-arg ref="rootAuthorizationRule" />
372371
<constructor-arg ref="authorizationDecisionCache" />
372+
<!-- Setting the rootRule as a property, instead of as a constructor arg, allows rules themselves to depend on the
373+
authorizationService. While this does not appear to be a useful scenario, it is provided for flexibility, and
374+
might avoid some third-party usage issues implementing rules. -->
375+
<property name="rootRule" ref="rootAuthorizationRule" />
373376
<pen:publish as-type="INTERFACES" />
374377
</bean>
375378

core/src/main/java/org/pentaho/platform/engine/security/authorization/core/AuthorizationService.java

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,29 @@
2626
import org.pentaho.platform.engine.security.authorization.core.decisions.DefaultAuthorizationDecision;
2727
import org.pentaho.platform.engine.security.authorization.core.exceptions.AuthorizationRequestCycleException;
2828
import org.pentaho.platform.engine.security.authorization.core.exceptions.AuthorizationRequestUndefinedActionException;
29+
import org.pentaho.platform.engine.security.authorization.core.rules.AbstractAuthorizationRule;
2930
import org.springframework.util.Assert;
3031

3132
import java.util.ArrayDeque;
3233
import java.util.Deque;
3334
import java.util.Objects;
3435
import java.util.Optional;
3536

37+
/**
38+
* The {@code AuthorizationService} class is the default implementation of the {@link IAuthorizationService} interface.
39+
* <p>
40+
* It performs authorization evaluations based on a root authorization rule, which may delegate to other rules as
41+
* needed.
42+
* <p>
43+
* The implementation is thread-safe, as each authorization evaluation is performed within its own
44+
* {@link AuthorizationContext}, which tracks the evaluation state for that specific request.
45+
* Of course, the thread-safety of the overall service also depends on the thread-safety of the provided authorization
46+
* rules.
47+
* <p>
48+
* One exception is setting the root rule, using {@link #setRootRule(IAuthorizationRule)} which is not thread-safe. Care
49+
* must be taken to avoid inconsistent decisions in concurrent evaluations. This is intended to be used during
50+
* application initialization.
51+
*/
3652
public class AuthorizationService implements IAuthorizationService {
3753

3854
private static final Log logger = LogFactory.getLog( AuthorizationService.class );
@@ -207,17 +223,45 @@ protected IAuthorizationDecision getDefaultDecision( @NonNull IAuthorizationRequ
207223
}
208224
}
209225

226+
/**
227+
* An authorization rule that always abstains. Used as a default root rule if none is provided.
228+
*/
229+
private static class AbstainAuthorizationRule extends AbstractAuthorizationRule<IAuthorizationRequest> {
230+
231+
@NonNull
232+
@Override
233+
public Class<IAuthorizationRequest> getRequestType() {
234+
return IAuthorizationRequest.class;
235+
}
236+
237+
@NonNull
238+
@Override
239+
public Optional<IAuthorizationDecision> authorize( @NonNull IAuthorizationRequest request,
240+
@NonNull IAuthorizationContext context ) {
241+
return abstain();
242+
}
243+
}
244+
210245
@NonNull
211246
private final IAuthorizationActionService actionService;
212247

213248
@NonNull
214-
private final IAuthorizationRule<? extends IAuthorizationRequest> rootRule;
249+
private IAuthorizationRule<? extends IAuthorizationRequest> rootRule;
250+
251+
/**
252+
* Constructs an instance of the authorization service with a default root rule that always abstains.
253+
*
254+
* @param actionService The service providing access to authorization actions.
255+
*/
256+
public AuthorizationService( @NonNull IAuthorizationActionService actionService ) {
257+
this( actionService, new AbstainAuthorizationRule() );
258+
}
215259

216260
/**
217261
* Constructs an instance of the authorization service with a given root rule.
218262
*
219263
* @param actionService The service providing access to authorization actions.
220-
* @param rootRule The root authorization rule.
264+
* @param rootRule The root authorization rule.
221265
*/
222266
public AuthorizationService( @NonNull IAuthorizationActionService actionService,
223267
@NonNull IAuthorizationRule<? extends IAuthorizationRequest> rootRule ) {
@@ -263,17 +307,30 @@ protected AuthorizationContext createContext( @NonNull IAuthorizationOptions opt
263307
* @return The root rule.
264308
*/
265309
@NonNull
266-
protected final IAuthorizationRule<? extends IAuthorizationRequest> getRootRule() {
310+
public final IAuthorizationRule<? extends IAuthorizationRequest> getRootRule() {
267311
return rootRule;
268312
}
269313

314+
/**
315+
* Sets the root authorization rule.
316+
* <p>
317+
* Warning: changing the root rule at runtime may lead to inconsistent authorization decisions if there are
318+
* concurrent authorization evaluations. This method should only be used during application initialization.
319+
*
320+
* @param rootRule The root rule.
321+
*/
322+
public final void setRootRule( @NonNull IAuthorizationRule<? extends IAuthorizationRequest> rootRule ) {
323+
Assert.notNull( rootRule, "Argument 'rootRule' is required" );
324+
this.rootRule = rootRule;
325+
}
326+
270327
/**
271328
* Gets the authorization action service.
272329
*
273330
* @return The action service.
274331
*/
275332
@NonNull
276-
protected IAuthorizationActionService getActionService() {
333+
protected final IAuthorizationActionService getActionService() {
277334
return actionService;
278335
}
279336
}

core/src/main/java/org/pentaho/platform/engine/security/authorization/core/CachingAuthorizationService.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,27 @@ public class CachingAuthorizationService extends AuthorizationService {
2929
@NonNull
3030
private final IAuthorizationDecisionCache decisionCache;
3131

32+
/**
33+
* Constructs an instance of the authorization service with a default root rule that always abstains.
34+
*
35+
* @param actionService The service providing access to authorization actions.
36+
* @param decisionCache The cache for authorization decisions.
37+
*/
38+
public CachingAuthorizationService( @NonNull IAuthorizationActionService actionService,
39+
@NonNull IAuthorizationDecisionCache decisionCache ) {
40+
super( actionService );
41+
42+
Assert.notNull( decisionCache, "Argument 'decisionCache' is required" );
43+
44+
this.decisionCache = decisionCache;
45+
}
46+
3247
/**
3348
* Constructs an instance of the authorization service with a given root rule.
3449
*
3550
* @param actionService The service providing access to authorization actions.
3651
* @param rootRule The root authorization rule.
52+
* @param decisionCache The cache for authorization decisions.
3753
*/
3854
public CachingAuthorizationService( @NonNull IAuthorizationActionService actionService,
3955
@NonNull IAuthorizationRule<? extends IAuthorizationRequest> rootRule,

0 commit comments

Comments
 (0)