Add a toggle to use context attributes on the regex policy provider

Signed-off-by: remi <remi.tuveri@gmail.com>
This commit is contained in:
remi 2023-12-19 07:52:26 +00:00 committed by Pedro Igor
parent 326ba672cd
commit b22efeec78
7 changed files with 91 additions and 5 deletions

View file

@ -73,7 +73,9 @@ public class RegexPolicyProvider implements PolicyProvider {
} }
private String getClaimValue(Evaluation evaluation, RegexPolicyRepresentation policy) { 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(); String targetClaim = policy.getTargetClaim();
try { try {

View file

@ -81,6 +81,7 @@ public class RegexPolicyProviderFactory implements PolicyProviderFactory<RegexPo
representation.setTargetClaim(config.get("targetClaim")); representation.setTargetClaim(config.get("targetClaim"));
representation.setPattern(config.get("pattern")); representation.setPattern(config.get("pattern"));
representation.setTargetContextAttributes(Boolean.parseBoolean(config.get("targetContextAttributes")));
return representation; return representation;
} }
@ -110,6 +111,7 @@ public class RegexPolicyProviderFactory implements PolicyProviderFactory<RegexPo
config.put("targetClaim", representation.getTargetClaim()); config.put("targetClaim", representation.getTargetClaim());
config.put("pattern", representation.getPattern()); config.put("pattern", representation.getPattern());
config.put("targetContextAttributes", String.valueOf(representation.isTargetContextAttributes()));
policy.setConfig(config); policy.setConfig(config);
} }

View file

@ -23,6 +23,7 @@ public class RegexPolicyRepresentation extends AbstractPolicyRepresentation {
private String targetClaim; private String targetClaim;
private String pattern; private String pattern;
private boolean targetContextAttributes;
@Override @Override
public String getType() { public String getType() {
@ -45,4 +46,12 @@ public class RegexPolicyRepresentation extends AbstractPolicyRepresentation {
this.pattern = pattern; this.pattern = pattern;
} }
public boolean isTargetContextAttributes() {
return targetContextAttributes;
}
public void setTargetContextAttributes(boolean targetContextAttributes) {
this.targetContextAttributes = targetContextAttributes;
}
} }

View file

@ -2337,6 +2337,8 @@ roleID=Role ID
roleNameLdapAttributeHelp=Name of LDAP attribute, which is used in role objects for name and RDN of role. Usually it will be 'cn'. In this case typical group/role object may have DN like 'cn\=role1,ou\=finance,dc\=example,dc\=org'. roleNameLdapAttributeHelp=Name of LDAP attribute, which is used in role objects for name and RDN of role. Usually it will be 'cn'. In this case typical group/role object may have DN like 'cn\=role1,ou\=finance,dc\=example,dc\=org'.
origin=Origin origin=Origin
regexPattern=Regex pattern regexPattern=Regex pattern
targetContextAttributes=Target Context Attributes
targetContextAttributesHelp=Defines the evaluation of context attributes (claims) instead of identity attributes
filteredByClaim=Verify essential claim filteredByClaim=Verify essential claim
rowCancelBtnAriaLabel=Cancel edits for {{messageBundle}} rowCancelBtnAriaLabel=Cancel edits for {{messageBundle}}
validateSignatureHelp=Enable/disable signature validation of external IDP signatures. validateSignatureHelp=Enable/disable signature validation of external IDP signatures.

View file

@ -1,6 +1,6 @@
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useFormContext } from "react-hook-form"; import { Controller, useFormContext } from "react-hook-form";
import { FormGroup } from "@patternfly/react-core"; import { Checkbox, FormGroup } from "@patternfly/react-core";
import { HelpItem } from "ui-shared"; import { HelpItem } from "ui-shared";
import { KeycloakTextInput } from "../../../components/keycloak-text-input/KeycloakTextInput"; import { KeycloakTextInput } from "../../../components/keycloak-text-input/KeycloakTextInput";
@ -8,6 +8,7 @@ import { KeycloakTextInput } from "../../../components/keycloak-text-input/Keycl
export const Regex = () => { export const Regex = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
control,
register, register,
formState: { errors }, formState: { errors },
} = useFormContext(); } = useFormContext();
@ -54,6 +55,29 @@ export const Regex = () => {
{...register("pattern", { required: true })} {...register("pattern", { required: true })}
/> />
</FormGroup> </FormGroup>
<FormGroup
label={t("targetContextAttributes")}
fieldId="targetContextAttributes"
labelIcon={
<HelpItem
helpText={t("targetContextAttributesHelp")}
fieldLabelId="targetContextAttributes"
/>
}
>
<Controller
name="targetContextAttributes"
defaultValue={false}
control={control}
render={({ field }) => (
<Checkbox
id="targetContextAttributes"
isChecked={field.value}
onChange={field.onChange}
/>
)}
/>
</FormGroup>
</> </>
); );
}; };

View file

@ -1241,7 +1241,7 @@ public class RepresentationToModel {
PolicyProviderFactory provider = authorization.getProviderFactory(model.getType()); PolicyProviderFactory provider = authorization.getProviderFactory(model.getType());
if (provider == null) { 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) { if (representation instanceof PolicyRepresentation) {

View file

@ -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-user").password("password").addAttribute("canCreateItems","true"))
.user(UserBuilder.create().username("my-user2").password("password").addAttribute("canCreateItems","false")) .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("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()) .group(GroupBuilder.create().name("ADMIN").singleAttribute("attribute","example").build())
.user(UserBuilder.create().username("admin").password("password").addGroups("ADMIN")) .user(UserBuilder.create().username("admin").password("password").addGroups("ADMIN"))
@ -125,6 +126,7 @@ public class RegexPolicyTest extends AbstractAuthzTest {
createResource("Resource D"); createResource("Resource D");
createResource("Resource E"); createResource("Resource E");
createResource("Resource ITEM"); createResource("Resource ITEM");
createResource("Resource CONTEXT");
ScopeRepresentation scopeRead = new ScopeRepresentation(); ScopeRepresentation scopeRead = new ScopeRepresentation();
scopeRead.setName("read"); scopeRead.setName("read");
ScopeRepresentation scopeDelete = new ScopeRepresentation(); 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 json-array Policy", "json-complex.some-array[1]", "bar");
createRegexPolicy("Regex user attribute to json-Complex Policy", "customPermissions.canCreateItems", "true"); createRegexPolicy("Regex user attribute to json-Complex Policy", "customPermissions.canCreateItems", "true");
createRegexPolicyExtended("attribute-policy","attributes.values","^example$",Logic.POSITIVE); 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 A Permission", "Resource A", "Regex foo Policy");
createResourcePermission("Resource B Permission", "Resource B", "Regex bar 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"); createResourcePermission("Resource ITEM Permission", "Resource ITEM", "Regex user attribute to json-Complex Policy");
createResourceScopesPermissionExtended("read-permission","service",DecisionStrategy.UNANIMOUS,"read","attribute-policy"); createResourceScopesPermissionExtended("read-permission","service",DecisionStrategy.UNANIMOUS,"read","attribute-policy");
createResourcePermission("Resource CONTEXT Permission", "Resource CONTEXT", "Regex context policy");
} }
private void createResource(String name) { private void createResource(String name) {
@ -170,11 +173,16 @@ public class RegexPolicyTest extends AbstractAuthzTest {
private void createRegexPolicy(String name, String targetClaim, String pattern) { 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(); RegexPolicyRepresentation policy = new RegexPolicyRepresentation();
policy.setName(name); policy.setName(name);
policy.setTargetClaim(targetClaim); policy.setTargetClaim(targetClaim);
policy.setPattern(pattern); policy.setPattern(pattern);
policy.setTargetContextAttributes(targetContextAttributes);
getClient().authorization().policies().regex().create(policy).close(); 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. // Access Resource D with taro.
request = new PermissionRequest("Resource D"); request = new PermissionRequest("Resource D");
PermissionResponse foo = authzClient.protection().permission().create(request);
ticket = authzClient.protection().permission().create(request).getTicket(); ticket = authzClient.protection().permission().create(request).getTicket();
try { try {
authzClient.authorization("taro", "password").authorize(new AuthorizationRequest(ticket)); 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() { private AuthzClient getAuthzClient() {
return AuthzClient.create(getClass().getResourceAsStream("/authorization-test/default-keycloak.json")); return AuthzClient.create(getClass().getResourceAsStream("/authorization-test/default-keycloak.json"));
} }