Skip to content

Commit

Permalink
Allow all POST requests to be processed as a macaroon request (#52)
Browse files Browse the repository at this point in the history
* Enable Macaroon voter only if authz server and macaroon filter are enabled
  • Loading branch information
enricovianello authored Oct 18, 2024
1 parent 7212135 commit c3a4a10
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* 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.authz.voters;

import java.util.Collection;

import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.util.Assert;

public class MacaroonAuthzVoter implements AccessDecisionVoter<FilterInvocation> {

@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}

@Override
public boolean supports(Class<?> clazz) {
return true;
}

@Override
public int vote(Authentication authentication, FilterInvocation filterInvocation,
Collection<ConfigAttribute> attributes) {
Assert.notNull(authentication, "authentication must not be null");
Assert.notNull(filterInvocation, "filterInvocation must not be null");

if (HttpMethod.POST.name().equals(filterInvocation.getHttpRequest().getMethod())) {
return ACCESS_GRANTED;
}
return ACCESS_ABSTAIN;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import static java.util.Objects.isNull;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.Filter;
Expand All @@ -42,7 +41,8 @@
import org.italiangrid.storm.webdav.scitag.SciTagTransfer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.google.common.collect.Sets;

import io.milton.http.HttpManager;
import io.milton.http.Request;
Expand All @@ -53,7 +53,7 @@ public class MiltonFilter implements Filter {

public static final Logger LOG = LoggerFactory.getLogger(MiltonFilter.class);

static final Set<String> WEBDAV_METHOD_SET = new HashSet<String>();
static final Set<String> WEBDAV_METHOD_SET = Sets.newHashSet();
static final String SA_ROOT_PATH = "sa-root";

static {
Expand All @@ -74,7 +74,6 @@ public class MiltonFilter implements Filter {

private final ReplaceContentStrategy rcs;

@Autowired
public MiltonFilter(FilesystemAccess fsAccess, ExtendedAttributesHelper attrsHelper,
PathResolver resolver, ReplaceContentStrategy rcs) {

Expand Down Expand Up @@ -121,7 +120,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
doMilton((HttpServletRequest) request, (HttpServletResponse) response);
} else
chain.doFilter(request, response);

}

public void doMilton(HttpServletRequest request, HttpServletResponse response) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.italiangrid.storm.webdav.authz.voters.FineGrainedAuthzVoter;
import org.italiangrid.storm.webdav.authz.voters.FineGrainedCopyMoveAuthzVoter;
import org.italiangrid.storm.webdav.authz.voters.LocalAuthzVoter;
import org.italiangrid.storm.webdav.authz.voters.MacaroonAuthzVoter;
import org.italiangrid.storm.webdav.authz.voters.UnanimousDelegatedVoter;
import org.italiangrid.storm.webdav.authz.voters.WlcgScopeAuthzCopyMoveVoter;
import org.italiangrid.storm.webdav.authz.voters.WlcgScopeAuthzVoter;
Expand Down Expand Up @@ -282,6 +283,10 @@ protected AccessDecisionManager fineGrainedAccessDecisionManager() throws Malfor
voters.add(new LocalAuthzVoter(serviceConfigurationProperties, pathResolver,
new LocalAuthorizationPdp(serviceConfigurationProperties), localURLService));
}
if (serviceConfigurationProperties.getAuthzServer().isEnabled()
&& serviceConfigurationProperties.getMacaroonFilter().isEnabled()) {
voters.add(new MacaroonAuthzVoter());
}
voters.add(new WebExpressionVoter());
voters.add(fineGrainedVoters);
voters.add(wlcgVoters);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* 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.test.macaroon;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

import org.italiangrid.storm.webdav.authz.VOMSAuthenticationFilter;
import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties;
import org.italiangrid.storm.webdav.macaroon.MacaroonRequestFilter;
import org.italiangrid.storm.webdav.test.utils.voms.WithMockVOMSUser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import com.fasterxml.jackson.databind.ObjectMapper;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles({"dev", "lhcb"})
@WithAnonymousUser
public class MacaroonRequestLhcbIntegrationTests {

public static final Instant NOW = Instant.parse("2018-01-01T00:00:00.00Z");
public static final Instant NOW_PLUS_2H =
NOW.plusSeconds(TimeUnit.HOURS.toSeconds(2)).truncatedTo(ChronoUnit.SECONDS);

public static final String EMPTY_JSON_OBJECT = "{}";

@TestConfiguration
static class Configuration {
@Bean
@Primary
Clock mockClock() {
return Clock.fixed(NOW, ZoneId.systemDefault());
}
}

@Autowired
MockMvc mvc;

@Autowired
VOMSAuthenticationFilter filter;

@Autowired
ServiceConfigurationProperties props;

@Autowired
ObjectMapper mapper;

@BeforeEach
public void setup() {
filter.setCheckForPrincipalChanges(false);
}

@Test
@WithMockVOMSUser(saReadPermissions = {"lhcb_disk"}, vos = {"lhcb"})
void macaroonIssuedWithNoWritePermissions() throws Exception {
mvc
.perform(post("/disk/lhcb/source").contentType(MacaroonRequestFilter.MACAROON_REQUEST_CONTENT_TYPE)
.content(EMPTY_JSON_OBJECT))
.andExpect(status().isOk())
.andExpect(jsonPath("$.macaroon").exists());
}

}
55 changes: 55 additions & 0 deletions src/test/resources/application-lhcb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
server:
jetty:
accesslog:
enabled: false
oauth:
enable-oidc: false

storm:
sa:
config-dir: src/test/resources/lhcb/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

voms:
trust-store:
dir: src/test/resources/vomsdir

redirector:
enabled: false

authz:
policies:
- sa: lhcb_disk
description: Grant all access to lhcb VOMS group members for /failover and its subfolders
actions:
- all
paths:
- /failover/**
effect: permit
principals:
- type: vo
params:
vo: lhcb
- sa: lhcb_disk
description: Grant all access to lhcb prod VOMS group members
actions:
- all
effect: permit
principals:
- type: fqan
params:
fqan: /lhcb/Role=production/Capability=NULL
- sa: lhcb_disk
description: Grant only read and list access to lhcb VOMS group members
actions:
- read
- list
effect: permit
principals:
- type: vo
params:
vo: lhcb
52 changes: 52 additions & 0 deletions src/test/resources/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#
# 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.
#

logging.pattern.dateformat="yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC
logging.pattern.level= [%X{storm.requestId}] %X{tpc.clientInfo} %5p
logging.level.root=WARN
logging.level.org.italiangrid.storm=DEBUG


#logging.level.org.italiangrid.storm.webdav.tpc=DEBUG

#logging.level.org.springframework.test.web.servlet.result=DEBUG

#logging.level.org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory=WARN
#logging.level.org.italiangrid.storm.webdav.authz=DEBUG
#logging.level.org.springframework.web=DEBUG
#logging.level.org.italiangrid.storm.webdav.oauth=DEBUG
#logging.level.org.italiangrid.storm.webdav.oidc=DEBUG
#logging.level.org.springframework.security=DEBUG


## Voters
#logging.level.org.italiangrid.storm.webdav.authz.voters=DEBUG
#logging.level.org.italiangrid.storm.webdav.authz.pdp=DEBUG
#logging.level.org.springframework.security.web.util.matcher=DEBUG
#logging.level.org.springframework.security.access=DEBUG

## HTTP Client
### Connection management
# logging.level.org.apache.http.impl.conn=DEBUG
# logging.level.org.apache.http.impl.client=DEBUG
#logging.level.org.apache.http.client=DEBUG
### Wire
#logging.level.org.apache.http=DEBUG
#logging.level.org.apache.http.wire=DEBUG

#logging.level.org.eclipse.jetty.io=ERROR
#logging.level.org.eclipse.jetty.server=DEBUG
#logging.level.org.eclipse.jetty.server.HttpOutput=ERROR
12 changes: 12 additions & 0 deletions src/test/resources/lhcb/sa.d/disk.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name=lhcb_disk
rootPath=src/test/resources/lhcb/storage/lhcb_disk
filesystemType=posixfs
accessPoints=/disk/lhcb
vos=
orgs=https://lhcb-auth.web.cern.ch/
anonymousReadEnabled=false
authenticatedReadEnabled=false
orgsGrantReadPermissions=false
orgsGrantWritePermissions=false
wlcgScopeAuthzEnabled=true
fineGrainedAuthzEnabled=true
Empty file.

0 comments on commit c3a4a10

Please sign in to comment.