SEBSERV-44 added Exam API endpoints and discovery
This commit is contained in:
parent
985e1ae386
commit
e90bf79034
34 changed files with 499 additions and 84 deletions
|
@ -67,6 +67,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
|
|||
// private String adminEndpoint;
|
||||
@Value("${sebserver.webservice.api.redirect.unauthorized}")
|
||||
private String unauthorizedRedirect;
|
||||
@Value("${sebserver.webservice.api.exam.endpoint.discovery}")
|
||||
private String examAPIDiscoveryEndpoint;
|
||||
|
||||
/** Spring bean name of user password encoder */
|
||||
public static final String USER_PASSWORD_ENCODER_BEAN_NAME = "userPasswordEncoder";
|
||||
|
@ -100,9 +102,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
|
|||
web
|
||||
.ignoring()
|
||||
.antMatchers("/error")
|
||||
// TODO this may not be necessary, test with separated GUI and webservice server
|
||||
//.antMatchers(this.adminEndpoint + API.INFO_ENDPOINT + "/**")
|
||||
;
|
||||
.antMatchers(this.examAPIDiscoveryEndpoint);
|
||||
}
|
||||
|
||||
@RequestMapping("/error")
|
||||
|
|
|
@ -116,6 +116,6 @@ public final class API {
|
|||
|
||||
public static final String EXAM_API_PING_ENDPOINT = "/sebping";
|
||||
|
||||
public static final String EXAM_API_EVENT_ENDPOINT = "/sebevent";
|
||||
public static final String EXAM_API_EVENT_ENDPOINT = "/seblog";
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gbl.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public final class ExamAPIDiscovery {
|
||||
|
||||
@JsonProperty("title")
|
||||
public final String title;
|
||||
|
||||
@JsonProperty("description")
|
||||
public final String description;
|
||||
|
||||
@JsonProperty("server-location")
|
||||
public final String serverLocation;
|
||||
|
||||
@JsonProperty("api-versions")
|
||||
public final Collection<ExamAPIVersion> versions;
|
||||
|
||||
public ExamAPIDiscovery(
|
||||
final String title,
|
||||
final String description,
|
||||
final String serverLocation,
|
||||
final Collection<ExamAPIVersion> versions) {
|
||||
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.serverLocation = serverLocation;
|
||||
this.versions = versions;
|
||||
}
|
||||
|
||||
public ExamAPIDiscovery(
|
||||
final String title,
|
||||
final String description,
|
||||
final String serverLocation,
|
||||
final ExamAPIVersion... versions) {
|
||||
|
||||
this(
|
||||
title,
|
||||
description,
|
||||
serverLocation,
|
||||
(versions != null) ? Arrays.asList(versions) : Collections.emptyList());
|
||||
}
|
||||
|
||||
public static final class ExamAPIVersion {
|
||||
|
||||
@JsonProperty("name")
|
||||
public final String name;
|
||||
|
||||
@JsonProperty("endpoints")
|
||||
public final Collection<Endpoint> endpoints;
|
||||
|
||||
public ExamAPIVersion(
|
||||
final String name,
|
||||
final Collection<Endpoint> endpoints) {
|
||||
|
||||
this.name = name;
|
||||
this.endpoints = endpoints;
|
||||
}
|
||||
|
||||
public ExamAPIVersion(
|
||||
final String name,
|
||||
final Endpoint... endpoints) {
|
||||
|
||||
this.name = name;
|
||||
this.endpoints = (endpoints != null) ? Arrays.asList(endpoints) : Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Endpoint {
|
||||
|
||||
@JsonProperty("name")
|
||||
public final String name;
|
||||
|
||||
@JsonProperty("descripiton")
|
||||
public final String descripiton;
|
||||
|
||||
@JsonProperty("location")
|
||||
public final String location;
|
||||
|
||||
@JsonProperty("authorization")
|
||||
public final String authorization;
|
||||
|
||||
public Endpoint(final String name, final String descripiton, final String location,
|
||||
final String authorization) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.descripiton = descripiton;
|
||||
this.location = location;
|
||||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gbl.model.seb;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public final class RunningExam {
|
||||
|
||||
@JsonProperty()
|
||||
public final String examId;
|
||||
@JsonProperty()
|
||||
public final String name;
|
||||
@JsonProperty()
|
||||
public final String url;
|
||||
|
||||
public RunningExam(final String examId, final String name, final String url) {
|
||||
super();
|
||||
this.examId = examId;
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gbl.model.seb;
|
||||
|
||||
public class RunningExams {
|
||||
// TODO
|
||||
}
|
|
@ -293,4 +293,14 @@ public final class Utils {
|
|||
return toCharArray(CharBuffer.wrap(chars));
|
||||
}
|
||||
|
||||
public static String toString(final CharSequence charSequence) {
|
||||
if (charSequence == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(charSequence);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,11 +50,12 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
|||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class SebExamConfigForm implements TemplateComposer {
|
||||
public class SebExamConfigSettingsForm implements TemplateComposer {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SebExamConfigForm.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(SebExamConfigSettingsForm.class);
|
||||
|
||||
private static final String VIEW_TEXT_KEY_PREFIX = "sebserver.examconfig.props.form.views.";
|
||||
private static final String VIEW_TEXT_KEY_PREFIX =
|
||||
"sebserver.examconfig.props.form.views.";
|
||||
private static final String KEY_SAVE_TO_HISTORY_SUCCESS =
|
||||
"sebserver.examconfig.action.saveToHistory.success";
|
||||
private static final String KEY_UNDO_SUCCESS =
|
||||
|
@ -68,7 +69,7 @@ public class SebExamConfigForm implements TemplateComposer {
|
|||
private final CurrentUser currentUser;
|
||||
private final ExamConfigurationService examConfigurationService;
|
||||
|
||||
protected SebExamConfigForm(
|
||||
protected SebExamConfigSettingsForm(
|
||||
final PageService pageService,
|
||||
final RestService restService,
|
||||
final CurrentUser currentUser,
|
|
@ -19,7 +19,7 @@ import ch.ethz.seb.sebserver.gui.content.LmsSetupList;
|
|||
import ch.ethz.seb.sebserver.gui.content.QuizDiscoveryList;
|
||||
import ch.ethz.seb.sebserver.gui.content.SebClientConfigForm;
|
||||
import ch.ethz.seb.sebserver.gui.content.SebClientConfigList;
|
||||
import ch.ethz.seb.sebserver.gui.content.SebExamConfigForm;
|
||||
import ch.ethz.seb.sebserver.gui.content.SebExamConfigSettingsForm;
|
||||
import ch.ethz.seb.sebserver.gui.content.SebExamConfigList;
|
||||
import ch.ethz.seb.sebserver.gui.content.SebExamConfigPropForm;
|
||||
import ch.ethz.seb.sebserver.gui.content.UserAccountChangePasswordForm;
|
||||
|
@ -60,7 +60,7 @@ public enum PageStateDefinition implements PageState {
|
|||
SEB_EXAM_CONFIG_LIST(Type.LIST_VIEW, SebExamConfigList.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||
SEB_EXAM_CONFIG_VIEW(Type.FORM_VIEW, SebExamConfigPropForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||
SEB_EXAM_CONFIG_PROP_EDIT(Type.FORM_EDIT, SebExamConfigPropForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||
SEB_EXAM_CONFIG_EDIT(Type.FORM_VIEW, SebExamConfigForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||
SEB_EXAM_CONFIG_EDIT(Type.FORM_VIEW, SebExamConfigSettingsForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||
|
||||
;
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
|
|||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.LmsSetupRecord;
|
||||
|
@ -325,10 +326,10 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
|
|||
record.getInstitutionId(),
|
||||
record.getName(),
|
||||
LmsType.valueOf(record.getLmsType()),
|
||||
(plainClientId != null) ? plainClientId.toString() : null,
|
||||
Utils.toString(plainClientId),
|
||||
null,
|
||||
record.getLmsUrl(),
|
||||
(plainAccessToken != null) ? plainAccessToken.toString() : null,
|
||||
Utils.toString(plainAccessToken),
|
||||
BooleanUtils.toBooleanObject(record.getActive())));
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ public interface SebClientConfigService {
|
|||
" </dict>\r\n" +
|
||||
"</plist>";
|
||||
|
||||
String getServerURL();
|
||||
|
||||
boolean hasSebClientConfigurationForInstitution(Long institutionId);
|
||||
|
||||
Result<SebClientConfig> autoCreateSebClientConfigurationForInstitution(Long institutionId);
|
||||
|
@ -48,4 +50,6 @@ public interface SebClientConfigService {
|
|||
OutputStream out,
|
||||
final String modelId);
|
||||
|
||||
Result<String> getEncodedClientSecret(String clientId);
|
||||
|
||||
}
|
||||
|
|
|
@ -16,14 +16,24 @@ import java.io.PipedOutputStream;
|
|||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import ch.ethz.seb.sebserver.WebSecurityConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
|
@ -49,6 +59,9 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
private final SebClientConfigDAO sebClientConfigDAO;
|
||||
private final ClientCredentialService clientCredentialService;
|
||||
private final SebConfigEncryptionService sebConfigEncryptionService;
|
||||
@Autowired
|
||||
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME)
|
||||
private PasswordEncoder clientPasswordEncoder;
|
||||
private final ZipService zipService;
|
||||
private final String httpScheme;
|
||||
private final String serverAddress;
|
||||
|
@ -99,6 +112,49 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
.flatMap(this.sebClientConfigDAO::createNew);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> getEncodedClientSecret(final String clientId) {
|
||||
return Result.tryCatch(() -> {
|
||||
final Collection<SebClientConfig> clientConfigs = this.sebClientConfigDAO.all(extractInstitution(), true)
|
||||
.getOrThrow();
|
||||
|
||||
final ClientCredentials clientCredentials = findClientCredentialsFor(clientId, clientConfigs);
|
||||
return this.clientPasswordEncoder.encode(
|
||||
this.clientCredentialService.getPlainClientSecret(clientCredentials));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public ClientCredentials findClientCredentialsFor(final String clientId,
|
||||
final Collection<SebClientConfig> clientConfigs) {
|
||||
for (final SebClientConfig config : clientConfigs) {
|
||||
try {
|
||||
final ClientCredentials clientCredentials =
|
||||
this.sebClientConfigDAO.getSebClientCredentials(config.getModelId())
|
||||
.getOrThrow();
|
||||
if (clientId.equals(this.clientCredentialService.getPlainClientId(clientCredentials))) {
|
||||
return clientCredentials;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while trying to fetch client credentials: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Long extractInstitution() {
|
||||
try {
|
||||
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
final HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
|
||||
return Long.parseLong(request.getParameter(API.PARAM_INSTITUTION_ID));
|
||||
} catch (final Exception e) {
|
||||
log.error(
|
||||
"Failed to extract institution from current request. Search client Id over all active client configurations");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportSebClientConfiguration(
|
||||
final OutputStream output,
|
||||
|
@ -107,12 +163,6 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
final SebClientConfig config = this.sebClientConfigDAO
|
||||
.byModelId(modelId).getOrThrow();
|
||||
|
||||
final String serverURL = UriComponentsBuilder.newInstance()
|
||||
.scheme(this.httpScheme)
|
||||
.host(this.serverAddress)
|
||||
.port(this.serverPort)
|
||||
.toUriString();
|
||||
|
||||
final ClientCredentials sebClientCredentials = this.sebClientConfigDAO
|
||||
.getSebClientCredentials(config.getModelId())
|
||||
.getOrThrow();
|
||||
|
@ -128,7 +178,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
|
||||
final String plainTextConfig = String.format(
|
||||
SEB_CLIENT_CONFIG_EXAMPLE_XML,
|
||||
serverURL,
|
||||
getServerURL(),
|
||||
String.valueOf(config.institutionId),
|
||||
plainClientId,
|
||||
plainClientSecret,
|
||||
|
@ -171,6 +221,15 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerURL() {
|
||||
return UriComponentsBuilder.newInstance()
|
||||
.scheme(this.httpScheme)
|
||||
.host(this.serverAddress)
|
||||
.port(this.serverPort)
|
||||
.toUriString();
|
||||
}
|
||||
|
||||
private void passwordEncryption(
|
||||
final OutputStream output,
|
||||
final CharSequence encryptionPassword,
|
||||
|
|
|
@ -36,7 +36,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_ATTRIBUTE_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_ATTRIBUTE_ENDPOINT)
|
||||
public class ConfigurationAttributeController extends EntityController<ConfigurationAttribute, ConfigurationAttribute> {
|
||||
|
||||
protected ConfigurationAttributeController(
|
||||
|
|
|
@ -36,7 +36,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_ENDPOINT)
|
||||
public class ConfigurationController extends EntityController<Configuration, Configuration> {
|
||||
|
||||
private final ConfigurationDAO configurationDAO;
|
||||
|
|
|
@ -33,7 +33,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_NODE_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_NODE_ENDPOINT)
|
||||
public class ConfigurationNodeController extends EntityController<ConfigurationNode, ConfigurationNode> {
|
||||
|
||||
private final ConfigurationDAO configurationDAO;
|
||||
|
|
|
@ -41,7 +41,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_VALUE_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_VALUE_ENDPOINT)
|
||||
public class ConfigurationValueController extends EntityController<ConfigurationValue, ConfigurationValue> {
|
||||
|
||||
private final ConfigurationDAO configurationDAO;
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.ExamAPIDiscovery;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebClientConfigService;
|
||||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("${sebserver.webservice.api.exam.endpoint.discovery}")
|
||||
public class ExamAPIDiscoveryController {
|
||||
|
||||
private final SebClientConfigService sebClientConfigService;
|
||||
private final String examAPI_V1_Endpoint;
|
||||
|
||||
protected ExamAPIDiscoveryController(
|
||||
final SebClientConfigService sebClientConfigService,
|
||||
@Value("${sebserver.webservice.api.exam.endpoint.v1}") final String examAPI_V1_Endpoint) {
|
||||
|
||||
this.sebClientConfigService = sebClientConfigService;
|
||||
this.examAPI_V1_Endpoint = examAPI_V1_Endpoint;
|
||||
}
|
||||
|
||||
private ExamAPIDiscovery DISCOVERY_INFO;
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
this.DISCOVERY_INFO = new ExamAPIDiscovery(
|
||||
"Safe Exam Browser Server / Exam API Description",
|
||||
"This is a description of Safe Exam Browser Server's Exam API",
|
||||
this.sebClientConfigService.getServerURL(),
|
||||
Arrays.asList(new ExamAPIDiscovery.ExamAPIVersion(
|
||||
"v1",
|
||||
Arrays.asList(
|
||||
new ExamAPIDiscovery.Endpoint(
|
||||
"access-token-endpoint",
|
||||
"request OAuth2 access token with client credentials grant",
|
||||
API.OAUTH_TOKEN_ENDPOINT,
|
||||
"Basic"),
|
||||
new ExamAPIDiscovery.Endpoint(
|
||||
"seb-handshake-endpoint",
|
||||
"endpoint to establish SEB - SEB Server connection",
|
||||
this.examAPI_V1_Endpoint + API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||
"Bearer"),
|
||||
new ExamAPIDiscovery.Endpoint(
|
||||
"seb-configuration-endpoint",
|
||||
"endpoint to get SEB exam configuration in exchange of connection-token and exam identifier",
|
||||
this.examAPI_V1_Endpoint + API.EXAM_API_CONFIGURATION_REQUEST_ENDPOINT,
|
||||
"Bearer"),
|
||||
new ExamAPIDiscovery.Endpoint(
|
||||
"seb-ping-endpoint",
|
||||
"endpoint to send pings to while running exam",
|
||||
this.examAPI_V1_Endpoint + API.EXAM_API_PING_ENDPOINT,
|
||||
"Bearer"),
|
||||
new ExamAPIDiscovery.Endpoint(
|
||||
"seb-ping-endpoint",
|
||||
"endpoint to send log events to while running exam",
|
||||
this.examAPI_V1_Endpoint + API.EXAM_API_EVENT_ENDPOINT,
|
||||
"Bearer")))));
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
method = RequestMethod.GET,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public ExamAPIDiscovery getDiscovery() {
|
||||
return this.DISCOVERY_INFO;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,37 +8,44 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.model.seb.PingResponse;
|
||||
import ch.ethz.seb.sebserver.gbl.model.seb.RunningExams;
|
||||
import ch.ethz.seb.sebserver.gbl.model.seb.RunningExam;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.exam.endpoint}")
|
||||
public class ExamAPIController {
|
||||
@RequestMapping("${sebserver.webservice.api.exam.endpoint.v1}")
|
||||
public class ExamAPI_V1_Controller {
|
||||
|
||||
@RequestMapping(
|
||||
path = API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||
method = RequestMethod.GET,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public RunningExams handshake(
|
||||
public Collection<RunningExam> handshake(
|
||||
@RequestParam(name = API.PARAM_INSTITUTION_ID, required = true) final Long institutionId,
|
||||
final HttpServletRequest request,
|
||||
final HttpServletResponse response) {
|
||||
|
||||
// TODO
|
||||
return null;
|
||||
return Arrays.asList(new RunningExam("1", "testExam", "TODO"));
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
|
@ -46,14 +53,15 @@ public class ExamAPIController {
|
|||
method = RequestMethod.GET,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.TEXT_XML_VALUE)
|
||||
public RunningExams getConfig(
|
||||
public ResponseEntity<StreamingResponseBody> getConfig(
|
||||
@RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = true) final String examId,
|
||||
final HttpServletRequest request,
|
||||
final HttpServletResponse response) {
|
||||
@RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = true) final String examId) {
|
||||
|
||||
// TODO
|
||||
return null;
|
||||
// 1. check connection validity (connection token)
|
||||
// 2. get and stream SEB Exam configuration for specified exam (Id)
|
||||
final StreamingResponseBody stream = out -> out.write(Utils.toByteArray("TODO SEB Config"));
|
||||
return new ResponseEntity<>(stream, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
|
@ -54,7 +54,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.EXAM_ADMINISTRATION_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_ADMINISTRATION_ENDPOINT)
|
||||
public class ExamAdministrationController extends ActivatableEntityController<Exam, Exam> {
|
||||
|
||||
private final ExamDAO examDAO;
|
||||
|
|
|
@ -35,7 +35,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.EXAM_CONFIGURATION_MAP_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_CONFIGURATION_MAP_ENDPOINT)
|
||||
public class ExamConfigurationMappingController extends EntityController<ExamConfigurationMap, ExamConfigurationMap> {
|
||||
|
||||
private final ExamDAO examDao;
|
||||
|
|
|
@ -31,7 +31,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.EXAM_INDICATOR_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_INDICATOR_ENDPOINT)
|
||||
public class IndicatorController extends EntityController<Indicator, Indicator> {
|
||||
|
||||
private final ExamDAO examDao;
|
||||
|
|
|
@ -24,7 +24,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.INFO_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.INFO_ENDPOINT)
|
||||
public class InfoController {
|
||||
|
||||
private final InstitutionDAO institutionDAO;
|
||||
|
|
|
@ -28,7 +28,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.INSTITUTION_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.INSTITUTION_ENDPOINT)
|
||||
public class InstitutionController extends ActivatableEntityController<Institution, Institution> {
|
||||
|
||||
private final InstitutionDAO institutionDAO;
|
||||
|
|
|
@ -22,9 +22,9 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
|
||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
||||
|
@ -43,7 +43,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.LMS_SETUP_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.LMS_SETUP_ENDPOINT)
|
||||
public class LmsSetupController extends ActivatableEntityController<LmsSetup, LmsSetup> {
|
||||
|
||||
private final LmsAPIService lmsAPIService;
|
||||
|
|
|
@ -30,7 +30,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.ORIENTATION_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.ORIENTATION_ENDPOINT)
|
||||
public class OrientationController extends EntityController<Orientation, Orientation> {
|
||||
|
||||
private final ConfigurationAttributeDAO configurationAttributeDAO;
|
||||
|
|
|
@ -33,7 +33,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.QUIZ_DISCOVERY_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.QUIZ_DISCOVERY_ENDPOINT)
|
||||
public class QuizController {
|
||||
|
||||
private final int defaultPageSize;
|
||||
|
|
|
@ -44,7 +44,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
@WebServiceProfile
|
||||
@RestController
|
||||
@EnableAsync
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONFIG_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONFIG_ENDPOINT)
|
||||
public class SebClientConfigController extends ActivatableEntityController<SebClientConfig, SebClientConfig> {
|
||||
|
||||
private final SebClientConfigService sebClientConfigService;
|
||||
|
|
|
@ -33,7 +33,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.USER_ACTIVITY_LOG_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.USER_ACTIVITY_LOG_ENDPOINT)
|
||||
public class UserActivityLogController {
|
||||
|
||||
private final UserActivityLogDAO userActivityLogDAO;
|
||||
|
|
|
@ -28,7 +28,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.VIEW_ENDPOINT)
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.VIEW_ENDPOINT)
|
||||
public class ViewController extends EntityController<View, View> {
|
||||
|
||||
protected ViewController(
|
||||
|
|
|
@ -10,8 +10,6 @@ package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
|
|||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
@ -23,6 +21,9 @@ import org.springframework.security.oauth2.provider.client.BaseClientDetails;
|
|||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.WebSecurityConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebClientConfigService;
|
||||
|
||||
/** A ClientDetailsService to manage different API clients of SEB Server webservice API.
|
||||
*
|
||||
|
@ -34,16 +35,19 @@ import ch.ethz.seb.sebserver.WebSecurityConfig;
|
|||
@Component
|
||||
public class WebClientDetailsService implements ClientDetailsService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WebClientDetailsService.class);
|
||||
|
||||
private final SebClientConfigService sebClientConfigService;
|
||||
private final AdminAPIClientDetails adminClientDetails;
|
||||
@Autowired
|
||||
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME)
|
||||
private PasswordEncoder clientPasswordEncoder;
|
||||
|
||||
// TODO inject a collection of BaseClientDetails here to allow multiple admin client configurations
|
||||
public WebClientDetailsService(final AdminAPIClientDetails adminClientDetails) {
|
||||
public WebClientDetailsService(
|
||||
final AdminAPIClientDetails adminClientDetails,
|
||||
final SebClientConfigService sebClientConfigService) {
|
||||
|
||||
this.adminClientDetails = adminClientDetails;
|
||||
this.sebClientConfigService = sebClientConfigService;
|
||||
}
|
||||
|
||||
/** Load a client by the client id. This method must not return null.
|
||||
|
@ -67,25 +71,24 @@ public class WebClientDetailsService implements ClientDetailsService {
|
|||
return this.adminClientDetails;
|
||||
}
|
||||
|
||||
return getForExamClientAPI(clientId);
|
||||
return getForExamClientAPI(clientId)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
private ClientDetails getForExamClientAPI(final String clientId) {
|
||||
// TODO create ClientDetails from matching Institution
|
||||
if ("test".equals(clientId)) {
|
||||
final BaseClientDetails baseClientDetails = new BaseClientDetails(
|
||||
clientId,
|
||||
WebserviceResourceConfiguration.EXAM_API_RESOURCE_ID,
|
||||
null,
|
||||
"client_credentials",
|
||||
"");
|
||||
baseClientDetails.setScope(Collections.emptySet());
|
||||
baseClientDetails.setClientSecret(this.clientPasswordEncoder.encode("test"));
|
||||
return baseClientDetails;
|
||||
}
|
||||
protected Result<ClientDetails> getForExamClientAPI(final String clientId) {
|
||||
return this.sebClientConfigService.getEncodedClientSecret(clientId)
|
||||
.map(pwd -> {
|
||||
final BaseClientDetails baseClientDetails = new BaseClientDetails(
|
||||
Utils.toString(clientId),
|
||||
WebserviceResourceConfiguration.EXAM_API_RESOURCE_ID,
|
||||
null,
|
||||
"client_credentials",
|
||||
"");
|
||||
|
||||
log.warn("ClientDetails for clientId: {} not found", clientId);
|
||||
throw new ClientRegistrationException("clientId not found");
|
||||
baseClientDetails.setScope(Collections.emptySet());
|
||||
baseClientDetails.setClientSecret(pwd);
|
||||
return baseClientDetails;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,9 @@ sebserver.webservice.http.scheme=http
|
|||
sebserver.webservice.api.admin.endpoint=/admin-api/v1
|
||||
sebserver.webservice.api.admin.accessTokenValiditySeconds=1800
|
||||
sebserver.webservice.api.admin.refreshTokenValiditySeconds=-1
|
||||
sebserver.webservice.api.exam.endpoint=/exam-api/v1
|
||||
sebserver.webservice.api.exam.endpoint=/exam-api
|
||||
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
||||
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
||||
sebserver.webservice.api.exam.accessTokenValiditySeconds=1800
|
||||
sebserver.webservice.api.exam.refreshTokenValiditySeconds=-1
|
||||
|
||||
|
|
|
@ -13,13 +13,21 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.json.JacksonJsonParser;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
@ -31,17 +39,22 @@ import org.springframework.util.MultiValueMap;
|
|||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import ch.ethz.seb.sebserver.SEBServer;
|
||||
import ch.ethz.seb.sebserver.WebSecurityConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebClientConfigService;
|
||||
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.AdminAPIClientDetails;
|
||||
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebClientDetailsService;
|
||||
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebserviceResourceConfiguration;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(
|
||||
classes = SEBServer.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||
classes = { SEBServer.class },
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles("test")
|
||||
@AutoConfigureMockMvc
|
||||
public abstract class ExamAPIIntegrationTester {
|
||||
|
||||
@Value("${sebserver.webservice.api.exam.endpoint}")
|
||||
@Value("${sebserver.webservice.api.exam.endpoint.v1}")
|
||||
protected String endpoint;
|
||||
|
||||
@Autowired
|
||||
|
@ -53,10 +66,28 @@ public abstract class ExamAPIIntegrationTester {
|
|||
|
||||
protected MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
public WebClientDetailsService webClientDetailsService;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
|
||||
.addFilter(this.springSecurityFilterChain).build();
|
||||
Mockito.when(this.webClientDetailsService.loadClientByClientId(Mockito.anyString())).thenReturn(
|
||||
getForExamClientAPI());
|
||||
}
|
||||
|
||||
protected ClientDetails getForExamClientAPI() {
|
||||
final BaseClientDetails baseClientDetails = new BaseClientDetails(
|
||||
"test",
|
||||
WebserviceResourceConfiguration.EXAM_API_RESOURCE_ID,
|
||||
null,
|
||||
"client_credentials",
|
||||
"");
|
||||
baseClientDetails.setScope(Collections.emptySet());
|
||||
baseClientDetails
|
||||
.setClientSecret(ExamAPIIntegrationTester.this.clientPasswordEncoder.encode("test"));
|
||||
return baseClientDetails;
|
||||
}
|
||||
|
||||
protected String obtainAccessToken(
|
||||
|
@ -82,4 +113,12 @@ public abstract class ExamAPIIntegrationTester {
|
|||
return jsonParser.parseMap(resultString).get("access_token").toString();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
AdminAPIClientDetails adminClientDetails;
|
||||
@Autowired
|
||||
SebClientConfigService sebClientConfigService;
|
||||
@Autowired
|
||||
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME)
|
||||
private PasswordEncoder clientPasswordEncoder;
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("${sebserver.webservice.api.exam.endpoint}")
|
||||
@RequestMapping("${sebserver.webservice.api.exam.endpoint.v1}")
|
||||
@WebServiceProfile
|
||||
public class ExamAPITestController {
|
||||
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.integration.api.exam;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
|
||||
public class ExamDiscoveryEndpointTest extends ExamAPIIntegrationTester {
|
||||
|
||||
@Value("${sebserver.webservice.api.exam.endpoint.discovery}")
|
||||
private String discoveryEndpoint;
|
||||
@Autowired
|
||||
private JSONMapper jsonMapper;
|
||||
|
||||
@Test
|
||||
public void testExamDiscoveryEndpoint() throws Exception {
|
||||
// no authorization needed here
|
||||
|
||||
final String contentAsString = this.mockMvc.perform(get(this.discoveryEndpoint))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn().getResponse().getContentAsString();
|
||||
|
||||
final Object json = this.jsonMapper.readValue(contentAsString, Object.class);
|
||||
assertEquals(
|
||||
"{\r\n" +
|
||||
" \"title\" : \"Safe Exam Browser Server / Exam API Description\",\r\n" +
|
||||
" \"description\" : \"This is a description of Safe Exam Browser Server's Exam API\",\r\n" +
|
||||
" \"server-location\" : \"http://localhost:0\",\r\n" +
|
||||
" \"api-versions\" : [ {\r\n" +
|
||||
" \"name\" : \"v1\",\r\n" +
|
||||
" \"endpoints\" : [ {\r\n" +
|
||||
" \"name\" : \"access-token-endpoint\",\r\n" +
|
||||
" \"descripiton\" : \"request OAuth2 access token with client credentials grant\",\r\n" +
|
||||
" \"location\" : \"/oauth/token\",\r\n" +
|
||||
" \"authorization\" : \"Basic\"\r\n" +
|
||||
" }, {\r\n" +
|
||||
" \"name\" : \"seb-handshake-endpoint\",\r\n" +
|
||||
" \"descripiton\" : \"endpoint to establish SEB - SEB Server connection\",\r\n" +
|
||||
" \"location\" : \"/exam-api/v1/handshake\",\r\n" +
|
||||
" \"authorization\" : \"Bearer\"\r\n" +
|
||||
" }, {\r\n" +
|
||||
" \"name\" : \"seb-configuration-endpoint\",\r\n" +
|
||||
" \"descripiton\" : \"endpoint to get SEB exam configuration in exchange of connection-token and exam identifier\",\r\n"
|
||||
+
|
||||
" \"location\" : \"/exam-api/v1/examconfig\",\r\n" +
|
||||
" \"authorization\" : \"Bearer\"\r\n" +
|
||||
" }, {\r\n" +
|
||||
" \"name\" : \"seb-ping-endpoint\",\r\n" +
|
||||
" \"descripiton\" : \"endpoint to send pings to while running exam\",\r\n" +
|
||||
" \"location\" : \"/exam-api/v1/sebping\",\r\n" +
|
||||
" \"authorization\" : \"Bearer\"\r\n" +
|
||||
" }, {\r\n" +
|
||||
" \"name\" : \"seb-ping-endpoint\",\r\n" +
|
||||
" \"descripiton\" : \"endpoint to send log events to while running exam\",\r\n" +
|
||||
" \"location\" : \"/exam-api/v1/seblog\",\r\n" +
|
||||
" \"authorization\" : \"Bearer\"\r\n" +
|
||||
" } ]\r\n" +
|
||||
" } ]\r\n" +
|
||||
"}",
|
||||
this.jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(json));
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,8 @@ server.address=localhost
|
|||
server.port=8080
|
||||
server.servlet.context-path=/
|
||||
|
||||
spring.main.allow-bean-definition-overriding=true
|
||||
|
||||
spring.h2.console.enabled=true
|
||||
spring.datasource.platform=h2
|
||||
spring.datasource.url=jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
|
@ -15,6 +17,8 @@ sebserver.webservice.api.admin.endpoint=/admin-api
|
|||
sebserver.webservice.api.admin.accessTokenValiditySeconds=1800
|
||||
sebserver.webservice.api.admin.refreshTokenValiditySeconds=-1
|
||||
sebserver.webservice.api.exam.endpoint=/exam-api
|
||||
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
||||
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
||||
sebserver.webservice.api.exam.accessTokenValiditySeconds=1800
|
||||
sebserver.webservice.api.exam.refreshTokenValiditySeconds=-1
|
||||
sebserver.webservice.internalSecret=TO_SET
|
||||
|
|
Loading…
Reference in a new issue