merge conflicts
This commit is contained in:
commit
edb9f0cecf
139 changed files with 1799 additions and 816 deletions
101
README.md
101
README.md
|
@ -1,39 +1,72 @@
|
|||
keycloak
|
||||
Keycloak
|
||||
========
|
||||
|
||||
Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
|
||||
and video tutorials.
|
||||
Keycloak is an SSO Service for web apps and REST services. For more information visit [http://keycloak.org](http://keycloak.org).
|
||||
|
||||
Keycloak is an SSO Service for web apps and REST services.
|
||||
It can be used for social applications as well as enterprise applications. It is based on OpenID Connect with support for SAML 2.0 as well.
|
||||
Here's some of the features:
|
||||
|
||||
* SSO and Single Log Out for browser applications
|
||||
* Social Broker. Enable Google, Facebook, Yahoo, Twitter, GitHub, LinkedIn social login with no code required.
|
||||
* Optional LDAP/Active Directory integration
|
||||
* Optional User Registration
|
||||
* Password and TOTP support (via Google Authenticator or FreeOTP). Client cert auth coming soon.
|
||||
* User session management from both admin and user perspective
|
||||
* Customizable themes for user facing pages: login, grant pages, account management, emails, and admin console all customizable!
|
||||
* OAuth Bearer token auth for REST Services
|
||||
* Integrated Browser App to REST Service token propagation
|
||||
* Admin REST API
|
||||
* OAuth 2.0 Grant requests
|
||||
* CORS Support
|
||||
* CORS Web Origin management and validation
|
||||
* Completely centrally managed user and role mapping metadata. Minimal configuration at the application side
|
||||
* Admin Console for managing users, roles, role mappings, applications, user sessions, allowed CORS web origins, and OAuth clients.
|
||||
* Deployable as a WAR, appliance, or an Openshift cloud service (SaaS).
|
||||
* Supports JBoss AS7, EAP 6.x, Wildfly, Tomcat, and Jetty applications. Plans to support Node.js, RAILS, GRAILS, and other non-Java applications.
|
||||
* Javascript/HTML 5 adapter for pure Javascript apps
|
||||
* Session management from admin console
|
||||
* Revocation policies
|
||||
* Password policies
|
||||
* OpenID Connect Support
|
||||
* SAML Support
|
||||
* Token claim and SAML assertion mappings, role name mappings, etc. Ability to configure exactly what information you want in your tokens and SAML documents
|
||||
* IDP brokering or chaining. You can set up Keycloak to be a child IDP to another SAML or OIDC IDP.
|
||||
* Kerberos bridging. Logged in Kerberos users can access Keycloak SAML or OIDC applications via our Kerberos bridge.
|
||||
Building
|
||||
--------
|
||||
|
||||
Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
|
||||
and video tutorials.
|
||||
Ensure you have JDK 7 (or newer), Maven 3.2.1 (or newer) and Git installed
|
||||
|
||||
java -version
|
||||
mvn -version
|
||||
git --version
|
||||
|
||||
First clone the Keycloak repository:
|
||||
|
||||
git clone https://github.com/keycloak/keycloak.git
|
||||
cd keycloak
|
||||
|
||||
To build Keycloak run:
|
||||
|
||||
mvn install
|
||||
|
||||
This will build all modules and run the testsuite.
|
||||
|
||||
To build the distribution run:
|
||||
|
||||
mvn install -Pdistribution
|
||||
|
||||
Once completed you will find distribution archives in `distribution`.
|
||||
|
||||
|
||||
Starting Keycloak
|
||||
-----------------
|
||||
|
||||
To start Keycloak during development first build as specficied above, then run:
|
||||
|
||||
mvn -f testsuite/integration/pom.xml exec:java -Pkeycloak-server
|
||||
|
||||
|
||||
To start Keycloak from the appliance distribution first build the distribution it as specified above, then run:
|
||||
|
||||
tar xfz distribution/appliance-dist/target/keycloak-appliance-dist-all-<VERSION>.tar.gz
|
||||
cd keycloak-appliance-dist-all-<VERSION>/keycloak
|
||||
bin/standalone.sh
|
||||
|
||||
To stop the server press `Ctrl + C`.
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
* See [Hacking on Keycloak](misc/HackingOnKeycloak.md)
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
* [User Guide, Admin REST API and Javadocs](http://keycloak.jboss.org/docs)
|
||||
* Developer documentation
|
||||
* [Hacking on Keycloak](misc/HackingOnKeycloak.md) - how to become a Keycloak contributor
|
||||
* [Testsuite](misc/Testsuite.md) - details about testsuite, but also how to quickly run Keycloak during development and a few test tools (OTP generation, LDAP server, Mail server)
|
||||
* [Database Testing](misc/DatabaseTesting.md) - how to do testing of Keycloak on different databases
|
||||
* [Updating Database](misc/UpdatingDatabaseSchema.md) - how to change the Keycloak database
|
||||
* [Release Process](misc/ReleaseProcess.md) - how to release Keycloak
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
|
@ -33,8 +33,8 @@ import org.keycloak.events.EventType;
|
|||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.QueryParam;
|
||||
|
@ -215,7 +215,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
|||
//logger.error("Failed " + getConfig().getAlias() + " broker login: " + error);
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(error);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -240,7 +240,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
|||
}
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.IDENTITY_PROVIDER_LOGIN_FAILURE);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
|
||||
public SimpleHttp generateTokenRequest(String authorizationCode) {
|
||||
|
|
|
@ -30,16 +30,15 @@ import org.keycloak.jose.jws.JWSInput;
|
|||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.IdentityBrokerService;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.PemUtils;
|
||||
|
||||
|
@ -120,14 +119,14 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
|||
EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
|
||||
logger.error("usersession in different state");
|
||||
EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
|
||||
return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
|
||||
}
|
||||
return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||
}
|
||||
|
|
|
@ -39,12 +39,15 @@ import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
|||
import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
|
||||
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
|
||||
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.FormParam;
|
||||
|
@ -133,18 +136,18 @@ public class SAMLEndpoint {
|
|||
if (!checkSsl()) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
|
||||
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
||||
}
|
||||
if (!realm.isEnabled()) {
|
||||
event.event(EventType.LOGIN_ERROR);
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
|
||||
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
|
||||
}
|
||||
|
||||
if (samlRequest == null && samlResponse == null) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.INVALID_REQUEST);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST );
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
|
||||
}
|
||||
return null;
|
||||
|
@ -180,7 +183,7 @@ public class SAMLEndpoint {
|
|||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||
event.detail(Details.REASON, "invalid_destination");
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
if (config.isValidateSignature()) {
|
||||
try {
|
||||
|
@ -189,7 +192,7 @@ public class SAMLEndpoint {
|
|||
logger.error("validation failed", e);
|
||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SIGNATURE);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUESTER);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUESTER);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,7 +205,7 @@ public class SAMLEndpoint {
|
|||
} else {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.INVALID_TOKEN);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,7 +338,7 @@ public class SAMLEndpoint {
|
|||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||
event.detail(Details.REASON, "invalid_destination");
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
|
||||
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
|
||||
}
|
||||
if (config.isValidateSignature()) {
|
||||
try {
|
||||
|
@ -344,7 +347,7 @@ public class SAMLEndpoint {
|
|||
logger.error("validation failed", e);
|
||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SIGNATURE);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
|
||||
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
|
||||
}
|
||||
}
|
||||
if (statusResponse instanceof ResponseType) {
|
||||
|
@ -363,20 +366,20 @@ public class SAMLEndpoint {
|
|||
logger.error("no valid user session");
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, relayState);
|
||||
if (userSession == null) {
|
||||
logger.error("no valid user session");
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
|
||||
logger.error("usersession in different state");
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
|
||||
return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
|
||||
}
|
||||
return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.keycloak.events.EventBuilder;
|
|||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
|
||||
import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addColumn tableName="CREDENTIAL">
|
||||
<column name="CREATED_DATE" type="BIGINT"/>
|
||||
</addColumn>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_IDPM" tableName="IDENTITY_PROVIDER_MAPPER"/>
|
||||
<addPrimaryKey columnNames="IDP_MAPPER_ID, NAME" constraintName="CONSTRAINT_IDPMConfig" tableName="IDP_MAPPER_CONFIG"/>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER_MAPPER" constraintName="FK_IDPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
|
@ -48,5 +51,23 @@
|
|||
</update>
|
||||
<dropColumn tableName="CLIENT" columnName="DTYPE"/>
|
||||
<renameColumn tableName="CLIENT" newColumnName="CLIENT_ID" oldColumnName="NAME"/>
|
||||
<renameColumn tableName="REALM" newColumnName="MASTER_ADMIN_CLIENT" oldColumnName="MASTER_ADMIN_APP"/>
|
||||
|
||||
<renameTable oldTableName="REALM_APPLICATION" newTableName="REALM_CLIENT"/>
|
||||
<renameColumn tableName="REALM_CLIENT" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID"/>
|
||||
|
||||
<renameTable oldTableName="APPLICATION_DEFAULT_ROLES" newTableName="CLIENT_DEFAULT_ROLES"/>
|
||||
<renameColumn tableName="CLIENT_DEFAULT_ROLES" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID"/>
|
||||
|
||||
<renameTable oldTableName="APP_NODE_REGISTRATIONS" newTableName="CLIENT_NODE_REGISTRATIONS"/>
|
||||
<renameColumn tableName="CLIENT_NODE_REGISTRATIONS" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID"/>
|
||||
|
||||
<renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT" oldColumnName="APPLICATION"/>
|
||||
<renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT_ROLE" oldColumnName="APPLICATION_ROLE"/>
|
||||
<renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT_REALM_CONSTRAINT" oldColumnName="APP_REALM_CONSTRAINT"/>
|
||||
|
||||
<dropUniqueConstraint tableName="KEYCLOAK_ROLE" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2"/>
|
||||
<addUniqueConstraint columnNames="NAME,CLIENT_REALM_CONSTRAINT" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2-2" tableName="KEYCLOAK_ROLE"/>
|
||||
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
|
|
@ -19,6 +19,9 @@ public class Update1_2_0_RC1 extends Update {
|
|||
public void update(KeycloakSession session) {
|
||||
convertApplicationsToClients();
|
||||
convertOAuthClientsToClients();
|
||||
|
||||
db.getCollection("realms").update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("adminAppId", "clientId")), false, true);
|
||||
|
||||
}
|
||||
|
||||
private void convertApplicationsToClients() {
|
||||
|
@ -31,6 +34,10 @@ public class Update1_2_0_RC1 extends Update {
|
|||
DBCollection roles = db.getCollection("roles");
|
||||
roles.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("applicationId", "clientId")), false, true);
|
||||
log.debugv("Renamed roles.applicationId to roles.clientId");
|
||||
|
||||
db.getCollection("clients").dropIndex("realmId_1_name_1");
|
||||
ensureIndex("clients", new String[]{"realmId", "clientId"}, true, false);
|
||||
|
||||
}
|
||||
|
||||
private void convertOAuthClientsToClients() {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<module name="org.keycloak.keycloak-events-api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.ws.rs.api"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
||||
|
|
|
@ -128,7 +128,8 @@
|
|||
<para>
|
||||
In the admin console, per realm, you can set up a password policy to enforce that users pick hard to guess passwords.
|
||||
A password has to match all policies. The password policies that can be configured are hash iterations, length, digits,
|
||||
lowercase, uppercase, special characters, not username and regex patterns. Multiple regex patterns, separated by comma,
|
||||
lowercase, uppercase, special characters, not username, regex patterns and expired passwords. Expired Passwords policy
|
||||
restricts a user from resetting his password to N old expired passwords. Multiple regex patterns, separated by comma,
|
||||
can be specified. If there's more than one regex added, password has to match all fully.
|
||||
Increasing number of Hash Iterations (n) does not worsen anything (and certainly not the cipher) and it greatly increases the
|
||||
resistance to dictionary attacks. However the drawback to increasing n is that it has some cost (CPU usage, energy, delay) for
|
||||
|
|
|
@ -36,6 +36,11 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
|
|
@ -3,23 +3,35 @@ package org.keycloak.events.log;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.models.KeycloakContext;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import javax.ws.rs.core.Cookie;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JBossLoggingEventListenerProvider implements EventListenerProvider {
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final Logger logger;
|
||||
private final Logger.Level successLevel;
|
||||
private final Logger.Level errorLevel;
|
||||
|
||||
public JBossLoggingEventListenerProvider(Logger logger) {
|
||||
public JBossLoggingEventListenerProvider(KeycloakSession session, Logger logger, Logger.Level successLevel, Logger.Level errorLevel) {
|
||||
this.session = session;
|
||||
this.logger = logger;
|
||||
this.successLevel = successLevel;
|
||||
this.errorLevel = errorLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
Logger.Level level = event.getError() != null ? Logger.Level.WARN : Logger.Level.INFO;
|
||||
Logger.Level level = event.getError() != null ? errorLevel : successLevel;
|
||||
|
||||
if (logger.isEnabled(level)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
@ -55,7 +67,31 @@ public class JBossLoggingEventListenerProvider implements EventListenerProvider
|
|||
}
|
||||
}
|
||||
|
||||
logger.log(level, sb.toString());
|
||||
if (logger.isTraceEnabled()) {
|
||||
KeycloakContext context = session.getContext();
|
||||
UriInfo uriInfo = context.getUri();
|
||||
HttpHeaders headers = context.getRequestHeaders();
|
||||
if (uriInfo != null) {
|
||||
sb.append(", requestUri=");
|
||||
sb.append(uriInfo.getRequestUri().toString());
|
||||
}
|
||||
|
||||
if (headers != null) {
|
||||
sb.append(", cookies=[");
|
||||
boolean f = true;
|
||||
for (Map.Entry<String, Cookie> e : headers.getCookies().entrySet()) {
|
||||
if (f) {
|
||||
f = false;
|
||||
} else {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(e.getValue().toString());
|
||||
}
|
||||
sb.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
logger.log(logger.isTraceEnabled() ? Logger.Level.TRACE : level, sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ import org.keycloak.events.EventListenerProvider;
|
|||
import org.keycloak.events.EventListenerProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import sun.rmi.runtime.Log;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -16,13 +19,18 @@ public class JBossLoggingEventListenerProviderFactory implements EventListenerPr
|
|||
|
||||
private static final Logger logger = Logger.getLogger("org.keycloak.events");
|
||||
|
||||
private Logger.Level successLevel;
|
||||
private Logger.Level errorLevel;
|
||||
|
||||
@Override
|
||||
public EventListenerProvider create(KeycloakSession session) {
|
||||
return new JBossLoggingEventListenerProvider(logger);
|
||||
return new JBossLoggingEventListenerProvider(session, logger, successLevel, errorLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
successLevel = Logger.Level.valueOf(config.get("success-level", "debug").toUpperCase());
|
||||
errorLevel = Logger.Level.valueOf(config.get("error-level", "warn").toUpperCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
],
|
||||
"clients": [
|
||||
{
|
||||
"name": "examples-admin-client",
|
||||
"clientId": "examples-admin-client",
|
||||
"enabled": true,
|
||||
"fullScopeAllowed": true,
|
||||
"baseUrl": "/examples-admin-client",
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
},
|
||||
"clients": [
|
||||
{
|
||||
"name": "basic-auth-service",
|
||||
"clientId": "basic-auth-service",
|
||||
"enabled": true,
|
||||
"adminUrl": "/basicauth",
|
||||
"baseUrl": "/basicauth",
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"clients": [
|
||||
{
|
||||
"name": "facebook-authentication",
|
||||
"clientId": "facebook-authentication",
|
||||
"enabled": true,
|
||||
"publicClient" : true,
|
||||
"adminUrl": "/facebook-authentication",
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"clients": [
|
||||
{
|
||||
"name": "google-authentication",
|
||||
"clientId": "google-authentication",
|
||||
"enabled": true,
|
||||
"publicClient" : true,
|
||||
"adminUrl": "/google-authentication",
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"clients": [
|
||||
{
|
||||
"name": "saml-broker-authentication",
|
||||
"clientId": "saml-broker-authentication",
|
||||
"enabled": true,
|
||||
"publicClient" : true,
|
||||
"adminUrl": "/saml-broker-authentication",
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
},
|
||||
"clients": [
|
||||
{
|
||||
"name": "http://localhost:8080/auth/realms/saml-broker-authentication-realm",
|
||||
"clientId": "http://localhost:8080/auth/realms/saml-broker-authentication-realm",
|
||||
"protocol": "saml",
|
||||
"enabled": true,
|
||||
"redirectUris": [
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
},
|
||||
"clients": [
|
||||
{
|
||||
"name": "twitter-authentication",
|
||||
"clientId": "twitter-authentication",
|
||||
"enabled": true,
|
||||
"publicClient" : true,
|
||||
"adminUrl": "/twitter-authentication",
|
||||
|
@ -52,7 +52,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "admin-client",
|
||||
"clientId": "admin-client",
|
||||
"enabled": true,
|
||||
"fullScopeAllowed": true,
|
||||
"baseUrl": "/admin-client",
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
],
|
||||
"clients": [
|
||||
{
|
||||
"name": "cordova",
|
||||
"clientId": "cordova",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"redirectUris": ["http://localhost"],
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
],
|
||||
"clients": [
|
||||
{
|
||||
"name": "angular-cors-product",
|
||||
"clientId": "angular-cors-product",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"baseUrl": "http://localhost:8080/angular-cors-product/index.html",
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
],
|
||||
"clients": [
|
||||
{
|
||||
"name": "customer-portal",
|
||||
"clientId": "customer-portal",
|
||||
"enabled": true,
|
||||
"adminUrl": "/customer-portal",
|
||||
"baseUrl": "/customer-portal",
|
||||
|
@ -104,7 +104,7 @@
|
|||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"name": "customer-portal-js",
|
||||
"clientId": "customer-portal-js",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"baseUrl": "/customer-portal-js",
|
||||
|
@ -113,7 +113,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "angular-product",
|
||||
"clientId": "angular-product",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"baseUrl": "/angular-product/index.html",
|
||||
|
@ -122,7 +122,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "customer-portal-cli",
|
||||
"clientId": "customer-portal-cli",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"redirectUris": [
|
||||
|
@ -131,7 +131,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "product-portal",
|
||||
"clientId": "product-portal",
|
||||
"enabled": true,
|
||||
"adminUrl": "/product-portal",
|
||||
"baseUrl": "/product-portal",
|
||||
|
@ -141,14 +141,14 @@
|
|||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"name": "database-service",
|
||||
"clientId": "database-service",
|
||||
"enabled": true,
|
||||
"adminUrl": "/database",
|
||||
"baseUrl": "/database",
|
||||
"bearerOnly": true
|
||||
},
|
||||
{
|
||||
"name": "third-party",
|
||||
"clientId": "third-party",
|
||||
"enabled": true,
|
||||
"consentRequired": true,
|
||||
"redirectUris": [
|
||||
|
@ -158,7 +158,7 @@
|
|||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"name": "admin-client",
|
||||
"clientId": "admin-client",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"directGrantsOnly": true,
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
},
|
||||
"clients": [
|
||||
{
|
||||
"name": "customer-portal",
|
||||
"clientId": "customer-portal",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8181/customer-portal",
|
||||
"baseUrl": "http://localhost:8181/customer-portal",
|
||||
|
@ -146,7 +146,7 @@
|
|||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"name": "product-portal",
|
||||
"clientId": "product-portal",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8181/product-portal",
|
||||
"baseUrl": "http://localhost:8181/product-portal",
|
||||
|
@ -156,7 +156,7 @@
|
|||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"name": "builtin-cxf-app",
|
||||
"clientId": "builtin-cxf-app",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8181/cxf",
|
||||
"baseUrl": "http://localhost:8181/cxf",
|
||||
|
@ -166,21 +166,21 @@
|
|||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"name": "custom-cxf-endpoint",
|
||||
"clientId": "custom-cxf-endpoint",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8282/PersonServiceCF",
|
||||
"baseUrl": "http://localhost:8282/PersonServiceCF",
|
||||
"bearerOnly": true
|
||||
},
|
||||
{
|
||||
"name": "admin-camel-endpoint",
|
||||
"clientId": "admin-camel-endpoint",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8383/admin-camel-endpoint",
|
||||
"baseUrl": "http://localhost:8383/admin-camel-endpoint",
|
||||
"bearerOnly": true
|
||||
},
|
||||
{
|
||||
"name": "ssh-jmx-admin-client",
|
||||
"clientId": "ssh-jmx-admin-client",
|
||||
"enabled": true,
|
||||
"publicClient": false,
|
||||
"directGrantsOnly": true,
|
||||
|
@ -194,4 +194,4 @@
|
|||
"roles": [ "admin", "jmxAdmin" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
],
|
||||
"clients": [
|
||||
{
|
||||
"name": "js-console",
|
||||
"clientId": "js-console",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"baseUrl": "/js-console",
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
],
|
||||
"clients": [
|
||||
{
|
||||
"name": "kerberos-app",
|
||||
"clientId": "kerberos-app",
|
||||
"enabled": true,
|
||||
"baseUrl": "/kerberos-portal",
|
||||
"redirectUris": [
|
||||
|
@ -91,4 +91,4 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
],
|
||||
"clients": [
|
||||
{
|
||||
"name": "multi-tenant",
|
||||
"clientId": "multi-tenant",
|
||||
"enabled": true,
|
||||
"adminUrl": "/multitenant/tenant1",
|
||||
"baseUrl": "/multitenant/tenant1",
|
||||
|
|
|
@ -47,7 +47,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.resources.AccountService;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.net.URI;
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.keycloak.account.freemarker.model;
|
|||
|
||||
import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
|
|
|
@ -89,12 +89,14 @@ identityProviderRemovedMessage=Identity Provider erfolgreich entfernt.
|
|||
accountDisabledMessage=Benutzerkonto ist gesperrt, bitte kontaktieren Sie den Admin.
|
||||
|
||||
accountTemporarilyDisabledMessage=Benutzerkonto ist tempor\u00E4r gesperrt, bitte kontaktieren Sie den Admin oder versuchen Sie es sp\u00E4ter noch einmal.
|
||||
invalidPasswordMinLengthMessage=Ung\u00FCltiges Passwort: minimum l\u00E4nge {0}.
|
||||
invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Zahl(en) beinhalten.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Kleinbuchstaben beinhalten.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Grossbuchstaben beinhalten.
|
||||
invalidPasswordMinLengthMessage=Ung\u00FCltiges Passwort\: minimum l\u00E4nge {0}.
|
||||
invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Zahl(en) beinhalten.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Kleinbuchstaben beinhalten.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Grossbuchstaben beinhalten.
|
||||
invalidPasswordMinSpecialCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Spezialzeichen beinhalten.
|
||||
invalidPasswordNotUsernameMessage=Ung\u00FCltiges Passwort\: darf nicht gleich sein wie Benutzername.
|
||||
invalidPasswordRegexPatternMessage=Ung\u00FCltiges Passwort\: nicht Regex-Muster (n) entsprechen.
|
||||
invalidPasswordHistoryMessage=Ung\u00FCltiges Passwort: darf nicht gleich einem der letzten {0} Passwortgeschichte.
|
||||
|
||||
locale_de=Deutsch
|
||||
locale_en=Englisch
|
||||
|
|
|
@ -93,7 +93,9 @@ invalidPasswordMinLowerCaseCharsMessage=Invalid password: must contain at least
|
|||
invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} numerical digits.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
|
||||
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
|
||||
invalidPasswordNotUsernameMessage=Invalid password\: must not be equal to the username.
|
||||
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
|
||||
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
|
||||
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
|
||||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
doSave=Salvar
|
||||
doCancel=Cancelar
|
||||
doLogOutAllSessions=Sair de todas as sess\u00F5es
|
||||
doRemove=Remover
|
||||
doAdd=Adicionar
|
||||
doSignOut=Sair
|
||||
|
||||
editAccountHtmlTtile=Editar Conta
|
||||
federatedIdentitiesHtmlTitle=Identidades Federadas
|
||||
accountLogHtmlTitle=Log da conta
|
||||
changePasswordHtmlTitle=Alterar senha
|
||||
sessionsHtmlTitle=Sess\u00F5es
|
||||
accountManagementTitle=Gerenciamento de Contas Keycloak
|
||||
authenticatorTitle=Autenticator
|
||||
|
||||
authenticatorCode=C\u00F3digo autenticador
|
||||
email=Email
|
||||
firstName=Primeiro nome
|
||||
|
@ -12,6 +27,36 @@ region=Estado
|
|||
postal_code=CEP
|
||||
country=Pa\u00EDs
|
||||
|
||||
requiredFields=Campos obrigat\u00F3rios
|
||||
allFieldsRequired=Todos os campos s\u00E3o obrigat\u00F3rios
|
||||
|
||||
backToApplication=« Voltar para aplica\u00E7\u00E3o
|
||||
backTo=Voltar para {0}
|
||||
|
||||
date=Data
|
||||
event=Evento
|
||||
ip=IP
|
||||
client=Cliente
|
||||
clients=Clientes
|
||||
details=Detalhes
|
||||
started=Iniciado
|
||||
lastAccess=\u00DAltimo acesso
|
||||
expires=Expira
|
||||
applications=Aplica\u00E7\u00F5es
|
||||
|
||||
account=Account
|
||||
federatedIdentity=Federated Identity
|
||||
authenticator=Authenticator
|
||||
sessions=Sessions
|
||||
log=Log
|
||||
|
||||
configureAuthenticators=Autenticadores Configurados
|
||||
mobile=Mobile
|
||||
totpStep1=Instalar <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> em seu dispositivo mobile.
|
||||
totpStep2=Abra o aplicativo e escaneie o c\u00F3digo de barras ou digite o c\u00F3digo.
|
||||
totpStep3=Digite o c\u00F3digo fornecido pelo aplicativo e clique em Salvar para concluir a configura\u00E7\u00E3o.
|
||||
|
||||
|
||||
missingFirstNameMessage=Por favor, informe o primeiro nome.
|
||||
invalidEmailMessage=E-mail inv\u00E1lido.
|
||||
missingLastNameMessage=Por favor, informe o sobrenome.
|
||||
|
@ -41,18 +86,18 @@ federatedIdentityRemovingLastProviderMessage=Voc\u00EA n\u00E3o pode remover a \
|
|||
identityProviderRedirectErrorMessage=Falha ao redirecionar para o provedor de identidade
|
||||
identityProviderRemovedMessage=Provedor de identidade removido com sucesso
|
||||
|
||||
accountDisabledMessage=Conta desativada, contate administrador
|
||||
|
||||
doLogOutAllSessions=Sair de todas sess\u00F5es
|
||||
accountDisabledMessage=Conta desativada, contate o administrador
|
||||
|
||||
accountTemporarilyDisabledMessage=A conta est\u00E1 temporariamente indispon\u00EDvel, contate administrador ou tente novamente mais tarde
|
||||
invalidPasswordMinLengthMessage=Senha inv\u00E1lida: comprimento m\u00EDnimo {0}
|
||||
invalidPasswordMinLowerCaseCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres min\u00FAsculos
|
||||
invalidPasswordMinDigitsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} d\u00EDgitos num\u00E9ricos
|
||||
invalidPasswordMinUpperCaseCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres mai\u00FAsculos
|
||||
invalidPasswordMinSpecialCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres especiais
|
||||
invalidPasswordMinLengthMessage=Senha inv\u00E1lida\: comprimento m\u00EDnimo {0}
|
||||
invalidPasswordMinLowerCaseCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres min\u00FAsculos
|
||||
invalidPasswordMinDigitsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} d\u00EDgitos num\u00E9ricos
|
||||
invalidPasswordMinUpperCaseCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres mai\u00FAsculos
|
||||
invalidPasswordMinSpecialCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres especiais
|
||||
invalidPasswordNotUsernameMessage=Senha inv\u00E1lida\: n\u00E3o deve ser igual ao nome de usu\u00E1rio
|
||||
invalidPasswordRegexPatternMessage=Senha inv\u00E1lida\: n\u00E3o correspondem ao padr\u00E3o regex(s).
|
||||
invalidPasswordHistoryMessage=Senha inv\u00E1lida\: n\u00E3o deve ser igual a qualquer uma {0} \u00FAltima hist\u00F3ria senha.
|
||||
|
||||
locale_de=Deutsch
|
||||
locale_en=English
|
||||
locale_pt-BR=Portugu\u00EAs (BR)
|
||||
locale_pt-BR=Portugu\u00EAs (BR)
|
|
@ -900,14 +900,15 @@ module.factory('PasswordPolicy', function() {
|
|||
var p = {};
|
||||
|
||||
p.policyMessages = {
|
||||
hashIterations: "Number of hashing iterations. Default is 1. Recommended is 50000.",
|
||||
length: "Minimal password length (integer type). Default value is 8.",
|
||||
digits: "Minimal number (integer type) of digits in password. Default value is 1.",
|
||||
lowerCase: "Minimal number (integer type) of lowercase characters in password. Default value is 1.",
|
||||
upperCase: "Minimal number (integer type) of uppercase characters in password. Default value is 1.",
|
||||
specialChars: "Minimal number (integer type) of special characters in password. Default value is 1.",
|
||||
notUsername: "Block passwords that are equal to the username",
|
||||
regexPatterns: "Block passwords that do not match all of the regex patterns (string type)."
|
||||
hashIterations: "Number of hashing iterations. Default is 1. Recommended is 50000.",
|
||||
length: "Minimal password length (integer type). Default value is 8.",
|
||||
digits: "Minimal number (integer type) of digits in password. Default value is 1.",
|
||||
lowerCase: "Minimal number (integer type) of lowercase characters in password. Default value is 1.",
|
||||
upperCase: "Minimal number (integer type) of uppercase characters in password. Default value is 1.",
|
||||
specialChars: "Minimal number (integer type) of special characters in password. Default value is 1.",
|
||||
notUsername: "Block passwords that are equal to the username",
|
||||
regexPatterns: "Block passwords that do not match all of the regex patterns (string type).",
|
||||
passwordHistory: "Block passwords that are equal to previous passwords. Default value is 3."
|
||||
}
|
||||
|
||||
p.allPolicies = [
|
||||
|
@ -918,7 +919,8 @@ module.factory('PasswordPolicy', function() {
|
|||
{ name: 'upperCase', value: 1 },
|
||||
{ name: 'specialChars', value: 1 },
|
||||
{ name: 'notUsername', value: 1 },
|
||||
{ name: 'regexPatterns', value: ''}
|
||||
{ name: 'regexPatterns', value: ''},
|
||||
{ name: 'passwordHistory', value: 3 }
|
||||
];
|
||||
|
||||
p.parse = function(policyString) {
|
||||
|
|
|
@ -125,12 +125,14 @@ accountPasswordUpdatedMessage=Ihr Passwort wurde aktualisiert.
|
|||
|
||||
noAccessMessage=Kein Zugriff
|
||||
|
||||
invalidPasswordMinLengthMessage=Ung\u00FCltiges Passwort: minimum l\u00E4nge {0}.
|
||||
invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Zahl(en) beinhalten.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Kleinbuchstaben beinhalten.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Grossbuchstaben beinhalten.
|
||||
invalidPasswordMinSpecialCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Spezialzeichen beinhalten.
|
||||
invalidPasswordMinLengthMessage=Ung\u00FCltiges Passwort\: minimum l\u00E4nge {0}.
|
||||
invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Zahl(en) beinhalten.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Kleinbuchstaben beinhalten.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Grossbuchstaben beinhalten.
|
||||
invalidPasswordMinSpecialCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Spezialzeichen beinhalten.
|
||||
invalidPasswordNotUsernameMessage=Ung\u00FCltiges Passwort\: darf nicht gleich sein wie Benutzername.
|
||||
invalidPasswordRegexPatternMessage=Ung\u00FCltiges Passwort\: nicht Regex-Muster (n) entsprechen.
|
||||
invalidPasswordHistoryMessage=Ung\u00FCltiges Passwort\: darf nicht gleich einem der letzten {0} Passwortgeschichte.
|
||||
|
||||
failedToProcessResponseMessage=Konnte Response nicht verarbeiten.
|
||||
httpsRequiredMessage=HTTPS erforderlich.
|
||||
|
|
|
@ -69,7 +69,7 @@ personalInfo=Personal Info:
|
|||
|
||||
role_admin=Admin
|
||||
role_realm-admin=Realm Admin
|
||||
role_create-realm=Crate realm
|
||||
role_create-realm=Create realm
|
||||
role_view-realm=View realm
|
||||
role_view-users=View users
|
||||
role_view-applications=View applications
|
||||
|
@ -127,7 +127,9 @@ invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} nume
|
|||
invalidPasswordMinLowerCaseCharsMessage=Invalid password: must contain at least {0} lower case characters.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
|
||||
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
|
||||
invalidPasswordNotUsernameMessage=Invalid password\: must not be equal to the username.
|
||||
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
|
||||
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
|
||||
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
|
||||
|
||||
failedToProcessResponseMessage=Failed to process response
|
||||
httpsRequiredMessage=HTTPS required
|
||||
|
|
|
@ -11,8 +11,6 @@ registerWithTitle=Registre-se com {0}
|
|||
registerWithTitleHtml=Registre-se com <strong>{0}</strong>
|
||||
loginTitle=Entrar em {0}
|
||||
loginTitleHtml=Entrar em <strong>{0}</strong>
|
||||
loginOauthTitle=Acesso tempor\u00E1rio para {0}
|
||||
loginOauthTitleHtml=Acesso tempor\u00E1rio para <strong>{0}<strong> solicitado por <strong>{1}</strong>.
|
||||
loginTotpTitle=Configura\u00E7\u00E3o do autenticador mobile
|
||||
loginProfileTitle=Atualiza\u00E7\u00E3o de Informa\u00E7\u00F5es da Conta
|
||||
oauthGrantTitle=Concess\u00E3o OAuth
|
||||
|
@ -22,12 +20,14 @@ errorTitleHtml=N\u00F3s <strong>lamentamos</strong> ...
|
|||
emailVerifyTitle=Verifica\u00E7\u00E3o de e-mail
|
||||
emailForgotTitle=Esqueceu sua senha?
|
||||
updatePasswordTitle=Atualiza\u00E7\u00E3o de senha
|
||||
codeSuccessTitle=C\u00F3digo de sucesso
|
||||
codeErrorTitle=C\u00F3digo de erro\: {0}
|
||||
|
||||
noAccount=Novo usu\u00E1rio?
|
||||
username=Nome de usu\u00E1rio
|
||||
givenName=Primeiro nome
|
||||
usernameOrEmail=Nome de usu\u00E1rio ou email
|
||||
firstName=Primeiro nome
|
||||
givenName=Primeiro nome
|
||||
fullName=Nome completo
|
||||
lastName=Sobrenome
|
||||
familyName=Sobrenome
|
||||
|
@ -36,7 +36,7 @@ password=Senha
|
|||
passwordConfirm=Confirme a senha
|
||||
passwordNew=Nova senha
|
||||
passwordNewConfirm=Confirma\u00E7\u00E3o da nova senha
|
||||
rememberMe=Relembre-me
|
||||
rememberMe=Mantenha-me conectado
|
||||
authenticatorCode=C\u00F3digo autenticador
|
||||
address=Endere\u00E7o
|
||||
street=Logradouro
|
||||
|
@ -48,11 +48,12 @@ emailVerified=Email verificado
|
|||
gssDelegationCredential=gss delega\u00E7\u00E3o credencial
|
||||
|
||||
loginTotpStep1=Instale <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> em seu celular
|
||||
loginTotpStep2=Abra o aplicativo e escanei o c\u00F3digo de barras ou digite o c\u00F3digo
|
||||
loginTotpStep3=Digite o c\u00F3digo autenticador fornecido pelo aplicativo e clique em Enviar para concluir a configura\u00E7\u00E3o
|
||||
loginTotpStep2=Abra o aplicativo e escaneie o c\u00F3digo de barras ou digite o c\u00F3digo
|
||||
loginTotpStep3=Digite o c\u00F3digo fornecido pelo aplicativo e clique em Enviar para concluir a configura\u00E7\u00E3o
|
||||
loginTotpOneTime=C\u00F3digo autenticador
|
||||
|
||||
oauthGrantRequest=Voc\u00EA concede esses privil\u00E9gios de acesso?
|
||||
inResource=em <strong>{0}</strong>
|
||||
|
||||
emailVerifyInstruction1=Um e-mail com instru\u00E7\u00F5es para verificar o seu endere\u00E7o de e-mail foi enviado para voc\u00EA.
|
||||
emailVerifyInstruction2=Voc\u00EA n\u00E3o recebeu um c\u00F3digo de verifica\u00E7\u00E3o em seu e-mail?
|
||||
|
@ -62,6 +63,8 @@ backToLogin=« Voltar
|
|||
|
||||
emailInstruction=Digite seu nome de usu\u00E1rio ou endere\u00E7o de email e n\u00F3s lhe enviaremos instru\u00E7\u00F5es sobre como criar uma nova senha.
|
||||
|
||||
copyCodeInstruction=Por favor, copie o c\u00F3digo e cole-o em sua aplica\u00E7\u00E3o:
|
||||
|
||||
personalInfo=Informa\u00E7\u00F5es pessoais:
|
||||
|
||||
role_admin=Admin
|
||||
|
@ -119,12 +122,14 @@ accountPasswordUpdatedMessage=Sua senha foi atualizada
|
|||
|
||||
noAccessMessage=Sem acesso
|
||||
|
||||
invalidPasswordMinLengthMessage=Senha inv\u00E1lida: comprimento m\u00EDnimo {0}
|
||||
invalidPasswordMinDigitsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} d\u00EDgitos num\u00E9ricos
|
||||
invalidPasswordMinLowerCaseCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres min\u00FAsculos
|
||||
invalidPasswordMinUpperCaseCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres mai\u00FAsculos
|
||||
invalidPasswordMinSpecialCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres especiais
|
||||
invalidPasswordNotUsernameMessage=Senha inv\u00E1lida: n\u00E3o deve ser igual ao nome de usu\u00E1rio
|
||||
invalidPasswordMinLengthMessage=Senha inv\u00E1lida\: comprimento m\u00EDnimo {0}
|
||||
invalidPasswordMinDigitsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} d\u00EDgitos num\u00E9ricos
|
||||
invalidPasswordMinLowerCaseCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres min\u00FAsculos
|
||||
invalidPasswordMinUpperCaseCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres mai\u00FAsculos
|
||||
invalidPasswordMinSpecialCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres especiais
|
||||
invalidPasswordNotUsernameMessage=Senha inv\u00E1lida\: n\u00E3o deve ser igual ao nome de usu\u00E1rio
|
||||
invalidPasswordRegexPatternMessage=Senha inv\u00E1lida\: n\u00E3o correspondem ao padr\u00E3o regex(s).
|
||||
invalidPasswordHistoryMessage=Senha inv\u00E1lida\: n\u00E3o deve ser igual a qualquer uma {0} \u00FAltima hist\u00F3ria senha.
|
||||
|
||||
failedToProcessResponseMessage=Falha ao processar a resposta
|
||||
httpsRequiredMessage=HTTPS requerido
|
||||
|
|
|
@ -21,12 +21,6 @@ import org.keycloak.provider.Provider;
|
|||
*/
|
||||
public interface LoginFormsProvider extends Provider {
|
||||
|
||||
public LoginFormsProvider setRealm(RealmModel realm);
|
||||
|
||||
public LoginFormsProvider setUriInfo(UriInfo uriInfo);
|
||||
|
||||
public LoginFormsProvider setHttpHeaders(HttpHeaders httpHeaders);
|
||||
|
||||
public Response createResponse(UserModel.RequiredAction action);
|
||||
|
||||
public Response createLogin();
|
||||
|
@ -67,14 +61,8 @@ public interface LoginFormsProvider extends Provider {
|
|||
|
||||
public LoginFormsProvider setSuccess(String message, Object ... parameters);
|
||||
|
||||
public LoginFormsProvider setWarning(String message, Object ... parameters);
|
||||
|
||||
public LoginFormsProvider setUser(UserModel user);
|
||||
|
||||
public LoginFormsProvider setClient(ClientModel client);
|
||||
|
||||
public LoginFormsProvider setQueryParams(MultivaluedMap<String, String> queryParams);
|
||||
|
||||
public LoginFormsProvider setResponseHeader(String headerName, String headerValue);
|
||||
|
||||
public LoginFormsProvider setFormData(MultivaluedMap<String, String> formData);
|
||||
|
|
|
@ -37,9 +37,8 @@ import org.keycloak.models.RoleModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
@ -79,39 +78,20 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
private KeycloakSession session;
|
||||
private FreeMarkerUtil freeMarker;
|
||||
private RealmModel realm;
|
||||
|
||||
private UserModel user;
|
||||
|
||||
private ClientModel client;
|
||||
private ClientSessionModel clientSession;
|
||||
|
||||
private UriInfo uriInfo;
|
||||
|
||||
private HttpHeaders httpHeaders;
|
||||
|
||||
public FreeMarkerLoginFormsProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
|
||||
this.session = session;
|
||||
this.freeMarker = freeMarker;
|
||||
}
|
||||
|
||||
public LoginFormsProvider setRealm(RealmModel realm) {
|
||||
this.realm = realm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginFormsProvider setUriInfo(UriInfo uriInfo) {
|
||||
this.uriInfo = uriInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setHttpHeaders(HttpHeaders httpHeaders) {
|
||||
this.httpHeaders = httpHeaders;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Response createResponse(UserModel.RequiredAction action) {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
UriInfo uriInfo = session.getContext().getUri();
|
||||
|
||||
String actionMessage;
|
||||
LoginFormsPages page;
|
||||
|
||||
|
@ -150,13 +130,17 @@ import java.util.concurrent.TimeUnit;
|
|||
}
|
||||
|
||||
if (messages == null) {
|
||||
setWarning(actionMessage);
|
||||
setMessage(MessageType.WARNING, actionMessage);
|
||||
}
|
||||
|
||||
return createResponse(page);
|
||||
}
|
||||
|
||||
private Response createResponse(LoginFormsPages page) {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
ClientModel client = session.getContext().getClient();
|
||||
UriInfo uriInfo = session.getContext().getUri();
|
||||
|
||||
MultivaluedMap<String, String> queryParameterMap = queryParams != null ? queryParams : new MultivaluedMapImpl<String, String>();
|
||||
|
||||
String requestURI = uriInfo.getBaseUri().getPath();
|
||||
|
@ -191,7 +175,7 @@ import java.util.concurrent.TimeUnit;
|
|||
}
|
||||
|
||||
Properties messagesBundle;
|
||||
Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, httpHeaders);
|
||||
Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, session.getContext().getRequestHeaders());
|
||||
try {
|
||||
messagesBundle = theme.getMessages(locale);
|
||||
attributes.put("msg", new MessageFormatterMethod(locale, messagesBundle));
|
||||
|
@ -222,7 +206,7 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
if (realm != null) {
|
||||
attributes.put("realm", new RealmBean(realm));
|
||||
attributes.put("social", new IdentityProviderBean(realm, baseUri, this.uriInfo));
|
||||
attributes.put("social", new IdentityProviderBean(realm, baseUri, uriInfo));
|
||||
attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
|
||||
|
||||
if (realm.isInternationalizationEnabled()) {
|
||||
|
@ -365,22 +349,11 @@ import java.util.concurrent.TimeUnit;
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FreeMarkerLoginFormsProvider setWarning(String message, Object ... parameters) {
|
||||
setMessage(MessageType.WARNING, message, parameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FreeMarkerLoginFormsProvider setUser(UserModel user) {
|
||||
this.user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FreeMarkerLoginFormsProvider setClient(ClientModel client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FreeMarkerLoginFormsProvider setFormData(MultivaluedMap<String, String> formData) {
|
||||
this.formData = formData;
|
||||
return this;
|
||||
|
@ -411,12 +384,6 @@ import java.util.concurrent.TimeUnit;
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setQueryParams(MultivaluedMap<String, String> queryParams) {
|
||||
this.queryParams = queryParams;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setActionUri(URI actionUri) {
|
||||
this.actionUri = actionUri;
|
||||
|
|
|
@ -23,7 +23,7 @@ package org.keycloak.login.freemarker.model;
|
|||
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
|
|
|
@ -23,7 +23,7 @@ package org.keycloak.login.freemarker.model;
|
|||
|
||||
import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public class Keycloak {
|
|||
|
||||
target = client.target(config.getServerUrl());
|
||||
|
||||
target.register(new BearerAuthFilter(tokenManager.getAccessTokenString()));
|
||||
target.register(new BearerAuthFilter(tokenManager));
|
||||
}
|
||||
|
||||
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret){
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.admin.client.resource;
|
||||
|
||||
import org.keycloak.admin.client.token.TokenManager;
|
||||
|
||||
import javax.ws.rs.client.ClientRequestContext;
|
||||
import javax.ws.rs.client.ClientRequestFilter;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
@ -11,14 +13,23 @@ import java.io.IOException;
|
|||
public class BearerAuthFilter implements ClientRequestFilter {
|
||||
|
||||
private final String tokenString;
|
||||
private final TokenManager tokenManager;
|
||||
|
||||
public BearerAuthFilter(String tokenString) {
|
||||
this.tokenString = tokenString;
|
||||
this.tokenManager = null;
|
||||
}
|
||||
|
||||
public BearerAuthFilter(TokenManager tokenManager) {
|
||||
this.tokenManager = tokenManager;
|
||||
this.tokenString = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void filter(ClientRequestContext requestContext) throws IOException {
|
||||
String authHeader = "Bearer " + tokenString;
|
||||
String authHeader = "Bearer " + (tokenManager != null ? tokenManager.getAccessTokenString() : tokenString);
|
||||
|
||||
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public interface RoleMappingResource {
|
|||
@Path("realm")
|
||||
public RoleScopeResource realmLevel();
|
||||
|
||||
@Path("applications/{appName}")
|
||||
public RoleScopeResource applicationLevel(@PathParam("appName") String appName);
|
||||
@Path("clients/{clientId}")
|
||||
public RoleScopeResource clientLevel(@PathParam("clientId") String clientId);
|
||||
|
||||
}
|
||||
|
|
|
@ -66,8 +66,7 @@ public class TokenManager {
|
|||
|
||||
Form form = new Form()
|
||||
.param("grant_type", "refresh_token")
|
||||
.param("username", config.getUsername())
|
||||
.param("password", config.getPassword());
|
||||
.param("refresh_token", currentToken.getRefreshToken());
|
||||
|
||||
if(config.isPublicClient()){
|
||||
form.param("client_id", config.getClientId());
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
Test with various databases
|
||||
===========================
|
||||
|
||||
MongoDB
|
||||
-------
|
||||
|
||||
The Keycloak testsuite uses an embedded MongoDB when running tests so you don't have to have one running locally.
|
||||
|
||||
Run tests:
|
||||
|
||||
mvn install -Pmongo
|
||||
|
||||
|
||||
MySQL
|
||||
-----
|
||||
|
||||
Use the official [MySQL docker image](https://registry.hub.docker.com/_/mysql/).
|
||||
The simplest way to test with MySQL is to use the official [MySQL docker image](https://registry.hub.docker.com/_/mysql/).
|
||||
|
||||
Start MySQL:
|
||||
|
||||
|
@ -13,7 +22,7 @@ Start MySQL:
|
|||
|
||||
Run tests:
|
||||
|
||||
mvn clean install -Dkeycloak.connectionsJpa.url=jdbc:mysql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql`/keycloak -Dkeycloak.connectionsJpa.driver=com.mysql.jdbc.Driver -Dkeycloak.connectionsJpa.user=keycloak -Dkeycloak.connectionsJpa.password=keycloak
|
||||
mvn install -Dkeycloak.connectionsJpa.url=jdbc:mysql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql`/keycloak -Dkeycloak.connectionsJpa.driver=com.mysql.jdbc.Driver -Dkeycloak.connectionsJpa.user=keycloak -Dkeycloak.connectionsJpa.password=keycloak
|
||||
|
||||
Stop MySQl:
|
||||
|
||||
|
@ -23,7 +32,7 @@ Stop MySQl:
|
|||
PostgreSQL
|
||||
----------
|
||||
|
||||
Use the official [PostgreSQL docker image](https://registry.hub.docker.com/_/postgres/).
|
||||
The simplest way to test with PostgreSQL is to use the official [PostgreSQL docker image](https://registry.hub.docker.com/_/postgres/).
|
||||
|
||||
Start PostgreSQL:
|
||||
|
||||
|
@ -31,7 +40,7 @@ Start PostgreSQL:
|
|||
|
||||
Run tests:
|
||||
|
||||
mvn clean install -Dkeycloak.connectionsJpa.url=jdbc:postgresql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' postgres`:5432/keycloak -Dkeycloak.connectionsJpa.driver=org.postgresql.Driver -Dkeycloak.connectionsJpa.user=keycloak -Dkeycloak.connectionsJpa.password=keycloak
|
||||
mvn install -Dkeycloak.connectionsJpa.url=jdbc:postgresql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' postgres`:5432/keycloak -Dkeycloak.connectionsJpa.driver=org.postgresql.Driver -Dkeycloak.connectionsJpa.user=keycloak -Dkeycloak.connectionsJpa.password=keycloak
|
||||
|
||||
Stop PostgreSQL:
|
||||
|
||||
|
|
70
misc/HackingOnKeycloak.md
Normal file
70
misc/HackingOnKeycloak.md
Normal file
|
@ -0,0 +1,70 @@
|
|||
Hacking on Keycloak
|
||||
===================
|
||||
|
||||
GitHub Repository
|
||||
-----------------
|
||||
|
||||
### Create a GitHub account if you don't already have one
|
||||
|
||||
[Join GitHub](https://github.com/join)
|
||||
|
||||
### Fork Keycloak repository into your account
|
||||
|
||||
[https://github.com/keycloak/keycloak](https://github.com/keycloak/keycloak)
|
||||
|
||||
### Clone your newly forked copy onto your local workspace
|
||||
|
||||
git clone https://github.com/<your username>/keycloak.git
|
||||
cd keycloak
|
||||
|
||||
### Add a remote ref to upstream for pulling future updates
|
||||
|
||||
git remote add upstream https://github.com/keycloak/keycloak.git
|
||||
|
||||
### Pull later updates from upstream
|
||||
|
||||
git fetch upstream
|
||||
git rebase upstream/master
|
||||
|
||||
|
||||
Discuss changes
|
||||
---------------
|
||||
|
||||
Before starting work on a new feature or anything besides a minor bug fix join the [Keycloak Dev mailing list](https://lists.jboss.org/mailman/listinfo/keycloak-dev)
|
||||
and send a mail about your proposed changes. This is vital as otherwise you may waste days implementing a feature that is later rejected.
|
||||
|
||||
Once you have received feedback from the mailing list if there's not one already create a (JIRA issue)[https://issues.jboss.org/browse/KEYCLOAK].
|
||||
|
||||
|
||||
Implement changes
|
||||
-----------------
|
||||
|
||||
We don't currently enforce a code style in Keycloak, but a good reference is the code style used by WildFly. This can be
|
||||
retrieved from (https://github.com/wildfly/wildfly-core/tree/master/ide-configs)[https://github.com/wildfly/wildfly-core/tree/master/ide-configs].
|
||||
|
||||
If your changes requires updates to the database read [Updating Database Schema](UpdatingDatabaseSchema.md).
|
||||
|
||||
To try your changes out manually you can quickly start Keycloak from within your IDEA or Maven, to find out how to do this
|
||||
read [Testsuite](Testsuite.md). It's also important that you add tests to the testsuite for your changes.
|
||||
|
||||
|
||||
Get your changes merged into upstream
|
||||
-------------------------------------
|
||||
|
||||
Here's a quick check list for a good pull request (PR):
|
||||
|
||||
* Discussed and agreed on Keycloak Dev mailing list
|
||||
* One commit per PR
|
||||
* One feature/change per PR
|
||||
* No changes to code not directly related to your change (e.g. no formatting changes or refactoring to existing code, if you want to refactor/improve existing code that's a separate discussion to mailing list and JIRA issue)
|
||||
* A JIRA associated with your PR (include the JIRA issue number in commit comment)
|
||||
* All tests in testsuite pass
|
||||
* Do a rebase on upstream master
|
||||
|
||||
Once you're happy with your changes go to GitHub and create a PR.
|
||||
|
||||
|
||||
Release Keycloak
|
||||
----------------
|
||||
|
||||
* [Release Process](ReleaseProcess.md)
|
80
misc/ReleaseProcess.md
Normal file
80
misc/ReleaseProcess.md
Normal file
|
@ -0,0 +1,80 @@
|
|||
## Test
|
||||
|
||||
* Make sure tests pass on Travis
|
||||
* Make sure tests pass on Jenkins
|
||||
* Go through the (manual testing)[https://docs.google.com/spreadsheets/d/17C_WEHNE03r5DxN71OXGJaytjA6_WjZKCXRcsnmNQD4]
|
||||
|
||||
## Create release
|
||||
|
||||
* Get from github
|
||||
```
|
||||
$ git@github.com:keycloak/keycloak.git
|
||||
```
|
||||
|
||||
* Build everything to make sure its kosher.
|
||||
```
|
||||
$ cd keycloak
|
||||
$ mvn install
|
||||
```
|
||||
|
||||
* Build javadoc and jaxrs-doc
|
||||
```
|
||||
$ mvn javadoc:javadoc
|
||||
# This is for jaxrs-docs
|
||||
$ cd services
|
||||
$ mvn package
|
||||
# back to root keycloak dir
|
||||
$ cd ..
|
||||
```
|
||||
|
||||
* Upload to Nexus (from project root)
|
||||
```
|
||||
$ mvn -Pdistribution deploy
|
||||
```
|
||||
|
||||
* Login to Nexus and release the maven repository uploads in the staging area.
|
||||
|
||||
* Upload src and distro zips to sf.net/projects/keycloak. This includes appliance, war-dist, each adapter, and proxy distros. You need to create an adapters folder on sf.net and each uploaded adapter there.
|
||||
|
||||
* Upload documentation to docs.jboss.org
|
||||
```
|
||||
$ sftp keycloak@filemgmt.jboss.org
|
||||
> cd docs_htdocs/keycloak/docs
|
||||
> mkdir 1.0.0.Final (or whatever version)
|
||||
> quit
|
||||
|
||||
$ unzip distribution/examples-docs-zip/target/keycloak-examples-docs-dist.zip
|
||||
$ cd docs
|
||||
$ rsync -rv --protocol=28 * keycloak@filemgmt.jboss.org:/docs_htdocs/keycloak/docs/1.0.0.Final
|
||||
```
|
||||
|
||||
* tag release
|
||||
```
|
||||
$ git tag -a -m "1.0.0.Final" 1.0.0.Final
|
||||
$ git push --tags
|
||||
```
|
||||
|
||||
## Update Bower
|
||||
```
|
||||
$ git clone https://github.com/keycloak/keycloak-js-bower
|
||||
$ cp <keycloak.js from dist> dist/keycloak-js-bower
|
||||
$ cp <keycloak.min.js from dist> dist/keycloak-js-bower
|
||||
```
|
||||
Edit bower.json and set version (include -beta -rc, but not -final). Create tag.
|
||||
|
||||
## Update OpenShift Cartridge
|
||||
|
||||
See https://github.com/keycloak/openshift-keycloak-cartridge for details
|
||||
|
||||
## Update Docker image
|
||||
|
||||
Instructions TBD
|
||||
|
||||
## Maven central
|
||||
|
||||
Releases are automatically synced to Maven central, but this can take up to one day
|
||||
|
||||
## Announce
|
||||
|
||||
* Update Magnolia site to link keycloak docs and announcements.
|
||||
* Write a blog and email about release including links to download, migration guide, docs, and blurb about what's new
|
|
@ -8,14 +8,10 @@ The testsuite uses Sellenium. By default it uses the HtmlUnit WebDriver, but can
|
|||
|
||||
To run the tests with Firefox add `-Dbrowser=firefox` or for Chrome add `-Dbrowser=chrome`
|
||||
|
||||
Mongo
|
||||
-----
|
||||
Database
|
||||
--------
|
||||
|
||||
The testsuite is executed with JPA model implementation with data saved in H2 database by default. To run testsuite with Mongo model, just add property `-Dkeycloak.realm.provider=mongo` when executing it.
|
||||
This single property will cause that mongo will be used for realm-model, user-model and audit.
|
||||
|
||||
Note that this will automatically run embedded Mongo database on localhost/27018 and it will stop it after whole testsuite is finished.
|
||||
So you don't need to have Mongo installed on your laptop to run mongo execution tests.
|
||||
By default the testsuite uses an embedded H2 database to test with other databases see (Database Testing)[DatabaseTesting.md].
|
||||
|
||||
Test utils
|
||||
==========
|
|
@ -76,4 +76,4 @@ It should be added last to the `DefaultMongoUpdaterProvider#updates` array.
|
|||
Testing database migration
|
||||
--------------------------
|
||||
|
||||
Get the database from an old version of Keycloak that includes the demo applications. Start the server with this and test it.
|
||||
Get the database from an old version of Keycloak that includes the demo applications. Start the server with this and test it.
|
|
@ -14,6 +14,11 @@
|
|||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.iharder</groupId>
|
||||
<artifactId>base64</artifactId>
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface KeycloakContext {
|
||||
|
||||
UriInfo getUri();
|
||||
|
||||
HttpHeaders getRequestHeaders();
|
||||
|
||||
RealmModel getRealm();
|
||||
|
||||
void setRealm(RealmModel realm);
|
||||
|
||||
ClientModel getClient();
|
||||
|
||||
void setClient(ClientModel client);
|
||||
|
||||
}
|
|
@ -10,6 +10,8 @@ import java.util.Set;
|
|||
*/
|
||||
public interface KeycloakSession {
|
||||
|
||||
KeycloakContext getContext();
|
||||
|
||||
KeycloakTransactionManager getTransaction();
|
||||
|
||||
<T extends Provider> T getProvider(Class<T> clazz);
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
|
@ -18,6 +22,7 @@ public class PasswordPolicy {
|
|||
public static final String INVALID_PASSWORD_MIN_SPECIAL_CHARS_MESSAGE = "invalidPasswordMinSpecialCharsMessage";
|
||||
public static final String INVALID_PASSWORD_NOT_USERNAME = "invalidPasswordNotUsernameMessage";
|
||||
public static final String INVALID_PASSWORD_REGEX_PATTERN = "invalidPasswordRegexPatternMessage";
|
||||
public static final String INVALID_PASSWORD_HISTORY = "invalidPasswordHistoryMessage";
|
||||
|
||||
private List<Policy> policies;
|
||||
private String policyString;
|
||||
|
@ -67,10 +72,12 @@ public class PasswordPolicy {
|
|||
} else if (name.equals(HashIterations.NAME)) {
|
||||
list.add(new HashIterations(args));
|
||||
} else if (name.equals(RegexPatterns.NAME)) {
|
||||
for(String regexPattern : args) {
|
||||
for (String regexPattern : args) {
|
||||
Pattern.compile(regexPattern);
|
||||
}
|
||||
list.add(new RegexPatterns(args));
|
||||
} else if (name.equals(PasswordHistory.NAME)) {
|
||||
list.add(new PasswordHistory(args));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
|
@ -92,9 +99,35 @@ public class PasswordPolicy {
|
|||
return -1;
|
||||
}
|
||||
|
||||
public Error validate(String username, String password) {
|
||||
/**
|
||||
*
|
||||
* @return -1 if no expired passwords setting
|
||||
*/
|
||||
public int getExpiredPasswords() {
|
||||
if (policies == null)
|
||||
return -1;
|
||||
for (Policy p : policies) {
|
||||
Error error = p.validate(username, password);
|
||||
if (p instanceof PasswordHistory) {
|
||||
return ((PasswordHistory) p).passwordHistoryPolicyValue;
|
||||
}
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Error validate(UserModel user, String password) {
|
||||
for (Policy p : policies) {
|
||||
Error error = p.validate(user, password);
|
||||
if (error != null) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Error validate(String user, String password) {
|
||||
for (Policy p : policies) {
|
||||
Error error = p.validate(user, password);
|
||||
if (error != null) {
|
||||
return error;
|
||||
}
|
||||
|
@ -103,7 +136,8 @@ public class PasswordPolicy {
|
|||
}
|
||||
|
||||
private static interface Policy {
|
||||
public Error validate(String username, String password);
|
||||
public Error validate(UserModel user, String password);
|
||||
public Error validate(String user, String password);
|
||||
}
|
||||
|
||||
public static class Error {
|
||||
|
@ -131,9 +165,15 @@ public class PasswordPolicy {
|
|||
public HashIterations(String[] args) {
|
||||
iterations = intArg(NAME, 1, args);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Error validate(String username, String password) {
|
||||
public Error validate(String user, String password) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +188,11 @@ public class PasswordPolicy {
|
|||
public Error validate(String username, String password) {
|
||||
return username.equals(password) ? new Error(INVALID_PASSWORD_NOT_USERNAME) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
return validate(user.getUsername(), password);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Length implements Policy {
|
||||
|
@ -157,11 +202,17 @@ public class PasswordPolicy {
|
|||
public Length(String[] args) {
|
||||
min = intArg(NAME, 8, args);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Error validate(String username, String password) {
|
||||
return password.length() < min ? new Error(INVALID_PASSWORD_MIN_LENGTH_MESSAGE, min) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
return validate(user.getUsername(), password);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Digits implements Policy {
|
||||
|
@ -171,6 +222,7 @@ public class PasswordPolicy {
|
|||
public Digits(String[] args) {
|
||||
min = intArg(NAME, 1, args);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Error validate(String username, String password) {
|
||||
|
@ -182,6 +234,11 @@ public class PasswordPolicy {
|
|||
}
|
||||
return count < min ? new Error(INVALID_PASSWORD_MIN_DIGITS_MESSAGE, min) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
return validate(user.getUsername(), password);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LowerCase implements Policy {
|
||||
|
@ -191,7 +248,7 @@ public class PasswordPolicy {
|
|||
public LowerCase(String[] args) {
|
||||
min = intArg(NAME, 1, args);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Error validate(String username, String password) {
|
||||
int count = 0;
|
||||
|
@ -202,6 +259,11 @@ public class PasswordPolicy {
|
|||
}
|
||||
return count < min ? new Error(INVALID_PASSWORD_MIN_LOWER_CASE_CHARS_MESSAGE, min) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
return validate(user.getUsername(), password);
|
||||
}
|
||||
}
|
||||
|
||||
private static class UpperCase implements Policy {
|
||||
|
@ -222,6 +284,11 @@ public class PasswordPolicy {
|
|||
}
|
||||
return count < min ? new Error(INVALID_PASSWORD_MIN_UPPER_CASE_CHARS_MESSAGE, min) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
return validate(user.getUsername(), password);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SpecialChars implements Policy {
|
||||
|
@ -231,7 +298,7 @@ public class PasswordPolicy {
|
|||
public SpecialChars(String[] args) {
|
||||
min = intArg(NAME, 1, args);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Error validate(String username, String password) {
|
||||
int count = 0;
|
||||
|
@ -242,6 +309,11 @@ public class PasswordPolicy {
|
|||
}
|
||||
return count < min ? new Error(INVALID_PASSWORD_MIN_SPECIAL_CHARS_MESSAGE, min) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
return validate(user.getUsername(), password);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RegexPatterns implements Policy {
|
||||
|
@ -256,17 +328,96 @@ public class PasswordPolicy {
|
|||
public Error validate(String username, String password) {
|
||||
Pattern pattern = null;
|
||||
Matcher matcher = null;
|
||||
for(String regexPattern : regexPatterns) {
|
||||
for (String regexPattern : regexPatterns) {
|
||||
pattern = Pattern.compile(regexPattern);
|
||||
matcher = pattern.matcher(password);
|
||||
if (!matcher.matches()) {
|
||||
return new Error(INVALID_PASSWORD_REGEX_PATTERN, (Object)regexPatterns);
|
||||
return new Error(INVALID_PASSWORD_REGEX_PATTERN, (Object) regexPatterns);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
return validate(user.getUsername(), password);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PasswordHistory implements Policy {
|
||||
private static final String NAME = "passwordHistory";
|
||||
private int passwordHistoryPolicyValue;
|
||||
|
||||
public PasswordHistory(String[] args) {
|
||||
passwordHistoryPolicyValue = intArg(NAME, 3, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(String user, String password) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error validate(UserModel user, String password) {
|
||||
|
||||
if (passwordHistoryPolicyValue != -1) {
|
||||
|
||||
UserCredentialValueModel cred = getCredentialValueModel(user, UserCredentialModel.PASSWORD);
|
||||
if (cred != null) {
|
||||
if(new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue(), cred.getHashIterations())) {
|
||||
return new Error(INVALID_PASSWORD_HISTORY, passwordHistoryPolicyValue);
|
||||
}
|
||||
}
|
||||
|
||||
List<UserCredentialValueModel> passwordExpiredCredentials = getCredentialValueModels(user, passwordHistoryPolicyValue - 1,
|
||||
UserCredentialModel.PASSWORD_HISTORY);
|
||||
for (UserCredentialValueModel credential : passwordExpiredCredentials) {
|
||||
if (new Pbkdf2PasswordEncoder(credential.getSalt()).verify(password, credential.getValue(), credential.getHashIterations())) {
|
||||
return new Error(INVALID_PASSWORD_HISTORY, passwordHistoryPolicyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private UserCredentialValueModel getCredentialValueModel(UserModel user, String credType) {
|
||||
for (UserCredentialValueModel model : user.getCredentialsDirectly()) {
|
||||
if (model.getType().equals(credType)) {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<UserCredentialValueModel> getCredentialValueModels(UserModel user, int expiredPasswordsPolicyValue,
|
||||
String credType) {
|
||||
List<UserCredentialValueModel> credentialModels = new ArrayList<UserCredentialValueModel>();
|
||||
for (UserCredentialValueModel model : user.getCredentialsDirectly()) {
|
||||
if (model.getType().equals(credType)) {
|
||||
credentialModels.add(model);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(credentialModels, new Comparator<UserCredentialValueModel>() {
|
||||
public int compare(UserCredentialValueModel credFirst, UserCredentialValueModel credSecond) {
|
||||
if (credFirst.getCreatedDate() > credSecond.getCreatedDate()) {
|
||||
return -1;
|
||||
} else if (credFirst.getCreatedDate() < credSecond.getCreatedDate()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (credentialModels.size() > expiredPasswordsPolicyValue) {
|
||||
return credentialModels.subList(0, expiredPasswordsPolicyValue);
|
||||
}
|
||||
return credentialModels;
|
||||
}
|
||||
}
|
||||
|
||||
private static int intArg(String policy, int defaultValue, String... args) {
|
||||
if (args == null || args.length == 0) {
|
||||
return defaultValue;
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.UUID;
|
|||
*/
|
||||
public class UserCredentialModel {
|
||||
public static final String PASSWORD = "password";
|
||||
public static final String PASSWORD_HISTORY = "password-history";
|
||||
public static final String PASSWORD_TOKEN = "password-token";
|
||||
|
||||
// Secret is same as password but it is not hashed
|
||||
|
|
|
@ -12,6 +12,7 @@ public class UserCredentialValueModel {
|
|||
private String device;
|
||||
private byte[] salt;
|
||||
private int hashIterations;
|
||||
private long createdDate;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
|
@ -52,4 +53,13 @@ public class UserCredentialValueModel {
|
|||
public void setHashIterations(int iterations) {
|
||||
this.hashIterations = iterations;
|
||||
}
|
||||
|
||||
public long getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
public void setCreatedDate(long createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -323,7 +323,7 @@ public class UserFederationManager implements UserProvider {
|
|||
public void updateCredential(RealmModel realm, UserModel user, UserCredentialModel credential) {
|
||||
if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
if (realm.getPasswordPolicy() != null) {
|
||||
PasswordPolicy.Error error = realm.getPasswordPolicy().validate(user.getUsername(), credential.getValue());
|
||||
PasswordPolicy.Error error = realm.getPasswordPolicy().validate(user, credential.getValue());
|
||||
if (error != null) throw new ModelException(error.getMessage(), error.getParameters());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,23 @@ package org.keycloak.models.entities;
|
|||
*/
|
||||
public class CredentialEntity {
|
||||
|
||||
private String id;
|
||||
private String type;
|
||||
private String value;
|
||||
private String device;
|
||||
private byte[] salt;
|
||||
private int hashIterations;
|
||||
private long createdDate;
|
||||
private UserEntity user;
|
||||
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
|
@ -50,4 +62,21 @@ public class CredentialEntity {
|
|||
public void setHashIterations(int hashIterations) {
|
||||
this.hashIterations = hashIterations;
|
||||
}
|
||||
|
||||
public long getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
public void setCreatedDate(long createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
}
|
||||
|
||||
public UserEntity getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(UserEntity user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
private List<String> eventsListeners = new ArrayList<String>();
|
||||
private List<String> enabledEventTypes = new ArrayList<String>();
|
||||
|
||||
private String adminAppId;
|
||||
private String masterAdminClient;
|
||||
|
||||
private boolean internationalizationEnabled;
|
||||
private List<String> supportedLocales = new ArrayList<String>();
|
||||
|
@ -391,12 +391,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
this.enabledEventTypes = enabledEventTypes;
|
||||
}
|
||||
|
||||
public String getAdminAppId() {
|
||||
return adminAppId;
|
||||
public String getMasterAdminClient() {
|
||||
return masterAdminClient;
|
||||
}
|
||||
|
||||
public void setAdminAppId(String adminAppId) {
|
||||
this.adminAppId = adminAppId;
|
||||
public void setMasterAdminClient(String masterAdminClient) {
|
||||
this.masterAdminClient = masterAdminClient;
|
||||
}
|
||||
|
||||
public List<UserFederationProviderEntity> getUserFederationProviders() {
|
||||
|
|
|
@ -66,7 +66,7 @@ public class RepresentationToModel {
|
|||
if (rep.getFailureFactor() != null) newRealm.setFailureFactor(rep.getFailureFactor());
|
||||
if (rep.isEventsEnabled() != null) newRealm.setEventsEnabled(rep.isEventsEnabled());
|
||||
if (rep.getEventsExpiration() != null) newRealm.setEventsExpiration(rep.getEventsExpiration());
|
||||
if (rep.getEventsListeners() != null) newRealm.setEventsListeners(new HashSet<String>(rep.getEventsListeners()));
|
||||
if (rep.getEventsListeners() != null) newRealm.setEventsListeners(new HashSet<>(rep.getEventsListeners()));
|
||||
|
||||
if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore());
|
||||
|
||||
|
@ -411,8 +411,8 @@ public class RepresentationToModel {
|
|||
if (rep.getEmailTheme() != null) realm.setEmailTheme(rep.getEmailTheme());
|
||||
if (rep.isEventsEnabled() != null) realm.setEventsEnabled(rep.isEventsEnabled());
|
||||
if (rep.getEventsExpiration() != null) realm.setEventsExpiration(rep.getEventsExpiration());
|
||||
if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<String>(rep.getEventsListeners()));
|
||||
if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<String>(rep.getEnabledEventTypes()));
|
||||
if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<>(rep.getEventsListeners()));
|
||||
if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes()));
|
||||
|
||||
if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -79,6 +83,48 @@ public class PasswordPolicyTest {
|
|||
Assert.assertEquals("invalidPasswordNotUsernameMessage", policy.validate("jdoe", "jdoe").getMessage());
|
||||
Assert.assertNull(policy.validate("jdoe", "ab&d1234"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegexPatterns() {
|
||||
PasswordPolicy policy = null;
|
||||
try {
|
||||
policy = new PasswordPolicy("regexPatterns");
|
||||
fail("Expected NullPointerEXception: Regex Pattern cannot be null.");
|
||||
} catch (NullPointerException e) {
|
||||
// Expected NPE as regex pattern is null.
|
||||
}
|
||||
|
||||
try {
|
||||
policy = new PasswordPolicy("regexPatterns(*)");
|
||||
fail("Expected PatternSyntaxException: Regex Pattern cannot be null.");
|
||||
} catch (PatternSyntaxException e) {
|
||||
// Expected PSE as regex pattern(or any of its token) is not quantifiable.
|
||||
}
|
||||
|
||||
try {
|
||||
policy = new PasswordPolicy("regexPatterns(*,**)");
|
||||
fail("Expected PatternSyntaxException: Regex Pattern cannot be null.");
|
||||
} catch (PatternSyntaxException e) {
|
||||
// Expected PSE as regex pattern(or any of its token) is not quantifiable.
|
||||
}
|
||||
|
||||
//Fails to match one of the regex pattern
|
||||
policy = new PasswordPolicy("regexPatterns(jdoe,j*d)");
|
||||
Assert.assertEquals("invalidPasswordRegexPatternMessage", policy.validate("jdoe", "jdoe").getMessage());
|
||||
|
||||
////Fails to match all of the regex patterns
|
||||
policy = new PasswordPolicy("regexPatterns(j*p,j*d,adoe)");
|
||||
Assert.assertEquals("invalidPasswordRegexPatternMessage", policy.validate("jdoe", "jdoe").getMessage());
|
||||
|
||||
policy = new PasswordPolicy("regexPatterns([a-z][a-z][a-z][a-z][0-9])");
|
||||
Assert.assertEquals("invalidPasswordRegexPatternMessage", policy.validate("jdoe", "jdoe").getMessage());
|
||||
|
||||
policy = new PasswordPolicy("regexPatterns(jdoe)");
|
||||
Assert.assertNull(policy.validate("jdoe", "jdoe"));
|
||||
|
||||
policy = new PasswordPolicy("regexPatterns([a-z][a-z][a-z][a-z][0-9])");
|
||||
Assert.assertNull(policy.validate("jdoe", "jdoe0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplex() {
|
||||
|
|
|
@ -966,14 +966,14 @@ public class RealmAdapter implements RealmModel {
|
|||
@Override
|
||||
public void setMasterAdminClient(ClientModel client) {
|
||||
if (client == null) {
|
||||
realm.setAdminAppId(null);
|
||||
realm.setMasterAdminClient(null);
|
||||
this.masterAdminApp = null;
|
||||
} else {
|
||||
String appId = client.getId();
|
||||
if (appId == null) {
|
||||
throw new IllegalStateException("Master Admin app not initialized.");
|
||||
}
|
||||
realm.setAdminAppId(appId);
|
||||
realm.setMasterAdminClient(appId);
|
||||
this.masterAdminApp = client;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
*/
|
||||
package org.keycloak.models.file.adapter;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
||||
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -28,11 +30,14 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.connections.file.InMemoryModel;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.entities.FederatedIdentityEntity;
|
||||
|
@ -209,29 +214,80 @@ public class UserAdapter implements UserModel, Comparable {
|
|||
|
||||
@Override
|
||||
public void updateCredential(UserCredentialModel cred) {
|
||||
|
||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
updatePasswordCredential(cred);
|
||||
} else {
|
||||
CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = setCredentials(user, cred);
|
||||
credentialEntity.setValue(cred.getValue());
|
||||
user.getCredentials().add(credentialEntity);
|
||||
} else {
|
||||
credentialEntity.setValue(cred.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePasswordCredential(UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(cred.getType());
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
credentialEntity = setCredentials(user, cred);
|
||||
setValue(credentialEntity, cred);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
}
|
||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
byte[] salt = Pbkdf2PasswordEncoder.getSalt();
|
||||
int hashIterations = 1;
|
||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||
if (policy != null) {
|
||||
hashIterations = policy.getHashIterations();
|
||||
if (hashIterations == -1) hashIterations = 1;
|
||||
}
|
||||
credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
|
||||
credentialEntity.setSalt(salt);
|
||||
credentialEntity.setHashIterations(hashIterations);
|
||||
} else {
|
||||
credentialEntity.setValue(cred.getValue());
|
||||
|
||||
int expiredPasswordsPolicyValue = -1;
|
||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||
if(policy != null) {
|
||||
expiredPasswordsPolicyValue = policy.getExpiredPasswords();
|
||||
}
|
||||
|
||||
if (expiredPasswordsPolicyValue != -1) {
|
||||
user.getCredentials().remove(credentialEntity);
|
||||
credentialEntity.setType(UserCredentialModel.PASSWORD_HISTORY);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
|
||||
List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
|
||||
if (credentialEntities.size() > expiredPasswordsPolicyValue - 1) {
|
||||
user.getCredentials().removeAll(credentialEntities.subList(expiredPasswordsPolicyValue - 1, credentialEntities.size()));
|
||||
}
|
||||
|
||||
credentialEntity = setCredentials(user, cred);
|
||||
setValue(credentialEntity, cred);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
} else {
|
||||
List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
|
||||
if (credentialEntities != null && credentialEntities.size() > 0) {
|
||||
user.getCredentials().removeAll(credentialEntities);
|
||||
}
|
||||
setValue(credentialEntity, cred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CredentialEntity setCredentials(UserEntity user, UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(cred.getType());
|
||||
credentialEntity.setCreatedDate(new Date().getTime());
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
return credentialEntity;
|
||||
}
|
||||
|
||||
private void setValue(CredentialEntity credentialEntity, UserCredentialModel cred) {
|
||||
byte[] salt = getSalt();
|
||||
int hashIterations = 1;
|
||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||
if (policy != null) {
|
||||
hashIterations = policy.getHashIterations();
|
||||
if (hashIterations == -1)
|
||||
hashIterations = 1;
|
||||
}
|
||||
credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
|
||||
credentialEntity.setSalt(salt);
|
||||
credentialEntity.setHashIterations(hashIterations);
|
||||
}
|
||||
|
||||
private CredentialEntity getCredentialEntity(UserEntity userEntity, String credType) {
|
||||
|
@ -244,6 +300,30 @@ public class UserAdapter implements UserModel, Comparable {
|
|||
return null;
|
||||
}
|
||||
|
||||
private List<CredentialEntity> getCredentialEntities(UserEntity userEntity, String credType) {
|
||||
List<CredentialEntity> credentialEntities = new ArrayList<CredentialEntity>();
|
||||
for (CredentialEntity entity : userEntity.getCredentials()) {
|
||||
if (entity.getType().equals(credType)) {
|
||||
credentialEntities.add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Avoiding direct use of credSecond.getCreatedDate() - credFirst.getCreatedDate() to prevent Integer Overflow
|
||||
// Orders from most recent to least recent
|
||||
Collections.sort(credentialEntities, new Comparator<CredentialEntity>() {
|
||||
public int compare(CredentialEntity credFirst, CredentialEntity credSecond) {
|
||||
if (credFirst.getCreatedDate() > credSecond.getCreatedDate()) {
|
||||
return -1;
|
||||
} else if (credFirst.getCreatedDate() < credSecond.getCreatedDate()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
return credentialEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserCredentialValueModel> getCredentialsDirectly() {
|
||||
List<CredentialEntity> credentials = new ArrayList<CredentialEntity>(user.getCredentials());
|
||||
|
@ -253,6 +333,7 @@ public class UserAdapter implements UserModel, Comparable {
|
|||
UserCredentialValueModel credModel = new UserCredentialValueModel();
|
||||
credModel.setType(credEntity.getType());
|
||||
credModel.setDevice(credEntity.getDevice());
|
||||
credModel.setCreatedDate(credEntity.getCreatedDate());
|
||||
credModel.setValue(credEntity.getValue());
|
||||
credModel.setSalt(credEntity.getSalt());
|
||||
credModel.setHashIterations(credEntity.getHashIterations());
|
||||
|
@ -272,6 +353,7 @@ public class UserAdapter implements UserModel, Comparable {
|
|||
// credentialEntity.setId(KeycloakModelUtils.generateId());
|
||||
credentialEntity.setType(credModel.getType());
|
||||
// credentialEntity.setUser(user);
|
||||
credModel.setCreatedDate(credModel.getCreatedDate());
|
||||
user.getCredentials().add(credentialEntity);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package org.keycloak.models.cache.infinispan;
|
|||
import org.infinispan.Cache;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedApplication;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
||||
|
@ -77,19 +77,19 @@ public class InfinispanRealmCache implements RealmCache {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CachedApplication getApplication(String id) {
|
||||
public CachedClient getApplication(String id) {
|
||||
if (!enabled) return null;
|
||||
return get(id, CachedApplication.class);
|
||||
return get(id, CachedClient.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateApplication(CachedApplication app) {
|
||||
public void invalidateApplication(CachedClient app) {
|
||||
logger.tracev("Removing application {0}", app.getId());
|
||||
cache.remove(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedApplication(CachedApplication app) {
|
||||
public void addCachedClient(CachedClient app) {
|
||||
if (!enabled) return;
|
||||
logger.tracev("Adding application {0}", app.getId());
|
||||
cache.put(app.getId(), app);
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.keycloak.models.ProtocolMapperModel;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.entities.CachedApplication;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -24,9 +24,9 @@ public class ClientAdapter implements ClientModel {
|
|||
protected RealmCache cache;
|
||||
|
||||
protected ClientModel updated;
|
||||
protected CachedApplication cached;
|
||||
protected CachedClient cached;
|
||||
|
||||
public ClientAdapter(RealmModel cachedRealm, CachedApplication cached, CacheRealmProvider cacheSession, RealmCache cache) {
|
||||
public ClientAdapter(RealmModel cachedRealm, CachedClient cached, CacheRealmProvider cacheSession, RealmCache cache) {
|
||||
this.cachedRealm = cachedRealm;
|
||||
this.cache = cache;
|
||||
this.cacheSession = cacheSession;
|
||||
|
|
|
@ -6,8 +6,8 @@ import org.keycloak.models.KeycloakTransaction;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.entities.CachedApplication;
|
||||
import org.keycloak.models.cache.entities.CachedApplicationRole;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
@ -235,7 +235,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
|
|||
if (model == null) return null;
|
||||
if (roleInvalidations.contains(id)) return model;
|
||||
if (model.getContainer() instanceof ClientModel) {
|
||||
cached = new CachedApplicationRole(((ClientModel) model.getContainer()).getId(), model, realm);
|
||||
cached = new CachedClientRole(((ClientModel) model.getContainer()).getId(), model, realm);
|
||||
} else {
|
||||
cached = new CachedRealmRole(model, realm);
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
|
|||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
if (!cache.isEnabled()) return getDelegate().getClientById(id, realm);
|
||||
CachedApplication cached = cache.getApplication(id);
|
||||
CachedClient cached = cache.getApplication(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
@ -263,8 +263,8 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
|
|||
ClientModel model = getDelegate().getClientById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (appInvalidations.contains(id)) return model;
|
||||
cached = new CachedApplication(cache, getDelegate(), realm, model);
|
||||
cache.addCachedApplication(cached);
|
||||
cached = new CachedClient(cache, getDelegate(), realm, model);
|
||||
cache.addCachedClient(cached);
|
||||
} else if (appInvalidations.contains(id)) {
|
||||
return getDelegate().getClientById(id, realm);
|
||||
} else if (managedApplications.containsKey(id)) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.keycloak.models.cache;
|
||||
|
||||
import org.keycloak.models.cache.entities.CachedApplication;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
||||
|
@ -14,7 +14,7 @@ public class MemoryRealmCache implements RealmCache {
|
|||
|
||||
protected ConcurrentHashMap<String, CachedRealm> realmCache = new ConcurrentHashMap<String, CachedRealm>();
|
||||
protected ConcurrentHashMap<String, CachedRealm> realmCacheByName = new ConcurrentHashMap<String, CachedRealm>();
|
||||
protected ConcurrentHashMap<String, CachedApplication> applicationCache = new ConcurrentHashMap<String, CachedApplication>();
|
||||
protected ConcurrentHashMap<String, CachedClient> applicationCache = new ConcurrentHashMap<String, CachedClient>();
|
||||
protected ConcurrentHashMap<String, CachedRole> roleCache = new ConcurrentHashMap<String, CachedRole>();
|
||||
protected volatile boolean enabled = true;
|
||||
|
||||
|
@ -72,18 +72,18 @@ public class MemoryRealmCache implements RealmCache {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CachedApplication getApplication(String id) {
|
||||
public CachedClient getApplication(String id) {
|
||||
if (!enabled) return null;
|
||||
return applicationCache.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateApplication(CachedApplication app) {
|
||||
public void invalidateApplication(CachedClient app) {
|
||||
applicationCache.remove(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedApplication(CachedApplication app) {
|
||||
public void addCachedClient(CachedClient app) {
|
||||
if (!enabled) return;
|
||||
applicationCache.put(app.getId(), app);
|
||||
}
|
||||
|
|
|
@ -474,7 +474,7 @@ public class RealmAdapter implements RealmModel {
|
|||
public Map<String, ClientModel> getClientNameMap() {
|
||||
if (updated != null) return updated.getClientNameMap();
|
||||
Map<String, ClientModel> map = new HashMap<String, ClientModel>();
|
||||
for (String id : cached.getApplications().values()) {
|
||||
for (String id : cached.getClients().values()) {
|
||||
ClientModel model = cacheSession.getClientById(id, this);
|
||||
if (model == null) {
|
||||
throw new IllegalStateException("Cached application not found: " + id);
|
||||
|
@ -488,7 +488,7 @@ public class RealmAdapter implements RealmModel {
|
|||
public List<ClientModel> getClients() {
|
||||
if (updated != null) return updated.getClients();
|
||||
List<ClientModel> apps = new LinkedList<ClientModel>();
|
||||
for (String id : cached.getApplications().values()) {
|
||||
for (String id : cached.getClients().values()) {
|
||||
ClientModel model = cacheSession.getClientById(id, this);
|
||||
if (model == null) {
|
||||
throw new IllegalStateException("Cached application not found: " + id);
|
||||
|
@ -531,7 +531,7 @@ public class RealmAdapter implements RealmModel {
|
|||
@Override
|
||||
public ClientModel getClientByClientId(String clientId) {
|
||||
if (updated != null) return updated.getClientByClientId(clientId);
|
||||
String id = cached.getApplications().get(clientId);
|
||||
String id = cached.getClients().get(clientId);
|
||||
if (id == null) return null;
|
||||
return getClientById(id);
|
||||
}
|
||||
|
@ -752,7 +752,7 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public ClientModel getMasterAdminClient() {
|
||||
return cacheSession.getRealm(Config.getAdminRealm()).getClientById(cached.getMasterAdminApp());
|
||||
return cacheSession.getRealm(Config.getAdminRealm()).getClientById(cached.getMasterAdminClient());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.keycloak.models.cache;
|
||||
|
||||
import org.keycloak.models.cache.entities.CachedApplication;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
||||
|
@ -21,11 +21,11 @@ public interface RealmCache {
|
|||
|
||||
void invalidateCachedRealmById(String id);
|
||||
|
||||
CachedApplication getApplication(String id);
|
||||
CachedClient getApplication(String id);
|
||||
|
||||
void invalidateApplication(CachedApplication app);
|
||||
void invalidateApplication(CachedClient app);
|
||||
|
||||
void addCachedApplication(CachedApplication app);
|
||||
void addCachedClient(CachedClient app);
|
||||
|
||||
void invalidateCachedApplicationById(String id);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package org.keycloak.models.cache;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.entities.CachedApplicationRole;
|
||||
import org.keycloak.models.cache.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
@ -106,8 +106,8 @@ public class RoleAdapter implements RoleModel {
|
|||
if (cached instanceof CachedRealmRole) {
|
||||
return realm;
|
||||
} else {
|
||||
CachedApplicationRole appRole = (CachedApplicationRole)cached;
|
||||
return realm.getClientById(appRole.getAppId());
|
||||
CachedClientRole appRole = (CachedClientRole)cached;
|
||||
return realm.getClientById(appRole.getIdClient());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
package org.keycloak.models.cache.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CachedApplicationRole extends CachedRole {
|
||||
private final String appId;
|
||||
|
||||
public CachedApplicationRole(String appId, RoleModel model, RealmModel realm) {
|
||||
super(model, realm);
|
||||
this.appId = appId;
|
||||
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ import java.util.TreeMap;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CachedApplication {
|
||||
public class CachedClient {
|
||||
private String id;
|
||||
private String name;
|
||||
private String realm;
|
||||
|
@ -49,7 +49,7 @@ public class CachedApplication {
|
|||
private int nodeReRegistrationTimeout;
|
||||
private Map<String, Integer> registeredNodes;
|
||||
|
||||
public CachedApplication(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
|
||||
public CachedClient(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
|
||||
id = model.getId();
|
||||
secret = model.getSecret();
|
||||
name = model.getClientId();
|
||||
|
@ -79,7 +79,7 @@ public class CachedApplication {
|
|||
consentRequired = model.isConsentRequired();
|
||||
for (RoleModel role : model.getRoles()) {
|
||||
roles.put(role.getName(), role.getId());
|
||||
cache.addCachedRole(new CachedApplicationRole(id, role, realm));
|
||||
cache.addCachedRole(new CachedClientRole(id, role, realm));
|
||||
}
|
||||
|
||||
nodeReRegistrationTimeout = model.getNodeReRegistrationTimeout();
|
22
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java
vendored
Executable file
22
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java
vendored
Executable file
|
@ -0,0 +1,22 @@
|
|||
package org.keycloak.models.cache.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CachedClientRole extends CachedRole {
|
||||
private final String idClient;
|
||||
|
||||
public CachedClientRole(String idClient, RoleModel model, RealmModel realm) {
|
||||
super(model, realm);
|
||||
this.idClient = idClient;
|
||||
|
||||
}
|
||||
|
||||
public String getIdClient() {
|
||||
return idClient;
|
||||
}
|
||||
}
|
|
@ -66,7 +66,7 @@ public class CachedRealm {
|
|||
private String accountTheme;
|
||||
private String adminTheme;
|
||||
private String emailTheme;
|
||||
private String masterAdminApp;
|
||||
private String masterAdminClient;
|
||||
|
||||
private List<RequiredCredentialModel> requiredCredentials = new ArrayList<RequiredCredentialModel>();
|
||||
private List<UserFederationProviderModel> userFederationProviders = new ArrayList<UserFederationProviderModel>();
|
||||
|
@ -81,7 +81,6 @@ public class CachedRealm {
|
|||
private Set<String> enabledEventTypes = new HashSet<String>();
|
||||
private List<String> defaultRoles = new LinkedList<String>();
|
||||
private Map<String, String> realmRoles = new HashMap<String, String>();
|
||||
private Map<String, String> applications = new HashMap<String, String>();
|
||||
private Map<String, String> clients = new HashMap<String, String>();
|
||||
private boolean internationalizationEnabled;
|
||||
private Set<String> supportedLocales = new HashSet<String>();
|
||||
|
@ -155,7 +154,7 @@ public class CachedRealm {
|
|||
eventsListeners.addAll(model.getEventsListeners());
|
||||
enabledEventTypes.addAll(model.getEnabledEventTypes());
|
||||
defaultRoles.addAll(model.getDefaultRoles());
|
||||
masterAdminApp = model.getMasterAdminClient().getId();
|
||||
masterAdminClient = model.getMasterAdminClient().getId();
|
||||
|
||||
for (RoleModel role : model.getRoles()) {
|
||||
realmRoles.put(role.getName(), role.getId());
|
||||
|
@ -163,10 +162,10 @@ public class CachedRealm {
|
|||
cache.addCachedRole(cachedRole);
|
||||
}
|
||||
|
||||
for (ClientModel app : model.getClients()) {
|
||||
applications.put(app.getClientId(), app.getId());
|
||||
CachedApplication cachedApp = new CachedApplication(cache, delegate, model, app);
|
||||
cache.addCachedApplication(cachedApp);
|
||||
for (ClientModel client : model.getClients()) {
|
||||
clients.put(client.getClientId(), client.getId());
|
||||
CachedClient cachedClient = new CachedClient(cache, delegate, model, client);
|
||||
cache.addCachedClient(cachedClient);
|
||||
}
|
||||
|
||||
internationalizationEnabled = model.isInternationalizationEnabled();
|
||||
|
@ -180,8 +179,8 @@ public class CachedRealm {
|
|||
return id;
|
||||
}
|
||||
|
||||
public String getMasterAdminApp() {
|
||||
return masterAdminApp;
|
||||
public String getMasterAdminClient() {
|
||||
return masterAdminClient;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -196,10 +195,6 @@ public class CachedRealm {
|
|||
return realmRoles;
|
||||
}
|
||||
|
||||
public Map<String, String> getApplications() {
|
||||
return applications;
|
||||
}
|
||||
|
||||
public Map<String, String> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
|
|
@ -545,9 +545,9 @@ public class ClientAdapter implements ClientModel {
|
|||
|
||||
@Override
|
||||
public RoleModel getRole(String name) {
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("getAppRoleByName", RoleEntity.class);
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("getClientRoleByName", RoleEntity.class);
|
||||
query.setParameter("name", name);
|
||||
query.setParameter("application", entity);
|
||||
query.setParameter("client", entity);
|
||||
List<RoleEntity> roles = query.getResultList();
|
||||
if (roles.size() == 0) return null;
|
||||
return new RoleAdapter(realm, em, roles.get(0));
|
||||
|
@ -563,8 +563,8 @@ public class ClientAdapter implements ClientModel {
|
|||
RoleEntity roleEntity = new RoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setApplication(entity);
|
||||
roleEntity.setApplicationRole(true);
|
||||
roleEntity.setClient(entity);
|
||||
roleEntity.setClientRole(true);
|
||||
roleEntity.setRealmId(realm.getId());
|
||||
em.persist(roleEntity);
|
||||
entity.getRoles().add(roleEntity);
|
||||
|
@ -581,13 +581,13 @@ public class ClientAdapter implements ClientModel {
|
|||
|
||||
session.users().preRemove(getRealm(), roleModel);
|
||||
RoleEntity role = RoleAdapter.toRoleEntity(roleModel, em);
|
||||
if (!role.isApplicationRole()) return false;
|
||||
if (!role.isClientRole()) return false;
|
||||
|
||||
entity.getRoles().remove(role);
|
||||
entity.getDefaultRoles().remove(role);
|
||||
em.createNativeQuery("delete from COMPOSITE_ROLE where CHILD_ROLE = :role").setParameter("role", role).executeUpdate();
|
||||
em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", role).executeUpdate();
|
||||
role.setApplication(null);
|
||||
role.setClient(null);
|
||||
em.flush();
|
||||
em.remove(role);
|
||||
em.flush();
|
||||
|
|
|
@ -91,7 +91,7 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
|
||||
RealmAdapter adapter = new RealmAdapter(session, em, realm);
|
||||
session.users().preRemove(adapter);
|
||||
for (ClientEntity a : new LinkedList<>(realm.getApplications())) {
|
||||
for (ClientEntity a : new LinkedList<>(realm.getClients())) {
|
||||
adapter.removeClient(a.getId());
|
||||
}
|
||||
|
||||
|
|
|
@ -619,8 +619,8 @@ public class RealmAdapter implements RealmModel {
|
|||
@Override
|
||||
public List<ClientModel> getClients() {
|
||||
List<ClientModel> list = new ArrayList<ClientModel>();
|
||||
if (realm.getApplications() == null) return list;
|
||||
for (ClientEntity entity : realm.getApplications()) {
|
||||
if (realm.getClients() == null) return list;
|
||||
for (ClientEntity entity : realm.getClients()) {
|
||||
list.add(new ClientAdapter(this, em, session, entity));
|
||||
}
|
||||
return list;
|
||||
|
@ -633,15 +633,15 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public ClientModel addClient(String id, String clientId) {
|
||||
ClientEntity applicationData = new ClientEntity();
|
||||
applicationData.setId(id);
|
||||
applicationData.setClientId(clientId);
|
||||
applicationData.setEnabled(true);
|
||||
applicationData.setRealm(realm);
|
||||
realm.getApplications().add(applicationData);
|
||||
em.persist(applicationData);
|
||||
ClientEntity entity = new ClientEntity();
|
||||
entity.setId(id);
|
||||
entity.setClientId(clientId);
|
||||
entity.setEnabled(true);
|
||||
entity.setRealm(realm);
|
||||
realm.getClients().add(entity);
|
||||
em.persist(entity);
|
||||
em.flush();
|
||||
final ClientModel resource = new ClientAdapter(this, em, session, applicationData);
|
||||
final ClientModel resource = new ClientAdapter(this, em, session, entity);
|
||||
em.flush();
|
||||
session.getKeycloakSessionFactory().publish(new ClientCreationEvent() {
|
||||
@Override
|
||||
|
@ -655,15 +655,15 @@ public class RealmAdapter implements RealmModel {
|
|||
@Override
|
||||
public boolean removeClient(String id) {
|
||||
if (id == null) return false;
|
||||
ClientModel application = getClientById(id);
|
||||
if (application == null) return false;
|
||||
ClientModel client = getClientById(id);
|
||||
if (client == null) return false;
|
||||
|
||||
for (RoleModel role : application.getRoles()) {
|
||||
application.removeRole(role);
|
||||
for (RoleModel role : client.getRoles()) {
|
||||
client.removeRole(role);
|
||||
}
|
||||
|
||||
ClientEntity clientEntity = null;
|
||||
Iterator<ClientEntity> it = realm.getApplications().iterator();
|
||||
Iterator<ClientEntity> it = realm.getClients().iterator();
|
||||
while (it.hasNext()) {
|
||||
ClientEntity ae = it.next();
|
||||
if (ae.getId().equals(id)) {
|
||||
|
@ -672,12 +672,12 @@ public class RealmAdapter implements RealmModel {
|
|||
break;
|
||||
}
|
||||
}
|
||||
for (ClientEntity a : realm.getApplications()) {
|
||||
for (ClientEntity a : realm.getClients()) {
|
||||
if (a.getId().equals(id)) {
|
||||
clientEntity = a;
|
||||
}
|
||||
}
|
||||
if (application == null) {
|
||||
if (client == null) {
|
||||
return false;
|
||||
}
|
||||
em.remove(clientEntity);
|
||||
|
@ -1066,13 +1066,13 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public ClientModel getMasterAdminClient() {
|
||||
return new ClientAdapter(this, em, session, realm.getMasterAdminApp());
|
||||
return new ClientAdapter(this, em, session, realm.getMasterAdminClient());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMasterAdminClient(ClientModel client) {
|
||||
ClientEntity appEntity = client !=null ? em.getReference(ClientEntity.class, client.getId()) : null;
|
||||
realm.setMasterAdminApp(appEntity);
|
||||
realm.setMasterAdminClient(appEntity);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
|
|
|
@ -104,8 +104,8 @@ public class RoleAdapter implements RoleModel {
|
|||
|
||||
@Override
|
||||
public RoleContainerModel getContainer() {
|
||||
if (role.isApplicationRole()) {
|
||||
return realm.getClientById(role.getApplication().getId());
|
||||
if (role.isClientRole()) {
|
||||
return realm.getClientById(role.getClient().getId());
|
||||
|
||||
} else {
|
||||
return realm;
|
||||
|
|
|
@ -18,7 +18,11 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
|||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -160,7 +164,6 @@ public class UserAdapter implements UserModel {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getFirstName() {
|
||||
return user.getFirstName();
|
||||
|
@ -208,33 +211,86 @@ public class UserAdapter implements UserModel {
|
|||
|
||||
@Override
|
||||
public void updateCredential(UserCredentialModel cred) {
|
||||
|
||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
updatePasswordCredential(cred);
|
||||
} else {
|
||||
CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = setCredentials(user, cred);
|
||||
credentialEntity.setValue(cred.getValue());
|
||||
em.persist(credentialEntity);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
} else {
|
||||
credentialEntity.setValue(cred.getValue());
|
||||
}
|
||||
}
|
||||
em.flush();
|
||||
}
|
||||
|
||||
private void updatePasswordCredential(UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setId(KeycloakModelUtils.generateId());
|
||||
credentialEntity.setType(cred.getType());
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
credentialEntity.setUser(user);
|
||||
credentialEntity = setCredentials(user, cred);
|
||||
setValue(credentialEntity, cred);
|
||||
em.persist(credentialEntity);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
}
|
||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
byte[] salt = getSalt();
|
||||
int hashIterations = 1;
|
||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||
if (policy != null) {
|
||||
hashIterations = policy.getHashIterations();
|
||||
if (hashIterations == -1) hashIterations = 1;
|
||||
}
|
||||
credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
|
||||
credentialEntity.setSalt(salt);
|
||||
credentialEntity.setHashIterations(hashIterations);
|
||||
} else {
|
||||
credentialEntity.setValue(cred.getValue());
|
||||
|
||||
int expiredPasswordsPolicyValue = -1;
|
||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||
if(policy != null) {
|
||||
expiredPasswordsPolicyValue = policy.getExpiredPasswords();
|
||||
}
|
||||
|
||||
if (expiredPasswordsPolicyValue != -1) {
|
||||
user.getCredentials().remove(credentialEntity);
|
||||
credentialEntity.setType(UserCredentialModel.PASSWORD_HISTORY);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
|
||||
List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
|
||||
if (credentialEntities.size() > expiredPasswordsPolicyValue - 1) {
|
||||
user.getCredentials().removeAll(credentialEntities.subList(expiredPasswordsPolicyValue - 1, credentialEntities.size()));
|
||||
}
|
||||
|
||||
credentialEntity = setCredentials(user, cred);
|
||||
setValue(credentialEntity, cred);
|
||||
em.persist(credentialEntity);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
} else {
|
||||
List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
|
||||
if (credentialEntities != null && credentialEntities.size() > 0) {
|
||||
user.getCredentials().removeAll(credentialEntities);
|
||||
}
|
||||
setValue(credentialEntity, cred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CredentialEntity setCredentials(UserEntity user, UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setId(KeycloakModelUtils.generateId());
|
||||
credentialEntity.setType(cred.getType());
|
||||
credentialEntity.setCreatedDate(new Date().getTime());
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
em.flush();
|
||||
credentialEntity.setUser(user);
|
||||
return credentialEntity;
|
||||
}
|
||||
|
||||
private void setValue(CredentialEntity credentialEntity, UserCredentialModel cred) {
|
||||
byte[] salt = getSalt();
|
||||
int hashIterations = 1;
|
||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||
if (policy != null) {
|
||||
hashIterations = policy.getHashIterations();
|
||||
if (hashIterations == -1)
|
||||
hashIterations = 1;
|
||||
}
|
||||
credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
|
||||
credentialEntity.setSalt(salt);
|
||||
credentialEntity.setHashIterations(hashIterations);
|
||||
}
|
||||
|
||||
private CredentialEntity getCredentialEntity(UserEntity userEntity, String credType) {
|
||||
|
@ -247,6 +303,30 @@ public class UserAdapter implements UserModel {
|
|||
return null;
|
||||
}
|
||||
|
||||
private List<CredentialEntity> getCredentialEntities(UserEntity userEntity, String credType) {
|
||||
List<CredentialEntity> credentialEntities = new ArrayList<CredentialEntity>();
|
||||
for (CredentialEntity entity : userEntity.getCredentials()) {
|
||||
if (entity.getType().equals(credType)) {
|
||||
credentialEntities.add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Avoiding direct use of credSecond.getCreatedDate() - credFirst.getCreatedDate() to prevent Integer Overflow
|
||||
// Orders from most recent to least recent
|
||||
Collections.sort(credentialEntities, new Comparator<CredentialEntity>() {
|
||||
public int compare(CredentialEntity credFirst, CredentialEntity credSecond) {
|
||||
if (credFirst.getCreatedDate() > credSecond.getCreatedDate()) {
|
||||
return -1;
|
||||
} else if (credFirst.getCreatedDate() < credSecond.getCreatedDate()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
return credentialEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserCredentialValueModel> getCredentialsDirectly() {
|
||||
List<CredentialEntity> credentials = new ArrayList<CredentialEntity>(user.getCredentials());
|
||||
|
@ -258,6 +338,7 @@ public class UserAdapter implements UserModel {
|
|||
credModel.setType(credEntity.getType());
|
||||
credModel.setDevice(credEntity.getDevice());
|
||||
credModel.setValue(credEntity.getValue());
|
||||
credModel.setCreatedDate(credEntity.getCreatedDate());
|
||||
credModel.setSalt(credEntity.getSalt());
|
||||
credModel.setHashIterations(credEntity.getHashIterations());
|
||||
|
||||
|
@ -276,6 +357,7 @@ public class UserAdapter implements UserModel {
|
|||
credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setId(KeycloakModelUtils.generateId());
|
||||
credentialEntity.setType(credModel.getType());
|
||||
credentialEntity.setCreatedDate(credModel.getCreatedDate());
|
||||
credentialEntity.setUser(user);
|
||||
em.persist(credentialEntity);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
|
|
|
@ -96,17 +96,17 @@ public class ClientEntity {
|
|||
@Column(name="NODE_REREG_TIMEOUT")
|
||||
private int nodeReRegistrationTimeout;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "application")
|
||||
@OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "client")
|
||||
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
|
||||
@JoinTable(name="APPLICATION_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="APPLICATION_ID")}, inverseJoinColumns = { @JoinColumn(name="ROLE_ID")})
|
||||
@JoinTable(name="CLIENT_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="CLIENT_ID")}, inverseJoinColumns = { @JoinColumn(name="ROLE_ID")})
|
||||
Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
|
||||
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name="NAME")
|
||||
@Column(name="VALUE")
|
||||
@CollectionTable(name="APP_NODE_REGISTRATIONS", joinColumns={ @JoinColumn(name="APPLICATION_ID") })
|
||||
@CollectionTable(name="CLIENT_NODE_REGISTRATIONS", joinColumns={ @JoinColumn(name="CLIENT_ID") })
|
||||
Map<String, Integer> registeredNodes = new HashMap<String, Integer>();
|
||||
|
||||
public RealmEntity getRealm() {
|
||||
|
|
|
@ -37,7 +37,9 @@ public class CredentialEntity {
|
|||
protected byte[] salt;
|
||||
@Column(name="HASH_ITERATIONS")
|
||||
protected int hashIterations;
|
||||
|
||||
@Column(name="CREATED_DATE")
|
||||
protected long createdDate;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name="USER_ID")
|
||||
protected UserEntity user;
|
||||
|
@ -97,4 +99,13 @@ public class CredentialEntity {
|
|||
public void setHashIterations(int hashIterations) {
|
||||
this.hashIterations = hashIterations;
|
||||
}
|
||||
|
||||
public long getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
public void setCreatedDate(long createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -104,8 +104,8 @@ public class RealmEntity {
|
|||
List<UserFederationProviderEntity> userFederationProviders = new ArrayList<UserFederationProviderEntity>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
|
||||
@JoinTable(name="REALM_APPLICATION", joinColumns={ @JoinColumn(name="REALM_ID") }, inverseJoinColumns={ @JoinColumn(name="APPLICATION_ID") })
|
||||
Collection<ClientEntity> applications = new ArrayList<ClientEntity>();
|
||||
@JoinTable(name="REALM_CLIENT", joinColumns={ @JoinColumn(name="REALM_ID") }, inverseJoinColumns={ @JoinColumn(name="CLIENT_ID") })
|
||||
Collection<ClientEntity> clients = new ArrayList<>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
||||
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
|
||||
|
@ -136,8 +136,8 @@ public class RealmEntity {
|
|||
protected Set<String> enabledEventTypes = new HashSet<String>();
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name="MASTER_ADMIN_APP")
|
||||
protected ClientEntity masterAdminApp;
|
||||
@JoinColumn(name="MASTER_ADMIN_CLIENT")
|
||||
protected ClientEntity masterAdminClient;
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
||||
protected List<IdentityProviderEntity> identityProviders = new ArrayList<IdentityProviderEntity>();
|
||||
|
@ -318,12 +318,12 @@ public class RealmEntity {
|
|||
this.requiredCredentials = requiredCredentials;
|
||||
}
|
||||
|
||||
public Collection<ClientEntity> getApplications() {
|
||||
return applications;
|
||||
public Collection<ClientEntity> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
public void setApplications(Collection<ClientEntity> applications) {
|
||||
this.applications = applications;
|
||||
public void setClients(Collection<ClientEntity> clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
public Collection<RoleEntity> getRoles() {
|
||||
|
@ -437,12 +437,12 @@ public class RealmEntity {
|
|||
this.enabledEventTypes = enabledEventTypes;
|
||||
}
|
||||
|
||||
public ClientEntity getMasterAdminApp() {
|
||||
return masterAdminApp;
|
||||
public ClientEntity getMasterAdminClient() {
|
||||
return masterAdminClient;
|
||||
}
|
||||
|
||||
public void setMasterAdminApp(ClientEntity masterAdminApp) {
|
||||
this.masterAdminApp = masterAdminApp;
|
||||
public void setMasterAdminClient(ClientEntity masterAdminClient) {
|
||||
this.masterAdminClient = masterAdminClient;
|
||||
}
|
||||
|
||||
public List<UserFederationProviderEntity> getUserFederationProviders() {
|
||||
|
|
|
@ -21,11 +21,11 @@ import java.util.Collection;
|
|||
*/
|
||||
@Entity
|
||||
@Table(name="KEYCLOAK_ROLE", uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "NAME", "APP_REALM_CONSTRAINT" })
|
||||
@UniqueConstraint(columnNames = { "NAME", "CLIENT_REALM_CONSTRAINT" })
|
||||
})
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="getAppRoleByName", query="select role from RoleEntity role where role.name = :name and role.application = :application"),
|
||||
@NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.applicationRole = false and role.name = :name and role.realm = :realm")
|
||||
@NamedQuery(name="getClientRoleByName", query="select role from RoleEntity role where role.name = :name and role.client = :client"),
|
||||
@NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.clientRole = false and role.name = :name and role.realm = :realm")
|
||||
})
|
||||
|
||||
public class RoleEntity {
|
||||
|
@ -46,16 +46,16 @@ public class RoleEntity {
|
|||
@JoinColumn(name = "REALM")
|
||||
private RealmEntity realm;
|
||||
|
||||
@Column(name="APPLICATION_ROLE")
|
||||
private boolean applicationRole;
|
||||
@Column(name="CLIENT_ROLE")
|
||||
private boolean clientRole;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "APPLICATION")
|
||||
private ClientEntity application;
|
||||
@JoinColumn(name = "CLIENT")
|
||||
private ClientEntity client;
|
||||
|
||||
// Hack to ensure that either name+application or name+realm are unique. Needed due to MS-SQL as it don't allow multiple NULL values in the column, which is part of constraint
|
||||
@Column(name="APP_REALM_CONSTRAINT", length = 36)
|
||||
private String appRealmConstraint;
|
||||
// Hack to ensure that either name+client or name+realm are unique. Needed due to MS-SQL as it don't allow multiple NULL values in the column, which is part of constraint
|
||||
@Column(name="CLIENT_REALM_CONSTRAINT", length = 36)
|
||||
private String clientRealmConstraint;
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY, cascade = {})
|
||||
@JoinTable(name = "COMPOSITE_ROLE", joinColumns = @JoinColumn(name = "COMPOSITE"), inverseJoinColumns = @JoinColumn(name = "CHILD_ROLE"))
|
||||
|
@ -101,12 +101,12 @@ public class RoleEntity {
|
|||
this.compositeRoles = compositeRoles;
|
||||
}
|
||||
|
||||
public boolean isApplicationRole() {
|
||||
return applicationRole;
|
||||
public boolean isClientRole() {
|
||||
return clientRole;
|
||||
}
|
||||
|
||||
public void setApplicationRole(boolean applicationRole) {
|
||||
this.applicationRole = applicationRole;
|
||||
public void setClientRole(boolean clientRole) {
|
||||
this.clientRole = clientRole;
|
||||
}
|
||||
|
||||
public RealmEntity getRealm() {
|
||||
|
@ -115,26 +115,26 @@ public class RoleEntity {
|
|||
|
||||
public void setRealm(RealmEntity realm) {
|
||||
this.realm = realm;
|
||||
this.appRealmConstraint = realm.getId();
|
||||
this.clientRealmConstraint = realm.getId();
|
||||
}
|
||||
|
||||
public ClientEntity getApplication() {
|
||||
return application;
|
||||
public ClientEntity getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setApplication(ClientEntity application) {
|
||||
this.application = application;
|
||||
if (application != null) {
|
||||
this.appRealmConstraint = application.getId();
|
||||
public void setClient(ClientEntity client) {
|
||||
this.client = client;
|
||||
if (client != null) {
|
||||
this.clientRealmConstraint = client.getId();
|
||||
}
|
||||
}
|
||||
|
||||
public String getAppRealmConstraint() {
|
||||
return appRealmConstraint;
|
||||
public String getClientRealmConstraint() {
|
||||
return clientRealmConstraint;
|
||||
}
|
||||
|
||||
public void setAppRealmConstraint(String appRealmConstraint) {
|
||||
this.appRealmConstraint = appRealmConstraint;
|
||||
public void setClientRealmConstraint(String clientRealmConstraint) {
|
||||
this.clientRealmConstraint = clientRealmConstraint;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -91,7 +91,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
|
||||
@Override
|
||||
public SslRequired getSslRequired() {
|
||||
return SslRequired.valueOf(realm.getSslRequired());
|
||||
return realm.getSslRequired() != null ? SslRequired.valueOf(realm.getSslRequired()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -604,11 +604,11 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
List<MongoClientEntity> appDatas = getMongoStore().loadEntities(MongoClientEntity.class, query, invocationContext);
|
||||
List<MongoClientEntity> clientEntities = getMongoStore().loadEntities(MongoClientEntity.class, query, invocationContext);
|
||||
|
||||
List<ClientModel> result = new ArrayList<ClientModel>();
|
||||
for (MongoClientEntity appData : appDatas) {
|
||||
result.add(new ClientAdapter(session, this, appData, invocationContext));
|
||||
for (MongoClientEntity clientEntity : clientEntities) {
|
||||
result.add(new ClientAdapter(session, this, clientEntity, invocationContext));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -620,14 +620,14 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
|
||||
@Override
|
||||
public ClientModel addClient(String id, String clientId) {
|
||||
MongoClientEntity appData = new MongoClientEntity();
|
||||
appData.setId(id);
|
||||
appData.setClientId(clientId);
|
||||
appData.setRealmId(getId());
|
||||
appData.setEnabled(true);
|
||||
getMongoStore().insertEntity(appData, invocationContext);
|
||||
MongoClientEntity clientEntity = new MongoClientEntity();
|
||||
clientEntity.setId(id);
|
||||
clientEntity.setClientId(clientId);
|
||||
clientEntity.setRealmId(getId());
|
||||
clientEntity.setEnabled(true);
|
||||
getMongoStore().insertEntity(clientEntity, invocationContext);
|
||||
|
||||
final ClientModel model = new ClientAdapter(session, this, appData, invocationContext);
|
||||
final ClientModel model = new ClientAdapter(session, this, clientEntity, invocationContext);
|
||||
session.getKeycloakSessionFactory().publish(new ClientCreationEvent() {
|
||||
@Override
|
||||
public ClientModel getCreatedClient() {
|
||||
|
@ -979,14 +979,14 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
|
||||
@Override
|
||||
public ClientModel getMasterAdminClient() {
|
||||
MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, realm.getAdminAppId(), invocationContext);
|
||||
MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, realm.getMasterAdminClient(), invocationContext);
|
||||
return appData != null ? new ClientAdapter(session, this, appData, invocationContext) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMasterAdminClient(ClientModel client) {
|
||||
String adminAppId = client != null ? client.getId() : null;
|
||||
realm.setAdminAppId(adminAppId);
|
||||
realm.setMasterAdminClient(adminAppId);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.models.mongo.keycloak.adapters;
|
||||
|
||||
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
||||
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -17,6 +19,8 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -29,7 +33,7 @@ import java.util.Set;
|
|||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implements UserModel {
|
||||
|
||||
|
||||
private final MongoUserEntity user;
|
||||
private final RealmModel realm;
|
||||
private final KeycloakSession session;
|
||||
|
@ -177,31 +181,81 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
|||
|
||||
@Override
|
||||
public void updateCredential(UserCredentialModel cred) {
|
||||
|
||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
updatePasswordCredential(cred);
|
||||
} else {
|
||||
CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = setCredentials(user, cred);
|
||||
credentialEntity.setValue(cred.getValue());
|
||||
user.getCredentials().add(credentialEntity);
|
||||
} else {
|
||||
credentialEntity.setValue(cred.getValue());
|
||||
}
|
||||
}
|
||||
getMongoStore().updateEntity(user, invocationContext);
|
||||
}
|
||||
|
||||
private void updatePasswordCredential(UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(cred.getType());
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
credentialEntity = setCredentials(user, cred);
|
||||
setValue(credentialEntity, cred);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
}
|
||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
byte[] salt = Pbkdf2PasswordEncoder.getSalt();
|
||||
int hashIterations = 1;
|
||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||
if (policy != null) {
|
||||
hashIterations = policy.getHashIterations();
|
||||
if (hashIterations == -1) hashIterations = 1;
|
||||
}
|
||||
credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
|
||||
credentialEntity.setSalt(salt);
|
||||
credentialEntity.setHashIterations(hashIterations);
|
||||
} else {
|
||||
credentialEntity.setValue(cred.getValue());
|
||||
}
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
|
||||
getMongoStore().updateEntity(user, invocationContext);
|
||||
int expiredPasswordsPolicyValue = -1;
|
||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||
if(policy != null) {
|
||||
expiredPasswordsPolicyValue = policy.getExpiredPasswords();
|
||||
}
|
||||
|
||||
if (expiredPasswordsPolicyValue != -1) {
|
||||
user.getCredentials().remove(credentialEntity);
|
||||
credentialEntity.setType(UserCredentialModel.PASSWORD_HISTORY);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
|
||||
List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
|
||||
if (credentialEntities.size() > expiredPasswordsPolicyValue - 1) {
|
||||
user.getCredentials().removeAll(credentialEntities.subList(expiredPasswordsPolicyValue - 1, credentialEntities.size()));
|
||||
}
|
||||
|
||||
credentialEntity = setCredentials(user, cred);
|
||||
setValue(credentialEntity, cred);
|
||||
user.getCredentials().add(credentialEntity);
|
||||
} else {
|
||||
List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
|
||||
if (credentialEntities != null && credentialEntities.size() > 0) {
|
||||
user.getCredentials().removeAll(credentialEntities);
|
||||
}
|
||||
setValue(credentialEntity, cred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CredentialEntity setCredentials(MongoUserEntity user, UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(cred.getType());
|
||||
credentialEntity.setCreatedDate(new Date().getTime());
|
||||
credentialEntity.setDevice(cred.getDevice());
|
||||
return credentialEntity;
|
||||
}
|
||||
|
||||
private void setValue(CredentialEntity credentialEntity, UserCredentialModel cred) {
|
||||
byte[] salt = getSalt();
|
||||
int hashIterations = 1;
|
||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||
if (policy != null) {
|
||||
hashIterations = policy.getHashIterations();
|
||||
if (hashIterations == -1)
|
||||
hashIterations = 1;
|
||||
}
|
||||
credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
|
||||
credentialEntity.setSalt(salt);
|
||||
credentialEntity.setHashIterations(hashIterations);
|
||||
}
|
||||
|
||||
private CredentialEntity getCredentialEntity(MongoUserEntity userEntity, String credType) {
|
||||
|
@ -214,6 +268,30 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
|||
return null;
|
||||
}
|
||||
|
||||
private List<CredentialEntity> getCredentialEntities(MongoUserEntity userEntity, String credType) {
|
||||
List<CredentialEntity> credentialEntities = new ArrayList<CredentialEntity>();
|
||||
for (CredentialEntity entity : userEntity.getCredentials()) {
|
||||
if (entity.getType().equals(credType)) {
|
||||
credentialEntities.add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Avoiding direct use of credSecond.getCreatedDate() - credFirst.getCreatedDate() to prevent Integer Overflow
|
||||
// Orders from most recent to least recent
|
||||
Collections.sort(credentialEntities, new Comparator<CredentialEntity>() {
|
||||
public int compare(CredentialEntity credFirst, CredentialEntity credSecond) {
|
||||
if (credFirst.getCreatedDate() > credSecond.getCreatedDate()) {
|
||||
return -1;
|
||||
} else if (credFirst.getCreatedDate() < credSecond.getCreatedDate()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
return credentialEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserCredentialValueModel> getCredentialsDirectly() {
|
||||
List<CredentialEntity> credentials = user.getCredentials();
|
||||
|
@ -222,6 +300,7 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
|||
UserCredentialValueModel credModel = new UserCredentialValueModel();
|
||||
credModel.setType(credEntity.getType());
|
||||
credModel.setDevice(credEntity.getDevice());
|
||||
credModel.setCreatedDate(credEntity.getCreatedDate());
|
||||
credModel.setValue(credEntity.getValue());
|
||||
credModel.setSalt(credEntity.getSalt());
|
||||
credModel.setHashIterations(credEntity.getHashIterations());
|
||||
|
@ -239,6 +318,7 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
|||
if (credentialEntity == null) {
|
||||
credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(credModel.getType());
|
||||
credModel.setCreatedDate(credModel.getCreatedDate());
|
||||
user.getCredentials().add(credentialEntity);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,10 @@ import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
|||
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.ConditionsType;
|
||||
import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationDataType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AudienceRestrictionType;
|
||||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||
import org.w3c.dom.Document;
|
||||
import java.net.URI;
|
||||
|
||||
import static org.keycloak.saml.common.util.StringUtil.isNotNull;
|
||||
|
||||
|
@ -156,6 +158,11 @@ public class SAML2LoginResponseBuilder {
|
|||
|
||||
AssertionType assertion = responseType.getAssertions().get(0).getAssertion();
|
||||
|
||||
//Add request issuer as the audience restriction
|
||||
AudienceRestrictionType audience = new AudienceRestrictionType();
|
||||
audience.addAudience(URI.create(requestIssuer));
|
||||
assertion.getConditions().addCondition(audience);
|
||||
|
||||
//Update Conditions NotOnOrAfter
|
||||
if(assertionExpiration > 0) {
|
||||
ConditionsType conditions = assertion.getConditions();
|
||||
|
|
|
@ -30,7 +30,7 @@ import org.keycloak.services.managers.ResourceAdminManager;
|
|||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.resources.admin.ClientAttributeCertificateResource;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
@ -152,7 +152,7 @@ public class SamlProtocol implements LoginProtocol {
|
|||
return builder.redirectBinding().response();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE );
|
||||
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,7 +309,7 @@ public class SamlProtocol implements LoginProtocol {
|
|||
samlDocument = builder.buildDocument(samlModel);
|
||||
} catch (Exception e) {
|
||||
logger.error("failed", e);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo,headers, Messages.FAILED_TO_PROCESS_RESPONSE);
|
||||
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
|
||||
}
|
||||
|
||||
SAML2BindingBuilder2 bindingBuilder = new SAML2BindingBuilder2();
|
||||
|
@ -331,7 +331,7 @@ public class SamlProtocol implements LoginProtocol {
|
|||
publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
|
||||
} catch (Exception e) {
|
||||
logger.error("failed", e);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE);
|
||||
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
|
||||
}
|
||||
bindingBuilder.encrypt(publicKey);
|
||||
}
|
||||
|
@ -343,7 +343,7 @@ public class SamlProtocol implements LoginProtocol {
|
|||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("failed", e);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE );
|
||||
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.keycloak.services.managers.ClientSessionCode;
|
|||
import org.keycloak.services.managers.HttpAuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.util.StreamUtil;
|
||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
|
@ -102,18 +102,18 @@ public class SamlService {
|
|||
if (!checkSsl()) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED );
|
||||
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
||||
}
|
||||
if (!realm.isEnabled()) {
|
||||
event.event(EventType.LOGIN_ERROR);
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
|
||||
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
|
||||
}
|
||||
|
||||
if (samlRequest == null && samlResponse == null) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.INVALID_TOKEN);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST );
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
|
||||
}
|
||||
return null;
|
||||
|
@ -127,7 +127,7 @@ public class SamlService {
|
|||
if (!uriInfo.getAbsolutePath().toString().equals(statusResponse.getDestination())) {
|
||||
event.error(Errors.INVALID_SAML_LOGOUT_RESPONSE);
|
||||
event.detail(Details.REASON, "invalid_destination");
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
|
||||
AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
|
||||
|
@ -135,7 +135,7 @@ public class SamlService {
|
|||
logger.warn("Unknown saml response.");
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.INVALID_TOKEN);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
// assume this is a logout response
|
||||
UserSessionModel userSession = authResult.getSession();
|
||||
|
@ -144,7 +144,7 @@ public class SamlService {
|
|||
logger.warn("UserSession is not tagged as logging out.");
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.INVALID_SAML_LOGOUT_RESPONSE);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
logger.debug("logout response");
|
||||
Response response = authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||
|
@ -157,7 +157,7 @@ public class SamlService {
|
|||
if (documentHolder == null) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.INVALID_TOKEN);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
|
||||
SAML2Object samlObject = documentHolder.getSamlObject();
|
||||
|
@ -169,32 +169,34 @@ public class SamlService {
|
|||
if (client == null) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER);
|
||||
return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
|
||||
}
|
||||
|
||||
if (!client.isEnabled()) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.CLIENT_DISABLED);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED);
|
||||
return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
|
||||
}
|
||||
if ((client instanceof ClientModel) && ((ClientModel)client).isBearerOnly()) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.NOT_ALLOWED);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.BEARER_ONLY);
|
||||
return ErrorPage.error(session, Messages.BEARER_ONLY);
|
||||
}
|
||||
if (client.isDirectGrantsOnly()) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.NOT_ALLOWED);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.DIRECT_GRANTS_ONLY );
|
||||
return ErrorPage.error(session, Messages.DIRECT_GRANTS_ONLY);
|
||||
}
|
||||
|
||||
session.getContext().setClient(client);
|
||||
|
||||
try {
|
||||
verifySignature(documentHolder, client);
|
||||
} catch (VerificationException e) {
|
||||
SamlService.logger.error("request validation failed", e);
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.INVALID_SIGNATURE);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUESTER);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUESTER);
|
||||
}
|
||||
logger.debug("verified request");
|
||||
if (samlObject instanceof AuthnRequestType) {
|
||||
|
@ -212,7 +214,7 @@ public class SamlService {
|
|||
} else {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.INVALID_TOKEN);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +228,7 @@ public class SamlService {
|
|||
if (!uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
|
||||
event.error(Errors.INVALID_SAML_AUTHN_REQUEST);
|
||||
event.detail(Details.REASON, "invalid_destination");
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
String bindingType = getBindingType(requestAbstractType);
|
||||
if ("true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING))) bindingType = SamlProtocol.SAML_POST_BINDING;
|
||||
|
@ -248,7 +250,7 @@ public class SamlService {
|
|||
|
||||
if (redirect == null) {
|
||||
event.error(Errors.INVALID_REDIRECT_URI);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI );
|
||||
return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
|
||||
}
|
||||
|
||||
|
||||
|
@ -271,7 +273,7 @@ public class SamlService {
|
|||
} else {
|
||||
event.error(Errors.INVALID_SAML_AUTHN_REQUEST);
|
||||
event.detail(Details.REASON, "unsupported_nameid_format");
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNSUPPORTED_NAME_ID_FORMAT);
|
||||
return ErrorPage.error(session, Messages.UNSUPPORTED_NAME_ID_FORMAT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,7 +285,7 @@ public class SamlService {
|
|||
HttpAuthenticationManager.HttpAuthOutput httpAuthOutput = httpAuthManager.spnegoAuthenticate(headers);
|
||||
if (httpAuthOutput.getResponse() != null) return httpAuthOutput.getResponse();
|
||||
|
||||
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
|
||||
LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode());
|
||||
|
||||
// Attach state from SPNEGO authentication
|
||||
|
@ -335,7 +337,7 @@ public class SamlService {
|
|||
if (!uriInfo.getAbsolutePath().equals(logoutRequest.getDestination())) {
|
||||
event.error(Errors.INVALID_SAML_LOGOUT_REQUEST);
|
||||
event.detail(Details.REASON, "invalid_destination");
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
|
||||
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
|
||||
|
@ -374,7 +376,7 @@ public class SamlService {
|
|||
if (redirectUri != null) {
|
||||
redirectUri = RedirectUtils.verifyRedirectUri(uriInfo, redirectUri, realm, client);
|
||||
if (redirectUri == null) {
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI );
|
||||
return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
|
||||
}
|
||||
}
|
||||
if (redirectUri != null) {
|
||||
|
|
|
@ -14,6 +14,13 @@
|
|||
}
|
||||
},
|
||||
|
||||
"eventsListener": {
|
||||
"jboss-logging" : {
|
||||
"success-level": "debug",
|
||||
"error-level": "warn"
|
||||
}
|
||||
},
|
||||
|
||||
"realm": {
|
||||
"provider": "jpa"
|
||||
},
|
||||
|
|
|
@ -18,7 +18,6 @@ import org.keycloak.protocol.oidc.endpoints.ValidateTokenEndpoint;
|
|||
import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
|
@ -204,7 +203,7 @@ public class OIDCLoginProtocolService {
|
|||
@Path("oauth/oob")
|
||||
@GET
|
||||
public Response installedAppUrnCallback(final @QueryParam("code") String code, final @QueryParam("error") String error, final @QueryParam("error_description") String errorDescription) {
|
||||
LoginFormsProvider forms = Flows.forms(session, realm, null, uriInfo, headers);
|
||||
LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class);
|
||||
if (code != null) {
|
||||
return forms.setClientSessionCode(code).createCode();
|
||||
} else {
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
package org.keycloak.protocol.oidc;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.wellknown.WellKnownProvider;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -29,8 +28,17 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
|
|||
|
||||
public static final List<String> DEFAULT_RESPONSE_MODES_SUPPORTED = list("query");
|
||||
|
||||
private KeycloakSession session;
|
||||
|
||||
public OIDCWellKnownProvider(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig(RealmModel realm, UriInfo uriInfo) {
|
||||
public Object getConfig() {
|
||||
UriInfo uriInfo = session.getContext().getUri();
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
|
||||
UriBuilder uriBuilder = RealmsResource.protocolUrl(uriInfo);
|
||||
|
||||
OIDCConfigurationRepresentation config = new OIDCConfigurationRepresentation();
|
||||
|
|
|
@ -11,16 +11,13 @@ import org.keycloak.wellknown.WellKnownProviderFactory;
|
|||
*/
|
||||
public class OIDCWellKnownProviderFactory implements WellKnownProviderFactory {
|
||||
|
||||
private WellKnownProvider provider;
|
||||
|
||||
@Override
|
||||
public WellKnownProvider create(KeycloakSession session) {
|
||||
return provider;
|
||||
return new OIDCWellKnownProvider(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
provider = new OIDCWellKnownProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,7 +26,6 @@ public class OIDCWellKnownProviderFactory implements WellKnownProviderFactory {
|
|||
|
||||
@Override
|
||||
public void close() {
|
||||
provider = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,8 +25,7 @@ import org.keycloak.services.managers.AuthenticationManager;
|
|||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.managers.HttpAuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
@ -116,7 +115,7 @@ public class AuthorizationEndpoint {
|
|||
action = Action.REGISTER;
|
||||
|
||||
if (!realm.isRegistrationAllowed()) {
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.REGISTRATION_NOT_ALLOWED);
|
||||
throw new ErrorPageException(session, Messages.REGISTRATION_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -148,21 +147,21 @@ public class AuthorizationEndpoint {
|
|||
private void checkSsl() {
|
||||
if (!uriInfo.getBaseUri().getScheme().equals("https") && realm.getSslRequired().isRequired(clientConnection)) {
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
|
||||
throw new ErrorPageException(session, Messages.HTTPS_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRealm() {
|
||||
if (!realm.isEnabled()) {
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
|
||||
throw new ErrorPageException(session, Messages.REALM_NOT_ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkClient() {
|
||||
if (clientId == null) {
|
||||
event.error(Errors.INVALID_REQUEST);
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.MISSING_PARAMETER, OIDCLoginProtocol.CLIENT_ID_PARAM );
|
||||
throw new ErrorPageException(session, Messages.MISSING_PARAMETER, OIDCLoginProtocol.CLIENT_ID_PARAM );
|
||||
}
|
||||
|
||||
event.client(clientId);
|
||||
|
@ -170,18 +169,20 @@ public class AuthorizationEndpoint {
|
|||
client = realm.getClientByClientId(clientId);
|
||||
if (client == null) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.CLIENT_NOT_FOUND );
|
||||
throw new ErrorPageException(session, Messages.CLIENT_NOT_FOUND );
|
||||
}
|
||||
|
||||
if ((client instanceof ClientModel) && ((ClientModel) client).isBearerOnly()) {
|
||||
event.error(Errors.NOT_ALLOWED);
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.BEARER_ONLY );
|
||||
throw new ErrorPageException(session, Messages.BEARER_ONLY );
|
||||
}
|
||||
|
||||
if (client.isDirectGrantsOnly()) {
|
||||
event.error(Errors.NOT_ALLOWED);
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.DIRECT_GRANTS_ONLY);
|
||||
throw new ErrorPageException(session, Messages.DIRECT_GRANTS_ONLY);
|
||||
}
|
||||
|
||||
session.getContext().setClient(client);
|
||||
}
|
||||
|
||||
private void checkResponseType() {
|
||||
|
@ -190,7 +191,7 @@ public class AuthorizationEndpoint {
|
|||
responseType = legacyResponseType;
|
||||
} else {
|
||||
event.error(Errors.INVALID_REQUEST);
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.MISSING_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM );
|
||||
throw new ErrorPageException(session, Messages.MISSING_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +201,7 @@ public class AuthorizationEndpoint {
|
|||
action = Action.CODE;
|
||||
} else {
|
||||
event.error(Errors.INVALID_REQUEST);
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.INVALID_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM );
|
||||
throw new ErrorPageException(session, Messages.INVALID_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +211,7 @@ public class AuthorizationEndpoint {
|
|||
redirectUri = RedirectUtils.verifyRedirectUri(uriInfo, redirectUriParam, realm, client);
|
||||
if (redirectUri == null) {
|
||||
event.error(Errors.INVALID_REDIRECT_URI);
|
||||
throw new ErrorPageException(session, realm, uriInfo, headers, Messages.INVALID_PARAMETER, OIDCLoginProtocol.REDIRECT_URI_PARAM);
|
||||
throw new ErrorPageException(session, Messages.INVALID_PARAMETER, OIDCLoginProtocol.REDIRECT_URI_PARAM);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,7 +239,7 @@ public class AuthorizationEndpoint {
|
|||
IdentityProviderModel identityProviderModel = realm.getIdentityProviderByAlias(idpHint);
|
||||
|
||||
if (identityProviderModel == null) {
|
||||
return Flows.forms(session, realm, null, uriInfo, headers)
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setError(Messages.IDENTITY_PROVIDER_NOT_FOUND, idpHint)
|
||||
.createErrorPage();
|
||||
}
|
||||
|
@ -272,14 +273,13 @@ public class AuthorizationEndpoint {
|
|||
return buildRedirectToIdentityProvider(identityProviders.get(0).getAlias(), accessCode);
|
||||
}
|
||||
|
||||
return Flows.forms(session, realm, null, uriInfo, headers).setError(Messages.IDENTITY_PROVIDER_NOT_UNIQUE, realm.getName()).createErrorPage();
|
||||
return session.getProvider(LoginFormsProvider.class).setError(Messages.IDENTITY_PROVIDER_NOT_UNIQUE, realm.getName()).createErrorPage();
|
||||
}
|
||||
|
||||
return Flows.forms(session, realm, null, uriInfo, headers).setError(Messages.REALM_SUPPORTS_NO_CREDENTIALS, realm.getName()).createErrorPage();
|
||||
return session.getProvider(LoginFormsProvider.class).setError(Messages.REALM_SUPPORTS_NO_CREDENTIALS, realm.getName()).createErrorPage();
|
||||
}
|
||||
|
||||
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
|
||||
.setClientSessionCode(accessCode);
|
||||
LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode);
|
||||
|
||||
// Attach state from SPNEGO authentication
|
||||
if (httpAuthOutput.getChallenge() != null) {
|
||||
|
@ -307,7 +307,7 @@ public class AuthorizationEndpoint {
|
|||
private Response buildRegister() {
|
||||
authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
|
||||
|
||||
return Flows.forms(session, realm, client, uriInfo, headers)
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode())
|
||||
.createRegistration();
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.keycloak.services.ErrorResponseException;
|
|||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.Cors;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
|
@ -92,7 +92,7 @@ public class LogoutEndpoint {
|
|||
event.event(EventType.LOGOUT);
|
||||
event.detail(Details.REDIRECT_URI, redirect);
|
||||
event.error(Errors.INVALID_REDIRECT_URI);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI);
|
||||
return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
|
||||
}
|
||||
redirect = validatedUri;
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ public class LogoutEndpoint {
|
|||
if (error) {
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.INVALID_TOKEN);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
|
||||
return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.keycloak.services.ErrorResponseException;
|
|||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.resources.Cors;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import javax.ws.rs.OPTIONS;
|
||||
import javax.ws.rs.POST;
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.keycloak.representations.AccessToken;
|
|||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.managers.AppAuthManager;
|
||||
import org.keycloak.services.resources.Cors;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.FormParam;
|
||||
|
|
|
@ -15,10 +15,9 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.*;
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package org.keycloak.services;
|
||||
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakContext;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultKeycloakContext implements KeycloakContext {
|
||||
|
||||
private RealmModel realm;
|
||||
|
||||
private ClientModel client;
|
||||
|
||||
@Override
|
||||
public UriInfo getUri() {
|
||||
return ResteasyProviderFactory.getContextData(UriInfo.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getRequestHeaders() {
|
||||
return ResteasyProviderFactory.getContextData(HttpHeaders.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRealm(RealmModel realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClient(ClientModel client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
package org.keycloak.services;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.models.KeycloakContext;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakTransactionManager;
|
||||
|
@ -12,6 +15,7 @@ import org.keycloak.models.cache.CacheUserProvider;
|
|||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
@ -32,11 +36,18 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
private UserProvider userModel;
|
||||
private UserSessionProvider sessionProvider;
|
||||
private UserFederationManager federationManager;
|
||||
private KeycloakContext context;
|
||||
|
||||
public DefaultKeycloakSession(DefaultKeycloakSessionFactory factory) {
|
||||
this.factory = factory;
|
||||
this.transactionManager = new DefaultKeycloakTransactionManager();
|
||||
federationManager = new UserFederationManager(this);
|
||||
context = new DefaultKeycloakContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
private RealmProvider getRealmProvider() {
|
||||
|
|
|
@ -19,35 +19,20 @@
|
|||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.services.resources.flows;
|
||||
package org.keycloak.services;
|
||||
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class Flows {
|
||||
public class ErrorPage {
|
||||
|
||||
private Flows() {
|
||||
}
|
||||
|
||||
public static LoginFormsProvider forms(KeycloakSession session, RealmModel realm, ClientModel client, UriInfo uriInfo, HttpHeaders headers) {
|
||||
return session.getProvider(LoginFormsProvider.class).setRealm(realm).setUriInfo(uriInfo).setClient(client).setHttpHeaders(headers);
|
||||
}
|
||||
|
||||
public static ErrorFlows errors() {
|
||||
return new ErrorFlows();
|
||||
}
|
||||
|
||||
public static Response forwardToSecurityFailurePage(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String message, Object ... parameters) {
|
||||
return Flows.forms(session, realm, null, uriInfo, headers).setError(message,parameters).createErrorPage();
|
||||
public static Response error(KeycloakSession session, String message, Object... parameters) {
|
||||
return session.getProvider(LoginFormsProvider.class).setError(message, parameters).createErrorPage();
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue