Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
W
workshop-keycloak
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Jira
Jira
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jannik Hüls
workshop-keycloak
Commits
8b544375
Commit
8b544375
authored
Dec 12, 2018
by
Jannik Hüls
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added authenticator implementation example
parent
c1558bff
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
871 additions
and
0 deletions
+871
-0
authenticator/README.md
authenticator/README.md
+22
-0
authenticator/pom.xml
authenticator/pom.xml
+198
-0
authenticator/secret-question-config.ftl
authenticator/secret-question-config.ftl
+33
-0
authenticator/secret-question.ftl
authenticator/secret-question.ftl
+34
-0
authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticator.java
...k/examples/authenticator/SecretQuestionAuthenticator.java
+136
-0
authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticatorFactory.java
...les/authenticator/SecretQuestionAuthenticatorFactory.java
+119
-0
authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java
...mples/authenticator/SecretQuestionCredentialProvider.java
+131
-0
authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProviderFactory.java
...uthenticator/SecretQuestionCredentialProviderFactory.java
+37
-0
authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java
.../examples/authenticator/SecretQuestionRequiredAction.java
+59
-0
authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredActionFactory.java
...es/authenticator/SecretQuestionRequiredActionFactory.java
+65
-0
authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
...services/org.keycloak.authentication.AuthenticatorFactory
+18
-0
authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory
...ervices/org.keycloak.authentication.RequiredActionFactory
+18
-0
authenticator/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory
...ervices/org.keycloak.credential.CredentialProviderFactory
+1
-0
No files found.
authenticator/README.md
0 → 100755
View file @
8b544375
# Example Custom Authenticator
1.
First, Keycloak must be running.
2.
Execute the follow. This will build the example and deploy it
$ mvn clean install wildfly:deploy
3.
Copy the secret-question.ftl and secret-question-config.ftl files to the themes/base/login directory.
4.
Login to admin console. Hit browser refresh if you are already logged in so that the new providers show up.
5.
Go to the Authentication menu item and go to the Flow tab, you will be able to view the currently
defined flows. You cannot modify an built in flows, so, to add the Authenticator you
have to copy an existing flow or create your own. Copy the "Browser" flow.
6.
In your copy, click the "Actions" menu item and "Add Execution". Pick Secret Question
7.
Next you have to register the required action that you created. Click on the Required Actions tab in the Authenticaiton menu.
Click on the Register button and choose your new Required Action.
Your new required action should now be displayed and enabled in the required actions list.
authenticator/pom.xml
0 → 100755
View file @
8b544375
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
>
<name>
Authenticator Example
</name>
<description/>
<modelVersion>
4.0.0
</modelVersion>
<groupId>
de.codecentric.keycloak
</groupId>
<artifactId>
authenticator-required-action-example
</artifactId>
<packaging>
jar
</packaging>
<version>
1.0-SNAPSHOT
</version>
<properties>
<version.compiler.maven.plugin>
3.5.1
</version.compiler.maven.plugin>
<version.hibernate.javax.persistence>
1.0.0.Final
</version.hibernate.javax.persistence>
<version.jboss-ejb-api>
1.0.0.Final
</version.jboss-ejb-api>
<arquillian-graphene.version>
2.3.2
</arquillian-graphene.version>
<arquillian-phantom.version>
1.2.1.Final
</arquillian-phantom.version>
<version.junit>
4.12
</version.junit>
<version.wildfly.maven.plugin>
1.2.2.Final
</version.wildfly.maven.plugin>
<keycloak.version>
4.6.0.Final
</keycloak.version>
<wildfly.version>
11.0.0.Final
</wildfly.version>
<jackson.version>
2.9.5
</jackson.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>
org.jboss.arquillian
</groupId>
<artifactId>
arquillian-bom
</artifactId>
<version>
1.4.0.Final
</version>
<type>
pom
</type>
<scope>
import
</scope>
</dependency>
<dependency>
<groupId>
org.wildfly.bom
</groupId>
<artifactId>
wildfly-javaee7
</artifactId>
<version>
${wildfly.version}
</version>
<type>
pom
</type>
<scope>
import
</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>
org.keycloak
</groupId>
<artifactId>
keycloak-core
</artifactId>
<version>
${keycloak.version}
</version>
<scope>
provided
</scope>
</dependency>
<dependency>
<groupId>
org.keycloak
</groupId>
<artifactId>
keycloak-server-spi
</artifactId>
<version>
${keycloak.version}
</version>
<scope>
provided
</scope>
</dependency>
<dependency>
<groupId>
org.keycloak
</groupId>
<artifactId>
keycloak-server-spi-private
</artifactId>
<scope>
provided
</scope>
<version>
${keycloak.version}
</version>
</dependency>
<dependency>
<groupId>
org.keycloak
</groupId>
<artifactId>
keycloak-services
</artifactId>
<scope>
provided
</scope>
<version>
${keycloak.version}
</version>
</dependency>
<dependency>
<groupId>
org.jboss.logging
</groupId>
<artifactId>
jboss-logging
</artifactId>
<scope>
provided
</scope>
</dependency>
<dependency>
<groupId>
org.hibernate.javax.persistence
</groupId>
<artifactId>
hibernate-jpa-2.1-api
</artifactId>
<version>
${version.hibernate.javax.persistence}
</version>
<scope>
provided
</scope>
</dependency>
<dependency>
<groupId>
org.jboss.spec.javax.ejb
</groupId>
<artifactId>
jboss-ejb-api_3.2_spec
</artifactId>
<version>
${version.jboss-ejb-api}
</version>
<scope>
provided
</scope>
</dependency>
<dependency>
<groupId>
org.jboss.arquillian.graphene
</groupId>
<artifactId>
graphene-webdriver
</artifactId>
<version>
${arquillian-graphene.version}
</version>
<type>
pom
</type>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
org.jboss.arquillian.extension
</groupId>
<artifactId>
arquillian-phantom-driver
</artifactId>
<version>
${arquillian-phantom.version}
</version>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
org.jboss.arquillian.junit
</groupId>
<artifactId>
arquillian-junit-container
</artifactId>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
junit
</groupId>
<artifactId>
junit
</artifactId>
<version>
${version.junit}
</version>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
org.keycloak
</groupId>
<artifactId>
keycloak-test-helper
</artifactId>
<version>
${keycloak.version}
</version>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
com.fasterxml.jackson.core
</groupId>
<artifactId>
jackson-databind
</artifactId>
<version>
${jackson.version}
</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>
maven.oracle.com
</id>
<name>
oracle-maven-repo
</name>
<url>
https://maven.oracle.com
</url>
<layout>
default
</layout>
<releases>
<enabled>
true
</enabled>
<updatePolicy>
always
</updatePolicy>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>
maven.oracle.com
</id>
<name>
oracle-maven-repo
</name>
<url>
https://maven.oracle.com
</url>
<layout>
default
</layout>
<releases>
<enabled>
true
</enabled>
<updatePolicy>
always
</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
<build>
<finalName>
authenticator-required-action-example
</finalName>
<plugins>
<plugin>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-compiler-plugin
</artifactId>
<version>
${version.compiler.maven.plugin}
</version>
<configuration>
<source>
1.8
</source>
<target>
1.8
</target>
</configuration>
</plugin>
<plugin>
<groupId>
org.wildfly.plugins
</groupId>
<artifactId>
wildfly-maven-plugin
</artifactId>
<version>
${version.wildfly.maven.plugin}
</version>
<configuration>
<skip>
false
</skip>
</configuration>
</plugin>
<plugin>
<artifactId>
maven-enforcer-plugin
</artifactId>
<executions>
<execution>
<id>
enforce-quickstart-realm-file-exist
</id>
<phase>
validate
</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
authenticator/secret-question-config.ftl
0 → 100755
View file @
8b544375
<#import "template.ftl" as layout>
<@layout.registrationLayout; section>
<#if section = "title">
${msg("loginTitle",realm.name)}
<#elseif section = "header">
Setup Secret Question
<#elseif section = "form">
<form id="kc-totp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="totp" class="${properties.kcLabelClass!}">What is your mom's first name?</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input id="totp" name="secret_answer" type="text" class="${properties.kcInputClass!}" />
</div>
</div>
<div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}">
</div>
</div>
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<div class="${properties.kcFormButtonsWrapperClass!}">
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doSubmit")}"/>
</div>
</div>
</div>
</form>
</#if>
</@layout.registrationLayout>
\ No newline at end of file
authenticator/secret-question.ftl
0 → 100755
View file @
8b544375
<#import "template.ftl" as layout>
<@layout.registrationLayout; section>
<#if section = "title">
${msg("loginTitle",realm.name)}
<#elseif section = "header">
${msg("loginTitleHtml",realm.name)}
<#elseif section = "form">
<form id="kc-totp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="totp" class="${properties.kcLabelClass!}">What is your mom's first name</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input id="totp" name="secret_answer" type="text" class="${properties.kcInputClass!}" />
</div>
</div>
<div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}">
</div>
</div>
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<div class="${properties.kcFormButtonsWrapperClass!}">
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="cancel" id="kc-cancel" type="submit" value="${msg("doCancel")}"/>
</div>
</div>
</div>
</form>
</#if>
</@layout.registrationLayout>
\ No newline at end of file
authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticator.java
0 → 100755
View file @
8b544375
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.keycloak.examples.authenticator
;
import
org.jboss.resteasy.spi.HttpResponse
;
import
org.jboss.resteasy.spi.ResteasyProviderFactory
;
import
org.keycloak.authentication.AuthenticationFlowContext
;
import
org.keycloak.authentication.AuthenticationFlowError
;
import
org.keycloak.authentication.Authenticator
;
import
org.keycloak.common.util.ServerCookie
;
import
org.keycloak.models.AuthenticatorConfigModel
;
import
org.keycloak.models.KeycloakSession
;
import
org.keycloak.models.RealmModel
;
import
org.keycloak.models.UserCredentialModel
;
import
org.keycloak.models.UserModel
;
import
javax.ws.rs.core.Cookie
;
import
javax.ws.rs.core.HttpHeaders
;
import
javax.ws.rs.core.MultivaluedMap
;
import
javax.ws.rs.core.Response
;
import
java.net.URI
;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public
class
SecretQuestionAuthenticator
implements
Authenticator
{
public
static
final
String
CREDENTIAL_TYPE
=
"secret_question"
;
protected
boolean
hasCookie
(
AuthenticationFlowContext
context
)
{
Cookie
cookie
=
context
.
getHttpRequest
().
getHttpHeaders
().
getCookies
().
get
(
"SECRET_QUESTION_ANSWERED"
);
boolean
result
=
cookie
!=
null
;
if
(
result
)
{
System
.
out
.
println
(
"Bypassing secret question because cookie is set"
);
}
return
result
;
}
@Override
public
void
authenticate
(
AuthenticationFlowContext
context
)
{
if
(
hasCookie
(
context
))
{
context
.
success
();
return
;
}
Response
challenge
=
context
.
form
().
createForm
(
"secret-question.ftl"
);
context
.
challenge
(
challenge
);
}
@Override
public
void
action
(
AuthenticationFlowContext
context
)
{
MultivaluedMap
<
String
,
String
>
formData
=
context
.
getHttpRequest
().
getDecodedFormParameters
();
if
(
formData
.
containsKey
(
"cancel"
))
{
context
.
cancelLogin
();
return
;
}
boolean
validated
=
validateAnswer
(
context
);
if
(!
validated
)
{
Response
challenge
=
context
.
form
()
.
setError
(
"badSecret"
)
.
createForm
(
"secret-question.ftl"
);
context
.
failureChallenge
(
AuthenticationFlowError
.
INVALID_CREDENTIALS
,
challenge
);
return
;
}
setCookie
(
context
);
context
.
success
();
}
protected
void
setCookie
(
AuthenticationFlowContext
context
)
{
AuthenticatorConfigModel
config
=
context
.
getAuthenticatorConfig
();
int
maxCookieAge
=
60
*
60
*
24
*
30
;
// 30 days
if
(
config
!=
null
)
{
maxCookieAge
=
Integer
.
valueOf
(
config
.
getConfig
().
get
(
"cookie.max.age"
));
}
URI
uri
=
context
.
getUriInfo
().
getBaseUriBuilder
().
path
(
"realms"
).
path
(
context
.
getRealm
().
getName
()).
build
();
addCookie
(
"SECRET_QUESTION_ANSWERED"
,
"true"
,
uri
.
getRawPath
(),
null
,
null
,
maxCookieAge
,
false
,
true
);
}
public
static
void
addCookie
(
String
name
,
String
value
,
String
path
,
String
domain
,
String
comment
,
int
maxAge
,
boolean
secure
,
boolean
httpOnly
)
{
HttpResponse
response
=
ResteasyProviderFactory
.
getContextData
(
HttpResponse
.
class
);
StringBuffer
cookieBuf
=
new
StringBuffer
();
ServerCookie
.
appendCookieValue
(
cookieBuf
,
1
,
name
,
value
,
path
,
domain
,
comment
,
maxAge
,
secure
,
httpOnly
);
String
cookie
=
cookieBuf
.
toString
();
response
.
getOutputHeaders
().
add
(
HttpHeaders
.
SET_COOKIE
,
cookie
);
}
protected
boolean
validateAnswer
(
AuthenticationFlowContext
context
)
{
MultivaluedMap
<
String
,
String
>
formData
=
context
.
getHttpRequest
().
getDecodedFormParameters
();
String
secret
=
formData
.
getFirst
(
"secret_answer"
);
UserCredentialModel
input
=
new
UserCredentialModel
();
input
.
setType
(
SecretQuestionCredentialProvider
.
SECRET_QUESTION
);
input
.
setValue
(
secret
);
return
context
.
getSession
().
userCredentialManager
().
isValid
(
context
.
getRealm
(),
context
.
getUser
(),
input
);
}
@Override
public
boolean
requiresUser
()
{
return
true
;
}
@Override
public
boolean
configuredFor
(
KeycloakSession
session
,
RealmModel
realm
,
UserModel
user
)
{
return
session
.
userCredentialManager
().
isConfiguredFor
(
realm
,
user
,
SecretQuestionCredentialProvider
.
SECRET_QUESTION
);
}
@Override
public
void
setRequiredActions
(
KeycloakSession
session
,
RealmModel
realm
,
UserModel
user
)
{
user
.
addRequiredAction
(
SecretQuestionRequiredAction
.
PROVIDER_ID
);
}
@Override
public
void
close
()
{
}
}
authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticatorFactory.java
0 → 100755
View file @
8b544375
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.keycloak.examples.authenticator
;
import
org.keycloak.Config
;
import
org.keycloak.authentication.Authenticator
;
import
org.keycloak.authentication.AuthenticatorFactory
;
import
org.keycloak.authentication.ConfigurableAuthenticatorFactory
;
import
org.keycloak.models.AuthenticationExecutionModel
;
import
org.keycloak.models.KeycloakSession
;
import
org.keycloak.models.KeycloakSessionFactory
;
import
org.keycloak.provider.ProviderConfigProperty
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public
class
SecretQuestionAuthenticatorFactory
implements
AuthenticatorFactory
,
ConfigurableAuthenticatorFactory
{
public
static
final
String
PROVIDER_ID
=
"secret-question-authenticator"
;
private
static
final
SecretQuestionAuthenticator
SINGLETON
=
new
SecretQuestionAuthenticator
();
@Override
public
String
getId
()
{
return
PROVIDER_ID
;
}
@Override
public
Authenticator
create
(
KeycloakSession
session
)
{
return
SINGLETON
;
}
private
static
AuthenticationExecutionModel
.
Requirement
[]
REQUIREMENT_CHOICES
=
{
AuthenticationExecutionModel
.
Requirement
.
REQUIRED
,
AuthenticationExecutionModel
.
Requirement
.
DISABLED
};
@Override
public
AuthenticationExecutionModel
.
Requirement
[]
getRequirementChoices
()
{
return
REQUIREMENT_CHOICES
;
}
@Override
public
boolean
isUserSetupAllowed
()
{
return
true
;
}
@Override
public
boolean
isConfigurable
()
{
return
true
;
}
@Override
public
List
<
ProviderConfigProperty
>
getConfigProperties
()
{
return
configProperties
;
}
private
static
final
List
<
ProviderConfigProperty
>
configProperties
=
new
ArrayList
<
ProviderConfigProperty
>();
static
{
ProviderConfigProperty
property
;
property
=
new
ProviderConfigProperty
();
property
.
setName
(
"cookie.max.age"
);
property
.
setLabel
(
"Cookie Max Age"
);
property
.
setType
(
ProviderConfigProperty
.
STRING_TYPE
);
property
.
setHelpText
(
"Max age in seconds of the SECRET_QUESTION_COOKIE."
);
configProperties
.
add
(
property
);
}
@Override
public
String
getHelpText
()
{
return
"A secret question that a user has to answer. i.e. What is your mother's maiden name."
;
}
@Override
public
String
getDisplayType
()
{
return
"Secret Question"
;
}
@Override
public
String
getReferenceCategory
()
{
return
"Secret Question"
;
}
@Override
public
void
init
(
Config
.
Scope
config
)
{
}
@Override
public
void
postInit
(
KeycloakSessionFactory
factory
)
{
}
@Override
public
void
close
()
{
}
}
authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java
0 → 100644
View file @
8b544375
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.keycloak.examples.authenticator
;
import
org.keycloak.common.util.Time
;
import
org.keycloak.credential.CredentialInput
;
import
org.keycloak.credential.CredentialInputUpdater
;
import
org.keycloak.credential.CredentialInputValidator
;
import
org.keycloak.credential.CredentialModel
;
import
org.keycloak.credential.CredentialProvider
;
import
org.keycloak.models.KeycloakSession
;
import
org.keycloak.models.RealmModel
;
import
org.keycloak.models.UserCredentialModel
;
import
org.keycloak.models.UserModel
;
import
org.keycloak.models.cache.CachedUserModel
;
import
org.keycloak.models.cache.OnUserCache
;
import
java.util.Collections
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public
class
SecretQuestionCredentialProvider
implements
CredentialProvider
,
CredentialInputValidator
,
CredentialInputUpdater
,
OnUserCache
{
public
static
final
String
SECRET_QUESTION
=
"SECRET_QUESTION"
;
public
static
final
String
CACHE_KEY
=
SecretQuestionCredentialProvider
.
class
.
getName
()
+
"."
+
SECRET_QUESTION
;
protected
KeycloakSession
session
;
public
SecretQuestionCredentialProvider
(
KeycloakSession
session
)
{
this
.
session
=
session
;
}
public
CredentialModel
getSecret
(
RealmModel
realm
,
UserModel
user
)
{
CredentialModel
secret
=
null
;
if
(
user
instanceof
CachedUserModel
)
{
CachedUserModel
cached
=
(
CachedUserModel
)
user
;
secret
=
(
CredentialModel
)
cached
.
getCachedWith
().
get
(
CACHE_KEY
);
}
else
{
List
<
CredentialModel
>
creds
=
session
.
userCredentialManager
().
getStoredCredentialsByType
(
realm
,
user
,
SECRET_QUESTION
);
if
(!
creds
.
isEmpty
())
secret
=
creds
.
get
(
0
);
}
return
secret
;
}
@Override