<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div class="moz-cite-prefix">Shane - the configuration API is
important. Go ahead.<br>
<br>
On 11/06/2012 10:34 AM, Jason Porter wrote:<br>
</div>
<blockquote
cite="mid:CAF9TksNcbOV=RmEEbDsdtyGtfMf0Y5i_N1QqLNY9YC49M6LpRg@mail.gmail.com"
type="cite">+1 at all sounds good to me.
<div class="gmail_extra"><br>
<br>
<div class="gmail_quote">On Tue, Nov 6, 2012 at 3:08 AM, Shane
Bryzak <span dir="ltr"><<a moz-do-not-send="true"
href="mailto:sbryzak@redhat.com" target="_blank">sbryzak@redhat.com</a>></span>
wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex">Hey guys,<br>
<br>
For the past few days I've been wracking my brain trying to
come up with<br>
a design for the IDM configuration API that doesn't suck,
and allows<br>
easy configuration in both Java SE and EE environments while
maintaining<br>
separation between the API and implementation. I think I've
come up<br>
with something that's quite flexible and extensible, so I'd
like to run<br>
it past everyone for some feedback.<br>
<br>
The starting point for configuring the Identity Management
API is a<br>
concrete class called IdentityConfiguration:<br>
<br>
public class IdentityConfiguration {<br>
private List<IdentityStoreConfiguration>
configuredStores = new<br>
ArrayList<IdentityStoreConfiguration>();<br>
public List<IdentityStoreConfiguration>
getConfiguredStores() {<br>
return configuredStores;<br>
}<br>
public void
addStoreConfiguration(IdentityStoreConfiguration config) {<br>
configuredStores.add(config);<br>
}<br>
}<br>
<br>
This class simply provides a holder for one or more<br>
IdentityStoreConfiguration(s), an abstract class that
provides the<br>
basics for configuring an IdentityStore:<br>
<br>
public abstract class IdentityStoreConfiguration {<br>
private final Map<String,String> properties = new<br>
HashMap<String,String>();<br>
private final Set<Feature> supportedFeatures =
new HashSet<Feature>();<br>
public Set<Feature> getSupportedFeatures() {<br>
return supportedFeatures;<br>
}<br>
public void addSupportedFeature(Feature feature) {<br>
supportedFeatures.add(feature);<br>
}<br>
public void removeSupportedFeature(Feature feature) {<br>
supportedFeatures.remove(feature);<br>
}<br>
public void setProperty(String name, String value) {<br>
properties.put(name, value);<br>
}<br>
public String getPropertyValue(String name) {<br>
return properties.get(name);<br>
}<br>
}<br>
<br>
Each IdentityStore implementation (such as JPAIdentityStore,<br>
LDAPIdentityStore, etc) should have a corresponding<br>
IdentityStoreConfiguration implementation that provides an
API for<br>
setting specific property values for its IdentityStore. For
example,<br>
this is the one for JPAIdentityStore which allows a number
of entity<br>
classes to be set:<br>
<br>
public class JPAIdentityStoreConfiguration extends<br>
IdentityStoreConfiguration {<br>
private Class<?> identityClass;<br>
private Class<?> membershipClass;<br>
private Class<?> credentialClass;<br>
private Class<?> attributeClass;<br>
public Class<?> getIdentityClass() {<br>
return identityClass;<br>
}<br>
public void setIdentityClass(Class<?>
identityClass) {<br>
this.identityClass = identityClass;<br>
}<br>
public Class<?> getCredentialClass() {<br>
return credentialClass;<br>
}<br>
public void setCredentialClass(Class<?>
credentialClass) {<br>
this.credentialClass = credentialClass;<br>
}<br>
public Class<?> getMembershipClass() {<br>
return membershipClass;<br>
}<br>
public void setMembershipClass(Class<?>
membershipClass) {<br>
this.membershipClass = membershipClass;<br>
}<br>
public Class<?> getAttributeClass() {<br>
return attributeClass;<br>
}<br>
public void setAttributeClass(Class<?>
attributeClass) {<br>
this.attributeClass = attributeClass;<br>
}<br>
}<br>
<br>
The IdentityStore-specific configurations are intended to be
part of the<br>
API, so they should be placed in the API module within the<br>
org.picketlink.idm.config package.<br>
<br>
After creating an IdentityConfiguration, you can create an<br>
IdentityManager and provide the configuration via the
bootstrap() method:<br>
<br>
IdentityConfiguration identityConfig = new
IdentityConfiguration();<br>
JPAIdentityStoreConfiguration storeConfig = new<br>
JPAIdentityStoreConfiguration();<br>
// snip storeConfig configuration<br>
<br>
identityConfig.addStoreConfiguration(storeConfig);<br>
<br>
IdentityManager identityManager = new
DefaultIdentityManager();<br>
identityManager.bootstrap(identityConfig, new<br>
DefaultIdentityStoreInvocationContextFactory(null));<br>
<br>
The reason we use bootstrap() instead of performing
configuration in the<br>
constructor is so the developer has a chance to override the<br>
IdentityStoreFactory. This is an SPI interface that defines
a couple of<br>
methods which are used to control which
IdentityStoreConfigurations<br>
correspond to which IdentityStore.<br>
<br>
public interface IdentityStoreFactory {<br>
/**<br>
* Creates an instance of an IdentityStore using the
provided<br>
configuration<br>
*<br>
* @param config<br>
* @return<br>
*/<br>
IdentityStore
createIdentityStore(IdentityStoreConfiguration config);<br>
<br>
/**<br>
* Maps specific implementations of
IdentityStoreConfiguration to a<br>
corresponding<br>
* IdentityStore implementation.<br>
*<br>
* @param configClass<br>
* @param storeClass<br>
*/<br>
void mapConfiguration(Class<? extends
IdentityStoreConfiguration><br>
configClass,<br>
Class<? extends IdentityStore>
storeClass);<br>
}<br>
<br>
By default, the DefaultIdentityManager will create and use
an instance<br>
of DefaultIdentityStoreFactory - this factory knows about
all the<br>
built-in IdentityStore implementations and can create
IdentityStore<br>
instances based on their corresponding
IdentityStoreConfiguration. If a<br>
developer wished to provide their own IdentityStore though
(or override<br>
the behaviour of one of the built-in ones) then we need to
provide a<br>
hook to allow them to override the default factory with
their own. This<br>
is provided by the IdentityManager.setIdentityStoreFactory()
method - by<br>
invoking this method with a new IdentityStoreFactory the
developer can<br>
provide an alternative factory for creating IdentityStore
instances<br>
based on non built-in configurations before bootstrap() is
called.<br>
<br>
Here's an example where the built-in identity stores are
supplemented<br>
with support for a native Hibernate-based identity store:<br>
<br>
IdentityManager identityManager = new
DefaultIdentityManager();<br>
<br>
DefaultIdentityStoreFactory factory = new
DefaultIdentityStoreFactory();<br>
factory.mapConfiguration(HibernateIdentityStoreConfiguration.class,<br>
HibernateIdentityStore.class);<br>
identityManager.setIdentityStoreFactory(factory);<br>
<br>
identityManager.bootstrap(identityConfig, new<br>
DefaultIdentityStoreInvocationContextFactory(null));<br>
<br>
The last piece of the puzzle is the second parameter to the
bootstrap()<br>
method. This parameter should be an instance of<br>
IdentityStoreInvocationContextFactory (I know, it's a
mouthful), an SPI<br>
interface that declares a single method:<br>
<br>
public interface IdentityStoreInvocationContextFactory {<br>
IdentityStoreInvocationContext getContext(IdentityStore
store);<br>
}<br>
<br>
The implementation of this interface is responsible for
creating<br>
IdentityStoreInvocationContext instances, which are passed
as a<br>
parameter value to pretty much all of the IdentityStore
methods and<br>
allow the IdentityStore implementation to be shared between
multiple<br>
threads (i.e. a stateless design). The
IdentityStoreInvocationContext<br>
is responsible for providing the IdentityStore with any
state (besides<br>
the IdentityStore configuration) required to execute its
requested<br>
operation. It also provides a gateway to the event bridge,
which allows<br>
events to be raised during an IdentityStore operation and
propagated to<br>
any environment, such as the CDI event bus. The<br>
IdentityStoreInvocationContext implementation/s are
currently still a<br>
work in progress, however the basic API should not change.<br>
<br>
This essentially wraps up the description of the
configuration API. What<br>
I haven't touched upon yet are the builders (classes that
parse a<br>
configuration source such as a file to create an<br>
IdentityStoreConfiguration) however I'm hoping Anil that you
might want<br>
to have a go at this.<br>
<br>
With these changes comes a small TO-DO list:<br>
<br>
1) IdentityStoreConfiguration implementations should be
created in the<br>
API module within the org.picketlink.idm.config package for
each of the<br>
IdentityStore implementations (we already have one for LDAP,
but it<br>
needs to be moved and possibly renamed to
LDAPIdentityStoreConfiguration<br>
for consistency).<br>
<br>
2) During the course of the refactor I had to provide
workarounds for<br>
many of the tests, and I also managed to totally break quite
a few<br>
others. The test suite needs a quick review to see why the
tests are<br>
broken, and once configurations are provided for the other
identity<br>
stores the workarounds can be removed.<br>
<br>
3) The getFeatureSet() method should be correctly
implemented for all<br>
IdentityStores. With the change to the configuration API we
now have<br>
proper support for partitioning, and it's important that
this method<br>
accurately reflect the underlying capabilities of the
IdentityStore<br>
implementation for this feature to work correctly.<br>
<br>
Thanks for listening, and I'm looking forward to getting
some feedback<br>
on this!<br>
<br>
Shane<br>
_______________________________________________<br>
security-dev mailing list<br>
<a moz-do-not-send="true"
href="mailto:security-dev@lists.jboss.org">security-dev@lists.jboss.org</a><br>
<a moz-do-not-send="true"
href="https://lists.jboss.org/mailman/listinfo/security-dev"
target="_blank">https://lists.jboss.org/mailman/listinfo/security-dev</a><br>
</blockquote>
</div>
<br>
<br clear="all">
<div><br>
</div>
-- <br>
Jason Porter<br>
<a moz-do-not-send="true"
href="http://lightguard-jp.blogspot.com" target="_blank">http://lightguard-jp.blogspot.com</a><br>
<a moz-do-not-send="true" href="http://twitter.com/lightguardjp"
target="_blank">http://twitter.com/lightguardjp</a><br>
<br>
Software Engineer<br>
Open Source Advocate<br>
<br>
PGP key id: 926CCFF5<br>
PGP key available at: <a moz-do-not-send="true"
href="http://keyserver.net" target="_blank">keyserver.net</a>,
<a moz-do-not-send="true" href="http://pgp.mit.edu"
target="_blank">pgp.mit.edu</a><br>
</div>
<br>
</blockquote>
</body>
</html>