();
+ res.put("error", "invalid_grant");
+ res.put("error_description", "Auth error");
+ return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+ }
+ AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken());
+ return Response.ok(res).build();
+
+ }
+
+ protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token)
+ {
+ byte[] tokenBytes = null;
+ try
+ {
+ tokenBytes = JsonSerialization.toByteArray(token, false);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ String encodedToken = new JWSBuilder()
+ .content(tokenBytes)
+ .rsa256(privateKey);
+
+ return accessTokenResponse(token, encodedToken);
+ }
+
+ protected AccessTokenResponse accessTokenResponse(SkeletonKeyToken token, String encodedToken)
+ {
+ AccessTokenResponse res = new AccessTokenResponse();
+ res.setToken(encodedToken);
+ res.setTokenType("bearer");
+ if (token.getExpiration() != 0)
+ {
+ long time = token.getExpiration() - (System.currentTimeMillis() / 1000);
+ res.setExpiresIn(time);
+ }
+ return res;
+ }
+
+ @Path("{realm}/auth/request")
+ @GET
+ public Response requestAccessCode(@PathParam("realm") String realmId,
+ @QueryParam("response_type") String responseType,
+ @QueryParam("redirect_uri") String redirect,
+ @QueryParam("client_id") String clientId,
+ @QueryParam("scope") String scopeParam,
+ @QueryParam("state") String state)
+ {
+ RealmModel realm = adapter.getRealm(realmId);
+ if (realm == null)
+ {
+ throw new NotFoundException("Realm not found");
+ }
+ if (!realm.isEnabled())
+ {
+ throw new NotAuthorizedException("Realm not enabled");
+ }
+ User client = realm.getIdm().getUser(clientId);
+ if (client == null)
+ return Response.ok("Security Alert
Unknown client trying to get access to your account.
").type("text/html").build();
+
+ return loginForm(null, redirect, clientId, scopeParam, state, realm, client);
+ }
+
+ private Response loginForm(String validationError, String redirect, String clientId, String scopeParam, String state, RealmModel realm, User client)
+ {
+ StringBuffer html = new StringBuffer();
+ if (scopeParam != null)
+ {
+ html.append("Grant Request For ").append(realm.getName()).append(" Realm
");
+ if (validationError != null)
+ {
+ try
+ {
+ Thread.sleep(1000); // put in a delay
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ html.append("").append(validationError).append("
");
+ }
+ html.append("A Third Party is requesting access to the following resources
");
+ html.append("");
+ SkeletonKeyScope scope = tokenManager.decodeScope(scopeParam);
+ Map resourceMap = realm.getResourceMap();
+
+ for (String res : scope.keySet())
+ {
+ ResourceModel resource = resourceMap.get(res);
+ html.append("Resource: ").append(resource.getName()).append(" | Roles:");
+ Set scopeMapping = resource.getScope(client);
+ for (String role : scope.get(res))
+ {
+ html.append(" ").append(role);
+ if (!scopeMapping.contains("*") && !scopeMapping.contains(role))
+ {
+ return Response.ok("Security AlertKnown client not authorized for the requested scope. ").type("text/html").build();
+ }
+ }
+ html.append(" |
");
+ }
+ html.append("
To Authorize, please login below
");
+ }
+ else
+ {
+ Set scopeMapping = realm.getScope(client);
+ if (scopeMapping.contains("*"))
+ {
+ html.append("Login For ").append(realm.getName()).append(" Realm
");
+ if (validationError != null)
+ {
+ try
+ {
+ Thread.sleep(1000); // put in a delay
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ html.append("").append(validationError).append("
");
+ }
+ }
+ else
+ {
+ html.append("Grant Request For ").append(realm.getName()).append(" Realm
");
+ if (validationError != null)
+ {
+ try
+ {
+ Thread.sleep(1000); // put in a delay
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ html.append("").append(validationError).append("
");
+ }
+ SkeletonKeyScope scope = new SkeletonKeyScope();
+ List resources = realm.getResources();
+ boolean found = false;
+ for (ResourceModel resource : resources)
+ {
+ Set resourceScope = resource.getScope(client);
+ if (resourceScope == null) continue;
+ if (resourceScope.size() == 0) continue;
+ if (!found)
+ {
+ found = true;
+ html.append("A Third Party is requesting access to the following resources
");
+ html.append("");
+ }
+ html.append("Resource: ").append(resource.getName()).append(" | Roles:");
+ // todo add description of role
+ for (String role : resourceScope)
+ {
+ html.append(" ").append(role);
+ scope.add(resource.getName(), role);
+ }
+ }
+ if (!found)
+ {
+ return Response.ok("Security AlertKnown client not authorized to access this realm. ").type("text/html").build();
+ }
+ html.append(" |
");
+ try
+ {
+ String json = JsonSerialization.toString(scope, false);
+ scopeParam = Base64Url.encode(json.getBytes("UTF-8"));
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ }
+ }
+
+ UriBuilder formActionUri = uriInfo.getBaseUriBuilder().path(TokenService.class).path(TokenService.class, "login");
+ String action = formActionUri.build(realm.getId()).toString();
+ html.append("");
+ return Response.ok(html.toString()).type("text/html").build();
+ }
+}
diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java
new file mode 100755
index 000000000000..998bedd01836
--- /dev/null
+++ b/services/src/test/java/org/keycloak/test/AdapterTest.java
@@ -0,0 +1,159 @@
+package org.keycloak.test;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.keycloak.representations.idm.RequiredCredentialRepresentation;
+import org.keycloak.services.model.RealmManager;
+import org.keycloak.services.model.RealmModel;
+import org.keycloak.services.model.RealmResourceRelationship;
+import org.keycloak.services.model.RequiredCredentialModel;
+import org.keycloak.services.model.RequiredCredentialRelationship;
+import org.keycloak.services.model.ScopeRelationship;
+import org.keycloak.services.model.UserCredentialModel;
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.config.IdentityConfigurationBuilder;
+import org.picketlink.idm.credential.Credentials;
+import org.picketlink.idm.credential.Password;
+import org.picketlink.idm.credential.UsernamePasswordCredentials;
+import org.picketlink.idm.file.internal.FileUtils;
+import org.picketlink.idm.internal.IdentityManagerFactory;
+import org.picketlink.idm.model.Realm;
+import org.picketlink.idm.model.Role;
+import org.picketlink.idm.model.SimpleRole;
+import org.picketlink.idm.model.SimpleUser;
+import org.picketlink.idm.model.User;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AdapterTest
+{
+ private static IdentityManagerFactory factory;
+ public static final String WORKING_DIRECTORY = "/tmp/keycloak";
+ public RealmManager adapter;
+ public RealmModel realmModel;
+ @Before
+ public void before() throws Exception
+ {
+ after();
+ factory = createFactory();
+ adapter = new RealmManager(factory);
+ }
+
+ private static IdentityManagerFactory createFactory() {
+ IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
+
+ builder
+ .stores()
+ .file()
+ .addRealm(Realm.DEFAULT_REALM)
+ .workingDirectory(WORKING_DIRECTORY)
+ .preserveState(true)
+ .supportAllFeatures()
+ .supportRelationshipType(RealmResourceRelationship.class, RequiredCredentialRelationship.class, ScopeRelationship.class);
+
+ return new IdentityManagerFactory(builder.build());
+ }
+
+ @After
+ public void after() throws Exception
+ {
+ File file = new File(WORKING_DIRECTORY);
+ FileUtils.delete(file);
+ Thread.sleep(10); // my windows machine seems to have delays on deleting files sometimes
+ }
+
+ @Test
+ public void test1CreateRealm() throws Exception
+ {
+ realmModel = adapter.create("JUGGLER");
+ realmModel.setAccessCodeLifespan(100);
+ realmModel.setCookieLoginAllowed(true);
+ realmModel.setEnabled(true);
+ realmModel.setName("JUGGLER");
+ realmModel.setPrivateKeyPem("0234234");
+ realmModel.setPublicKeyPem("0234234");
+ realmModel.setTokenLifespan(1000);
+ realmModel.updateRealm();
+
+ System.out.println(realmModel.getId());
+ realmModel = adapter.getRealm(realmModel.getId());
+ Assert.assertNotNull(realmModel);
+ Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
+ Assert.assertEquals(realmModel.getTokenLifespan(), 1000);
+ Assert.assertEquals(realmModel.isEnabled(), true);
+ Assert.assertEquals(realmModel.getName(), "JUGGLER");
+ Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234");
+ Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234");
+ }
+
+ @Test
+ public void test2RequiredCredential() throws Exception
+ {
+ test1CreateRealm();
+ RequiredCredentialModel creds = new RequiredCredentialModel();
+ creds.setSecret(true);
+ creds.setType(RequiredCredentialRepresentation.PASSWORD);
+ creds.setInput(true);
+ realmModel.addRequiredCredential(creds);
+ creds = new RequiredCredentialModel();
+ creds.setSecret(true);
+ creds.setType(RequiredCredentialRepresentation.TOTP);
+ creds.setInput(true);
+ realmModel.addRequiredCredential(creds);
+ List storedCreds = realmModel.getRequiredCredentials();
+ Assert.assertEquals(2, storedCreds.size());
+ boolean totp = false;
+ boolean password = false;
+ for (RequiredCredentialModel cred : storedCreds)
+ {
+ if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) password = true;
+ else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) totp = true;
+ }
+ Assert.assertTrue(totp);
+ Assert.assertTrue(password);
+ }
+
+ @Test
+ public void testCredentialValidation() throws Exception
+ {
+ test1CreateRealm();
+ User user = new SimpleUser("bburke");
+ realmModel.getIdm().add(user);
+ UserCredentialModel cred = new UserCredentialModel();
+ cred.setType(RequiredCredentialRepresentation.PASSWORD);
+ cred.setValue("geheim");
+ realmModel.updateCredential(user, cred);
+ IdentityManager idm = realmModel.getIdm();
+ UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user.getLoginName(), new Password("geheim"));
+ idm.validateCredentials(creds);
+ Assert.assertEquals(creds.getStatus(), Credentials.Status.VALID);
+ }
+
+ @Test
+ public void testRoles() throws Exception
+ {
+ test1CreateRealm();
+ IdentityManager idm = realmModel.getIdm();
+ idm.add(new SimpleRole("admin"));
+ idm.add(new SimpleRole("user"));
+ List roles = realmModel.getRoles();
+ Assert.assertEquals(2, roles.size());
+ SimpleUser user = new SimpleUser("bburke");
+ idm.add(user);
+ Role role = idm.getRole("user");
+ idm.grantRole(user, role);
+ Assert.assertTrue(idm.hasRole(user, role));
+ }
+
+
+}
diff --git a/services/src/test/resources/META-INF/persistence.xml b/services/src/test/resources/META-INF/persistence.xml
new file mode 100755
index 000000000000..1e5524fe058e
--- /dev/null
+++ b/services/src/test/resources/META-INF/persistence.xml
@@ -0,0 +1,29 @@
+
+
+ org.hibernate.ejb.HibernatePersistence
+
+ org.picketlink.idm.jpa.schema.IdentityObject
+ org.picketlink.idm.jpa.schema.PartitionObject
+ org.picketlink.idm.jpa.schema.RelationshipObject
+ org.picketlink.idm.jpa.schema.RelationshipIdentityObject
+ org.picketlink.idm.jpa.schema.RelationshipIdentityWeakObject
+ org.picketlink.idm.jpa.schema.RelationshipObjectAttribute
+ org.picketlink.idm.jpa.schema.IdentityObjectAttribute
+ org.picketlink.idm.jpa.schema.CredentialObject
+ org.picketlink.idm.jpa.schema.CredentialObjectAttribute
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/services/src/test/resources/testrealm.json b/services/src/test/resources/testrealm.json
new file mode 100644
index 000000000000..6002c79a8398
--- /dev/null
+++ b/services/src/test/resources/testrealm.json
@@ -0,0 +1,101 @@
+{
+ "realm" : "test-realm",
+ "enabled" : true,
+ "tokenLifespan" : 6000,
+ "accessCodeLifespan" : 30,
+ "requiredCredentials" : [
+ {
+ "type" : "Password",
+ "input" : true,
+ "secret" : true
+ }
+ ],
+ "users" : [
+ {
+ "username" : "wburke",
+ "enabled" : true,
+ "attributes" : {
+ "email" : "bburke@redhat.com"
+ },
+ "credentials" : [
+ { "type" : "Password",
+ "value" : "userpassword" }
+ ]
+ },
+ {
+ "username" : "loginclient",
+ "enabled" : true,
+ "credentials" : [
+ { "type" : "Password",
+ "value" : "clientpassword" }
+ ]
+ },
+ {
+ "username" : "admin",
+ "enabled" : true,
+ "credentials" : [
+ { "type" : "Password",
+ "value" : "adminpassword" }
+ ]
+ },
+ {
+ "username" : "oauthclient",
+ "enabled" : true,
+ "credentials" : [
+ { "type" : "Password",
+ "value" : "clientpassword" }
+ ]
+ }
+ ],
+ "roleMappings" : [
+ {
+ "username" : "admin",
+ "roles" : ["admin"]
+ }
+ ],
+ "scopeMappings" : [
+ {
+ "username" : "loginclient",
+ "roles" : ["login"]
+ }
+ ],
+ "resources" : [
+ {
+ "name" : "Application",
+ "roles" : ["admin", "user"],
+ "roleMappings" : [
+ {
+ "username" : "wburke",
+ "roles" : ["user"]
+ },
+ {
+ "username" : "admin",
+ "roles" : ["admin"]
+ }
+ ],
+ "scopeMappings" : [
+ {
+ "username" : "oauthclient",
+ "roles" : ["user"]
+ }
+ ]
+ },
+ {
+ "name" : "OtherApp",
+ "roles" : ["admin", "user"],
+ "roleMappings" : [
+ {
+ "username" : "wburke",
+ "roles" : ["user"]
+ },
+ {
+ "username" : "admin",
+ "roles" : ["admin"]
+ }
+ ]
+ }
+
+ ]
+
+
+}
\ No newline at end of file