<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<p><br>
</p>
<br>
<div class="moz-cite-prefix">On 8/18/16 5:12 AM, Stian Thorgersen
wrote:<br>
</div>
<blockquote
cite="mid:CAJgngAcFW92iXH0U_SZuiXiuQ02aGk-At1D8nM-t-+1PBYLvxg@mail.gmail.com"
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"><<a moz-do-not-send="true"
href="mailto:bburke@redhat.com" target="_blank">bburke@redhat.com</a>></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'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't
guarentee I won't break existing User Federation
Providers. Here'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:'Menlo';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:'Menlo';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:'Menlo';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<String> 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's walk through an authentication flow,
specificaly for OTP.</p>
<p>1. Authenticator calls KeycloakSession.users().<wbr>isConfiguredFor(realm,
user, "OTP"). 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().<wbr>requiredActionsFor(...).
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's say that in this particular example, the
generic OTP Requried Action screen is invoked. In
that case, this required action provider
callsKeycloakSession.users().<wbr>updateCredential.
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().<wbr>isValid() 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'm pretty sure we can just allow the
develoepr to plug in their own $routeProvider for the
admin console. We don'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>
Generic rendering from a metamodel provided by a REST API is
something we can and should support. It won't be completely pretty,
but should work for most things. If we don'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.<br>
<br>
<blockquote
cite="mid:CAJgngAcFW92iXH0U_SZuiXiuQ02aGk-At1D8nM-t-+1PBYLvxg@mail.gmail.com"
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>
Moving to Angular 2 seems daunting. <br>
<br>
<a class="moz-txt-link-freetext" href="https://angular.io/docs/ts/latest/guide/upgrade.html#">https://angular.io/docs/ts/latest/guide/upgrade.html#</a><br>
<br>
<br>
<blockquote
cite="mid:CAJgngAcFW92iXH0U_SZuiXiuQ02aGk-At1D8nM-t-+1PBYLvxg@mail.gmail.com"
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>
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.<br>
<br>
<blockquote
cite="mid:CAJgngAcFW92iXH0U_SZuiXiuQ02aGk-At1D8nM-t-+1PBYLvxg@mail.gmail.com"
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>
Again, we can focus on generic rendering for Account Service. Then
it will translate quite easily to whatever we do here.<br>
<br>
Bill<br>
</body>
</html>