<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>
          &lt;T extends Provider&gt; T getProvider(Class&lt;T&gt; 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

                &lt;T extends Provider&gt; void
                registerProvider(Class&lt;T&gt; 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">&lt;T

                  extends Provider&gt; T getProvider(Class&lt;T&gt;
                  clazz, Object... obj);<br>
                  &lt;T extends Provider&gt; T
                  getProvider(Class&lt;T&gt; 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">&lt;<a moz-do-not-send="true"
                      href="mailto:bburke@redhat.com" target="_blank">bburke@redhat.com</a>&gt;</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">&lt;<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>&gt;</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>