Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
99028e9cab
60 changed files with 662 additions and 43 deletions
|
@ -10,6 +10,7 @@
|
|||
|
||||
<addColumn tableName="CLIENT">
|
||||
<column name="ROOT_URL" type="VARCHAR(255)"/>
|
||||
<column name="DESCRIPTION" type="VARCHAR(255)"/>
|
||||
</addColumn>
|
||||
|
||||
<createTable tableName="OFFLINE_USER_SESSION">
|
||||
|
@ -47,5 +48,11 @@
|
|||
|
||||
<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>
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
|
||||
"connectionsInfinispan": {
|
||||
"default" : {
|
||||
"cacheContainer" : "java:jboss/infinispan/Keycloak"
|
||||
"cacheContainer" : "java:comp/env/infinispan/Keycloak"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ JBoss, Home of Professional Open Source.
|
||||
~ Copyright 2014, 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.
|
||||
-->
|
||||
|
||||
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-server-subsystem.infinispan">
|
||||
|
||||
<resources>
|
||||
<!-- Insert resources here -->
|
||||
</resources>
|
||||
|
||||
<dependencies>
|
||||
<module name="org.infinispan" export="true"/>
|
||||
</dependencies>
|
||||
</module>
|
|
@ -54,6 +54,9 @@
|
|||
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||
|
||||
<module name="org.jboss.msc"/>
|
||||
|
||||
<!-- suppress unsupported dependency 'org.infinispan:main' warning -->
|
||||
<module name="org.keycloak.keycloak-server-subsystem.infinispan"/>
|
||||
</dependencies>
|
||||
<exclusions>
|
||||
<module name="org.jboss.resteasy.resteasy-jackson2-provider"/>
|
||||
|
|
|
@ -39,4 +39,10 @@
|
|||
<servlet-name>Keycloak REST Interface</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<resource-env-ref>
|
||||
<resource-env-ref-name>infinispan/Keycloak</resource-env-ref-name>
|
||||
<resource-env-ref-type>org.infinispan.manager.EmbeddedCacheManager</resource-env-ref-type>
|
||||
<lookup-name>java:jboss/infinispan/Keycloak</lookup-name>
|
||||
</resource-env-ref>
|
||||
</web-app>
|
||||
|
|
|
@ -290,6 +290,8 @@
|
|||
|
||||
<module-def name="org.keycloak.keycloak-server-subsystem"/>
|
||||
|
||||
<module-def name="org.keycloak.keycloak-server-subsystem.infinispan"/>
|
||||
|
||||
<module-def name="org.keycloak.keycloak-wildfly-extensions">
|
||||
<maven-resource group="org.keycloak" artifact="keycloak-wildfly-extensions"/>
|
||||
</module-def>
|
||||
|
|
|
@ -54,6 +54,9 @@
|
|||
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||
|
||||
<module name="org.jboss.msc"/>
|
||||
|
||||
<!-- suppress unsupported dependency 'org.infinispan:main' warning -->
|
||||
<module name="org.keycloak.keycloak-server-subsystem.infinispan"/>
|
||||
</dependencies>
|
||||
<exclusions>
|
||||
<module name="org.jboss.resteasy.resteasy-jackson2-provider"/>
|
||||
|
|
|
@ -39,4 +39,10 @@
|
|||
<servlet-name>Keycloak REST Interface</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<resource-env-ref>
|
||||
<resource-env-ref-name>infinispan/Keycloak</resource-env-ref-name>
|
||||
<resource-env-ref-type>org.infinispan.manager.EmbeddedCacheManager</resource-env-ref-type>
|
||||
<lookup-name>java:jboss/infinispan/Keycloak</lookup-name>
|
||||
</resource-env-ref>
|
||||
</web-app>
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ JBoss, Home of Professional Open Source.
|
||||
~ Copyright 2014, 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.
|
||||
-->
|
||||
|
||||
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-server-subsystem.infinispan">
|
||||
|
||||
<resources>
|
||||
<!-- Insert resources here -->
|
||||
</resources>
|
||||
|
||||
<dependencies>
|
||||
<module name="org.infinispan" export="true"/>
|
||||
</dependencies>
|
||||
</module>
|
|
@ -20,13 +20,13 @@
|
|||
<include>**/**</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<!--<fileSet>
|
||||
<fileSet>
|
||||
<directory>cli</directory>
|
||||
<includes>
|
||||
<include>*.cli</include>
|
||||
</includes>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</fileSet>-->
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
<files>
|
||||
|
|
|
@ -5,4 +5,7 @@
|
|||
/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")
|
||||
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:add(mode="SYNC",owners="1")
|
||||
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
|
||||
/subsystem=keycloak-server:add(web-context=auth)
|
||||
:shutdown(restart=true)
|
|
@ -1,2 +1,10 @@
|
|||
/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",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()
|
||||
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
|
||||
/subsystem=keycloak-server:add(web-context=auth)
|
||||
/subsystem=keycloak-server:add(web-context=auth)
|
||||
:shutdown(restart=true)
|
|
@ -1,7 +0,0 @@
|
|||
/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",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()
|
|
@ -47,13 +47,13 @@
|
|||
</includes>
|
||||
<outputDirectory></outputDirectory>
|
||||
</fileSet>
|
||||
<!--<fileSet>
|
||||
<fileSet>
|
||||
<directory>cli</directory>
|
||||
<includes>
|
||||
<include>*.cli</include>
|
||||
</includes>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</fileSet>-->
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
<files>
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
/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")
|
||||
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:add(mode="SYNC",owners="1")
|
||||
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
|
||||
/subsystem=keycloak-server:add(web-context=auth)
|
||||
:shutdown(restart=true)
|
|
@ -1,2 +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)
|
||||
/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()
|
||||
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
|
||||
/subsystem=keycloak-server:add(web-context=auth)
|
|
@ -1,6 +0,0 @@
|
|||
/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>
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
|
||||
<section id="overlay_install">
|
||||
<title>Install on existing WildFly 9.0.0.Final</title>
|
||||
<title>Install on existing WildFly 9.0.1.Final</title>
|
||||
<para>
|
||||
Keycloak can be installed into an existing WildFly 9.0.0.Final server. To do this download
|
||||
<literal>keycloak-overlay-&project.version;.zip</literal> or <literal>keycloak-overlay-&project.version;.tar.gz</literal>.
|
||||
|
@ -59,22 +59,23 @@
|
|||
(username: <emphasis>admin</emphasis> and password: <emphasis>admin</emphasis>). Keycloak will then prompt you to
|
||||
enter in a new password.
|
||||
</para>
|
||||
<!--<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>cd <WILDFLY_HOME>/bin
|
||||
./jboss-cli.sh -c --file=keycloak-install.cli</programlisting>
|
||||
Or if you are running in clustering (HA) mode (by having used -c standalone-ha.xml) then run:
|
||||
<programlisting>cd <WILDFLY_HOME>/bin
|
||||
./jboss-cli.sh -c --file=keycloak-install-ha.cli</programlisting>
|
||||
You may see exceptions in the server log, but after restarting the server they should be gone.
|
||||
You can restart the server 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>-->
|
||||
</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>.
|
||||
Same procedure as WildFly 9.0.1.Final, but download <literal>keycloak-overlay-eap6-&project.version;.zip</literal> or <literal>keycloak-overlay-eap6-&project.version;.tar.gz</literal>.
|
||||
</para>
|
||||
</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),
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -20,6 +20,7 @@ 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;
|
||||
|
@ -63,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);
|
||||
|
@ -71,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);
|
||||
|
||||
|
@ -83,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);
|
||||
|
||||
|
@ -96,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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -91,6 +91,9 @@ public interface RealmModel extends RoleContainerModel {
|
|||
|
||||
void setResetPasswordAllowed(boolean resetPasswordAllowed);
|
||||
|
||||
boolean isRevokeRefreshToken();
|
||||
void setRevokeRefreshToken(boolean revokeRefreshToken);
|
||||
|
||||
int getSsoSessionIdleTimeout();
|
||||
void setSsoSessionIdleTimeout(int seconds);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
@ -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());
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
package org.keycloak.models.utils;
|
||||
|
||||
import org.keycloak.models.session.PersistentClientSessionModel;
|
||||
import org.keycloak.models.session.PersistentUserSessionModel;
|
||||
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());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ public class CachedRealm implements Serializable {
|
|||
private int failureFactor;
|
||||
//--- end brute force settings
|
||||
|
||||
private boolean revokeRefreshToken;
|
||||
private int ssoSessionIdleTimeout;
|
||||
private int ssoSessionMaxLifespan;
|
||||
private int accessTokenLifespan;
|
||||
|
@ -136,6 +137,7 @@ public class CachedRealm implements Serializable {
|
|||
failureFactor = model.getFailureFactor();
|
||||
//--- end brute force settings
|
||||
|
||||
revokeRefreshToken = model.isRevokeRefreshToken();
|
||||
ssoSessionIdleTimeout = model.getSsoSessionIdleTimeout();
|
||||
ssoSessionMaxLifespan = model.getSsoSessionMaxLifespan();
|
||||
accessTokenLifespan = model.getAccessTokenLifespan();
|
||||
|
@ -313,6 +315,10 @@ public class CachedRealm implements Serializable {
|
|||
return editUsernameAllowed;
|
||||
}
|
||||
|
||||
public boolean isRevokeRefreshToken() {
|
||||
return revokeRefreshToken;
|
||||
}
|
||||
|
||||
public int getSsoSessionIdleTimeout() {
|
||||
return ssoSessionIdleTimeout;
|
||||
}
|
||||
|
|
|
@ -65,6 +65,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 boolean isEnabled() {
|
||||
return entity.isEnabled();
|
||||
|
|
|
@ -336,6 +336,16 @@ public class RealmAdapter implements RealmModel {
|
|||
realm.setNotBefore(notBefore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRevokeRefreshToken() {
|
||||
return realm.isRevokeRefreshToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevokeRefreshToken(boolean revokeRefreshToken) {
|
||||
realm.setRevokeRefreshToken(revokeRefreshToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessTokenLifespan() {
|
||||
return realm.getAccessTokenLifespan();
|
||||
|
|
|
@ -34,6 +34,8 @@ public class ClientEntity {
|
|||
private String id;
|
||||
@Column(name = "NAME")
|
||||
private String name;
|
||||
@Column(name = "DESCRIPTION")
|
||||
private String description;
|
||||
@Column(name = "CLIENT_ID")
|
||||
private String clientId;
|
||||
@Column(name="ENABLED")
|
||||
|
@ -143,6 +145,14 @@ public class ClientEntity {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,8 @@ public class RealmEntity {
|
|||
@Column(name="EDIT_USERNAME_ALLOWED")
|
||||
protected boolean editUsernameAllowed;
|
||||
|
||||
@Column(name="REVOKE_REFRESH_TOKEN")
|
||||
private boolean revokeRefreshToken;
|
||||
@Column(name="SSO_IDLE_TIMEOUT")
|
||||
private int ssoSessionIdleTimeout;
|
||||
@Column(name="SSO_MAX_LIFESPAN")
|
||||
|
@ -288,6 +290,14 @@ public class RealmEntity {
|
|||
this.editUsernameAllowed = editUsernameAllowed;
|
||||
}
|
||||
|
||||
public boolean isRevokeRefreshToken() {
|
||||
return revokeRefreshToken;
|
||||
}
|
||||
|
||||
public void setRevokeRefreshToken(boolean revokeRefreshToken) {
|
||||
this.revokeRefreshToken = revokeRefreshToken;
|
||||
}
|
||||
|
||||
public int getSsoSessionIdleTimeout() {
|
||||
return ssoSessionIdleTimeout;
|
||||
}
|
||||
|
|
|
@ -70,6 +70,15 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
|
|||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() { return getMongoEntity().getDescription(); }
|
||||
|
||||
@Override
|
||||
public void setDescription(String description) {
|
||||
getMongoEntity().setDescription(description);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientId(String clientId) {
|
||||
getMongoEntity().setClientId(clientId);
|
||||
|
|
|
@ -311,6 +311,16 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRevokeRefreshToken() {
|
||||
return realm.isRevokeRefreshToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevokeRefreshToken(boolean revokeRefreshToken) {
|
||||
realm.setRevokeRefreshToken(revokeRefreshToken);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSsoSessionIdleTimeout() {
|
||||
|
|
|
@ -161,6 +161,15 @@ public class TokenManager {
|
|||
}
|
||||
|
||||
int currentTime = Time.currentTime();
|
||||
|
||||
if (realm.isRevokeRefreshToken() && !refreshToken.getType().equals(TokenUtil.TOKEN_TYPE_OFFLINE)) {
|
||||
if (refreshToken.getIssuedAt() < validation.clientSession.getTimestamp()) {
|
||||
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token");
|
||||
}
|
||||
|
||||
validation.clientSession.setTimestamp(currentTime);
|
||||
}
|
||||
|
||||
validation.userSession.setLastSessionRefresh(currentTime);
|
||||
|
||||
AccessTokenResponseBuilder responseBuilder = responseBuilder(realm, authorizedClient, event, session, validation.userSession, validation.clientSession)
|
||||
|
|
|
@ -73,6 +73,7 @@ public class ClientsResource {
|
|||
ClientRepresentation client = new ClientRepresentation();
|
||||
client.setId(clientModel.getId());
|
||||
client.setClientId(clientModel.getClientId());
|
||||
client.setDescription(clientModel.getDescription());
|
||||
rep.add(client);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
|||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -41,6 +43,7 @@ public abstract class AbstractClientTest {
|
|||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
RealmModel testRealm = manager.createRealm(REALM_NAME);
|
||||
testRealm.setEnabled(true);
|
||||
testRealm.setAccessCodeLifespanUserAction(600);
|
||||
KeycloakModelUtils.generateRealmKeys(testRealm);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -48,6 +48,7 @@ public class ClientTest extends AbstractClientTest {
|
|||
private String createClient() {
|
||||
ClientRepresentation rep = new ClientRepresentation();
|
||||
rep.setClientId("my-app");
|
||||
rep.setDescription("my-app description");
|
||||
rep.setEnabled(true);
|
||||
Response response = realm.clients().create(rep);
|
||||
response.close();
|
||||
|
@ -79,6 +80,19 @@ public class ClientTest extends AbstractClientTest {
|
|||
assertTrue(rep.isEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* See <a href="https://issues.jboss.org/browse/KEYCLOAK-1918">KEYCLOAK-1918</a>
|
||||
*/
|
||||
@Test
|
||||
public void getClientDescription() {
|
||||
|
||||
String id = createClient();
|
||||
|
||||
ClientRepresentation rep = realm.clients().get(id).toRepresentation();
|
||||
assertEquals(id, rep.getId());
|
||||
assertEquals("my-app description", rep.getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientSessions() throws Exception {
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
|
||||
|
|
|
@ -1,18 +1,37 @@
|
|||
package org.keycloak.testsuite.admin;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.forms.ResetPasswordTest;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordResetPage;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||
import org.keycloak.testsuite.rule.GreenMailRule;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import javax.mail.internet.MimeMultipart;
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import javax.ws.rs.ClientErrorException;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -27,6 +46,31 @@ import static org.junit.Assert.fail;
|
|||
*/
|
||||
public class UserTest extends AbstractClientTest {
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@Rule
|
||||
public GreenMailRule greenMail = new GreenMailRule();
|
||||
|
||||
@WebResource
|
||||
protected LoginPasswordUpdatePage passwordUpdatePage;
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
super.before();
|
||||
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
RealmModel testRealm = manager.getRealm(REALM_NAME);
|
||||
greenMail.configureRealm(testRealm);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String createUser() {
|
||||
return createUser("user1", "user1@localhost");
|
||||
}
|
||||
|
@ -396,6 +440,40 @@ public class UserTest extends AbstractClientTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendResetPasswordEmailSuccess() throws IOException, MessagingException {
|
||||
UserRepresentation userRep = new UserRepresentation();
|
||||
userRep.setEnabled(true);
|
||||
userRep.setUsername("user1");
|
||||
userRep.setEmail("user1@test.com");
|
||||
Response response = realm.users().create(userRep);
|
||||
String id = ApiUtil.getCreatedId(response);
|
||||
response.close();
|
||||
UserResource user = realm.users().get(id);
|
||||
List<String> actions = new LinkedList<>();
|
||||
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
user.executeActionsEmail("account", actions);
|
||||
|
||||
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
|
||||
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||
|
||||
String link = ResetPasswordTest.getPasswordResetEmailLink(message);
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
assertTrue(passwordUpdatePage.isCurrent());
|
||||
|
||||
passwordUpdatePage.changePassword("new-pass", "new-pass");
|
||||
|
||||
assertEquals("Your account has been updated.", driver.getTitle());
|
||||
|
||||
driver.navigate().to(link);
|
||||
|
||||
assertEquals("We're sorry...", driver.getTitle());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void sendVerifyEmail() {
|
||||
UserRepresentation userRep = new UserRepresentation();
|
||||
|
|
|
@ -630,7 +630,7 @@ public class ResetPasswordTest {
|
|||
}
|
||||
}
|
||||
|
||||
private String getPasswordResetEmailLink(MimeMessage message) throws IOException, MessagingException {
|
||||
public static String getPasswordResetEmailLink(MimeMessage message) throws IOException, MessagingException {
|
||||
Multipart multipart = (Multipart) message.getContent();
|
||||
|
||||
final String textContentType = multipart.getBodyPart(0).getContentType();
|
||||
|
|
|
@ -32,6 +32,7 @@ public class ClientModelTest extends AbstractModelTest {
|
|||
realm = realmManager.createRealm("original");
|
||||
client = realm.addClient("application");
|
||||
client.setName("Application");
|
||||
client.setDescription("Description");
|
||||
client.setBaseUrl("http://base");
|
||||
client.setManagementUrl("http://management");
|
||||
client.setClientId("app-name");
|
||||
|
@ -87,6 +88,7 @@ public class ClientModelTest extends AbstractModelTest {
|
|||
public static void assertEquals(ClientModel expected, ClientModel actual) {
|
||||
Assert.assertEquals(expected.getClientId(), actual.getClientId());
|
||||
Assert.assertEquals(expected.getName(), actual.getName());
|
||||
Assert.assertEquals(expected.getDescription(), actual.getDescription());
|
||||
Assert.assertEquals(expected.getBaseUrl(), actual.getBaseUrl());
|
||||
Assert.assertEquals(expected.getManagementUrl(), actual.getManagementUrl());
|
||||
Assert.assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles());
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.enums.SslRequired;
|
|||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
@ -181,6 +182,93 @@ public class RefreshTokenTest {
|
|||
Time.setOffset(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshTokenReuseTokenWithoutRefreshTokensRevoked() throws Exception {
|
||||
try {
|
||||
oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
Event loginEvent = events.expectLogin().assertEvent();
|
||||
|
||||
String sessionId = loginEvent.getSessionId();
|
||||
String codeId = loginEvent.getDetails().get(Details.CODE_ID);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
AccessTokenResponse response1 = oauth.doAccessTokenRequest(code, "password");
|
||||
RefreshToken refreshToken1 = oauth.verifyRefreshToken(response1.getRefreshToken());
|
||||
|
||||
events.expectCodeToToken(codeId, sessionId).assertEvent();
|
||||
|
||||
Time.setOffset(2);
|
||||
|
||||
AccessTokenResponse response2 = oauth.doRefreshTokenRequest(response1.getRefreshToken(), "password");
|
||||
Assert.assertEquals(200, response2.getStatusCode());
|
||||
|
||||
events.expectRefresh(refreshToken1.getId(), sessionId).assertEvent();
|
||||
|
||||
AccessTokenResponse response3 = oauth.doRefreshTokenRequest(response1.getRefreshToken(), "password");
|
||||
|
||||
Assert.assertEquals(200, response3.getStatusCode());
|
||||
|
||||
events.expectRefresh(refreshToken1.getId(), sessionId).assertEvent();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshTokenReuseTokenWithRefreshTokensRevoked() throws Exception {
|
||||
try {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setRevokeRefreshToken(true);
|
||||
}
|
||||
});
|
||||
|
||||
oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
Event loginEvent = events.expectLogin().assertEvent();
|
||||
|
||||
String sessionId = loginEvent.getSessionId();
|
||||
String codeId = loginEvent.getDetails().get(Details.CODE_ID);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
AccessTokenResponse response1 = oauth.doAccessTokenRequest(code, "password");
|
||||
RefreshToken refreshToken1 = oauth.verifyRefreshToken(response1.getRefreshToken());
|
||||
|
||||
events.expectCodeToToken(codeId, sessionId).assertEvent();
|
||||
|
||||
Time.setOffset(2);
|
||||
|
||||
AccessTokenResponse response2 = oauth.doRefreshTokenRequest(response1.getRefreshToken(), "password");
|
||||
RefreshToken refreshToken2 = oauth.verifyRefreshToken(response2.getRefreshToken());
|
||||
|
||||
Assert.assertEquals(200, response2.getStatusCode());
|
||||
|
||||
events.expectRefresh(refreshToken1.getId(), sessionId).assertEvent();
|
||||
|
||||
AccessTokenResponse response3 = oauth.doRefreshTokenRequest(response1.getRefreshToken(), "password");
|
||||
|
||||
Assert.assertEquals(400, response3.getStatusCode());
|
||||
|
||||
events.expectRefresh(refreshToken1.getId(), sessionId).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
|
||||
oauth.doRefreshTokenRequest(response2.getRefreshToken(), "password");
|
||||
|
||||
events.expectRefresh(refreshToken2.getId(), sessionId).assertEvent();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setRevokeRefreshToken(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
PrivateKey privateKey;
|
||||
PublicKey publicKey;
|
||||
|
||||
|
|
|
@ -11,10 +11,7 @@ import org.junit.Test;
|
|||
import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.RefreshToken;
|
||||
import org.keycloak.services.managers.ClientManager;
|
||||
|
@ -24,6 +21,7 @@ import org.keycloak.testsuite.OAuthClient;
|
|||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.keycloak.util.Time;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -233,7 +231,47 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void grantAccessTokenExpiredPassword() throws Exception {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)"));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
Time.setOffset(60 * 60 * 48);
|
||||
|
||||
oauth.clientId("resource-owner");
|
||||
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "test-user@localhost", "password");
|
||||
|
||||
assertEquals(400, response.getStatusCode());
|
||||
|
||||
assertEquals("invalid_grant", response.getError());
|
||||
assertEquals("Account is not fully set up", response.getErrorDescription());
|
||||
|
||||
events.expectLogin()
|
||||
.client("resource-owner")
|
||||
.session((String) null)
|
||||
.clearDetails()
|
||||
.error(Errors.RESOLVE_REQUIRED_ACTIONS)
|
||||
.user((String) null)
|
||||
.assertEvent();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy(""));
|
||||
UserModel user = manager.getSession().users().getUserByEmail("test-user@localhost", appRealm);
|
||||
user.removeRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
|
|
|
@ -24,10 +24,13 @@ package org.keycloak.testsuite.rule;
|
|||
import com.icegreen.greenmail.util.GreenMail;
|
||||
import com.icegreen.greenmail.util.ServerSetup;
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.net.SocketException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -63,6 +66,14 @@ public class GreenMailRule extends ExternalResource {
|
|||
}
|
||||
}
|
||||
|
||||
public void configureRealm(RealmModel realm) {
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("from", "auto@keycloak.org");
|
||||
config.put("host", "localhost");
|
||||
config.put("port", "3025");
|
||||
realm.setSmtpConfig(config);
|
||||
}
|
||||
|
||||
public MimeMessage[] getReceivedMessages() {
|
||||
return greenMail.getReceivedMessages();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue