|
|
|
|
@@ -1076,10 +1076,10 @@ More about error handling shown in above example, see section
|
|
|
|
|
|
|
|
|
|
[[sm-security]]
|
|
|
|
|
== State Machine Security
|
|
|
|
|
Security features are build atop of functionality from a _Spring
|
|
|
|
|
Security_. Security features are handy when it is required to protect
|
|
|
|
|
part of a state machine execution and interaction with it. More
|
|
|
|
|
detailed info can be found from section <<sm-security-details>>.
|
|
|
|
|
Security features are build atop of functionality from a
|
|
|
|
|
{spring-security-site}[_Spring Security_]. Security features are
|
|
|
|
|
handy when it is required to protect part of a state machine
|
|
|
|
|
execution and interaction with it.
|
|
|
|
|
|
|
|
|
|
[IMPORTANT]
|
|
|
|
|
====
|
|
|
|
|
@@ -1088,13 +1088,26 @@ we don't go into details of how overall security framework works. For
|
|
|
|
|
this read _Spring Security_ reference documentation.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
First level of defence with a security is naturally protecting events
|
|
|
|
|
which really are a driver from user point of view what is going to
|
|
|
|
|
happen in a state machine. More fine grained security settings can
|
|
|
|
|
then be defined for transitions and actions. This can be think of like
|
|
|
|
|
allowing an employee to access a building, walk around it and then
|
|
|
|
|
giving more detailed access rights to enter different rooms and allow
|
|
|
|
|
to switch lights on and off while being on those rooms. If you trust
|
|
|
|
|
your users then event security may be all you need, if you don't, then
|
|
|
|
|
more detailed security needs to be applied.
|
|
|
|
|
|
|
|
|
|
More detailed info can be
|
|
|
|
|
found from section <<sm-security-details>>.
|
|
|
|
|
|
|
|
|
|
[TIP]
|
|
|
|
|
====
|
|
|
|
|
For complete example, see sample <<statemachine-examples-security>>.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
=== Configuring Security
|
|
|
|
|
All generic configurations for security are done from
|
|
|
|
|
All generic configurations for security are done in
|
|
|
|
|
`SecurityConfigurer` which is obtained from
|
|
|
|
|
`StateMachineConfigurationConfigurer`. Security is disabled on
|
|
|
|
|
default even if _Spring Security_ classes are
|
|
|
|
|
@@ -1172,38 +1185,125 @@ Attributes are easier to use but are relatively limited in terms of
|
|
|
|
|
functionality. Expressions provide more features but are a little bit
|
|
|
|
|
of harder to use.
|
|
|
|
|
|
|
|
|
|
This section is splitted into different subsections because we're
|
|
|
|
|
re-using something from a _Spring Security_ and then do custom
|
|
|
|
|
attributes and expression handling for events and transitions.
|
|
|
|
|
|
|
|
|
|
==== Generic Attribute Usage
|
|
|
|
|
On default `AccessDecisionManager` instances for events and
|
|
|
|
|
transitions use a `RoleVoter`, meaning you can use role attributes.
|
|
|
|
|
transitions both use a `RoleVoter`, meaning you can use role attributes
|
|
|
|
|
familiar from _Spring Security_.
|
|
|
|
|
|
|
|
|
|
For attributes we have 3 different comparison types, `ANY`, `ALL` and
|
|
|
|
|
`MAJORITY` which maps into voters `AffirmativeBased`, `UnanimousBased`
|
|
|
|
|
and `ConsensusBased` respectively.
|
|
|
|
|
`MAJORITY` which maps into default access decision managers
|
|
|
|
|
`AffirmativeBased`, `UnanimousBased` and `ConsensusBased` respectively.
|
|
|
|
|
If custom `AccessDecisionManager` has been defined, comparison type is
|
|
|
|
|
effectively discarded as it's only used to create a default manager.
|
|
|
|
|
|
|
|
|
|
==== Generic Expression Usage
|
|
|
|
|
Security expressions needs to return either _TRUE_ or _FALSE_.
|
|
|
|
|
|
|
|
|
|
The base class for expression root objects is
|
|
|
|
|
`SecurityExpressionRoot`. This provides some common expressions which
|
|
|
|
|
are available in both transition and event security.
|
|
|
|
|
|
|
|
|
|
[[common-expressions]]
|
|
|
|
|
.Common built-in expressions
|
|
|
|
|
|===
|
|
|
|
|
| Expression | Description
|
|
|
|
|
|
|
|
|
|
| `hasRole([role])`
|
|
|
|
|
| Returns `true` if the current principal has the specified role. By
|
|
|
|
|
default if the supplied role does not start with 'ROLE_' it will be
|
|
|
|
|
added. This can be customized by modifying the `defaultRolePrefix` on
|
|
|
|
|
`DefaultWebSecurityExpressionHandler`.
|
|
|
|
|
|
|
|
|
|
| `hasAnyRole([role1,role2])`
|
|
|
|
|
| Returns `true` if the current principal has any of the supplied
|
|
|
|
|
roles (given as a comma-separated list of strings). By default if the
|
|
|
|
|
supplied role does not start with 'ROLE_' it will be added. This can
|
|
|
|
|
be customized by modifying the `defaultRolePrefix` on
|
|
|
|
|
`DefaultWebSecurityExpressionHandler`.
|
|
|
|
|
|
|
|
|
|
| `hasAuthority([authority])`
|
|
|
|
|
| Returns `true` if the current principal has the specified authority.
|
|
|
|
|
|
|
|
|
|
| `hasAnyAuthority([authority1,authority2])`
|
|
|
|
|
| Returns `true` if the current principal has any of the supplied
|
|
|
|
|
roles (given as a comma-separated list of strings)
|
|
|
|
|
|
|
|
|
|
| `principal`
|
|
|
|
|
| Allows direct access to the principal object representing the
|
|
|
|
|
current user
|
|
|
|
|
|
|
|
|
|
| `authentication`
|
|
|
|
|
| Allows direct access to the current `Authentication` object obtained
|
|
|
|
|
from the `SecurityContext`
|
|
|
|
|
|
|
|
|
|
| `permitAll`
|
|
|
|
|
| Always evaluates to `true`
|
|
|
|
|
|
|
|
|
|
| `denyAll`
|
|
|
|
|
| Always evaluates to `false`
|
|
|
|
|
|
|
|
|
|
| `isAnonymous()`
|
|
|
|
|
| Returns `true` if the current principal is an anonymous user
|
|
|
|
|
|
|
|
|
|
| `isRememberMe()`
|
|
|
|
|
| Returns `true` if the current principal is a remember-me user
|
|
|
|
|
|
|
|
|
|
| `isAuthenticated()`
|
|
|
|
|
| Returns `true` if the user is not anonymous
|
|
|
|
|
|
|
|
|
|
| `isFullyAuthenticated()`
|
|
|
|
|
| Returns `true` if the user is not an anonymous or a remember-me user
|
|
|
|
|
|
|
|
|
|
| `hasPermission(Object target, Object permission)`
|
|
|
|
|
| Returns `true` if the user has access to the provided target for the
|
|
|
|
|
given permission. For example, `hasPermission(domainObject, 'read')`
|
|
|
|
|
|
|
|
|
|
| `hasPermission(Object targetId, String targetType, Object
|
|
|
|
|
permission)`
|
|
|
|
|
| Returns `true` if the user has access to the provided target for the
|
|
|
|
|
given permission. For example, `hasPermission(1,
|
|
|
|
|
'com.example.domain.Message', 'read')`
|
|
|
|
|
|===
|
|
|
|
|
|
|
|
|
|
==== Event Attributes
|
|
|
|
|
Event id can be matched by using prefix _EVENT__. For example matching
|
|
|
|
|
event `A` would match with attribte _EVENT_A_.
|
|
|
|
|
Event id can be matched by using prefix `EVENT_`. For example matching
|
|
|
|
|
event `A` would match with attribte `EVENT_A`.
|
|
|
|
|
|
|
|
|
|
==== Event Expressions
|
|
|
|
|
For generic options for expressions, see _Spring Security_ docs. We
|
|
|
|
|
inherit from `SecurityExpressionRoot` and options from there are
|
|
|
|
|
available.
|
|
|
|
|
The base class for expression root object for event is
|
|
|
|
|
`EventSecurityExpressionRoot`. This provides access to a `Message`
|
|
|
|
|
object which is passed around with eventing.
|
|
|
|
|
|
|
|
|
|
.Event expressions
|
|
|
|
|
|===
|
|
|
|
|
| Expression | Description
|
|
|
|
|
|
|
|
|
|
| `hasEvent(Object event)`
|
|
|
|
|
| Returns `true` if the event matches given event.
|
|
|
|
|
|
|
|
|
|
|===
|
|
|
|
|
|
|
|
|
|
==== Transition Attributes
|
|
|
|
|
Matching transition sources and targets, use prefixes
|
|
|
|
|
_TRANSITION_SOURCE__ and _TRANSITION_TARGET__ respectively.
|
|
|
|
|
`TRANSITION_SOURCE_` and `TRANSITION_TARGET_` respectively.
|
|
|
|
|
|
|
|
|
|
==== Transition Expressions
|
|
|
|
|
For generic options for expressions, see _Spring Security_ docs. We
|
|
|
|
|
inherit from `SecurityExpressionRoot` and options from there are
|
|
|
|
|
available.
|
|
|
|
|
The base class for expression root object for transition is
|
|
|
|
|
`TransitionSecurityExpressionRoot`. This provides access to a
|
|
|
|
|
`Transition`
|
|
|
|
|
object which is passed around for transition changes.
|
|
|
|
|
|
|
|
|
|
.Transition expressions
|
|
|
|
|
|===
|
|
|
|
|
| Expression | Description
|
|
|
|
|
|
|
|
|
|
| `hasSource(Object source)`
|
|
|
|
|
| Returns `true` if the transition source matches given source.
|
|
|
|
|
|
|
|
|
|
| `hasTarget(Object target)`
|
|
|
|
|
| Returns `true` if the transition target matches given target.
|
|
|
|
|
|
|
|
|
|
|===
|
|
|
|
|
|
|
|
|
|
[[sm-security-details]]
|
|
|
|
|
=== Understanding Security
|
|
|
|
|
@@ -1212,14 +1312,17 @@ state machine. Not really something you'd need to know but it is
|
|
|
|
|
always better to be transparent instead of hiding all the magic what
|
|
|
|
|
happens behind a scenes.
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
|
|
|
|
Security only makes sense if _State Machine_ is executed in a wallet
|
|
|
|
|
garden where user don't have direct access to the application thus
|
|
|
|
|
could modify Spring Security's `SecurityContext` hold in a thread
|
|
|
|
|
local. If user controls the jvm, then effectively there is no security
|
|
|
|
|
at all.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Integration point for security is done with a
|
|
|
|
|
`StateMachineInterceptor` which is then added automatically into a
|
|
|
|
|
<<sm-interceptor,_StateMachineInterceptor_>> which is then added automatically into a
|
|
|
|
|
state machine if security is enabled. Specific class is a
|
|
|
|
|
`StateMachineSecurityInterceptor` which intercepts events and
|
|
|
|
|
transitions. This interceptor then consults Spring Security's
|
|
|
|
|
|