From a81305a5304b0c11653e0e5b72cb81bf763d249f Mon Sep 17 00:00:00 2001 From: Federica Agostini Date: Wed, 30 Oct 2024 12:22:44 +0100 Subject: [PATCH] Add jwt-client as principal type --- .../config/FineGrainedAuthzPolicyParser.java | 4 + .../FineGrainedAuthzPolicyProperties.java | 1 + .../config/validation/PrincipalValidator.java | 3 + .../oauth/StormJwtAuthoritiesConverter.java | 10 +- .../oauth/authority/JwtClientAuthority.java | 56 +++++++++ .../oidc/OidcGrantedAuthoritiesMapper.java | 14 ++- src/main/resources/application-fga.yml | 51 +++++++++ .../AuthorizationIntegrationTests.java | 108 +++++++++++++++++- .../webdav/test/authz/pdp/AuthzPdpTests.java | 41 +++++++ src/test/resources/application-authz-test.yml | 13 ++- src/test/resources/conf/sa.d/fga.properties | 27 +++++ src/test/resources/storage/fga/file | 1 + 12 files changed, 314 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/italiangrid/storm/webdav/oauth/authority/JwtClientAuthority.java create mode 100644 src/main/resources/application-fga.yml create mode 100644 src/test/resources/conf/sa.d/fga.properties create mode 100644 src/test/resources/storage/fga/file diff --git a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java index 715a61e1..1e545b06 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java @@ -40,6 +40,7 @@ import org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.Action; import org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties; import org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType; +import org.italiangrid.storm.webdav.oauth.authority.JwtClientAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtGroupAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtIssuerAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtScopeAuthority; @@ -137,6 +138,9 @@ PrincipalMatcher parsePrincipal(PrincipalProperties p) { } else if (PrincipalType.JWT_SUBJECT.equals(p.getType())) { matcher = AuthorityHolder.fromAuthority( new JwtSubjectAuthority(p.getParams().get("iss"), p.getParams().get("sub"))); + } else if (PrincipalType.JWT_CLIENT.equals(p.getType())) { + matcher = AuthorityHolder.fromAuthority( + new JwtClientAuthority(p.getParams().get("iss"), p.getParams().get("id"))); } else if (PrincipalType.VO.equals(p.getType())) { matcher = AuthorityHolder.fromAuthority(new VOMSVOAuthority(p.getParams().get("vo"))); } else if (PrincipalType.VO_MAP.equals(p.getType())) { diff --git a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyProperties.java b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyProperties.java index 29be9fb0..dc3d35c8 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyProperties.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyProperties.java @@ -45,6 +45,7 @@ public enum PrincipalType { JWT_SCOPE, JWT_ISSUER, JWT_SUBJECT, + JWT_CLIENT, VO, FQAN, VO_MAP, diff --git a/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java b/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java index fc7d4dc0..dab3fe78 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java @@ -18,6 +18,7 @@ import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.String.format; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.FQAN; +import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_CLIENT; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_GROUP; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_SCOPE; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_ISSUER; @@ -50,6 +51,8 @@ public class PrincipalValidator implements .put(JWT_SUBJECT, "iss") .put(JWT_SUBJECT, "sub") .put(JWT_ISSUER, "iss") + .put(JWT_CLIENT, "iss") + .put(JWT_CLIENT, "id") .put(VO, "vo") .put(VO_MAP, "vo") .put(X509_SUBJECT, "subject") diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java b/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java index a9f9160d..6a68bd03 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java @@ -24,11 +24,11 @@ import org.italiangrid.storm.webdav.authz.SAPermission; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; import org.italiangrid.storm.webdav.config.StorageAreaConfiguration; +import org.italiangrid.storm.webdav.oauth.authority.JwtClientAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtGroupAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtIssuerAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtScopeAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtSubjectAuthority; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.converter.Converter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; @@ -41,7 +41,6 @@ public class StormJwtAuthoritiesConverter extends GrantedAuthoritiesMapperSupport implements Converter> { - @Autowired public StormJwtAuthoritiesConverter(StorageAreaConfiguration conf, ServiceConfigurationProperties props) { super(conf, props); @@ -108,8 +107,11 @@ protected Collection extractAuthorities(Jwt jwt) { authorities.addAll(extractOauthGroupAuthorities(jwt)); authorities.addAll(extractOauthScopeAuthorities(jwt)); - authorities.add(new JwtIssuerAuthority(jwt.getIssuer().toString())); - authorities.add(new JwtSubjectAuthority(jwt.getIssuer().toString(), jwt.getSubject())); + authorities.add(new JwtIssuerAuthority(issuer)); + authorities.add(new JwtSubjectAuthority(issuer, jwt.getSubject())); + if (jwt.getClaim("client_id") != null) { + authorities.add(new JwtClientAuthority(issuer, jwt.getClaim("client_id"))); + } return authorities; } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/authority/JwtClientAuthority.java b/src/main/java/org/italiangrid/storm/webdav/oauth/authority/JwtClientAuthority.java new file mode 100644 index 00000000..c36fb310 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/authority/JwtClientAuthority.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023. + * + * 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.italiangrid.storm.webdav.oauth.authority; + +public class JwtClientAuthority extends JwtAuthority { + + private static final long serialVersionUID = 1L; + + public static final String AUTH_TEMPLATE = "O_client(%s,%s)"; + + private final String clientId; + + public JwtClientAuthority(String issuer, String clientId) { + super(issuer, String.format(AUTH_TEMPLATE, issuer, clientId)); + this.clientId = clientId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((clientId == null) ? 0 : clientId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + JwtClientAuthority other = (JwtClientAuthority) obj; + if (clientId == null) { + if (other.clientId != null) + return false; + } else if (!clientId.equals(other.clientId)) + return false; + return true; + } + +} diff --git a/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java b/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java index d30036ba..72a0e6fd 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java +++ b/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java @@ -17,15 +17,16 @@ import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.Set; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; import org.italiangrid.storm.webdav.config.StorageAreaConfiguration; import org.italiangrid.storm.webdav.oauth.GrantedAuthoritiesMapperSupport; +import org.italiangrid.storm.webdav.oauth.authority.JwtClientAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtGroupAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtIssuerAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtSubjectAuthority; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; @@ -37,7 +38,6 @@ public class OidcGrantedAuthoritiesMapper extends GrantedAuthoritiesMapperSupport implements GrantedAuthoritiesMapper { - @Autowired public OidcGrantedAuthoritiesMapper(StorageAreaConfiguration conf, ServiceConfigurationProperties props) { super(conf, props); @@ -66,9 +66,13 @@ protected Collection mapAuthorities(OidcUserAuthority userAuth authorities.addAll(authzMap.get(idTokenIssuer)); authorities.addAll(grantGroupAuthorities(userAuthority)); - authorities.add(new JwtIssuerAuthority(userAuthority.getIdToken().getIssuer().toString())); - authorities.add(new JwtSubjectAuthority(userAuthority.getIdToken().getIssuer().toString(), - userAuthority.getIdToken().getSubject())); + authorities.add(new JwtIssuerAuthority(idTokenIssuer)); + authorities.add(new JwtSubjectAuthority(idTokenIssuer, userAuthority.getIdToken().getSubject())); + Optional clientIdClaim = + Optional.ofNullable(userAuthority.getIdToken().getClaim("client_id")); + if (clientIdClaim.isPresent()) { + authorities.add(new JwtClientAuthority(idTokenIssuer, clientIdClaim.get())); + } return authorities; } diff --git a/src/main/resources/application-fga.yml b/src/main/resources/application-fga.yml new file mode 100644 index 00000000..7a207fb3 --- /dev/null +++ b/src/main/resources/application-fga.yml @@ -0,0 +1,51 @@ +server: + jetty: + accesslog: + enabled: false + +management: + endpoints: + web: + exposure: + include: env + +oauth: + enable-oidc: false + issuers: + - name: iam-dev + issuer: https://iam-dev.cloud.cnaf.infn.it/ + +storm: + connector: + port: 8086 + securePort: 9443 + sa: + config-dir: src/test/resources/conf/sa.d + tls: + trust-anchors-dir: src/test/resources/trust-anchors + certificate-path: src/test/resources/hostcert/hostcert.pem + private-key-path: src/test/resources/hostcert/hostkey.pem + authz-server: + enabled: true + voms: + trust-store: + dir: src/test/resources/vomsdir + tape: + well-known: + source: src/test/resources/well-known/wlcg-tape-rest-api.json + + authz: + policies: + - sa: fga + actions: + - all + effect: permit + description: Grant read/write access to a specific client + paths: + - /** + principals: + - type: jwt-client + params: + iss: https://iam-dev.cloud.cnaf.infn.it/ + id: 42999a63-7449-43fb-952e-42f2d75b865b + diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java index 2bf656b5..db4c60c4 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java @@ -57,6 +57,9 @@ public class AuthorizationIntegrationTests { public static final String UNKNOWN_ISSUER = "https://unknown.example"; public static final String EXAMPLE_ISSUER = "https://issuer.example"; + public static final String AUTHORIZED_JWT_CLIENT_ID = "1234"; + public static final String UNAUTHORIZED_JWT_CLIENT_ID = "5678"; + @Autowired private MockMvc mvc; @@ -127,11 +130,8 @@ void issuerChecksAreEnforcedForWlcgScopeBasedAuthz() throws Exception { @Test void getAccessAsJwtUserWithoutScopeLeadsToAccessDenied() throws Exception { - Jwt token = Jwt.withTokenValue("test") - .header("kid", "rsa1") - .issuer(WLCG_ISSUER) - .subject("123") - .build(); + Jwt token = + Jwt.withTokenValue("test").header("kid", "rsa1").issuer(WLCG_ISSUER).subject("123").build(); mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token))) .andExpect(status().isForbidden()); @@ -180,6 +180,104 @@ void getAccessAsJwtWithWriteCapabilityResultsInAccessDenied() throws Exception { } + @Test + void readAccessAsJwtWithAllowedClient() throws Exception { + Jwt token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(EXAMPLE_ISSUER) + .claim("client_id", AUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isNotFound()); + + } + + @Test + void readAccessWithoutMatchedJWTIsDenied() throws Exception { + Jwt token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(EXAMPLE_ISSUER) + .claim("client_id", UNAUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(UNKNOWN_ISSUER) + .claim("client_id", AUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(UNKNOWN_ISSUER) + .claim("client_id", UNAUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test").header("kid", "rsa1").issuer(UNKNOWN_ISSUER).build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + } + + @Test + void writeAccessAsJwtWithAllowedClient() throws Exception { + Jwt token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(EXAMPLE_ISSUER) + .claim("client_id", AUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isOk()); + + } + + @Test + void writeAccessWithoutMatchedJWTIsDenied() throws Exception { + Jwt token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(EXAMPLE_ISSUER) + .claim("client_id", UNAUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(UNKNOWN_ISSUER) + .claim("client_id", AUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(UNKNOWN_ISSUER) + .claim("client_id", UNAUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test").header("kid", "rsa1").issuer(UNKNOWN_ISSUER).build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + } + @WithMockVOMSUser(vos = "wlcg", saReadPermissions = {"wlcg"}) @Test void localVomsCopyRequiresWithReadPermissionsGetsAccessDenied() throws Exception { diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java index e7bdf69e..342326fc 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java @@ -37,6 +37,7 @@ import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationResult; import org.italiangrid.storm.webdav.authz.pdp.principal.Anyone; import org.italiangrid.storm.webdav.authz.pdp.principal.AuthorityHolder; +import org.italiangrid.storm.webdav.oauth.authority.JwtClientAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtGroupAuthority; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; @@ -57,6 +58,8 @@ public class AuthzPdpTests { public static final String TEST_ISSUER = "https://test.example"; public static final String TEST2_ISSUER = "https://test2.example"; + public static final String AUTHORIZED_JWT_CLIENT_ID = "1234"; + public static final String UNAUTHORIZED_JWT_CLIENT_ID = "5678"; @Mock PathAuthorizationPolicyRepository repo; @@ -258,6 +261,44 @@ void oauthGroupHolderPolicyApplied() { assertThat(result.getPolicy().isPresent(), is(true)); assertThat(result.getPolicy().get(), is(oauthTestPolicy)); } + + @Test + void oauthClientHolderPolicyApplied() { + + when(request.getServletPath()).thenReturn("/test/file0"); + when(request.getMethod()).thenReturn("GET"); + + when(authentication.getAuthorities()) + .thenReturn(authorities(new JwtClientAuthority(TEST_ISSUER, AUTHORIZED_JWT_CLIENT_ID))); + + PathAuthorizationPolicy oauthTestPolicy = PathAuthorizationPolicy.builder() + .withPermit() + .withPrincipalMatcher(AuthorityHolder + .fromAuthority(new JwtClientAuthority(TEST_ISSUER, AUTHORIZED_JWT_CLIENT_ID))) + .withRequestMatcher(new AntPathRequestMatcher("/test/**", "GET")) + .build(); + + PathAuthorizationPolicy denyAllPolicy = PathAuthorizationPolicy.builder() + .withDeny() + .withPrincipalMatcher(new Anyone()) + .withRequestMatcher(new AntPathRequestMatcher("/**")) + .build(); + + List policies = Lists.newArrayList(oauthTestPolicy, denyAllPolicy); + when(repo.getPolicies()).thenReturn(policies); + + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, authentication)); + assertThat(result.getDecision(), is(PERMIT)); + assertThat(result.getPolicy().isPresent(), is(true)); + assertThat(result.getPolicy().get(), is(oauthTestPolicy)); + + when(authentication.getAuthorities()) + .thenReturn(authorities(new JwtClientAuthority(TEST_ISSUER, UNAUTHORIZED_JWT_CLIENT_ID))); + + result = pdp.authorizeRequest(newAuthorizationRequest(request, authentication)); + assertThat(result.getDecision(), is(DENY)); + } @Test void multiplePrincipalMatchersWorkAsExpected() { diff --git a/src/test/resources/application-authz-test.yml b/src/test/resources/application-authz-test.yml index df719d4f..9d38c178 100644 --- a/src/test/resources/application-authz-test.yml +++ b/src/test/resources/application-authz-test.yml @@ -55,4 +55,15 @@ storm: params: iss: https://issuer.example group: /example/admins - + - sa: wlcg + actions: + - all + effect: permit + description: Grant read/write access to a specific JWT client ID + paths: + - /** + principals: + - type: jwt-client + params: + iss: https://issuer.example + id: 1234 \ No newline at end of file diff --git a/src/test/resources/conf/sa.d/fga.properties b/src/test/resources/conf/sa.d/fga.properties new file mode 100644 index 00000000..e9f0a67d --- /dev/null +++ b/src/test/resources/conf/sa.d/fga.properties @@ -0,0 +1,27 @@ +# +# Copyright (c) Istituto Nazionale di Fisica Nucleare, 2018. +# +# 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. +# + +name=fga +rootPath=src/test/resources/storage/fga +filesystemType=posixfs +accessPoints=/fga +authenticatedReadEnabled=false +anonymousReadEnabled=false +orgs=https://iam-dev.cloud.cnaf.infn.it/ +orgsGrantReadPermission=false +orgsGrantWritePermission=false +fineGrainedAuthzEnabled=true +wlcgScopeAuthzEnabled=true diff --git a/src/test/resources/storage/fga/file b/src/test/resources/storage/fga/file new file mode 100644 index 00000000..af218bce --- /dev/null +++ b/src/test/resources/storage/fga/file @@ -0,0 +1 @@ +Testing the fine-grained authorization