SEBSERV-131 implementation
This commit is contained in:
parent
ab56ce3cc4
commit
97f174d740
7 changed files with 182 additions and 3 deletions
|
@ -157,6 +157,7 @@ public final class API {
|
||||||
public static final String EXAM_INDICATOR_ENDPOINT = "/indicator";
|
public static final String EXAM_INDICATOR_ENDPOINT = "/indicator";
|
||||||
|
|
||||||
public static final String SEB_CLIENT_CONFIG_ENDPOINT = "/client_configuration";
|
public static final String SEB_CLIENT_CONFIG_ENDPOINT = "/client_configuration";
|
||||||
|
public static final String SEB_CLIENT_CONFIG_CREDENTIALS_PATH_SEGMENT = "/credentials";
|
||||||
public static final String SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT = "/download";
|
public static final String SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT = "/download";
|
||||||
|
|
||||||
public static final String CONFIGURATION_NODE_ENDPOINT = "/configuration-node";
|
public static final String CONFIGURATION_NODE_ENDPOINT = "/configuration-node";
|
||||||
|
|
|
@ -8,20 +8,34 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gbl.client;
|
package ch.ethz.seb.sebserver.gbl.client;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
/** Defines a simple data bean holding (encrypted) client credentials */
|
/** Defines a simple data bean holding (encrypted) client credentials */
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public final class ClientCredentials {
|
public final class ClientCredentials {
|
||||||
|
|
||||||
|
public static final String ATTR_CLIENT_ID = "clientId";
|
||||||
|
public static final String ATTR_SECRET = "secret";
|
||||||
|
public static final String ATTR_ACCESS_TOKEN = "accessToken";
|
||||||
|
|
||||||
/** The client id or client name parameter */
|
/** The client id or client name parameter */
|
||||||
|
@JsonProperty(ATTR_CLIENT_ID)
|
||||||
public final CharSequence clientId;
|
public final CharSequence clientId;
|
||||||
/** The client secret parameter */
|
/** The client secret parameter */
|
||||||
|
@JsonProperty(ATTR_SECRET)
|
||||||
public final CharSequence secret;
|
public final CharSequence secret;
|
||||||
/** An client access token if supported */
|
/** An client access token if supported */
|
||||||
|
@JsonProperty(ATTR_ACCESS_TOKEN)
|
||||||
public final CharSequence accessToken;
|
public final CharSequence accessToken;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
public ClientCredentials(
|
public ClientCredentials(
|
||||||
final CharSequence clientId,
|
@JsonProperty(ATTR_CLIENT_ID) final CharSequence clientId,
|
||||||
final CharSequence secret,
|
@JsonProperty(ATTR_SECRET) final CharSequence secret,
|
||||||
final CharSequence accessToken) {
|
@JsonProperty(ATTR_ACCESS_TOKEN) final CharSequence accessToken) {
|
||||||
|
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
this.secret = secret;
|
this.secret = secret;
|
||||||
|
@ -35,26 +49,44 @@ public final class ClientCredentials {
|
||||||
this(clientId, secret, null);
|
this(clientId, secret, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CharSequence getClientId() {
|
||||||
|
return this.clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence getSecret() {
|
||||||
|
return this.secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence getAccessToken() {
|
||||||
|
return this.accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
public boolean hasClientId() {
|
public boolean hasClientId() {
|
||||||
return this.clientId != null && this.clientId.length() > 0;
|
return this.clientId != null && this.clientId.length() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
public boolean hasSecret() {
|
public boolean hasSecret() {
|
||||||
return this.secret != null && this.secret.length() > 0;
|
return this.secret != null && this.secret.length() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
public boolean hasAccessToken() {
|
public boolean hasAccessToken() {
|
||||||
return this.accessToken != null && this.accessToken.length() > 0;
|
return this.accessToken != null && this.accessToken.length() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
public String clientIdAsString() {
|
public String clientIdAsString() {
|
||||||
return hasClientId() ? this.clientId.toString() : null;
|
return hasClientId() ? this.clientId.toString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
public String secretAsString() {
|
public String secretAsString() {
|
||||||
return hasSecret() ? this.secret.toString() : null;
|
return hasSecret() ? this.secret.toString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
public String accessTokenAsString() {
|
public String accessTokenAsString() {
|
||||||
return hasAccessToken() ? this.accessToken.toString() : null;
|
return hasAccessToken() ? this.accessToken.toString() : null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -532,6 +532,11 @@ public enum ActionDefinition {
|
||||||
ImageIcon.DELETE,
|
ImageIcon.DELETE,
|
||||||
PageStateDefinitionImpl.SEB_CLIENT_CONFIG_LIST,
|
PageStateDefinitionImpl.SEB_CLIENT_CONFIG_LIST,
|
||||||
ActionCategory.FORM),
|
ActionCategory.FORM),
|
||||||
|
SEB_CLIENT_CONFIG_SHOW_CREDENTIALS(
|
||||||
|
new LocTextKey("sebserver.clientconfig.action.credentials"),
|
||||||
|
ImageIcon.SECURE,
|
||||||
|
PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW,
|
||||||
|
ActionCategory.FORM),
|
||||||
|
|
||||||
SEB_EXAM_CONFIG_LIST(
|
SEB_EXAM_CONFIG_LIST(
|
||||||
new LocTextKey("sebserver.examconfig.action.list"),
|
new LocTextKey("sebserver.examconfig.action.list"),
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
package ch.ethz.seb.sebserver.gui.content.configs;
|
package ch.ethz.seb.sebserver.gui.content.configs;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -27,6 +28,7 @@ import org.springframework.stereotype.Component;
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
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.EntityType;
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.client.ClientCredentials;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
||||||
|
@ -44,6 +46,7 @@ import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
|
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService;
|
import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.download.SEBClientConfigDownload;
|
import ch.ethz.seb.sebserver.gui.service.remote.download.SEBClientConfigDownload;
|
||||||
|
@ -52,11 +55,13 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeactivateClientConfig;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeactivateClientConfig;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeleteClientConfig;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeleteClientConfig;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientConfig;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientConfig;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientCredentials;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.NewClientConfig;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.NewClientConfig;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.SaveClientConfig;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.SaveClientConfig;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck;
|
||||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||||
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
@ -76,6 +81,15 @@ public class SEBClientConfigForm implements TemplateComposer {
|
||||||
private static final LocTextKey FORM_UPDATE_TIME_TEXT_KEY =
|
private static final LocTextKey FORM_UPDATE_TIME_TEXT_KEY =
|
||||||
new LocTextKey("sebserver.clientconfig.form.update.time");
|
new LocTextKey("sebserver.clientconfig.form.update.time");
|
||||||
|
|
||||||
|
private static final LocTextKey CLIENT_CREDENTIALS_TITLE_TEXT_KEY =
|
||||||
|
new LocTextKey("sebserver.clientconfig.form.credentials.title");
|
||||||
|
private static final LocTextKey CLIENT_CREDENTIALS_INFO_TEXT_KEY =
|
||||||
|
new LocTextKey("sebserver.clientconfig.form.credentials.info");
|
||||||
|
private static final LocTextKey CLIENT_CREDENTIALS_NAME_TEXT_KEY =
|
||||||
|
new LocTextKey("sebserver.clientconfig.form.credentials.name");
|
||||||
|
private static final LocTextKey CLIENT_CREDENTIALS_SECRET_TEXT_KEY =
|
||||||
|
new LocTextKey("sebserver.clientconfig.form.credentials.secret");
|
||||||
|
|
||||||
private static final LocTextKey FORM_DATE_TEXT_KEY =
|
private static final LocTextKey FORM_DATE_TEXT_KEY =
|
||||||
new LocTextKey("sebserver.clientconfig.form.date");
|
new LocTextKey("sebserver.clientconfig.form.date");
|
||||||
private static final LocTextKey CLIENT_PURPOSE_TEXT_KEY =
|
private static final LocTextKey CLIENT_PURPOSE_TEXT_KEY =
|
||||||
|
@ -210,6 +224,12 @@ public class SEBClientConfigForm implements TemplateComposer {
|
||||||
.withExec(this::deleteConnectionConfig)
|
.withExec(this::deleteConnectionConfig)
|
||||||
.publishIf(() -> modifyGrant && isReadonly)
|
.publishIf(() -> modifyGrant && isReadonly)
|
||||||
|
|
||||||
|
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_SHOW_CREDENTIALS)
|
||||||
|
.withEntityKey(entityKey)
|
||||||
|
.withExec(getClientCredentialFunction(this.pageService, this.cryptor))
|
||||||
|
.ignoreMoveAwayFromEdit()
|
||||||
|
.publishIf(() -> modifyGrant && isReadonly)
|
||||||
|
|
||||||
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_EXPORT)
|
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_EXPORT)
|
||||||
.withEntityKey(entityKey)
|
.withEntityKey(entityKey)
|
||||||
.withExec(action -> {
|
.withExec(action -> {
|
||||||
|
@ -558,6 +578,60 @@ public class SEBClientConfigForm implements TemplateComposer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Function<PageAction, PageAction> getClientCredentialFunction(
|
||||||
|
final PageService pageService,
|
||||||
|
final Cryptor cryptor) {
|
||||||
|
|
||||||
|
final RestService restService = pageService.getResourceService().getRestService();
|
||||||
|
return action -> {
|
||||||
|
|
||||||
|
final ClientCredentials credentials = restService
|
||||||
|
.getBuilder(GetClientCredentials.class)
|
||||||
|
.withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId)
|
||||||
|
.call()
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
final WidgetFactory widgetFactory = pageService.getWidgetFactory();
|
||||||
|
final ModalInputDialog<Void> dialog = new ModalInputDialog<>(
|
||||||
|
action.pageContext().getParent().getShell(),
|
||||||
|
widgetFactory);
|
||||||
|
|
||||||
|
dialog.setDialogWidth(720);
|
||||||
|
|
||||||
|
dialog.open(
|
||||||
|
CLIENT_CREDENTIALS_TITLE_TEXT_KEY,
|
||||||
|
action.pageContext(),
|
||||||
|
pc -> {
|
||||||
|
|
||||||
|
final Composite content = widgetFactory.defaultPageLayout(
|
||||||
|
pc.getParent());
|
||||||
|
|
||||||
|
widgetFactory.labelLocalized(
|
||||||
|
content,
|
||||||
|
CustomVariant.TEXT_H3,
|
||||||
|
CLIENT_CREDENTIALS_INFO_TEXT_KEY);
|
||||||
|
|
||||||
|
pageService.formBuilder(
|
||||||
|
action.pageContext().copyOf(content))
|
||||||
|
.readonly(true)
|
||||||
|
.withDefaultSpanLabel(1)
|
||||||
|
.withDefaultSpanInput(6)
|
||||||
|
|
||||||
|
.addField(FormBuilder.text(
|
||||||
|
"ClientId",
|
||||||
|
CLIENT_CREDENTIALS_NAME_TEXT_KEY,
|
||||||
|
credentials.clientIdAsString()))
|
||||||
|
|
||||||
|
.addField(FormBuilder.password(
|
||||||
|
"ClientSecret",
|
||||||
|
CLIENT_CREDENTIALS_SECRET_TEXT_KEY,
|
||||||
|
cryptor.decrypt(credentials.secret).getOrThrow()))
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
return action;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static final class FormHandleAnchor {
|
private static final class FormHandleAnchor {
|
||||||
|
|
||||||
FormHandle<SEBClientConfig> formHandle;
|
FormHandle<SEBClientConfig> formHandle;
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.gui.service.remote.webservice.api.seb.clientconfig;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.client.ClientCredentials;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class GetClientCredentials extends RestCall<ClientCredentials> {
|
||||||
|
|
||||||
|
public GetClientCredentials() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.GET_SINGLE,
|
||||||
|
null,
|
||||||
|
new TypeReference<ClientCredentials>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.SEB_CLIENT_CONFIG_ENDPOINT
|
||||||
|
+ API.SEB_CLIENT_CONFIG_CREDENTIALS_PATH_SEGMENT
|
||||||
|
+ API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ 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.POSTMapper;
|
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.client.ClientCredentials;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
|
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||||
|
@ -81,6 +82,25 @@ public class SEBClientConfigController extends ActivatableEntityController<SEBCl
|
||||||
this.sebClientConfigService = sebClientConfigService;
|
this.sebClientConfigService = sebClientConfigService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.SEB_CLIENT_CONFIG_CREDENTIALS_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT,
|
||||||
|
method = RequestMethod.GET,
|
||||||
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public ClientCredentials getClientCredentials(
|
||||||
|
@PathVariable final String modelId,
|
||||||
|
@RequestParam(
|
||||||
|
name = Entity.FILTER_ATTR_INSTITUTION,
|
||||||
|
required = true,
|
||||||
|
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) {
|
||||||
|
|
||||||
|
checkReadPrivilege(institutionId);
|
||||||
|
|
||||||
|
return this.entityDAO.byModelId(modelId)
|
||||||
|
.flatMap(this.authorization::checkWrite)
|
||||||
|
.flatMap(config -> ((SEBClientConfigDAO) this.entityDAO).getSEBClientCredentials(modelId))
|
||||||
|
.getOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
path = API.SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT,
|
path = API.SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT,
|
||||||
method = RequestMethod.GET,
|
method = RequestMethod.GET,
|
||||||
|
|
|
@ -810,6 +810,11 @@ sebserver.clientconfig.form.certificate.tooltip=Choose identity certificate to b
|
||||||
sebserver.clientconfig.form.type.async=Use asymmetric-only encryption
|
sebserver.clientconfig.form.type.async=Use asymmetric-only encryption
|
||||||
sebserver.clientconfig.form.type.async.tooltip=Use old asymmetric-only encryption (for SEB < 2.2)
|
sebserver.clientconfig.form.type.async.tooltip=Use old asymmetric-only encryption (for SEB < 2.2)
|
||||||
|
|
||||||
|
sebserver.clientconfig.form.credentials.title=Client Credentials of Connection Configuration
|
||||||
|
sebserver.clientconfig.form.credentials.info=A SEB client that loads this connection configuration<br/>uses the following credentials to securely connect to the SEB Server.
|
||||||
|
sebserver.clientconfig.form.credentials.name=Client Id
|
||||||
|
sebserver.clientconfig.form.credentials.secret=Secret
|
||||||
|
|
||||||
sebserver.clientconfig.config.purpose.START_EXAM=Starting an Exam
|
sebserver.clientconfig.config.purpose.START_EXAM=Starting an Exam
|
||||||
sebserver.clientconfig.config.purpose.START_EXAM.tooltip=If the connection configuration is loaded via a SEB-Link, the local configuration will not be overwritten.
|
sebserver.clientconfig.config.purpose.START_EXAM.tooltip=If the connection configuration is loaded via a SEB-Link, the local configuration will not be overwritten.
|
||||||
sebserver.clientconfig.config.purpose.CONFIGURE_CLIENT=Configure a Client
|
sebserver.clientconfig.config.purpose.CONFIGURE_CLIENT=Configure a Client
|
||||||
|
@ -820,6 +825,7 @@ sebserver.clientconfig.action.list.view=View Connection Configuration
|
||||||
sebserver.clientconfig.action.list.modify=Edit Connection Configuration
|
sebserver.clientconfig.action.list.modify=Edit Connection Configuration
|
||||||
sebserver.clientconfig.action.modify=Edit Connection Configuration
|
sebserver.clientconfig.action.modify=Edit Connection Configuration
|
||||||
sebserver.clientconfig.action.save=Save Connection Configuration
|
sebserver.clientconfig.action.save=Save Connection Configuration
|
||||||
|
sebserver.clientconfig.action.credentials=Show Client Credentials
|
||||||
sebserver.clientconfig.action.activate=Activate Connection Configuration
|
sebserver.clientconfig.action.activate=Activate Connection Configuration
|
||||||
sebserver.clientconfig.action.deactivate=Deactivate Connection Configuration
|
sebserver.clientconfig.action.deactivate=Deactivate Connection Configuration
|
||||||
sebserver.clientconfig.action.export=Export Connection Configuration
|
sebserver.clientconfig.action.export=Export Connection Configuration
|
||||||
|
|
Loading…
Reference in a new issue