<html><head><meta http-equiv="Content-Type" content="text/html charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><br><div><div>On Nov 12, 2012, at 1:08 AM, Shane Bryzak <<a href="mailto:sbryzak@redhat.com">sbryzak@redhat.com</a>> wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite">
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
<div bgcolor="#FFFFFF" text="#000000">
<div class="moz-cite-prefix">On 11/09/2012 07:57 AM, Boleslaw
Dawidowicz wrote:<br>
</div>
<blockquote cite="mid:85B41740-5111-442A-B2DF-14E8610039EE@redhat.com" type="cite">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<div>On Nov 6, 2012, at 11:10 PM, Shane Bryzak <<a moz-do-not-send="true" href="mailto:sbryzak@redhat.com">sbryzak@redhat.com</a>>
wrote:<br>
<br>
</div>
<blockquote type="cite">
<div>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
<div class="moz-cite-prefix">On 11/07/2012 07:35 AM, Boleslaw
Dawidowicz wrote:<br>
</div>
<blockquote cite="mid:5F7EEC45-6F54-4C08-8318-D5BE0502ADB2@redhat.com" type="cite">
<meta http-equiv="content-type" content="text/html;
charset=UTF-8">
<div>Also +1. It looks really good.</div>
<div><br>
</div>
<div>I assume the strategy to handle operations when two
stores are configured is IdentityManager implementation
area. I wonder if we should make this part also more
flexible in some way. Not really thinking about
IdentityStoreRepository kind of design I had in 1.x as it
is probably a bit too much. However it should be at least
easy to extend DefaultIdentityManager to add some
customizations to how specific operations are handled. Or
we should have something like GenericIdentityManager for
such purpose.</div>
</blockquote>
<br>
This feature (which I've been referring to as partitioning) is
supported by requiring each configured IdentityStore to
provide metadata as to which features are supported, via the
getFeatureSet() method:<br>
<br>
Set<Feature> getFeatureSet();<br>
<br>
Feature is an enum defining all currently supported identity
management operations:<br>
<br>
public enum Feature { createUser, readUser, updateUser,
deleteUser, <br>
createGroup, readGroup, updateGroup,
deleteGroup,<br>
createRole, readRole, updateRole,
deleteRole,<br>
createMembership, readMembership,
updateMembership, deleteMembership,<br>
validateCredential,
updateCredential,<br>
all }<br>
<br>
When an IdentityManager method is invoked, the correct
IdentityStore for the required operation is selected based on
its supported feature set. Here's an example of this, in the
createUser() method:<br>
<br>
<br>
@Override<br>
public User createUser(String name) {<br>
User user = new SimpleUser(name);<br>
IdentityStore store =
getStoreForFeature(Feature.createUser); <br>
store.createUser(getContextFactory().getContext(store), user);<br>
return user;<br>
}<br>
<br>
The getStoreForFeature() method iterates through the
configured stores and returns the one that supports the
specified Feature, in this example Feature.createUser. This
way we can configure multiple stores with one of them
providing user-related operations, one providing group and
role operations, and so forth.<br>
<br>
</div>
</blockquote>
<div><br>
</div>
<div>This will still not cover all use cases.</div>
<div><br>
</div>
<div>You have few stores that support credential validation and
implement some kind of strategy like in pam. I'm not saying that
spi should be more complex - no I think it is rather perfect for
the balance between complexity and features. Just pointing out
that people will need to extend IdentityManager to introduce
some more complex multi store behaviors. Implementation should
be done with it in mind. Avoid private methods and so on.</div>
</blockquote>
<br>
It should be relatively simple to extend the default IdentityManager
and override this behaviour if the developer wants something else.<br>
<br>
<blockquote cite="mid:85B41740-5111-442A-B2DF-14E8610039EE@redhat.com" type="cite"><br>
<blockquote type="cite">
<div>
<blockquote cite="mid:5F7EEC45-6F54-4C08-8318-D5BE0502ADB2@redhat.com" type="cite">
<div><br>
</div>
<div>Looking forward to see your ideas around design of
event handling part. I think it will be critical to truly
pluggable and extendable.<br>
</div>
</blockquote>
<br>
This feature was quite simple. I've opted to go with the
"class as an event" model, where each event type is
represented by a POJO containing the relevant event state.
For example, the following event is raised when a new user is
created:<br>
<br>
public class UserCreatedEvent extends AbstractBaseEvent {<br>
private User user;<br>
<br>
public UserCreatedEvent(User user) {<br>
this.user = user;<br>
}<br>
<br>
public User getUser() {<br>
return user;<br>
}<br>
}<br>
<br>
In this example, the event class contains a reference to the
User that was created. In addition to this, each event class
should extend AbstractBaseEvent which also provides an event
context:<br>
<br>
public abstract class AbstractBaseEvent {<br>
private EventContext context = new EventContext();<br>
<br>
public EventContext getContext() {<br>
return context;<br>
}<br>
}<br>
<br>
The EventContext provides a general purpose mechanism for
passing arbitrary state:<br>
<br>
public class EventContext {<br>
private Map<String,Object> context;<br>
<br>
public Object getValue(String name) {<br>
return context != null ? context.get(name) : null;<br>
}<br>
<br>
public void setValue(String name, Object value) {<br>
if (context == null) {<br>
context = new HashMap<String,Object>();<br>
}<br>
context.put(name, value);<br>
}<br>
<br>
public boolean contains(String name) {<br>
return context != null &&
context.containsKey(name);<br>
}<br>
<br>
public boolean isEmpty() {<br>
return context == null || context.isEmpty();<br>
}<br>
}<br>
<br>
It is via the EventContext that we can pass IdentityStore
implementation-specific state (for example the actual entity
bean instance that was persisted to the database in the case
of a JPA backed IdentityStore) or any additional state that
might be relevant to the event.<br>
<br>
As for bridging the event itself, the
IdentityStoreInvocationContext provides access to the
EventBridge via the getEventBridge() method:<br>
<br>
EventBridge getEventBridge();<br>
<br>
This is an extremely simple interface declaring just a single
method:<br>
<br>
public interface EventBridge {<br>
void raiseEvent(Object event);<br>
}<br>
<br>
The idea here is that you can provide an EventBridge
implementation tailored for the environment that you're
running in. In an EE6 environment, this is a piece of cake as
we just pass the event straight on through to the CDI event
bus.<br>
<br>
That pretty much sums up event handling.<br>
</div>
</blockquote>
<div><br>
</div>
<div>Looks pretty simple indeed. What I had in mind was something
with post and per operation events but I guess that if needed
those would fit this design right? It more seems about defining
valid event types and raising points.</div>
<div><br>
</div>
<div>I would like to be able to have access to full context - not
just the single store. Use case is for example to synchronize or
propagate objects from one identity store into other one in case
of specific operation.</div>
</blockquote>
<br>
Pre and post operation events will fit this design easily, yes.
Propagation should be possible by creating a 'wrapper' IdentityStore
that synchronizes identity state from one of the wrapped stores into
another (I wouldn't base this on the event API). I'm not quite
convinced that synchronization is such a useful thing though, could
you enlighten me with a use case where this would be required?<br>
<br>
<br></div></blockquote><div><br></div><div>Simple use case is where you have store with username/password and some basic group information. You want to include it but don't need to query it all the time. This matches minimal LDAP integration in some scenarios. What you can do is to just perform authentication against this store and do initial sync of some attributes and group memberships into DB. Then for single attributes it can be still desired to propagate new values to the original store. Using events for new values propagation and some wrapper Identity store for sync during auth can be more efficient way then setting up whole identity store federation approach. </div></div></body></html>