<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On 18 August 2016 at 21:00, Bill Burke <span dir="ltr">&lt;<a href="mailto:bburke@redhat.com" target="_blank">bburke@redhat.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
  
    
  
  <div bgcolor="#FFFFFF" text="#000000"><div><div class="h5">
    <p><br>
    </p>
    <br>
    <div>On 8/18/16 5:12 AM, Stian Thorgersen
      wrote:<br>
    </div>
    <blockquote type="cite">
      <div dir="ltr"><br>
        <div class="gmail_extra"><br>
          <div class="gmail_quote">On 16 August 2016 at 00:57, Bill
            Burke <span dir="ltr">&lt;<a href="mailto:bburke@redhat.com" target="_blank">bburke@redhat.com</a>&gt;</span>
            wrote:<br>
            <blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
              <div bgcolor="#FFFFFF" text="#000000"> I&#39;m currently
                working on a new credential SPI that will replace
                existing methods on UserProvider and UserModel, as well
                as replacing UserCredentialModel, etc.  This is a work
                in progress where we may see multiple iterations in
                master.  I hope to remain backward compatible, but can&#39;t
                guarentee I won&#39;t break existing User Federation
                Providers.  Here&#39;s an initial writeup to explain
                things.  Credentials revolve around these 4 events that
                are initiated by authentication flows, the admin
                console, and the account service.
                <p>* Is the user configured for a specific credential
                  type</p>
                <p>* Is a credential valid</p>
                <p>* What required actions must be taken for an
                  unconfigured credential type</p>
                <p>* update a credential</p>
                <p>How each of these events is resolved will depend on
                  the configuration of the system and these interfaces:</p>
                <p> </p>
                <pre style="background-color:#ffffff;color:#000000;font-family:&#39;Menlo&#39;;font-size:9.0pt"><span style="color:#000080;font-weight:bold">public interface </span>CredentialInput {
    String getType();
}
</pre>
                <p> </p>
                <p> </p>
                <pre style="background-color:#ffffff;color:#000000;font-family:&#39;Menlo&#39;;font-size:9.0pt"><span style="color:#000080;font-weight:bold">public interface </span>CredentialInputValidator {
    <span style="color:#000080;font-weight:bold">boolean </span>supportsCredentialType(String credentialType);
    <span style="color:#000080;font-weight:bold">boolean </span>isConfiguredFor(RealmModel realm, UserModel user, String credentialType);
    <span style="color:#000080;font-weight:bold">boolean </span>isValid(RealmModel realm, UserModel user, CredentialInput input);

}
</pre>
                <p> </p>
                <pre style="background-color:#ffffff;color:#000000;font-family:&#39;Menlo&#39;;font-size:9.0pt"><span style="color:#000080;font-weight:bold">public interface </span>CredentialInputUpdater {
    <span style="color:#000080;font-weight:bold">boolean </span>supportsCredentialType(String credentialType);
    Set&lt;String&gt; requiredActionsFor(RealmModel realm, UserModel user, String credentialType);
    <span style="color:#000080;font-weight:bold">void </span>updateCredential(RealmModel realm, UserModel user, CredentialInput input);
}

</pre>
                <p>Two different types of components will be able to
                  implement these interfaces.  UserStorageProviders
                  (user federation) and CredentialProviders. 
                  CredentialProviders are components configured at the
                  realm level.  CredentialProviders are responsible for
                  managing one or more types of credential types and are
                  the bridge between CredentialInput and where the
                  credential is stored.  UserStorageProvider is always
                  asked first whether it can complete the requested
                  action, then CredentialProviders are queried in order
                  of their priority.</p>
                <p>Each UserStorageProvider and/or CredentialProvider
                  can implement the OnUserCache callback interface
                  discussed in my previous custom caching email.  This
                  allows each credential type to decide whether it will
                  be cached or not along with the user.  For example,
                  HOTP cannot be cached.</p>
                <p>So, for example, there will be a
                  KeycloakMobileOTPProvider.  This deals with Google
                  Authenticator and FreeOTP as well as storing these
                  things within Keycloak storage, it also looks at the
                  OTP policy of the realm to determine how to update and
                  store the OTP secret and stuff.  There is also a
                  KeycloakPasswordProvider which hooks into Keycloak
                  storage and the PasswordPolicies set up by the realm. 
                  When a user is cached, the KeycloakPasswordProvider
                  will add the hashed password to the user cache, the
                  KeycloakMobileOTPProvider will add the OTP secret to
                  cache if its  not HOTP and needs to maintain a
                  counter.</p>
                <p>Let&#39;s walk through an authentication flow,
                  specificaly for OTP.</p>
                <p>1. Authenticator calls KeycloakSession.users().isConf<wbr>iguredFor(realm,
                  user, &quot;OTP&quot;).  If the user was loaded by a
                  UserStorageProvider and that provider implements the
                  CredentialInputValidator interface, isConfiguredFor()
                  is called on that.  If that returns false, each
                  CredentialProvider is iterated on to call
                  isConfiguredFor().</p>
                <p>2. If OTP is required and not configured for the
                  user, the Authenticator then calls
                  KeycloakSession.users().requir<wbr>edActionsFor(...). 
                  Again, UserStorageProvider is queried first, then the
                  CredneitalProviders.  The first provider that returns
                  a non-empty set will end the query and the set of
                  required actions will be returned.</p>
                <p>3a. Let&#39;s say that in this particular example, the
                  generic OTP Requried Action screen is invoked.  In
                  that case, this required action provider
                  callsKeycloakSession.users().u<wbr>pdateCredential. 
                  The first UserStorageProvider or CredentialProvider
                  that can handle this credential type will save the
                  credential.<br>
                </p>
                <p>3b. If OTP is configured for user, the OTP is
                  obtained by the Authenticator and
                  KeycloakSession.users().isVali<wbr>d() method is
                  called.   Again, UserStorageProvider first, then each
                  CredentialProvider.  Each provider is queried until
                  one returns true or the list is exhausted.  FYI, This
                  algorithm allows for multiple OTP authenticators per
                  user.</p>
                <p>** Admin console and Account Service UIs **</p>
                <p>Like we do for other components, the
                  UserStorageProvider or CredentialProvider can
                  optionally provide a list of ProviderConfigProperties
                  for the admin console and/or account serviceso that it
                  can create a credential for a specific user.  There
                  will be separate property lists for admin console and
                  account service.  If a specific custom screen is
                  desired, I&#39;m pretty sure we can just allow the
                  develoepr to plug in their own $routeProvider for the
                  admin console.  We don&#39;t have a pluggable mechanism
                  for the account service yet (or a way to generic
                  render either).  This will need to be developed
                  eventually.<br>
                </p>
              </div>
            </blockquote>
            <div>Extending admin console is fine in community, but would
              be hard to support. </div>
          </div>
        </div>
      </div>
    </blockquote></div></div>
    Generic rendering from a metamodel provided by a REST API is
    something we can and should support.  It won&#39;t be completely pretty,
    but should work for most things.  If we don&#39;t want to support
    Angular extensions, then the user can just completely punt and have
    a completely different web app to configure a specific component.</div></blockquote><div><br></div><div>I didn&#39;t say we shouldn&#39;t support extensions to the UI, but we need to consider how to do it. If we tie it to much to Angular 1 specifics than it becomes even harder to migrate to Angular 2.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div bgcolor="#FFFFFF" text="#000000"><span class=""><br>
    <br>
    <blockquote type="cite">
      <div dir="ltr">
        <div class="gmail_extra">
          <div class="gmail_quote">
            <div>Especially as we would eventually have to move to
              AngularJS 2.0, which drastically changes things. </div>
          </div>
        </div>
      </div>
    </blockquote>
    <br></span>
    Moving to Angular 2 seems daunting.  <br>
    <br>
    <a href="https://angular.io/docs/ts/latest/guide/upgrade.html#" target="_blank">https://angular.io/docs/ts/<wbr>latest/guide/upgrade.html#</a></div></blockquote><div><br></div><div>Yep, it&#39;s a complete rewrite! Sucks.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div bgcolor="#FFFFFF" text="#000000"><span class=""><br>
    <br>
    <br>
    <blockquote type="cite">
      <div dir="ltr">
        <div class="gmail_extra">
          <div class="gmail_quote">
            <div>I also wonder if we should provide an higher level
              extension to register extensions than having to do
              $routeProviders etc as that sounds like a fairly low level
              approach. Do you have any example?</div>
            <div><br>
            </div>
          </div>
        </div>
      </div>
    </blockquote>
    <br>
    <br></span>
    Look at the old ldap provider for an example of overriding a
    specific component UI over a fallback generic one.  It just has a
    more specific route than the generic page. </div></blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div bgcolor="#FFFFFF" text="#000000"><span class=""><br>
    <br>
    <blockquote type="cite">
      <div dir="ltr">
        <div class="gmail_extra">
          <div class="gmail_quote">
            <div>Account service should be completely scrapped and
              replaced with something more modern. The UI needs a
              complete revamp and it should also be changed to AngularJS
              and REST service to make it more customizable and
              extensible.<br>
            </div>
          </div>
        </div>
      </div>
    </blockquote></span>
    Again, we can focus on generic rendering for Account Service.  Then
    it will translate quite easily to whatever we do here.</div></blockquote><div><br></div><div>I don&#39;t think generic rendering works for account service as it wants to be more end user friendly. Generic rendering is probably OK for the admin console most of the time, but it&#39;s never going to be brilliant for usability.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div bgcolor="#FFFFFF" text="#000000"><span class="HOEnZb"><font color="#888888"><br>
    <br>
    Bill<br>
  </font></span></div>

</blockquote></div><br></div></div>