Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;

import de.hs_mannheim.informatik.ct.util.CookieManager;
import de.hs_mannheim.informatik.ct.controller.exception.InvalidRoomPinException;
import de.hs_mannheim.informatik.ct.model.Room;
import de.hs_mannheim.informatik.ct.model.RoomVisit;
Expand Down Expand Up @@ -136,7 +137,7 @@ public String checkIn(@PathVariable String roomId,

@PostMapping("/checkIn")
@Transactional
public String checkIn(@ModelAttribute RoomVisit.Data visitData, Model model) throws UnsupportedEncodingException, InvalidRoomPinException, InvalidEmailException, InvalidExternalUserdataException {
public String checkIn(@ModelAttribute RoomVisit.Data visitData, Model model, CookieManager cookieManager) throws UnsupportedEncodingException, InvalidRoomPinException, InvalidEmailException, InvalidExternalUserdataException {
isRoomPinValidOrThrow(visitData);

val room = roomService.getRoomOrThrow(visitData.getRoomId());
Expand Down Expand Up @@ -167,6 +168,8 @@ public String checkIn(@ModelAttribute RoomVisit.Data visitData, Model model) thr
// room manager should always be allowed to check-in
// and needs to be checked in before the browser is forwarded to a different page!
val visit = roomVisitService.visitRoom(visitor, room);

cookieManager.addCookie(CookieManager.Cookies.CHECKED_IN_EMAIL, visitorEmail);

if (visitData.isPrivileged()) {
val encodedVisitorEmail = URLEncoder.encode(visitorEmail, "UTF-8");
Expand Down Expand Up @@ -201,7 +204,7 @@ private void isRoomPinValidOrThrow(Data visitData) throws InvalidRoomPinExceptio
*/
@PostMapping("/checkInOverride")
@Transactional
public String checkInWithOverride(@ModelAttribute RoomVisit.Data visitData, Model model) throws
public String checkInWithOverride(@ModelAttribute RoomVisit.Data visitData, Model model, CookieManager cookieManager) throws
UnsupportedEncodingException, InvalidEmailException, InvalidExternalUserdataException, InvalidRoomPinException {

// TODO: this method is very similar with the normal check-in, maybe this should be refactored?
Expand All @@ -219,6 +222,9 @@ public String checkInWithOverride(@ModelAttribute RoomVisit.Data visitData, Mode
roomVisitService.checkOutVisitor(visitor);

val visit = roomVisitService.visitRoom(visitor, room);

cookieManager.addCookie(CookieManager.Cookies.CHECKED_IN_EMAIL, visitorEmail);

val currentVisitCount = roomVisitService.getVisitorCount(room);
visitData = new RoomVisit.Data(visit, currentVisitCount);
model.addAttribute("visitData", visitData);
Expand All @@ -227,9 +233,9 @@ public String checkInWithOverride(@ModelAttribute RoomVisit.Data visitData, Mode
}

@PostMapping("/checkOut")
public String checkOut(@ModelAttribute RoomVisit.Data visitData) {
public String checkOut(@ModelAttribute RoomVisit.Data visitData, CookieManager cookieManager) {
val visitor = getVisitorOrThrow(visitData.getVisitorEmail());

cookieManager.removeCookie(CookieManager.Cookies.CHECKED_IN_EMAIL);
roomVisitService.checkOutVisitor(visitor);
return "redirect:/r/checkedOut";
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Corona Tracking Tool der Hochschule Mannheim
* Copyright (C) 2021 Hochschule Mannheim
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hs_mannheim.informatik.ct.controller.interceptor;

import de.hs_mannheim.informatik.ct.controller.Utilities;
import de.hs_mannheim.informatik.ct.model.RoomVisit;
import de.hs_mannheim.informatik.ct.persistence.services.RoomVisitService;
import de.hs_mannheim.informatik.ct.persistence.services.VisitorService;
import de.hs_mannheim.informatik.ct.util.CookieManager;
import lombok.val;
import lombok.var;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;

public class CheckInInterceptor implements HandlerInterceptor {

@Autowired
private VisitorService visitorService;

@Autowired
private RoomVisitService roomVisitService;

@Autowired
private Utilities util;

private static final String CHECKED_IN_COOKIE_NAME = "checkedInEmail";

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
val cookieManager = new CookieManager(request, response);
var isCheckedIn = false;
val checkedInEmail = cookieManager.getCookieValue(CookieManager.Cookies.CHECKED_IN_EMAIL);
if(checkedInEmail != null){
val checkedInRoom = getCheckedInRoomName(checkedInEmail);
if(checkedInRoom != null){
isCheckedIn = true;
request.setAttribute("checkedInRoom", checkedInRoom);
}else{
cookieManager.removeCookie(CookieManager.Cookies.CHECKED_IN_EMAIL);
}
}
request.setAttribute("checkedInEmail", checkedInEmail);
request.setAttribute("isCheckedIn", isCheckedIn);
}

private String getCheckedInRoomName(String email){
List<RoomVisit> roomVisits = findCurrentRoomVisitsByEmail(email);
return roomVisits.size() > 0 ? roomVisits.get(0).getRoom().getName() : null;
}

private List<RoomVisit> findCurrentRoomVisitsByEmail(String email){
List<RoomVisit> roomVisits = new ArrayList<>();
val visitor = visitorService.findVisitorByEmail(email);
if(visitor.isPresent()) {
for(val roomVisit : roomVisitService.getCheckedInRoomVisits(visitor.get())){
roomVisits.add(roomVisit);
}
}
return roomVisits;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Corona Tracking Tool der Hochschule Mannheim
* Copyright (C) 2021 Hochschule Mannheim
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hs_mannheim.informatik.ct.controller.resolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import de.hs_mannheim.informatik.ct.util.CookieManager;

public class CookieManagerResolver implements HandlerMethodArgumentResolver {

@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameter().getType() == CookieManager.class;
}

@Override
public Object resolveArgument(
MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) throws Exception {

HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
HttpServletResponse response = (HttpServletResponse) nativeWebRequest.getNativeResponse();

return new CookieManager(request, response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public class ContactTracingService {

@NonNull
public List<Contact<? extends Visit>> getVisitorContacts(@NonNull Visitor visitor) {
val contacts = new ArrayList<Contact<?>>();
val contacts = new ArrayList<Contact<? extends Visit>>();

for (val service : visitServices) {
contacts.addAll(service.getVisitorContacts(visitor));
}
Expand Down
151 changes: 151 additions & 0 deletions src/main/java/de/hs_mannheim/informatik/ct/util/CookieManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't we manage cookies in a much simpler fashion before this? Can you briefly explain, why this is neccessary, pls?

* Corona Tracking Tool der Hochschule Mannheim
* Copyright (C) 2021 Hochschule Mannheim
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package de.hs_mannheim.informatik.ct.util;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalTime;

import org.springframework.web.util.WebUtils;

import lombok.val;

public class CookieManager {
private HttpServletRequest request;
private HttpServletResponse response;

public CookieManager(HttpServletRequest request){
this.request = request;
}

public CookieManager(HttpServletRequest request, HttpServletResponse response){
this(request);
this.response = response;
}

public enum Cookies{
CHECKED_IN_EMAIL("checkedInEmail");

String name;

Cookies(String name) {
this.name = name;
}

String getName(){
return this.name;
}
}

/**
* create a new cookie with a cookie factory
*
* @param cookieType cookie type
* @param value value of the cookie
*/
private Cookie createCookie(Cookies cookieType, String value) {
Cookie cookie = null;
switch (cookieType){
case CHECKED_IN_EMAIL:
cookie = new CookieBuilder(cookieType.getName(), value)
.maxAge(getSecondsTill(LocalTime.parse(ScheduledMaintenanceTasks.FORCED_END_TIME)))
.build();
break;
}
return cookie;
}

/**
* add a cookie to the http response
*
* @param cookieType cookie type
* @param value value of the cookie
*/
public void addCookie(Cookies cookieType, String value) {
val cookie = createCookie(cookieType, value);
response.addCookie(cookie);
}

/**
* remove a cookie from the http response
*
* @param cookieType cookie type
*/
public void removeCookie(Cookies cookieType) {
Cookie cookie = new Cookie(cookieType.getName(), "");
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
}

/**
* get a cookie from http request
*
* @param cookieType cookie type
*/
public String getCookieValue(Cookies cookieType){
val cookie = WebUtils.getCookie(request, cookieType.getName());
return cookie!=null ? cookie.getValue() : null;
}

/**
* remove a cookie from the http response
*
* @param maxAgeEndTime time when the cookie should be invalid
* @return max age of the cookie
*/
private int getSecondsTill(LocalTime maxAgeEndTime){
int now = LocalTime.now().toSecondOfDay();
int endTime = maxAgeEndTime.toSecondOfDay();
int nextDayEndTime = ((24 * 60 * 60) + endTime);
return (now > endTime) ? nextDayEndTime - now : endTime - now;
}

private static class CookieBuilder{
private String name, value, path;
private int maxAge;

public CookieBuilder(String name, String value){
this.name = name;
this.value = value;
this.path = "/";
this.maxAge = 0;
}

public CookieBuilder maxAge(int maxAge){
this.maxAge = maxAge;
return this;
}

public CookieBuilder path(String path){
this.path = path;
return this;
}

public Cookie build(){
val cookie = new Cookie(name, value);
cookie.setPath(path);
if(maxAge>0){
cookie.setMaxAge(maxAge);
}
return cookie;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class ScheduledMaintenanceTasks {

private final int CRON_HOUR = 3;
private final int CRON_MINUTE = 55;
private final String FORCED_END_TIME = "00:00:00";
public static final String FORCED_END_TIME = "00:00:00";

//@Scheduled(fixedRate = 5 * 60 * 1000) // Every 5 Minutes
@Scheduled(cron = "0 " + CRON_MINUTE + " " + CRON_HOUR + " * * *") // 3:55 AM
Expand Down
Loading