SudoService (interface)

Allows a block of code to be executed within an arbitrary ExecutionContext , allowing the who, when and where to be temporarily switched.

Most typically this service is used to temporarily change the &qout;who", that is the user reported by the UserService 's UserService#currentUser() getUser() - hence the name SudoService. But the user’s locale and timezome can also be changed, as well as the time reported by org.apache.isis.applib.services.clock.ClockService .

The primary use case for this service is for fixture scripts and integration tests.

API

SudoService.java
interface SudoService {
  RoleMemento ACCESS_ALL_ROLE;     (1)
  T call(final UnaryOperator<ExecutionContext> sudoMapper, final Callable<T> supplier)     (2)
  void run(final UnaryOperator<ExecutionContext> sudoMapper, final ThrowingRunnable runnable)     (3)
}
1 ACCESS_ALL_ROLE

If included in the list of roles, then will disable security checks (can view and use all object members).

2 call(UnaryOperator, Callable)

Executes the supplied Callable block, within the provided ExecutionContext .

3 run(UnaryOperator, ThrowingRunnable)

Executes the supplied Callable block, within the provided ExecutionContext .

Members

ACCESS_ALL_ROLE

If included in the list of roles, then will disable security checks (can view and use all object members).

call(UnaryOperator, Callable)

Executes the supplied Callable block, within the provided ExecutionContext .

run(UnaryOperator, ThrowingRunnable)

Executes the supplied Callable block, within the provided ExecutionContext .

Implementation

The core framework provides a default implementation of this service (o.a.i.core.runtimeservices.sudo.SudoServiceDefault).

Usage

This domain service is useful both for integration testing and while running fixture scripts.

For example, to integration test a workflow system, whereby objects are moved from one user to another, it is helpful to switch the effective user to verify that the task was assigned correctly.

Or, this fixture script uses the SudoService to set up ToDoItem objects:

protected void execute(final ExecutionContext ec) {
    ...
    ExecutionContext ec =
        ExecutionContext.ofUserWithSystemDefaults(
            UserMemento.ofName("joe"));

    sudoService.sudo(ec, () -> wrap(toDoItem).completed());
    ...
}

Interaction with Shiro

When sudo(…​) is called the "effective user" is reported by UserService. However, it does not propagate through to the Shiro security mechanism, which continue to be evaluated according to the permissions of the current user.

This can be a problem in certain use cases. For example if running a fixture script (which uses the WrapperFactory) from within an implementation of UserRegistrationService, this is likely to result in HiddenExceptions being thrown because there is no effective user.

In such cases, permission checking can simply be disabled by specifying SudoService.ACCESS_ALL_ROLE as one of the roles. For example:

protected void execute(final ExecutionContext ec) {
    ...
    ExecutionContext ec =
        ExecutionContext.ofUserWithSystemDefaults(
            UserMemento.ofNameAndRoleNames("joe", SudoService.ACCESS_ALL_ROLE)));

    sudoService.sudo(ec, () -> wrap(toDoItem).completed());
    ...
}

In the future this service may be used more deeply, eg to propagate permissions through to the Shiro security mechanism also.

See also

  • ExecutionContext

  • SudoServiceListener

  • the InteractionFactory is a lower-level framework (non-API) service, that provides the ability to programmatically start a new "interaction"; these can be nested.