commit
07e12f3b5f
71 changed files with 476 additions and 89 deletions
|
@ -23,4 +23,19 @@ public class CollectionUtil {
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return true if all items from col1 are in col2 and viceversa. Order is not taken into account
|
||||||
|
public static <T> boolean collectionEquals(Collection<T> col1, Collection<T> col2) {
|
||||||
|
if (col1.size() != col2.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (T item : col1) {
|
||||||
|
if (!col2.contains(item)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
<addColumn tableName="IDENTITY_PROVIDER">
|
<addColumn tableName="IDENTITY_PROVIDER">
|
||||||
<column name="FIRST_BROKER_LOGIN_FLOW_ID" type="VARCHAR(36)">
|
<column name="FIRST_BROKER_LOGIN_FLOW_ID" type="VARCHAR(36)">
|
||||||
<constraints nullable="false"/>
|
<constraints nullable="true"/>
|
||||||
</column>
|
</column>
|
||||||
</addColumn>
|
</addColumn>
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@
|
||||||
<column name="ACCESS_TOKEN_LIFE_IMPLICIT" type="INT" defaultValueNumeric="0"/>
|
<column name="ACCESS_TOKEN_LIFE_IMPLICIT" type="INT" defaultValueNumeric="0"/>
|
||||||
</addColumn>
|
</addColumn>
|
||||||
|
|
||||||
|
<dropDefaultValue tableName="IDENTITY_PROVIDER" columnName="UPDATE_PROFILE_FIRST_LGN_MD" />
|
||||||
<dropColumn tableName="IDENTITY_PROVIDER" columnName="UPDATE_PROFILE_FIRST_LGN_MD"/>
|
<dropColumn tableName="IDENTITY_PROVIDER" columnName="UPDATE_PROFILE_FIRST_LGN_MD"/>
|
||||||
|
|
||||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_GROUP" tableName="KEYCLOAK_GROUP"/>
|
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_GROUP" tableName="KEYCLOAK_GROUP"/>
|
||||||
|
@ -83,13 +84,14 @@
|
||||||
<column name="IMPLICIT_FLOW_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
<column name="IMPLICIT_FLOW_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||||
<constraints nullable="false"/>
|
<constraints nullable="false"/>
|
||||||
</column>
|
</column>
|
||||||
<column name="DIRECT_ACCESS_GRANTS_ENABLED" type="BOOLEAN" defaultValueBoolean="true">
|
<column name="DIRECT_ACCESS_GRANTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||||
<constraints nullable="false"/>
|
<constraints nullable="false"/>
|
||||||
</column>
|
</column>
|
||||||
</addColumn>
|
</addColumn>
|
||||||
|
|
||||||
<update tableName="CLIENT">
|
<update tableName="CLIENT">
|
||||||
<column name="STANDARD_FLOW_ENABLED" valueBoolean="false"/>
|
<column name="STANDARD_FLOW_ENABLED" valueBoolean="false"/>
|
||||||
|
<column name="DIRECT_ACCESS_GRANTS_ENABLED" valueBoolean="true"/>
|
||||||
<where>DIRECT_GRANTS_ONLY = :value</where>
|
<where>DIRECT_GRANTS_ONLY = :value</where>
|
||||||
<whereParams>
|
<whereParams>
|
||||||
<param valueBoolean="true" />
|
<param valueBoolean="true" />
|
||||||
|
|
|
@ -27,7 +27,7 @@ public class Update1_7_0 extends Update {
|
||||||
boolean directGrantsOnly = client.getBoolean("directGrantsOnly", false);
|
boolean directGrantsOnly = client.getBoolean("directGrantsOnly", false);
|
||||||
client.append("standardFlowEnabled", !directGrantsOnly);
|
client.append("standardFlowEnabled", !directGrantsOnly);
|
||||||
client.append("implicitFlowEnabled", false);
|
client.append("implicitFlowEnabled", false);
|
||||||
client.append("directAccessGrantsEnabled", true);
|
client.append("directAccessGrantsEnabled", directGrantsOnly);
|
||||||
client.removeField("directGrantsOnly");
|
client.removeField("directGrantsOnly");
|
||||||
|
|
||||||
clients.save(client);
|
clients.save(client);
|
||||||
|
|
|
@ -27,6 +27,8 @@ public interface OAuth2Constants {
|
||||||
|
|
||||||
String AUTHORIZATION_CODE = "authorization_code";
|
String AUTHORIZATION_CODE = "authorization_code";
|
||||||
|
|
||||||
|
String IMPLICIT = "implicit";
|
||||||
|
|
||||||
String PASSWORD = "password";
|
String PASSWORD = "password";
|
||||||
|
|
||||||
String CLIENT_CREDENTIALS = "client_credentials";
|
String CLIENT_CREDENTIALS = "client_credentials";
|
||||||
|
|
|
@ -30,6 +30,7 @@ public class ClientRepresentation {
|
||||||
protected Boolean implicitFlowEnabled;
|
protected Boolean implicitFlowEnabled;
|
||||||
protected Boolean directAccessGrantsEnabled;
|
protected Boolean directAccessGrantsEnabled;
|
||||||
protected Boolean serviceAccountsEnabled;
|
protected Boolean serviceAccountsEnabled;
|
||||||
|
@Deprecated
|
||||||
protected Boolean directGrantsOnly;
|
protected Boolean directGrantsOnly;
|
||||||
protected Boolean publicClient;
|
protected Boolean publicClient;
|
||||||
protected Boolean frontchannelLogout;
|
protected Boolean frontchannelLogout;
|
||||||
|
@ -216,6 +217,7 @@ public class ClientRepresentation {
|
||||||
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public Boolean isDirectGrantsOnly() {
|
public Boolean isDirectGrantsOnly() {
|
||||||
return directGrantsOnly;
|
return directGrantsOnly;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,9 @@ public class OIDCClientRepresentation {
|
||||||
|
|
||||||
private String token_endpoint_auth_method;
|
private String token_endpoint_auth_method;
|
||||||
|
|
||||||
private String grant_types;
|
private List<String> grant_types;
|
||||||
|
|
||||||
private String response_types;
|
private List<String> response_types;
|
||||||
|
|
||||||
private String client_id;
|
private String client_id;
|
||||||
|
|
||||||
|
@ -68,19 +68,19 @@ public class OIDCClientRepresentation {
|
||||||
this.token_endpoint_auth_method = token_endpoint_auth_method;
|
this.token_endpoint_auth_method = token_endpoint_auth_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGrantTypes() {
|
public List<String> getGrantTypes() {
|
||||||
return grant_types;
|
return grant_types;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGrantTypes(String grantTypes) {
|
public void setGrantTypes(List<String> grantTypes) {
|
||||||
this.grant_types = grantTypes;
|
this.grant_types = grantTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getResponseTypes() {
|
public List<String> getResponseTypes() {
|
||||||
return response_types;
|
return response_types;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResponseTypes(String responseTypes) {
|
public void setResponseTypes(List<String> responseTypes) {
|
||||||
this.response_types = responseTypes;
|
this.response_types = responseTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,11 +82,24 @@
|
||||||
<section>
|
<section>
|
||||||
<title>Migrating to 1.7.0.CR1</title>
|
<title>Migrating to 1.7.0.CR1</title>
|
||||||
<simplesect>
|
<simplesect>
|
||||||
<title>Option 'Update Profile On First Login' moved from Identity provider to Review Profile authenticator</title>
|
<title>Direct access grants disabled by default for clients</title>
|
||||||
<para>
|
<para>
|
||||||
form-error-page in web.xml will no longer work for client adapter authentication errors. You must define an error-page for
|
In order to add more compliance with OpenID Connect specification, we added flags into admin console to Client Settings page,
|
||||||
the the various HTTP error codes. See documentation for more details if you want to catch and handle adapter error conditions.
|
where you can enable/disable various kinds of OpenID Connect/OAuth2 flows (Standard flow, Implicit flow, Direct Access Grants, Service Accounts).
|
||||||
|
As part of this, we have <literal>Direct Access Grants</literal> (corresponds to OAuth2 <literal>Resource Owner Password Credentials Grant</literal>) disabled by default for new clients.
|
||||||
</para>
|
</para>
|
||||||
|
<para>
|
||||||
|
Clients migrated from previous version have <literal>Direct Access Grants</literal> enabled just if they had flag <literal>Direct Grants Only</literal> on. The
|
||||||
|
<literal>Direct Grants Only</literal> flag was removed as if you enable Direct Access Grants and disable both Standard+Implicit flow, you will achieve same effect.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
We also added builtin client <literal>admin-cli</literal> to each realm. This client has <literal>Direct Access Grants</literal> enabled.
|
||||||
|
So if you're using Admin REST API or Keycloak admin-client, you should update your configuration to use <literal>admin-cli</literal> instead
|
||||||
|
of <literal>security-admin-console</literal> as the latter one doesn't have direct access grants enabled anymore by default.
|
||||||
|
</para>
|
||||||
|
</simplesect>
|
||||||
|
<simplesect>
|
||||||
|
<title>Option 'Update Profile On First Login' moved from Identity provider to Review Profile authenticator</title>
|
||||||
<para>
|
<para>
|
||||||
In this version, we added <literal>First Broker Login</literal>, which allows you to specify what exactly should be done
|
In this version, we added <literal>First Broker Login</literal>, which allows you to specify what exactly should be done
|
||||||
when new user is logged through Identity provider (or Social provider), but there is no existing Keycloak user
|
when new user is logged through Identity provider (or Social provider), but there is no existing Keycloak user
|
||||||
|
@ -100,6 +113,13 @@
|
||||||
and then you configure the option under <literal>Review Profile</literal> authenticator.
|
and then you configure the option under <literal>Review Profile</literal> authenticator.
|
||||||
</para>
|
</para>
|
||||||
</simplesect>
|
</simplesect>
|
||||||
|
<simplesect>
|
||||||
|
<title>Element 'form-error-page' in web.xml not supported anymore</title>
|
||||||
|
<para>
|
||||||
|
form-error-page in web.xml will no longer work for client adapter authentication errors. You must define an error-page for
|
||||||
|
the various HTTP error codes. See documentation for more details if you want to catch and handle adapter error conditions.
|
||||||
|
</para>
|
||||||
|
</simplesect>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>Migrating to 1.6.0.Final</title>
|
<title>Migrating to 1.6.0.Final</title>
|
||||||
|
|
|
@ -12,6 +12,7 @@ public interface Details {
|
||||||
String REDIRECT_URI = "redirect_uri";
|
String REDIRECT_URI = "redirect_uri";
|
||||||
String RESPONSE_TYPE = "response_type";
|
String RESPONSE_TYPE = "response_type";
|
||||||
String RESPONSE_MODE = "response_mode";
|
String RESPONSE_MODE = "response_mode";
|
||||||
|
String GRANT_TYPE = "grant_type";
|
||||||
String AUTH_TYPE = "auth_type";
|
String AUTH_TYPE = "auth_type";
|
||||||
String AUTH_METHOD = "auth_method";
|
String AUTH_METHOD = "auth_method";
|
||||||
String IDENTITY_PROVIDER = "identity_provider";
|
String IDENTITY_PROVIDER = "identity_provider";
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"clientId": "examples-admin-client",
|
"clientId": "examples-admin-client",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"fullScopeAllowed": true,
|
"fullScopeAllowed": true,
|
||||||
"baseUrl": "/examples-admin-client",
|
"baseUrl": "/examples-admin-client",
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"clientId": "basic-auth-service",
|
"clientId": "basic-auth-service",
|
||||||
|
"standardFlowEnabled": false,
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"adminUrl": "/basicauth",
|
"adminUrl": "/basicauth",
|
||||||
"baseUrl": "/basicauth",
|
"baseUrl": "/basicauth",
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"fullScopeAllowed": true,
|
"fullScopeAllowed": true,
|
||||||
"baseUrl": "/admin-client",
|
"baseUrl": "/admin-client",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"/admin-client/*"
|
"/admin-client/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -178,7 +178,8 @@
|
||||||
"clientId": "admin-client",
|
"clientId": "admin-client",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"publicClient": true,
|
"publicClient": true,
|
||||||
"directGrantsOnly": true
|
"standardFlowEnabled": false,
|
||||||
|
"directAccessGrantsEnabled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"clientId": "product-sa-client",
|
"clientId": "product-sa-client",
|
||||||
|
|
|
@ -182,7 +182,8 @@
|
||||||
"clientId": "ssh-jmx-admin-client",
|
"clientId": "ssh-jmx-admin-client",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"publicClient": false,
|
"publicClient": false,
|
||||||
"directGrantsOnly": true,
|
"standardFlowEnabled": false,
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"secret": "password"
|
"secret": "password"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -55,6 +55,7 @@ role_read-token=Read token
|
||||||
role_offline-access=Offline access
|
role_offline-access=Offline access
|
||||||
client_account=Account
|
client_account=Account
|
||||||
client_security-admin-console=Security Admin Console
|
client_security-admin-console=Security Admin Console
|
||||||
|
client_admin-cli=Admin CLI
|
||||||
client_realm-management=Realm Management
|
client_realm-management=Realm Management
|
||||||
client_broker=Broker
|
client_broker=Broker
|
||||||
|
|
||||||
|
|
|
@ -865,7 +865,6 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se
|
||||||
$scope.client = {
|
$scope.client = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
standardFlowEnabled: true,
|
standardFlowEnabled: true,
|
||||||
directAccessGrantsEnabled: true,
|
|
||||||
attributes: {}
|
attributes: {}
|
||||||
};
|
};
|
||||||
$scope.client.attributes['saml_signature_canonicalization_method'] = $scope.canonicalization[0].value;
|
$scope.client.attributes['saml_signature_canonicalization_method'] = $scope.canonicalization[0].value;
|
||||||
|
|
|
@ -113,6 +113,7 @@ role_read-token=Read token
|
||||||
role_offline-access=Offline access
|
role_offline-access=Offline access
|
||||||
client_account=Account
|
client_account=Account
|
||||||
client_security-admin-console=Security Admin Console
|
client_security-admin-console=Security Admin Console
|
||||||
|
client_admin-cli=Admin CLI
|
||||||
client_realm-management=Realm Management
|
client_realm-management=Realm Management
|
||||||
client_broker=Broker
|
client_broker=Broker
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.migration;
|
package org.keycloak.migration;
|
||||||
|
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
|
|
||||||
|
@ -21,4 +22,6 @@ public interface MigrationProvider extends Provider {
|
||||||
|
|
||||||
List<ProtocolMapperModel> getBuiltinMappers(String protocol);
|
List<ProtocolMapperModel> getBuiltinMappers(String protocol);
|
||||||
|
|
||||||
|
void setupAdminCli(RealmModel realm);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,14 @@ package org.keycloak.migration.migrators;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.keycloak.migration.MigrationProvider;
|
||||||
import org.keycloak.migration.ModelVersion;
|
import org.keycloak.migration.ModelVersion;
|
||||||
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
@ -17,7 +21,24 @@ public class MigrateTo1_7_0 {
|
||||||
public void migrate(KeycloakSession session) {
|
public void migrate(KeycloakSession session) {
|
||||||
List<RealmModel> realms = session.realms().getRealms();
|
List<RealmModel> realms = session.realms().getRealms();
|
||||||
for (RealmModel realm : realms) {
|
for (RealmModel realm : realms) {
|
||||||
|
// Set default accessToken timeout for implicit flow
|
||||||
realm.setAccessTokenLifespanForImplicitFlow(Constants.DEFAULT_ACCESS_TOKEN_LIFESPAN_FOR_IMPLICIT_FLOW_TIMEOUT);
|
realm.setAccessTokenLifespanForImplicitFlow(Constants.DEFAULT_ACCESS_TOKEN_LIFESPAN_FOR_IMPLICIT_FLOW_TIMEOUT);
|
||||||
|
|
||||||
|
// Add 'admin-cli' builtin client
|
||||||
|
MigrationProvider migrationProvider = session.getProvider(MigrationProvider.class);
|
||||||
|
migrationProvider.setupAdminCli(realm);
|
||||||
|
|
||||||
|
// add firstBrokerLogin flow and set it to all identityProviders
|
||||||
|
DefaultAuthenticationFlows.migrateFlows(realm);
|
||||||
|
AuthenticationFlowModel firstBrokerLoginFlow = realm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW);
|
||||||
|
|
||||||
|
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||||
|
for (IdentityProviderModel identityProvider : identityProviders) {
|
||||||
|
if (identityProvider.getFirstBrokerLoginFlowId() == null) {
|
||||||
|
identityProvider.setFirstBrokerLoginFlowId(firstBrokerLoginFlow.getId());
|
||||||
|
realm.updateIdentityProvider(identityProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.keycloak.OAuth2Constants;
|
||||||
*/
|
*/
|
||||||
public interface Constants {
|
public interface Constants {
|
||||||
String ADMIN_CONSOLE_CLIENT_ID = "security-admin-console";
|
String ADMIN_CONSOLE_CLIENT_ID = "security-admin-console";
|
||||||
|
String ADMIN_CLI_CLIENT_ID = "admin-cli";
|
||||||
|
|
||||||
String ACCOUNT_MANAGEMENT_CLIENT_ID = "account";
|
String ACCOUNT_MANAGEMENT_CLIENT_ID = "account";
|
||||||
String IMPERSONATION_SERVICE_CLIENT_ID = "impersonation";
|
String IMPERSONATION_SERVICE_CLIENT_ID = "impersonation";
|
||||||
|
|
|
@ -34,7 +34,6 @@ public class ClientEntity extends AbstractIdentifiableEntity {
|
||||||
private boolean implicitFlowEnabled;
|
private boolean implicitFlowEnabled;
|
||||||
private boolean directAccessGrantsEnabled;
|
private boolean directAccessGrantsEnabled;
|
||||||
private boolean serviceAccountsEnabled;
|
private boolean serviceAccountsEnabled;
|
||||||
private boolean directGrantsOnly;
|
|
||||||
private int nodeReRegistrationTimeout;
|
private int nodeReRegistrationTimeout;
|
||||||
|
|
||||||
// We are using names of defaultRoles (not ids)
|
// We are using names of defaultRoles (not ids)
|
||||||
|
@ -278,14 +277,6 @@ public class ClientEntity extends AbstractIdentifiableEntity {
|
||||||
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDirectGrantsOnly() {
|
|
||||||
return directGrantsOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDirectGrantsOnly(boolean directGrantsOnly) {
|
|
||||||
this.directGrantsOnly = directGrantsOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getDefaultRoles() {
|
public List<String> getDefaultRoles() {
|
||||||
return defaultRoles;
|
return defaultRoles;
|
||||||
}
|
}
|
||||||
|
|
|
@ -460,6 +460,10 @@ public class RepresentationToModel {
|
||||||
newRealm.setClientAuthenticationFlow(newRealm.getFlowByAlias(rep.getClientAuthenticationFlow()));
|
newRealm.setClientAuthenticationFlow(newRealm.getFlowByAlias(rep.getClientAuthenticationFlow()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Added in 1.7
|
||||||
|
if (newRealm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW) == null) {
|
||||||
|
DefaultAuthenticationFlows.firstBrokerLoginFlow(newRealm, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void convertDeprecatedSocialProviders(RealmRepresentation rep) {
|
private static void convertDeprecatedSocialProviders(RealmRepresentation rep) {
|
||||||
|
@ -776,17 +780,19 @@ public class RepresentationToModel {
|
||||||
if (resourceRep.getBaseUrl() != null) client.setBaseUrl(resourceRep.getBaseUrl());
|
if (resourceRep.getBaseUrl() != null) client.setBaseUrl(resourceRep.getBaseUrl());
|
||||||
if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly());
|
if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly());
|
||||||
if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired());
|
if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired());
|
||||||
if (resourceRep.isStandardFlowEnabled() != null) client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled());
|
|
||||||
if (resourceRep.isImplicitFlowEnabled() != null) client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled());
|
|
||||||
if (resourceRep.isDirectAccessGrantsEnabled() != null) client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled());
|
|
||||||
if (resourceRep.isServiceAccountsEnabled() != null) client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled());
|
|
||||||
|
|
||||||
// Backwards compatibility only
|
// Backwards compatibility only
|
||||||
if (resourceRep.isDirectGrantsOnly() != null) {
|
if (resourceRep.isDirectGrantsOnly() != null) {
|
||||||
logger.warn("Using deprecated 'directGrantsOnly' configuration in JSON representation. It will be removed in future versions");
|
logger.warn("Using deprecated 'directGrantsOnly' configuration in JSON representation. It will be removed in future versions");
|
||||||
client.setStandardFlowEnabled(!resourceRep.isDirectGrantsOnly());
|
client.setStandardFlowEnabled(!resourceRep.isDirectGrantsOnly());
|
||||||
|
client.setDirectAccessGrantsEnabled(resourceRep.isDirectGrantsOnly());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (resourceRep.isStandardFlowEnabled() != null) client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled());
|
||||||
|
if (resourceRep.isImplicitFlowEnabled() != null) client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled());
|
||||||
|
if (resourceRep.isDirectAccessGrantsEnabled() != null) client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled());
|
||||||
|
if (resourceRep.isServiceAccountsEnabled() != null) client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled());
|
||||||
|
|
||||||
if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient());
|
if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient());
|
||||||
if (resourceRep.isFrontchannelLogout() != null) client.setFrontchannelLogout(resourceRep.isFrontchannelLogout());
|
if (resourceRep.isFrontchannelLogout() != null) client.setFrontchannelLogout(resourceRep.isFrontchannelLogout());
|
||||||
if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol());
|
if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol());
|
||||||
|
|
|
@ -726,7 +726,6 @@ public class RealmAdapter implements RealmModel {
|
||||||
entity.setClientId(clientId);
|
entity.setClientId(clientId);
|
||||||
entity.setEnabled(true);
|
entity.setEnabled(true);
|
||||||
entity.setStandardFlowEnabled(true);
|
entity.setStandardFlowEnabled(true);
|
||||||
entity.setDirectAccessGrantsEnabled(true);
|
|
||||||
entity.setRealm(realm);
|
entity.setRealm(realm);
|
||||||
realm.getClients().add(entity);
|
realm.getClients().add(entity);
|
||||||
em.persist(entity);
|
em.persist(entity);
|
||||||
|
|
|
@ -811,7 +811,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
clientEntity.setRealmId(getId());
|
clientEntity.setRealmId(getId());
|
||||||
clientEntity.setEnabled(true);
|
clientEntity.setEnabled(true);
|
||||||
clientEntity.setStandardFlowEnabled(true);
|
clientEntity.setStandardFlowEnabled(true);
|
||||||
clientEntity.setDirectAccessGrantsEnabled(true);
|
|
||||||
getMongoStore().insertEntity(clientEntity, invocationContext);
|
getMongoStore().insertEntity(clientEntity, invocationContext);
|
||||||
|
|
||||||
final ClientModel model = new ClientAdapter(session, this, clientEntity, invocationContext);
|
final ClientModel model = new ClientAdapter(session, this, clientEntity, invocationContext);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.protocol.saml.EntityDescriptorDescriptionConverter;
|
import org.keycloak.protocol.saml.EntityDescriptorDescriptionConverter;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.services.clientregistration.AbstractClientRegistrationProvider;
|
import org.keycloak.services.clientregistration.AbstractClientRegistrationProvider;
|
||||||
|
import org.keycloak.services.clientregistration.ClientRegistrationException;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
|
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
|
||||||
|
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
||||||
import org.keycloak.services.clientregistration.ClientRegistrationService;
|
import org.keycloak.services.clientregistration.ClientRegistrationService;
|
||||||
import org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory;
|
import org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory;
|
||||||
import org.keycloak.services.resources.RealmsResource;
|
import org.keycloak.services.resources.RealmsResource;
|
||||||
|
@ -22,13 +23,13 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
|
||||||
|
|
||||||
public static final List<String> DEFAULT_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = list("RS256");
|
public static final List<String> DEFAULT_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = list("RS256");
|
||||||
|
|
||||||
public static final List<String> DEFAULT_GRANT_TYPES_SUPPORTED = list(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD, OAuth2Constants.CLIENT_CREDENTIALS);
|
public static final List<String> DEFAULT_GRANT_TYPES_SUPPORTED = list(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD, OAuth2Constants.CLIENT_CREDENTIALS);
|
||||||
|
|
||||||
public static final List<String> DEFAULT_RESPONSE_TYPES_SUPPORTED = list(OAuth2Constants.CODE);
|
public static final List<String> DEFAULT_RESPONSE_TYPES_SUPPORTED = list(OAuth2Constants.CODE, OIDCResponseType.NONE, OIDCResponseType.ID_TOKEN, "id_token token", "code id_token", "code token", "code id_token token");
|
||||||
|
|
||||||
public static final List<String> DEFAULT_SUBJECT_TYPES_SUPPORTED = list("public");
|
public static final List<String> DEFAULT_SUBJECT_TYPES_SUPPORTED = list("public");
|
||||||
|
|
||||||
public static final List<String> DEFAULT_RESPONSE_MODES_SUPPORTED = list("query");
|
public static final List<String> DEFAULT_RESPONSE_MODES_SUPPORTED = list("query", "fragment", "form_post");
|
||||||
|
|
||||||
private KeycloakSession session;
|
private KeycloakSession session;
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,8 @@ public class TokenEndpoint {
|
||||||
} else {
|
} else {
|
||||||
throw new ErrorResponseException(Errors.INVALID_REQUEST, "Invalid " + OIDCLoginProtocol.GRANT_TYPE_PARAM, Response.Status.BAD_REQUEST);
|
throw new ErrorResponseException(Errors.INVALID_REQUEST, "Invalid " + OIDCLoginProtocol.GRANT_TYPE_PARAM, Response.Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event.detail(Details.GRANT_TYPE, grantType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response buildAuthorizationCodeAccessTokenResponse() {
|
public Response buildAuthorizationCodeAccessTokenResponse() {
|
||||||
|
@ -223,6 +225,11 @@ public class TokenEndpoint {
|
||||||
throw new ErrorResponseException("invalid_grant", "Auth error", Response.Status.BAD_REQUEST);
|
throw new ErrorResponseException("invalid_grant", "Auth error", Response.Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!client.isStandardFlowEnabled()) {
|
||||||
|
event.error(Errors.NOT_ALLOWED);
|
||||||
|
throw new ErrorResponseException("invalid_grant", "Client not allowed to exchange code", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
UserModel user = session.users().getUserById(userSession.getUser().getId(), realm);
|
UserModel user = session.users().getUserById(userSession.getUser().getId(), realm);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
event.error(Errors.USER_NOT_FOUND);
|
event.error(Errors.USER_NOT_FOUND);
|
||||||
|
@ -327,7 +334,12 @@ public class TokenEndpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response buildResourceOwnerPasswordCredentialsGrant() {
|
public Response buildResourceOwnerPasswordCredentialsGrant() {
|
||||||
event.detail(Details.AUTH_METHOD, "oauth_credentials").detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD);
|
event.detail(Details.AUTH_METHOD, "oauth_credentials");
|
||||||
|
|
||||||
|
if (!client.isDirectAccessGrantsEnabled()) {
|
||||||
|
event.error(Errors.NOT_ALLOWED);
|
||||||
|
throw new ErrorResponseException("invalid_grant", "Client not allowed for direct access grants", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
if (client.isConsentRequired()) {
|
if (client.isConsentRequired()) {
|
||||||
event.error(Errors.CONSENT_DENIED);
|
event.error(Errors.CONSENT_DENIED);
|
||||||
|
@ -393,8 +405,6 @@ public class TokenEndpoint {
|
||||||
throw new ErrorResponseException("unauthorized_client", "Client not enabled to retrieve service account", Response.Status.UNAUTHORIZED);
|
throw new ErrorResponseException("unauthorized_client", "Client not enabled to retrieve service account", Response.Status.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
event.detail(Details.RESPONSE_TYPE, OAuth2Constants.CLIENT_CREDENTIALS);
|
|
||||||
|
|
||||||
UserModel clientUser = session.users().getUserByServiceAccountClient(client);
|
UserModel clientUser = session.users().getUserByServiceAccountClient(client);
|
||||||
|
|
||||||
if (clientUser == null || client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER) == null) {
|
if (clientUser == null || client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER) == null) {
|
||||||
|
|
|
@ -42,7 +42,7 @@ public abstract class OIDCRedirectUriBuilder {
|
||||||
|
|
||||||
|
|
||||||
// http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes
|
// http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes
|
||||||
public static class QueryRedirectUriBuilder extends OIDCRedirectUriBuilder {
|
private static class QueryRedirectUriBuilder extends OIDCRedirectUriBuilder {
|
||||||
|
|
||||||
protected QueryRedirectUriBuilder(KeycloakUriBuilder uriBuilder) {
|
protected QueryRedirectUriBuilder(KeycloakUriBuilder uriBuilder) {
|
||||||
super(uriBuilder);
|
super(uriBuilder);
|
||||||
|
@ -64,7 +64,7 @@ public abstract class OIDCRedirectUriBuilder {
|
||||||
|
|
||||||
|
|
||||||
// http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes
|
// http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes
|
||||||
public static class FragmentRedirectUriBuilder extends OIDCRedirectUriBuilder {
|
private static class FragmentRedirectUriBuilder extends OIDCRedirectUriBuilder {
|
||||||
|
|
||||||
private StringBuilder fragment;
|
private StringBuilder fragment;
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ public abstract class OIDCRedirectUriBuilder {
|
||||||
|
|
||||||
|
|
||||||
// http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
|
// http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
|
||||||
public static class FormPostRedirectUriBuilder extends OIDCRedirectUriBuilder {
|
private static class FormPostRedirectUriBuilder extends OIDCRedirectUriBuilder {
|
||||||
|
|
||||||
private Map<String, String> params = new HashMap<>();
|
private Map<String, String> params = new HashMap<>();
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,16 @@ public class OIDCResponseType {
|
||||||
return new OIDCResponseType(allowedTypes);
|
return new OIDCResponseType(allowedTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static OIDCResponseType parse(List<String> responseTypes) {
|
||||||
|
OIDCResponseType result = new OIDCResponseType(new ArrayList<String>());
|
||||||
|
for (String respType : responseTypes) {
|
||||||
|
OIDCResponseType responseType = parse(respType);
|
||||||
|
result.responseTypes.addAll(responseType.responseTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static void validateAllowedTypes(List<String> responseTypes) {
|
private static void validateAllowedTypes(List<String> responseTypes) {
|
||||||
if (responseTypes.size() == 0) {
|
if (responseTypes.size() == 0) {
|
||||||
throw new IllegalStateException("No responseType provided");
|
throw new IllegalStateException("No responseType provided");
|
||||||
|
@ -53,9 +63,6 @@ public class OIDCResponseType {
|
||||||
if (responseTypes.contains(NONE) && responseTypes.size() > 1) {
|
if (responseTypes.contains(NONE) && responseTypes.size() > 1) {
|
||||||
throw new IllegalArgumentException("None not allowed with some other response_type");
|
throw new IllegalArgumentException("None not allowed with some other response_type");
|
||||||
}
|
}
|
||||||
if (responseTypes.contains(ID_TOKEN) && responseTypes.size() == 1) {
|
|
||||||
throw new IllegalArgumentException("Not supported to use response_type=id_token alone");
|
|
||||||
}
|
|
||||||
if (responseTypes.contains(TOKEN) && responseTypes.size() == 1) {
|
if (responseTypes.contains(TOKEN) && responseTypes.size() == 1) {
|
||||||
throw new IllegalArgumentException("Not supported to use response_type=token alone");
|
throw new IllegalArgumentException("Not supported to use response_type=token alone");
|
||||||
}
|
}
|
||||||
|
@ -72,7 +79,7 @@ public class OIDCResponseType {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isImplicitFlow() {
|
public boolean isImplicitFlow() {
|
||||||
return hasResponseType(TOKEN) && hasResponseType(ID_TOKEN) && !hasResponseType(CODE);
|
return hasResponseType(ID_TOKEN) && !hasResponseType(CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.keycloak.services.clientregistration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class ClientRegistrationException extends RuntimeException {
|
||||||
|
|
||||||
|
public ClientRegistrationException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRegistrationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRegistrationException(Throwable throwable) {
|
||||||
|
super(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRegistrationException(String message, Throwable throwable) {
|
||||||
|
super(message, throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,48 @@
|
||||||
package org.keycloak.services.clientregistration.oidc;
|
package org.keycloak.services.clientregistration.oidc;
|
||||||
|
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
|
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
|
import org.keycloak.services.clientregistration.ClientRegistrationException;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
public class DescriptionConverter {
|
public class DescriptionConverter {
|
||||||
|
|
||||||
public static ClientRepresentation toInternal(OIDCClientRepresentation clientOIDC) {
|
public static ClientRepresentation toInternal(OIDCClientRepresentation clientOIDC) throws ClientRegistrationException {
|
||||||
ClientRepresentation client = new ClientRepresentation();
|
ClientRepresentation client = new ClientRepresentation();
|
||||||
client.setClientId(clientOIDC.getClientId());
|
client.setClientId(clientOIDC.getClientId());
|
||||||
client.setName(clientOIDC.getClientName());
|
client.setName(clientOIDC.getClientName());
|
||||||
client.setRedirectUris(clientOIDC.getRedirectUris());
|
client.setRedirectUris(clientOIDC.getRedirectUris());
|
||||||
client.setBaseUrl(clientOIDC.getClientUri());
|
client.setBaseUrl(clientOIDC.getClientUri());
|
||||||
|
|
||||||
|
List<String> oidcResponseTypes = clientOIDC.getResponseTypes();
|
||||||
|
if (oidcResponseTypes == null || oidcResponseTypes.isEmpty()) {
|
||||||
|
oidcResponseTypes = Collections.singletonList(OIDCResponseType.CODE);
|
||||||
|
}
|
||||||
|
List<String> oidcGrantTypes = clientOIDC.getGrantTypes();
|
||||||
|
|
||||||
|
try {
|
||||||
|
OIDCResponseType responseType = OIDCResponseType.parse(oidcResponseTypes);
|
||||||
|
client.setStandardFlowEnabled(responseType.hasResponseType(OIDCResponseType.CODE));
|
||||||
|
client.setImplicitFlowEnabled(responseType.isImplicitOrHybridFlow());
|
||||||
|
if (oidcGrantTypes != null) {
|
||||||
|
client.setDirectAccessGrantsEnabled(oidcGrantTypes.contains(OAuth2Constants.PASSWORD));
|
||||||
|
client.setServiceAccountsEnabled(oidcGrantTypes.contains(OAuth2Constants.CLIENT_CREDENTIALS));
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
throw new ClientRegistrationException(iae.getMessage(), iae);
|
||||||
|
}
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +55,45 @@ public class DescriptionConverter {
|
||||||
response.setRedirectUris(client.getRedirectUris());
|
response.setRedirectUris(client.getRedirectUris());
|
||||||
response.setRegistrationAccessToken(client.getRegistrationAccessToken());
|
response.setRegistrationAccessToken(client.getRegistrationAccessToken());
|
||||||
response.setRegistrationClientUri(uri.toString());
|
response.setRegistrationClientUri(uri.toString());
|
||||||
|
response.setResponseTypes(getOIDCResponseTypes(client));
|
||||||
|
response.setGrantTypes(getOIDCGrantTypes(client));
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<String> getOIDCResponseTypes(ClientRepresentation client) {
|
||||||
|
List<String> responseTypes = new ArrayList<>();
|
||||||
|
if (client.isStandardFlowEnabled()) {
|
||||||
|
responseTypes.add(OAuth2Constants.CODE);
|
||||||
|
responseTypes.add(OIDCResponseType.NONE);
|
||||||
|
}
|
||||||
|
if (client.isImplicitFlowEnabled()) {
|
||||||
|
responseTypes.add(OIDCResponseType.ID_TOKEN);
|
||||||
|
responseTypes.add("id_token token");
|
||||||
|
}
|
||||||
|
if (client.isStandardFlowEnabled() && client.isImplicitFlowEnabled()) {
|
||||||
|
responseTypes.add("code id_token");
|
||||||
|
responseTypes.add("code token");
|
||||||
|
responseTypes.add("code id_token token");
|
||||||
|
}
|
||||||
|
return responseTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getOIDCGrantTypes(ClientRepresentation client) {
|
||||||
|
List<String> grantTypes = new ArrayList<>();
|
||||||
|
if (client.isStandardFlowEnabled()) {
|
||||||
|
grantTypes.add(OAuth2Constants.AUTHORIZATION_CODE);
|
||||||
|
}
|
||||||
|
if (client.isImplicitFlowEnabled()) {
|
||||||
|
grantTypes.add(OAuth2Constants.IMPLICIT);
|
||||||
|
}
|
||||||
|
if (client.isDirectAccessGrantsEnabled()) {
|
||||||
|
grantTypes.add(OAuth2Constants.PASSWORD);
|
||||||
|
}
|
||||||
|
if (client.isServiceAccountsEnabled()) {
|
||||||
|
grantTypes.add(OAuth2Constants.CLIENT_CREDENTIALS);
|
||||||
|
}
|
||||||
|
grantTypes.add(OAuth2Constants.REFRESH_TOKEN);
|
||||||
|
return grantTypes;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.keycloak.services.clientregistration.oidc;
|
package org.keycloak.services.clientregistration.oidc;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -9,6 +10,7 @@ import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.services.ErrorResponseException;
|
import org.keycloak.services.ErrorResponseException;
|
||||||
import org.keycloak.services.clientregistration.AbstractClientRegistrationProvider;
|
import org.keycloak.services.clientregistration.AbstractClientRegistrationProvider;
|
||||||
import org.keycloak.services.clientregistration.ClientRegistrationAuth;
|
import org.keycloak.services.clientregistration.ClientRegistrationAuth;
|
||||||
|
import org.keycloak.services.clientregistration.ClientRegistrationException;
|
||||||
import org.keycloak.services.clientregistration.ErrorCodes;
|
import org.keycloak.services.clientregistration.ErrorCodes;
|
||||||
|
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
|
@ -21,6 +23,8 @@ import java.net.URI;
|
||||||
*/
|
*/
|
||||||
public class OIDCClientRegistrationProvider extends AbstractClientRegistrationProvider {
|
public class OIDCClientRegistrationProvider extends AbstractClientRegistrationProvider {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(OIDCClientRegistrationProvider.class);
|
||||||
|
|
||||||
public OIDCClientRegistrationProvider(KeycloakSession session) {
|
public OIDCClientRegistrationProvider(KeycloakSession session) {
|
||||||
super(session);
|
super(session);
|
||||||
}
|
}
|
||||||
|
@ -33,12 +37,17 @@ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationPr
|
||||||
throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client Identifier included", Response.Status.BAD_REQUEST);
|
throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client Identifier included", Response.Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
|
ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
|
||||||
client = create(client);
|
client = create(client);
|
||||||
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
|
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
|
||||||
clientOIDC = DescriptionConverter.toExternalResponse(client, uri);
|
clientOIDC = DescriptionConverter.toExternalResponse(client, uri);
|
||||||
clientOIDC.setClientIdIssuedAt(Time.currentTime());
|
clientOIDC.setClientIdIssuedAt(Time.currentTime());
|
||||||
return Response.created(uri).entity(clientOIDC).build();
|
return Response.created(uri).entity(clientOIDC).build();
|
||||||
|
} catch (ClientRegistrationException cre) {
|
||||||
|
log.error(cre.getMessage());
|
||||||
|
throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client metadata invalid", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -54,11 +63,16 @@ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationPr
|
||||||
@Path("{clientId}")
|
@Path("{clientId}")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public Response updateOIDC(@PathParam("clientId") String clientId, OIDCClientRepresentation clientOIDC) {
|
public Response updateOIDC(@PathParam("clientId") String clientId, OIDCClientRepresentation clientOIDC) {
|
||||||
|
try {
|
||||||
ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
|
ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
|
||||||
client = update(clientId, client);
|
client = update(clientId, client);
|
||||||
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
|
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
|
||||||
clientOIDC = DescriptionConverter.toExternalResponse(client, uri);
|
clientOIDC = DescriptionConverter.toExternalResponse(client, uri);
|
||||||
return Response.ok(clientOIDC).build();
|
return Response.ok(clientOIDC).build();
|
||||||
|
} catch (ClientRegistrationException cre) {
|
||||||
|
log.error(cre.getMessage());
|
||||||
|
throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client metadata invalid", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
|
|
|
@ -113,6 +113,7 @@ public class RealmManager implements RealmImporter {
|
||||||
setupAccountManagement(realm);
|
setupAccountManagement(realm);
|
||||||
setupBrokerService(realm);
|
setupBrokerService(realm);
|
||||||
setupAdminConsole(realm);
|
setupAdminConsole(realm);
|
||||||
|
setupAdminCli(realm);
|
||||||
setupImpersonationService(realm);
|
setupImpersonationService(realm);
|
||||||
setupAuthenticationFlows(realm);
|
setupAuthenticationFlows(realm);
|
||||||
setupRequiredActions(realm);
|
setupRequiredActions(realm);
|
||||||
|
@ -158,6 +159,30 @@ public class RealmManager implements RealmImporter {
|
||||||
adminConsole.addScopeMapping(adminRole);
|
adminConsole.addScopeMapping(adminRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setupAdminCli(RealmModel realm) {
|
||||||
|
ClientModel adminCli = realm.getClientByClientId(Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
|
if (adminCli == null) {
|
||||||
|
adminCli = new ClientManager(this).createClient(realm, Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
|
adminCli.setName("${client_" + Constants.ADMIN_CLI_CLIENT_ID + "}");
|
||||||
|
adminCli.setEnabled(true);
|
||||||
|
adminCli.setPublicClient(true);
|
||||||
|
adminCli.setFullScopeAllowed(false);
|
||||||
|
adminCli.setStandardFlowEnabled(false);
|
||||||
|
adminCli.setDirectAccessGrantsEnabled(true);
|
||||||
|
|
||||||
|
RoleModel adminRole;
|
||||||
|
if (realm.getName().equals(Config.getAdminRealm())) {
|
||||||
|
adminRole = realm.getRole(AdminRoles.ADMIN);
|
||||||
|
} else {
|
||||||
|
String realmAdminApplicationClientId = getRealmAdminClientId(realm);
|
||||||
|
ClientModel realmAdminApp = realm.getClientByClientId(realmAdminApplicationClientId);
|
||||||
|
adminRole = realmAdminApp.getRole(AdminRoles.REALM_ADMIN);
|
||||||
|
}
|
||||||
|
adminCli.addScopeMapping(adminRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public String getRealmAdminClientId(RealmModel realm) {
|
public String getRealmAdminClientId(RealmModel realm) {
|
||||||
return Constants.REALM_MANAGEMENT_CLIENT_ID;
|
return Constants.REALM_MANAGEMENT_CLIENT_ID;
|
||||||
}
|
}
|
||||||
|
@ -375,6 +400,16 @@ public class RealmManager implements RealmImporter {
|
||||||
|
|
||||||
if (!hasBrokerClient(rep)) setupBrokerService(realm);
|
if (!hasBrokerClient(rep)) setupBrokerService(realm);
|
||||||
if (!hasAdminConsoleClient(rep)) setupAdminConsole(realm);
|
if (!hasAdminConsoleClient(rep)) setupAdminConsole(realm);
|
||||||
|
|
||||||
|
boolean postponeAdminCliSetup = false;
|
||||||
|
if (!hasAdminCliClient(rep)) {
|
||||||
|
if (hasRealmAdminManagementClient(rep)) {
|
||||||
|
postponeAdminCliSetup = true;
|
||||||
|
} else {
|
||||||
|
setupAdminCli(realm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasRealmRole(rep, Constants.OFFLINE_ACCESS_ROLE)) setupOfflineTokens(realm);
|
if (!hasRealmRole(rep, Constants.OFFLINE_ACCESS_ROLE)) setupOfflineTokens(realm);
|
||||||
|
|
||||||
RepresentationToModel.importRealm(session, rep, realm);
|
RepresentationToModel.importRealm(session, rep, realm);
|
||||||
|
@ -389,6 +424,10 @@ public class RealmManager implements RealmImporter {
|
||||||
setupImpersonationService(realm);
|
setupImpersonationService(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (postponeAdminCliSetup) {
|
||||||
|
setupAdminCli(realm);
|
||||||
|
}
|
||||||
|
|
||||||
setupAuthenticationFlows(realm);
|
setupAuthenticationFlows(realm);
|
||||||
setupRequiredActions(realm);
|
setupRequiredActions(realm);
|
||||||
|
|
||||||
|
@ -428,6 +467,10 @@ public class RealmManager implements RealmImporter {
|
||||||
return hasClient(rep, Constants.ADMIN_CONSOLE_CLIENT_ID);
|
return hasClient(rep, Constants.ADMIN_CONSOLE_CLIENT_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasAdminCliClient(RealmRepresentation rep) {
|
||||||
|
return hasClient(rep, Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasClient(RealmRepresentation rep, String clientId) {
|
private boolean hasClient(RealmRepresentation rep, String clientId) {
|
||||||
if (rep.getClients() != null) {
|
if (rep.getClients() != null) {
|
||||||
for (ClientRepresentation clientRep : rep.getClients()) {
|
for (ClientRepresentation clientRep : rep.getClients()) {
|
||||||
|
|
|
@ -10,12 +10,14 @@ import org.keycloak.migration.MigrationProvider;
|
||||||
import org.keycloak.models.ClaimMask;
|
import org.keycloak.models.ClaimMask;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.protocol.LoginProtocol;
|
import org.keycloak.protocol.LoginProtocol;
|
||||||
import org.keycloak.protocol.LoginProtocolFactory;
|
import org.keycloak.protocol.LoginProtocolFactory;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Various common utils needed for migration from older version to newer
|
* Various common utils needed for migration from older version to newer
|
||||||
|
@ -59,6 +61,11 @@ public class DefaultMigrationProvider implements MigrationProvider {
|
||||||
return providerFactory.getBuiltinMappers();
|
return providerFactory.getBuiltinMappers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setupAdminCli(RealmModel realm) {
|
||||||
|
new RealmManager(session).setupAdminCli(realm);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.GroupRepresentation;
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.services.clientregistration.ClientRegistrationException;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.LDAPConnectionTestManager;
|
import org.keycloak.services.managers.LDAPConnectionTestManager;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package org.keycloak.test;
|
package org.keycloak.test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
||||||
|
@ -16,7 +19,7 @@ public class ResponseTypeTest {
|
||||||
assertFail("foo");
|
assertFail("foo");
|
||||||
assertSuccess("code");
|
assertSuccess("code");
|
||||||
assertSuccess("none");
|
assertSuccess("none");
|
||||||
assertFail("id_token");
|
assertSuccess("id_token");
|
||||||
assertFail("token");
|
assertFail("token");
|
||||||
assertFail("refresh_token");
|
assertFail("refresh_token");
|
||||||
assertSuccess("id_token token");
|
assertSuccess("id_token token");
|
||||||
|
@ -27,6 +30,38 @@ public class ResponseTypeTest {
|
||||||
assertFail("code refresh_token");
|
assertFail("code refresh_token");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleResponseTypes() {
|
||||||
|
try {
|
||||||
|
OIDCResponseType.parse(Arrays.asList("code", "token"));
|
||||||
|
Assert.fail("Not expected to parse with success");
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
}
|
||||||
|
|
||||||
|
OIDCResponseType responseType = OIDCResponseType.parse(Collections.singletonList("code"));
|
||||||
|
Assert.assertTrue(responseType.hasResponseType("code"));
|
||||||
|
Assert.assertFalse(responseType.hasResponseType("none"));
|
||||||
|
Assert.assertFalse(responseType.isImplicitOrHybridFlow());
|
||||||
|
|
||||||
|
responseType = OIDCResponseType.parse(Arrays.asList("code", "none"));
|
||||||
|
Assert.assertTrue(responseType.hasResponseType("code"));
|
||||||
|
Assert.assertTrue(responseType.hasResponseType("none"));
|
||||||
|
Assert.assertFalse(responseType.isImplicitOrHybridFlow());
|
||||||
|
|
||||||
|
responseType = OIDCResponseType.parse(Arrays.asList("code", "code token"));
|
||||||
|
Assert.assertTrue(responseType.hasResponseType("code"));
|
||||||
|
Assert.assertFalse(responseType.hasResponseType("none"));
|
||||||
|
Assert.assertTrue(responseType.hasResponseType("token"));
|
||||||
|
Assert.assertFalse(responseType.hasResponseType("id_token"));
|
||||||
|
Assert.assertTrue(responseType.isImplicitOrHybridFlow());
|
||||||
|
Assert.assertFalse(responseType.isImplicitFlow());
|
||||||
|
|
||||||
|
responseType = OIDCResponseType.parse(Arrays.asList("id_token", "id_token token"));
|
||||||
|
Assert.assertFalse(responseType.hasResponseType("code"));
|
||||||
|
Assert.assertTrue(responseType.isImplicitOrHybridFlow());
|
||||||
|
Assert.assertTrue(responseType.isImplicitFlow());
|
||||||
|
}
|
||||||
|
|
||||||
private void assertSuccess(String responseType) {
|
private void assertSuccess(String responseType) {
|
||||||
OIDCResponseType.parse(responseType);
|
OIDCResponseType.parse(responseType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ public class ContainersTestEnricher {
|
||||||
private void initializeAdminClient() {
|
private void initializeAdminClient() {
|
||||||
adminClient.set(Keycloak.getInstance(
|
adminClient.set(Keycloak.getInstance(
|
||||||
getAuthServerContextRootFromSystemProperty() + "/auth",
|
getAuthServerContextRootFromSystemProperty() + "/auth",
|
||||||
MASTER, ADMIN, ADMIN, Constants.ADMIN_CONSOLE_CLIENT_ID));
|
MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeOAuthClient() {
|
private void initializeOAuthClient() {
|
||||||
|
|
|
@ -104,7 +104,7 @@ public abstract class AbstractClientRegistrationTest extends AbstractKeycloakTes
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getToken(String username, String password) {
|
private String getToken(String username, String password) {
|
||||||
return oauthClient.getToken(REALM_NAME, "security-admin-console", null, username, password).getToken();
|
return oauthClient.getToken(REALM_NAME, Constants.ADMIN_CLI_CLIENT_ID, null, username, password).getToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,16 @@ package org.keycloak.testsuite.client;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.client.registration.Auth;
|
import org.keycloak.client.registration.Auth;
|
||||||
import org.keycloak.client.registration.ClientRegistrationException;
|
import org.keycloak.client.registration.ClientRegistrationException;
|
||||||
|
import org.keycloak.common.util.CollectionUtil;
|
||||||
|
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
||||||
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
|
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
|
||||||
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
|
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
@ -49,6 +53,8 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
|
||||||
assertEquals("http://root", response.getClientUri());
|
assertEquals("http://root", response.getClientUri());
|
||||||
assertEquals(1, response.getRedirectUris().size());
|
assertEquals(1, response.getRedirectUris().size());
|
||||||
assertEquals("http://redirect", response.getRedirectUris().get(0));
|
assertEquals("http://redirect", response.getRedirectUris().get(0));
|
||||||
|
assertEquals(Arrays.asList("code", "none"), response.getResponseTypes());
|
||||||
|
assertEquals(Arrays.asList(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.REFRESH_TOKEN), response.getGrantTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -59,6 +65,8 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
|
||||||
OIDCClientRepresentation rep = reg.oidc().get(response.getClientId());
|
OIDCClientRepresentation rep = reg.oidc().get(response.getClientId());
|
||||||
assertNotNull(rep);
|
assertNotNull(rep);
|
||||||
assertNotEquals(response.getRegistrationAccessToken(), rep.getRegistrationAccessToken());
|
assertNotEquals(response.getRegistrationAccessToken(), rep.getRegistrationAccessToken());
|
||||||
|
assertTrue(CollectionUtil.collectionEquals(Arrays.asList("code", "none"), response.getResponseTypes()));
|
||||||
|
assertTrue(CollectionUtil.collectionEquals(Arrays.asList(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.REFRESH_TOKEN), response.getGrantTypes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -67,11 +75,26 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
|
||||||
reg.auth(Auth.token(response));
|
reg.auth(Auth.token(response));
|
||||||
|
|
||||||
response.setRedirectUris(Collections.singletonList("http://newredirect"));
|
response.setRedirectUris(Collections.singletonList("http://newredirect"));
|
||||||
|
response.setResponseTypes(Arrays.asList("code", "id_token token", "code id_token token"));
|
||||||
|
response.setGrantTypes(Arrays.asList(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD));
|
||||||
|
|
||||||
OIDCClientRepresentation updated = reg.oidc().update(response);
|
OIDCClientRepresentation updated = reg.oidc().update(response);
|
||||||
|
|
||||||
assertEquals(1, updated.getRedirectUris().size());
|
assertTrue(CollectionUtil.collectionEquals(Collections.singletonList("http://newredirect"), updated.getRedirectUris()));
|
||||||
assertEquals("http://newredirect", updated.getRedirectUris().get(0));
|
assertTrue(CollectionUtil.collectionEquals(Arrays.asList(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD), updated.getGrantTypes()));
|
||||||
|
assertTrue(CollectionUtil.collectionEquals(Arrays.asList(OAuth2Constants.CODE, OIDCResponseType.NONE, OIDCResponseType.ID_TOKEN, "id_token token", "code id_token", "code token", "code id_token token"), updated.getResponseTypes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateClientError() throws ClientRegistrationException {
|
||||||
|
try {
|
||||||
|
OIDCClientRepresentation response = create();
|
||||||
|
reg.auth(Auth.token(response));
|
||||||
|
response.setResponseTypes(Arrays.asList("code", "token"));
|
||||||
|
reg.oidc().update(response);
|
||||||
|
fail("Not expected to end with success");
|
||||||
|
} catch (ClientRegistrationException cre) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -135,7 +135,7 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
|
||||||
return expect(EventType.CLIENT_LOGIN)
|
return expect(EventType.CLIENT_LOGIN)
|
||||||
.detail(Details.CODE_ID, isCodeId())
|
.detail(Details.CODE_ID, isCodeId())
|
||||||
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
|
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.CLIENT_CREDENTIALS)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS)
|
||||||
.removeDetail(Details.CODE_ID)
|
.removeDetail(Details.CODE_ID)
|
||||||
.session(isUUID());
|
.session(isUUID());
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,7 @@ public class AdapterTestStrategy extends ExternalResource {
|
||||||
Assert.assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad"));
|
Assert.assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad"));
|
||||||
|
|
||||||
// View stats
|
// View stats
|
||||||
List<Map<String, String>> stats = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", "security-admin-console").realm("demo").getClientSessionStats();
|
List<Map<String, String>> stats = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID).realm("demo").getClientSessionStats();
|
||||||
Map<String, String> customerPortalStats = null;
|
Map<String, String> customerPortalStats = null;
|
||||||
Map<String, String> productPortalStats = null;
|
Map<String, String> productPortalStats = null;
|
||||||
for (Map<String, String> s : stats) {
|
for (Map<String, String> s : stats) {
|
||||||
|
@ -594,7 +594,7 @@ public class AdapterTestStrategy extends ExternalResource {
|
||||||
loginAndCheckSession(driver, loginPage);
|
loginAndCheckSession(driver, loginPage);
|
||||||
|
|
||||||
// logout mposolda with admin client
|
// logout mposolda with admin client
|
||||||
Keycloak keycloakAdmin = Keycloak.getInstance(AUTH_SERVER_URL, "master", "admin", "admin", Constants.ADMIN_CONSOLE_CLIENT_ID);
|
Keycloak keycloakAdmin = Keycloak.getInstance(AUTH_SERVER_URL, "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
ApiUtil.findClientByClientId(keycloakAdmin.realm("demo"), "session-portal").logoutUser("mposolda");
|
ApiUtil.findClientByClientId(keycloakAdmin.realm("demo"), "session-portal").logoutUser("mposolda");
|
||||||
|
|
||||||
// bburke should be still logged with original httpSession in our browser window
|
// bburke should be still logged with original httpSession in our browser window
|
||||||
|
|
|
@ -131,7 +131,7 @@ public class RelativeUriAdapterTest {
|
||||||
Assert.assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad"));
|
Assert.assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad"));
|
||||||
|
|
||||||
// View stats
|
// View stats
|
||||||
List<Map<String, String>> stats = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", "security-admin-console").realm("demo").getClientSessionStats();
|
List<Map<String, String>> stats = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID).realm("demo").getClientSessionStats();
|
||||||
Map<String, String> customerPortalStats = null;
|
Map<String, String> customerPortalStats = null;
|
||||||
Map<String, String> productPortalStats = null;
|
Map<String, String> productPortalStats = null;
|
||||||
for (Map<String, String> s : stats) {
|
for (Map<String, String> s : stats) {
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class AddUserTest {
|
||||||
try {
|
try {
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
Keycloak keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "addusertest-admin", "password", Constants.ADMIN_CONSOLE_CLIENT_ID);
|
Keycloak keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "addusertest-admin", "password", Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
keycloak.realms().findAll();
|
keycloak.realms().findAll();
|
||||||
|
|
||||||
RealmRepresentation testRealm = new RealmRepresentation();
|
RealmRepresentation testRealm = new RealmRepresentation();
|
||||||
|
|
|
@ -45,10 +45,12 @@ public abstract class AbstractClientTest {
|
||||||
testRealm.setEnabled(true);
|
testRealm.setEnabled(true);
|
||||||
testRealm.setAccessCodeLifespanUserAction(600);
|
testRealm.setAccessCodeLifespanUserAction(600);
|
||||||
KeycloakModelUtils.generateRealmKeys(testRealm);
|
KeycloakModelUtils.generateRealmKeys(testRealm);
|
||||||
|
|
||||||
|
appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CONSOLE_CLIENT_ID);
|
keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
realm = keycloak.realm(REALM_NAME);
|
realm = keycloak.realm(REALM_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ public class AdminAPITest {
|
||||||
RealmManager manager = new RealmManager(session);
|
RealmManager manager = new RealmManager(session);
|
||||||
|
|
||||||
RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
|
RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
|
||||||
ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
|
ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
TokenManager tm = new TokenManager();
|
TokenManager tm = new TokenManager();
|
||||||
UserModel admin = session.users().getUserByUsername("admin", adminRealm);
|
UserModel admin = session.users().getUserByUsername("admin", adminRealm);
|
||||||
ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
|
ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.admin.client.resource.ProtocolMappersResource;
|
import org.keycloak.admin.client.resource.ProtocolMappersResource;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
|
@ -42,7 +43,7 @@ public class ClientTest extends AbstractClientTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getClients() {
|
public void getClients() {
|
||||||
assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console", "broker");
|
assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console", "broker", Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createClient() {
|
private String createClient() {
|
||||||
|
@ -60,7 +61,7 @@ public class ClientTest extends AbstractClientTest {
|
||||||
String id = createClient();
|
String id = createClient();
|
||||||
|
|
||||||
assertNotNull(realm.clients().get(id));
|
assertNotNull(realm.clients().get(id));
|
||||||
assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console", "broker", "my-app");
|
assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console", "broker", "my-app", Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -117,7 +117,7 @@ public class ImpersonationTest {
|
||||||
RealmManager manager = new RealmManager(session);
|
RealmManager manager = new RealmManager(session);
|
||||||
|
|
||||||
RealmModel adminRealm = manager.getRealm(realm);
|
RealmModel adminRealm = manager.getRealm(realm);
|
||||||
ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
|
ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
TokenManager tm = new TokenManager();
|
TokenManager tm = new TokenManager();
|
||||||
UserModel admin = session.users().getUserByUsername(username, adminRealm);
|
UserModel admin = session.users().getUserByUsername(username, adminRealm);
|
||||||
ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
|
ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
|
||||||
|
|
|
@ -77,6 +77,8 @@ public class FederationProvidersIntegrationTest {
|
||||||
ldapFedProvider.getLdapIdentityStore().updatePassword(john, "Password1");
|
ldapFedProvider.getLdapIdentityStore().updatePassword(john, "Password1");
|
||||||
|
|
||||||
LDAPObject existing = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "existing", "Existing", "Foo", "existing@email.org", null, "5678");
|
LDAPObject existing = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "existing", "Existing", "Foo", "existing@email.org", null, "5678");
|
||||||
|
|
||||||
|
appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,8 @@ public class BruteForceTest {
|
||||||
|
|
||||||
appRealm.setBruteForceProtected(true);
|
appRealm.setBruteForceProtected(true);
|
||||||
appRealm.setFailureFactor(2);
|
appRealm.setFailureFactor(2);
|
||||||
|
|
||||||
|
appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -116,7 +118,7 @@ public class BruteForceTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAdminToken() throws Exception {
|
public String getAdminToken() throws Exception {
|
||||||
String clientId = Constants.ADMIN_CONSOLE_CLIENT_ID;
|
String clientId = Constants.ADMIN_CLI_CLIENT_ID;
|
||||||
return oauth.doGrantAccessTokenRequest("master", "admin", "admin", null, clientId, null).getAccessToken();
|
return oauth.doGrantAccessTokenRequest("master", "admin", "admin", null, clientId, null).getAccessToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,11 @@ public class CustomFlowTest {
|
||||||
// Set passthrough clientAuthenticator for our clients
|
// Set passthrough clientAuthenticator for our clients
|
||||||
ClientModel dummyClient = new ClientManager().createClient(appRealm, "dummy-client");
|
ClientModel dummyClient = new ClientManager().createClient(appRealm, "dummy-client");
|
||||||
dummyClient.setClientAuthenticatorType(PassThroughClientAuthenticator.PROVIDER_ID);
|
dummyClient.setClientAuthenticatorType(PassThroughClientAuthenticator.PROVIDER_ID);
|
||||||
appRealm.getClientByClientId("test-app").setClientAuthenticatorType(PassThroughClientAuthenticator.PROVIDER_ID);
|
dummyClient.setDirectAccessGrantsEnabled(true);
|
||||||
|
|
||||||
|
ClientModel testApp = appRealm.getClientByClientId("test-app");
|
||||||
|
testApp.setClientAuthenticatorType(PassThroughClientAuthenticator.PROVIDER_ID);
|
||||||
|
testApp.setDirectAccessGrantsEnabled(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -218,7 +222,7 @@ public class CustomFlowTest {
|
||||||
.client(clientId)
|
.client(clientId)
|
||||||
.user(userId)
|
.user(userId)
|
||||||
.session(accessToken.getSessionState())
|
.session(accessToken.getSessionState())
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||||
.detail(Details.TOKEN_ID, accessToken.getId())
|
.detail(Details.TOKEN_ID, accessToken.getId())
|
||||||
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
||||||
.detail(Details.USERNAME, login)
|
.detail(Details.USERNAME, login)
|
||||||
|
|
|
@ -48,6 +48,7 @@ public class JaxrsBasicAuthTest {
|
||||||
app.setEnabled(true);
|
app.setEnabled(true);
|
||||||
app.setSecret("password");
|
app.setSecret("password");
|
||||||
app.setFullScopeAllowed(true);
|
app.setFullScopeAllowed(true);
|
||||||
|
app.setDirectAccessGrantsEnabled(true);
|
||||||
|
|
||||||
JaxrsBasicAuthTest.appRealm = appRealm;
|
JaxrsBasicAuthTest.appRealm = appRealm;
|
||||||
}
|
}
|
||||||
|
|
|
@ -456,7 +456,7 @@ public class SamlAdapterTestStrategy extends ExternalResource {
|
||||||
|
|
||||||
public static void uploadSP(String AUTH_SERVER_URL) {
|
public static void uploadSP(String AUTH_SERVER_URL) {
|
||||||
try {
|
try {
|
||||||
Keycloak keycloak = Keycloak.getInstance(AUTH_SERVER_URL, "master", "admin", "admin", Constants.ADMIN_CONSOLE_CLIENT_ID, null);
|
Keycloak keycloak = Keycloak.getInstance(AUTH_SERVER_URL, "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID, null);
|
||||||
RealmResource admin = keycloak.realm("demo");
|
RealmResource admin = keycloak.realm("demo");
|
||||||
|
|
||||||
admin.toRepresentation();
|
admin.toRepresentation();
|
||||||
|
|
|
@ -62,15 +62,19 @@ public class GroupTest {
|
||||||
@Override
|
@Override
|
||||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
ClientModel app = new ClientManager(manager).createClient(appRealm, "resource-owner");
|
ClientModel app = new ClientManager(manager).createClient(appRealm, "resource-owner");
|
||||||
|
app.setDirectAccessGrantsEnabled(true);
|
||||||
app.setSecret("secret");
|
app.setSecret("secret");
|
||||||
|
|
||||||
|
app = appRealm.getClientByClientId("test-app");
|
||||||
|
app.setDirectAccessGrantsEnabled(true);
|
||||||
|
|
||||||
UserModel user = session.users().addUser(appRealm, "direct-login");
|
UserModel user = session.users().addUser(appRealm, "direct-login");
|
||||||
user.setEmail("direct-login@localhost");
|
user.setEmail("direct-login@localhost");
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
|
|
||||||
|
|
||||||
session.users().updateCredential(appRealm, user, UserCredentialModel.password("password"));
|
session.users().updateCredential(appRealm, user, UserCredentialModel.password("password"));
|
||||||
keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CONSOLE_CLIENT_ID);
|
keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -257,7 +261,7 @@ public class GroupTest {
|
||||||
.client(clientId)
|
.client(clientId)
|
||||||
.user(userId)
|
.user(userId)
|
||||||
.session(accessToken.getSessionState())
|
.session(accessToken.getSessionState())
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||||
.detail(Details.TOKEN_ID, accessToken.getId())
|
.detail(Details.TOKEN_ID, accessToken.getId())
|
||||||
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
||||||
.detail(Details.USERNAME, login)
|
.detail(Details.USERNAME, login)
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class ImportTest extends AbstractModelTest {
|
||||||
Assert.assertEquals(0, session.users().getFederatedIdentities(user, realm).size());
|
Assert.assertEquals(0, session.users().getFederatedIdentities(user, realm).size());
|
||||||
|
|
||||||
List<ClientModel> resources = realm.getClients();
|
List<ClientModel> resources = realm.getClients();
|
||||||
Assert.assertEquals(7, resources.size());
|
Assert.assertEquals(8, resources.size());
|
||||||
|
|
||||||
// Test applications imported
|
// Test applications imported
|
||||||
ClientModel application = realm.getClientByClientId("Application");
|
ClientModel application = realm.getClientByClientId("Application");
|
||||||
|
@ -101,7 +101,7 @@ public class ImportTest extends AbstractModelTest {
|
||||||
Assert.assertNotNull(otherApp);
|
Assert.assertNotNull(otherApp);
|
||||||
Assert.assertNull(nonExisting);
|
Assert.assertNull(nonExisting);
|
||||||
Map<String, ClientModel> clients = realm.getClientNameMap();
|
Map<String, ClientModel> clients = realm.getClientNameMap();
|
||||||
Assert.assertEquals(7, clients.size());
|
Assert.assertEquals(8, clients.size());
|
||||||
Assert.assertTrue(clients.values().contains(application));
|
Assert.assertTrue(clients.values().contains(application));
|
||||||
Assert.assertTrue(clients.values().contains(otherApp));
|
Assert.assertTrue(clients.values().contains(otherApp));
|
||||||
Assert.assertTrue(clients.values().contains(accountApp));
|
Assert.assertTrue(clients.values().contains(accountApp));
|
||||||
|
|
|
@ -92,7 +92,14 @@ import static org.junit.Assert.*;
|
||||||
public class AccessTokenTest {
|
public class AccessTokenTest {
|
||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
public static KeycloakRule keycloakRule = new KeycloakRule();
|
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
|
appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public WebRule webRule = new WebRule(this);
|
public WebRule webRule = new WebRule(this);
|
||||||
|
|
|
@ -61,6 +61,7 @@ public class ClientAuthSignedJWTTest {
|
||||||
app1.setClientAuthenticatorType(JWTClientAuthenticator.PROVIDER_ID);
|
app1.setClientAuthenticatorType(JWTClientAuthenticator.PROVIDER_ID);
|
||||||
|
|
||||||
ClientModel app2 = appRealm.addClient("client2");
|
ClientModel app2 = appRealm.addClient("client2");
|
||||||
|
app2.setDirectAccessGrantsEnabled(true);
|
||||||
new ClientManager(manager).enableServiceAccount(app2);
|
new ClientManager(manager).enableServiceAccount(app2);
|
||||||
app2.setClientAuthenticatorType(JWTClientAuthenticator.PROVIDER_ID);
|
app2.setClientAuthenticatorType(JWTClientAuthenticator.PROVIDER_ID);
|
||||||
|
|
||||||
|
@ -189,7 +190,7 @@ public class ClientAuthSignedJWTTest {
|
||||||
events.expectLogin()
|
events.expectLogin()
|
||||||
.client("client2")
|
.client("client2")
|
||||||
.session(accessToken.getSessionState())
|
.session(accessToken.getSessionState())
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||||
.detail(Details.TOKEN_ID, accessToken.getId())
|
.detail(Details.TOKEN_ID, accessToken.getId())
|
||||||
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
||||||
.detail(Details.USERNAME, "test-user@localhost")
|
.detail(Details.USERNAME, "test-user@localhost")
|
||||||
|
|
|
@ -67,6 +67,7 @@ public class OfflineTokenTest {
|
||||||
appRealm.setSsoSessionIdleTimeout(30);
|
appRealm.setSsoSessionIdleTimeout(30);
|
||||||
|
|
||||||
ClientModel app = new ClientManager(manager).createClient(appRealm, "offline-client");
|
ClientModel app = new ClientManager(manager).createClient(appRealm, "offline-client");
|
||||||
|
app.setDirectAccessGrantsEnabled(true);
|
||||||
app.setSecret("secret1");
|
app.setSecret("secret1");
|
||||||
String testAppRedirectUri = appRealm.getClientByClientId("test-app").getRedirectUris().iterator().next();
|
String testAppRedirectUri = appRealm.getClientByClientId("test-app").getRedirectUris().iterator().next();
|
||||||
offlineClientAppUri = UriUtils.getOrigin(testAppRedirectUri) + "/offline-client";
|
offlineClientAppUri = UriUtils.getOrigin(testAppRedirectUri) + "/offline-client";
|
||||||
|
@ -319,7 +320,7 @@ public class OfflineTokenTest {
|
||||||
.client("offline-client")
|
.client("offline-client")
|
||||||
.user(userId)
|
.user(userId)
|
||||||
.session(token.getSessionState())
|
.session(token.getSessionState())
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||||
.detail(Details.TOKEN_ID, token.getId())
|
.detail(Details.TOKEN_ID, token.getId())
|
||||||
.detail(Details.REFRESH_TOKEN_ID, offlineToken.getId())
|
.detail(Details.REFRESH_TOKEN_ID, offlineToken.getId())
|
||||||
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE)
|
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE)
|
||||||
|
@ -361,7 +362,7 @@ public class OfflineTokenTest {
|
||||||
.client("offline-client")
|
.client("offline-client")
|
||||||
.user(userId)
|
.user(userId)
|
||||||
.session(token.getSessionState())
|
.session(token.getSessionState())
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||||
.detail(Details.TOKEN_ID, token.getId())
|
.detail(Details.TOKEN_ID, token.getId())
|
||||||
.detail(Details.REFRESH_TOKEN_ID, offlineToken.getId())
|
.detail(Details.REFRESH_TOKEN_ID, offlineToken.getId())
|
||||||
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE)
|
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE)
|
||||||
|
|
|
@ -71,7 +71,14 @@ import static org.junit.Assert.assertNull;
|
||||||
public class RefreshTokenTest {
|
public class RefreshTokenTest {
|
||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
public static KeycloakRule keycloakRule = new KeycloakRule();
|
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
|
appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public WebRule webRule = new WebRule(this);
|
public WebRule webRule = new WebRule(this);
|
||||||
|
|
|
@ -35,9 +35,11 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
|
||||||
@Override
|
@Override
|
||||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
ClientModel app = new ClientManager(manager).createClient(appRealm, "resource-owner");
|
ClientModel app = new ClientManager(manager).createClient(appRealm, "resource-owner");
|
||||||
|
app.setDirectAccessGrantsEnabled(true);
|
||||||
app.setSecret("secret");
|
app.setSecret("secret");
|
||||||
|
|
||||||
ClientModel app2 = new ClientManager(manager).createClient(appRealm, "resource-owner-public");
|
ClientModel app2 = new ClientManager(manager).createClient(appRealm, "resource-owner-public");
|
||||||
|
app2.setDirectAccessGrantsEnabled(true);
|
||||||
app2.setPublicClient(true);
|
app2.setPublicClient(true);
|
||||||
|
|
||||||
UserModel user = session.users().addUser(appRealm, "direct-login");
|
UserModel user = session.users().addUser(appRealm, "direct-login");
|
||||||
|
@ -94,7 +96,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
|
||||||
.client(clientId)
|
.client(clientId)
|
||||||
.user(userId)
|
.user(userId)
|
||||||
.session(accessToken.getSessionState())
|
.session(accessToken.getSessionState())
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||||
.detail(Details.TOKEN_ID, accessToken.getId())
|
.detail(Details.TOKEN_ID, accessToken.getId())
|
||||||
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
||||||
.detail(Details.USERNAME, login)
|
.detail(Details.USERNAME, login)
|
||||||
|
@ -130,7 +132,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
|
||||||
events.expectLogin()
|
events.expectLogin()
|
||||||
.client("resource-owner")
|
.client("resource-owner")
|
||||||
.session(accessToken.getSessionState())
|
.session(accessToken.getSessionState())
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||||
.detail(Details.TOKEN_ID, accessToken.getId())
|
.detail(Details.TOKEN_ID, accessToken.getId())
|
||||||
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
||||||
.removeDetail(Details.CODE_ID)
|
.removeDetail(Details.CODE_ID)
|
||||||
|
@ -191,6 +193,41 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void grantAccessTokenClientNotAllowed() throws Exception {
|
||||||
|
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||||
|
@Override
|
||||||
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
|
ClientModel client = appRealm.getClientByClientId("resource-owner");
|
||||||
|
client.setDirectAccessGrantsEnabled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
oauth.clientId("resource-owner");
|
||||||
|
|
||||||
|
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "test-user@localhost", "password");
|
||||||
|
|
||||||
|
assertEquals(400, response.getStatusCode());
|
||||||
|
|
||||||
|
assertEquals("invalid_grant", response.getError());
|
||||||
|
|
||||||
|
events.expectLogin()
|
||||||
|
.client("resource-owner")
|
||||||
|
.session((String) null)
|
||||||
|
.clearDetails()
|
||||||
|
.error(Errors.NOT_ALLOWED)
|
||||||
|
.user((String) null)
|
||||||
|
.assertEvent();
|
||||||
|
|
||||||
|
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||||
|
@Override
|
||||||
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
|
ClientModel client = appRealm.getClientByClientId("resource-owner");
|
||||||
|
client.setDirectAccessGrantsEnabled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void grantAccessTokenVerifyEmail() throws Exception {
|
public void grantAccessTokenVerifyEmail() throws Exception {
|
||||||
|
|
||||||
|
@ -286,7 +323,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
|
||||||
events.expectLogin()
|
events.expectLogin()
|
||||||
.client("resource-owner")
|
.client("resource-owner")
|
||||||
.session((String) null)
|
.session((String) null)
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||||
.removeDetail(Details.CODE_ID)
|
.removeDetail(Details.CODE_ID)
|
||||||
.removeDetail(Details.REDIRECT_URI)
|
.removeDetail(Details.REDIRECT_URI)
|
||||||
.removeDetail(Details.CONSENT)
|
.removeDetail(Details.CONSENT)
|
||||||
|
@ -308,7 +345,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
|
||||||
.client("resource-owner")
|
.client("resource-owner")
|
||||||
.user((String) null)
|
.user((String) null)
|
||||||
.session((String) null)
|
.session((String) null)
|
||||||
.detail(Details.RESPONSE_TYPE, OAuth2Constants.PASSWORD)
|
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||||
.detail(Details.USERNAME, "invalid")
|
.detail(Details.USERNAME, "invalid")
|
||||||
.removeDetail(Details.CODE_ID)
|
.removeDetail(Details.CODE_ID)
|
||||||
.removeDetail(Details.REDIRECT_URI)
|
.removeDetail(Details.REDIRECT_URI)
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
import org.keycloak.representations.UserInfo;
|
import org.keycloak.representations.UserInfo;
|
||||||
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||||
import org.keycloak.testsuite.rule.WebResource;
|
import org.keycloak.testsuite.rule.WebResource;
|
||||||
import org.keycloak.testsuite.rule.WebRule;
|
import org.keycloak.testsuite.rule.WebRule;
|
||||||
|
@ -56,7 +57,14 @@ import static org.junit.Assert.assertNotNull;
|
||||||
public class UserInfoTest {
|
public class UserInfoTest {
|
||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
public static KeycloakRule keycloakRule = new KeycloakRule();
|
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
|
appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public WebRule webRule = new WebRule(this);
|
public WebRule webRule = new WebRule(this);
|
||||||
|
|
|
@ -457,7 +457,7 @@ public class SamlBindingTest {
|
||||||
|
|
||||||
public static void uploadSP() {
|
public static void uploadSP() {
|
||||||
try {
|
try {
|
||||||
Keycloak keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CONSOLE_CLIENT_ID, null);
|
Keycloak keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID, null);
|
||||||
RealmResource admin = keycloak.realm("demo");
|
RealmResource admin = keycloak.realm("demo");
|
||||||
|
|
||||||
admin.toRepresentation();
|
admin.toRepresentation();
|
||||||
|
|
|
@ -120,6 +120,7 @@
|
||||||
{
|
{
|
||||||
"name": "customer-portal",
|
"name": "customer-portal",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"adminUrl": "http://localhost:8081/customer-portal",
|
"adminUrl": "http://localhost:8081/customer-portal",
|
||||||
"baseUrl": "http://localhost:8081/customer-portal",
|
"baseUrl": "http://localhost:8081/customer-portal",
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
|
|
|
@ -156,6 +156,7 @@
|
||||||
"name": "Applicationn",
|
"name": "Applicationn",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"implicitFlowEnabled": true,
|
"implicitFlowEnabled": true,
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"nodeReRegistrationTimeout": 50,
|
"nodeReRegistrationTimeout": 50,
|
||||||
"registeredNodes": {
|
"registeredNodes": {
|
||||||
"node1": 10,
|
"node1": 10,
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"adminUrl": "http://localhost:8082/customer-portal",
|
"adminUrl": "http://localhost:8082/customer-portal",
|
||||||
"baseUrl": "http://localhost:8082/customer-portal",
|
"baseUrl": "http://localhost:8082/customer-portal",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8082/customer-portal/*"
|
"http://localhost:8082/customer-portal/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
"fullScopeAllowed": true,
|
"fullScopeAllowed": true,
|
||||||
"adminUrl": "http://localhost:8080/customer-portal",
|
"adminUrl": "http://localhost:8080/customer-portal",
|
||||||
"baseUrl": "http://localhost:8080/customer-portal",
|
"baseUrl": "http://localhost:8080/customer-portal",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8080/customer-portal/*"
|
"http://localhost:8080/customer-portal/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"adminUrl": "http://localhost:8082/customer-portal",
|
"adminUrl": "http://localhost:8082/customer-portal",
|
||||||
"baseUrl": "http://localhost:8082/customer-portal",
|
"baseUrl": "http://localhost:8082/customer-portal",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8082/customer-portal/*"
|
"http://localhost:8082/customer-portal/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
"fullScopeAllowed": true,
|
"fullScopeAllowed": true,
|
||||||
"adminUrl": "http://localhost:8080/customer-portal",
|
"adminUrl": "http://localhost:8080/customer-portal",
|
||||||
"baseUrl": "http://localhost:8080/customer-portal",
|
"baseUrl": "http://localhost:8080/customer-portal",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8080/customer-portal/*"
|
"http://localhost:8080/customer-portal/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"adminUrl": "http://localhost:8082/customer-portal",
|
"adminUrl": "http://localhost:8082/customer-portal",
|
||||||
"baseUrl": "http://localhost:8082/customer-portal",
|
"baseUrl": "http://localhost:8082/customer-portal",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8082/customer-portal/*"
|
"http://localhost:8082/customer-portal/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"adminUrl": "http://localhost:8082/customer-portal",
|
"adminUrl": "http://localhost:8082/customer-portal",
|
||||||
"baseUrl": "http://localhost:8082/customer-portal",
|
"baseUrl": "http://localhost:8082/customer-portal",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8082/customer-portal/*"
|
"http://localhost:8082/customer-portal/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"adminUrl": "http://localhost:8082/customer-portal",
|
"adminUrl": "http://localhost:8082/customer-portal",
|
||||||
"baseUrl": "http://localhost:8082/customer-portal",
|
"baseUrl": "http://localhost:8082/customer-portal",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8082/customer-portal/*"
|
"http://localhost:8082/customer-portal/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"adminUrl": "http://localhost:8082/customer-portal",
|
"adminUrl": "http://localhost:8082/customer-portal",
|
||||||
"baseUrl": "http://localhost:8082/customer-portal",
|
"baseUrl": "http://localhost:8082/customer-portal",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8082/customer-portal/*"
|
"http://localhost:8082/customer-portal/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
"fullScopeAllowed": true,
|
"fullScopeAllowed": true,
|
||||||
"adminUrl": "http://localhost:8080/customer-portal",
|
"adminUrl": "http://localhost:8080/customer-portal",
|
||||||
"baseUrl": "http://localhost:8080/customer-portal",
|
"baseUrl": "http://localhost:8080/customer-portal",
|
||||||
|
"directAccessGrantsEnabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8080/customer-portal/*"
|
"http://localhost:8080/customer-portal/*"
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in a new issue