registration page

This commit is contained in:
anhefti 2020-01-23 16:46:05 +01:00
parent ed8387216b
commit 2f64cf92e0
27 changed files with 615 additions and 31 deletions

View file

@ -80,7 +80,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
.ignoring() .ignoring()
.antMatchers("/error") .antMatchers("/error")
.antMatchers(this.examAPIDiscoveryEndpoint) .antMatchers(this.examAPIDiscoveryEndpoint)
.antMatchers(this.adminAPIEndpoint + API.INFO_ENDPOINT + API.LOGO_PATH_SEGMENT + "/**"); .antMatchers(this.adminAPIEndpoint + API.INFO_ENDPOINT + API.LOGO_PATH_SEGMENT + "/**")
.antMatchers(this.adminAPIEndpoint + API.INFO_ENDPOINT + API.INFO_INST_PATH_SEGMENT + "/**")
.antMatchers(this.adminAPIEndpoint + API.REGISTER_ENDPOINT);
} }
@RequestMapping("/error") @RequestMapping("/error")

View file

@ -23,6 +23,9 @@ import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
/** Global Constants used in SEB Server web-service as well as in web-gui component */ /** Global Constants used in SEB Server web-service as well as in web-gui component */
public final class Constants { public final class Constants {
public static final String DEFAULT_LANG_CODE = "en";
public static final String DEFAULT_TIME_ZONE_CODE = "UTC";
public static final int SEB_FILE_HEADER_SIZE = 4; public static final int SEB_FILE_HEADER_SIZE = 4;
public static final int JN_CRYPTOR_ITERATIONS = 10000; public static final int JN_CRYPTOR_ITERATIONS = 10000;
public static final int JN_CRYPTOR_VERSION_HEADER_SIZE = 1; public static final int JN_CRYPTOR_VERSION_HEADER_SIZE = 1;

View file

@ -46,8 +46,13 @@ public final class API {
public static final String SELF_PATH_SEGMENT = "/self"; public static final String SELF_PATH_SEGMENT = "/self";
public static final String INFO_ENDPOINT = "/info"; public static final String INFO_ENDPOINT = "/info";
public static final String INFO_PARAM_INST_SUFFIX = "urlSuffix";
public static final String INFO_INST_PATH_SEGMENT = "/institution";
public static final String INFO_INST_ENDPOINT = INFO_INST_PATH_SEGMENT + "/{" + INFO_PARAM_INST_SUFFIX + "}";
public static final String LOGO_PATH_SEGMENT = "/logo"; public static final String LOGO_PATH_SEGMENT = "/logo";
public static final String INSTITUTIONAL_LOGO_PATH = LOGO_PATH_SEGMENT + "/{urlSuffix}";
public static final String INSTITUTIONAL_LOGO_PATH = LOGO_PATH_SEGMENT + "/{" + INFO_PARAM_INST_SUFFIX + "}";
public static final String REGISTER_ENDPOINT = "/register";
public static final String NAMES_PATH_SEGMENT = "/names"; public static final String NAMES_PATH_SEGMENT = "/names";

View file

@ -436,6 +436,10 @@ public final class Utils {
return toCharArray(CharBuffer.wrap(chars)); return toCharArray(CharBuffer.wrap(chars));
} }
public static void clear(final CharSequence charSequence) {
clearCharArray(toCharArray(charSequence));
}
public static String toString(final CharSequence charSequence) { public static String toString(final CharSequence charSequence) {
if (charSequence == null) { if (charSequence == null) {
return null; return null;

View file

@ -38,8 +38,6 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter {
public static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher( public static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
// OAuth entry-points // OAuth entry-points
new AntPathRequestMatcher(API.OAUTH_REVOKE_TOKEN_ENDPOINT), new AntPathRequestMatcher(API.OAUTH_REVOKE_TOKEN_ENDPOINT),
// GUI entry-point
// new AntPathRequestMatcher(guiEntryPoint),
// RAP/RWT resources has to be accessible // RAP/RWT resources has to be accessible
new AntPathRequestMatcher("/rwt-resources/**"), new AntPathRequestMatcher("/rwt-resources/**"),
// project specific static resources // project specific static resources

View file

@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.Charsets; import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.binary.Base64InputStream; import org.apache.commons.codec.binary.Base64InputStream;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -40,12 +41,16 @@ import org.springframework.web.client.RestTemplate;
import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService;
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.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService;
import ch.ethz.seb.sebserver.gui.widget.ImageUploadSelection; import ch.ethz.seb.sebserver.gui.widget.ImageUploadSelection;
@Lazy @Lazy
@Component @Component
final class InstitutionalAuthenticationEntryPoint implements AuthenticationEntryPoint { @GuiProfile
public final class InstitutionalAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final String INST_SUFFIX_ATTRIBUTE = "instSuffix";
private static final Logger log = LoggerFactory.getLogger(InstitutionalAuthenticationEntryPoint.class); private static final Logger log = LoggerFactory.getLogger(InstitutionalAuthenticationEntryPoint.class);
@ -111,7 +116,7 @@ final class InstitutionalAuthenticationEntryPoint implements AuthenticationEntry
final String logoImageBase64 = requestLogoImage(institutionalEndpoint); final String logoImageBase64 = requestLogoImage(institutionalEndpoint);
if (StringUtils.isNotBlank(logoImageBase64)) { if (StringUtils.isNotBlank(logoImageBase64)) {
request.getSession().setAttribute(API.PARAM_LOGO_IMAGE, logoImageBase64); request.getSession().setAttribute(API.PARAM_LOGO_IMAGE, logoImageBase64);
request.getSession().setAttribute("themeId", "sms"); request.getSession().setAttribute(INST_SUFFIX_ATTRIBUTE, institutionalEndpoint);
forwardToEntryPoint(request, response, this.guiEntryPoint); forwardToEntryPoint(request, response, this.guiEntryPoint);
} else { } else {
request.getSession().removeAttribute(API.PARAM_LOGO_IMAGE); request.getSession().removeAttribute(API.PARAM_LOGO_IMAGE);
@ -132,19 +137,31 @@ final class InstitutionalAuthenticationEntryPoint implements AuthenticationEntry
dispatcher.forward(request, response); dispatcher.forward(request, response);
} }
private String extractInstitutionalEndpoint(final HttpServletRequest request) { public static String extractInstitutionalEndpoint(final HttpServletRequest request) {
final String requestURI = request.getRequestURI(); final String requestURI = request.getRequestURI();
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Trying to verify insitution from requested entrypoint url: {}", requestURI); log.debug("Trying to verify insitution from requested entrypoint url: {}", requestURI);
} }
final String instPrefix = requestURI.replaceAll("/", ""); try {
if (StringUtils.isBlank(instPrefix)) { return requestURI.substring(
requestURI.lastIndexOf(Constants.SLASH) + 1,
requestURI.length());
} catch (final Exception e) {
log.error("Fauled to extract institutional URL suffix: {}", e.getMessage());
return null; return null;
} }
}
return instPrefix; public static String extractInstitutionalEndpoint() {
try {
final Object attribute = RWT.getUISession().getHttpSession().getAttribute(INST_SUFFIX_ATTRIBUTE);
return (attribute != null) ? String.valueOf(attribute) : null;
} catch (final Exception e) {
log.warn("Failed to extract institutional endpoint form user session: {}", e.getMessage());
return null;
}
} }
private String requestLogoImage(final String institutionalEndpoint) { private String requestLogoImage(final String institutionalEndpoint) {

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.gui.content; package ch.ethz.seb.sebserver.gui.content;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
@ -20,6 +21,7 @@ import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -28,6 +30,7 @@ import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext;
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.DefaultRegisterPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
import ch.ethz.seb.sebserver.gui.widget.Message; import ch.ethz.seb.sebserver.gui.widget.Message;
@ -44,12 +47,20 @@ public class LoginPage implements TemplateComposer {
private final AuthorizationContextHolder authorizationContextHolder; private final AuthorizationContextHolder authorizationContextHolder;
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
private final I18nSupport i18nSupport; private final I18nSupport i18nSupport;
private final DefaultRegisterPage defaultRegisterPage;
private final boolean registreringEnabled;
public LoginPage(
final PageService pageService,
final DefaultRegisterPage defaultRegisterPage,
@Value("${sebserver.gui.self-registering:false}") final Boolean registreringEnabled) {
public LoginPage(final PageService pageService) {
this.pageService = pageService; this.pageService = pageService;
this.authorizationContextHolder = pageService.getAuthorizationContextHolder(); this.authorizationContextHolder = pageService.getAuthorizationContextHolder();
this.widgetFactory = pageService.getWidgetFactory(); this.widgetFactory = pageService.getWidgetFactory();
this.i18nSupport = pageService.getI18nSupport(); this.i18nSupport = pageService.getI18nSupport();
this.defaultRegisterPage = defaultRegisterPage;
this.registreringEnabled = BooleanUtils.toBoolean(registreringEnabled);
} }
@Override @Override
@ -61,7 +72,7 @@ public class LoginPage implements TemplateComposer {
rowLayout.marginWidth = 20; rowLayout.marginWidth = 20;
rowLayout.marginRight = 100; rowLayout.marginRight = 100;
loginGroup.setLayout(rowLayout); loginGroup.setLayout(rowLayout);
loginGroup.setData(RWT.CUSTOM_VARIANT, "login"); loginGroup.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN.key);
final Label name = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.username"); final Label name = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.username");
name.setLayoutData(new GridData(300, -1)); name.setLayoutData(new GridData(300, -1));
@ -75,15 +86,19 @@ public class LoginPage implements TemplateComposer {
final Text loginPassword = this.widgetFactory.passwordInput(loginGroup); final Text loginPassword = this.widgetFactory.passwordInput(loginGroup);
loginPassword.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); loginPassword.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
final Button button = this.widgetFactory.buttonLocalized(loginGroup, "sebserver.login.login");
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
button.setLayoutData(gridData);
final SEBServerAuthorizationContext authorizationContext = this.authorizationContextHolder final SEBServerAuthorizationContext authorizationContext = this.authorizationContextHolder
.getAuthorizationContext(RWT.getUISession().getHttpSession()); .getAuthorizationContext(RWT.getUISession().getHttpSession());
button.addListener(SWT.Selection, event -> { final Composite buttons = new Composite(loginGroup, SWT.NONE);
buttons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
buttons.setLayout(new GridLayout(2, false));
buttons.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN_BACK.key);
final Button loginButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.login.login");
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
loginButton.setLayoutData(gridData);
loginButton.addListener(SWT.Selection, event -> {
login( login(
pageContext, pageContext,
loginName.getText(), loginName.getText(),
@ -117,6 +132,15 @@ public class LoginPage implements TemplateComposer {
} }
}); });
if (this.registreringEnabled) {
final Button registerButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.login.register");
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
registerButton.setLayoutData(gridData);
registerButton.addListener(SWT.Selection, event -> {
pageContext.forwardToPage(this.defaultRegisterPage);
});
}
} }
private void login( private void login(

View file

@ -0,0 +1,204 @@
/*
* Copyright (c) 2020 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.content;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.InstitutionalAuthenticationEntryPoint;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
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.remote.webservice.api.institution.GetInstitutionInfo;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class RegisterPage implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(RegisterPage.class);
static final LocTextKey TITLE_TEXT_KEY =
new LocTextKey("sebserver.login.register.form.title");
static final LocTextKey FORM_PASSWORD_CONFIRM_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.password.confirm");
static final LocTextKey FORM_PASSWORD_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.password");
static final LocTextKey FORM_ROLES_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.roles");
static final LocTextKey FORM_TIMEZONE_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.timezone");
static final LocTextKey FORM_MAIL_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.mail");
static final LocTextKey FORM_USERNAME_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.username");
static final LocTextKey FORM_NAME_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.name");
static final LocTextKey FORM_INSTITUTION_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.institution");
static final LocTextKey FORM_LANG_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.language");
private final PageService pageService;
private final ResourceService resourceService;
private final WidgetFactory widgetFactory;
private final I18nSupport i18nSupport;
private final WebserviceURIService webserviceURIService;
private final RestTemplate restTemplate;
private final boolean multilingual;
protected RegisterPage(
final PageService pageService,
final WebserviceURIService webserviceURIService,
final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
@Value("${sebserver.gui.multilingual:false}") final Boolean multilingual) {
this.pageService = pageService;
this.resourceService = pageService.getResourceService();
this.widgetFactory = pageService.getWidgetFactory();
this.i18nSupport = pageService.getI18nSupport();
this.webserviceURIService = webserviceURIService;
this.multilingual = BooleanUtils.toBoolean(multilingual);
this.restTemplate = new RestTemplate();
final ClientHttpRequestFactory clientHttpRequestFactory = clientHttpRequestFactoryService
.getClientHttpRequestFactory()
.getOrThrow();
this.restTemplate.setRequestFactory(clientHttpRequestFactory);
}
@Override
public void compose(final PageContext pageContext) {
final String institutionId = InstitutionalAuthenticationEntryPoint
.extractInstitutionalEndpoint();
final List<EntityName> institutions = this.pageService
.getRestService()
.getBuilder(GetInstitutionInfo.class)
.withRestTemplate(this.restTemplate)
.withURIVariable(API.INFO_PARAM_INST_SUFFIX, institutionId)
.call()
.getOrThrow();
final boolean definedInstitution = institutions.size() == 1;
final Supplier<List<Tuple<String>>> instResources = () -> institutions
.stream()
.map(entityName -> new Tuple<>(entityName.modelId, entityName.name))
.sorted(ResourceService.RESOURCE_COMPARATOR)
.collect(Collectors.toList());
final Composite content = this.widgetFactory.defaultPageLayout(
pageContext.getParent(),
TITLE_TEXT_KEY);
content.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN.key);
// The UserAccount form
final FormBuilder formBuilder = this.pageService.formBuilder(
pageContext.copyOf(content))
.readonly(false)
.putStaticValueIf(
() -> !this.multilingual,
Domain.USER.ATTR_LANGUAGE,
"en")
.addField(FormBuilder.singleSelection(
Domain.USER.ATTR_INSTITUTION_ID,
FORM_INSTITUTION_TEXT_KEY,
(definedInstitution) ? institutions.get(0).modelId : null,
instResources)
.readonly(definedInstitution))
.addField(FormBuilder.text(
Domain.USER.ATTR_NAME,
FORM_NAME_TEXT_KEY))
.addField(FormBuilder.text(
Domain.USER.ATTR_USERNAME,
FORM_USERNAME_TEXT_KEY))
.addField(FormBuilder.text(
Domain.USER.ATTR_EMAIL,
FORM_MAIL_TEXT_KEY))
.addFieldIf(
() -> this.multilingual,
() -> FormBuilder.singleSelection(
Domain.USER.ATTR_LANGUAGE,
FORM_LANG_TEXT_KEY,
Constants.DEFAULT_LANG_CODE,
this.resourceService::languageResources))
.addField(FormBuilder.singleSelection(
Domain.USER.ATTR_TIMEZONE,
FORM_TIMEZONE_TEXT_KEY,
Constants.DEFAULT_TIME_ZONE_CODE,
this.resourceService::timeZoneResources))
.addField(FormBuilder.text(
PasswordChange.ATTR_NAME_NEW_PASSWORD,
FORM_PASSWORD_TEXT_KEY)
.asPasswordField())
.addField(FormBuilder.text(
PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD,
FORM_PASSWORD_CONFIRM_TEXT_KEY)
.asPasswordField());
//formBuilder.formParent.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN_BACK.key);
formBuilder.build();
final Composite buttons = new Composite(content, SWT.NONE);
buttons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
buttons.setLayout(new GridLayout(2, false));
buttons.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN_BACK.key);
final Button registerButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.login.register");
GridData gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
registerButton.setLayoutData(gridData);
registerButton.addListener(SWT.Selection, event -> {
});
final Button cancelButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.overall.action.cancel");
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
cancelButton.setLayoutData(gridData);
cancelButton.addListener(SWT.Selection, event -> {
pageContext.forwardToLoginPage();
});
}
}

View file

@ -10,8 +10,10 @@ package ch.ethz.seb.sebserver.gui.content;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.tomcat.util.buf.StringUtils; import org.apache.tomcat.util.buf.StringUtils;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -74,16 +76,21 @@ public class UserAccountForm implements TemplateComposer {
new LocTextKey("sebserver.useraccount.form.name"); new LocTextKey("sebserver.useraccount.form.name");
static final LocTextKey FORM_INSTITUTION_TEXT_KEY = static final LocTextKey FORM_INSTITUTION_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.institution"); new LocTextKey("sebserver.useraccount.form.institution");
static final LocTextKey FORM_LANG_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.language");
private final PageService pageService; private final PageService pageService;
private final ResourceService resourceService; private final ResourceService resourceService;
private final boolean multilingual;
protected UserAccountForm( protected UserAccountForm(
final PageService pageService, final PageService pageService,
final ResourceService resourceService) { final ResourceService resourceService,
@Value("${sebserver.gui.multilingual:false}") final Boolean multilingual) {
this.pageService = pageService; this.pageService = pageService;
this.resourceService = resourceService; this.resourceService = resourceService;
this.multilingual = BooleanUtils.toBoolean(multilingual);
} }
@Override @Override
@ -149,7 +156,8 @@ public class UserAccountForm implements TemplateComposer {
.putStaticValueIf(isNotNew, .putStaticValueIf(isNotNew,
Domain.USER.ATTR_INSTITUTION_ID, Domain.USER.ATTR_INSTITUTION_ID,
String.valueOf(userAccount.getInstitutionId())) String.valueOf(userAccount.getInstitutionId()))
.putStaticValue( .putStaticValueIf(
() -> !this.multilingual,
Domain.USER.ATTR_LANGUAGE, Domain.USER.ATTR_LANGUAGE,
"en") "en")
.addFieldIf( .addFieldIf(
@ -172,12 +180,13 @@ public class UserAccountForm implements TemplateComposer {
Domain.USER.ATTR_EMAIL, Domain.USER.ATTR_EMAIL,
FORM_MAIL_TEXT_KEY, FORM_MAIL_TEXT_KEY,
userAccount.getEmail())) userAccount.getEmail()))
// .addField(FormBuilder.singleSelection( .addFieldIf(
// Domain.USER.ATTR_LANGUAGE, () -> this.multilingual,
// "sebserver.useraccount.form.language", () -> FormBuilder.singleSelection(
// userAccount.getLanguage().getLanguage(), Domain.USER.ATTR_LANGUAGE,
// this.resourceService::languageResources) FORM_LANG_TEXT_KEY,
// .readonly(true)) userAccount.getLanguage().getLanguage(),
this.resourceService::languageResources))
.addField(FormBuilder.singleSelection( .addField(FormBuilder.singleSelection(
Domain.USER.ATTR_TIMEZONE, Domain.USER.ATTR_TIMEZONE,
FORM_TIMEZONE_TEXT_KEY, FORM_TIMEZONE_TEXT_KEY,

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.page.impl;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.LoginPage; import ch.ethz.seb.sebserver.gui.content.LoginPage;
import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
@ -21,6 +22,7 @@ import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
* SEBLogin template */ * SEBLogin template */
@Lazy @Lazy
@Component @Component
@GuiProfile
public class DefaultLoginPage implements PageDefinition { public class DefaultLoginPage implements PageDefinition {
@Override @Override

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.page.impl;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.MainPage; import ch.ethz.seb.sebserver.gui.content.MainPage;
import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
@ -21,6 +22,7 @@ import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
* SEBMainPage template */ * SEBMainPage template */
@Lazy @Lazy
@Component @Component
@GuiProfile
public class DefaultMainPage implements PageDefinition { public class DefaultMainPage implements PageDefinition {
@Override @Override

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 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.page.impl;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.RegisterPage;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.PageDefinition;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
@Lazy
@Component
@GuiProfile
public class DefaultRegisterPage implements PageDefinition {
@Override
public Class<? extends TemplateComposer> composer() {
return DefaultPageLayout.class;
}
@Override
public PageContext applyPageContext(final PageContext pageContext) {
return pageContext.withAttribute(
AttributeKeys.PAGE_TEMPLATE_COMPOSER_NAME,
RegisterPage.class.getName());
}
}

View file

@ -59,6 +59,7 @@ public abstract class RestCall<T> {
GET_DEPENDENCIES, GET_DEPENDENCIES,
GET_LIST, GET_LIST,
NEW, NEW,
REGISTER,
SAVE, SAVE,
DELETE, DELETE,
ACTIVATION_ACTIVATE, ACTIVATION_ACTIVATE,

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020 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.institution;
import java.util.List;
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.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetInstitutionInfo extends RestCall<List<EntityName>> {
public GetInstitutionInfo() {
super(new TypeKey<>(
CallType.GET_NAMES,
EntityType.INSTITUTION,
new TypeReference<List<EntityName>>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.INFO_ENDPOINT + API.INFO_INST_ENDPOINT);
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2020 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.useraccount;
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.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class RegisterNewUser extends RestCall<UserInfo> {
public RegisterNewUser() {
super(new TypeKey<>(
CallType.REGISTER,
EntityType.USER,
new TypeReference<UserInfo>() {
}),
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.REGISTER_ENDPOINT);
}
}

View file

@ -19,6 +19,7 @@ import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
@GuiProfile @GuiProfile
public class WebserviceURIService { public class WebserviceURIService {
private final String servletContextPath;
private final String webserviceServerAddress; private final String webserviceServerAddress;
private final UriComponentsBuilder webserviceURIBuilder; private final UriComponentsBuilder webserviceURIBuilder;
@ -26,12 +27,15 @@ public class WebserviceURIService {
@Value("${sebserver.gui.webservice.protocol}") final String webserviceProtocol, @Value("${sebserver.gui.webservice.protocol}") final String webserviceProtocol,
@Value("${sebserver.gui.webservice.address}") final String webserviceServerAdress, @Value("${sebserver.gui.webservice.address}") final String webserviceServerAdress,
@Value("${sebserver.gui.webservice.port}") final String webserviceServerPort, @Value("${sebserver.gui.webservice.port}") final String webserviceServerPort,
@Value("${server.servlet.context-path}") final String servletContextPath,
@Value("${sebserver.gui.webservice.apipath}") final String webserviceAPIPath) { @Value("${sebserver.gui.webservice.apipath}") final String webserviceAPIPath) {
this.servletContextPath = servletContextPath;
this.webserviceServerAddress = webserviceProtocol + "://" + webserviceServerAdress + ":" + webserviceServerPort; this.webserviceServerAddress = webserviceProtocol + "://" + webserviceServerAdress + ":" + webserviceServerPort;
this.webserviceURIBuilder = UriComponentsBuilder this.webserviceURIBuilder = UriComponentsBuilder
.fromHttpUrl(webserviceProtocol + "://" + webserviceServerAdress) .fromHttpUrl(webserviceProtocol + "://" + webserviceServerAdress)
.port(webserviceServerPort) .port(webserviceServerPort)
.path(servletContextPath)
.path(webserviceAPIPath); .path(webserviceAPIPath);
} }
@ -45,12 +49,14 @@ public class WebserviceURIService {
public String getOAuthTokenURI() { public String getOAuthTokenURI() {
return UriComponentsBuilder.fromHttpUrl(this.webserviceServerAddress) return UriComponentsBuilder.fromHttpUrl(this.webserviceServerAddress)
.path(this.servletContextPath)
.path(API.OAUTH_TOKEN_ENDPOINT) .path(API.OAUTH_TOKEN_ENDPOINT)
.toUriString(); .toUriString();
} }
public String getOAuthRevokeTokenURI() { public String getOAuthRevokeTokenURI() {
return UriComponentsBuilder.fromHttpUrl(this.webserviceServerAddress) return UriComponentsBuilder.fromHttpUrl(this.webserviceServerAddress)
.path(this.servletContextPath)
.path(API.OAUTH_REVOKE_TOKEN_ENDPOINT) .path(API.OAUTH_REVOKE_TOKEN_ENDPOINT)
.toUriString(); .toUriString();
} }

View file

@ -152,7 +152,10 @@ public class WidgetFactory {
CONFIG_INPUT_READONLY("inputreadonly"), CONFIG_INPUT_READONLY("inputreadonly"),
DARK_COLOR_LABEL("colordark"), DARK_COLOR_LABEL("colordark"),
LIGHT_COLOR_LABEL("colorlight") LIGHT_COLOR_LABEL("colorlight"),
LOGIN("login"),
LOGIN_BACK("login-back")
; ;

View file

@ -9,7 +9,9 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api; package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.Collection; import java.util.Collection;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -18,6 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
@ -51,12 +54,28 @@ public class InfoController {
.all(null, true) .all(null, true)
.getOrThrow() .getOrThrow()
.stream() .stream()
.filter(inst -> inst.urlSuffix != null && urlSuffix.endsWith(inst.urlSuffix)) .filter(inst -> inst.urlSuffix != null && urlSuffix.equals(inst.urlSuffix))
.findFirst() .findFirst()
.map(inst -> inst.logoImage) .map(inst -> inst.logoImage)
.orElse(null); .orElse(null);
} }
@RequestMapping(
path = API.INFO_INST_ENDPOINT,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Collection<EntityKey> getInstitutionInfo(@PathVariable(required = false) final String urlSuffix) {
return this.institutionDAO
.all(null, true)
.getOrThrow()
.stream()
.filter(inst -> BooleanUtils.isTrue(inst.active) &&
(inst.urlSuffix == null ||
urlSuffix.equals(inst.urlSuffix)))
.map(inst -> inst.getEntityKey())
.collect(Collectors.toList());
}
@RequestMapping( @RequestMapping(
path = API.PRIVILEGES_PATH_SEGMENT, path = API.PRIVILEGES_PATH_SEGMENT,
method = RequestMethod.GET, method = RequestMethod.GET,

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2020 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.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Locale;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTimeZone;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.FieldError;
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 ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
@WebServiceProfile
@RestController
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.REGISTER_ENDPOINT)
public class RegisterUserController {
private final InstitutionDAO institutionDAO;
private final UserActivityLogDAO userActivityLogDAO;
private final UserDAO userDAO;
private final ClientCredentialService clientCredentialService;
protected RegisterUserController(
final InstitutionDAO institutionDAO,
final UserActivityLogDAO userActivityLogDAO,
final UserDAO userDAO,
final ClientCredentialService clientCredentialService,
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder userPasswordEncoder) {
this.institutionDAO = institutionDAO;
this.userActivityLogDAO = userActivityLogDAO;
this.userDAO = userDAO;
this.clientCredentialService = clientCredentialService;
}
@RequestMapping(
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public UserInfo registerNewUser(
@RequestParam(name = Domain.USER.ATTR_INSTITUTION_ID, required = true) final String institutionId,
@RequestParam(name = Domain.USER.ATTR_NAME, required = true) final String name,
@RequestParam(name = Domain.USER.ATTR_USERNAME, required = true) final String username,
@RequestParam(name = Domain.USER.ATTR_EMAIL, required = false) final String email,
@RequestParam(
name = Domain.USER.ATTR_LANGUAGE,
required = false,
defaultValue = Constants.DEFAULT_LANG_CODE) final String lang,
@RequestParam(
name = Domain.USER.ATTR_TIMEZONE,
required = false,
defaultValue = Constants.DEFAULT_TIME_ZONE_CODE) final String timezone,
@RequestParam(name = PasswordChange.ATTR_NAME_NEW_PASSWORD, required = true) final String pwd,
@RequestParam(name = PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, required = true) final String rpwd) {
final Collection<APIMessage> errors = new ArrayList<>();
// check institution info
Long instId = null;
if (StringUtils.isNotBlank(institutionId)) {
try {
instId = Long.parseLong(institutionId);
} catch (final Exception e) {
instId = this.institutionDAO
.all(null, true)
.getOrThrow()
.stream()
.filter(inst -> inst.urlSuffix != null && institutionId.equals(inst.urlSuffix))
.findFirst()
.map(inst -> inst.id)
.orElse(null);
}
}
if (instId == null) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
"user",
Domain.USER.ATTR_INSTITUTION_ID,
"user:institutionId:notNull")));
}
// check password-match
final CharSequence rawPWD = this.clientCredentialService.decrypt(pwd);
final CharSequence rawRPWD = this.clientCredentialService.decrypt(rpwd);
if (!rawPWD.equals(rawRPWD)) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
"passwordChange",
PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD,
"user:confirmNewPassword:password.mismatch")));
}
if (!errors.isEmpty()) {
throw new APIMessageException(errors);
}
final UserMod user = new UserMod(
null,
instId,
name,
username,
rawPWD,
rawRPWD,
email,
Locale.forLanguageTag(lang),
DateTimeZone.forID(timezone),
new HashSet<>(Arrays.asList(UserRole.EXAM_SUPPORTER.name())));
return this.userDAO.createNew(user)
.flatMap(this.userActivityLogDAO::logCreate)
.map(u -> {
Utils.clear(rawPWD);
Utils.clear(rawRPWD);
return u;
})
.getOrThrow();
}
}

View file

@ -1,6 +1,5 @@
server.address=localhost server.address=localhost
server.port=8080 server.port=8080
server.servlet.context-path=/
sebserver.gui.entrypoint=/gui sebserver.gui.entrypoint=/gui
sebserver.gui.webservice.protocol=http sebserver.gui.webservice.protocol=http

View file

@ -1,6 +1,5 @@
server.address=localhost server.address=localhost
server.port=8090 server.port=8090
server.servlet.context-path=/
logging.file=log/sebserver.log logging.file=log/sebserver.log

View file

@ -110,6 +110,7 @@ sebserver.webservice.lms.address.alias=
########################################################## ##########################################################
### SEB Server GUI configuration ### SEB Server GUI configuration
sebserver.gui.self-registering=true
sebserver.gui.multilingual=false sebserver.gui.multilingual=false
sebserver.gui.supported.languages=en sebserver.gui.supported.languages=en
sebserver.gui.entrypoint=/gui sebserver.gui.entrypoint=/gui

View file

@ -107,6 +107,10 @@ sebserver.logout.invalid-session.message=You have been signed out because of a u
sebserver.login.password.change=Information sebserver.login.password.change=Information
sebserver.login.password.change.success=The password was successfully changed. Please sign in with your new password sebserver.login.password.change.success=The password was successfully changed. Please sign in with your new password
sebserver.login.register=Register
sebserver.login.register.form.title=Register As New User
################################ ################################
# Main Page # Main Page
################################ ################################

View file

@ -120,6 +120,10 @@ Label.colorlight {
padding: 2px 5px 2px 5px; padding: 2px 5px 2px 5px;
} }
Composite {
background-color: transparent;
}
Composite.bordered { Composite.bordered {
border: 2px; border: 2px;
} }
@ -177,6 +181,12 @@ Composite.login {
border-radius: 2px; border-radius: 2px;
} }
Composite.login-back {
background-color: #EAECEE;
margin: 0px 0 0 0;
padding: 0px 0px 0px 0px;
}
Composite.error { Composite.error {
border: 1px solid #aa0000; border: 1px solid #aa0000;
border-radius: 1px; border-radius: 1px;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 B

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

View file

@ -74,7 +74,7 @@ public abstract class GuiIntegrationTest {
final HttpSession sessionMock = Mockito.mock(HttpSession.class); final HttpSession sessionMock = Mockito.mock(HttpSession.class);
final WebserviceURIService webserviceURIService = new WebserviceURIService( final WebserviceURIService webserviceURIService = new WebserviceURIService(
"http", "localhost", "8080", this.endpoint); "http", "localhost", "8080", "/", this.endpoint);
final ClientHttpRequestFactoryService clientHttpRequestFactoryService = Mockito final ClientHttpRequestFactoryService clientHttpRequestFactoryService = Mockito
.mock(ClientHttpRequestFactoryService.class); .mock(ClientHttpRequestFactoryService.class);