keycloak-uncached

Changes

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/RequiredActionProviderSimpleRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RequiredActionProviderSimpleRepresentation.java
new file mode 100644
index 0000000..1e0a60d
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/RequiredActionProviderSimpleRepresentation.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.representations.idm;
+
+/**
+ * Some endpoints (like register new required action) doesn't support all the fields (like setEnabled etc).
+ * So this is just simplified version of full RequiredActionProviderRepresentation
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class RequiredActionProviderSimpleRepresentation {
+
+    private String name;
+    private String providerId;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getProviderId() {
+        return providerId;
+    }
+
+    public void setProviderId(String providerId) {
+        this.providerId = providerId;
+    }
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthenticationManagementResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthenticationManagementResource.java
index d5de7be..d28f6be 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthenticationManagementResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthenticationManagementResource.java
@@ -24,6 +24,7 @@ import org.keycloak.representations.idm.AuthenticatorConfigInfoRepresentation;
 import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
 import org.keycloak.representations.idm.ConfigPropertyRepresentation;
 import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
+import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -127,22 +128,17 @@ public interface AuthenticationManagementResource {
     @Path("/executions/{executionId}/config")
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
-    Response newExecutionConfig(@PathParam("executionId") String execution, AuthenticatorConfigRepresentation config);
-
-    @Path("/executions/{executionId}/config/{id}")
-    @GET
-    @Produces(MediaType.APPLICATION_JSON)
-    AuthenticatorConfigRepresentation getAuthenticatorConfig(@PathParam("executionId") String execution,@PathParam("id") String id);
+    Response newExecutionConfig(@PathParam("executionId") String executionId, AuthenticatorConfigRepresentation config);
 
     @Path("unregistered-required-actions")
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    List<Map<String, String>> getUnregisteredRequiredActions();
+    List<RequiredActionProviderSimpleRepresentation> getUnregisteredRequiredActions();
 
     @Path("register-required-action")
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
-    void registereRequiredAction(Map<String, String> data);
+    void registerRequiredAction(RequiredActionProviderSimpleRepresentation action);
 
     @Path("required-actions")
     @GET
@@ -161,7 +157,7 @@ public interface AuthenticationManagementResource {
 
     @Path("required-actions/{alias}")
     @DELETE
-    void updateRequiredAction(@PathParam("alias") String alias);
+    void removeRequiredAction(@PathParam("alias") String alias);
 
     @Path("config-description/{providerId}")
     @GET
@@ -173,11 +169,6 @@ public interface AuthenticationManagementResource {
     @Produces(MediaType.APPLICATION_JSON)
     Map<String, List<ConfigPropertyRepresentation>> getPerClientConfigDescription();
 
-    @Path("config")
-    @POST
-    @Consumes(MediaType.APPLICATION_JSON)
-    Response createAuthenticatorConfig(AuthenticatorConfigRepresentation config);
-
     @Path("config/{id}")
     @GET
     @Produces(MediaType.APPLICATION_JSON)
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index 0a39094..a720a32 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -701,6 +701,7 @@ public class AuthenticationManagementResource {
      *
      * @param execution Execution id
      * @param id Configuration id
+     * @deprecated Use rather {@link #getAuthenticatorConfig(String)}
      */
     @Path("/executions/{executionId}/config/{id}")
     @GET
@@ -760,7 +761,7 @@ public class AuthenticationManagementResource {
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @NoCache
-    public void registereRequiredAction(Map<String, String> data) {
+    public void registerRequiredAction(Map<String, String> data) {
         auth.requireManage();
 
         String providerId = data.get("providerId");
@@ -857,7 +858,7 @@ public class AuthenticationManagementResource {
      */
     @Path("required-actions/{alias}")
     @DELETE
-    public void updateRequiredAction(@PathParam("alias") String alias) {
+    public void removeRequiredAction(@PathParam("alias") String alias) {
         auth.requireManage();
 
         RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
@@ -937,6 +938,7 @@ public class AuthenticationManagementResource {
     /**
      * Create new authenticator configuration
      * @param rep JSON describing new authenticator configuration
+     * @deprecated Use {@link #newExecutionConfig(String, AuthenticatorConfigRepresentation)} instead
      */
     @Path("config")
     @POST
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/AbstractCustomAccountManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/AbstractCustomAccountManagementTest.java
index c8f6d56..f73deb7 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/AbstractCustomAccountManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/AbstractCustomAccountManagementTest.java
@@ -18,8 +18,7 @@
 package org.keycloak.testsuite.account.custom;
 
 import java.util.List;
-import javax.ws.rs.core.GenericType;
-import javax.ws.rs.core.Response;
+
 import org.junit.Before;
 import org.keycloak.admin.client.resource.AuthenticationManagementResource;
 import org.keycloak.models.AuthenticationExecutionModel;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AuthenticatorConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AuthenticatorConfigTest.java
new file mode 100644
index 0000000..c5f7ed1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AuthenticatorConfigTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.admin.authentication;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator;
+import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticatorFactory;
+import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
+import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
+import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
+import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.admin.ApiUtil;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AuthenticatorConfigTest extends AbstractAuthenticationTest {
+
+    private String executionId;
+
+    @Before
+    public void beforeConfigTest() {
+        Response response = authMgmtResource.createFlow(newFlow("firstBrokerLogin2", "firstBrokerLogin2", "basic-flow", true, false));
+        Assert.assertEquals(201, response.getStatus());
+        response.close();
+
+        HashMap<String, String> params = new HashMap<>();
+        params.put("provider", IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID);
+        authMgmtResource.addExecution("firstBrokerLogin2", params);
+
+        List<AuthenticationExecutionInfoRepresentation> executionReps = authMgmtResource.getExecutions("firstBrokerLogin2");
+        AuthenticationExecutionInfoRepresentation exec = findExecutionByProvider(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, executionReps);
+        Assert.assertNotNull(exec);
+        executionId = exec.getId();
+    }
+
+    @Override
+    public void afterAbstractKeycloakTest() {
+        AuthenticationFlowRepresentation flowRep = findFlowByAlias("firstBrokerLogin2", authMgmtResource.getFlows());
+        authMgmtResource.deleteFlow(flowRep.getId());
+    }
+
+
+    @Test
+    public void testCreateConfig() {
+        AuthenticatorConfigRepresentation cfg = newConfig("foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true");
+
+        // Attempt to create config for non-existent execution
+        Response response = authMgmtResource.newExecutionConfig("exec-id-doesnt-exists", cfg);
+        Assert.assertEquals(404, response.getStatus());
+        response.close();
+
+        // Create config success
+        String cfgId = createConfig(executionId, cfg);
+
+        // Assert found
+        AuthenticatorConfigRepresentation cfgRep = authMgmtResource.getAuthenticatorConfig(cfgId);
+        assertConfig(cfgRep, cfgId, "foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true");
+
+        // Cleanup
+        authMgmtResource.removeAuthenticatorConfig(cfgId);
+    }
+
+
+    @Test
+    public void testUpdateConfig() {
+        AuthenticatorConfigRepresentation cfg = newConfig("foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true");
+        String cfgId = createConfig(executionId, cfg);
+        AuthenticatorConfigRepresentation cfgRep = authMgmtResource.getAuthenticatorConfig(cfgId);
+
+        // Try to update not existent config
+        try {
+            authMgmtResource.updateAuthenticatorConfig("not-existent", cfgRep);
+            Assert.fail("Config didn't found");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Assert nothing changed
+        cfgRep = authMgmtResource.getAuthenticatorConfig(cfgId);
+        assertConfig(cfgRep, cfgId, "foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true");
+
+        // Update success
+        cfgRep.setAlias("foo2");
+        cfgRep.getConfig().put("configKey2", "configValue2");
+        authMgmtResource.updateAuthenticatorConfig(cfgRep.getId(), cfgRep);
+
+        // Assert updated
+        cfgRep = authMgmtResource.getAuthenticatorConfig(cfgRep.getId());
+        assertConfig(cfgRep, cfgId, "foo2",
+                IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true",
+                "configKey2", "configValue2");
+    }
+
+
+    @Test
+    public void testRemoveConfig() {
+        AuthenticatorConfigRepresentation cfg = newConfig("foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true");
+        String cfgId = createConfig(executionId, cfg);
+        AuthenticatorConfigRepresentation cfgRep = authMgmtResource.getAuthenticatorConfig(cfgId);
+
+        // Assert execution has our config
+        AuthenticationExecutionInfoRepresentation execution = findExecutionByProvider(
+                IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, authMgmtResource.getExecutions("firstBrokerLogin2"));
+        Assert.assertEquals(cfgRep.getId(), execution.getAuthenticationConfig());
+
+
+        // Test remove not-existent
+        try {
+            authMgmtResource.removeAuthenticatorConfig("not-existent");
+            Assert.fail("Config didn't found");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Test remove our config
+        authMgmtResource.removeAuthenticatorConfig(cfgRep.getId());
+
+        // Assert config not found
+        try {
+            authMgmtResource.getAuthenticatorConfig(cfgRep.getId());
+            Assert.fail("Not expected to find config");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Assert execution doesn't have our config
+        execution = findExecutionByProvider(
+                IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, authMgmtResource.getExecutions("firstBrokerLogin2"));
+        Assert.assertNull(execution.getAuthenticationConfig());
+    }
+
+
+    private String createConfig(String executionId, AuthenticatorConfigRepresentation cfg) {
+        Response resp = authMgmtResource.newExecutionConfig(executionId, cfg);
+        Assert.assertEquals(201, resp.getStatus());
+        String cfgId = ApiUtil.getCreatedId(resp);
+        Assert.assertNotNull(cfgId);
+        return cfgId;
+    }
+
+    private AuthenticatorConfigRepresentation newConfig(String alias, String cfgKey, String cfgValue) {
+        AuthenticatorConfigRepresentation cfg = new AuthenticatorConfigRepresentation();
+        cfg.setAlias(alias);
+        Map<String, String> cfgMap = new HashMap<>();
+        cfgMap.put(cfgKey, cfgValue);
+        cfg.setConfig(cfgMap);
+        return cfg;
+    }
+
+    private void assertConfig(AuthenticatorConfigRepresentation cfgRep, String id, String alias, String... fields) {
+        Assert.assertEquals(id, cfgRep.getId());
+        Assert.assertEquals(alias, cfgRep.getAlias());
+        Assert.assertMap(cfgRep.getConfig(), fields);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java
index 7852e22..be0b9c4 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java
@@ -19,15 +19,19 @@ package org.keycloak.testsuite.admin.authentication;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.keycloak.authentication.AuthenticationFlow;
+import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
+import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
 import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
 import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 
 import javax.ws.rs.BadRequestException;
-import javax.ws.rs.core.GenericType;
+import javax.ws.rs.NotFoundException;
 import javax.ws.rs.core.Response;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
@@ -44,6 +48,15 @@ public class ExecutionTest extends AbstractAuthenticationTest {
             authMgmtResource.addExecution("browser", params);
             Assert.fail("add execution to built-in flow should fail");
         } catch (BadRequestException expected) {
+            // Expected
+        }
+
+        // try add execution to not-existent flow
+        try {
+            authMgmtResource.addExecution("not-existent", params);
+            Assert.fail("add execution to not-existent flow should fail");
+        } catch (BadRequestException expected) {
+            // Expected
         }
 
         // copy built-in flow so we get a new editable flow
@@ -95,6 +108,16 @@ public class ExecutionTest extends AbstractAuthenticationTest {
         rep.setAuthenticator("auth-cookie");
         rep.setRequirement(OPTIONAL);
 
+        // Should fail - missing parent flow
+        response = authMgmtResource.addExecution(rep);
+        try {
+            Assert.assertEquals("added execution missing parent flow", 400, response.getStatus());
+        } finally {
+            response.close();
+        }
+
+        // Should fail - not existent parent flow
+        rep.setParentFlow("not-existent-id");
         response = authMgmtResource.addExecution(rep);
         try {
             Assert.assertEquals("added execution missing parent flow", 400, response.getStatus());
@@ -102,6 +125,16 @@ public class ExecutionTest extends AbstractAuthenticationTest {
             response.close();
         }
 
+        // Should fail - add execution to builtin flow
+        AuthenticationFlowRepresentation browserFlow = findFlowByAlias("browser", authMgmtResource.getFlows());
+        rep.setParentFlow(browserFlow.getId());
+        response = authMgmtResource.addExecution(rep);
+        try {
+            Assert.assertEquals("added execution to builtin flow", 400, response.getStatus());
+        } finally {
+            response.close();
+        }
+
         // get Copy of browser flow id, and set it on execution
         List<AuthenticationFlowRepresentation> flows = authMgmtResource.getFlows();
         AuthenticationFlowRepresentation flow = findFlowByAlias("Copy of browser", flows);
@@ -145,4 +178,70 @@ public class ExecutionTest extends AbstractAuthenticationTest {
         AuthenticationExecutionInfoRepresentation exec2 = findExecutionByProvider("auth-cookie", executionReps);
         compareExecution(exec, exec2);
     }
+
+    @Test
+    public void testClientFlowExecutions() {
+        // Create client flow
+        AuthenticationFlowRepresentation clientFlow = newFlow("new-client-flow", "desc", AuthenticationFlow.CLIENT_FLOW, true, false);
+        Response response = authMgmtResource.createFlow(clientFlow);
+        Assert.assertEquals(201, response.getStatus());
+        response.close();
+
+        // Add execution to it
+        Map<String, String> executionData = new HashMap<>();
+        executionData.put("provider", ClientIdAndSecretAuthenticator.PROVIDER_ID);
+        authMgmtResource.addExecution("new-client-flow", executionData);
+
+        // Check executions of not-existent flow - SHOULD FAIL
+        try {
+            authMgmtResource.getExecutions("not-existent");
+            Assert.fail("Not expected to find executions");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Check existent executions
+        List<AuthenticationExecutionInfoRepresentation> executions = authMgmtResource.getExecutions("new-client-flow");
+        AuthenticationExecutionInfoRepresentation executionRep = findExecutionByProvider(ClientIdAndSecretAuthenticator.PROVIDER_ID, executions);
+        Assert.assertNotNull(executionRep);
+
+        // Update execution with not-existent flow - SHOULD FAIL
+        try {
+            authMgmtResource.updateExecutions("not-existent", executionRep);
+            Assert.fail("Not expected to update execution with not-existent flow");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Update execution with not-existent ID - SHOULD FAIL
+        try {
+            AuthenticationExecutionInfoRepresentation executionRep2 = new AuthenticationExecutionInfoRepresentation();
+            executionRep2.setId("not-existent");
+            authMgmtResource.updateExecutions("new-client-flow", executionRep2);
+            Assert.fail("Not expected to update not-existent execution");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Update success
+        executionRep.setRequirement(ALTERNATIVE);
+        authMgmtResource.updateExecutions("new-client-flow", executionRep);
+
+        // Check updated
+        executionRep = findExecutionByProvider(ClientIdAndSecretAuthenticator.PROVIDER_ID, authMgmtResource.getExecutions("new-client-flow"));
+        Assert.assertEquals(ALTERNATIVE, executionRep.getRequirement());
+
+        // Remove execution with not-existent ID
+        try {
+            authMgmtResource.removeExecution("not-existent");
+            Assert.fail("Didn't expect to find execution");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Successfuly remove execution and flow
+        authMgmtResource.removeExecution(executionRep.getId());
+        AuthenticationFlowRepresentation rep = findFlowByAlias("new-client-flow", authMgmtResource.getFlows());
+        authMgmtResource.deleteFlow(rep.getId());
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java
index 301da3f..80d17bb 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java
@@ -23,6 +23,7 @@ import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentat
 import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 
 import javax.ws.rs.BadRequestException;
+import javax.ws.rs.NotFoundException;
 import javax.ws.rs.core.Response;
 import java.util.HashMap;
 import java.util.List;
@@ -55,6 +56,15 @@ public class FlowTest extends AbstractAuthenticationTest {
             response.close();
         }
 
+        // try create flow without alias
+        response = authMgmtResource.createFlow(newFlow(null, "Browser flow", "basic-flow", true, false));
+        try {
+            Assert.assertEquals("createFlow using the alias of existing flow should fail", 409, response.getStatus());
+        } finally {
+            response.close();
+        }
+
+
         // create new flow that should succeed
         AuthenticationFlowRepresentation newFlow = newFlow("browser-2", "Browser flow", "basic-flow", true, false);
         response = authMgmtResource.createFlow(newFlow);
@@ -71,25 +81,46 @@ public class FlowTest extends AbstractAuthenticationTest {
         Assert.assertNotNull("created flow visible in parent", found);
         compareFlows(newFlow, found);
 
+        // check lookup flow with unexistent ID
+        try {
+            authMgmtResource.getFlow("id-123-notExistent");
+            Assert.fail("Not expected to find unexistent flow");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
         // check that new flow is returned individually
         AuthenticationFlowRepresentation found2 = authMgmtResource.getFlow(found.getId());
         Assert.assertNotNull("created flow visible directly", found2);
         compareFlows(newFlow, found2);
 
 
-        // add execution flow using a different method
+        // add execution flow to some parent flow
         Map<String, String> data = new HashMap<>();
         data.put("alias", "SomeFlow");
         data.put("type", "basic-flow");
         data.put("description", "Test flow");
         data.put("provider", "registration-page-form");
 
+        // inexistent parent flow - should fail
         try {
             authMgmtResource.addExecutionFlow("inexistent-parent-flow-alias", data);
             Assert.fail("addExecutionFlow for inexistent parent should have failed");
         } catch (Exception expected) {
+            // Expected
+        }
+
+        // already existent flow - should fail
+        try {
+            data.put("alias", "browser");
+            authMgmtResource.addExecutionFlow("browser-2", data);
+            Assert.fail("addExecutionFlow should have failed as browser flow already exists");
+        } catch (Exception expected) {
+            // Expected
         }
 
+        // Successfully add flow
+        data.put("alias", "SomeFlow");
         authMgmtResource.addExecutionFlow("browser-2", data);
 
         // check that new flow is returned in a children list
@@ -117,6 +148,14 @@ public class FlowTest extends AbstractAuthenticationTest {
         flows = authMgmtResource.getFlows();
         found = findFlowByAlias("browser-2", flows);
         Assert.assertNull("flow deleted", found);
+
+        // Check deleting flow second time will fail
+        try {
+            authMgmtResource.deleteFlow("id-123-notExistent");
+            Assert.fail("Not expected to delete flow, which doesn't exists");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
     }
 
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java
index 0f8487b..b243711 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java
@@ -61,7 +61,7 @@ public class InitialFlowsTest extends AbstractAuthenticationTest {
                 // separately load referenced configurations
                 String configId = exec.getAuthenticationConfig();
                 if (configId != null && !configs.containsKey(configId)) {
-                    configs.put(configId, authMgmtResource.getAuthenticatorConfig(exec.getId(), configId));
+                    configs.put(configId, authMgmtResource.getAuthenticatorConfig(configId));
                 }
             }
             result.add(new FlowExecutions(flow, executionReps));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
index 0580062..a6f899c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
@@ -17,8 +17,11 @@
 
 package org.keycloak.testsuite.admin.authentication;
 
-import org.junit.Assert;
 import org.junit.Test;
+import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticatorFactory;
+import org.keycloak.representations.idm.AuthenticatorConfigInfoRepresentation;
+import org.keycloak.representations.idm.ConfigPropertyRepresentation;
+import org.keycloak.testsuite.Assert;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -28,6 +31,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
+import javax.ws.rs.NotFoundException;
+
 /**
  * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
  */
@@ -80,6 +85,41 @@ public class ProvidersTest extends AbstractAuthenticationTest {
         compareProviders(expected, result);
     }
 
+    @Test
+    public void testPerClientConfigDescriptions() {
+        Map<String, List<ConfigPropertyRepresentation>> configs = authMgmtResource.getPerClientConfigDescription();
+        Assert.assertTrue(configs.containsKey("client-jwt"));
+        Assert.assertTrue(configs.containsKey("client-secret"));
+        Assert.assertTrue(configs.containsKey("testsuite-client-passthrough"));
+        Assert.assertTrue(configs.get("client-jwt").isEmpty());
+        Assert.assertTrue(configs.get("client-secret").isEmpty());
+        List<ConfigPropertyRepresentation> cfg = configs.get("testsuite-client-passthrough");
+        Assert.assertProviderConfigProperty(cfg.get(0), "passthroughauth.foo", "Foo Property", null,
+                "Foo Property of this authenticator, which does nothing", "String");
+        Assert.assertProviderConfigProperty(cfg.get(1), "passthroughauth.bar", "Bar Property", null,
+                "Bar Property of this authenticator, which does nothing", "boolean");
+    }
+
+    @Test
+    public void testAuthenticatorConfigDescription() {
+        // Try some not-existent provider
+        try {
+            authMgmtResource.getAuthenticatorConfigDescription("not-existent");
+            Assert.fail("Don't expected to find provider 'not-existent'");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        AuthenticatorConfigInfoRepresentation infoRep = authMgmtResource.getAuthenticatorConfigDescription(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID);
+        Assert.assertEquals("Create User If Unique", infoRep.getName());
+        Assert.assertEquals(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, infoRep.getProviderId());
+        Assert.assertEquals("Detect if there is existing Keycloak account with same email like identity provider. If no, create new user", infoRep.getHelpText());
+        Assert.assertEquals(1, infoRep.getProperties().size());
+        Assert.assertProviderConfigProperty(infoRep.getProperties().get(0), "require.password.update.after.registration", "Require Password Update After Registration",
+                null, "If this option is true and new user is successfully imported from Identity Provider to Keycloak (there is no duplicated email or username detected in Keycloak DB), then this user is required to update his password",
+                "boolean");
+    }
+
 
     @Test
     public void testInitialAuthenticationProviders() {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java
index 0ac6864..7418cb1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java
@@ -20,6 +20,8 @@ package org.keycloak.testsuite.admin.authentication;
 import org.junit.Assert;
 import org.junit.Test;
 import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
+import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
+import org.keycloak.testsuite.actions.DummyRequiredActionFactory;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -28,6 +30,8 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import javax.ws.rs.NotFoundException;
+
 /**
  * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
  */
@@ -63,6 +67,58 @@ public class RequiredActionsTest extends AbstractAuthenticationTest {
         compareRequiredAction(forUpdate, updated);
     }
 
+    @Test
+    public void testCRUDRequiredAction() {
+        // Just Dummy RequiredAction is not registered in the realm
+        List<RequiredActionProviderSimpleRepresentation> result = authMgmtResource.getUnregisteredRequiredActions();
+        Assert.assertEquals(1, result.size());
+        RequiredActionProviderSimpleRepresentation action = result.get(0);
+        Assert.assertEquals(DummyRequiredActionFactory.PROVIDER_ID, action.getProviderId());
+        Assert.assertEquals("Dummy Action", action.getName());
+
+        // Register it
+        authMgmtResource.registerRequiredAction(action);
+
+        // Try to find not-existent action - should fail
+        try {
+            authMgmtResource.getRequiredAction("not-existent");
+            Assert.fail("Didn't expect to find requiredAction of alias 'not-existent'");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Find existent
+        RequiredActionProviderRepresentation rep = authMgmtResource.getRequiredAction(DummyRequiredActionFactory.PROVIDER_ID);
+        compareRequiredAction(rep, newRequiredAction(DummyRequiredActionFactory.PROVIDER_ID, "Dummy Action",
+                true, false, Collections.emptyMap()));
+
+        // Update not-existent - should fail
+        try {
+            authMgmtResource.updateRequiredAction("not-existent", rep);
+            Assert.fail("Not expected to update not-existent requiredAction");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Update (set it as defaultAction)
+        rep.setDefaultAction(true);
+        authMgmtResource.updateRequiredAction(DummyRequiredActionFactory.PROVIDER_ID, rep);
+        compareRequiredAction(rep, newRequiredAction(DummyRequiredActionFactory.PROVIDER_ID, "Dummy Action",
+                true, true, Collections.emptyMap()));
+
+        // Remove unexistent - should fail
+        try {
+            authMgmtResource.removeRequiredAction("not-existent");
+            Assert.fail("Not expected to remove not-existent requiredAction");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
+        // Remove success
+        authMgmtResource.removeRequiredAction(DummyRequiredActionFactory.PROVIDER_ID);
+
+    }
+
 
     private RequiredActionProviderRepresentation findRequiredActionByAlias(String alias, List<RequiredActionProviderRepresentation> list) {
         for (RequiredActionProviderRepresentation a: list) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ShiftExecutionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ShiftExecutionTest.java
index f9d4f00..9ce4f53 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ShiftExecutionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ShiftExecutionTest.java
@@ -21,7 +21,8 @@ import org.junit.Assert;
 import org.junit.Test;
 import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
 
-import javax.ws.rs.core.GenericType;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.BadRequestException;
 import javax.ws.rs.core.Response;
 import java.util.HashMap;
 import java.util.List;
@@ -50,6 +51,14 @@ public class ShiftExecutionTest extends AbstractAuthenticationTest {
         AuthenticationExecutionInfoRepresentation last = executions.get(executions.size() - 1);
         AuthenticationExecutionInfoRepresentation oneButLast = executions.get(executions.size() - 2);
 
+        // Not possible to raisePriority of not-existent flow
+        try {
+            authMgmtResource.raisePriority("not-existent");
+            Assert.fail("Not expected to raise priority of not existent flow");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
         // shift last execution up
         authMgmtResource.raisePriority(last.getId());
 
@@ -61,6 +70,14 @@ public class ShiftExecutionTest extends AbstractAuthenticationTest {
         Assert.assertEquals("Execution shifted up - N", last.getId(), oneButLast2.getId());
         Assert.assertEquals("Execution shifted up - N-1", oneButLast.getId(), last2.getId());
 
+        // Not possible to lowerPriority of not-existent flow
+        try {
+            authMgmtResource.lowerPriority("not-existent");
+            Assert.fail("Not expected to raise priority of not existent flow");
+        } catch (NotFoundException nfe) {
+            // Expected
+        }
+
         // shift one before last down
         authMgmtResource.lowerPriority(oneButLast2.getId());
 
@@ -72,4 +89,29 @@ public class ShiftExecutionTest extends AbstractAuthenticationTest {
         Assert.assertEquals("Execution shifted down - N", last.getId(), last2.getId());
         Assert.assertEquals("Execution shifted down - N-1", oneButLast.getId(), oneButLast2.getId());
     }
+
+    @Test
+    public void testBuiltinShiftNotAllowed() {
+        List<AuthenticationExecutionInfoRepresentation> executions = authMgmtResource.getExecutions("browser");
+
+        AuthenticationExecutionInfoRepresentation last = executions.get(executions.size() - 1);
+        AuthenticationExecutionInfoRepresentation oneButLast = executions.get(executions.size() - 2);
+
+        // Not possible to raise - It's builtin flow
+        try {
+            authMgmtResource.raisePriority(last.getId());
+            Assert.fail("Not expected to raise priority of builtin flow");
+        } catch (BadRequestException nfe) {
+            // Expected
+        }
+
+        // Not possible to lower - It's builtin flow
+        try {
+            authMgmtResource.lowerPriority(oneButLast.getId());
+            Assert.fail("Not expected to lower priority of builtin flow");
+        } catch (BadRequestException nfe) {
+            // Expected
+        }
+
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
index 29a3661..3b5d1ce 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
@@ -38,6 +38,7 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
+import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.UserFederationMapperRepresentation;
 import org.keycloak.representations.idm.UserFederationProviderRepresentation;
@@ -349,16 +350,14 @@ public class PermissionsTest extends AbstractKeycloakTest {
         invoke((realm) -> { realm.flows().lowerPriority("nosuch"); }, Resource.REALM, true);
         invoke((realm) -> { realm.flows().removeExecution("nosuch"); }, Resource.REALM, true);
         invoke((realm, response) -> { response.set(realm.flows().newExecutionConfig("nosuch", new AuthenticatorConfigRepresentation())); }, Resource.REALM, true);
-        invoke((realm) -> { realm.flows().getAuthenticatorConfig("nosuch", "nosuch"); }, Resource.REALM, false);
         invoke((realm) -> { realm.flows().getUnregisteredRequiredActions(); }, Resource.REALM, false);
-        invoke((realm) -> { realm.flows().registereRequiredAction(new HashMap<>()); }, Resource.REALM, true);
+        invoke((realm) -> { realm.flows().registerRequiredAction(new RequiredActionProviderSimpleRepresentation()); }, Resource.REALM, true);
         invoke((realm) -> { realm.flows().getRequiredActions(); }, Resource.REALM, false, true);
         invoke((realm) -> { realm.flows().getRequiredAction("nosuch"); }, Resource.REALM, false);
-        invoke((realm) -> { realm.flows().updateRequiredAction("nosuch"); }, Resource.REALM, true);
+        invoke((realm) -> { realm.flows().removeRequiredAction("nosuch"); }, Resource.REALM, true);
         invoke((realm) -> { realm.flows().updateRequiredAction("nosuch", new RequiredActionProviderRepresentation()); }, Resource.REALM, true);
         invoke((realm) -> { realm.flows().getAuthenticatorConfigDescription("nosuch"); }, Resource.REALM, false);
         invoke((realm) -> { realm.flows().getPerClientConfigDescription(); }, Resource.REALM, false, true);
-        invoke((realm, response) -> { response.set(realm.flows().createAuthenticatorConfig(new AuthenticatorConfigRepresentation())); }, Resource.REALM, true);
         invoke((realm) -> { realm.flows().getAuthenticatorConfig("nosuch"); }, Resource.REALM, false);
         invoke((realm) -> { realm.flows().removeAuthenticatorConfig("nosuch"); }, Resource.REALM, true);
         invoke((realm) -> { realm.flows().updateAuthenticatorConfig("nosuch", new AuthenticatorConfigRepresentation()); }, Resource.REALM, true);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java
index 72989a5..c1267ff 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java
@@ -270,15 +270,7 @@ public class UserFederationMapperTest extends AbstractAdminTest {
         Assert.assertEquals("ldap-1", rep.getFederationProviderDisplayName());
         Assert.assertEquals(federationMapperType, rep.getFederationMapperType());
 
-        if (config == null) {
-            config = new String[] {};
-        }
-        Assert.assertEquals(rep.getConfig().size() * 2, config.length);
-        for (int i=0 ; i<config.length ; i+=2) {
-            String key = config[i];
-            String value = config[i+1];
-            Assert.assertEquals(value, rep.getConfig().get(key));
-        }
+        Assert.assertMap(rep.getConfig(), config);
     }
 
     private UserFederationMapperRepresentation findMapperByName(List<UserFederationMapperRepresentation> mappers, String name) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java
index 7e702a0..4d0f948 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java
@@ -31,7 +31,6 @@ import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.LDAPConstants;
 import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
-import org.keycloak.representations.idm.ConfigPropertyRepresentation;
 import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
 import org.keycloak.representations.idm.UserFederationProviderRepresentation;
 import org.keycloak.representations.idm.UserFederationSyncResultRepresentation;
@@ -65,8 +64,8 @@ public class UserFederationTest extends AbstractAdminTest {
         Assert.assertTrue(dummyConfiguredProvider.getOptions() == null || dummyConfiguredProvider.getOptions().isEmpty());
         Assert.assertEquals("Dummy User Federation Provider Help Text", dummyConfiguredProvider.getHelpText());
         Assert.assertEquals(2, dummyConfiguredProvider.getProperties().size());
-        assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(0), "prop1", "Prop1", "prop1Default", "Prop1 HelpText", ProviderConfigProperty.STRING_TYPE);
-        assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(1), "prop2", "Prop2", "true", "Prop2 HelpText", ProviderConfigProperty.BOOLEAN_TYPE);
+        Assert.assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(0), "prop1", "Prop1", "prop1Default", "Prop1 HelpText", ProviderConfigProperty.STRING_TYPE);
+        Assert.assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(1), "prop2", "Prop2", "true", "Prop2 HelpText", ProviderConfigProperty.BOOLEAN_TYPE);
 
         try {
             userFederation().getProviderFactory("not-existent");
@@ -80,14 +79,6 @@ public class UserFederationTest extends AbstractAdminTest {
         return realm.userFederation();
     }
 
-    private void assertProviderConfigProperty(ConfigPropertyRepresentation property, String name, String label, String defaultValue, String helpText, String type) {
-        Assert.assertEquals(name, property.getName());
-        Assert.assertEquals(label, property.getLabel());
-        Assert.assertEquals(defaultValue, property.getDefaultValue());
-        Assert.assertEquals(helpText, property.getHelpText());
-        Assert.assertEquals(type, property.getType());
-    }
-
 
     @Test
     public void testCreateProvider() {
@@ -332,16 +323,8 @@ public class UserFederationTest extends AbstractAdminTest {
         Assert.assertEquals(fullSyncPeriod, rep.getFullSyncPeriod());
         Assert.assertEquals(changeSyncPeriod, rep.getChangedSyncPeriod());
         Assert.assertEquals(lastSync, rep.getLastSync());
-        if (config == null) {
-            config = new String[] {};
-        }
 
-        Assert.assertEquals(rep.getConfig().size() * 2, config.length);
-        for (int i=0 ; i<config.length ; i+=2) {
-            String key = config[i];
-            String value = config[i+1];
-            Assert.assertEquals(value, rep.getConfig().get(key));
-        }
+        Assert.assertMap(rep.getConfig(), config);
     }
 
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java
index 267c832..fda9990 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java
@@ -18,6 +18,7 @@
 package org.keycloak.testsuite;
 
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ConfigPropertyRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
@@ -27,6 +28,7 @@ import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentat
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -74,4 +76,34 @@ public class Assert extends org.junit.Assert {
 
         throw new IllegalArgumentException();
     }
+
+
+    /**
+     * Assert all the fields from map available. Array "expected" contains pairs when first value from pair is expected key
+     * and second is the expected value from the map for target key.
+     *
+     * Example config = {"key1" -> "value1" , "key2" -> "value2" }
+     * then assertMap(config, "key1", "value1", "key2", "value2" will return true
+     *
+     */
+    public static void assertMap(Map<String, String> config, String... expected) {
+        if (expected == null) {
+            expected = new String[] {};
+        }
+
+        Assert.assertEquals(config.size() * 2, expected.length);
+        for (int i=0 ; i<expected.length ; i+=2) {
+            String key = expected[i];
+            String value = expected[i+1];
+            Assert.assertEquals(value, config.get(key));
+        }
+    }
+
+    public static void assertProviderConfigProperty(ConfigPropertyRepresentation property, String name, String label, String defaultValue, String helpText, String type) {
+        Assert.assertEquals(name, property.getName());
+        Assert.assertEquals(label, property.getLabel());
+        Assert.assertEquals(defaultValue, property.getDefaultValue());
+        Assert.assertEquals(helpText, property.getHelpText());
+        Assert.assertEquals(type, property.getType());
+    }
 }