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:
parent
326ba672cd
commit
b22efeec78
7 changed files with 91 additions and 5 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue