+
Skip to content

Ensure regex policies work for multi-valued claims #33674

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,29 +58,32 @@ public void close() {
public void evaluate(Evaluation evaluation) {
AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
RegexPolicyRepresentation policy = representationFunction.apply(evaluation.getPolicy(), authorizationProvider);
String value = getClaimValue(evaluation, policy);
String[] value = getClaimValue(evaluation, policy);

if (value == null) {
return;
}

Pattern pattern = Pattern.compile(policy.getPattern());
Matcher matcher = pattern.matcher(value);
if (matcher.matches()) {
evaluation.grant();
logger.debugf("policy %s evaluated with status %s on identity %s and claim value %s", policy.getName(), evaluation.getEffect(), evaluation.getContext().getIdentity().getId(), getClaimValue(evaluation, policy));
for (String claimValue : value) {
Matcher matcher = pattern.matcher(claimValue);
if (matcher.matches()) {
evaluation.grant();
logger.debugf("policy %s evaluated with status %s on identity %s and claim value %s", policy.getName(),
evaluation.getEffect(), evaluation.getContext().getIdentity().getId(), claimValue);
}
}
}

private String getClaimValue(Evaluation evaluation, RegexPolicyRepresentation policy) {
private String[] getClaimValue(Evaluation evaluation, RegexPolicyRepresentation policy) {
Attributes attributes = policy.isTargetContextAttributes()
? evaluation.getContext().getAttributes()
: evaluation.getContext().getIdentity().getAttributes();
String targetClaim = policy.getTargetClaim();

try {
if (hasPath(targetClaim)) {
return resolveJsonValue(attributes, targetClaim);
return new String[] { resolveJsonValue(attributes, targetClaim) };
}

return resolveSimpleValue(attributes, targetClaim);
Expand All @@ -89,14 +92,14 @@ private String getClaimValue(Evaluation evaluation, RegexPolicyRepresentation po
}
}

private String resolveSimpleValue(Attributes attributes, String targetClaim) {
private String[] resolveSimpleValue(Attributes attributes, String targetClaim) {
Attributes.Entry value = attributes.getValue(targetClaim);

if (value == null || value.isEmpty()) {
return null;
}

return value.asString(0);
return value.getValues();
}

private String resolveJsonValue(Attributes attributes, String targetClaim) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ public Entry(String name, Collection<String> values) {
this.values = values.toArray(new String[values.size()]);
}

public String[] getValues() {
return values;
}

private String getName() {
return this.name;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authorization.client.AuthorizationDeniedException;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
Expand Down Expand Up @@ -68,6 +69,11 @@ public void addTestRealms(List<RealmRepresentation> testRealms) {
claims.put("claim.name", "json-complex");
ProtocolMapperRepresentation userAttrJsonComplexProtocolMapper = addClaimMapper("userAttrJsonComplex", claims);

claims.put("user.attribute", "multi-value");
claims.put("claim.name", "multi-value");
ProtocolMapperRepresentation userAttrMultiValueProtocolMapper = addClaimMapper("userAttrMultiValue", claims);
userAttrMultiValueProtocolMapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, "true");

ProtocolMapperRepresentation userAttributesProtocolMapper = new ProtocolMapperRepresentation();
userAttributesProtocolMapper.setName("canWriteItems");
userAttributesProtocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
Expand Down Expand Up @@ -110,11 +116,14 @@ public void addTestRealms(List<RealmRepresentation> testRealms) {
.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"))
.user(UserBuilder.create().username("multi-value-user-1").password("password").addAttribute("multi-value", "foo", "bar"))
.user(UserBuilder.create().username("multi-value-user-2").password("password").addAttribute("multi-value", "foo", "foobar"))
.user(UserBuilder.create().username("multi-value-bad-user").password("password").addAttribute("multi-value", "foo", "baz"))


.client(ClientBuilder.create().clientId("resource-server-test").secret("secret").authorizationServicesEnabled(true)
.redirectUris("http://localhost/resource-server-test").directAccessGrants()
.protocolMapper(userAttrFooProtocolMapper, userAttrBarProtocolMapper, userAttrJsonProtocolMapper, userAttrJsonComplexProtocolMapper,userAttributesProtocolMapper,groupAttributesProtocolMapper))
.protocolMapper(userAttrFooProtocolMapper,userAttrBarProtocolMapper,userAttrJsonProtocolMapper,userAttrJsonComplexProtocolMapper,userAttributesProtocolMapper,groupAttributesProtocolMapper,userAttrMultiValueProtocolMapper))
.build());
}

Expand All @@ -127,6 +136,7 @@ public void configureAuthorization() throws Exception {
createResource("Resource E");
createResource("Resource ITEM");
createResource("Resource CONTEXT");
createResource("Resource MULTIVALUE");
ScopeRepresentation scopeRead = new ScopeRepresentation();
scopeRead.setName("read");
ScopeRepresentation scopeDelete = new ScopeRepresentation();
Expand All @@ -144,6 +154,7 @@ public void configureAuthorization() throws Exception {
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);
createRegexPolicy("Regex multi value Policy", "multi-value", ".*bar");

createResourcePermission("Resource A Permission", "Resource A", "Regex foo Policy");
createResourcePermission("Resource B Permission", "Resource B", "Regex bar Policy");
Expand All @@ -155,6 +166,7 @@ public void configureAuthorization() throws Exception {
createResourceScopesPermissionExtended("read-permission","service",DecisionStrategy.UNANIMOUS,"read","attribute-policy");

createResourcePermission("Resource CONTEXT Permission", "Resource CONTEXT", "Regex context policy");
createResourcePermission("Resource MULTIVALUE Permission", "Resource MULTIVALUE", "Regex multi value Policy");
}

private void createResource(String name) {
Expand Down Expand Up @@ -277,6 +289,34 @@ public void testWithExpectedUserAttributeValueMappedToComplexJsonClaim() {

}

@Test
public void testWithExpectedUserAttributeValueMappedToMultiValuedClaim() {
AuthzClient authzClient = getAuthzClient();
PermissionRequest request = new PermissionRequest("Resource MULTIVALUE");
String ticket = authzClient.protection().permission().create(request).getTicket();
AuthorizationRequest theRequest = new AuthorizationRequest(ticket);
AuthorizationResponse response = authzClient.authorization("multi-value-user-1", "password").authorize(theRequest);
assertNotNull(response.getToken());

response = authzClient.authorization("multi-value-user-2", "password").authorize(theRequest);
assertNotNull(response.getToken());
}

@Test
public void testWithoutExpectedUserAttributeValueMappedToMultiValuedClaim() {
AuthzClient authzClient = getAuthzClient();
PermissionRequest request = new PermissionRequest("Resource MULTIVALUE");
String ticket = authzClient.protection().permission().create(request).getTicket();
AuthorizationRequest theRequest = new AuthorizationRequest(ticket);
try {

AuthorizationResponse response = authzClient.authorization("multi-value-bad-user", "password").authorize(theRequest);
fail("Should fail.");
} catch ( AuthorizationDeniedException ignore) {

}
}

@Test
public void testWithExpectedUserAttributeValueMappedToComplexJsonClaimPermissions() {
AuthzClient authzClient = getAuthzClient();
Expand Down
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载