<html>
<head>
<meta content="text/html; charset=windows-1252"
http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div class="moz-cite-prefix">Hi Bill,<br>
<br>
few notes from me. I am mostly adding the scenarios, which will be
good to support/improve or which we should still keep supporting
after refactoring IMO. Sorry for bit longer email :)<br>
<br>
Note: When I talk "LDAP", I usually mean "LDAP or any other 3rd
party federation storage"<br>
<br>
<br>
Persistent vs. Transient modes<br>
----------------------------------------<br>
<br>
I think we should still keep some support for "persistent" mode
(the case when user is imported to Keycloak local DB).<br>
<br>
Assuming the scenario:<br>
- john is imported from LDAP with "unsynced" mode<br>
- john changes his password in account management. LDAP is
unsynced (read-only), so the password needs to be changed in
Keycloak storage<br>
- After server restart, john should have set the new password, not
the old one from LDAP. Hence the only possibility seems to keep
support for "import" this user into Keycloak DB and have user
persistent.<br>
<br>
The similar situation applies for any update of user, which can't
be saved back to LDAP (either because LDAP is read-only or because
it doesn't support store the keycloak metadata like social links,
consents, required actions etc)<br>
<br>
Regarding this, I am thinking about possible import modes like:<br>
1) Persistent : User attributes from LDAP are imported into
Keycloak DB (same behaviour like now)<br>
<br>
2) Transient : User attributes from LDAP are not imported into
Keycloak DB, but just cached locally. Any updates of the user are
either dropped after server restart or disallowed. For example:
Maybe consents can be just dropped, but some other things like
requiredRoles should be rather disallowed to change? For example
if admin adds some requiredAction to user "john", the
requiredAction shouldn't disappear after server restart. This
would be a security hole IMO. So in that case, if admin tries to
manually add requiredAction to such user, the exception will be
thrown so admin is aware that this is not supported. Anyway, for
support any writing to cache, the cache will need to be replicated
though (for example: user consent saved into cache on node1 should
be visible on node2 as well).<br>
<br>
3) Hybrid : LDAP user is not imported into Keycloak DB
immediatelly. They are imported "on-demand" after user is updated
and the update can't be saved to LDAP<br>
<br>
<br>
Caching<br>
-----------<br>
<br>
I hope new SPI will allow us more flexibility to support various
scenarios. It will be good to support at least those IMO:<br>
<br>
1) Keep the option we have now (LDAP user data are always read
from "online" LDAP, but other user data are cached, so no need to
read them from Keycloak DB)<br>
<br>
2) Option to allow support for caching of LDAP data even if mode
is "persistent" . I want LDAP users imported into Keycloak DB, but
still I don't want to always "validate" user in LDAP during each
request like it's now. This will be good for people, who prefer
performance rather then always seeing latest LDAP data.<br>
<br>
3) I agree that will be cool to support different expiration time
based if it's LDAP user or just Keycloak-local user. Infinispan
allows it though with something like : cache.put("ldapuser",
ldapUser, 60, TimeUnit.SECONDS);<br>
<br>
<br>
<br>
Transactions and 3rd party updates<br>
-----------------------------------------------<br>
<br>
- Will be good to improve registration of user to LDAP. Ideally
during registration new user to LDAP, we should allow to send all
data at once. (currently UserFederationProvider.register supports
sending just username). Also we should allow to specify if
register to 3rd party provider should be done *before* or *after*
the registration to Keycloak local DB. For details, see
<a class="moz-txt-link-freetext" href="https://issues.jboss.org/browse/KEYCLOAK-1075">https://issues.jboss.org/browse/KEYCLOAK-1075</a> and all the comments
from users...<br>
<br>
- Also updating user should be ideally at once. For example if you
call:<br>
user.setFirstName("john");<br>
user.setLastName("Doe");<br>
user.setEmail(<a class="moz-txt-link-rfc2396E" href="mailto:john@email.cz">"john@email.cz"</a>);<br>
<br>
we shouldn't have 3 update calls to LDAP, but just one. Maybe we
can address this with transaction abstraction? I've already did
something for LDAP provider (see TxAwareLDAPUserModelDelegate ),
however will be good to provide something more generic for user
storage SPI. Then when KeycloakTransactionManager.commit is
called, the data are send to federation storage<br>
<br>
<br>
Sync users<br>
--------------<br>
We should still keep the option to sync users into Keycloak DB as
we have now. Note some persistent storages like LDAP are limited
with pagination. So the easiest possibility for some admins is
just to sync users, so they can easily search them in admin
console.<br>
<br>
<br>
Proxy yes/no?<br>
------------------<br>
<br>
As we discussed in F2F, the proxy is maybe a bit complicated
pattern, but it's very flexible. It allows to proxy/override
exactly just the methods you want (for example: add
UPDATE_PASSWORD requiredAction to user if ActiveDirectory password
is expired. Add group mappings or role mappings dynamically etc)<br>
<br>
Marek<br>
<br>
On 13/06/16 14:56, Bill Burke wrote:<br>
</div>
<blockquote
cite="mid:96df1edb-cfee-9459-3ddd-926e418ceb4a@redhat.com"
type="cite">
<meta content="text/html; charset=windows-1252"
http-equiv="Content-Type">
<p>I'm working on a new SPI right now. Here is my notebad to
capture how things work, issues weed to consider, and problems
we have to solve:</p>
<p><a moz-do-not-send="true" class="moz-txt-link-freetext"
href="https://github.com/keycloak/keycloak/wiki/2.0-User-Federation-Storage-SPI">https://github.com/keycloak/keycloak/wiki/2.0-User-Federation-Storage-SPI</a><br>
</p>
<br>
<div class="moz-cite-prefix">On 6/13/16 3:39 AM, Marek Posolda
wrote:<br>
</div>
<blockquote cite="mid:575E6344.3000605@redhat.com" type="cite">
<meta content="text/html; charset=windows-1252"
http-equiv="Content-Type">
<div class="moz-cite-prefix">We discussed some time ago how to
ensure that UserFederationProvider lifecycle is properly tight
to KeycloakSession <a moz-do-not-send="true"
class="moz-txt-link-freetext"
href="http://lists.jboss.org/pipermail/keycloak-dev/2016-April/007123.html">http://lists.jboss.org/pipermail/keycloak-dev/2016-April/007123.html</a>
. The last we discussed was to add new method on
KeycloakSession like:<br>
<br>
<T extends Provider> T getProvider(Class<T> clazz,
String id, String instanceId);<br>
<br>
where instanceId is the state associated with the provider (in
case of UserFederationProvider it will be DB ID of
UserFederationProviderModelId). That way, the
UserFederationProviderFactory.create can load the
UserFederationProviderModel (assumption is that RealmModel is
available in KeycloakContext, so
UserFederationProviderFactory.create has access to RealmModel
+ providerDatabaseId to load it from DB).<br>
<br>
In the thread, you can see that I've initially proposed
something similar to your proposal, but it's a bit more
complex though. Hopefully going "simple" way and adding just
the method with "instanceId" String argument can solve the
issue.<br>
<br>
Marek<br>
<br>
On 10/06/16 01:36, Ariel Carrera wrote:<br>
</div>
<blockquote
cite="mid:CAFzO_6c=RBb=Xin_G4vDcqeihqQ0x5U4q5G09TRapwCOa2RCuw@mail.gmail.com"
type="cite">
<div dir="ltr">
<div>There is not problem! :)</div>
<div>One more thing, I solved the problem of multiple
"federation provider" instances, adding this code to the
DefaultKeycloakSession (and the method definition in
KeycloakSession interface):</div>
<div>
<div> </div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px
0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">public
<T extends Provider> void
registerProvider(Class<T> clazz, Provider
provider, String id) {<br>
Integer hash = clazz.hashCode() + id.hashCode();<br>
providers.put(hash, provider);<br>
}</blockquote>
<div><br>
And into
MyUserFederationProviderFactory.getInstance(session,
model) something like this:</div>
<div><br>
</div>
<div>
<blockquote class="gmail_quote" style="margin:0px 0px
0px
0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">public
UserFederationProvider getInstance(KeycloakSession
session, UserFederationProviderModel model){<br>
<span class="" style="white-space:pre">                </span>UserFederationProvider
provider = (UserFederationProvider)
session.getProvider(UserFederationProvider.class,
model.getId());<br>
<span class="" style="white-space:pre">                </span>if
(provider == null){<br>
<span class="" style="white-space:pre">                        </span>lazyInit(session);<br>
<span class="" style="white-space:pre">        </span>
provider = new
MyUserFederationProvider(session, model, config,
......);<br>
<span class="" style="white-space:pre">        </span>
((KeycloakSession)session).registerProvider(UserFederationProvider.class,
provider, model.getId());<br>
<span class="" style="white-space:pre">                </span>};<br>
return provider;<br>
}</blockquote>
<br>
After a few tests and debug it seems to work...
creating, catching, and closing provider instances as
expected.</div>
<div><br>
</div>
<div><br>
</div>
<div>
<div>In future versions as you said, maybe would be
better include a way to instantiate a complex
object/provider instead of doing</div>
<blockquote class="gmail_quote" style="margin:0px 0px
0px
0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">ProviderFactory.create(KeycloakSession
session) <br>
some kind of method like<br>
ProviderFactory.create(KeycloakSession session,
Object... obj);</blockquote>
<div>and the appropriate method into the KeycloakSession</div>
<blockquote class="gmail_quote" style="margin:0px 0px
0px
0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><T
extends Provider> T getProvider(Class<T>
clazz, Object... obj);<br>
<T extends Provider> T
getProvider(Class<T> clazz, String id, Object...
obj);</blockquote>
<div><br>
</div>
<div>And why not a map into the keycloakSession to store
some additional context data to share between
providers during same request? It's only a vague idea</div>
</div>
<div><br>
</div>
<div>Regards!</div>
<div class="gmail_extra"><br>
<div class="gmail_quote">2016-06-09 17:14 GMT-03:00 Bill
Burke <span dir="ltr"><<a moz-do-not-send="true"
href="mailto:bburke@redhat.com" target="_blank">bburke@redhat.com</a>></span>:<br>
<blockquote class="gmail_quote" style="margin:0px 0px
0px
0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
<div bgcolor="#FFFFFF" text="#000000">
<p>Its gonna be awhile. Its going to be difficult
to make everything both backward compatible and
cover all the current and future use cases we
need to cover. Listen on the dev list. I
should post some info soon on what the new impl
will look like.<br>
</p>
<div>
<div class="h5"> <br>
<div>On 6/9/16 3:57 PM, Ariel Carrera wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">Yes Bill, exactly! I will
waiting to test it Thanks!<br>
</div>
<div class="gmail_extra"><br>
<div class="gmail_quote">2016-06-09 16:29
GMT-03:00 Bill Burke <span dir="ltr"><<a
moz-do-not-send="true"
class="moz-txt-link-abbreviated"
href="mailto:bburke@redhat.com"><a class="moz-txt-link-abbreviated" href="mailto:bburke@redhat.com">bburke@redhat.com</a></a>></span>:<br>
<blockquote class="gmail_quote"
style="margin:0px 0px 0px
0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><span><br>
<br>
On 6/9/16 2:52 PM, Ariel Carrera
wrote:<br>
<blockquote class="gmail_quote"
style="margin:0px 0px 0px
0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
Hi Bill, is a little expensive for
me because I am creating a new
entity manager to connect with a
legacy database, and
creating/enlisting a transaction
per instance.<br>
For example in a simple flow case
where a user needs to click "I
forgot my password" link to
recover the password, there is
more than nine or ten instances
created to do this. It's really
not a big problem but I think that
is not necessary and can be
implemented like others spi
providers catched into the
keycloak session.<br>
<br>
</blockquote>
</span> This is good feedback. We
need a way to associate a provider, by
name, to the KeycloakSession. Maybe
we just need a way to associate
anything with the KeycloakSession
period.<span><br>
<br>
<blockquote class="gmail_quote"
style="margin:0px 0px 0px
0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
In my case, another difficulty is
synchronization between an old
authentication system and keycloak
implemented on demand (there is no
full/partial syncrhonization
because the legacy system is still
working and need to work together
for a while). Also I implemented
synchronization support but at
this moment it not used.<br>
Every time that keycloak needs to
validate a user (isValid)
recovered from the user storage or
cache, a query to the legacy
system is made. Added to this... I
need to recover some attributes
and roles changes produced on the
legacy system.... so I decided to
implement a "user federation
cache" with a short term
expiration to improve the
performance with certain
synchronization delay tolerance.<br>
<br>
In a few words I have: a custom
User Federation Provider + on
deman synchronization + a user
Federation Provider Cache (my own
cache SPI).<br>
<br>
Maybe an optional spi to obtain a
custom container from infinispan
could be a good choice to add to
the new implementation and provide
another one tool to do things with
better performance.<br>
<br>
</blockquote>
</span> I think the new model might
solve your caching needs. There will
be no importing by default. This
means no synching, etc. Keycloak will
only store metadata that your user
store can't provide. User Federation
Providers will work just as the
default Keycloak user store and user
cache.<br>
<br>
</blockquote>
</div>
<br>
<br clear="all">
<div><br>
</div>
-- <br>
<div data-smartmail="gmail_signature">Tatú</div>
</div>
</blockquote>
<br>
</div>
</div>
</div>
</blockquote>
</div>
<br>
<br clear="all">
<div><br>
</div>
-- <br>
<div class="gmail_signature"
data-smartmail="gmail_signature">Tatú</div>
</div>
</div>
</div>
<br>
<fieldset class="mimeAttachmentHeader"></fieldset>
<br>
<pre wrap="">_______________________________________________
keycloak-dev mailing list
<a moz-do-not-send="true" class="moz-txt-link-abbreviated" href="mailto:keycloak-dev@lists.jboss.org">keycloak-dev@lists.jboss.org</a>
<a moz-do-not-send="true" class="moz-txt-link-freetext" href="https://lists.jboss.org/mailman/listinfo/keycloak-dev">https://lists.jboss.org/mailman/listinfo/keycloak-dev</a></pre>
</blockquote>
<br>
<br>
<fieldset class="mimeAttachmentHeader"></fieldset>
<br>
<pre wrap="">_______________________________________________
keycloak-dev mailing list
<a moz-do-not-send="true" class="moz-txt-link-abbreviated" href="mailto:keycloak-dev@lists.jboss.org">keycloak-dev@lists.jboss.org</a>
<a moz-do-not-send="true" class="moz-txt-link-freetext" href="https://lists.jboss.org/mailman/listinfo/keycloak-dev">https://lists.jboss.org/mailman/listinfo/keycloak-dev</a></pre>
</blockquote>
<br>
<br>
<fieldset class="mimeAttachmentHeader"></fieldset>
<br>
<pre wrap="">_______________________________________________
keycloak-dev mailing list
<a class="moz-txt-link-abbreviated" href="mailto:keycloak-dev@lists.jboss.org">keycloak-dev@lists.jboss.org</a>
<a class="moz-txt-link-freetext" href="https://lists.jboss.org/mailman/listinfo/keycloak-dev">https://lists.jboss.org/mailman/listinfo/keycloak-dev</a></pre>
</blockquote>
<br>
</body>
</html>