merged development
This commit is contained in:
		
						commit
						a1660de341
					
				
					 10 changed files with 69 additions and 37 deletions
				
			
		
							
								
								
									
										12
									
								
								.github/workflows/buildReporting.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/buildReporting.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -53,6 +53,8 @@ jobs: | ||||||
|     - |     - | ||||||
|       name: Reporting |       name: Reporting | ||||||
|       uses: codecov/codecov-action@v4 |       uses: codecov/codecov-action@v4 | ||||||
|  |       env: | ||||||
|  |         CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | ||||||
|       with: |       with: | ||||||
|         flags: unittests  |         flags: unittests  | ||||||
|         name: SEB Server Build  |         name: SEB Server Build  | ||||||
|  | @ -92,7 +94,7 @@ jobs: | ||||||
|       uses: docker/setup-qemu-action@v3 |       uses: docker/setup-qemu-action@v3 | ||||||
|     - |     - | ||||||
|       name: Set up Docker Buildx |       name: Set up Docker Buildx | ||||||
|       uses: docker/setup-buildx-action@v2 |       uses: docker/setup-buildx-action@v4 | ||||||
|     - |     - | ||||||
|       name: Login to DockerHub |       name: Login to DockerHub | ||||||
|       uses: docker/login-action@v2 |       uses: docker/login-action@v2 | ||||||
|  | @ -147,13 +149,13 @@ jobs: | ||||||
|         TAGS: ${{ steps.meta.outputs.tags }} |         TAGS: ${{ steps.meta.outputs.tags }} | ||||||
|          |          | ||||||
|   cleanup: |   cleanup: | ||||||
|     needs: docker-build |     needs: [maven-build-reporting, docker-build] | ||||||
|      # Run only on tagging |     if: | | ||||||
|     if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') |       always() | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|     - |     - | ||||||
|       name: Delete Artifacts |       name: Delete Artifacts | ||||||
|       uses: geekyeggo/delete-artifact@v4 |       uses: geekyeggo/delete-artifact@v5 | ||||||
|       with: |       with: | ||||||
|           name: Package |           name: Package | ||||||
|  |  | ||||||
|  | @ -40,9 +40,6 @@ 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}") | ||||||
|  | @ -77,25 +74,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E | ||||||
|         return new BCryptPasswordEncoder(4); |         return new BCryptPasswordEncoder(4); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @RequestMapping(API.CHECK_PATH) | ||||||
|     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) |  | ||||||
|     public void check() throws IOException { |     public void check() throws IOException { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @RequestMapping(ERROR_PATH) |     @RequestMapping(API.ERROR_PATH) | ||||||
|     public void handleError(final HttpServletResponse response) throws IOException { |     public void handleError(final HttpServletResponse response) throws IOException { | ||||||
|         response.getOutputStream().print(response.getStatus()); |         response.getOutputStream().print(response.getStatus()); | ||||||
|         response.setHeader(HttpHeaders.LOCATION, this.guiRedirect); |         response.setHeader(HttpHeaders.LOCATION, this.guiRedirect); | ||||||
|  |  | ||||||
|  | @ -12,6 +12,9 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; | ||||||
| 
 | 
 | ||||||
| public final class API { | public final class API { | ||||||
| 
 | 
 | ||||||
|  |     public static final String ERROR_PATH = "/sebserver/error"; | ||||||
|  |     public static final String CHECK_PATH = "/sebserver/check"; | ||||||
|  | 
 | ||||||
|     public enum BulkActionType { |     public enum BulkActionType { | ||||||
|         HARD_DELETE, |         HARD_DELETE, | ||||||
|         DEACTIVATE, |         DEACTIVATE, | ||||||
|  |  | ||||||
|  | @ -281,7 +281,7 @@ public final class Exam implements GrantEntity { | ||||||
|             } |             } | ||||||
|             return result; |             return result; | ||||||
|         } else { |         } else { | ||||||
|             return null; |             return Collections.emptyList(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -42,6 +42,10 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter { | ||||||
|     private String remoteProctoringViewServletEndpoint; |     private String remoteProctoringViewServletEndpoint; | ||||||
|     @Value("${springdoc.api-docs.enabled:false}") |     @Value("${springdoc.api-docs.enabled:false}") | ||||||
|     private boolean springDocsAPIEnabled; |     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 */ |     /** Gui-service related public URLS from spring web security perspective */ | ||||||
|     public static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher( |     public static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher( | ||||||
|  | @ -52,28 +56,42 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter { | ||||||
|             // project specific static resources |             // project specific static resources | ||||||
|             new AntPathRequestMatcher("/images/**"), |             new AntPathRequestMatcher("/images/**"), | ||||||
| 
 | 
 | ||||||
|             new AntPathRequestMatcher("/favicon.ico")); |             new AntPathRequestMatcher("/favicon.ico") | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void configure(final WebSecurity web) { |     public void configure(final WebSecurity web) { | ||||||
|         web |         web | ||||||
|                 .ignoring() |             .ignoring() | ||||||
|                 .requestMatchers(PUBLIC_URLS) |             .antMatchers(this.guiEntryPoint) | ||||||
|                 .antMatchers(this.guiEntryPoint) |         ; | ||||||
|                 .antMatchers(this.remoteProctoringEndpoint) |  | ||||||
|                 .antMatchers(this.remoteProctoringEndpoint + this.remoteProctoringViewServletEndpoint + "/*"); |  | ||||||
| 
 | 
 | ||||||
|         if (this.springDocsAPIEnabled) { |         if (this.springDocsAPIEnabled) { | ||||||
|             web.ignoring().antMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**"); |             web.ignoring().antMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void configure(final HttpSecurity http) throws Exception { |     public void configure(final HttpSecurity http) throws Exception { | ||||||
|         http |         http | ||||||
|                 .sessionManagement() |                 .sessionManagement() | ||||||
|                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS) |                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS) | ||||||
|                 .and() |                 .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("/**") |                 .antMatcher("/**") | ||||||
|                 .authorizeRequests() |                 .authorizeRequests() | ||||||
|                 .anyRequest() |                 .anyRequest() | ||||||
|  |  | ||||||
|  | @ -57,6 +57,8 @@ public class ExamIndicatorsList implements TemplateComposer { | ||||||
|             new LocTextKey("sebserver.exam.indicator.list.pleaseSelect"); |             new LocTextKey("sebserver.exam.indicator.list.pleaseSelect"); | ||||||
|     private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE = |     private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE = | ||||||
|             new LocTextKey("sebserver.exam.indicator.list.empty"); |             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 PageService pageService; | ||||||
|     private final ResourceService resourceService; |     private final ResourceService resourceService; | ||||||
|  | @ -144,6 +146,7 @@ public class ExamIndicatorsList implements TemplateComposer { | ||||||
|                         indicatorTable::getMultiSelection, |                         indicatorTable::getMultiSelection, | ||||||
|                         this::deleteSelectedIndicator, |                         this::deleteSelectedIndicator, | ||||||
|                         INDICATOR_EMPTY_SELECTION_TEXT_KEY) |                         INDICATOR_EMPTY_SELECTION_TEXT_KEY) | ||||||
|  |                 .withConfirm(() -> CONFIRM_MESSAGE_REMOVE_INDICATOR) | ||||||
|                 .publishIf(() -> indicatorEnabled && !isLight && editable && indicatorTable.hasAnyContent(), false) |                 .publishIf(() -> indicatorEnabled && !isLight && editable && indicatorTable.hasAnyContent(), false) | ||||||
| 
 | 
 | ||||||
|                 .newAction(ActionDefinition.EXAM_INDICATOR_NEW) |                 .newAction(ActionDefinition.EXAM_INDICATOR_NEW) | ||||||
|  |  | ||||||
|  | @ -32,11 +32,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueCon | ||||||
| @WebServiceProfile | @WebServiceProfile | ||||||
| public class RealNumberConverter implements AttributeValueConverter { | 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<String> SUPPORTED_ATTR_NAMES = Utils.immutableSetOf( |     public static final Set<String> SUPPORTED_ATTR_NAMES = Utils.immutableSetOf( | ||||||
|             "defaultPageZoomLevel", |             "defaultPageZoomLevel", | ||||||
|             "defaultTextZoomLevel"); |             "defaultTextZoomLevel", | ||||||
|  |             "screenProctoringImageDownscale"); | ||||||
| 
 | 
 | ||||||
|     private static final String XML_TEMPLATE = "<key>%s</key><real>%s</real>"; |     private static final String XML_TEMPLATE = "<key>%s</key><real>%s</real>"; | ||||||
|     private static final String JSON_TEMPLATE = "\"%s\":%s"; |     private static final String JSON_TEMPLATE = "\"%s\":%s"; | ||||||
|  | @ -89,6 +90,12 @@ public class RealNumberConverter implements AttributeValueConverter { | ||||||
|             realVal = 0; |             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( |         out.write(Utils.toByteArray(String.format( | ||||||
|                 template, |                 template, | ||||||
|                 AttributeValueConverter.extractName(attribute), |                 AttributeValueConverter.extractName(attribute), | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ import java.io.IOException; | ||||||
| import javax.servlet.http.HttpServletRequest; | import javax.servlet.http.HttpServletRequest; | ||||||
| import javax.servlet.http.HttpServletResponse; | import javax.servlet.http.HttpServletResponse; | ||||||
| 
 | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.api.API; | ||||||
| import org.apache.catalina.filters.RemoteIpFilter; | import org.apache.catalina.filters.RemoteIpFilter; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | 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; | import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebserviceResourceConfiguration; | ||||||
| 
 | 
 | ||||||
| /** This is the main web-security Spring configuration for SEB-Server webservice API | /** This is the main web-security Spring configuration for SEB-Server webservice API | ||||||
|  * |  * <p> | ||||||
|  * Currently two separated Rest API's are implemented, one for administration and maintenance |  * 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 |  * 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 |  * 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. |  * grant with refresh-token, same as in the prototype and the ExamAPI uses the client_credential grant. | ||||||
|  * |  * <p> | ||||||
|  * There is a Spring Authorization-Server defining this two clients (AdminAPIClient and ExamAPIClient) as well as |  * 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 |  * two Spring Resource-Server for the separation of the different API's | ||||||
|  * |  * <p> | ||||||
|  * The endpoint of the AdministrationAPI can be configured within the key; sebserver.webservice.api.admin.endpoint |  * The endpoint of the AdministrationAPI can be configured within the key; sebserver.webservice.api.admin.endpoint | ||||||
|  * and is by default set to "/admin-api/**" |  * and is by default set to "/admin-api/**" | ||||||
|  * |  * <p> | ||||||
|  * The endpoint of the ExamAPI can be configured within the key; sebserver.webservice.api.exam.endpoint |  * The endpoint of the ExamAPI can be configured within the key; sebserver.webservice.api.exam.endpoint | ||||||
|  * and is by default set to "/exam-api/**" */ |  * and is by default set to "/exam-api/**" */ | ||||||
| @WebServiceProfile | @WebServiceProfile | ||||||
|  |  | ||||||
|  | @ -11,6 +11,8 @@ package ch.ethz.seb.sebserver.webservice.weblayer.oauth; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.List; | 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.authentication.AuthenticationManager; | ||||||
| import org.springframework.security.config.annotation.web.builders.HttpSecurity; | import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||||||
| import org.springframework.security.config.http.SessionCreationPolicy; | 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"; |     public static final String ADMIN_API_RESOURCE_ID = "seb-server-administration-api"; | ||||||
|     /** The resource identifier of the Exam API resources */ |     /** The resource identifier of the Exam API resources */ | ||||||
|     public static final String EXAM_API_RESOURCE_ID = "seb-server-exam-api"; |     public static final String EXAM_API_RESOURCE_ID = "seb-server-exam-api"; | ||||||
|  |     @Value("${sebserver.webservice.api.exam.endpoint.discovery}") | ||||||
|  |     private String examAPIDiscoveryEndpoint; | ||||||
| 
 | 
 | ||||||
|     public WebserviceResourceConfiguration( |     public WebserviceResourceConfiguration( | ||||||
|             final TokenStore tokenStore, |             final TokenStore tokenStore, | ||||||
|  | @ -74,6 +78,16 @@ public abstract class WebserviceResourceConfiguration extends ResourceServerConf | ||||||
|                 .sessionManagement() |                 .sessionManagement() | ||||||
|                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS) |                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS) | ||||||
|                 .and() |                 .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 + "/**") |                 .antMatcher(configurerAdapter.apiEndpoint + "/**") | ||||||
|                 .authorizeRequests() |                 .authorizeRequests() | ||||||
|                 .anyRequest() |                 .anyRequest() | ||||||
|  |  | ||||||
|  | @ -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.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.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.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.actions=  | ||||||
| sebserver.exam.clientgroup.list.title=Client Groups | sebserver.exam.clientgroup.list.title=Client Groups | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti