added check endpoint and fixed gui endpoint
This commit is contained in:
parent
e73dd5105a
commit
9d5ed34ec6
6 changed files with 105 additions and 63 deletions
|
@ -40,6 +40,9 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
@Order(7)
|
@Order(7)
|
||||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ErrorController {
|
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ErrorController {
|
||||||
|
|
||||||
|
private static final String ERROR_PATH = "/sebserver/error";
|
||||||
|
private static final String CHECK_PATH = "/sebserver/check";
|
||||||
|
|
||||||
@Value("${sebserver.webservice.http.redirect.gui}")
|
@Value("${sebserver.webservice.http.redirect.gui}")
|
||||||
private String guiRedirect;
|
private String guiRedirect;
|
||||||
@Value("${sebserver.webservice.api.exam.endpoint.discovery}")
|
@Value("${sebserver.webservice.api.exam.endpoint.discovery}")
|
||||||
|
@ -78,22 +81,27 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
|
||||||
public void configure(final WebSecurity web) {
|
public void configure(final WebSecurity web) {
|
||||||
web
|
web
|
||||||
.ignoring()
|
.ignoring()
|
||||||
.antMatchers("/error")
|
.antMatchers(ERROR_PATH)
|
||||||
|
.antMatchers(CHECK_PATH)
|
||||||
.antMatchers(this.examAPIDiscoveryEndpoint)
|
.antMatchers(this.examAPIDiscoveryEndpoint)
|
||||||
.antMatchers(this.adminAPIEndpoint + API.INFO_ENDPOINT + API.LOGO_PATH_SEGMENT + "/**")
|
.antMatchers(this.adminAPIEndpoint + API.INFO_ENDPOINT + API.LOGO_PATH_SEGMENT + "/**")
|
||||||
.antMatchers(this.adminAPIEndpoint + API.INFO_ENDPOINT + API.INFO_INST_PATH_SEGMENT + "/**")
|
.antMatchers(this.adminAPIEndpoint + API.INFO_ENDPOINT + API.INFO_INST_PATH_SEGMENT + "/**")
|
||||||
.antMatchers(this.adminAPIEndpoint + API.REGISTER_ENDPOINT);
|
.antMatchers(this.adminAPIEndpoint + API.REGISTER_ENDPOINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/error")
|
@RequestMapping(CHECK_PATH)
|
||||||
|
public void check() throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(ERROR_PATH)
|
||||||
public void handleError(final HttpServletResponse response) throws IOException {
|
public void handleError(final HttpServletResponse response) throws IOException {
|
||||||
//response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
|
response.getOutputStream().print(response.getStatus());
|
||||||
response.setHeader(HttpHeaders.LOCATION, this.guiRedirect);
|
response.setHeader(HttpHeaders.LOCATION, this.guiRedirect);
|
||||||
response.flushBuffer();
|
response.flushBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getErrorPath() {
|
public String getErrorPath() {
|
||||||
return "/error";
|
return ERROR_PATH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,60 +116,56 @@ public final class InstitutionalAuthenticationEntryPoint implements Authenticati
|
||||||
|
|
||||||
final String institutionalEndpoint = extractInstitutionalEndpoint(request);
|
final String institutionalEndpoint = extractInstitutionalEndpoint(request);
|
||||||
|
|
||||||
if (StringUtils.isNoneBlank(institutionalEndpoint) && log.isDebugEnabled()) {
|
if (StringUtils.isNotBlank(institutionalEndpoint)) {
|
||||||
log.debug("No default gui entrypoint requested: {}", institutionalEndpoint);
|
if (log.isDebugEnabled()) {
|
||||||
} else {
|
log.debug("No default gui entrypoint requested: {}", institutionalEndpoint);
|
||||||
request.getSession().setAttribute(INST_SUFFIX_ATTRIBUTE, null);
|
|
||||||
request.getSession().removeAttribute(API.PARAM_LOGO_IMAGE);
|
|
||||||
forwardToEntryPoint(request, response, this.guiEntryPoint, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
final RestTemplate restTemplate = new RestTemplate();
|
|
||||||
final List<EntityName> institutions = restTemplate
|
|
||||||
.exchange(
|
|
||||||
this.webserviceURIService.getURIBuilder()
|
|
||||||
.path(API.INFO_ENDPOINT + API.INFO_INST_ENDPOINT)
|
|
||||||
.toUriString(),
|
|
||||||
HttpMethod.GET,
|
|
||||||
HttpEntity.EMPTY,
|
|
||||||
new ParameterizedTypeReference<List<EntityName>>() {
|
|
||||||
},
|
|
||||||
institutionalEndpoint,
|
|
||||||
API.INFO_PARAM_INST_SUFFIX,
|
|
||||||
institutionalEndpoint)
|
|
||||||
.getBody();
|
|
||||||
|
|
||||||
if (institutions != null && !institutions.isEmpty()) {
|
|
||||||
request.getSession().setAttribute(
|
|
||||||
INST_SUFFIX_ATTRIBUTE,
|
|
||||||
StringUtils.isNotBlank(institutionalEndpoint)
|
|
||||||
? institutionalEndpoint
|
|
||||||
: null);
|
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
|
||||||
log.debug("Known and active gui entrypoint requested: {}", institutions);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String logoImageBase64 = requestLogoImage(institutionalEndpoint);
|
|
||||||
if (StringUtils.isNotBlank(logoImageBase64)) {
|
|
||||||
request.getSession().setAttribute(API.PARAM_LOGO_IMAGE, logoImageBase64);
|
|
||||||
|
|
||||||
}
|
|
||||||
forwardToEntryPoint(request, response, this.guiEntryPoint, false);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
|
||||||
log.error("Failed to extract and set institutional endpoint request: ", e);
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
final RestTemplate restTemplate = new RestTemplate();
|
||||||
|
final List<EntityName> institutions = restTemplate
|
||||||
|
.exchange(
|
||||||
|
this.webserviceURIService.getURIBuilder()
|
||||||
|
.path(API.INFO_ENDPOINT + API.INFO_INST_ENDPOINT)
|
||||||
|
.toUriString(),
|
||||||
|
HttpMethod.GET,
|
||||||
|
HttpEntity.EMPTY,
|
||||||
|
new ParameterizedTypeReference<List<EntityName>>() {
|
||||||
|
},
|
||||||
|
institutionalEndpoint,
|
||||||
|
API.INFO_PARAM_INST_SUFFIX,
|
||||||
|
institutionalEndpoint)
|
||||||
|
.getBody();
|
||||||
|
|
||||||
|
if (institutions != null && !institutions.isEmpty()) {
|
||||||
|
request.getSession().setAttribute(
|
||||||
|
INST_SUFFIX_ATTRIBUTE,
|
||||||
|
StringUtils.isNotBlank(institutionalEndpoint)
|
||||||
|
? institutionalEndpoint
|
||||||
|
: null);
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Known and active gui entrypoint requested: {}", institutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String logoImageBase64 = requestLogoImage(institutionalEndpoint);
|
||||||
|
if (StringUtils.isNotBlank(logoImageBase64)) {
|
||||||
|
request.getSession().setAttribute(API.PARAM_LOGO_IMAGE, logoImageBase64);
|
||||||
|
|
||||||
|
}
|
||||||
|
forwardToEntryPoint(request, response, this.guiEntryPoint, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Failed to extract and set institutional endpoint request: ", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
request.getSession().setAttribute(INST_SUFFIX_ATTRIBUTE, null);
|
request.getSession().setAttribute(INST_SUFFIX_ATTRIBUTE, null);
|
||||||
request.getSession().removeAttribute(API.PARAM_LOGO_IMAGE);
|
request.getSession().removeAttribute(API.PARAM_LOGO_IMAGE);
|
||||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
forwardToEntryPoint(request, response, this.guiEntryPoint, true);
|
forwardToEntryPoint(request, response, this.guiEntryPoint, institutionalEndpoint == null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,16 +199,25 @@ public final class InstitutionalAuthenticationEntryPoint implements Authenticati
|
||||||
|
|
||||||
public static String extractInstitutionalEndpoint(final HttpServletRequest request) {
|
public static String extractInstitutionalEndpoint(final HttpServletRequest request) {
|
||||||
final String requestURI = request.getRequestURI();
|
final String requestURI = request.getRequestURI();
|
||||||
if (StringUtils.isBlank(requestURI) || requestURI.equals(Constants.SLASH.toString())) {
|
if (StringUtils.isBlank(requestURI)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (requestURI.equals(Constants.SLASH.toString())) {
|
||||||
log.debug("Trying to verify institution from requested entrypoint url: {}", requestURI);
|
return StringUtils.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return requestURI.substring(requestURI.lastIndexOf(Constants.SLASH) + 1);
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Trying to verify institution from requested entrypoint url: {}", requestURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] split = StringUtils.split(requestURI, Constants.SLASH);
|
||||||
|
if (split.length > 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return split[0];
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Failed to extract institutional URL suffix: {}", e.getMessage());
|
log.error("Failed to extract institutional URL suffix: {}", e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -102,7 +102,7 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
if (active > 0) {
|
if (active > 0) {
|
||||||
throw new IllegalStateException("Townhall, for exam: " + examId + " already existis");
|
throw new IllegalStateException("Townhall, for exam: " + examId + " already exists");
|
||||||
}
|
}
|
||||||
|
|
||||||
final String newCollectingRoomName = UUID.randomUUID().toString();
|
final String newCollectingRoomName = UUID.randomUUID().toString();
|
||||||
|
|
|
@ -21,6 +21,7 @@ import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
@ -35,6 +36,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||||
|
@ -100,7 +102,7 @@ public class ExamAPI_V1_Controller {
|
||||||
|
|
||||||
final POSTMapper mapper = new POSTMapper(formParams, request.getQueryString());
|
final POSTMapper mapper = new POSTMapper(formParams, request.getQueryString());
|
||||||
|
|
||||||
final String remoteAddr = request.getRemoteAddr();
|
final String remoteAddr = this.getClientAddress(request);
|
||||||
final Long institutionId = (instIdRequestParam != null)
|
final Long institutionId = (instIdRequestParam != null)
|
||||||
? instIdRequestParam
|
? instIdRequestParam
|
||||||
: mapper.getLong(API.PARAM_INSTITUTION_ID);
|
: mapper.getLong(API.PARAM_INSTITUTION_ID);
|
||||||
|
@ -158,7 +160,7 @@ public class ExamAPI_V1_Controller {
|
||||||
return CompletableFuture.runAsync(
|
return CompletableFuture.runAsync(
|
||||||
() -> {
|
() -> {
|
||||||
|
|
||||||
final String remoteAddr = request.getRemoteAddr();
|
final String remoteAddr = this.getClientAddress(request);
|
||||||
final Long institutionId = getInstitutionId(principal);
|
final Long institutionId = getInstitutionId(principal);
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
|
@ -198,7 +200,7 @@ public class ExamAPI_V1_Controller {
|
||||||
return CompletableFuture.runAsync(
|
return CompletableFuture.runAsync(
|
||||||
() -> {
|
() -> {
|
||||||
|
|
||||||
final String remoteAddr = request.getRemoteAddr();
|
final String remoteAddr = this.getClientAddress(request);
|
||||||
final Long institutionId = getInstitutionId(principal);
|
final Long institutionId = getInstitutionId(principal);
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
|
@ -234,7 +236,7 @@ public class ExamAPI_V1_Controller {
|
||||||
return CompletableFuture.runAsync(
|
return CompletableFuture.runAsync(
|
||||||
() -> {
|
() -> {
|
||||||
|
|
||||||
final String remoteAddr = request.getRemoteAddr();
|
final String remoteAddr = this.getClientAddress(request);
|
||||||
final Long institutionId = getInstitutionId(principal);
|
final Long institutionId = getInstitutionId(principal);
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
|
@ -419,4 +421,23 @@ public class ExamAPI_V1_Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getClientAddress(final HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
final String ipAddress = request.getHeader("X-FORWARDED-FOR");
|
||||||
|
|
||||||
|
if (ipAddress == null) {
|
||||||
|
return request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipAddress.contains(",")) {
|
||||||
|
return StringUtils.split(ipAddress, Constants.COMMA)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipAddress;
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.warn("Failed to verify client IP address: {}", e.getMessage());
|
||||||
|
return request.getHeader("X-FORWARDED-FOR");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,7 +316,7 @@ public class ExamProctoringController {
|
||||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||||
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId) {
|
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId) {
|
||||||
|
|
||||||
checkAccess(institutionId, examId);
|
checkExamReadAccess(institutionId);
|
||||||
|
|
||||||
return this.examProcotringRoomService.getTownhallRoomData(examId)
|
return this.examProcotringRoomService.getTownhallRoomData(examId)
|
||||||
.getOrElse(() -> RemoteProctoringRoom.NULL_ROOM);
|
.getOrElse(() -> RemoteProctoringRoom.NULL_ROOM);
|
||||||
|
@ -351,7 +351,8 @@ public class ExamProctoringController {
|
||||||
// first create and register a room to collect all connection of the exam
|
// first create and register a room to collect all connection of the exam
|
||||||
// As long as the room exists new connections will join this room immediately
|
// As long as the room exists new connections will join this room immediately
|
||||||
// after have been applied to the default collecting room
|
// after have been applied to the default collecting room
|
||||||
final RemoteProctoringRoom townhallRoom = this.examProcotringRoomService.createTownhallRoom(examId, subject)
|
final RemoteProctoringRoom townhallRoom = this.examProcotringRoomService
|
||||||
|
.createTownhallRoom(examId, subject)
|
||||||
.onError(error -> this.examProcotringRoomService.disposeTownhallRoom(examId))
|
.onError(error -> this.examProcotringRoomService.disposeTownhallRoom(examId))
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
|
@ -590,6 +591,13 @@ public class ExamProctoringController {
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkExamReadAccess(final Long institutionId) {
|
||||||
|
this.authorization.check(
|
||||||
|
PrivilegeType.READ,
|
||||||
|
EntityType.EXAM,
|
||||||
|
institutionId);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkAccess(final Long institutionId, final Long examId) {
|
private void checkAccess(final Long institutionId, final Long examId) {
|
||||||
this.authorization.check(
|
this.authorization.check(
|
||||||
PrivilegeType.READ,
|
PrivilegeType.READ,
|
||||||
|
|
|
@ -36,7 +36,7 @@ logging.level.ch=INFO
|
||||||
|
|
||||||
### spring actuator configuration
|
### spring actuator configuration
|
||||||
management.endpoints.web.base-path=/mprofile
|
management.endpoints.web.base-path=/mprofile
|
||||||
management.endpoints.web.exposure.include=metrics,logfile,loggers,heapdump
|
management.endpoints.web.exposure.include=metrics,logfile,loggers,heapdump,health
|
||||||
|
|
||||||
##########################################################
|
##########################################################
|
||||||
### Overall Security Settings
|
### Overall Security Settings
|
||||||
|
|
Loading…
Reference in a new issue