diff --git a/.github/workflows/buildReporting.yml b/.github/workflows/buildReporting.yml index b5923992..73222563 100644 --- a/.github/workflows/buildReporting.yml +++ b/.github/workflows/buildReporting.yml @@ -53,6 +53,8 @@ jobs: - name: Reporting uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: flags: unittests name: SEB Server Build @@ -92,7 +94,7 @@ jobs: uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v4 - name: Login to DockerHub uses: docker/login-action@v2 @@ -147,13 +149,13 @@ jobs: TAGS: ${{ steps.meta.outputs.tags }} cleanup: - needs: docker-build - # Run only on tagging - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + needs: [maven-build-reporting, docker-build] + if: | + always() runs-on: ubuntu-latest steps: - name: Delete Artifacts - uses: geekyeggo/delete-artifact@v4 + uses: geekyeggo/delete-artifact@v5 with: name: Package diff --git a/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java index 9dd9029f..177bb475 100644 --- a/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java @@ -40,9 +40,6 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; @Order(7) 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}") private String guiRedirect; @Value("${sebserver.webservice.api.exam.endpoint.discovery}") @@ -77,25 +74,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E return new BCryptPasswordEncoder(4); } - @Override - public void configure(final WebSecurity web) { - web - .ignoring() - .antMatchers(ERROR_PATH) - .antMatchers(CHECK_PATH) - .antMatchers(this.examAPIDiscoveryEndpoint) - .antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT) - .antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT + API.PASSWORD_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.REGISTER_ENDPOINT); - } - - @RequestMapping(CHECK_PATH) + @RequestMapping(API.CHECK_PATH) public void check() throws IOException { } - @RequestMapping(ERROR_PATH) + @RequestMapping(API.ERROR_PATH) public void handleError(final HttpServletResponse response) throws IOException { response.getOutputStream().print(response.getStatus()); response.setHeader(HttpHeaders.LOCATION, this.guiRedirect); diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java index c76bf372..71a32884 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java @@ -12,6 +12,9 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; public final class API { + public static final String ERROR_PATH = "/sebserver/error"; + public static final String CHECK_PATH = "/sebserver/check"; + public enum BulkActionType { HARD_DELETE, DEACTIVATE, diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java index 9cec7f43..abb8bfe2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java @@ -281,7 +281,7 @@ public final class Exam implements GrantEntity { } return result; } else { - return null; + return Collections.emptyList(); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java index caacff0c..7b36682c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java @@ -42,6 +42,10 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter { private String remoteProctoringViewServletEndpoint; @Value("${springdoc.api-docs.enabled:false}") private boolean springDocsAPIEnabled; + @Value("${sebserver.webservice.api.exam.endpoint.discovery}") + private String examAPIDiscoveryEndpoint; + @Value("${sebserver.webservice.api.admin.endpoint}") + private String adminAPIEndpoint; /** Gui-service related public URLS from spring web security perspective */ public static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher( @@ -52,28 +56,42 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter { // project specific static resources new AntPathRequestMatcher("/images/**"), - new AntPathRequestMatcher("/favicon.ico")); + new AntPathRequestMatcher("/favicon.ico") + ); @Override public void configure(final WebSecurity web) { web - .ignoring() - .requestMatchers(PUBLIC_URLS) - .antMatchers(this.guiEntryPoint) - .antMatchers(this.remoteProctoringEndpoint) - .antMatchers(this.remoteProctoringEndpoint + this.remoteProctoringViewServletEndpoint + "/*"); + .ignoring() + .antMatchers(this.guiEntryPoint) + ; if (this.springDocsAPIEnabled) { web.ignoring().antMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**"); } } + + @Override public void configure(final HttpSecurity http) throws Exception { http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() + .authorizeRequests() + .antMatchers(this.remoteProctoringEndpoint).permitAll() + .antMatchers(this.remoteProctoringEndpoint + this.remoteProctoringViewServletEndpoint + "/*").permitAll() + .requestMatchers(PUBLIC_URLS).permitAll() + .antMatchers(API.ERROR_PATH).permitAll() + .antMatchers(API.CHECK_PATH).permitAll() + .antMatchers(this.examAPIDiscoveryEndpoint).permitAll() + .antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT).permitAll() + .antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT + API.PASSWORD_PATH_SEGMENT).permitAll() + .antMatchers(adminAPIEndpoint + API.INFO_ENDPOINT + API.LOGO_PATH_SEGMENT + "/**").permitAll() + .antMatchers(adminAPIEndpoint + API.INFO_ENDPOINT + API.INFO_INST_PATH_SEGMENT + "/**").permitAll() + .antMatchers(adminAPIEndpoint + API.REGISTER_ENDPOINT).permitAll() + .and() .antMatcher("/**") .authorizeRequests() .anyRequest() diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java index 599824b1..24b16f87 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java @@ -57,6 +57,8 @@ public class ExamIndicatorsList implements TemplateComposer { new LocTextKey("sebserver.exam.indicator.list.pleaseSelect"); private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE = new LocTextKey("sebserver.exam.indicator.list.empty"); + private static final LocTextKey CONFIRM_MESSAGE_REMOVE_INDICATOR = + new LocTextKey("sebserver.exam.indicator.list.delete.confirm"); private final PageService pageService; private final ResourceService resourceService; @@ -144,6 +146,7 @@ public class ExamIndicatorsList implements TemplateComposer { indicatorTable::getMultiSelection, this::deleteSelectedIndicator, INDICATOR_EMPTY_SELECTION_TEXT_KEY) + .withConfirm(() -> CONFIRM_MESSAGE_REMOVE_INDICATOR) .publishIf(() -> indicatorEnabled && !isLight && editable && indicatorTable.hasAnyContent(), false) .newAction(ActionDefinition.EXAM_INDICATOR_NEW) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/converter/RealNumberConverter.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/converter/RealNumberConverter.java index 982874b2..275ef83b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/converter/RealNumberConverter.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/converter/RealNumberConverter.java @@ -32,11 +32,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueCon @WebServiceProfile public class RealNumberConverter implements AttributeValueConverter { - private static final Logger log = LoggerFactory.getLogger(IntegerConverter.class); + private static final Logger log = LoggerFactory.getLogger(RealNumberConverter.class); public static final Set SUPPORTED_ATTR_NAMES = Utils.immutableSetOf( "defaultPageZoomLevel", - "defaultTextZoomLevel"); + "defaultTextZoomLevel", + "screenProctoringImageDownscale"); private static final String XML_TEMPLATE = "%s%s"; private static final String JSON_TEMPLATE = "\"%s\":%s"; @@ -89,6 +90,12 @@ public class RealNumberConverter implements AttributeValueConverter { realVal = 0; } + // NOTE: this is a special case for screenProctoringImageDownscale selector to get the selected real value + // from the selection-index instead using the index. See SEBSERV-527 + if ("screenProctoringImageDownscale".equals(attribute.name)) { + realVal = realVal / 10.0 + 1.0; + } + out.write(Utils.toByteArray(String.format( template, AttributeValueConverter.extractName(attribute), diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java index 76993e77..0624fdb9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java @@ -13,6 +13,7 @@ import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import ch.ethz.seb.sebserver.gbl.api.API; import org.apache.catalina.filters.RemoteIpFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,18 +51,18 @@ import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebClientDetailsService; import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebserviceResourceConfiguration; /** This is the main web-security Spring configuration for SEB-Server webservice API - * + *

* Currently two separated Rest API's are implemented, one for administration and maintenance * of the SEB-Server (AdminAPI) and one for SEB-Client connection on running exams and eventually * also for LMS communication), if needed (ExamAPI). The AdministrationAPI uses OAuth 2 password * grant with refresh-token, same as in the prototype and the ExamAPI uses the client_credential grant. - * + *

* There is a Spring Authorization-Server defining this two clients (AdminAPIClient and ExamAPIClient) as well as * two Spring Resource-Server for the separation of the different API's - * + *

* The endpoint of the AdministrationAPI can be configured within the key; sebserver.webservice.api.admin.endpoint * and is by default set to "/admin-api/**" - * + *

* The endpoint of the ExamAPI can be configured within the key; sebserver.webservice.api.exam.endpoint * and is by default set to "/exam-api/**" */ @WebServiceProfile diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebserviceResourceConfiguration.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebserviceResourceConfiguration.java index f7e70ad2..04790c34 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebserviceResourceConfiguration.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebserviceResourceConfiguration.java @@ -11,6 +11,8 @@ package ch.ethz.seb.sebserver.webservice.weblayer.oauth; import java.util.Arrays; import java.util.List; +import ch.ethz.seb.sebserver.gbl.api.API; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -30,6 +32,8 @@ public abstract class WebserviceResourceConfiguration extends ResourceServerConf public static final String ADMIN_API_RESOURCE_ID = "seb-server-administration-api"; /** The resource identifier of the Exam API resources */ public static final String EXAM_API_RESOURCE_ID = "seb-server-exam-api"; + @Value("${sebserver.webservice.api.exam.endpoint.discovery}") + private String examAPIDiscoveryEndpoint; public WebserviceResourceConfiguration( final TokenStore tokenStore, @@ -74,6 +78,16 @@ public abstract class WebserviceResourceConfiguration extends ResourceServerConf .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() + .authorizeRequests() + .antMatchers(API.ERROR_PATH).permitAll() + .antMatchers(API.CHECK_PATH).permitAll() + .antMatchers(this.examAPIDiscoveryEndpoint).permitAll() + .antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT).permitAll() + .antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT + API.PASSWORD_PATH_SEGMENT).permitAll() + .antMatchers(configurerAdapter.apiEndpoint + API.INFO_ENDPOINT + API.LOGO_PATH_SEGMENT + "/**").permitAll() + .antMatchers(configurerAdapter.apiEndpoint + API.INFO_ENDPOINT + API.INFO_INST_PATH_SEGMENT + "/**").permitAll() + .antMatchers(configurerAdapter.apiEndpoint + API.REGISTER_ENDPOINT).permitAll() + .and() .antMatcher(configurerAdapter.apiEndpoint + "/**") .authorizeRequests() .anyRequest() diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 434fdaee..8de1f7bd 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -709,6 +709,7 @@ sebserver.exam.indicator.list.column.thresholds=Thresholds sebserver.exam.indicator.list.column.thresholds.tooltip=The thresholds of the indicator sebserver.exam.indicator.list.empty=There is currently no indicator defined for this exam. Please create a new one sebserver.exam.indicator.list.pleaseSelect=At first please select an indicator from the list +sebserver.exam.indicator.list.delete.confirm=Are you sure to delete the selected Indicator from the Exam? sebserver.exam.clientgroup.list.actions=  sebserver.exam.clientgroup.list.title=Client Groups