diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/regex/RegexPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/regex/RegexPolicyProvider.java index 1e7f050e553..501fa56ba24 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/regex/RegexPolicyProvider.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/regex/RegexPolicyProvider.java @@ -73,7 +73,9 @@ public class RegexPolicyProvider implements PolicyProvider { } private String getClaimValue(Evaluation evaluation, RegexPolicyRepresentation policy) { - Attributes attributes = evaluation.getContext().getIdentity().getAttributes(); + Attributes attributes = policy.isTargetContextAttributes() + ? evaluation.getContext().getAttributes() + : evaluation.getContext().getIdentity().getAttributes(); String targetClaim = policy.getTargetClaim(); try { diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/regex/RegexPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/regex/RegexPolicyProviderFactory.java index 3ab77816cf5..e5104a0cdf8 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/regex/RegexPolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/regex/RegexPolicyProviderFactory.java @@ -81,6 +81,7 @@ public class RegexPolicyProviderFactory implements PolicyProviderFactory { const { t } = useTranslation(); const { + control, register, formState: { errors }, } = useFormContext(); @@ -54,6 +55,29 @@ export const Regex = () => { {...register("pattern", { required: true })} /> + + } + > + ( + + )} + /> + ); }; diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index ec47720d52e..3c4ca360838 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -1241,7 +1241,7 @@ public class RepresentationToModel { PolicyProviderFactory provider = authorization.getProviderFactory(model.getType()); if (provider == null) { - throw new RuntimeException("Could find policy provider with type [" + model.getType() + "]"); + throw new RuntimeException("Couldn't find policy provider with type [" + model.getType() + "]"); } if (representation instanceof PolicyRepresentation) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/RegexPolicyTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/RegexPolicyTest.java index c359edf9c07..467d944d9eb 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/RegexPolicyTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/RegexPolicyTest.java @@ -107,6 +107,7 @@ public class RegexPolicyTest extends AbstractAuthzTest { .user(UserBuilder.create().username("my-user").password("password").addAttribute("canCreateItems","true")) .user(UserBuilder.create().username("my-user2").password("password").addAttribute("canCreateItems","false")) .user(UserBuilder.create().username("my-user3").password("password").addAttribute("otherClaim","something")) + .user(UserBuilder.create().username("context-user").password("password").addAttribute("custom", "foo")) .group(GroupBuilder.create().name("ADMIN").singleAttribute("attribute","example").build()) .user(UserBuilder.create().username("admin").password("password").addGroups("ADMIN")) @@ -125,6 +126,7 @@ public class RegexPolicyTest extends AbstractAuthzTest { createResource("Resource D"); createResource("Resource E"); createResource("Resource ITEM"); + createResource("Resource CONTEXT"); ScopeRepresentation scopeRead = new ScopeRepresentation(); scopeRead.setName("read"); ScopeRepresentation scopeDelete = new ScopeRepresentation(); @@ -141,6 +143,7 @@ public class RegexPolicyTest extends AbstractAuthzTest { createRegexPolicy("Regex json-array Policy", "json-complex.some-array[1]", "bar"); createRegexPolicy("Regex user attribute to json-Complex Policy", "customPermissions.canCreateItems", "true"); createRegexPolicyExtended("attribute-policy","attributes.values","^example$",Logic.POSITIVE); + createRegexPolicy("Regex context policy", "custom", "^foo$", true); createResourcePermission("Resource A Permission", "Resource A", "Regex foo Policy"); createResourcePermission("Resource B Permission", "Resource B", "Regex bar Policy"); @@ -151,7 +154,7 @@ public class RegexPolicyTest extends AbstractAuthzTest { createResourcePermission("Resource ITEM Permission", "Resource ITEM", "Regex user attribute to json-Complex Policy"); createResourceScopesPermissionExtended("read-permission","service",DecisionStrategy.UNANIMOUS,"read","attribute-policy"); - + createResourcePermission("Resource CONTEXT Permission", "Resource CONTEXT", "Regex context policy"); } private void createResource(String name) { @@ -170,11 +173,16 @@ public class RegexPolicyTest extends AbstractAuthzTest { private void createRegexPolicy(String name, String targetClaim, String pattern) { + createRegexPolicy(name, targetClaim, pattern, false); + } + + private void createRegexPolicy(String name, String targetClaim, String pattern, Boolean targetContextAttributes) { RegexPolicyRepresentation policy = new RegexPolicyRepresentation(); policy.setName(name); policy.setTargetClaim(targetClaim); policy.setPattern(pattern); + policy.setTargetContextAttributes(targetContextAttributes); getClient().authorization().policies().regex().create(policy).close(); } @@ -359,6 +367,8 @@ private void createRegexPolicyExtended(String name, String targetClaim, String p // Access Resource D with taro. request = new PermissionRequest("Resource D"); + PermissionResponse foo = authzClient.protection().permission().create(request); + ticket = authzClient.protection().permission().create(request).getTicket(); try { authzClient.authorization("taro", "password").authorize(new AuthorizationRequest(ticket)); @@ -368,6 +378,43 @@ private void createRegexPolicyExtended(String name, String targetClaim, String p } } + @Test + public void testWithExpectedContextAttribute() { + AuthzClient authzClient = getAuthzClient(); + PermissionRequest request = new PermissionRequest("Resource CONTEXT"); + request.setClaim("custom", "foo"); + String ticket = authzClient.protection().permission().create(request).getTicket(); + AuthorizationRequest theRequest = new AuthorizationRequest(ticket); + AuthorizationResponse response = authzClient.authorization("my-user", "password").authorize(theRequest); + assertNotNull(response.getToken()); + } + + @Test + public void testWithExpectedContextAttributeAsUserAttribute() { + AuthzClient authzClient = getAuthzClient(); + PermissionRequest request = new PermissionRequest("Resource CONTEXT"); + String ticket = authzClient.protection().permission().create(request).getTicket(); + try { + authzClient.authorization("context-user", "password").authorize(new AuthorizationRequest(ticket)); + fail("failed because it should thrown an exception with 403 Forbidden Status"); + } catch (AuthorizationDeniedException ignored) { + + } + } + + @Test + public void testWithoutExpectedContextAttribute() { + AuthzClient authzClient = getAuthzClient(); + PermissionRequest request = new PermissionRequest("Resource CONTEXT"); + String ticket = authzClient.protection().permission().create(request).getTicket(); + try { + authzClient.authorization("my-user", "password").authorize(new AuthorizationRequest(ticket)); + fail("failed because it should thrown an exception with 403 Forbidden Status"); + } catch (AuthorizationDeniedException ignored) { + + } + } + private AuthzClient getAuthzClient() { return AuthzClient.create(getClass().getResourceAsStream("/authorization-test/default-keycloak.json")); }