/* * 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.adapter.example.authorization; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.keycloak.testsuite.util.IOUtil.loadRealm; import static org.keycloak.testsuite.util.WaitUtils.pause; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import org.jboss.arquillian.container.test.api.Deployer; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.BeforeClass; import org.junit.Test; import org.keycloak.admin.client.resource.AuthorizationResource; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.admin.client.resource.ResourcePermissionsResource; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.testsuite.ProfileAssume; import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest; import org.keycloak.testsuite.util.WaitUtils; import org.openqa.selenium.By; /** * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> */ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleAdapterTest { protected static final String REALM_NAME = "servlet-policy-enforcer-authz"; protected static final String RESOURCE_SERVER_ID = "servlet-policy-enforcer"; @BeforeClass public static void enabled() { ProfileAssume.assumePreview(); } @ArquillianResource private Deployer deployer; @Override public void addAdapterTestRealms(List<RealmRepresentation> testRealms) { testRealms.add( loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json"))); } @Deployment(name = RESOURCE_SERVER_ID, managed = false) public static WebArchive deployment() throws IOException { return exampleDeployment(RESOURCE_SERVER_ID); } @Test public void testPattern1() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/resource/a/b"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 1 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/resource/a/b"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 1 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/resource/a/b"); assertFalse(wasDenied()); }); } @Test public void testPattern2() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/a/resource-a"); assertFalse(wasDenied()); navigateTo("/b/resource-a"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 2 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/a/resource-a"); assertTrue(wasDenied()); navigateTo("/b/resource-a"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 2 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/b/resource-a"); assertFalse(wasDenied()); }); } @Test public void testPattern3() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/a/resource-b"); assertFalse(wasDenied()); navigateTo("/b/resource-b"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 3 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/a/resource-b"); assertTrue(wasDenied()); navigateTo("/b/resource-b"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 3 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/b/resource-b"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 2 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/b/resource-a"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 3 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/a/resource-b"); assertTrue(wasDenied()); navigateTo("/b/resource-a"); assertFalse(wasDenied()); }); } @Test public void testPattern4() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/resource-c"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 4 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/resource-c"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 4 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/resource-c"); assertFalse(wasDenied()); }); } @Test public void testPattern5() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/resource/a/resource-d"); assertFalse(wasDenied()); navigateTo("/resource/b/resource-d"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 5 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/resource/a/resource-d"); assertTrue(wasDenied()); navigateTo("/resource/b/resource-d"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 5 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/resource/b/resource-d"); assertFalse(wasDenied()); }); } @Test public void testPattern6() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/resource/a"); assertFalse(wasDenied()); navigateTo("/resource/b"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 6 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/resource/a"); assertTrue(wasDenied()); navigateTo("/resource/b"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 6 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/resource/b"); assertFalse(wasDenied()); }); } @Test public void testPattern7() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/resource/a/f/b"); assertFalse(wasDenied()); navigateTo("/resource/c/f/d"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 7 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/resource/a/f/b"); assertTrue(wasDenied()); navigateTo("/resource/c/f/d"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 7 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/resource/c/f/d"); assertFalse(wasDenied()); }); } @Test public void testPattern8() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/resource"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 8 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/resource"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 8 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/resource"); assertFalse(wasDenied()); }); } @Test public void testPattern9() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/file/*.suffix"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 9 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/file/*.suffix"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 9 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/file/*.suffix"); assertFalse(wasDenied()); }); } @Test public void testPattern10() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/resource/a/i/b/c/d/e"); assertFalse(wasDenied()); navigateTo("/resource/a/i/b/c/"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 10 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/resource/a/i/b/c/d/e"); assertTrue(wasDenied()); navigateTo("/resource/a/i/b/c/d"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 10 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/resource/a/i/b/c/d"); assertFalse(wasDenied()); }); } @Test public void testPattern11UsingResourceInstancePermission() throws Exception { performTests(() -> { login("alice", "alice"); navigateTo("/api/v1/resource-a"); assertFalse(wasDenied()); navigateTo("/api/v1/resource-b"); assertFalse(wasDenied()); ResourceRepresentation resource = new ResourceRepresentation("/api/v1/resource-c"); resource.setUri(resource.getName()); getAuthorizationResource().resources().create(resource); createResourcePermission(resource.getName() + " permission", resource.getName(), "Default Policy"); login("alice", "alice"); navigateTo(resource.getUri()); assertFalse(wasDenied()); updatePermissionPolicies(resource.getName() + " permission", "Deny Policy"); login("alice", "alice"); navigateTo(resource.getUri()); assertTrue(wasDenied()); updatePermissionPolicies(resource.getName() + " permission", "Default Policy"); login("alice", "alice"); navigateTo(resource.getUri()); assertFalse(wasDenied()); navigateTo("/api/v1"); assertTrue(wasDenied()); navigateTo("/api/v1/"); assertTrue(wasDenied()); navigateTo("/api"); assertTrue(wasDenied()); navigateTo("/api/"); assertTrue(wasDenied()); }); } @Test public void testPriorityOfURIForResource() { performTests(() -> { login("alice", "alice"); navigateTo("/realm_uri"); assertTrue(wasDenied()); navigateTo("/keycloak_json_uri"); assertFalse(wasDenied()); updatePermissionPolicies("Pattern 12 Permission", "Deny Policy"); login("alice", "alice"); navigateTo("/realm_uri"); assertTrue(wasDenied()); navigateTo("/keycloak_json_uri"); assertTrue(wasDenied()); updatePermissionPolicies("Pattern 12 Permission", "Default Policy"); login("alice", "alice"); navigateTo("/realm_uri"); assertTrue(wasDenied()); navigateTo("/keycloak_json_uri"); assertFalse(wasDenied()); }); } private void navigateTo(String path) { this.driver.navigate().to(getResourceServerUrl() + path); } private void performTests(ExceptionRunnable assertion) { performTests(() -> {}, assertion); } private void performTests(ExceptionRunnable beforeDeploy, ExceptionRunnable assertion) { try { beforeDeploy.run(); deployer.deploy(RESOURCE_SERVER_ID); assertion.run(); } catch (FileNotFoundException cause) { throw new RuntimeException("Failed to import authorization settings", cause); } catch (Exception cause) { throw new RuntimeException("Error while executing tests", cause); } finally { deployer.undeploy(RESOURCE_SERVER_ID); } } private AuthorizationResource getAuthorizationResource() { return getClientResource(RESOURCE_SERVER_ID).authorization(); } private ClientResource getClientResource(String clientId) { ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients(); ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0); return clients.get(resourceServer.getId()); } private void logOut() { navigateTo(); By by = By.xpath("//a[text() = 'Sign Out']"); WaitUtils.waitUntilElement(by); this.driver.findElement(by).click(); pause(500); } private void login(String username, String password) { try { navigateTo(); if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) { logOut(); navigateTo(); } this.loginPage.form().login(username, password); navigateTo(); assertFalse(wasDenied()); } catch (Exception cause) { throw new RuntimeException("Login failed", cause); } } private void navigateTo() { this.driver.navigate().to(getResourceServerUrl()); WaitUtils.waitUntilElement(By.xpath("//p[text() = 'Welcome']")); } private boolean wasDenied() { return this.driver.getPageSource().contains("You can not access this resource"); } private URL getResourceServerUrl() { try { return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID); } catch (MalformedURLException e) { throw new RuntimeException("Could not obtain resource server url.", e); } } private void updatePermissionPolicies(String permissionName, String... policyNames) { ResourcePermissionsResource permissions = getAuthorizationResource().permissions().resource(); ResourcePermissionRepresentation permission = permissions.findByName(permissionName); permission.addPolicy(policyNames); permissions.findById(permission.getId()).update(permission); } private void createResourcePermission(String name, String resourceName, String... policyNames) { ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation(); permission.setName(name); permission.addResource(resourceName); permission.addPolicy(policyNames); getAuthorizationResource().permissions().resource().create(permission); } private interface ExceptionRunnable { void run() throws Exception; } }