Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
aaddaa6c25
200 changed files with 5909 additions and 2962 deletions
|
@ -27,5 +27,6 @@
|
|||
<artifactId>infinispan-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -66,6 +66,19 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
|
|||
} else {
|
||||
initEmbedded();
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
if (cacheManager.getCacheConfiguration(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME) == null) {
|
||||
logger.warnf("No configuration provided for '%s' cache. Using '%s' configuration as template",
|
||||
InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, InfinispanConnectionProvider.SESSION_CACHE_NAME);
|
||||
|
||||
Configuration sessionCacheConfig = cacheManager.getCacheConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME);
|
||||
if (sessionCacheConfig != null) {
|
||||
ConfigurationBuilder confBuilder = new ConfigurationBuilder().read(sessionCacheConfig);
|
||||
Configuration offlineSessionConfig = confBuilder.build();
|
||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, offlineSessionConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ public interface InfinispanConnectionProvider extends Provider {
|
|||
static final String REALM_CACHE_NAME = "realms";
|
||||
static final String USER_CACHE_NAME = "users";
|
||||
static final String SESSION_CACHE_NAME = "sessions";
|
||||
static final String OFFLINE_SESSION_CACHE_NAME = "offlineSessions";
|
||||
static final String LOGIN_FAILURE_CACHE_NAME = "loginFailures";
|
||||
|
||||
<K, V> Cache<K, V> getCache(String name);
|
||||
|
|
|
@ -10,22 +10,27 @@
|
|||
|
||||
<addColumn tableName="CLIENT">
|
||||
<column name="ROOT_URL" type="VARCHAR(255)"/>
|
||||
<column name="DESCRIPTION" type="VARCHAR(255)"/>
|
||||
</addColumn>
|
||||
|
||||
<createTable tableName="OFFLINE_USER_SESSION">
|
||||
<column name="USER_SESSION_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="USER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="USER_SESSION_ID" type="VARCHAR(36)">
|
||||
<column name="REALM_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="LAST_SESSION_REFRESH" type="INT"/>
|
||||
<column name="OFFLINE" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="DATA" type="CLOB"/>
|
||||
</createTable>
|
||||
|
||||
<createTable tableName="OFFLINE_CLIENT_SESSION">
|
||||
<column name="USER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="CLIENT_SESSION_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
|
@ -35,14 +40,19 @@
|
|||
<column name="CLIENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="OFFLINE" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="DATA" type="CLOB"/>
|
||||
</createTable>
|
||||
|
||||
<addPrimaryKey columnNames="USER_SESSION_ID" constraintName="CONSTRAINT_OFFLINE_US_SES_PK" tableName="OFFLINE_USER_SESSION"/>
|
||||
<addPrimaryKey columnNames="CLIENT_SESSION_ID" constraintName="CONSTRAINT_OFFLINE_CL_SES_PK" tableName="OFFLINE_CLIENT_SESSION"/>
|
||||
<addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="OFFLINE_USER_SESSION" constraintName="FK_OFFLINE_US_SES_USER" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
|
||||
<addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="OFFLINE_CLIENT_SESSION" constraintName="FK_OFFLINE_CL_SES_USER" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
|
||||
<addForeignKeyConstraint baseColumnNames="USER_SESSION_ID" baseTableName="OFFLINE_CLIENT_SESSION" constraintName="FK_OFFLINE_CL_US_SES" referencedColumnNames="USER_SESSION_ID" referencedTableName="OFFLINE_USER_SESSION"/>
|
||||
<addPrimaryKey columnNames="USER_SESSION_ID, OFFLINE" constraintName="CONSTRAINT_OFFLINE_US_SES_PK" tableName="OFFLINE_USER_SESSION"/>
|
||||
<addPrimaryKey columnNames="CLIENT_SESSION_ID, OFFLINE" constraintName="CONSTRAINT_OFFLINE_CL_SES_PK" tableName="OFFLINE_CLIENT_SESSION"/>
|
||||
|
||||
<addColumn tableName="REALM">
|
||||
<column name="REVOKE_REFRESH_TOKEN" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
|
@ -29,8 +29,8 @@
|
|||
<class>org.keycloak.models.jpa.entities.AuthenticationExecutionEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.AuthenticatorConfigEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RequiredActionProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.OfflineUserSessionEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.OfflineClientSessionEntity</class>
|
||||
<class>org.keycloak.models.jpa.session.PersistentUserSessionEntity</class>
|
||||
<class>org.keycloak.models.jpa.session.PersistentClientSessionEntity</class>
|
||||
|
||||
<!-- JpaAuditProviders -->
|
||||
<class>org.keycloak.events.jpa.EventEntity</class>
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.connections.mongo;
|
|||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoClientOptions;
|
||||
import com.mongodb.MongoClientURI;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
|
||||
|
@ -35,6 +36,8 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
|||
"org.keycloak.models.mongo.keycloak.entities.MongoClientEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoOnlineUserSessionEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoOfflineUserSessionEntity",
|
||||
"org.keycloak.models.entities.IdentityProviderEntity",
|
||||
"org.keycloak.models.entities.ClientIdentityProviderMappingEntity",
|
||||
"org.keycloak.models.entities.RequiredCredentialEntity",
|
||||
|
@ -48,8 +51,8 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
|||
"org.keycloak.models.entities.AuthenticationFlowEntity",
|
||||
"org.keycloak.models.entities.AuthenticatorConfigEntity",
|
||||
"org.keycloak.models.entities.RequiredActionProviderEntity",
|
||||
"org.keycloak.models.entities.OfflineUserSessionEntity",
|
||||
"org.keycloak.models.entities.OfflineClientSessionEntity",
|
||||
"org.keycloak.models.entities.PersistentUserSessionEntity",
|
||||
"org.keycloak.models.entities.PersistentClientSessionEntity",
|
||||
};
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
|
||||
|
@ -147,31 +150,55 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
|||
* @throws UnknownHostException
|
||||
*/
|
||||
protected MongoClient createMongoClient() throws UnknownHostException {
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
operationalInfo = new LinkedHashMap<>();
|
||||
String dbName = config.get("db", "keycloak");
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
String uriString = config.get("uri");
|
||||
if (uriString != null) {
|
||||
MongoClientURI uri = new MongoClientURI(uriString);
|
||||
MongoClient client = new MongoClient(uri);
|
||||
|
||||
MongoClientOptions clientOptions = getClientOptions();
|
||||
StringBuilder hostsBuilder = new StringBuilder();
|
||||
for (int i=0 ; i<uri.getHosts().size() ; i++) {
|
||||
if (i!=0) {
|
||||
hostsBuilder.append(", ");
|
||||
}
|
||||
hostsBuilder.append(uri.getHosts().get(i));
|
||||
}
|
||||
String hosts = hostsBuilder.toString();
|
||||
|
||||
MongoClient client;
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
|
||||
operationalInfo.put("mongoHosts", hosts);
|
||||
operationalInfo.put("mongoDatabaseName", dbName);
|
||||
operationalInfo.put("mongoUser", uri.getUsername());
|
||||
operationalInfo.put("mongoDriverVersion", client.getVersion());
|
||||
|
||||
logger.debugv("Initialized mongo model. host(s): %s, db: %s", uri.getHosts(), dbName);
|
||||
return client;
|
||||
} else {
|
||||
client = new MongoClient(new ServerAddress(host, port), clientOptions);
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
|
||||
MongoClientOptions clientOptions = getClientOptions();
|
||||
|
||||
MongoClient client;
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
|
||||
} else {
|
||||
client = new MongoClient(new ServerAddress(host, port), clientOptions);
|
||||
}
|
||||
|
||||
operationalInfo.put("mongoServerAddress", client.getAddress().toString());
|
||||
operationalInfo.put("mongoDatabaseName", dbName);
|
||||
operationalInfo.put("mongoUser", user);
|
||||
operationalInfo.put("mongoDriverVersion", client.getVersion());
|
||||
|
||||
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
|
||||
return client;
|
||||
}
|
||||
|
||||
operationalInfo = new LinkedHashMap<>();
|
||||
operationalInfo.put("mongoServerAddress", client.getAddress().toString());
|
||||
operationalInfo.put("mongoDatabaseName", dbName);
|
||||
operationalInfo.put("mongoUser", user);
|
||||
operationalInfo.put("mongoDriverVersion", client.getVersion());
|
||||
|
||||
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
|
||||
return client;
|
||||
}
|
||||
|
||||
protected MongoClientOptions getClientOptions() {
|
||||
|
|
|
@ -41,6 +41,9 @@ public interface OAuth2Constants {
|
|||
// http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
|
||||
String OFFLINE_ACCESS = "offline_access";
|
||||
|
||||
String UI_LOCALES_PARAM = "ui_locales";
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ public class ClientRepresentation {
|
|||
protected String id;
|
||||
protected String clientId;
|
||||
protected String name;
|
||||
protected String description;
|
||||
protected String rootUrl;
|
||||
protected String adminUrl;
|
||||
protected String baseUrl;
|
||||
|
@ -51,6 +52,14 @@ public class ClientRepresentation {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ public class RealmRepresentation {
|
|||
protected String id;
|
||||
protected String realm;
|
||||
protected Integer notBefore;
|
||||
protected Boolean revokeRefreshToken;
|
||||
protected Integer accessTokenLifespan;
|
||||
protected Integer ssoSessionIdleTimeout;
|
||||
protected Integer ssoSessionMaxLifespan;
|
||||
|
@ -166,6 +167,14 @@ public class RealmRepresentation {
|
|||
this.sslRequired = sslRequired;
|
||||
}
|
||||
|
||||
public Boolean getRevokeRefreshToken() {
|
||||
return revokeRefreshToken;
|
||||
}
|
||||
|
||||
public void setRevokeRefreshToken(Boolean revokeRefreshToken) {
|
||||
this.revokeRefreshToken = revokeRefreshToken;
|
||||
}
|
||||
|
||||
public Integer getAccessTokenLifespan() {
|
||||
return accessTokenLifespan;
|
||||
}
|
||||
|
|
16
dependencies/server-all/pom.xml
vendored
16
dependencies/server-all/pom.xml
vendored
|
@ -141,22 +141,6 @@
|
|||
<artifactId>mongo-java-driver</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- export/import -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-zip</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.idyl</groupId>
|
||||
<artifactId>winzipaes</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk16</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
"provider": "jpa"
|
||||
},
|
||||
|
||||
"userSessionPersister": {
|
||||
"provider": "jpa"
|
||||
},
|
||||
|
||||
"timer": {
|
||||
"provider": "basic"
|
||||
},
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
|
||||
<module xmlns="urn:jboss:module:1.3" name="de.idyl.winzipaes">
|
||||
<resources>
|
||||
<artifact name="${de.idyl:winzipaes}"/>
|
||||
</resources>
|
||||
<dependencies>
|
||||
<module name="javax.api"/>
|
||||
<module name="org.bouncycastle"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
|
@ -1,26 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
|
||||
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-export-import-zip">
|
||||
<resources>
|
||||
<artifact name="${org.keycloak:keycloak-export-import-zip}"/>
|
||||
</resources>
|
||||
<dependencies>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-model-api"/>
|
||||
<module name="org.keycloak.keycloak-invalidation-cache-model"/>
|
||||
<module name="org.keycloak.keycloak-export-import-api"/>
|
||||
<module name="javax.ws.rs.api"/>
|
||||
<module name="org.codehaus.jackson.jackson-core-asl"/>
|
||||
<module name="org.codehaus.jackson.jackson-mapper-asl"/>
|
||||
<module name="org.codehaus.jackson.jackson-xc"/>
|
||||
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="org.bouncycastle" />
|
||||
<module name="de.idyl.winzipaes"/>
|
||||
<module name="javax.api"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
|
@ -21,7 +21,6 @@
|
|||
<module name="org.keycloak.keycloak-export-import-api" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-dir" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-single-file" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-zip" services="import"/>
|
||||
<module name="org.keycloak.keycloak-forms-common-freemarker" services="import"/>
|
||||
<module name="org.keycloak.keycloak-forms-common-themes" services="import"/>
|
||||
<module name="org.keycloak.keycloak-invalidation-cache-infinispan" services="import"/>
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
<module name="org.keycloak.keycloak-export-import-api" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-dir" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-single-file" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-zip" services="import"/>
|
||||
<module name="org.keycloak.keycloak-forms-common-freemarker" services="import"/>
|
||||
<module name="org.keycloak.keycloak-forms-common-themes" services="import"/>
|
||||
<module name="org.keycloak.keycloak-invalidation-cache-infinispan" services="import"/>
|
||||
|
|
|
@ -278,15 +278,6 @@
|
|||
<maven-resource group="org.mongodb" artifact="mongo-java-driver"/>
|
||||
</module-def>
|
||||
|
||||
<!-- export/import -->
|
||||
|
||||
<module-def name="org.keycloak.keycloak-export-import-zip">
|
||||
<maven-resource group="org.keycloak" artifact="keycloak-export-import-zip"/>
|
||||
</module-def>
|
||||
<module-def name="de.idyl.winzipaes">
|
||||
<maven-resource group="de.idyl" artifact="winzipaes"/>
|
||||
</module-def>
|
||||
|
||||
<module-def name="org.liquibase">
|
||||
<maven-resource group="org.liquibase" artifact="liquibase-core"/>
|
||||
</module-def>
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
|
||||
<module xmlns="urn:jboss:module:1.1" name="de.idyl.winzipaes">
|
||||
<resources>
|
||||
<!-- Insert resources here -->
|
||||
</resources>
|
||||
<dependencies>
|
||||
<module name="javax.api"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
|
@ -21,7 +21,6 @@
|
|||
<module name="org.keycloak.keycloak-export-import-api" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-dir" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-single-file" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-zip" services="import"/>
|
||||
<module name="org.keycloak.keycloak-forms-common-freemarker" services="import"/>
|
||||
<module name="org.keycloak.keycloak-forms-common-themes" services="import"/>
|
||||
<module name="org.keycloak.keycloak-invalidation-cache-infinispan" services="import"/>
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
|
||||
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-export-import-zip">
|
||||
<resources>
|
||||
<!-- Insert resources here -->
|
||||
</resources>
|
||||
<dependencies>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-model-api"/>
|
||||
<module name="org.keycloak.keycloak-invalidation-cache-model"/>
|
||||
<module name="org.keycloak.keycloak-export-import-api"/>
|
||||
<module name="javax.ws.rs.api"/>
|
||||
<module name="org.codehaus.jackson.jackson-core-asl"/>
|
||||
<module name="org.codehaus.jackson.jackson-mapper-asl"/>
|
||||
<module name="org.codehaus.jackson.jackson-xc"/>
|
||||
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="org.bouncycastle" />
|
||||
<module name="de.idyl.winzipaes"/>
|
||||
<module name="javax.api"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
|
@ -31,7 +31,6 @@
|
|||
<module name="org.keycloak.keycloak-export-import-api" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-dir" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-single-file" services="import"/>
|
||||
<module name="org.keycloak.keycloak-export-import-zip" services="import"/>
|
||||
<module name="org.keycloak.keycloak-forms-common-freemarker" services="import"/>
|
||||
<module name="org.keycloak.keycloak-forms-common-themes" services="import"/>
|
||||
<module name="org.keycloak.keycloak-invalidation-cache-infinispan" services="import"/>
|
||||
|
|
|
@ -20,6 +20,13 @@
|
|||
<include>**/**</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<!--<fileSet>
|
||||
<directory>cli</directory>
|
||||
<includes>
|
||||
<include>*.cli</include>
|
||||
</includes>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</fileSet>-->
|
||||
</fileSets>
|
||||
|
||||
<files>
|
||||
|
@ -45,14 +52,6 @@
|
|||
<source>src/main/providers/README.txt</source>
|
||||
<outputDirectory>standalone/configuration/providers</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>cli/keycloak-prepare.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>cli/keycloak-install.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
|
||||
</assembly>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true,enabled=true)
|
||||
/subsystem=logging/logger=org.jboss.resteasy.resteasy_jaxrs.i18n/:add(level=ERROR)
|
||||
/subsystem=infinispan/cache-container=keycloak:add(jndi-name="infinispan/Keycloak")
|
||||
/subsystem=infinispan/cache-container=keycloak/transport=TRANSPORT:add(lock-timeout=60000)
|
||||
/subsystem=infinispan/cache-container=keycloak/invalidation-cache=realms:add(mode="SYNC")
|
||||
/subsystem=infinispan/cache-container=keycloak/invalidation-cache=users:add(mode="SYNC")
|
||||
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:add(mode="SYNC",owners="1")
|
||||
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:add(mode="SYNC",owners="1")
|
|
@ -1,2 +1,7 @@
|
|||
/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true,enabled=true)
|
||||
/subsystem=logging/logger=org.jboss.resteasy.resteasy_jaxrs.i18n/:add(level=ERROR)
|
||||
/subsystem=logging/logger=org.jboss.resteasy.resteasy_jaxrs.i18n/:add(level=ERROR)
|
||||
/subsystem=infinispan/cache-container=keycloak:add(jndi-name="infinispan/Keycloak",start="EAGER")
|
||||
/subsystem=infinispan/cache-container=keycloak/local-cache=realms:add()
|
||||
/subsystem=infinispan/cache-container=keycloak/local-cache=users:add()
|
||||
/subsystem=infinispan/cache-container=keycloak/local-cache=sessions:add()
|
||||
/subsystem=infinispan/cache-container=keycloak/local-cache=loginFailures:add()
|
|
@ -14,7 +14,6 @@
|
|||
<outputDirectory>modules/system/layers/base</outputDirectory>
|
||||
<includes>
|
||||
<include>com/google/zxing/**</include>
|
||||
<include>de/idyl/winzipaes/**</include>
|
||||
<include>org/freemarker/**</include>
|
||||
<include>org/keycloak/**</include>
|
||||
<include>org/liquibase/**</include>
|
||||
|
@ -48,7 +47,13 @@
|
|||
</includes>
|
||||
<outputDirectory></outputDirectory>
|
||||
</fileSet>
|
||||
|
||||
<!--<fileSet>
|
||||
<directory>cli</directory>
|
||||
<includes>
|
||||
<include>*.cli</include>
|
||||
</includes>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</fileSet>-->
|
||||
</fileSets>
|
||||
|
||||
<files>
|
||||
|
@ -66,10 +71,6 @@
|
|||
<source>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/keycloak-server.json</source>
|
||||
<outputDirectory>standalone/configuration</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>cli/keycloak-install.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
|
||||
</assembly>
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
|
||||
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
|
||||
/subsystem=keycloak-server:add(web-context=auth)
|
|
@ -0,0 +1,7 @@
|
|||
/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
|
||||
/subsystem=infinispan/cache-container=keycloak:add(jndi-name="infinispan/Keycloak")
|
||||
/subsystem=infinispan/cache-container=keycloak/transport=TRANSPORT:add(lock-timeout=60000)
|
||||
/subsystem=infinispan/cache-container=keycloak/invalidation-cache=realms:add(mode="SYNC")
|
||||
/subsystem=infinispan/cache-container=keycloak/invalidation-cache=users:add(mode="SYNC")
|
||||
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:add(mode="SYNC",owners="1")
|
||||
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:add(mode="SYNC",owners="1")
|
|
@ -0,0 +1,6 @@
|
|||
/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
|
||||
/subsystem=infinispan/cache-container=keycloak:add(jndi-name="infinispan/Keycloak")
|
||||
/subsystem=infinispan/cache-container=keycloak/local-cache=realms:add()
|
||||
/subsystem=infinispan/cache-container=keycloak/local-cache=users:add()
|
||||
/subsystem=infinispan/cache-container=keycloak/local-cache=sessions:add()
|
||||
/subsystem=infinispan/cache-container=keycloak/local-cache=loginFailures:add()
|
|
@ -79,6 +79,19 @@
|
|||
|
||||
<section>
|
||||
<title>Version specific migration</title>
|
||||
<section>
|
||||
<title>Migrating to 1.6.0.Final</title>
|
||||
<simplesect>
|
||||
<title>Refresh tokens are not reusable anymore</title>
|
||||
<para>
|
||||
Old versions of Keycloak allowed reusing refresh tokens multiple times. Keycloak no longer permits
|
||||
this by default. When a refresh token is used to obtain a new access token a new refresh token is also
|
||||
included. This new refresh token should be used next time the access token is refreshed. If this is
|
||||
a problem for you it's possible to enable reuse of refresh tokens in the admin console under token
|
||||
settings.
|
||||
</para>
|
||||
</simplesect>
|
||||
</section>
|
||||
<section>
|
||||
<title>Migrating to 1.5.0.Final</title>
|
||||
<simplesect>
|
||||
|
|
|
@ -8,12 +8,11 @@
|
|||
<para>
|
||||
You can export/import your database either to:
|
||||
<itemizedlist>
|
||||
<listitem>Encrypted ZIP file on local filesystem</listitem>
|
||||
<listitem>Directory on local filesystem</listitem>
|
||||
<listitem>Single JSON file on your filesystem</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
When importing using the "dir" or "zip" strategies, note that the files need to follow the naming convention specified below.
|
||||
When importing using the "dir" strategy, note that the files need to follow the naming convention specified below.
|
||||
If you are importing files which were previously exported, the files already follow this convention.
|
||||
<itemizedlist>
|
||||
<listitem>{REALM_NAME}-realm.json, such as "acme-roadrunner-affairs-realm.json" for the realm named "acme-roadrunner-affairs"</listitem>
|
||||
|
@ -21,26 +20,10 @@
|
|||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
Encrypted ZIP is recommended as export contains many sensitive informations like passwords of your users (even if they are hashed),
|
||||
but also their email addresses, and especially private keys of the realms. Directory and Single JSON file are useful especially
|
||||
for testing as data in the files are not protected. On the other hand, it's useful if you want to look at all your data in JSON
|
||||
files directly.
|
||||
</para>
|
||||
<para>
|
||||
If you import to ZIP or Directory, you can specify also the number of users to be stored in each JSON file. So if you have
|
||||
If you import to Directory, you can specify also the number of users to be stored in each JSON file. So if you have
|
||||
very large amount of users in your database, you likely don't want to import them into single file as the file might be very big.
|
||||
Processing of each file is done in separate transaction as exporting/importing all users at once could also lead to memory issues.
|
||||
</para>
|
||||
<para>
|
||||
So to export the content of your Keycloak database into encrypted ZIP, you can execute Keycloak server with the System properties like:
|
||||
<programlisting><![CDATA[
|
||||
bin/standalone.sh -Dkeycloak.migration.action=export
|
||||
-Dkeycloak.migration.provider=zip -Dkeycloak.migration.zipFile=<FILE TO EXPORT TO>
|
||||
-Dkeycloak.migration.zipPassword=<PASSWORD TO DECRYPT EXPORT>
|
||||
]]></programlisting>
|
||||
Then you can move or copy the encrypted ZIP file into second environment and you can trigger import from it into Keycloak server with the same command but use
|
||||
<literal>-Dkeycloak.migration.action=import</literal> instead of <literal>export</literal> .
|
||||
</para>
|
||||
<para>
|
||||
To export into unencrypted directory you can use:
|
||||
<programlisting><![CDATA[
|
||||
|
@ -80,7 +63,7 @@ bin/standalone.sh -Dkeycloak.migration.action=import
|
|||
<term>-Dkeycloak.migration.usersExportStrategy</term>
|
||||
<listitem>
|
||||
<para>
|
||||
can be used to specify for ZIP or Directory providers to specify where to import users.
|
||||
can be used to specify for Directory providers to specify where to import users.
|
||||
Possible values are:
|
||||
<itemizedlist>
|
||||
<listitem>DIFFERENT_FILES - Users will be exported into more different files according to maximum number of users per file. This is default value</listitem>
|
||||
|
|
|
@ -59,30 +59,23 @@
|
|||
(username: <emphasis>admin</emphasis> and password: <emphasis>admin</emphasis>). Keycloak will then prompt you to
|
||||
enter in a new password.
|
||||
</para>
|
||||
<para>
|
||||
To add Keycloak to other sever configurations (standalone.xml, standalone-ha.xml, etc.) start the server with
|
||||
the desired server-config. Then execute the following CLI script:
|
||||
<programlisting><WILDFLY_HOME>/bin/jboss-cli.sh -c --file=keycloak-install.cli</programlisting>
|
||||
</para>
|
||||
<!--<para>-->
|
||||
<!--To add Keycloak to other sever configurations (standalone.xml, standalone-ha.xml, etc.) start the server with-->
|
||||
<!--the desired server-config. If you are running the server in standalone mode run:-->
|
||||
<!--<programlisting><WILDFLY_HOME>/bin/jboss-cli.sh -c --file=keycloak-prepare.cli</programlisting>-->
|
||||
<!--Or if you are running in clustering (HA) mode run:-->
|
||||
<!--<programlisting><WILDFLY_HOME>/bin/jboss-cli.sh -c --file=keycloak-prepare-ha.cli</programlisting>-->
|
||||
<!--After that you need to restart the server, you can do this with:-->
|
||||
<!--<programlisting><WILDFLY_HOME>/bin/jboss-cli.sh -c :reload</programlisting>-->
|
||||
<!--Finally you need to run:-->
|
||||
<!--<programlisting><WILDFLY_HOME>/bin/jboss-cli.sh -c --file=keycloak-install.cli</programlisting>-->
|
||||
<!--</para>-->
|
||||
</section>
|
||||
<section>
|
||||
<title>Install on existing JBoss EAP 6.4.0.GA</title>
|
||||
<para>
|
||||
Same procedure as WildFly 9.0.0.Final, but download <literal>keycloak-overlay-eap6-&project.version;.zip</literal> or <literal>keycloak-overlay-eap6-&project.version;.tar.gz</literal>.
|
||||
</para>
|
||||
<para>
|
||||
However, for EAP, adding Keycloak to other sever configurations (standalone.xml, standalone-ha.xml, etc.) requires two CLI scripts. Start the server with
|
||||
the desired server-config. Then execute the following CLI scripts with a restart in between:
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<programlisting><EAP_HOME>/bin/jboss-cli.sh -c --file=keycloak-prepare.cli</programlisting>
|
||||
</listitem>
|
||||
<listitem>Restart the server with the same server-config.</listitem>
|
||||
<listitem>
|
||||
<programlisting><EAP_HOME>/bin/jboss-cli.sh -c --file=keycloak-install.cli</programlisting>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title id="demo_install">Install Development Bundle</title>
|
||||
|
@ -329,6 +322,30 @@
|
|||
Supported long option is <literal>maxAutoConnectRetryTime</literal>. See <ulink url="http://api.mongodb.org/java/2.11.4/com/mongodb/MongoClientOptions.html">Mongo documentation</ulink>
|
||||
for details about those options and their default values.
|
||||
</para>
|
||||
<para>
|
||||
Alternatively, you can configure MongoDB using a MongoDB <ulink url="http://docs.mongodb.org/manual/reference/connection-string/">connection URI</ulink>.
|
||||
In this case, you define all information concerning the connection and authentication within the URI, as described in the MongoDB documentation.
|
||||
Please note that the database specified within the URI is only used for authentication. To change the database used by keycloak you have to set
|
||||
<literal>db</literal> property as before. Therefore, a configuration like the
|
||||
following
|
||||
<programlisting><![CDATA[
|
||||
"connectionsMongo": {
|
||||
"default": {
|
||||
"uri": "mongodb://user:password@127.0.0.1/authentication",
|
||||
"db": "keycloak"
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
will authenticate the user against the authentication database, but store all keycloak related data in the keycloak database.
|
||||
|
||||
</para>
|
||||
<section>
|
||||
<title>MongoDB Replica Sets</title>
|
||||
<para>
|
||||
In order to use a mongo replica set for Keycloak, one has to use URI based configuration, which supports the
|
||||
definition of replica sets out of the box: <literal>mongodb://host1:27017,host2:27017,host3:27017/</literal>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
|
|
@ -70,6 +70,7 @@ public enum EventType {
|
|||
CUSTOM_REQUIRED_ACTION(true),
|
||||
CUSTOM_REQUIRED_ACTION_ERROR(true),
|
||||
EXECUTE_ACTIONS(true),
|
||||
EXECUTE_ACTIONS_ERROR(true),
|
||||
|
||||
CLIENT_INFO(false),
|
||||
CLIENT_INFO_ERROR(false),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.keycloak.exportimport.util;
|
||||
|
||||
import org.keycloak.models.OfflineClientSessionModel;
|
||||
import org.keycloak.models.OfflineUserSessionModel;
|
||||
import org.keycloak.models.session.PersistentClientSessionModel;
|
||||
import org.keycloak.models.session.PersistentUserSessionModel;
|
||||
import org.keycloak.representations.idm.OfflineUserSessionRepresentation;
|
||||
import org.keycloak.util.Base64;
|
||||
import org.codehaus.jackson.JsonEncoding;
|
||||
|
@ -11,7 +11,6 @@ import org.codehaus.jackson.map.ObjectMapper;
|
|||
import org.codehaus.jackson.map.SerializationConfig;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -298,27 +297,27 @@ public class ExportUtils {
|
|||
}
|
||||
}
|
||||
|
||||
// Offline sessions
|
||||
List<OfflineUserSessionRepresentation> offlineSessionReps = new LinkedList<>();
|
||||
Collection<OfflineUserSessionModel> offlineSessions = session.users().getOfflineUserSessions(realm, user);
|
||||
Collection<OfflineClientSessionModel> offlineClientSessions = session.users().getOfflineClientSessions(realm, user);
|
||||
|
||||
Map<String, List<OfflineClientSessionModel>> processed = new HashMap<>();
|
||||
for (OfflineClientSessionModel clsm : offlineClientSessions) {
|
||||
String userSessionId = clsm.getUserSessionId();
|
||||
List<OfflineClientSessionModel> current = processed.get(userSessionId);
|
||||
if (current == null) {
|
||||
current = new LinkedList<>();
|
||||
processed.put(userSessionId, current);
|
||||
}
|
||||
current.add(clsm);
|
||||
}
|
||||
|
||||
for (OfflineUserSessionModel userSession : offlineSessions) {
|
||||
OfflineUserSessionRepresentation sessionRep = ModelToRepresentation.toRepresentation(realm, userSession, processed.get(userSession.getUserSessionId()));
|
||||
offlineSessionReps.add(sessionRep);
|
||||
}
|
||||
userRep.setOfflineUserSessions(offlineSessionReps);
|
||||
// // Offline sessions
|
||||
// List<OfflineUserSessionRepresentation> offlineSessionReps = new LinkedList<>();
|
||||
// Collection<PersistentUserSessionModel> offlineSessions = session.users().getOfflineUserSessions(realm, user);
|
||||
// Collection<PersistentClientSessionModel> offlineClientSessions = session.users().getOfflineClientSessions(realm, user);
|
||||
//
|
||||
// Map<String, List<PersistentClientSessionModel>> processed = new HashMap<>();
|
||||
// for (PersistentClientSessionModel clsm : offlineClientSessions) {
|
||||
// String userSessionId = clsm.getUserSessionId();
|
||||
// List<PersistentClientSessionModel> current = processed.get(userSessionId);
|
||||
// if (current == null) {
|
||||
// current = new LinkedList<>();
|
||||
// processed.put(userSessionId, current);
|
||||
// }
|
||||
// current.add(clsm);
|
||||
// }
|
||||
//
|
||||
// for (PersistentUserSessionModel userSession : offlineSessions) {
|
||||
// OfflineUserSessionRepresentation sessionRep = ModelToRepresentation.toRepresentation(realm, userSession, processed.get(userSession.getUserSessionId()));
|
||||
// offlineSessionReps.add(sessionRep);
|
||||
// }
|
||||
// userRep.setOfflineUserSessions(offlineSessionReps);
|
||||
|
||||
return userRep;
|
||||
}
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-export-import-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.6.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-export-import-zip</artifactId>
|
||||
<name>Keycloak Export Import To Encrypted ZIP</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.idyl</groupId>
|
||||
<artifactId>winzipaes</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -1,82 +0,0 @@
|
|||
package org.keycloak.exportimport.zip;
|
||||
|
||||
import de.idyl.winzipaes.AesZipFileEncrypter;
|
||||
import de.idyl.winzipaes.impl.AESEncrypter;
|
||||
import de.idyl.winzipaes.impl.AESEncrypterBC;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.representations.VersionRepresentation;
|
||||
import org.keycloak.exportimport.util.ExportUtils;
|
||||
import org.keycloak.exportimport.util.MultipleStepsExportProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ZipExportProvider extends MultipleStepsExportProvider {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ZipExportProvider.class);
|
||||
|
||||
private final AesZipFileEncrypter encrypter;
|
||||
private final String password;
|
||||
|
||||
public ZipExportProvider(File zipFile, String password) {
|
||||
if (zipFile.exists()) {
|
||||
throw new IllegalStateException("File " + zipFile.getAbsolutePath() + " already exists");
|
||||
}
|
||||
this.password = password;
|
||||
|
||||
try {
|
||||
AESEncrypter encrypter = new AESEncrypterBC();
|
||||
this.encrypter = new AesZipFileEncrypter(zipFile, encrypter);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
logger.infof("Exporting into zip file %s", zipFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeRealm(String fileName, RealmRepresentation rep) throws IOException {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
JsonSerialization.mapper.writeValue(stream, rep);
|
||||
writeStream(fileName, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeUsers(String fileName, KeycloakSession session, RealmModel realm, List<UserModel> users) throws IOException {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
ExportUtils.exportUsersToStream(session, realm, users, JsonSerialization.mapper, stream);
|
||||
writeStream(fileName, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeVersion(String fileName, VersionRepresentation version) throws IOException {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
JsonSerialization.mapper.writeValue(stream, version);
|
||||
writeStream(fileName, stream);
|
||||
}
|
||||
|
||||
private void writeStream(String fileName, ByteArrayOutputStream stream) throws IOException {
|
||||
byte[] byteArray = stream.toByteArray();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(byteArray);
|
||||
this.encrypter.add(fileName, bis, this.password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
this.encrypter.close();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package org.keycloak.exportimport.zip;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.exportimport.ExportImportConfig;
|
||||
import org.keycloak.exportimport.ExportProvider;
|
||||
import org.keycloak.exportimport.ExportProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ZipExportProviderFactory implements ExportProviderFactory {
|
||||
|
||||
|
||||
public static final String PROVIDER_ID = "zip";
|
||||
|
||||
@Override
|
||||
public ExportProvider create(KeycloakSession session) {
|
||||
String fileName = ExportImportConfig.getZipFile();
|
||||
String password = ExportImportConfig.getZipPassword();
|
||||
if (fileName == null) {
|
||||
throw new IllegalArgumentException("ZIP file for export not provided");
|
||||
}
|
||||
if (password == null) {
|
||||
throw new IllegalArgumentException("Password for encrypting ZIP not provided");
|
||||
}
|
||||
return new ZipExportProvider(new File(fileName), password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
package org.keycloak.exportimport.zip;
|
||||
|
||||
import de.idyl.winzipaes.AesZipFileDecrypter;
|
||||
import de.idyl.winzipaes.impl.AESDecrypter;
|
||||
import de.idyl.winzipaes.impl.AESDecrypterBC;
|
||||
import de.idyl.winzipaes.impl.ExtZipEntry;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.exportimport.ImportProvider;
|
||||
import org.keycloak.exportimport.Strategy;
|
||||
import org.keycloak.exportimport.util.ExportImportSessionTask;
|
||||
import org.keycloak.exportimport.util.ImportUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ZipImportProvider implements ImportProvider {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ZipImportProvider.class);
|
||||
|
||||
private final AesZipFileDecrypter decrypter;
|
||||
private final String password;
|
||||
|
||||
public ZipImportProvider(File zipFile, String password) {
|
||||
try {
|
||||
if (!zipFile.exists()) {
|
||||
throw new IllegalStateException("File " + zipFile.getAbsolutePath() + " doesn't exists");
|
||||
}
|
||||
|
||||
AESDecrypter decrypter = new AESDecrypterBC();
|
||||
this.decrypter = new AesZipFileDecrypter(zipFile, decrypter);
|
||||
this.password = password;
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
logger.infof("Importing from ZIP file %s", zipFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importModel(KeycloakSessionFactory factory, Strategy strategy) throws IOException {
|
||||
List<String> realmNames = getRealmsToImport();
|
||||
|
||||
for (String realmName : realmNames) {
|
||||
importRealm(factory, realmName, strategy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMasterRealmExported() throws IOException {
|
||||
List<String> realmNames = getRealmsToImport();
|
||||
return realmNames.contains(Config.getAdminRealm());
|
||||
}
|
||||
|
||||
private List<String> getRealmsToImport() throws IOException {
|
||||
List<String> realmNames = new ArrayList<String>();
|
||||
for (ExtZipEntry entry : this.decrypter.getEntryList()) {
|
||||
String entryName = entry.getName();
|
||||
if (entryName.endsWith("-realm.json")) {
|
||||
// Parse "foo" from "foo-realm.json"
|
||||
String realmName = entryName.substring(0, entryName.length() - 11);
|
||||
|
||||
// Ensure that master realm is imported first
|
||||
if (Config.getAdminRealm().equals(realmName)) {
|
||||
realmNames.add(0, realmName);
|
||||
} else {
|
||||
realmNames.add(realmName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return realmNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importRealm(KeycloakSessionFactory factory, final String realmName, final Strategy strategy) throws IOException {
|
||||
try {
|
||||
// Import realm first
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
this.decrypter.extractEntry(this.decrypter.getEntry(realmName + "-realm.json"), bos, this.password);
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
|
||||
final RealmRepresentation realmRep = JsonSerialization.mapper.readValue(bis, RealmRepresentation.class);
|
||||
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
ImportUtils.importRealm(session, realmRep, strategy);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Import users
|
||||
for (ExtZipEntry entry : this.decrypter.getEntryList()) {
|
||||
String name = entry.getName();
|
||||
if (name.matches(realmName + "-users-[0-9]+\\.json")) {
|
||||
bos = new ByteArrayOutputStream();
|
||||
this.decrypter.extractEntry(entry, bos, this.password);
|
||||
final ByteArrayInputStream bis2 = new ByteArrayInputStream(bos.toByteArray());
|
||||
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, bis2);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new RuntimeException(dfe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
this.decrypter.close();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package org.keycloak.exportimport.zip;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.exportimport.ExportImportConfig;
|
||||
import org.keycloak.exportimport.ImportProvider;
|
||||
import org.keycloak.exportimport.ImportProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ZipImportProviderFactory implements ImportProviderFactory {
|
||||
|
||||
@Override
|
||||
public ImportProvider create(KeycloakSession session) {
|
||||
String fileName = ExportImportConfig.getZipFile();
|
||||
String password = ExportImportConfig.getZipPassword();
|
||||
if (fileName == null) {
|
||||
throw new IllegalArgumentException("ZIP file for import not provided");
|
||||
}
|
||||
if (password == null) {
|
||||
throw new IllegalArgumentException("Password for decrypting ZIP not provided");
|
||||
}
|
||||
return new ZipImportProvider(new File(fileName), password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ZipExportProviderFactory.PROVIDER_ID;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
org.keycloak.exportimport.zip.ZipExportProviderFactory
|
|
@ -1 +0,0 @@
|
|||
org.keycloak.exportimport.zip.ZipImportProviderFactory
|
|
@ -18,7 +18,6 @@
|
|||
<module>export-import-api</module>
|
||||
<module>export-import-dir</module>
|
||||
<module>export-import-single-file</module>
|
||||
<module>export-import-zip</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -51,7 +51,6 @@ import org.keycloak.events.Event;
|
|||
import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
|
||||
import org.keycloak.freemarker.FreeMarkerException;
|
||||
import org.keycloak.freemarker.FreeMarkerUtil;
|
||||
import org.keycloak.freemarker.LocaleHelper;
|
||||
import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.freemarker.ThemeProvider;
|
||||
import org.keycloak.freemarker.beans.AdvancedMessageFormatterMethod;
|
||||
|
@ -65,7 +64,6 @@ 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.Urls;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -130,7 +128,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
|
|||
logger.warn("Failed to load properties", e);
|
||||
}
|
||||
|
||||
Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, headers);
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
Properties messagesBundle;
|
||||
try {
|
||||
messagesBundle = theme.getMessages(locale);
|
||||
|
@ -213,10 +211,6 @@ public class FreeMarkerAccountProvider implements AccountProvider {
|
|||
String result = freeMarker.processTemplate(attributes, Templates.getTemplate(page), theme);
|
||||
Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML).entity(result);
|
||||
BrowserSecurityHeaderSetup.headers(builder, realm);
|
||||
|
||||
String keycloakLocaleCookiePath = Urls.localeCookiePath(baseUri, realm.getName());
|
||||
|
||||
LocaleHelper.updateLocaleCookie(builder, locale, realm, uriInfo, keycloakLocaleCookiePath);
|
||||
return builder.build();
|
||||
} catch (FreeMarkerException e) {
|
||||
logger.error("Failed to process template", e);
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.services.offline.OfflineTokenUtils;
|
||||
import org.keycloak.services.managers.UserSessionManager;
|
||||
import org.keycloak.util.MultivaluedHashMap;
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ public class ApplicationsBean {
|
|||
|
||||
public ApplicationsBean(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
|
||||
Set<ClientModel> offlineClients = OfflineTokenUtils.findClientsWithOfflineToken(session, realm, user);
|
||||
Set<ClientModel> offlineClients = new UserSessionManager(session).findClientsWithOfflineToken(realm, user);
|
||||
|
||||
List<ClientModel> realmClients = realm.getClients();
|
||||
for (ClientModel client : realmClients) {
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
|
||||
* as indicated by the @author tags. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.keycloak.freemarker;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.NewCookie;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
|
||||
*/
|
||||
public class LocaleHelper {
|
||||
public final static String LOCALE_COOKIE = "KEYCLOAK_LOCALE";
|
||||
public static final String UI_LOCALES_PARAM = "ui_locales";
|
||||
public static final String KC_LOCALE_PARAM = "kc_locale";
|
||||
|
||||
public static Locale getLocale(RealmModel realm, UserModel user) {
|
||||
return getLocale(realm, user, null, null);
|
||||
}
|
||||
|
||||
public static Locale getLocale(RealmModel realm, UserModel user, UriInfo uriInfo, HttpHeaders httpHeaders) {
|
||||
if(!realm.isInternationalizationEnabled()){
|
||||
return Locale.ENGLISH;
|
||||
}
|
||||
|
||||
//0. kc_locale query parameter
|
||||
if(uriInfo != null && uriInfo.getQueryParameters().containsKey(KC_LOCALE_PARAM)){
|
||||
String localeString = uriInfo.getQueryParameters().getFirst(KC_LOCALE_PARAM);
|
||||
Locale locale = findLocale(realm.getSupportedLocales(), localeString);
|
||||
if(locale != null){
|
||||
if(user != null){
|
||||
user.setSingleAttribute(UserModel.LOCALE, locale.toLanguageTag());
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
//1. Locale cookie
|
||||
if(httpHeaders != null && httpHeaders.getCookies().containsKey(LOCALE_COOKIE)){
|
||||
String localeString = httpHeaders.getCookies().get(LOCALE_COOKIE).getValue();
|
||||
Locale locale = findLocale(realm.getSupportedLocales(), localeString);
|
||||
if(locale != null){
|
||||
if(user != null && user.getFirstAttribute(UserModel.LOCALE) == null){
|
||||
user.setSingleAttribute(UserModel.LOCALE, locale.toLanguageTag());
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
//2. User profile
|
||||
if(user != null && user.getAttributes().containsKey(UserModel.LOCALE)){
|
||||
String localeString = user.getFirstAttribute(UserModel.LOCALE);
|
||||
Locale locale = findLocale(realm.getSupportedLocales(), localeString);
|
||||
if(locale != null){
|
||||
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
//3. ui_locales query parameter
|
||||
if(uriInfo != null && uriInfo.getQueryParameters().containsKey(UI_LOCALES_PARAM)){
|
||||
String localeString = uriInfo.getQueryParameters().getFirst(UI_LOCALES_PARAM);
|
||||
Locale locale = findLocale(realm.getSupportedLocales(), localeString.split(" "));
|
||||
if(locale != null){
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
//4. Accept-Language http header
|
||||
if(httpHeaders !=null && httpHeaders.getAcceptableLanguages() != null && !httpHeaders.getAcceptableLanguages().isEmpty()){
|
||||
for(Locale l : httpHeaders.getAcceptableLanguages()){
|
||||
String localeString = l.toLanguageTag();
|
||||
Locale locale = findLocale(realm.getSupportedLocales(), localeString);
|
||||
if(locale != null){
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//5. Default realm locale
|
||||
if(realm.getDefaultLocale() != null){
|
||||
return Locale.forLanguageTag(realm.getDefaultLocale());
|
||||
}
|
||||
|
||||
return Locale.ENGLISH;
|
||||
}
|
||||
|
||||
public static void updateLocaleCookie(Response.ResponseBuilder builder,
|
||||
Locale locale,
|
||||
RealmModel realm,
|
||||
UriInfo uriInfo,
|
||||
String path) {
|
||||
if (locale == null) {
|
||||
return;
|
||||
}
|
||||
boolean secure = realm.getSslRequired().isRequired(uriInfo.getRequestUri().getHost());
|
||||
builder.cookie(new NewCookie(LocaleHelper.LOCALE_COOKIE, locale.toLanguageTag(), path, null, null, 31536000, secure));
|
||||
}
|
||||
|
||||
public static Locale findLocale(Set<String> supportedLocales, String ... localeStrings) {
|
||||
for(String localeString : localeStrings){
|
||||
Locale result = null;
|
||||
Locale search = Locale.forLanguageTag(localeString);
|
||||
for(String languageTag : supportedLocales) {
|
||||
Locale locale = Locale.forLanguageTag(languageTag);
|
||||
if(locale.getLanguage().equals(search.getLanguage())){
|
||||
if(locale.getCountry().equals("") && result == null){
|
||||
result = locale;
|
||||
}
|
||||
if(locale.getCountry().equals(search.getCountry())){
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(result != null){
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.freemarker.beans;
|
||||
|
||||
import org.keycloak.freemarker.LocaleHelper;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
@ -22,7 +21,7 @@ public class LocaleBean {
|
|||
supported = new LinkedList<>();
|
||||
for (String l : realm.getSupportedLocales()) {
|
||||
String label = messages.getProperty("locale_" + l, l);
|
||||
String url = uriBuilder.replaceQueryParam(LocaleHelper.KC_LOCALE_PARAM, l).build().toString();
|
||||
String url = uriBuilder.replaceQueryParam("kc_locale", l).build().toString();
|
||||
supported.add(new Locale(label, url));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package org.keycloak.freemarke;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.freemarker.LocaleHelper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
|
||||
*/
|
||||
public class LocaleHelperTest {
|
||||
@Test
|
||||
public void findLocaleTest(){
|
||||
Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de").toLanguageTag());
|
||||
Assert.assertEquals("en", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "en").toLanguageTag());
|
||||
Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de-CH").toLanguageTag());
|
||||
Assert.assertEquals("de-CH", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de-CH").toLanguageTag());
|
||||
Assert.assertEquals("de-DE", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de-DE").toLanguageTag());
|
||||
Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de").toLanguageTag());
|
||||
Assert.assertNull(LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de-CH","de-DE")), "de"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findLocalesTest(){
|
||||
Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de en".split(" ")).toLanguageTag());
|
||||
Assert.assertEquals("en", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "en de".split(" ")).toLanguageTag());
|
||||
Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de-CH en".split(" ")).toLanguageTag());
|
||||
Assert.assertEquals("de-CH", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de-CH de".split(" ")).toLanguageTag());
|
||||
Assert.assertEquals("de-DE", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de-DE de-CH de".split(" ")).toLanguageTag());
|
||||
Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "en fr de".split(" ")).toLanguageTag());
|
||||
Assert.assertNull(LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de-CH","de-DE")), "de en fr".split(" ")));
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
# TIPS to encode UTF-8 to ISO
|
||||
# TIPS to encode UTF-8 to ISO
|
||||
# native2ascii -encoding ISO8859_1 srcFile > dstFile
|
||||
|
||||
doSave=Sauvegarder
|
||||
|
@ -45,19 +45,19 @@ role_view-users=Voir les utilisateurs
|
|||
role_view-applications=Voir les applications
|
||||
role_view-clients=Voir les clients
|
||||
role_view-events=Voir les \u00e9v\u00e9nements
|
||||
role_view-identity-providers=Voir les fournisseurs d'identit\u00e9s
|
||||
role_view-identity-providers=Voir les fournisseurs d''identit\u00e9s
|
||||
role_manage-realm=G\u00e9rer le domaine
|
||||
role_manage-users=G\u00e9rer les utilisateurs
|
||||
role_manage-applications=G\u00e9rer les applications
|
||||
role_manage-identity-providers=G\u00e9rer les fournisseurs d'identit\u00e9s
|
||||
role_manage-identity-providers=G\u00e9rer les fournisseurs d''identit\u00e9s
|
||||
role_manage-clients=G\u00e9rer les clients
|
||||
role_manage-events=G\u00e9rer les \u00e9v\u00e9nements
|
||||
role_view-profile=Voir le profile
|
||||
role_manage-account=G\u00e9rer le compte
|
||||
role_read-token=Lire le jeton d'authentification
|
||||
role_read-token=Lire le jeton d''authentification
|
||||
role_offline-access=Acc\u00e9s hors-ligne
|
||||
client_account=Compte
|
||||
client_security-admin-console=Console d'administration de la s\u00e9curit\u00e9
|
||||
client_security-admin-console=Console d''administration de la s\u00e9curit\u00e9
|
||||
client_realm-management=Gestion du domaine
|
||||
client_broker=Broker
|
||||
|
||||
|
@ -65,7 +65,7 @@ client_broker=Broker
|
|||
requiredFields=Champs obligatoires
|
||||
allFieldsRequired=Tous les champs obligatoires
|
||||
|
||||
backToApplication=« Revenir \u00e0 l'application
|
||||
backToApplication=« Revenir \u00e0 l''application
|
||||
backTo=Revenir \u00e0 {0}
|
||||
|
||||
date=Date
|
||||
|
@ -86,68 +86,68 @@ sessions=Sessions
|
|||
log=Connexion
|
||||
|
||||
application=Application
|
||||
availablePermissions=Permissions Disponibles
|
||||
availablePermissions=Permissions Disponibles
|
||||
grantedPermissions=Permissions accord\u00e9es
|
||||
grantedPersonalInfo=Informations personnels accord\u00e9es
|
||||
additionalGrants=Droits additionnels
|
||||
action=Action
|
||||
inResource=dans
|
||||
fullAccess=Acc\u00e9s complet
|
||||
offlineToken=Jeton d'authentification hors-ligne
|
||||
offlineToken=Jeton d''authentification hors-ligne
|
||||
revoke=R\u00e9voquer un droit
|
||||
|
||||
configureAuthenticators=Authentifications configur\u00e9es.
|
||||
mobile=T\u00e9l\u00e9phone mobile
|
||||
totpStep1=Installlez <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou bien Google Authenticator sur votre mobile. Ces deux applications sont disponibles sur <a href="https://play.google.com">Google Play</a> et Apple App Store.
|
||||
totpStep2=Ouvrez l'application et scanner le code bar ou entrez la clef.
|
||||
totpStep3=Entrez le code \u00e0 usage unique fourni par l'application et cliquez sur Sauvegarder pour terminer.
|
||||
totpStep1=Installez <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou bien Google Authenticator sur votre mobile. Ces deux applications sont disponibles sur <a href="https://play.google.com">Google Play</a> et Apple App Store.
|
||||
totpStep2=Ouvrez l''application et scanner le code bar ou entrez la clef.
|
||||
totpStep3=Entrez le code \u00e0 usage unique fourni par l''application et cliquez sur Sauvegarder pour terminer.
|
||||
|
||||
missingUsernameMessage=Veuillez entrez votre nom d'utilisateur.
|
||||
missingFirstNameMessage=Veuillez entrez votre pr\u00e9nom.
|
||||
missingUsernameMessage=Veuillez entrer votre nom d''utilisateur.
|
||||
missingFirstNameMessage=Veuillez entrer votre pr\u00e9nom.
|
||||
invalidEmailMessage=Courriel invalide.
|
||||
missingLastNameMessage=Veuillez entrez votre nom.
|
||||
missingEmailMessage=Veuillez entrez votre courriel.
|
||||
missingPasswordMessage=Veuillez entrez votre mot de passe.
|
||||
missingLastNameMessage=Veuillez entrer votre nom.
|
||||
missingEmailMessage=Veuillez entrer votre courriel.
|
||||
missingPasswordMessage=Veuillez entrer votre mot de passe.
|
||||
notMatchPasswordMessage=Les mots de passe ne sont pas identiques
|
||||
|
||||
missingTotpMessage=Veuillez entrez le code authentification.
|
||||
missingTotpMessage=Veuillez entrer le code authentification.
|
||||
invalidPasswordExistingMessage=Mot de passe existant invalide.
|
||||
invalidPasswordConfirmMessage=Le mot de passe de confirmation ne correspond pas.
|
||||
invalidTotpMessage=Le code d'authentification est invalide.
|
||||
invalidTotpMessage=Le code d''authentification est invalide.
|
||||
|
||||
usernameExistsMessage=Le nom d'utilisateur existe d\u00e9j\u00e0.
|
||||
usernameExistsMessage=Le nom d''utilisateur existe d\u00e9j\u00e0.
|
||||
emailExistsMessage=Le courriel existe d\u00e9j\u00e0.
|
||||
|
||||
readOnlyUserMessage=Vous ne pouvez pas mettre \u00e0 jour votre compte car il est en lecture seule.
|
||||
readOnlyPasswordMessage=Vous ne pouvez pas mettre \u00e0 jour votre mot de passe car votre compte est en lecture seule.
|
||||
|
||||
successTotpMessage=L'authentification via t\u00e9l\u00e9phone mobile est configur\u00e9e.
|
||||
successTotpRemovedMessage=L'authentification via t\u00e9l\u00e9phone mobile est supprim\u00e9e.
|
||||
successTotpMessage=L''authentification via t\u00e9l\u00e9phone mobile est configur\u00e9e.
|
||||
successTotpRemovedMessage=L''authentification via t\u00e9l\u00e9phone mobile est supprim\u00e9e.
|
||||
|
||||
successGrantRevokedMessage=Droit r\u00e9voqu\u00e9 avec succ\u00e8s.
|
||||
|
||||
accountUpdatedMessage=Votre compte a \u00e9t\u00e9 mis \u00e0 jour.
|
||||
accountPasswordUpdatedMessage=Votre mot de passe a \u00e9t\u00e9 mis \u00e0 jour.
|
||||
|
||||
missingIdentityProviderMessage=Le fournisseur d'identit\u00e9 n'est pas sp\u00e9cifi\u00e9.
|
||||
missingIdentityProviderMessage=Le fournisseur d''identit\u00e9 n''est pas sp\u00e9cifi\u00e9.
|
||||
invalidFederatedIdentityActionMessage=Action manquante ou invalide.
|
||||
identityProviderNotFoundMessage=Le fournisseur d'identit\u00e9 sp\u00e9cifi\u00e9 n'est pas trouv\u00e9.
|
||||
federatedIdentityLinkNotActiveMessage=Cette identit\u00e9 n'est plus active dor\u00e9navant.
|
||||
federatedIdentityRemovingLastProviderMessage=Vous ne pouvez pas supprimer votre derni\u00e8re f\u00e9d\u00e9ration d'identit\u00e9 sans avoir de mot de passe sp\u00e9cifi\u00e9.
|
||||
identityProviderRedirectErrorMessage=Erreur de redirection vers le fournisseur d'identit\u00e9.
|
||||
identityProviderRemovedMessage=Le fournisseur d'identit\u00e9 a \u00e9t\u00e9 supprim\u00e9 correctement.
|
||||
identityProviderNotFoundMessage=Le fournisseur d''identit\u00e9 sp\u00e9cifi\u00e9 n''est pas trouv\u00e9.
|
||||
federatedIdentityLinkNotActiveMessage=Cette identit\u00e9 n''est plus active dor\u00e9navant.
|
||||
federatedIdentityRemovingLastProviderMessage=Vous ne pouvez pas supprimer votre derni\u00e8re f\u00e9d\u00e9ration d''identit\u00e9 sans avoir de mot de passe sp\u00e9cifi\u00e9.
|
||||
identityProviderRedirectErrorMessage=Erreur de redirection vers le fournisseur d''identit\u00e9.
|
||||
identityProviderRemovedMessage=Le fournisseur d''identit\u00e9 a \u00e9t\u00e9 supprim\u00e9 correctement.
|
||||
|
||||
accountDisabledMessage=Ce compte est d\u00e9sactiv\u00e9, veuillez contacter votre administrateur.
|
||||
|
||||
accountTemporarilyDisabledMessage=Ce compte est temporairement d\u00e9sactiv\u00e9, veuillez contacter votre administrateur ou r\u00e9essayez plus tard..
|
||||
invalidPasswordMinLengthMessage=Mot de passe invalide: longueur minimale {0}.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.
|
||||
invalidPasswordMinDigitsMessage=Mot de passe invalide: doit contenir au moins {0} chiffre(s).
|
||||
invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en majuscule.
|
||||
invalidPasswordMinSpecialCharsMessage=Mot de passe invalide: doit contenir au moins {0} caractère(s) spéciaux.
|
||||
invalidPasswordNotUsernameMessage=Mot de passe invalide: ne doit pas être identique au nom d'utilisateur.
|
||||
invalidPasswordRegexPatternMessage=Mot de passe invalide: ne valide pas l'expression rationnelle.
|
||||
invalidPasswordHistoryMessage=Mot de passe invalide: ne doit pas être égal aux {0} derniers mot de passe.
|
||||
invalidPasswordMinLengthMessage=Mot de passe invalide : longueur minimale {0}.
|
||||
invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en minuscule.
|
||||
invalidPasswordMinDigitsMessage=Mot de passe invalide : doit contenir au moins {0} chiffre(s).
|
||||
invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en majuscule.
|
||||
invalidPasswordMinSpecialCharsMessage=Mot de passe invalide : doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
|
||||
invalidPasswordNotUsernameMessage=Mot de passe invalide : ne doit pas etre identique au nom d''utilisateur.
|
||||
invalidPasswordRegexPatternMessage=Mot de passe invalide : ne valide pas l''expression rationnelle.
|
||||
invalidPasswordHistoryMessage=Mot de passe invalide : ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
|
||||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
|
|
|
@ -160,7 +160,7 @@ select-file=de Select file
|
|||
view-details=de View details
|
||||
clear-import=de Clear import
|
||||
client-id.tooltip=de Specifies ID referenced in URI and tokens. For example 'my-client'
|
||||
client.name.tooltip=de Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example: ${my_client}
|
||||
client.name.tooltip=de Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example\: ${my_client}
|
||||
client.enabled.tooltip=de Disabled clients cannot initiate a login or have obtain access tokens.
|
||||
consent-required=de Consent Required
|
||||
consent-required.tooltip=de If enabled users have to consent to client access.
|
||||
|
@ -460,3 +460,4 @@ realm=de Realm
|
|||
identity-provider-mappers=de Identity Provider Mappers
|
||||
create-identity-provider-mapper=de Create Identity Provider Mapper
|
||||
add-identity-provider-mapper=de Add Identity Provider Mapper
|
||||
client.description.tooltip=de Specifies description of the client. For example 'My Client for TimeSheets'. Supports keys for localized values as well. For example\: ${my_client_description}
|
||||
|
|
|
@ -66,6 +66,8 @@ realm-cache-enabled=Realm Cache Enabled
|
|||
realm-cache-enabled.tooltip=Enable/disable cache for realm, client and role data.
|
||||
user-cache-enabled=User Cache Enabled
|
||||
user-cache-enabled.tooltip=Enable/disable user and user role mapping cache.
|
||||
revoke-refresh-token=Revoke Refresh Token
|
||||
revoke-refresh-token.tooltip=If enabled refresh tokens can only be used once. Otherwise refresh tokens are not revoked when used and can be used multiple times.
|
||||
sso-session-idle=SSO Session Idle
|
||||
seconds=Seconds
|
||||
minutes=Minutes
|
||||
|
@ -160,7 +162,7 @@ select-file=Select file
|
|||
view-details=View details
|
||||
clear-import=Clear import
|
||||
client-id.tooltip=Specifies ID referenced in URI and tokens. For example 'my-client'
|
||||
client.name.tooltip=Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example: ${my_client}
|
||||
client.name.tooltip=Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example\: ${my_client}
|
||||
client.enabled.tooltip=Disabled clients cannot initiate a login or have obtain access tokens.
|
||||
consent-required=Consent Required
|
||||
consent-required.tooltip=If enabled users have to consent to client access.
|
||||
|
@ -458,3 +460,4 @@ realm=Realm
|
|||
identity-provider-mappers=Identity Provider Mappers
|
||||
create-identity-provider-mapper=Create Identity Provider Mapper
|
||||
add-identity-provider-mapper=Add Identity Provider Mapper
|
||||
client.description.tooltip=Specifies description of the client. For example 'My Client for TimeSheets'. Supports keys for localized values as well. For example\: ${my_client_description}
|
||||
|
|
|
@ -2030,16 +2030,11 @@ module.filter('capitalize', function() {
|
|||
if (!input) {
|
||||
return;
|
||||
}
|
||||
var result = input.substring(0, 1).toUpperCase();
|
||||
var s = input.substring(1);
|
||||
for (var i=0; i<s.length ; i++) {
|
||||
var c = s[i];
|
||||
if (c.match(/[A-Z]/)) {
|
||||
result = result.concat(" ")
|
||||
};
|
||||
result = result.concat(c);
|
||||
var splittedWords = input.split(/\s+/);
|
||||
for (var i=0; i<splittedWords.length ; i++) {
|
||||
splittedWords[i] = splittedWords[i].charAt(0).toUpperCase() + splittedWords[i].slice(1);
|
||||
};
|
||||
return result;
|
||||
return splittedWords.join(" ");
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -38,6 +38,13 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'client.name.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="description">{{:: 'description' | translate}} </label>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" type="text" id="description" name="description" data-ng-model="client.description">
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'client.description.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix block">
|
||||
<label class="col-md-2 control-label" for="enabled">{{:: 'enabled' | translate}}</label>
|
||||
<div class="col-sm-6">
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="alias">Alias </label>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" type="text" id="alias" name="alias" data-ng-model="flow.alias" autofocus>
|
||||
<input class="form-control" type="text" id="alias" name="alias" data-ng-model="flow.alias" autofocus required>
|
||||
</div>
|
||||
<kc-tooltip>Specifies display name for the flow.</kc-tooltip>
|
||||
</div>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<input ui-select2="eventSelectOptions" id="enabledEventTypes" ng-model="eventsConfig.enabledEventTypes" data-placeholder="Select event types..."/>
|
||||
</div>
|
||||
|
||||
<span tooltip-trigger="mouseover mouseout" tooltip-placement="right" tooltip="Configure what event types are saved. By default events related to login and users modifying their accounts are persisted." class="fa fa-info-circle"></span>
|
||||
<span tooltip-trigger="mouseover mouseout" tooltip-placement="right" tooltip="Configure what event types are saved." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-ng-show="access.manageEvents && eventsConfig.eventsEnabled">
|
||||
|
|
|
@ -3,6 +3,17 @@
|
|||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="revokeRefreshToken">{{:: 'revoke-refresh-token' | translate}}</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<input ng-model="realm.revokeRefreshToken" name="revokeRefreshToken" id="revokeRefreshToken" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
|
||||
</div>
|
||||
|
||||
<kc-tooltip>{{:: 'revoke-refresh-token.tooltip' | translate}}
|
||||
</kc-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="ssoSessionIdleTimeout">{{:: 'sso-session-idle' | translate}}</label>
|
||||
|
||||
|
|
|
@ -196,4 +196,5 @@ emailVerifiedMessage=Ihr E-Mail Adresse wurde erfolgreich verifiziert.
|
|||
locale_de=Deutsch
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
|
|
|
@ -196,6 +196,7 @@ locale_de=German
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
|
||||
backToApplication=« Back to Application
|
||||
missingParameterMessage=Missing parameters\: {0}
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
doLogIn=Connexion
|
||||
doRegister=Enregistrement
|
||||
doCancel=Annuler
|
||||
doSubmit=Soumettre
|
||||
doYes=Oui
|
||||
doNo=Non
|
||||
doContinue=Continuer
|
||||
doAccept=Accepter
|
||||
doDecline=Decliner
|
||||
doForgotPassword=Mot de passe oublié ?
|
||||
doClickHere=Cliquez ici
|
||||
doImpersonate=Impersonate
|
||||
kerberosNotConfigured=Kerberos non configuré
|
||||
kerberosNotConfiguredTitle=Kerberos non configuré
|
||||
bypassKerberosDetail=Si vous n''êtes pas connecté via Kerberos ou bie que votre navigateur n''est opas configurer pour la connexion via Kerberos. Veuillez cliquer pour vous connecter via un autre moyen.
|
||||
kerberosNotSetUp=Kerberos n''est pas configuré. Connexion impossible.
|
||||
registerWithTitle=Enregistrement avec {0}
|
||||
registerWithTitleHtml=Enregistrement avec<strong>{0}</strong>
|
||||
loginTitle=Connecté {0}
|
||||
loginTitleHtml=Connecté <strong>{0}</strong>
|
||||
impersonateTitle={0} utilisateur impersonate
|
||||
impersonateTitleHtml=<strong>{0}</strong> utilisateur impersonate</strong>
|
||||
realmChoice=Domaine
|
||||
unknownUser=Utilisateur inconnu
|
||||
loginTotpTitle=Configuration de l''authentification par mobile
|
||||
loginProfileTitle=Mise à jour du compte
|
||||
loginTimeout=Le temps imparti pour la connexion est écoulé. Le processus de connexion redémarre depuis le debut.
|
||||
oauthGrantTitle=OAuth Grant
|
||||
oauthGrantTitleHtml=Accès temporaire pour <strong>{0}</strong> demandé par
|
||||
errorTitle=Nous sommes désolé ...
|
||||
errorTitleHtml=Nous sommes <strong>désolé</strong> ...
|
||||
emailVerifyTitle=Vérification du courriel
|
||||
emailForgotTitle=Mot de passe oublié ?
|
||||
updatePasswordTitle=Mise à jour du mot de passe
|
||||
codeSuccessTitle=Code succès
|
||||
codeErrorTitle=Code Erreur\: {0}
|
||||
|
||||
termsTitle=Termes et Conditions
|
||||
termsTitleHtml=Termes et Conditions
|
||||
termsText=<p>Termes et conditions à définir</p>
|
||||
|
||||
recaptchaFailed=Re-captcha invalide
|
||||
recaptchaNotConfigured=Re-captcha est requis, mais il n''est pas configuré
|
||||
consentDenied=Consentement refusé.
|
||||
|
||||
noAccount=Nouvel utilisateur?
|
||||
username=Nom d''utilisateur
|
||||
usernameOrEmail=Nom d''utilisateur ou courriel
|
||||
firstName=Prénom
|
||||
givenName=Prénom
|
||||
fullName=Nom complet
|
||||
lastName=Nom
|
||||
familyName=Nom de famille
|
||||
email=Courriel
|
||||
password=Mot de passe
|
||||
passwordConfirm=Confirmation du mot de passe
|
||||
passwordNew=Nouveau mot de passe
|
||||
passwordNewConfirm=Confirmation du nouveau not de passe
|
||||
rememberMe=Se souvenir de moi
|
||||
authenticatorCode=Code à usage unique
|
||||
address=Adresse
|
||||
street=Rue
|
||||
locality=Ville ou Localité
|
||||
region=État, Province, ou Région
|
||||
postal_code=Code postal
|
||||
country=Pays
|
||||
emailVerified=Courriel vérifié
|
||||
gssDelegationCredential=GSS Delegation Credential
|
||||
|
||||
loginTotpStep1=Installez <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou bien Google Authenticator sur votre mobile. Ces deux applications sont disponibles sur <a href="https://play.google.com">Google Play</a> et Apple App Store.
|
||||
loginTotpStep2=Ouvrez l''application et scanner le code bar ou entrez la clef.
|
||||
loginTotpStep3=Entrez le code \u00e0 usage unique fourni par l''application et cliquez sur Sauvegarder pour terminer.
|
||||
loginTotpOneTime=Code à usage unique
|
||||
|
||||
oauthGrantRequest=Voulez-vous accorder ces privileges d''accès ?
|
||||
inResource=dans
|
||||
|
||||
emailVerifyInstruction1=Un courriel avec des instructions à suivre vous a été envoyé.
|
||||
emailVerifyInstruction2=Vous n''avez pas reçu de code dans le courriel ?
|
||||
emailVerifyInstruction3=Pour renvoyer le courriel.
|
||||
|
||||
backToLogin=« Retour à la connexion
|
||||
|
||||
emailInstruction=Entrez votre nom d''utilisateur ou votre courriel, un email va vous \u00eatre envoyer vous permettant de créer un nouveau mot de passe.
|
||||
|
||||
copyCodeInstruction=Copiez le code et recopiez le dans votre application :
|
||||
|
||||
personalInfo=Information personnelle:
|
||||
role_admin=Adminitrateur
|
||||
role_realm-admin=Administrateur du domaine
|
||||
role_create-realm=Cr\u00e9er un domaine
|
||||
role_view-realm=Voir un domaine
|
||||
role_view-users=Voir les utilisateurs
|
||||
role_view-applications=Voir les applications
|
||||
role_view-clients=Voir les clients
|
||||
role_view-events=Voir les \u00e9v\u00e9nements
|
||||
role_view-identity-providers=Voir les fournisseurs d''identit\u00e9s
|
||||
role_manage-realm=G\u00e9rer le domaine
|
||||
role_manage-users=G\u00e9rer les utilisateurs
|
||||
role_manage-applications=G\u00e9rer les applications
|
||||
role_manage-identity-providers=G\u00e9rer les fournisseurs d''identit\u00e9s
|
||||
role_manage-clients=G\u00e9rer les clients
|
||||
role_manage-events=G\u00e9rer les \u00e9v\u00e9nements
|
||||
role_view-profile=Voir le profile
|
||||
role_manage-account=G\u00e9rer le compte
|
||||
role_read-token=Lire le jeton d''authentification
|
||||
role_offline-access=Acc\u00e9s hors-ligne
|
||||
client_account=Compte
|
||||
client_security-admin-console=Console d''administration de la s\u00e9curit\u00e9
|
||||
client_realm-management=Gestion du domaine
|
||||
client_broker=Broker
|
||||
|
||||
invalidUserMessage=Nom d''utilisateur ou mot de passe invalide.
|
||||
invalidEmailMessage=Adresse courriel invalide.
|
||||
accountDisabledMessage=Compte désactivé, contactez votre administrateur.
|
||||
accountTemporarilyDisabledMessage=Ce compte est temporairement désactivé, contactez votre administrateur ou bien réassayer plus tard.
|
||||
expiredCodeMessage=Fin de connexion. Veuillez vous reconnecter.
|
||||
|
||||
missingFirstNameMessage=Veuillez entrer votre pr\u00e9nom.
|
||||
missingLastNameMessage=Veuillez entrer votre nom.
|
||||
missingEmailMessage=Veuillez entrer votre courriel.
|
||||
missingUsernameMessage=Veuillez entrer votre nom d''utilisateur.
|
||||
missingPasswordMessage=Veuillez entrer votre mot de passe.
|
||||
missingTotpMessage=Veuillez entrer votre code d''authentification.
|
||||
notMatchPasswordMessage=Les mots de passe ne sont pas identiques.
|
||||
|
||||
invalidPasswordExistingMessage=Mot de passe existant invalide.
|
||||
invalidPasswordConfirmMessage=Le mot de passe de confirmation ne correspond pas.
|
||||
invalidTotpMessage=Le code d''authentification est invalide.
|
||||
|
||||
usernameExistsMessage=Le nom d''utilisateur existe d\u00e9j\u00e0.
|
||||
emailExistsMessage=Le courriel existe d\u00e9j\u00e0.
|
||||
|
||||
federatedIdentityEmailExistsMessage=Cet utilisateur avec ce courriel existe déjà. Veuillez vous connecté au gestionnaire de compte pour lier le compte.
|
||||
federatedIdentityUsernameExistsMessage=Cet utilisateur avec ce nom d''utilisateur existe déjà. Veuillez vous connecté au gestionnaire de compte pour lier le compte.
|
||||
|
||||
configureTotpMessage=Vous devez configurer l''authentification par mobile pour activer votre compte.
|
||||
updateProfileMessage=Vous devez mettre à jour votre profile pour activer votre compte.
|
||||
updatePasswordMessage=Vous devez changer votre mot de passe pour activer votre compte.
|
||||
verifyEmailMessage=Vous devez vérifier votre courriel pour activer votre compte.
|
||||
|
||||
emailSentMessage=Vous devriez recevoir rapidement un courriel avec de plus ample instructions.
|
||||
emailSendErrorMessage=Erreur lors de l''envoie du courriel, veuillez essayer plus tard.
|
||||
|
||||
accountUpdatedMessage=Votre compte a été mis à jour.
|
||||
accountPasswordUpdatedMessage=Votre mot de passe a été mis à jour.
|
||||
|
||||
noAccessMessage=Aucun accès
|
||||
|
||||
invalidPasswordMinLengthMessage=Mot de passe invalide : longueur minimale {0}.
|
||||
invalidPasswordMinDigitsMessage=Mot de passe invalide : doit contenir au moins {0} chiffre(s).
|
||||
invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en minuscule.
|
||||
invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en majuscule.
|
||||
invalidPasswordMinSpecialCharsMessage=Mot de passe invalide : doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
|
||||
invalidPasswordNotUsernameMessage=Mot de passe invalide : ne doit pas etre identique au nom d''utilisateur.
|
||||
invalidPasswordRegexPatternMessage=Mot de passe invalide : ne valide pas l''expression rationnelle.
|
||||
invalidPasswordHistoryMessage=Mot de passe invalide : ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
|
||||
|
||||
|
||||
failedToProcessResponseMessage=Erreur lors du traitement de la réponse
|
||||
httpsRequiredMessage=Le protocole HTTPS est requis
|
||||
realmNotEnabledMessage=Le domaine n''est pas activé
|
||||
invalidRequestMessage=Requete invalide
|
||||
failedLogout=La déconnexion a échouée
|
||||
unknownLoginRequesterMessage=Compte inconnu du demandeur
|
||||
loginRequesterNotEnabledMessage=La connexion du demandeur n''est pas active
|
||||
bearerOnlyMessage=Les applications Bearer-only ne sont pas autorisées à initier la connexion par navigateur.
|
||||
directGrantsOnlyMessage=Les clients Direct-grants-only ne sont pas autorisés à initier la connexion par navigateur.
|
||||
invalidRedirectUriMessage=L''uri de redirection est invalide
|
||||
unsupportedNameIdFormatMessage=NameIDFormat non supporté
|
||||
invlidRequesterMessage=Demandeur invalide
|
||||
registrationNotAllowedMessage=L''enregistrement n''est pas autorisé
|
||||
resetCredentialNotAllowedMessage=La remise \u00e0 z\u00e9ro n''est pas autorisé
|
||||
|
||||
permissionNotApprovedMessage=La permission n''est pas approuvée.
|
||||
noRelayStateInResponseMessage=Aucun état de relais dans la réponse du fournisseur d''identité.
|
||||
identityProviderAlreadyLinkedMessage=L''identité retournée par le fournisseur d''identité est déjà liée avec un autre utilisateur.
|
||||
insufficientPermissionMessage=Permissions insuffisantes pour lier les identités.
|
||||
couldNotProceedWithAuthenticationRequestMessage=Impossible de continuer avec la requête d''authentification vers le fournisseur d''identité.
|
||||
couldNotObtainTokenMessage=Impossible de récuperer le jeton du fournisseur d''identité.
|
||||
unexpectedErrorRetrievingTokenMessage=Erreur inattendue lors de la récupération du jeton provenant du fournisseur d''identité.
|
||||
unexpectedErrorHandlingResponseMessage=Erreur inattendue lors du traitement de la réponse provenant du fournisseur d''identité.
|
||||
identityProviderAuthenticationFailedMessage=L''authentification a échouée. Impossible de s''authentifier avec le fournisseur d''identité.
|
||||
couldNotSendAuthenticationRequestMessage=Impossible d''envoyer la requête d''authentification vers le fournisseur d''identité.
|
||||
unexpectedErrorHandlingRequestMessage=Erreur inattendue lors du traitement de la requête vers le fournisseur d''identité.
|
||||
invalidAccessCodeMessage=Code d''accès invalide.
|
||||
sessionNotActiveMessage=La session n''est pas active.
|
||||
invalidCodeMessage=Une erreur est survenue, veuillez vous reconnecter à votre application.
|
||||
identityProviderUnexpectedErrorMessage=Erreur inattendue lors de l''authentification avec fournisseur d''identité.
|
||||
identityProviderNotFoundMessage=Impossible de trouver le fournisseur d''identité avec ce identifiant.
|
||||
realmSupportsNoCredentialsMessage=Ce domaine ne supporte aucun type d''accréditation.
|
||||
identityProviderNotUniqueMessage=Ce domaine autorise plusieurs fournisseurs d''identité. Impossible de déterminer fournisseurs d''identité avec lequel s''authentifier.
|
||||
emailVerifiedMessage=Votre adresse courriel a été vérifiée.
|
||||
|
||||
locale_de=German
|
||||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
|
||||
|
||||
backToApplication=« Revenir à l''application
|
||||
missingParameterMessage=Paramètres manquants \: {0}
|
||||
clientNotFoundMessage=Client inconnu.
|
||||
invalidParameterMessage=Paramètres invalide \: {0}
|
|
@ -189,6 +189,7 @@ locale_de=German
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (Brasil)
|
||||
locale_fr=Français
|
||||
|
||||
backToApplication=« Torna all''Applicazione
|
||||
missingParameterMessage=Parametri Mancanti\: {0}
|
||||
|
|
|
@ -194,6 +194,7 @@ locale_de=Deutsch
|
|||
locale_en=English
|
||||
locale_it=Italian
|
||||
locale_pt-BR=Portugu\u00EAs (BR)
|
||||
locale_fr=Français
|
||||
|
||||
backToApplication=« Voltar para o aplicativo
|
||||
missingParameterMessage=Par\u00E2metros que faltam\: {0}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
emailVerificationSubject=V\u00a9rification du courriel
|
||||
emailVerificationBody=Quelqu''un vient de cr\u00a9er un compte "{2}" avec votre courriel. Si c''est bien vous, veuillez cliquer sur le lien ci-dessous afin de v\u00a9rifier votre adresse de courriel\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSinon ignorer ce message.
|
||||
emailVerificationBodyHtml=<p>Quelqu''un a cr\u00a9er un compte "{2}" avec votre courriel. Si c''est bien vous, veuillez cliquer sur le lien ci-dessous afin de v\u00a9rifier votre adresse de courriel</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Sinon veuillez ignorer ce message.</p>
|
||||
passwordResetSubject=R\u00a9initialiser le mot de passe
|
||||
passwordResetBody=Quelqu''un vient de demander une reinitialisation de mot de passe de votre compte {2}. Si c''est bien vous, veuillez cliquer sur le lien ci-dessous pour le mettre \u00a0 jour .\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSinon ignorer ce message, aucun changement ne sera effectuer sur votre compte.
|
||||
passwordResetBodyHtml=<p>Quelqu''un vient de demander une reinitialisation de mot de passe de votre compte {2}. Si c''est bien vous, veuillez cliquer sur le lien ci-dessous pour le mettre \u00a0 jour.</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Sinon ignorer ce message, aucun changement ne sera effectuer sur votre compte.</p>
|
||||
executeActionsSubject=Mettre \u00a0 jour votre compte
|
||||
executeActionsBody=Votre adminitrateurvient de demander une mise \u00a0 jour de votre compte {2}. veuillez cliquer sur le lien ci-dessous afin commencer le processus.\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSi vous n''etes pas au courant de cette requete, ignorer ce message, aucun changement ne sera effectuer sur votre compte.
|
||||
executeActionsBodyHtml=<p>Votre adminitrateurvient de demander une mise \u00a0 jour de votre compte {2}. veuillez cliquer sur le lien ci-dessous afin commencer le processus.</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Si vous n''etes pas au courant de cette requete, ignorer ce message, aucun changement ne sera effectuer sur votre compte.</p>
|
||||
eventLoginErrorSubject=Erreur de connexion
|
||||
eventLoginErrorBody=Une tentative de connexion a \u00a9t\u00a9 d\u00a9tect\u00a9e pour votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.
|
||||
eventLoginErrorBodyHtml=<p>Une tentative de connexion a \u00a9t\u00a9 d\u00a9tect\u00a9e pour votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.</p>
|
||||
eventRemoveTotpSubject=Suppression du TOTP
|
||||
eventRemoveTotpBody=Le TOTP a \u00a9t\u00a9 supprim\u00a9 de votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.
|
||||
eventRemoveTotpBodyHtml=<p>Le TOTP a \u00a9t\u00a9 supprim\u00a9 de votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.</p>
|
||||
eventUpdatePasswordSubject=Mise \u00a0 jour du mot de passe
|
||||
eventUpdatePasswordBody=Votre mot de passe \u00a0 chang\u00a9 sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.
|
||||
eventUpdatePasswordBodyHtml=<p>Votre mot de passe \u00a0 chang\u00a9 sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.</p>
|
||||
eventUpdateTotpSubject=Mise \u00a0 jour du TOTP
|
||||
eventUpdateTotpBody=Le TOTP a \u00a9t\u00a9 mis \u00a0 jour pour votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.
|
||||
eventUpdateTotpBodyHtml=<p>Le TOTP a \u00a9t\u00a9 mis \u00a0 jour pour votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.</p>
|
|
@ -29,6 +29,11 @@
|
|||
<artifactId>keycloak-model-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-events-api</artifactId>
|
||||
|
|
|
@ -20,11 +20,11 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailProvider;
|
||||
import org.keycloak.email.freemarker.beans.EventBean;
|
||||
import org.keycloak.email.freemarker.beans.ProfileBean;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.freemarker.FreeMarkerException;
|
||||
import org.keycloak.freemarker.FreeMarkerUtil;
|
||||
import org.keycloak.freemarker.LocaleHelper;
|
||||
import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.freemarker.ThemeProvider;
|
||||
import org.keycloak.freemarker.beans.MessageFormatterMethod;
|
||||
|
@ -64,6 +64,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
@Override
|
||||
public void sendEvent(Event event) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("event", new EventBean(event));
|
||||
|
||||
send(toCamelCase(event.getType()) + "Subject", "event-" + event.getType().toString().toLowerCase() + ".ftl", attributes);
|
||||
|
@ -72,6 +73,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
@Override
|
||||
public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
|
||||
|
@ -84,6 +86,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
@Override
|
||||
public void sendExecuteActions(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
|
||||
|
@ -97,6 +100,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
@Override
|
||||
public void sendVerifyEmail(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
|
||||
|
@ -110,7 +114,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
try {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
Theme theme = themeProvider.getTheme(realm.getEmailTheme(), Theme.Type.EMAIL);
|
||||
Locale locale = LocaleHelper.getLocale(realm, user);
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
attributes.put("locale", locale);
|
||||
Properties rb = theme.getMessages(locale);
|
||||
attributes.put("msg", new MessageFormatterMethod(locale, rb));
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* 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.email.freemarker.beans;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
* @author Vlastimil Elias (velias at redhat dot com)
|
||||
*/
|
||||
public class ProfileBean {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ProfileBean.class);
|
||||
|
||||
private UserModel user;
|
||||
private final Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
public ProfileBean(UserModel user) {
|
||||
this.user = user;
|
||||
|
||||
if (user.getAttributes() != null) {
|
||||
for (Map.Entry<String, List<String>> attr : user.getAttributes().entrySet()) {
|
||||
List<String> attrValue = attr.getValue();
|
||||
if (attrValue != null && attrValue.size() > 0) {
|
||||
attributes.put(attr.getKey(), attrValue.get(0));
|
||||
}
|
||||
|
||||
if (attrValue != null && attrValue.size() > 1) {
|
||||
logger.warnf("There are more values for attribute '%s' of user '%s' . Will display just first value", attr.getKey(), user.getUsername());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getUsername() { return user.getUsername(); }
|
||||
|
||||
public String getFirstName() {
|
||||
return user.getFirstName();
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return user.getLastName();
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return user.getEmail();
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ import org.keycloak.email.EmailProvider;
|
|||
import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
|
||||
import org.keycloak.freemarker.FreeMarkerException;
|
||||
import org.keycloak.freemarker.FreeMarkerUtil;
|
||||
import org.keycloak.freemarker.LocaleHelper;
|
||||
import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.freemarker.ThemeProvider;
|
||||
import org.keycloak.freemarker.beans.AdvancedMessageFormatterMethod;
|
||||
|
@ -201,7 +200,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
}
|
||||
|
||||
Properties messagesBundle;
|
||||
Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, session.getContext().getRequestHeaders());
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
try {
|
||||
messagesBundle = theme.getMessages(locale);
|
||||
attributes.put("msg", new MessageFormatterMethod(locale, messagesBundle));
|
||||
|
@ -292,10 +291,6 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
for (Map.Entry<String, String> entry : httpResponseHeaders.entrySet()) {
|
||||
builder.header(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
String cookiePath = Urls.localeCookiePath(baseUri, realm.getName());
|
||||
LocaleHelper.updateLocaleCookie(builder, locale, realm, uriInfo, cookiePath);
|
||||
|
||||
return builder.build();
|
||||
} catch (FreeMarkerException e) {
|
||||
logger.error("Failed to process template", e);
|
||||
|
@ -345,7 +340,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
}
|
||||
|
||||
Properties messagesBundle;
|
||||
Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, session.getContext().getRequestHeaders());
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
try {
|
||||
messagesBundle = theme.getMessages(locale);
|
||||
attributes.put("msg", new MessageFormatterMethod(locale, messagesBundle));
|
||||
|
@ -393,9 +388,6 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
for (Map.Entry<String, String> entry : httpResponseHeaders.entrySet()) {
|
||||
builder.header(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
String cookiePath = Urls.localeCookiePath(baseUri, realm.getName());
|
||||
LocaleHelper.updateLocaleCookie(builder, locale, realm, uriInfo, cookiePath);
|
||||
return builder.build();
|
||||
} catch (FreeMarkerException e) {
|
||||
logger.error("Failed to process template", e);
|
||||
|
|
|
@ -28,6 +28,10 @@ public interface ClientModel extends RoleContainerModel {
|
|||
|
||||
void setName(String name);
|
||||
|
||||
String getDescription();
|
||||
|
||||
void setDescription(String description);
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
void setEnabled(boolean enabled);
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.keycloak.models.utils.RealmImporter;
|
|||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -33,4 +34,6 @@ public interface KeycloakContext {
|
|||
|
||||
RealmImporter getRealmManager();
|
||||
|
||||
Locale resolveLocale(UserModel user);
|
||||
|
||||
}
|
||||
|
|
|
@ -91,12 +91,18 @@ public interface RealmModel extends RoleContainerModel {
|
|||
|
||||
void setResetPasswordAllowed(boolean resetPasswordAllowed);
|
||||
|
||||
boolean isRevokeRefreshToken();
|
||||
void setRevokeRefreshToken(boolean revokeRefreshToken);
|
||||
|
||||
int getSsoSessionIdleTimeout();
|
||||
void setSsoSessionIdleTimeout(int seconds);
|
||||
|
||||
int getSsoSessionMaxLifespan();
|
||||
void setSsoSessionMaxLifespan(int seconds);
|
||||
|
||||
// int getOfflineSessionIdleTimeout();
|
||||
// void setOfflineSessionIdleTimeout(int seconds);
|
||||
|
||||
int getAccessTokenLifespan();
|
||||
|
||||
void setAccessTokenLifespan(int seconds);
|
||||
|
@ -286,6 +292,10 @@ public interface RealmModel extends RoleContainerModel {
|
|||
|
||||
void setEventsEnabled(boolean enabled);
|
||||
|
||||
// boolean isPersistUserSessions();
|
||||
//
|
||||
// void setPersistUserSessions();
|
||||
|
||||
long getEventsExpiration();
|
||||
|
||||
void setEventsExpiration(long expiration);
|
||||
|
|
|
@ -475,70 +475,6 @@ public class UserFederationManager implements UserProvider {
|
|||
return (result != null) ? result : CredentialValidationOutput.failed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOfflineUserSession(RealmModel realm, UserModel user, OfflineUserSessionModel offlineUserSession) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
session.userStorage().addOfflineUserSession(realm, user, offlineUserSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
return session.userStorage().getOfflineUserSession(realm, user, userSessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
return session.userStorage().getOfflineUserSessions(realm, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
return session.userStorage().removeOfflineUserSession(realm, user, userSessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel offlineClientSession) {
|
||||
session.userStorage().addOfflineClientSession(realm, offlineClientSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
return session.userStorage().getOfflineClientSession(realm, user, clientSessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
return session.userStorage().getOfflineClientSessions(realm, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||
validateUser(realm, user);
|
||||
if (user == null) throw new IllegalStateException("Federated user no longer valid");
|
||||
return session.userStorage().removeOfflineClientSession(realm, user, clientSessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOfflineClientSessionsCount(RealmModel realm, ClientModel client) {
|
||||
return session.userStorage().getOfflineClientSessionsCount(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
|
||||
return session.userStorage().getOfflineClientSessions(realm, client, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -10,11 +9,11 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface UserModel {
|
||||
public static final String USERNAME = "username";
|
||||
public static final String LAST_NAME = "lastName";
|
||||
public static final String FIRST_NAME = "firstName";
|
||||
public static final String EMAIL = "email";
|
||||
public static final String LOCALE = "locale";
|
||||
String USERNAME = "username";
|
||||
String LAST_NAME = "lastName";
|
||||
String FIRST_NAME = "firstName";
|
||||
String EMAIL = "email";
|
||||
String LOCALE = "locale";
|
||||
|
||||
String getId();
|
||||
|
||||
|
|
|
@ -56,17 +56,5 @@ public interface UserProvider extends Provider {
|
|||
boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
|
||||
CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input);
|
||||
|
||||
void addOfflineUserSession(RealmModel realm, UserModel user, OfflineUserSessionModel offlineUserSession);
|
||||
OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel user, String userSessionId);
|
||||
Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user);
|
||||
boolean removeOfflineUserSession(RealmModel realm, UserModel user, String userSessionId);
|
||||
void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel offlineClientSession);
|
||||
OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId);
|
||||
Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user);
|
||||
boolean removeOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId);
|
||||
|
||||
int getOfflineClientSessionsCount(RealmModel realm, ClientModel client);
|
||||
Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int first, int max);
|
||||
|
||||
void close();
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.util.Map;
|
|||
public interface UserSessionModel {
|
||||
|
||||
String getId();
|
||||
RealmModel getRealm();
|
||||
|
||||
/**
|
||||
* If created via a broker external login, this is an identifier that can be
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.models;
|
|||
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -27,6 +28,8 @@ public interface UserSessionProvider extends Provider {
|
|||
int getActiveUserSessions(RealmModel realm, ClientModel client);
|
||||
void removeUserSession(RealmModel realm, UserSessionModel session);
|
||||
void removeUserSessions(RealmModel realm, UserModel user);
|
||||
|
||||
// Implementation should propagate removal of expired userSessions to userSessionPersister too
|
||||
void removeExpiredUserSessions(RealmModel realm);
|
||||
void removeUserSessions(RealmModel realm);
|
||||
void removeClientSession(RealmModel realm, ClientSessionModel clientSession);
|
||||
|
@ -40,6 +43,22 @@ public interface UserSessionProvider extends Provider {
|
|||
void onClientRemoved(RealmModel realm, ClientModel client);
|
||||
void onUserRemoved(RealmModel realm, UserModel user);
|
||||
|
||||
UserSessionModel createOfflineUserSession(UserSessionModel userSession);
|
||||
UserSessionModel getOfflineUserSession(RealmModel realm, String userSessionId);
|
||||
|
||||
// Removes the attached clientSessions as well
|
||||
void removeOfflineUserSession(RealmModel realm, String userSessionId);
|
||||
|
||||
ClientSessionModel createOfflineClientSession(ClientSessionModel clientSession);
|
||||
ClientSessionModel getOfflineClientSession(RealmModel realm, String clientSessionId);
|
||||
List<ClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user);
|
||||
|
||||
// Don't remove userSession even if it's last userSession
|
||||
void removeOfflineClientSession(RealmModel realm, String clientSessionId);
|
||||
|
||||
int getOfflineSessionsCount(RealmModel realm, ClientModel client);
|
||||
List<UserSessionModel> getOfflineUserSessions(RealmModel realm, ClientModel client, int first, int max);
|
||||
|
||||
void close();
|
||||
|
||||
}
|
||||
|
|
|
@ -7,4 +7,8 @@ import org.keycloak.provider.ProviderFactory;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface UserSessionProviderFactory extends ProviderFactory<UserSessionProvider> {
|
||||
|
||||
// This is supposed to prefill all userSessions and clientSessions from userSessionPersister to the userSession infinispan/memory storage
|
||||
void loadPersistentSessions(KeycloakSessionFactory sessionFactory, final int maxErrors, final int sessionsPerSegment);
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ public class ClientEntity extends AbstractIdentifiableEntity {
|
|||
|
||||
private String clientId;
|
||||
private String name;
|
||||
private String description;
|
||||
private String realmId;
|
||||
private boolean enabled;
|
||||
private String clientAuthenticatorType;
|
||||
|
@ -61,6 +62,10 @@ public class ClientEntity extends AbstractIdentifiableEntity {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() { return description; }
|
||||
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
package org.keycloak.models.entities;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class OfflineUserSessionEntity {
|
||||
|
||||
private String userSessionId;
|
||||
private String data;
|
||||
private List<OfflineClientSessionEntity> offlineClientSessions;
|
||||
|
||||
public String getUserSessionId() {
|
||||
return userSessionId;
|
||||
}
|
||||
|
||||
public void setUserSessionId(String userSessionId) {
|
||||
this.userSessionId = userSessionId;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public List<OfflineClientSessionEntity> getOfflineClientSessions() {
|
||||
return offlineClientSessions;
|
||||
}
|
||||
|
||||
public void setOfflineClientSessions(List<OfflineClientSessionEntity> offlineClientSessions) {
|
||||
this.offlineClientSessions = offlineClientSessions;
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ package org.keycloak.models.entities;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class OfflineClientSessionEntity {
|
||||
public class PersistentClientSessionEntity {
|
||||
|
||||
private String clientSessionId;
|
||||
private String clientId;
|
|
@ -0,0 +1,64 @@
|
|||
package org.keycloak.models.entities;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class PersistentUserSessionEntity {
|
||||
|
||||
private String id;
|
||||
private String realmId;
|
||||
private String userId;
|
||||
private int lastSessionRefresh;
|
||||
private String data;
|
||||
private List<PersistentClientSessionEntity> clientSessions;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public int getLastSessionRefresh() {
|
||||
return lastSessionRefresh;
|
||||
}
|
||||
|
||||
public void setLastSessionRefresh(int lastSessionRefresh) {
|
||||
this.lastSessionRefresh = lastSessionRefresh;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public List<PersistentClientSessionEntity> getClientSessions() {
|
||||
return clientSessions;
|
||||
}
|
||||
|
||||
public void setClientSessions(List<PersistentClientSessionEntity> clientSessions) {
|
||||
this.clientSessions = clientSessions;
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
private int failureFactor;
|
||||
//--- end brute force settings
|
||||
|
||||
private boolean revokeRefreshToken;
|
||||
private int ssoSessionIdleTimeout;
|
||||
private int ssoSessionMaxLifespan;
|
||||
private int accessTokenLifespan;
|
||||
|
@ -229,6 +230,14 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
this.failureFactor = failureFactor;
|
||||
}
|
||||
|
||||
public boolean isRevokeRefreshToken() {
|
||||
return revokeRefreshToken;
|
||||
}
|
||||
|
||||
public void setRevokeRefreshToken(boolean revokeRefreshToken) {
|
||||
this.revokeRefreshToken = revokeRefreshToken;
|
||||
}
|
||||
|
||||
public int getSsoSessionIdleTimeout() {
|
||||
return ssoSessionIdleTimeout;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ public class UserEntity extends AbstractIdentifiableEntity {
|
|||
private List<FederatedIdentityEntity> federatedIdentities;
|
||||
private String federationLink;
|
||||
private String serviceAccountClientLink;
|
||||
private List<OfflineUserSessionEntity> offlineUserSessions;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
|
@ -158,13 +157,5 @@ public class UserEntity extends AbstractIdentifiableEntity {
|
|||
public void setServiceAccountClientLink(String serviceAccountClientLink) {
|
||||
this.serviceAccountClientLink = serviceAccountClientLink;
|
||||
}
|
||||
|
||||
public List<OfflineUserSessionEntity> getOfflineUserSessions() {
|
||||
return offlineUserSessions;
|
||||
}
|
||||
|
||||
public void setOfflineUserSessions(List<OfflineUserSessionEntity> offlineUserSessions) {
|
||||
this.offlineUserSessions = offlineUserSessions;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package org.keycloak.models.session;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
||||
/**
|
||||
* Persistence of userSessions is disabled . Useful just if you never need survive of userSessions/clientSessions
|
||||
* among server restart. Offline sessions / offline tokens will be invalid after server restart as well,
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class DisabledUserSessionPersisterProvider implements UserSessionPersisterProviderFactory, UserSessionPersisterProvider {
|
||||
|
||||
public static final String ID = "disabled";
|
||||
|
||||
@Override
|
||||
public UserSessionPersisterProvider create(KeycloakSession session) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createUserSession(UserSessionModel userSession, boolean offline) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createClientSession(ClientSessionModel clientSession, boolean offline) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserSession(UserSessionModel userSession, boolean offline) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserSession(String userSessionId, boolean offline) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeClientSession(String clientSessionId, boolean offline) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRealmRemoved(RealmModel realm) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClientRemoved(RealmModel realm, ClientModel client) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserRemoved(RealmModel realm, UserModel user) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearDetachedUserSessions() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserSessionModel> loadUserSessions(int firstResult, int maxResults, boolean offline) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUserSessionsCount(boolean offline) {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,401 @@
|
|||
package org.keycloak.models.session;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class PersistentClientSessionAdapter implements ClientSessionModel {
|
||||
|
||||
private final PersistentClientSessionModel model;
|
||||
private final RealmModel realm;
|
||||
private final ClientModel client;
|
||||
private UserSessionModel userSession;
|
||||
|
||||
private PersistentClientSessionData data;
|
||||
|
||||
public PersistentClientSessionAdapter(ClientSessionModel clientSession) {
|
||||
data = new PersistentClientSessionData();
|
||||
data.setAction(clientSession.getAction());
|
||||
data.setAuthMethod(clientSession.getAuthMethod());
|
||||
data.setExecutionStatus(clientSession.getExecutionStatus());
|
||||
data.setNotes(clientSession.getNotes());
|
||||
data.setProtocolMappers(clientSession.getProtocolMappers());
|
||||
data.setRedirectUri(clientSession.getRedirectUri());
|
||||
data.setRoles(clientSession.getRoles());
|
||||
data.setTimestamp(clientSession.getTimestamp());
|
||||
data.setUserSessionNotes(clientSession.getUserSessionNotes());
|
||||
|
||||
model = new PersistentClientSessionModel();
|
||||
model.setClientId(clientSession.getClient().getId());
|
||||
model.setClientSessionId(clientSession.getId());
|
||||
if (clientSession.getAuthenticatedUser() != null) {
|
||||
model.setUserId(clientSession.getAuthenticatedUser().getId());
|
||||
}
|
||||
model.setUserSessionId(clientSession.getUserSession().getId());
|
||||
|
||||
realm = clientSession.getRealm();
|
||||
client = clientSession.getClient();
|
||||
userSession = clientSession.getUserSession();
|
||||
}
|
||||
|
||||
public PersistentClientSessionAdapter(PersistentClientSessionModel model, RealmModel realm, ClientModel client, UserSessionModel userSession) {
|
||||
this.model = model;
|
||||
this.realm = realm;
|
||||
this.client = client;
|
||||
this.userSession = userSession;
|
||||
}
|
||||
|
||||
// Lazily init data
|
||||
private PersistentClientSessionData getData() {
|
||||
if (data == null) {
|
||||
try {
|
||||
data = JsonSerialization.readValue(model.getData(), PersistentClientSessionData.class);
|
||||
} catch (IOException ioe) {
|
||||
throw new ModelException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Write updated model with latest serialized data
|
||||
public PersistentClientSessionModel getUpdatedModel() {
|
||||
try {
|
||||
String updatedData = JsonSerialization.writeValueAsString(getData());
|
||||
this.model.setData(updatedData);
|
||||
} catch (IOException ioe) {
|
||||
throw new ModelException(ioe);
|
||||
}
|
||||
|
||||
return this.model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return model.getClientSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSessionModel getUserSession() {
|
||||
return userSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserSession(UserSessionModel userSession) {
|
||||
this.userSession = userSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return getData().getRedirectUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRedirectUri(String uri) {
|
||||
getData().setRedirectUri(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimestamp() {
|
||||
return getData().getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimestamp(int timestamp) {
|
||||
getData().setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAction() {
|
||||
return getData().getAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAction(String action) {
|
||||
getData().setAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRoles() {
|
||||
return getData().getRoles();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRoles(Set<String> roles) {
|
||||
getData().setRoles(roles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getProtocolMappers() {
|
||||
return getData().getProtocolMappers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProtocolMappers(Set<String> protocolMappers) {
|
||||
getData().setProtocolMappers(protocolMappers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ExecutionStatus> getExecutionStatus() {
|
||||
return getData().getExecutionStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecutionStatus(String authenticator, ExecutionStatus status) {
|
||||
getData().getExecutionStatus().put(authenticator, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearExecutionStatus() {
|
||||
getData().getExecutionStatus().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getAuthenticatedUser() {
|
||||
return userSession.getUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticatedUser(UserModel user) {
|
||||
throw new IllegalStateException("Not supported setAuthenticatedUser");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthMethod() {
|
||||
return getData().getAuthMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthMethod(String method) {
|
||||
getData().setAuthMethod(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNote(String name) {
|
||||
PersistentClientSessionData entity = getData();
|
||||
return entity.getNotes()==null ? null : entity.getNotes().get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNote(String name, String value) {
|
||||
PersistentClientSessionData entity = getData();
|
||||
if (entity.getNotes() == null) {
|
||||
entity.setNotes(new HashMap<String, String>());
|
||||
}
|
||||
entity.getNotes().put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNote(String name) {
|
||||
PersistentClientSessionData entity = getData();
|
||||
if (entity.getNotes() != null) {
|
||||
entity.getNotes().remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getNotes() {
|
||||
PersistentClientSessionData entity = getData();
|
||||
if (entity.getNotes() == null || entity.getNotes().isEmpty()) return Collections.emptyMap();
|
||||
return entity.getNotes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRequiredActions() {
|
||||
return getData().getRequiredActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredAction(String action) {
|
||||
getData().getRequiredActions().add(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRequiredAction(String action) {
|
||||
getData().getRequiredActions().remove(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredAction(UserModel.RequiredAction action) {
|
||||
addRequiredAction(action.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRequiredAction(UserModel.RequiredAction action) {
|
||||
removeRequiredAction(action.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserSessionNote(String name, String value) {
|
||||
PersistentClientSessionData entity = getData();
|
||||
if (entity.getUserSessionNotes() == null) {
|
||||
entity.setUserSessionNotes(new HashMap<String, String>());
|
||||
}
|
||||
entity.getUserSessionNotes().put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getUserSessionNotes() {
|
||||
PersistentClientSessionData entity = getData();
|
||||
if (entity.getUserSessionNotes() == null || entity.getUserSessionNotes().isEmpty()) return Collections.emptyMap();
|
||||
return entity.getUserSessionNotes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearUserSessionNotes() {
|
||||
PersistentClientSessionData entity = getData();
|
||||
entity.setUserSessionNotes(new HashMap<String, String>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || !(o instanceof ClientSessionModel)) return false;
|
||||
|
||||
ClientSessionModel that = (ClientSessionModel) o;
|
||||
return that.getId().equals(getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
|
||||
protected static class PersistentClientSessionData {
|
||||
|
||||
@JsonProperty("authMethod")
|
||||
private String authMethod;
|
||||
|
||||
@JsonProperty("redirectUri")
|
||||
private String redirectUri;
|
||||
|
||||
@JsonProperty("protocolMappers")
|
||||
private Set<String> protocolMappers;
|
||||
|
||||
@JsonProperty("roles")
|
||||
private Set<String> roles;
|
||||
|
||||
@JsonProperty("notes")
|
||||
private Map<String, String> notes;
|
||||
|
||||
@JsonProperty("userSessionNotes")
|
||||
private Map<String, String> userSessionNotes;
|
||||
|
||||
@JsonProperty("executionStatus")
|
||||
private Map<String, ClientSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
|
||||
|
||||
@JsonProperty("timestamp")
|
||||
private int timestamp;
|
||||
|
||||
@JsonProperty("action")
|
||||
private String action;
|
||||
|
||||
@JsonProperty("requiredActions")
|
||||
private Set<String> requiredActions = new HashSet<>();
|
||||
|
||||
public String getAuthMethod() {
|
||||
return authMethod;
|
||||
}
|
||||
|
||||
public void setAuthMethod(String authMethod) {
|
||||
this.authMethod = authMethod;
|
||||
}
|
||||
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
public Set<String> getProtocolMappers() {
|
||||
return protocolMappers;
|
||||
}
|
||||
|
||||
public void setProtocolMappers(Set<String> protocolMappers) {
|
||||
this.protocolMappers = protocolMappers;
|
||||
}
|
||||
|
||||
public Set<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Set<String> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public Map<String, String> getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(Map<String, String> notes) {
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
public Map<String, String> getUserSessionNotes() {
|
||||
return userSessionNotes;
|
||||
}
|
||||
|
||||
public void setUserSessionNotes(Map<String, String> userSessionNotes) {
|
||||
this.userSessionNotes = userSessionNotes;
|
||||
}
|
||||
|
||||
public Map<String, ClientSessionModel.ExecutionStatus> getExecutionStatus() {
|
||||
return executionStatus;
|
||||
}
|
||||
|
||||
public void setExecutionStatus(Map<String, ClientSessionModel.ExecutionStatus> executionStatus) {
|
||||
this.executionStatus = executionStatus;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public Set<String> getRequiredActions() {
|
||||
return requiredActions;
|
||||
}
|
||||
|
||||
public void setRequiredActions(Set<String> requiredActions) {
|
||||
this.requiredActions = requiredActions;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
package org.keycloak.models;
|
||||
package org.keycloak.models.session;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class OfflineClientSessionModel {
|
||||
public class PersistentClientSessionModel {
|
||||
|
||||
private String clientSessionId;
|
||||
private String userSessionId;
|
|
@ -1,13 +1,14 @@
|
|||
package org.keycloak.services.offline;
|
||||
package org.keycloak.models.session;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OfflineUserSessionModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -15,23 +16,47 @@ import org.keycloak.util.JsonSerialization;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class OfflineUserSessionAdapter implements UserSessionModel {
|
||||
public class PersistentUserSessionAdapter implements UserSessionModel {
|
||||
|
||||
private final OfflineUserSessionModel model;
|
||||
private final PersistentUserSessionModel model;
|
||||
private final UserModel user;
|
||||
private final RealmModel realm;
|
||||
private final List<ClientSessionModel> clientSessions;
|
||||
|
||||
private OfflineUserSessionData data;
|
||||
private PersistentUserSessionData data;
|
||||
|
||||
public OfflineUserSessionAdapter(OfflineUserSessionModel model, UserModel user) {
|
||||
this.model = model;
|
||||
this.user = user;
|
||||
public PersistentUserSessionAdapter(UserSessionModel other) {
|
||||
this.data = new PersistentUserSessionData();
|
||||
data.setAuthMethod(other.getAuthMethod());
|
||||
data.setBrokerSessionId(other.getBrokerSessionId());
|
||||
data.setBrokerUserId(other.getBrokerUserId());
|
||||
data.setIpAddress(other.getIpAddress());
|
||||
data.setNotes(other.getNotes());
|
||||
data.setRememberMe(other.isRememberMe());
|
||||
data.setStarted(other.getStarted());
|
||||
data.setState(other.getState());
|
||||
|
||||
this.model = new PersistentUserSessionModel();
|
||||
this.model.setUserSessionId(other.getId());
|
||||
this.model.setLastSessionRefresh(other.getLastSessionRefresh());
|
||||
|
||||
this.user = other.getUser();
|
||||
this.realm = other.getRealm();
|
||||
this.clientSessions = other.getClientSessions();
|
||||
}
|
||||
|
||||
// lazily init representation
|
||||
private OfflineUserSessionData getData() {
|
||||
public PersistentUserSessionAdapter(PersistentUserSessionModel model, RealmModel realm, UserModel user, List<ClientSessionModel> clientSessions) {
|
||||
this.model = model;
|
||||
this.realm = realm;
|
||||
this.user = user;
|
||||
this.clientSessions = clientSessions;
|
||||
}
|
||||
|
||||
// Lazily init data
|
||||
private PersistentUserSessionData getData() {
|
||||
if (data == null) {
|
||||
try {
|
||||
data = JsonSerialization.readValue(model.getData(), OfflineUserSessionData.class);
|
||||
data = JsonSerialization.readValue(model.getData(), PersistentUserSessionData.class);
|
||||
} catch (IOException ioe) {
|
||||
throw new ModelException(ioe);
|
||||
}
|
||||
|
@ -40,6 +65,18 @@ public class OfflineUserSessionAdapter implements UserSessionModel {
|
|||
return data;
|
||||
}
|
||||
|
||||
// Write updated model with latest serialized data
|
||||
public PersistentUserSessionModel getUpdatedModel() {
|
||||
try {
|
||||
String updatedData = JsonSerialization.writeValueAsString(getData());
|
||||
this.model.setData(updatedData);
|
||||
} catch (IOException ioe) {
|
||||
throw new ModelException(ioe);
|
||||
}
|
||||
|
||||
return this.model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return model.getUserSessionId();
|
||||
|
@ -60,6 +97,11 @@ public class OfflineUserSessionAdapter implements UserSessionModel {
|
|||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoginUsername() {
|
||||
return user.getUsername();
|
||||
|
@ -87,17 +129,17 @@ public class OfflineUserSessionAdapter implements UserSessionModel {
|
|||
|
||||
@Override
|
||||
public int getLastSessionRefresh() {
|
||||
return 0;
|
||||
return model.getLastSessionRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastSessionRefresh(int seconds) {
|
||||
// Ignore
|
||||
model.setLastSessionRefresh(seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientSessionModel> getClientSessions() {
|
||||
throw new IllegalStateException("Not yet supported");
|
||||
return clientSessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,13 +149,19 @@ public class OfflineUserSessionAdapter implements UserSessionModel {
|
|||
|
||||
@Override
|
||||
public void setNote(String name, String value) {
|
||||
throw new IllegalStateException("Illegal to set note offline session");
|
||||
PersistentUserSessionData data = getData();
|
||||
if (data.getNotes() == null) {
|
||||
data.setNotes(new HashMap<String, String>());
|
||||
}
|
||||
data.getNotes().put(name, value);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNote(String name) {
|
||||
throw new IllegalStateException("Illegal to remove note from offline session");
|
||||
if (getData().getNotes() != null) {
|
||||
getData().getNotes().remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -123,16 +171,29 @@ public class OfflineUserSessionAdapter implements UserSessionModel {
|
|||
|
||||
@Override
|
||||
public State getState() {
|
||||
return null;
|
||||
return getData().getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(State state) {
|
||||
throw new IllegalStateException("Illegal to set state on offline session");
|
||||
getData().setState(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || !(o instanceof UserSessionModel)) return false;
|
||||
|
||||
protected static class OfflineUserSessionData {
|
||||
UserSessionModel that = (UserSessionModel) o;
|
||||
return that.getId().equals(getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
|
||||
protected static class PersistentUserSessionData {
|
||||
|
||||
@JsonProperty("brokerSessionId")
|
||||
private String brokerSessionId;
|
||||
|
@ -155,6 +216,9 @@ public class OfflineUserSessionAdapter implements UserSessionModel {
|
|||
@JsonProperty("notes")
|
||||
private Map<String, String> notes;
|
||||
|
||||
@JsonProperty("state")
|
||||
private State state;
|
||||
|
||||
public String getBrokerSessionId() {
|
||||
return brokerSessionId;
|
||||
}
|
||||
|
@ -211,5 +275,12 @@ public class OfflineUserSessionAdapter implements UserSessionModel {
|
|||
this.notes = notes;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
package org.keycloak.models;
|
||||
package org.keycloak.models.session;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class OfflineUserSessionModel {
|
||||
public class PersistentUserSessionModel {
|
||||
|
||||
private String userSessionId;
|
||||
private int lastSessionRefresh;
|
||||
|
||||
private String data;
|
||||
|
||||
public String getUserSessionId() {
|
||||
|
@ -16,6 +18,15 @@ public class OfflineUserSessionModel {
|
|||
this.userSessionId = userSessionId;
|
||||
}
|
||||
|
||||
public int getLastSessionRefresh() {
|
||||
return lastSessionRefresh;
|
||||
}
|
||||
|
||||
public void setLastSessionRefresh(int lastSessionRefresh) {
|
||||
this.lastSessionRefresh = lastSessionRefresh;
|
||||
}
|
||||
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package org.keycloak.models.session;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface UserSessionPersisterProvider extends Provider {
|
||||
|
||||
// Persist just userSession. Not it's clientSessions
|
||||
void createUserSession(UserSessionModel userSession, boolean offline);
|
||||
|
||||
// Assuming that corresponding userSession is already persisted
|
||||
void createClientSession(ClientSessionModel clientSession, boolean offline);
|
||||
|
||||
void updateUserSession(UserSessionModel userSession, boolean offline);
|
||||
|
||||
// Called during logout (for online session) or during periodic expiration. It will remove all corresponding clientSessions too
|
||||
void removeUserSession(String userSessionId, boolean offline);
|
||||
|
||||
// Called during revoke. It will remove userSession too if this was last clientSession attached to it
|
||||
void removeClientSession(String clientSessionId, boolean offline);
|
||||
|
||||
void onRealmRemoved(RealmModel realm);
|
||||
void onClientRemoved(RealmModel realm, ClientModel client);
|
||||
void onUserRemoved(RealmModel realm, UserModel user);
|
||||
|
||||
// Called at startup to remove userSessions without any clientSession
|
||||
void clearDetachedUserSessions();
|
||||
|
||||
// Called during startup. For each userSession, it loads also clientSessions
|
||||
List<UserSessionModel> loadUserSessions(int firstResult, int maxResults, boolean offline);
|
||||
|
||||
int getUserSessionsCount(boolean offline);
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.keycloak.models.session;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface UserSessionPersisterProviderFactory extends ProviderFactory<UserSessionPersisterProvider> {
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package org.keycloak.models.session;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserSessionPersisterSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "userSessionPersister";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return UserSessionPersisterProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return UserSessionPersisterProviderFactory.class;
|
||||
}
|
||||
}
|
|
@ -10,8 +10,8 @@ import org.keycloak.models.IdentityProviderMapperModel;
|
|||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OTPPolicy;
|
||||
import org.keycloak.models.OfflineClientSessionModel;
|
||||
import org.keycloak.models.OfflineUserSessionModel;
|
||||
import org.keycloak.models.session.PersistentClientSessionModel;
|
||||
import org.keycloak.models.session.PersistentUserSessionModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
|
@ -144,6 +144,7 @@ public class ModelToRepresentation {
|
|||
rep.setVerifyEmail(realm.isVerifyEmail());
|
||||
rep.setResetPasswordAllowed(realm.isResetPasswordAllowed());
|
||||
rep.setEditUsernameAllowed(realm.isEditUsernameAllowed());
|
||||
rep.setRevokeRefreshToken(realm.isRevokeRefreshToken());
|
||||
rep.setAccessTokenLifespan(realm.getAccessTokenLifespan());
|
||||
rep.setSsoSessionIdleTimeout(realm.getSsoSessionIdleTimeout());
|
||||
rep.setSsoSessionMaxLifespan(realm.getSsoSessionMaxLifespan());
|
||||
|
@ -250,11 +251,11 @@ public class ModelToRepresentation {
|
|||
}
|
||||
|
||||
if (realm.getEventsListeners() != null) {
|
||||
rep.setEventsListeners(new LinkedList<String>(realm.getEventsListeners()));
|
||||
rep.setEventsListeners(new LinkedList<>(realm.getEventsListeners()));
|
||||
}
|
||||
|
||||
if(realm.getEnabledEventTypes() != null) {
|
||||
rep.setEnabledEventTypes(new LinkedList<String>(realm.getEnabledEventTypes()));
|
||||
rep.setEnabledEventTypes(new LinkedList<>(realm.getEnabledEventTypes()));
|
||||
}
|
||||
|
||||
rep.setAdminEventsEnabled(realm.isAdminEventsEnabled());
|
||||
|
@ -299,6 +300,7 @@ public class ModelToRepresentation {
|
|||
rep.setId(clientModel.getId());
|
||||
rep.setClientId(clientModel.getClientId());
|
||||
rep.setName(clientModel.getName());
|
||||
rep.setDescription(clientModel.getDescription());
|
||||
rep.setEnabled(clientModel.isEnabled());
|
||||
rep.setAdminUrl(clientModel.getManagementUrl());
|
||||
rep.setPublicClient(clientModel.isPublicClient());
|
||||
|
@ -511,13 +513,13 @@ public class ModelToRepresentation {
|
|||
return rep;
|
||||
}
|
||||
|
||||
public static OfflineUserSessionRepresentation toRepresentation(RealmModel realm, OfflineUserSessionModel model, Collection<OfflineClientSessionModel> clientSessions) {
|
||||
public static OfflineUserSessionRepresentation toRepresentation(RealmModel realm, PersistentUserSessionModel model, Collection<PersistentClientSessionModel> clientSessions) {
|
||||
OfflineUserSessionRepresentation rep = new OfflineUserSessionRepresentation();
|
||||
rep.setData(model.getData());
|
||||
rep.setUserSessionId(model.getUserSessionId());
|
||||
|
||||
List<OfflineClientSessionRepresentation> clientSessionReps = new LinkedList<>();
|
||||
for (OfflineClientSessionModel clsm : clientSessions) {
|
||||
for (PersistentClientSessionModel clsm : clientSessions) {
|
||||
OfflineClientSessionRepresentation clrep = toRepresentation(realm, clsm);
|
||||
clientSessionReps.add(clrep);
|
||||
}
|
||||
|
@ -525,7 +527,7 @@ public class ModelToRepresentation {
|
|||
return rep;
|
||||
}
|
||||
|
||||
public static OfflineClientSessionRepresentation toRepresentation(RealmModel realm, OfflineClientSessionModel model) {
|
||||
public static OfflineClientSessionRepresentation toRepresentation(RealmModel realm, PersistentClientSessionModel model) {
|
||||
OfflineClientSessionRepresentation rep = new OfflineClientSessionRepresentation();
|
||||
|
||||
String clientInternalId = model.getClientId();
|
||||
|
|
|
@ -90,14 +90,7 @@ public class Pbkdf2PasswordEncoder {
|
|||
public static byte[] getSalt() {
|
||||
byte[] buffer = new byte[16];
|
||||
|
||||
SecureRandom secureRandom;
|
||||
|
||||
try {
|
||||
secureRandom = SecureRandom.getInstance(RNG_ALGORITHM);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("RNG algorithm not found");
|
||||
}
|
||||
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
secureRandom.nextBytes(buffer);
|
||||
|
||||
return buffer;
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
package org.keycloak.models.utils;
|
||||
|
||||
import org.keycloak.models.OfflineClientSessionModel;
|
||||
import org.keycloak.models.OfflineUserSessionModel;
|
||||
import org.keycloak.representations.idm.OfflineClientSessionRepresentation;
|
||||
import org.keycloak.representations.idm.OfflineUserSessionRepresentation;
|
||||
import org.keycloak.util.Base64;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.enums.SslRequired;
|
||||
|
@ -100,6 +96,9 @@ public class RepresentationToModel {
|
|||
|
||||
if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore());
|
||||
|
||||
if (rep.getRevokeRefreshToken() != null) newRealm.setRevokeRefreshToken(rep.getRevokeRefreshToken());
|
||||
else newRealm.setRevokeRefreshToken(false);
|
||||
|
||||
if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
|
||||
else newRealm.setAccessTokenLifespan(300);
|
||||
|
||||
|
@ -532,6 +531,7 @@ public class RepresentationToModel {
|
|||
if (rep.getAccessCodeLifespanUserAction() != null) realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
|
||||
if (rep.getAccessCodeLifespanLogin() != null) realm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin());
|
||||
if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore());
|
||||
if (rep.getRevokeRefreshToken() != null) realm.setRevokeRefreshToken(rep.getRevokeRefreshToken());
|
||||
if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
|
||||
if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
|
||||
if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan());
|
||||
|
@ -692,6 +692,7 @@ public class RepresentationToModel {
|
|||
|
||||
ClientModel client = resourceRep.getId()!=null ? realm.addClient(resourceRep.getId(), resourceRep.getClientId()) : realm.addClient(resourceRep.getClientId());
|
||||
if (resourceRep.getName() != null) client.setName(resourceRep.getName());
|
||||
if(resourceRep.getDescription() != null) client.setDescription(resourceRep.getDescription());
|
||||
if (resourceRep.isEnabled() != null) client.setEnabled(resourceRep.isEnabled());
|
||||
client.setManagementUrl(resourceRep.getAdminUrl());
|
||||
if (resourceRep.isSurrogateAuthRequired() != null)
|
||||
|
@ -793,6 +794,7 @@ public class RepresentationToModel {
|
|||
public static void updateClient(ClientRepresentation rep, ClientModel resource) {
|
||||
if (rep.getClientId() != null) resource.setClientId(rep.getClientId());
|
||||
if (rep.getName() != null) resource.setName(rep.getName());
|
||||
if (rep.getDescription() != null) resource.setDescription(rep.getDescription());
|
||||
if (rep.isEnabled() != null) resource.setEnabled(rep.isEnabled());
|
||||
if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly());
|
||||
if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired());
|
||||
|
@ -985,11 +987,6 @@ public class RepresentationToModel {
|
|||
user.addConsent(consentModel);
|
||||
}
|
||||
}
|
||||
if (userRep.getOfflineUserSessions() != null) {
|
||||
for (OfflineUserSessionRepresentation sessionRep : userRep.getOfflineUserSessions()) {
|
||||
importOfflineSession(session, newRealm, user, sessionRep);
|
||||
}
|
||||
}
|
||||
if (userRep.getServiceAccountClientId() != null) {
|
||||
String clientId = userRep.getServiceAccountClientId();
|
||||
ClientModel client = clientMap.get(clientId);
|
||||
|
@ -1160,28 +1157,29 @@ public class RepresentationToModel {
|
|||
return consentModel;
|
||||
}
|
||||
|
||||
public static void importOfflineSession(KeycloakSession session, RealmModel newRealm, UserModel user, OfflineUserSessionRepresentation sessionRep) {
|
||||
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
||||
model.setUserSessionId(sessionRep.getUserSessionId());
|
||||
model.setData(sessionRep.getData());
|
||||
session.users().addOfflineUserSession(newRealm, user, model);
|
||||
|
||||
for (OfflineClientSessionRepresentation csRep : sessionRep.getOfflineClientSessions()) {
|
||||
OfflineClientSessionModel csModel = new OfflineClientSessionModel();
|
||||
String clientId = csRep.getClient();
|
||||
ClientModel client = newRealm.getClientByClientId(clientId);
|
||||
if (client == null) {
|
||||
throw new RuntimeException("Unable to find client " + clientId + " referenced from offlineClientSession of user " + user.getUsername());
|
||||
}
|
||||
csModel.setClientId(client.getId());
|
||||
csModel.setUserId(user.getId());
|
||||
csModel.setClientSessionId(csRep.getClientSessionId());
|
||||
csModel.setUserSessionId(sessionRep.getUserSessionId());
|
||||
csModel.setData(csRep.getData());
|
||||
|
||||
session.users().addOfflineClientSession(newRealm, csModel);
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
// public static void importOfflineSession(KeycloakSession session, RealmModel newRealm, UserModel user, OfflineUserSessionRepresentation sessionRep) {
|
||||
// PersistentUserSessionModel model = new PersistentUserSessionModel();
|
||||
// model.setUserSessionId(sessionRep.getUserSessionId());
|
||||
// model.setData(sessionRep.getData());
|
||||
// session.users().createOfflineUserSession(newRealm, user, model);
|
||||
//
|
||||
// for (OfflineClientSessionRepresentation csRep : sessionRep.getOfflineClientSessions()) {
|
||||
// PersistentClientSessionModel csModel = new PersistentClientSessionModel();
|
||||
// String clientId = csRep.getClient();
|
||||
// ClientModel client = newRealm.getClientByClientId(clientId);
|
||||
// if (client == null) {
|
||||
// throw new RuntimeException("Unable to find client " + clientId + " referenced from offlineClientSession of user " + user.getUsername());
|
||||
// }
|
||||
// csModel.setClientId(client.getId());
|
||||
// csModel.setUserId(user.getId());
|
||||
// csModel.setClientSessionId(csRep.getClientSessionId());
|
||||
// csModel.setUserSessionId(sessionRep.getUserSessionId());
|
||||
// csModel.setData(csRep.getData());
|
||||
//
|
||||
// session.users().createOfflineClientSession(newRealm, csModel);
|
||||
// }
|
||||
// }
|
||||
|
||||
public static AuthenticationFlowModel toModel(AuthenticationFlowRepresentation rep) {
|
||||
AuthenticationFlowModel model = new AuthenticationFlowModel();
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
package org.keycloak.models.utils;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.OfflineClientSessionModel;
|
||||
import org.keycloak.models.OfflineUserSessionModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.models.session.DisabledUserSessionPersisterProvider
|
|
@ -3,4 +3,5 @@ org.keycloak.mappers.UserFederationMapperSpi
|
|||
org.keycloak.models.RealmSpi
|
||||
org.keycloak.models.UserSessionSpi
|
||||
org.keycloak.models.UserSpi
|
||||
org.keycloak.models.session.UserSessionPersisterSpi
|
||||
org.keycloak.migration.MigrationSpi
|
|
@ -24,8 +24,8 @@ import org.keycloak.models.FederatedIdentityModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OfflineClientSessionModel;
|
||||
import org.keycloak.models.OfflineUserSessionModel;
|
||||
import org.keycloak.models.session.PersistentClientSessionModel;
|
||||
import org.keycloak.models.session.PersistentUserSessionModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
|
@ -35,8 +35,8 @@ import org.keycloak.models.UserFederationProviderModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.entities.FederatedIdentityEntity;
|
||||
import org.keycloak.models.entities.OfflineClientSessionEntity;
|
||||
import org.keycloak.models.entities.OfflineUserSessionEntity;
|
||||
import org.keycloak.models.entities.PersistentClientSessionEntity;
|
||||
import org.keycloak.models.entities.PersistentUserSessionEntity;
|
||||
import org.keycloak.models.entities.UserEntity;
|
||||
import org.keycloak.models.file.adapter.UserAdapter;
|
||||
import org.keycloak.models.utils.CredentialValidation;
|
||||
|
@ -494,188 +494,4 @@ public class FileUserProvider implements UserProvider {
|
|||
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
return null; // not supported yet
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOfflineUserSession(RealmModel realm, UserModel userModel, OfflineUserSessionModel userSession) {
|
||||
userModel = getUserById(userModel.getId(), realm);
|
||||
UserEntity userEntity = ((UserAdapter) userModel).getUserEntity();
|
||||
|
||||
if (userEntity.getOfflineUserSessions() == null) {
|
||||
userEntity.setOfflineUserSessions(new ArrayList<OfflineUserSessionEntity>());
|
||||
}
|
||||
|
||||
if (getUserSessionEntityById(userEntity, userSession.getUserSessionId()) != null) {
|
||||
throw new ModelDuplicateException("User session already exists with id " + userSession.getUserSessionId() + " for user " + userEntity.getUsername());
|
||||
}
|
||||
|
||||
OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
|
||||
entity.setUserSessionId(userSession.getUserSessionId());
|
||||
entity.setData(userSession.getData());
|
||||
entity.setOfflineClientSessions(new ArrayList<OfflineClientSessionEntity>());
|
||||
userEntity.getOfflineUserSessions().add(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel userModel, String userSessionId) {
|
||||
userModel = getUserById(userModel.getId(), realm);
|
||||
UserEntity userEntity = ((UserAdapter) userModel).getUserEntity();
|
||||
|
||||
OfflineUserSessionEntity entity = getUserSessionEntityById(userEntity, userSessionId);
|
||||
return entity==null ? null : toModel(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel userModel) {
|
||||
userModel = getUserById(userModel.getId(), realm);
|
||||
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||
|
||||
if (user.getOfflineUserSessions()==null) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
List<OfflineUserSessionModel> result = new ArrayList<>();
|
||||
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
||||
result.add(toModel(entity));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
|
||||
OfflineUserSessionModel model = new OfflineUserSessionModel();
|
||||
model.setUserSessionId(entity.getUserSessionId());
|
||||
model.setData(entity.getData());
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeOfflineUserSession(RealmModel realm, UserModel userModel, String userSessionId) {
|
||||
userModel = getUserById(userModel.getId(), realm);
|
||||
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||
|
||||
OfflineUserSessionEntity entity = getUserSessionEntityById(user, userSessionId);
|
||||
if (entity != null) {
|
||||
user.getOfflineUserSessions().remove(entity);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private OfflineUserSessionEntity getUserSessionEntityById(UserEntity user, String userSessionId) {
|
||||
if (user.getOfflineUserSessions() != null) {
|
||||
for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
|
||||
if (entity.getUserSessionId().equals(userSessionId)) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel clientSession) {
|
||||
UserModel userModel = getUserById(clientSession.getUserId(), realm);
|
||||
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||
|
||||
OfflineUserSessionEntity userSessionEntity = getUserSessionEntityById(user, clientSession.getUserSessionId());
|
||||
if (userSessionEntity == null) {
|
||||
throw new ModelException("OfflineUserSession with ID " + clientSession.getUserSessionId() + " doesn't exist for user " + user.getUsername());
|
||||
}
|
||||
|
||||
OfflineClientSessionEntity clEntity = new OfflineClientSessionEntity();
|
||||
clEntity.setClientSessionId(clientSession.getClientSessionId());
|
||||
clEntity.setClientId(clientSession.getClientId());
|
||||
clEntity.setData(clientSession.getData());
|
||||
|
||||
userSessionEntity.getOfflineClientSessions().add(clEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel userModel, String clientSessionId) {
|
||||
userModel = getUserById(userModel.getId(), realm);
|
||||
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||
|
||||
if (user.getOfflineUserSessions() != null) {
|
||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
||||
return toModel(clSession, userSession.getUserSessionId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private OfflineClientSessionModel toModel(OfflineClientSessionEntity cls, String userSessionId) {
|
||||
OfflineClientSessionModel model = new OfflineClientSessionModel();
|
||||
model.setClientSessionId(cls.getClientSessionId());
|
||||
model.setClientId(cls.getClientId());
|
||||
model.setData(cls.getData());
|
||||
model.setUserSessionId(userSessionId);
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel userModel) {
|
||||
userModel = getUserById(userModel.getId(), realm);
|
||||
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||
|
||||
List<OfflineClientSessionModel> result = new ArrayList<>();
|
||||
|
||||
if (user.getOfflineUserSessions() != null) {
|
||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||
result.add(toModel(clSession, userSession.getUserSessionId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeOfflineClientSession(RealmModel realm, UserModel userModel, String clientSessionId) {
|
||||
userModel = getUserById(userModel.getId(), realm);
|
||||
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||
|
||||
if (user.getOfflineUserSessions() != null) {
|
||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||
if (clSession.getClientSessionId().equals(clientSessionId)) {
|
||||
userSession.getOfflineClientSessions().remove(clSession);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOfflineClientSessionsCount(RealmModel realm, ClientModel client) {
|
||||
return getOfflineClientSessions(realm, client, -1, -1).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
|
||||
List<OfflineClientSessionModel> result = new LinkedList<>();
|
||||
|
||||
List<UserModel> users = new ArrayList<>(inMemoryModel.getUsers(realm.getId()));
|
||||
users = sortedSubList(users, firstResult, maxResults);
|
||||
|
||||
for (UserModel userModel : users) {
|
||||
UserEntity user = ((UserAdapter) userModel).getUserEntity();
|
||||
for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
|
||||
for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
|
||||
if (clSession.getClientId().equals(client.getId())) {
|
||||
result.add(toModel(clSession, userSession.getUserSessionId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,12 @@ public class ClientAdapter implements ClientModel {
|
|||
entity.setName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() { return entity.getDescription(); }
|
||||
|
||||
@Override
|
||||
public void setDescription(String description) { entity.setDescription(description); }
|
||||
|
||||
@Override
|
||||
public Set<String> getWebOrigins() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
|
|
|
@ -325,6 +325,16 @@ public class RealmAdapter implements RealmModel {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isRevokeRefreshToken() {
|
||||
return realm.isRevokeRefreshToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevokeRefreshToken(boolean revokeRefreshToken) {
|
||||
realm.setRevokeRefreshToken(revokeRefreshToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSsoSessionIdleTimeout() {
|
||||
return realm.getSsoSessionIdleTimeout();
|
||||
|
|
|
@ -22,10 +22,7 @@ import org.keycloak.models.ClientModel;
|
|||
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
||||
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OTPPolicy;
|
||||
import org.keycloak.models.OfflineClientSessionModel;
|
||||
import org.keycloak.models.OfflineUserSessionModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -35,8 +32,6 @@ import org.keycloak.models.UserCredentialValueModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.entities.CredentialEntity;
|
||||
import org.keycloak.models.entities.FederatedIdentityEntity;
|
||||
import org.keycloak.models.entities.OfflineClientSessionEntity;
|
||||
import org.keycloak.models.entities.OfflineUserSessionEntity;
|
||||
import org.keycloak.models.entities.RoleEntity;
|
||||
import org.keycloak.models.entities.UserEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
@ -44,7 +39,6 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
|||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
@ -53,8 +47,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
||||
|
||||
/**
|
||||
* UserModel for JSON persistence.
|
||||
*
|
||||
|
|
|
@ -321,6 +321,18 @@ public class ClientAdapter implements ClientModel {
|
|||
updated.setName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
if (updated != null) return updated.getDescription();
|
||||
return cached.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDescription(String description) {
|
||||
getDelegateForUpdate();
|
||||
updated.setDescription(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSurrogateAuthRequired() {
|
||||
if (updated != null) return updated.isSurrogateAuthRequired();
|
||||
|
|
|
@ -4,6 +4,8 @@ import org.keycloak.models.*;
|
|||
import org.keycloak.models.cache.CacheUserProvider;
|
||||
import org.keycloak.models.cache.UserCache;
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
import org.keycloak.models.session.PersistentClientSessionModel;
|
||||
import org.keycloak.models.session.PersistentUserSessionModel;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
@ -121,7 +123,7 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
|||
if (model == null) return null;
|
||||
if (managedUsers.containsKey(id)) return managedUsers.get(id);
|
||||
if (userInvalidations.containsKey(id)) return model;
|
||||
cached = new CachedUser(this, realm, model);
|
||||
cached = new CachedUser(realm, model);
|
||||
cache.addCachedUser(realm.getId(), cached);
|
||||
} else if (managedUsers.containsKey(id)) {
|
||||
return managedUsers.get(id);
|
||||
|
@ -146,7 +148,7 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
|||
if (model == null) return null;
|
||||
if (managedUsers.containsKey(model.getId())) return managedUsers.get(model.getId());
|
||||
if (userInvalidations.containsKey(model.getId())) return model;
|
||||
cached = new CachedUser(this, realm, model);
|
||||
cached = new CachedUser(realm, model);
|
||||
cache.addCachedUser(realm.getId(), cached);
|
||||
} else if (userInvalidations.containsKey(cached.getId())) {
|
||||
return getDelegate().getUserById(cached.getId(), realm);
|
||||
|
@ -173,7 +175,7 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
|||
UserModel model = getDelegate().getUserByEmail(email, realm);
|
||||
if (model == null) return null;
|
||||
if (userInvalidations.containsKey(model.getId())) return model;
|
||||
cached = new CachedUser(this, realm, model);
|
||||
cached = new CachedUser(realm, model);
|
||||
cache.addCachedUser(realm.getId(), cached);
|
||||
} else if (userInvalidations.containsKey(cached.getId())) {
|
||||
return getDelegate().getUserByEmail(email, realm);
|
||||
|
@ -328,94 +330,4 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
|||
public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
|
||||
getDelegate().preRemove(client, protocolMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOfflineUserSession(RealmModel realm, UserModel user, OfflineUserSessionModel offlineUserSession) {
|
||||
registerUserInvalidation(realm, user.getId());
|
||||
getDelegate().addOfflineUserSession(realm, user, offlineUserSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||
if (isRegisteredForInvalidation(realm, user.getId())) {
|
||||
return getDelegate().getOfflineUserSession(realm, user, userSessionId);
|
||||
}
|
||||
|
||||
CachedUser cachedUser = cache.getCachedUser(realm.getId(), user.getId());
|
||||
if (cachedUser == null) {
|
||||
return getDelegate().getOfflineUserSession(realm, user, userSessionId);
|
||||
} else {
|
||||
return cachedUser.getOfflineUserSessions().get(userSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user) {
|
||||
if (isRegisteredForInvalidation(realm, user.getId())) {
|
||||
return getDelegate().getOfflineUserSessions(realm, user);
|
||||
}
|
||||
|
||||
CachedUser cachedUser = cache.getCachedUser(realm.getId(), user.getId());
|
||||
if (cachedUser == null) {
|
||||
return getDelegate().getOfflineUserSessions(realm, user);
|
||||
} else {
|
||||
return cachedUser.getOfflineUserSessions().values();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeOfflineUserSession(RealmModel realm, UserModel user, String userSessionId) {
|
||||
registerUserInvalidation(realm, user.getId());
|
||||
return getDelegate().removeOfflineUserSession(realm, user, userSessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel offlineClientSession) {
|
||||
registerUserInvalidation(realm, offlineClientSession.getUserId());
|
||||
getDelegate().addOfflineClientSession(realm, offlineClientSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||
if (isRegisteredForInvalidation(realm, user.getId())) {
|
||||
return getDelegate().getOfflineClientSession(realm, user, clientSessionId);
|
||||
}
|
||||
|
||||
CachedUser cachedUser = cache.getCachedUser(realm.getId(), user.getId());
|
||||
if (cachedUser == null) {
|
||||
return getDelegate().getOfflineClientSession(realm, user, clientSessionId);
|
||||
} else {
|
||||
return cachedUser.getOfflineClientSessions().get(clientSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user) {
|
||||
if (isRegisteredForInvalidation(realm, user.getId())) {
|
||||
return getDelegate().getOfflineClientSessions(realm, user);
|
||||
}
|
||||
|
||||
CachedUser cachedUser = cache.getCachedUser(realm.getId(), user.getId());
|
||||
if (cachedUser == null) {
|
||||
return getDelegate().getOfflineClientSessions(realm, user);
|
||||
} else {
|
||||
return cachedUser.getOfflineClientSessions().values();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId) {
|
||||
registerUserInvalidation(realm, user.getId());
|
||||
return getDelegate().removeOfflineClientSession(realm, user, clientSessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOfflineClientSessionsCount(RealmModel realm, ClientModel client) {
|
||||
return getDelegate().getOfflineClientSessionsCount(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
|
||||
return getDelegate().getOfflineClientSessions(realm, client, firstResult, maxResults);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -239,6 +239,18 @@ public class RealmAdapter implements RealmModel {
|
|||
updated.setEditUsernameAllowed(editUsernameAllowed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRevokeRefreshToken() {
|
||||
if (updated != null) return updated.isRevokeRefreshToken();
|
||||
return cached.isRevokeRefreshToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevokeRefreshToken(boolean revokeRefreshToken) {
|
||||
getDelegateForUpdate();
|
||||
updated.setRevokeRefreshToken(revokeRefreshToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSsoSessionIdleTimeout() {
|
||||
if (updated != null) return updated.getSsoSessionIdleTimeout();
|
||||
|
|
|
@ -25,6 +25,7 @@ public class CachedClient implements Serializable {
|
|||
private String id;
|
||||
private String clientId;
|
||||
private String name;
|
||||
private String description;
|
||||
private String realm;
|
||||
private Set<String> redirectUris = new HashSet<String>();
|
||||
private boolean enabled;
|
||||
|
@ -58,6 +59,7 @@ public class CachedClient implements Serializable {
|
|||
secret = model.getSecret();
|
||||
clientId = model.getClientId();
|
||||
name = model.getName();
|
||||
description = model.getDescription();
|
||||
this.realm = realm.getId();
|
||||
enabled = model.isEnabled();
|
||||
protocol = model.getProtocol();
|
||||
|
@ -103,6 +105,10 @@ public class CachedClient implements Serializable {
|
|||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() { return description; }
|
||||
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue