keycloak-uncached
Changes
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html 6(+6 -0)
Details
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
index e60727c..68ba134 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -20,6 +20,7 @@ public class RealmRepresentation {
protected Integer accessCodeLifespanUserAction;
protected Boolean enabled;
protected Boolean sslNotRequired;
+ protected Boolean passwordCredentialGrantAllowed;
protected Boolean registrationAllowed;
protected Boolean rememberMe;
protected Boolean verifyEmail;
@@ -242,6 +243,14 @@ public class RealmRepresentation {
this.publicKey = publicKey;
}
+ public Boolean isPasswordCredentialGrantAllowed() {
+ return passwordCredentialGrantAllowed;
+ }
+
+ public void setPasswordCredentialGrantAllowed(Boolean passwordCredentialGrantAllowed) {
+ this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed;
+ }
+
public Boolean isRegistrationAllowed() {
return registrationAllowed;
}
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html
index a2b9161..089b190 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html
@@ -67,6 +67,12 @@
</div>
</div>
<div class="form-group">
+ <label for="passwordCredentialGrantAllowed" class="col-sm-2 control-label">Password Credential Grant</label>
+ <div class="col-sm-4">
+ <input ng-model="realm.passwordCredentialGrantAllowedpasswordCredentialGrantAllowed" name="passwordCredentialGrantAllowed" id="passwordCredentialGrantAllowed" onoffswitch />
+ </div>
+ </div>
+ <div class="form-group">
<label for="requireSsl" class="col-sm-2 control-label">Require SSL</label>
<div class="col-sm-4">
<input ng-model="realm.requireSsl" name="requireSsl" id="requireSsl" onoffswitch />
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index 81041e0..f364503 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -16,6 +16,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private boolean registrationAllowed;
private boolean rememberMe;
private boolean verifyEmail;
+ private boolean passwordCredentialGrantAllowed;
private boolean resetPasswordAllowed;
private boolean social;
private boolean updateProfileOnInitialSocialLogin;
@@ -85,6 +86,14 @@ public class RealmEntity extends AbstractIdentifiableEntity {
this.sslNotRequired = sslNotRequired;
}
+ public boolean isPasswordCredentialGrantAllowed() {
+ return passwordCredentialGrantAllowed;
+ }
+
+ public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+ this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed;
+ }
+
public boolean isRegistrationAllowed() {
return registrationAllowed;
}
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index 8f348e0..c2ec9f3 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -29,6 +29,11 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
boolean isRegistrationAllowed();
void setRegistrationAllowed(boolean registrationAllowed);
+
+ boolean isPasswordCredentialGrantAllowed();
+
+ void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed);
+
boolean isRememberMe();
void setRememberMe(boolean rememberMe);
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index e0aff1b..62804eb 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -41,6 +41,7 @@ public class RealmEntity {
protected boolean enabled;
protected boolean sslNotRequired;
protected boolean registrationAllowed;
+ protected boolean passwordCredentialGrantAllowed;
protected boolean verifyEmail;
protected boolean resetPasswordAllowed;
protected boolean social;
@@ -154,6 +155,14 @@ public class RealmEntity {
this.sslNotRequired = sslNotRequired;
}
+ public boolean isPasswordCredentialGrantAllowed() {
+ return passwordCredentialGrantAllowed;
+ }
+
+ public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+ this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed;
+ }
+
public boolean isRegistrationAllowed() {
return registrationAllowed;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index da46471..339a270 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -115,6 +115,17 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public boolean isPasswordCredentialGrantAllowed() {
+ return realm.isPasswordCredentialGrantAllowed();
+ }
+
+ @Override
+ public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+ realm.setPasswordCredentialGrantAllowed(passwordCredentialGrantAllowed);
+ em.flush();
+ }
+
+ @Override
public boolean isRegistrationAllowed() {
return realm.isRegistrationAllowed();
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 97ef792..da01022 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -109,6 +109,17 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
@Override
+ public boolean isPasswordCredentialGrantAllowed() {
+ return realm.isPasswordCredentialGrantAllowed();
+ }
+
+ @Override
+ public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+ realm.setPasswordCredentialGrantAllowed(passwordCredentialGrantAllowed);
+ updateRealm();
+ }
+
+ @Override
public boolean isRegistrationAllowed() {
return realm.isRegistrationAllowed();
}
diff --git a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java b/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java
index 1f4b8d4..49ca015 100755
--- a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java
+++ b/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java
@@ -81,6 +81,7 @@ public class ModelToRepresentation {
rep.setSslNotRequired(realm.isSslNotRequired());
rep.setPublicKey(realm.getPublicKeyPem());
rep.setPrivateKey(realm.getPrivateKeyPem());
+ rep.setPasswordCredentialGrantAllowed(realm.isPasswordCredentialGrantAllowed());
rep.setRegistrationAllowed(realm.isRegistrationAllowed());
rep.setRememberMe(realm.isRememberMe());
rep.setBruteForceProtected(realm.isBruteForceProtected());
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 640eb39..1543087 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -174,6 +174,7 @@ public class RealmManager {
if (rep.getQuickLoginCheckMilliSeconds() != null) realm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds());
if (rep.getMaxDeltaTimeSeconds() != null) realm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds());
if (rep.getFailureFactor() != null) realm.setFailureFactor(rep.getFailureFactor());
+ if (rep.isPasswordCredentialGrantAllowed() != null) realm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed());
if (rep.isRegistrationAllowed() != null) realm.setRegistrationAllowed(rep.isRegistrationAllowed());
if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe());
if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail());
@@ -331,6 +332,7 @@ public class RealmManager {
else newRealm.setAccessCodeLifespanUserAction(300);
if (rep.isSslNotRequired() != null) newRealm.setSslNotRequired(rep.isSslNotRequired());
+ if (rep.isPasswordCredentialGrantAllowed() != null) newRealm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed());
if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed());
if (rep.isRememberMe() != null) newRealm.setRememberMe(rep.isRememberMe());
if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail());
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index 1d92234..77e470a 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -205,7 +205,11 @@ public class TokenService {
public Response grantAccessToken(final @HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader,
final MultivaluedMap<String, String> form) {
if (!checkSsl()) {
- throw new NotAcceptableException("HTTPS required");
+ return createError("https_required", "HTTPS required", Response.Status.FORBIDDEN);
+ }
+
+ if (!realm.isPasswordCredentialGrantAllowed()) {
+ return createError("not_enabled", "Resource Owner Password Credentials Grant not enabled", Response.Status.FORBIDDEN);
}
audit.event(Events.LOGIN).detail(Details.AUTH_METHOD, "oauth_credentials").detail(Details.RESPONSE_TYPE, "token");
@@ -224,12 +228,12 @@ public class TokenService {
if ( (client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
audit.error(Errors.NOT_ALLOWED);
- throw new ForbiddenException("Bearer-only applications are not allowed to invoke grants/access");
+ return createError("not_allowed", "Bearer-only applications are not allowed to invoke grants/access", Response.Status.FORBIDDEN);
}
if (!realm.isEnabled()) {
audit.error(Errors.REALM_DISABLED);
- throw new UnauthorizedException("Disabled realm");
+ return createError("realm_disabled", "Realm is disabled", Response.Status.UNAUTHORIZED);
}
AuthenticationStatus authenticationStatus = authManager.authenticateForm(clientConnection, realm, form);
@@ -1014,4 +1018,13 @@ public class TokenService {
return realm.isSslNotRequired() || uriInfo.getBaseUri().getScheme().equals("https");
}
+ private Response createError(String error, String errorDescription, Response.Status status) {
+ Map<String, String> e = new HashMap<String, String>();
+ e.put(OAuth2Constants.ERROR, error);
+ if (errorDescription != null) {
+ e.put(OAuth2Constants.ERROR_DESCRIPTION, errorDescription);
+ }
+ return Response.status(status).entity(e).type("application/json").build();
+ }
+
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
index 15a3f23..282bf5b 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
@@ -1,21 +1,11 @@
package org.keycloak.testsuite.oauth;
-import net.iharder.Base64;
import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.message.BasicNameValuePair;
-import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
-import org.keycloak.OAuth2Constants;
import org.keycloak.audit.Details;
import org.keycloak.audit.Errors;
-import org.keycloak.audit.Event;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.AccessToken;
@@ -28,11 +18,6 @@ import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.LinkedList;
-import java.util.List;
-
import static org.junit.Assert.assertEquals;
/**
@@ -46,6 +31,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
ApplicationModel app = appRealm.addApplication("resource-owner");
app.setSecret("secret");
+ appRealm.setPasswordCredentialGrantAllowed(true);
}
});
@@ -97,6 +83,33 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
}
@Test
+ public void grantAccessTokenNotEnabled() throws Exception {
+ try {
+ keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ appRealm.setPasswordCredentialGrantAllowed(false);
+ }
+ });
+
+ oauth.clientId("resource-owner");
+
+ OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "test-user@localhost", "password");
+
+ assertEquals(403, response.getStatusCode());
+ assertEquals("not_enabled", response.getError());
+
+ } finally {
+ keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ appRealm.setPasswordCredentialGrantAllowed(true);
+ }
+ });
+ }
+ }
+
+ @Test
public void grantAccessTokenLogout() throws Exception {
oauth.clientId("resource-owner");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index 77bd97e..1a4bf74 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -404,7 +404,8 @@ public class OAuthClient {
Assert.fail("Invalid content type");
}
- JSONObject responseJson = new JSONObject(IOUtils.toString(response.getEntity().getContent()));
+ String s = IOUtils.toString(response.getEntity().getContent());
+ JSONObject responseJson = new JSONObject(s);
if (statusCode == 200) {
accessToken = responseJson.getString("access_token");