Skip to content

Commit

Permalink
Merge ed5dd4a into 4b79481
Browse files Browse the repository at this point in the history
  • Loading branch information
syjer committed Dec 27, 2018
2 parents 4b79481 + ed5dd4a commit 796dbfd
Show file tree
Hide file tree
Showing 32 changed files with 841 additions and 228 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ You must specify a project property at the command line, such as
```
The local "bootRun" task has the following prerequisites:

- a PostgreSQL instance up and runnning on localhost:5432
- a PostgreSQL (version 9.6 or later) instance up and runnning on localhost:5432
- a _postgres_ user having password: _password_
- a database named _alfio_

Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ dependencies {
compile 'ch.digitalfondue.vatchecker:vatchecker:1.1'
compile 'ch.digitalfondue.basicxlsx:basicxlsx:0.5.0'
compile 'org.imgscalr:imgscalr-lib:4.2'
compile 'org.aspectj:aspectjweaver:1.9.2'

testCompile "ru.yandex.qatools.embed:postgresql-embedded:2.9" //we leave it to the v2.9 for now, since some user had issues on Windows

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/alfio/config/DataSourceConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import alfio.manager.FileDownloadManager;
import alfio.manager.UploadedResourceManager;
import alfio.manager.system.ConfigurationManager;
import alfio.repository.user.OrganizationRepository;
import alfio.util.TemplateManager;
import ch.digitalfondue.npjt.QueryFactory;
import ch.digitalfondue.npjt.QueryRepositoryScanner;
Expand Down Expand Up @@ -170,6 +171,12 @@ public FileDownloadManager fileDownloadManager() {
return new FileDownloadManager();
}

@Bean
public RowLevelSecurity.RoleAndOrganizationsAspect getRoleAndOrganizationsAspect(NamedParameterJdbcTemplate namedParameterJdbcTemplate,
OrganizationRepository organizationRepository) {
return new RowLevelSecurity.RoleAndOrganizationsAspect(namedParameterJdbcTemplate, organizationRepository);
}

@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/alfio/config/Initializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import alfio.util.DefaultExceptionHandler;
import com.openhtmltopdf.util.XRLog;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.Validate;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
Expand All @@ -35,7 +34,6 @@
import java.util.Objects;
import java.util.logging.Level;

@Log4j2
public class Initializer extends AbstractAnnotationConfigDispatcherServletInitializer {

public static final String PROFILE_DEV = "dev";
Expand Down
171 changes: 171 additions & 0 deletions src/main/java/alfio/config/RowLevelSecurity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/**
* This file is part of alf.io.
*
* alf.io is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* alf.io is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.config;

import alfio.repository.user.OrganizationRepository;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.jdbc.core.namedparam.EmptySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

@Log4j2
public class RowLevelSecurity {

private static final OrRequestMatcher IS_PUBLIC_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/resources/**"),
new AntPathRequestMatcher("/event/**"),
new AntPathRequestMatcher("/"),
new AntPathRequestMatcher("/file/**"),
new AntPathRequestMatcher("/api/events/**"),
new AntPathRequestMatcher("/api/webhook/**"),
new AntPathRequestMatcher("/session-expired"),
new AntPathRequestMatcher("/authentication"));

private static boolean isCurrentlyInAPublicUrlRequest() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
return IS_PUBLIC_URLS.matches(request);
}

private static boolean isInAHttpRequest() {
return RequestContextHolder.getRequestAttributes() != null;
}

private static boolean isLoggedUser() {
SecurityContext context = SecurityContextHolder.getContext();
if (context != null && context.getAuthentication() != null) {
return !"anonymousUser".equals(context.getAuthentication().getName());
}
return false;
}

private static boolean isAdmin() {
if(isLoggedUser()) {
return SecurityContextHolder.getContext().getAuthentication()
.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.anyMatch("ROLE_ADMIN"::equals);
}
return false;
}

@Aspect
public static class RoleAndOrganizationsAspect {
private final NamedParameterJdbcTemplate jdbcTemplate;

private final OrganizationRepository organizationRepository;
public RoleAndOrganizationsAspect(NamedParameterJdbcTemplate namedParameterJdbcTemplate,
OrganizationRepository organizationRepository) {
this.jdbcTemplate = namedParameterJdbcTemplate;
this.organizationRepository = organizationRepository;
}

@Around("within(alfio.manager..*) && (@target(org.springframework.transaction.annotation.Transactional) || " +
" @annotation(org.springframework.transaction.annotation.Transactional))")
public Object setRoleAndVariable(ProceedingJoinPoint joinPoint) throws Throwable {

if (isInAHttpRequest()) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
DataSource dataSource = jdbcTemplate.getJdbcTemplate().getDataSource();
Connection connection = DataSourceUtils.getConnection(dataSource);

boolean mustCheck = false;
String formattedOrgIds = "";
if (DataSourceUtils.isConnectionTransactional(connection, dataSource)) {
mustCheck = !isCurrentlyInAPublicUrlRequest() && isLoggedUser() && !isAdmin();
if (mustCheck) {
Set<Integer> orgIds = new TreeSet<>(organizationRepository.findAllOrganizationIdForUser(SecurityContextHolder.getContext().getAuthentication().getName()));
formattedOrgIds = orgIds.stream().map(s -> Integer.toString(s)).collect(Collectors.joining(",", "'{", "}'"));
jdbcTemplate.update("set local role application_user", new EmptySqlParameterSource());
jdbcTemplate.update("set local alfio.checkRowAccess = true", new EmptySqlParameterSource());
//cannot use bind variable when calling set local, it's ugly :(
jdbcTemplate.update("set local alfio.currentUserOrgs = " + formattedOrgIds, new EmptySqlParameterSource());
}
// note, the policy will check if the variable alfio.checkRowAccess is present before doing anything

//
}

if (log.isTraceEnabled()) {
logEntry(connection, dataSource, joinPoint, request, mustCheck, formattedOrgIds);
}
}

return joinPoint.proceed();
}




private static void logEntry(Connection connection, DataSource dataSource,
ProceedingJoinPoint joinPoint,
HttpServletRequest request,
boolean mustCheck,
String formattedOrgIds) {

StringBuilder sb = new StringBuilder();


if (DataSourceUtils.isConnectionTransactional(connection, dataSource)) {
sb.append("-----------\n");
sb.append("connection is transactional\n");
sb.append("URL IS ").append(request.getRequestURI()).append("\n");
sb.append(request.getRequestURL()).append("\n");
sb.append(joinPoint).append("\n");
sb.append("public url: ").append(isCurrentlyInAPublicUrlRequest()).append("\n");

if (SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null) {
sb.append("auth is ").append(SecurityContextHolder.getContext().getAuthentication().getName()).append("\n");
} else {
sb.append("no user\n");
}

sb.append("must check row access: ").append(mustCheck).append("\n");

if(mustCheck) {
sb.append("org ids are: ").append(formattedOrgIds).append("\n");
}
sb.append("-----------\n");

} else {
sb.append("-----------\n");
sb.append("connection is NOT transactional so the check will not be done!\n");
sb.append("URL IS ").append(request.getRequestURI()).append("\n");
sb.append(joinPoint).append("\n");
sb.append("-----------\n");
}

log.trace(sb.toString());
}
}
}
3 changes: 0 additions & 3 deletions src/main/java/alfio/config/SpringBootInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package alfio.config;

import com.openhtmltopdf.util.XRLog;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
Expand All @@ -33,7 +32,6 @@

import javax.servlet.Filter;
import javax.servlet.SessionCookieConfig;
import java.util.Objects;
import java.util.logging.Level;

import static org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext;
Expand All @@ -47,7 +45,6 @@
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration.class})
@Configuration
@Profile(Initializer.PROFILE_SPRING_BOOT)
@Log4j2
public class SpringBootInitializer {


Expand Down
8 changes: 3 additions & 5 deletions src/main/java/alfio/controller/TicketController.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import alfio.model.transaction.PaymentProxy;
import alfio.model.user.Organization;
import alfio.repository.TicketCategoryRepository;
import alfio.repository.TicketFieldRepository;
import alfio.repository.user.OrganizationRepository;
import alfio.util.ImageUtil;
import alfio.util.LocaleUtil;
Expand Down Expand Up @@ -67,7 +66,6 @@ public class TicketController {
private final ConfigurationManager configurationManager;
private final FileUploadManager fileUploadManager;
private final TicketHelper ticketHelper;
private final TicketFieldRepository ticketFieldRepository;

@RequestMapping(value = "/event/{eventName}/reservation/{reservationId}/{ticketIdentifier}", method = RequestMethod.GET)
public String showTicketOLD(@PathVariable("eventName") String eventName, @PathVariable("reservationId") String reservationId,
Expand Down Expand Up @@ -147,7 +145,7 @@ public String sendTicketByEmail(@PathVariable("eventName") String eventName,
return "OK";
}

private Ticket internalSendTicketByEmail(HttpServletRequest request, Triple<Event, TicketReservation, Ticket> data) throws IOException {
private Ticket internalSendTicketByEmail(HttpServletRequest request, Triple<Event, TicketReservation, Ticket> data) {
Ticket ticket = data.getRight();
Event event = data.getLeft();
Locale locale = LocaleUtil.getTicketLanguage(ticket, request);
Expand All @@ -163,7 +161,7 @@ private Ticket internalSendTicketByEmail(HttpServletRequest request, Triple<Even

@RequestMapping(value = "/event/{eventName}/ticket/{ticketIdentifier}/download-ticket", method = RequestMethod.GET)
public void generateTicketPdf(@PathVariable("eventName") String eventName,
@PathVariable("ticketIdentifier") String ticketIdentifier, HttpServletRequest request, HttpServletResponse response) throws IOException, WriterException {
@PathVariable("ticketIdentifier") String ticketIdentifier, HttpServletRequest request, HttpServletResponse response) throws IOException {

Optional<Triple<Event, TicketReservation, Ticket>> oData = ticketReservationManager.fetchCompleteAndAssigned(eventName, ticketIdentifier);
if(!oData.isPresent()) {
Expand Down Expand Up @@ -191,7 +189,7 @@ public void generateTicketPdf(@PathVariable("eventName") String eventName,

@RequestMapping(value = "/event/{eventName}/ticket/{ticketIdentifier}/code.png", method = RequestMethod.GET)
public void generateTicketCode(@PathVariable("eventName") String eventName,
@PathVariable("ticketIdentifier") String ticketIdentifier, HttpServletResponse response) throws IOException, WriterException {
@PathVariable("ticketIdentifier") String ticketIdentifier, HttpServletResponse response) throws IOException {

Optional<Triple<Event, TicketReservation, Ticket>> oData = ticketReservationManager.fetchCompleteAndAssigned(eventName, ticketIdentifier);
if(!oData.isPresent()) {
Expand Down
Loading

0 comments on commit 796dbfd

Please sign in to comment.