SEBSERV-18 SEBSERV-19 #ported from prototype

This commit is contained in:
anhefti 2019-01-30 17:02:27 +01:00
parent f21f959ad2
commit d5f7752e98
92 changed files with 4016 additions and 336 deletions

View file

@ -224,7 +224,13 @@
<artifactId>spring-security-jwt</artifactId>
<version>1.0.9.RELEASE</version>
</dependency>
<!-- Apache HTTP -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- Eclipse RAP / RWT -->
<dependency>
<groupId>org.eclipse.rap</groupId>

View file

@ -19,6 +19,7 @@ import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@ -90,6 +91,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
final HttpServletResponse response,
final AuthenticationException authException) throws IOException, ServletException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.sendRedirect(WebSecurityConfig.this.unauthorizedRedirect);
}
})
@ -104,6 +106,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
@RequestMapping("/error")
public void handleError(final HttpServletResponse response) throws IOException {
response.setStatus(HttpStatus.NOT_FOUND.value());
response.sendRedirect(this.unauthorizedRedirect);
}

View file

@ -6,9 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.model;
package ch.ethz.seb.sebserver.gbl.api;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -72,12 +74,14 @@ public class APIMessage implements Serializable {
return new APIMessage(this.messageCode, this.systemMessage, error.getMessage());
}
public ResponseEntity<APIMessage> createErrorResponse() {
return new ResponseEntity<>(of(), this.httpStatus);
public ResponseEntity<List<APIMessage>> createErrorResponse() {
final APIMessage message = of();
return new ResponseEntity<>(Arrays.asList(message), this.httpStatus);
}
public ResponseEntity<Object> createErrorResponse(final String details, final String... attributes) {
return new ResponseEntity<>(of(details, attributes), this.httpStatus);
final APIMessage message = of(details, attributes);
return new ResponseEntity<>(Arrays.asList(message), this.httpStatus);
}
}
@ -134,6 +138,24 @@ public class APIMessage implements Serializable {
return ErrorMessage.FIELD_VALIDATION.of(error.toString(), args);
}
public static String toHTML(final Collection<APIMessage> messages) {
final StringBuilder builder = new StringBuilder();
builder.append("<b>Messages:</b><br/><br/>");
messages.stream().forEach(message -> {
builder
.append("&nbsp;&nbsp;code&nbsp;:&nbsp;")
.append(message.messageCode)
.append("<br/>")
.append("&nbsp;&nbsp;system message&nbsp;:&nbsp;")
.append(message.systemMessage)
.append("<br/>")
.append("&nbsp;&nbsp;details&nbsp;:&nbsp;")
.append(StringUtils.abbreviate(message.details, 100))
.append("<br/><br/>");
});
return builder.toString();
}
public static class APIMessageException extends RuntimeException {
private static final long serialVersionUID = 1453431210820677296L;

View file

@ -0,0 +1,17 @@
/*
* 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.List;
public interface APIMessageError {
List<APIMessage> getErrorMessages();
}

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl;
package ch.ethz.seb.sebserver.gbl.api;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl;
package ch.ethz.seb.sebserver.gbl.api;
import java.util.Collections;
import java.util.List;

View file

@ -6,9 +6,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.api;
package ch.ethz.seb.sebserver.gbl.api;
public class RestAPI {
public class SEBServerRestEndpoints {
public static final String ENDPOINT_INSTITUTION = "/institution";

View file

@ -22,8 +22,8 @@ public interface Entity extends ModelIdAware {
@JsonIgnore
String getName();
public static EntityKeyAndName toName(final Entity entity) {
return new EntityKeyAndName(
public static EntityName toName(final Entity entity) {
return new EntityName(
entity.entityType(),
entity.getModelId(),
entity.getName());

View file

@ -14,7 +14,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
public class EntityName implements ModelIdAware, ModelNameAware {
@JsonProperty(value = "entityType", required = true)
public final EntityType entityType;
@ -24,7 +24,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
public final String name;
@JsonCreator
public EntityKeyAndName(
public EntityName(
@JsonProperty(value = "entityType", required = true) final EntityType entityType,
@JsonProperty(value = Domain.ATTR_ID, required = true) final String id,
@JsonProperty(value = "name", required = true) final String name) {
@ -34,7 +34,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
this.name = name;
}
public EntityKeyAndName(final EntityKey entityKey, final String name) {
public EntityName(final EntityKey entityKey, final String name) {
this.entityType = entityKey.entityType;
this.modelId = entityKey.modelId;
@ -74,7 +74,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
return false;
if (getClass() != obj.getClass())
return false;
final EntityKeyAndName other = (EntityKeyAndName) obj;
final EntityName other = (EntityName) obj;
if (this.entityType != other.entityType)
return false;
if (this.modelId == null) {

View file

@ -14,6 +14,7 @@ import java.util.Set;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.util.Utils;
public class EntityProcessingReport {

View file

@ -19,7 +19,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
import ch.ethz.seb.sebserver.gbl.model.EntityType;

View file

@ -13,7 +13,7 @@ import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION;

View file

@ -15,12 +15,12 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION;
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName;
import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;
@ -198,8 +198,8 @@ public final class LmsSetup implements GrantEntity, Activatable {
+ this.sebAuthSecret + ", active=" + this.active + "]";
}
public static EntityKeyAndName toName(final LmsSetup lmsSetup) {
return new EntityKeyAndName(
public static EntityName toName(final LmsSetup lmsSetup) {
return new EntityName(
EntityType.LMS_SETUP,
String.valueOf(lmsSetup.id),
lmsSetup.name);

View file

@ -156,6 +156,13 @@ public final class UserInfo implements GrantEntity, Activatable, Serializable {
return this.roles;
}
public boolean hasRole(final UserRole userRole) {
if (userRole == null) {
return false;
}
return this.roles.contains(userRole.name());
}
@Override
public int hashCode() {
final int prime = 31;

View file

@ -22,9 +22,9 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;

View file

@ -69,23 +69,58 @@ public final class Result<T> {
return this.value;
}
/** @return the error if some was reporter or null if there was no error */
public Throwable getError() {
return this.error;
/** Use this to get the resulting value. In an error case, a given error handling
* function is used that receives the error and returns a resulting value instead
* (or throw some error instead)
*
* @param errorHandler the error handling function
* @return */
public T get(final Function<Throwable, T> errorHandler) {
return this.error != null ? errorHandler.apply(this.error) : this.value;
}
public boolean hasError() {
return this.error != null;
public T get(final Consumer<Throwable> errorHandler, final Supplier<T> supplier) {
if (this.error != null) {
errorHandler.accept(this.error);
return supplier.get();
} else {
return this.value;
}
}
/** Use this to get the resulting value or (if null) to get a given other value
*
* @param other the other value to get if the computed value is null
* @return return either the computed value if existing or a given other value */
public T getOrElse(final T other) {
public T getOr(final T other) {
return this.value != null ? this.value : other;
}
/** Use this to get the resulting value if existing or throw an Runtime exception with
* given message otherwise.
*
* @param message the message for the RuntimeException in error case
* @return the resulting value */
public T getOrThrowRuntime(final String message) {
if (this.error != null) {
throw new RuntimeException(message, this.error);
}
return this.value;
}
public T getOrThrow() {
if (this.error != null) {
if (this.error instanceof RuntimeException) {
throw (RuntimeException) this.error;
} else {
throw new RuntimeException("RuntimeExceptionWrapper cause: ", this.error);
}
}
return this.value;
}
/** Use this to get the resulting value or (if null) to get a given other value
*
* @param supplier supplier to get the value from if the computed value is null
@ -94,6 +129,15 @@ public final class Result<T> {
return this.value != null ? this.value : supplier.get();
}
/** @return the error if some was reporter or null if there was no error */
public Throwable getError() {
return this.error;
}
public boolean hasError() {
return this.error != null;
}
/** If a value is present, performs the given action with the value,
* otherwise performs the given empty-based action.
*
@ -155,16 +199,6 @@ public final class Result<T> {
}
}
/** Use this to get the resulting value. In an error case, a given error handling
* function is used that receives the error and returns a resulting value instead
* (or throw some error instead)
*
* @param errorHandler the error handling function
* @return */
public T getOrHandleError(final Function<Throwable, T> errorHandler) {
return this.error != null ? errorHandler.apply(this.error) : this.value;
}
public Result<T> onErrorDo(final Consumer<Throwable> block) {
if (this.error != null) {
block.accept(this.error);
@ -172,31 +206,6 @@ public final class Result<T> {
return this;
}
/** Use this to get the resulting value if existing or throw an Runtime exception with
* given message otherwise.
*
* @param message the message for the RuntimeException in error case
* @return the resulting value */
public T getOrThrowRuntime(final String message) {
if (this.error != null) {
throw new RuntimeException(message, this.error);
}
return this.value;
}
public T getOrThrow() {
if (this.error != null) {
if (this.error instanceof RuntimeException) {
throw (RuntimeException) this.error;
} else {
throw new RuntimeException("RuntimeExceptionWrapper cause: ", this.error);
}
}
return this.value;
}
/** Use this to create a Result of a given resulting value.
*
* @param value resulting value

View file

@ -103,7 +103,7 @@ public final class Utils {
public static Long dateTimeStringToTimestamp(final String startTime, final Long defaultValue) {
return dateTimeStringToTimestamp(startTime)
.getOrElse(defaultValue);
.getOr(defaultValue);
}
public static <M extends Map<K, V>, K, V> M mapPut(final M map, final K key, final V value) {

View file

@ -11,7 +11,6 @@ package ch.ethz.seb.sebserver.gui;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@ -43,38 +42,4 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter {
.requestMatchers(PUBLIC_URLS);
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
System.out.println("**************** GuiWebConfig: ");
// //@formatter:off
// http
// .sessionManagement()
// .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// .and()
// .antMatcher("/**"/*this.guiEndpointPath + "/**"*/)
// .authorizeRequests()
// .anyRequest()
// .authenticated()
// .and()
// .exceptionHandling()
// .authenticationEntryPoint(
// new AuthenticationEntryPoint() {
//
// @Override
// public void commence(final HttpServletRequest request, final HttpServletResponse response,
// final AuthenticationException authException) throws IOException, ServletException {
// response.sendRedirect("/gui");
// }
//
// })
// .and()
// .formLogin().disable()
// .httpBasic().disable()
// .logout().disable()
// .headers().frameOptions().disable()
// .and()
// .csrf().disable();
//@formatter:on
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2018 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.i18n;
import java.util.Collection;
import java.util.Locale;
import org.joda.time.DateTime;
public interface I18nSupport {
Collection<Locale> supportedLanguages();
/** Get the current Locale either form a user if this is called from a logged in user context or the
* applications default locale.
*
* @return the current Locale to use in context */
Locale getCurrentLocale();
void setSessionLocale(Locale locale);
/** Format a DateTime to a text format to display.
*
* @param date
* @return */
String formatDisplayDate(DateTime date);
/** Get localized text of specified key for currently set Locale.
*
* @param key the key name of localized text
* @param args additional arguments to parse the localized text
* @return the text in current language parsed from localized text */
String getText(String key, Object... args);
/** Get localized text of specified key for currently set Locale.
*
* @param key LocTextKey instance
* @return the text in current language parsed from localized text */
String getText(LocTextKey key);
/** Get localized text of specified key for currently set Locale.
*
* @param key the key name of localized text
* @param def default text that is returned if no localized test with specified key was found
* @param args additional arguments to parse the localized text
* @return the text in current language parsed from localized text */
String getText(final String key, String def, Object... args);
/** Get localized text of specified key and Locale.
*
* @param key the key name of localized text
* @param locale the Locale
* @param args additional arguments to parse the localized text
* @return the text in current language parsed from localized text */
String getText(String key, Locale locale, Object... args);
/** Get localized text of specified key and Locale.
*
* @param key the key name of localized text
* @param locale the Locale
* @param def default text that is returned if no localized test with specified key was found
* @param args additional arguments to parse the localized text
* @return the text in current language parsed from localized text */
String getText(String key, Locale locale, String def, Object... args);
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2018 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.i18n;
import java.util.Arrays;
public class LocTextKey {
public final String name;
public final Object[] args;
public LocTextKey(final String name) {
this.name = name;
this.args = null;
}
public LocTextKey(final String name, final Object... args) {
this.name = name;
this.args = args;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final LocTextKey other = (LocTextKey) obj;
if (this.name == null) {
if (other.name != null)
return false;
} else if (!this.name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "LocTextKey [name=" + this.name + ", args=" + Arrays.toString(this.args) + "]";
}
}

View file

@ -0,0 +1,38 @@
/*
* 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.gui.service.i18n;
import java.util.Locale;
import org.eclipse.swt.widgets.Composite;
public interface PolyglotPageService {
String POLYGLOT_WIDGET_FUNCTION_KEY = "POLYGLOT_WIDGET_FUNCTION";
String POLYGLOT_TREE_ITEM_TEXT_DATA_KEY = "POLYGLOT_TREE_ITEM_TEXT_DATA";
/** Gets the underling I18nSupport
*
* @return the underling I18nSupport */
I18nSupport getI18nSupport();
/** The default locale for the page.
* Uses I18nSupport.getCurrentLocale to do so.
*
* @param root the root Composite of the page to change the language */
void setDefaultPageLocale(Composite root);
/** Sets the given Locale and if needed, updates the page language according to the
* given Locale
*
* @param root root the root Composite of the page to change the language
* @param locale the Locale to set */
void setPageLocale(Composite root, Locale locale);
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2018 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.i18n.impl;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import org.eclipse.rap.rwt.RWT;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
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.remote.webservice.auth.CurrentUser;
@Lazy
@Component
public class I18nSupportImpl implements I18nSupport {
private static final Logger log = LoggerFactory.getLogger(I18nSupportImpl.class);
public static final String EMPTY_DISPLAY_VALUE = "--";
private static final String ATTR_CURRENT_SESSION_LOCALE = "CURRENT_SESSION_LOCALE";
private final DateTimeFormatter displayDateFormatter;
private final CurrentUser currentUser;
private final MessageSource messageSource;
private final Locale defaultLocale = Locale.ENGLISH;
public I18nSupportImpl(
final CurrentUser currentUser,
final MessageSource messageSource,
@Value("${sebserver.gui.date.displayformat}") final String displayDateFormat) {
this.currentUser = currentUser;
this.messageSource = messageSource;
this.displayDateFormatter = DateTimeFormat
.forPattern(displayDateFormat)
.withZoneUTC();
}
private static final Collection<Locale> SUPPORTED = Arrays.asList(Locale.ENGLISH, Locale.GERMANY);
@Override
public Collection<Locale> supportedLanguages() {
return SUPPORTED;
}
@Override
public void setSessionLocale(final Locale locale) {
try {
RWT.getUISession()
.getHttpSession()
.setAttribute(ATTR_CURRENT_SESSION_LOCALE, locale);
RWT.setLocale(locale);
} catch (final IllegalStateException e) {
log.error("Set current locale for session failed: ", e);
}
}
@Override
public Locale getCurrentLocale() {
// first session-locale if available
try {
final Locale sessionLocale = (Locale) RWT.getUISession()
.getHttpSession()
.getAttribute(ATTR_CURRENT_SESSION_LOCALE);
if (sessionLocale != null) {
return sessionLocale;
}
} catch (final IllegalStateException e) {
log.warn("Get current locale for session failed: {}", e.getMessage());
}
// second user-locale if available
if (this.currentUser.isAvailable()) {
return this.currentUser.get().locale;
}
// last the default locale
return this.defaultLocale;
}
@Override
public String formatDisplayDate(final DateTime date) {
if (date == null) {
return EMPTY_DISPLAY_VALUE;
}
return date.toString(this.displayDateFormatter);
}
@Override
public String getText(final LocTextKey key) {
return getText(key.name, key.args);
}
@Override
public String getText(final String key, final Object... args) {
return getText(key, key, args);
}
@Override
public String getText(final String key, final String def, final Object... args) {
return this.messageSource.getMessage(key, args, def, this.getCurrentLocale());
}
@Override
public String getText(final String key, final Locale locale, final Object... args) {
return getText(key, locale, key, args);
}
@Override
public String getText(final String key, final Locale locale, final String def, final Object... args) {
return this.messageSource.getMessage(key, args, def, locale);
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2018 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.i18n.impl;
import java.util.Locale;
import java.util.function.Consumer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
import ch.ethz.seb.sebserver.gui.service.page.ComposerService;
/** Service that supports page language change on the fly */
@Lazy
@Service
@GuiProfile
public final class PolyglotPageServiceImpl implements PolyglotPageService {
private final I18nSupport i18nSupport;
public PolyglotPageServiceImpl(final I18nSupport i18nSupport) {
this.i18nSupport = i18nSupport;
}
@Override
public I18nSupport getI18nSupport() {
return this.i18nSupport;
}
@Override
public void setDefaultPageLocale(final Composite root) {
setPageLocale(root, this.i18nSupport.getCurrentLocale());
}
@Override
@SuppressWarnings("unchecked")
public void setPageLocale(final Composite root, final Locale locale) {
this.i18nSupport.setSessionLocale(locale);
ComposerService.traversePageTree(
root,
comp -> comp.getData(POLYGLOT_WIDGET_FUNCTION_KEY) != null,
comp -> ((Consumer<Control>) comp.getData(POLYGLOT_WIDGET_FUNCTION_KEY)).accept(comp));
root.layout(true, true);
}
}

View file

@ -0,0 +1,66 @@
/*
* 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.gui.service.page;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import ch.ethz.seb.sebserver.gui.RAPConfiguration.EntryPointService;
public interface ComposerService extends EntryPointService {
boolean validate(String composerName, PageContext pageContext);
PageDefinition mainPage();
PageDefinition loginPage();
void compose(
String composerName,
PageContext pageContext);
void compose(
Class<? extends TemplateComposer> composerType,
PageContext pageContext);
void composePage(
Class<? extends PageDefinition> pageType,
Composite root);
void composePage(
PageDefinition pageDefinition,
Composite root);
static void traversePageTree(
final Composite root,
final Predicate<Control> predicate,
final Consumer<Control> f) {
if (predicate.test(root)) {
f.accept(root);
}
final Control[] children = root.getChildren();
if (children != null) {
for (final Control control : children) {
if (!(control instanceof Composite)) {
if (predicate.test(control)) {
f.accept(control);
}
} else {
traversePageTree((Composite) control, predicate, f);
}
}
}
}
}

View file

@ -0,0 +1,128 @@
/*
* 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.gui.service.page;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent;
public interface PageContext {
public static final class PageAttr {
public final String name;
public final String value;
public PageAttr(final String name, final String value) {
this.name = name;
this.value = value;
}
}
public interface AttributeKeys {
public static final String ATTR_PAGE_TEMPLATE_COMPOSER_NAME = "ATTR_PAGE_TEMPLATE_COMPOSER_NAME";
public static final String INSTITUTION_ID = "INSTITUTION_ID";
// public static final String USER_NAME = "USER_NAME";
// public static final String PASSWORD = "PASSWORD";
//
//
// public static final String CONFIG_ID = "CONFIG_ID";
// public static final String CONFIG_VIEW_NAME = "CONFIG_VIEW_NAME";
// public static final String CONFIG_ATTRIBUTE_SAVE_TYPE = "CONFIG_ATTRIBUTE_SAVE_TYPE";
// public static final String CONFIG_ATTRIBUTE_VALUE = "CONFIG_ATTRIBUTE_VALUE";
//
// public static final String EXAM_ID = "EXAM_ID";
// public static final String STATE_NAME = "STATE_NAME";
//
// public static final String AUTHORIZATION_CONTEXT = "AUTHORIZATION_CONTEXT";
// public static final String AUTHORIZATION_HEADER = "AUTHORIZATION_HEADER";
public static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
public static final String LGOUT_SUCCESS = "LGOUT_SUCCESS";
}
ComposerService composerService();
Shell getShell();
/** Get the page root Component.
*
* @return the page root Component. */
Composite getRoot();
/** Get the Component that is currently set as parent during page tree compose
*
* @return the parent Component */
Composite getParent();
/** Create a copy of this PageContext with a new parent Composite.
*
* @param parent the new parent Composite
* @return a copy of this PageContext with a new parent Composite. */
PageContext copyOf(Composite parent);
/** Create a copy of this PageContext with and additionally page context attributes.
* The additionally page context attributes will get merged with them already defined
*
* @param attributes additionally page context attributes.
* @return a copy of this PageContext with with and additionally page context attributes. */
PageContext copyOfAttributes(PageContext otherContext);
/** Adds the specified attribute to the existing page context attributes.
*
* @param key the key of the attribute
* @param value the value of the attribute
* @return this PageContext instance (builder pattern) */
PageContext withAttr(String key, String value);
String getAttribute(String name);
String getAttribute(String name, String def);
boolean hasAttribute(String name);
/** Publishes a given PageEvent to the current page tree
* This goes through the page-tree and collects all listeners the are listen to
* the specified page event type.
*
* @param event the concrete PageEvent instance */
<T extends PageEvent> void publishPageEvent(T event);
/** Apply a confirm dialog with a specified confirm message and a callback code
* block that will be executed on users OK selection.
*
* @param confirmMessage
* @param onOK callback code block that will be executed on users OK selection */
void applyConfirmDialog(String confirmMessage, Runnable onOK);
void forwardToPage(
PageDefinition pageDefinition,
PageContext pageContext);
void forwardToMainPage(PageContext pageContext);
void forwardToLoginPage(PageContext pageContext);
/** Notify an error dialog to the user with specified error message and
* optional exception instance
*
* @param errorMessage the error message to display
* @param error the error as Throwable */
void notifyError(String errorMessage, Throwable error);
void notifyError(Throwable error);
<T> T logoutOnError(Throwable error);
}

View file

@ -0,0 +1,16 @@
/*
* 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.gui.service.page;
public interface PageDefinition {
Class<? extends TemplateComposer> composer();
PageContext applyPageContext(PageContext pageContext);
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018 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;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent;
public interface PageEventListener<T extends PageEvent> {
String LISTENER_ATTRIBUTE_KEY = "PageEventListener";
boolean match(Class<? extends PageEvent> eventType);
default int priority() {
return 1;
}
void notify(T event);
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2018 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;
import org.eclipse.swt.widgets.Event;
public interface PopupMenuComposer {
void onMenuEvent(final Event event);
}

View file

@ -0,0 +1,19 @@
/*
* Copyright (c) 2018 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;
public interface TemplateComposer {
default boolean validate(final PageContext context) {
return true;
}
void compose(PageContext composerCtx);
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2018 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.action;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType;
public enum ActionDefinition {
INSTITUTION_NEW(
"actions.new.institution",
IconButtonType.NEW_ACTION),
INSTITUTION_MODIFY(
"actions.modify.institution",
IconButtonType.SAVE_ACTION),
INSTITUTION_DELETE(
"actions.delete.institution",
IconButtonType.DELETE_ACTION),
LMS_SETUP_NEW(
"New LMS Setup",
IconButtonType.NEW_ACTION),
LMS_SETUP_MODIFY(
"Save LMS Setup",
IconButtonType.SAVE_ACTION),
LMS_SETUP_DELETE(
"Delete LMS Setup",
IconButtonType.DELETE_ACTION),
LMS_SETUP_TEST(
"Test LMS Setup",
IconButtonType.SAVE_ACTION),
SEB_CONFIG_NEW(
"New Configuration",
IconButtonType.NEW_ACTION),
SEB_CONFIG_MODIFY(
"Save Configuration",
IconButtonType.SAVE_ACTION),
SEB_CONFIG_DELETE(
"Delete Configuration",
IconButtonType.DELETE_ACTION),
EXAM_IMPORT(
"Import Exam",
IconButtonType.SAVE_ACTION),
EXAM_EDIT(
"Edit Selected Exam",
IconButtonType.NEW_ACTION),
EXAM_DELETE(
"Delete Selected Exam",
IconButtonType.DELETE_ACTION),
;
public final String name;
public final IconButtonType icon;
private ActionDefinition(final String name, final IconButtonType icon) {
this.name = name;
this.icon = icon;
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2018 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.action;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.template.ImageCell;
import org.eclipse.rap.rwt.template.Template;
import org.eclipse.rap.rwt.template.TextCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
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.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@Lazy
@Component
public class ActionPane implements TemplateComposer {
private static final String ACTION_EVENT_CALL_KEY = "ACTION_EVENT_CALL";
private final WidgetFactory widgetFactory;
public ActionPane(final WidgetFactory widgetFactory) {
super();
this.widgetFactory = widgetFactory;
}
@Override
public void compose(final PageContext composerCtx) {
final Label label = this.widgetFactory.labelLocalized(
composerCtx.getParent(),
"h3",
new LocTextKey("sebserver.actionpane.title"));
final GridData titleLayout = new GridData(SWT.FILL, SWT.TOP, true, false);
titleLayout.verticalIndent = 10;
titleLayout.horizontalIndent = 10;
label.setLayoutData(titleLayout);
final Tree actions = this.widgetFactory.treeLocalized(composerCtx.getParent(), SWT.SINGLE | SWT.FULL_SELECTION);
actions.setData(RWT.CUSTOM_VARIANT, "actions");
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
actions.setLayoutData(gridData);
final Template template = new Template();
final ImageCell imageCell = new ImageCell(template);
imageCell.setLeft(0, 0).setWidth(40).setTop(0).setBottom(0, 0).setHorizontalAlignment(SWT.LEFT);
imageCell.setBindingIndex(0);
final TextCell textCell = new TextCell(template);
textCell.setLeft(0, 30).setWidth(150).setTop(7).setBottom(0, 0).setHorizontalAlignment(SWT.LEFT);
textCell.setBindingIndex(0);
actions.setData(RWT.ROW_TEMPLATE, template);
actions.addListener(SWT.Selection, event -> {
final TreeItem treeItem = (TreeItem) event.item;
final Runnable action = (Runnable) treeItem.getData(ACTION_EVENT_CALL_KEY);
action.run();
if (!treeItem.isDisposed()) {
treeItem.getParent().deselectAll();
}
});
actions.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ActionPublishEventListener() {
@Override
public void notify(final ActionPublishEvent event) {
final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized(
actions,
event.actionDefinition.name);
actionItem.setImage(event.actionDefinition.icon.getImage(composerCtx.getParent().getDisplay()));
actionItem.setData(ACTION_EVENT_CALL_KEY,
new SafeActionExecution(composerCtx, event, event.run));
}
});
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2018 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.action;
public interface InstitutionActions {
// /** Use this higher-order function to create a new Institution action Runnable.
// *
// * @return */
// static Runnable newInstitution(final PageContext composerCtx, final RestServices restServices) {
// return () -> {
// final IdAndName newInstitutionId = restServices
// .sebServerAPICall(NewInstitution.class)
// .doAPICall()
// .onErrorThrow("Unexpected Error");
// composerCtx.notify(new ActionEvent(ActionDefinition.INSTITUTION_NEW, newInstitutionId));
// };
// }
//
// /** Use this higher-order function to create a delete Institution action Runnable.
// *
// * @return */
// static Runnable deleteInstitution(final PageContext composerCtx, final RestServices restServices,
// final String instId) {
// return () -> {
// restServices
// .sebServerAPICall(DeleteInstitution.class)
// .attribute(AttributeKeys.INSTITUTION_ID, instId)
// .doAPICall()
// .onErrorThrow("Unexpected Error");
// composerCtx.notify(new ActionEvent(ActionDefinition.INSTITUTION_DELETE, instId));
// };
// }
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2018 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.action;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent;
public class SafeActionExecution implements Runnable {
private static final Logger log = LoggerFactory.getLogger(SafeActionExecution.class);
private final PageContext pageContext;
private final ActionPublishEvent actionEvent;
private final Runnable actionExecution;
public SafeActionExecution(
final PageContext pageContext,
final ActionPublishEvent actionEvent,
final Runnable actionExecution) {
this.pageContext = pageContext;
this.actionEvent = actionEvent;
this.actionExecution = actionExecution;
}
@Override
public void run() {
try {
if (StringUtils.isNotBlank(this.actionEvent.confirmationMessage)) {
this.pageContext.applyConfirmDialog(
this.actionEvent.confirmationMessage,
createConfirmationCallback());
} else {
this.actionExecution.run();
}
} catch (final Throwable t) {
log.error("Failed to execute action: {}", this.actionEvent, t);
this.pageContext.notifyError("action.error.unexpected.message", t);
}
}
private final Runnable createConfirmationCallback() {
return new Runnable() {
@Override
public void run() {
try {
SafeActionExecution.this.actionExecution.run();
} catch (final Throwable t) {
log.error("Failed to execute action: {}", SafeActionExecution.this.actionEvent, t);
SafeActionExecution.this.pageContext.notifyError("action.error.unexpected.message", t);
}
}
};
}
}

View file

@ -0,0 +1,281 @@
/*
* Copyright (c) 2018 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.activity;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
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.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
import ch.ethz.seb.sebserver.gui.service.page.impl.MainPageState;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@Lazy
@Component
public class ActivitiesPane implements TemplateComposer {
private final WidgetFactory widgetFactory;
private final RestService restService;
private final AuthorizationContextHolder authorizationContextHolder;
private final Map<ActionDefinition, ActivityActionHandler> activityActionHandler =
new EnumMap<>(ActionDefinition.class);
public ActivitiesPane(
final WidgetFactory widgetFactory,
final RestService restService,
final AuthorizationContextHolder authorizationContextHolder,
final Collection<ActivityActionHandler> activityActionHandler) {
this.widgetFactory = widgetFactory;
this.restService = restService;
this.authorizationContextHolder = authorizationContextHolder;
for (final ActivityActionHandler aah : activityActionHandler) {
this.activityActionHandler.put(aah.handlesAction(), aah);
}
}
@Override
public void compose(final PageContext pageContext) {
final UserInfo userInfo = this.authorizationContextHolder
.getAuthorizationContext()
.getLoggedInUser()
.get(pageContext::logoutOnError);
final Label activities = this.widgetFactory.labelLocalized(
pageContext.getParent(),
"h3",
new LocTextKey("sebserver.activitiespane.title"));
final GridData activitiesGridData = new GridData(SWT.FILL, SWT.TOP, true, false);
activitiesGridData.horizontalIndent = 20;
activities.setLayoutData(activitiesGridData);
final Tree navigation =
this.widgetFactory.treeLocalized(pageContext.getParent(), SWT.SINGLE | SWT.FULL_SELECTION);
final GridData navigationGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
navigationGridData.horizontalIndent = 10;
navigation.setLayoutData(navigationGridData);
final List<EntityName> insitutionNames = this.restService
.getBuilder(GetInstitutionNames.class)
.call()
.get(pageContext::notifyError, () -> Collections.emptyList());
if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) {
// institutions (list) as root
final TreeItem institutions = this.widgetFactory.treeItemLocalized(
navigation,
Activity.INSTITUTION_ROOT.title);
ActivitySelection.inject(institutions, Activity.INSTITUTION_ROOT.createSelection());
for (final EntityName inst : insitutionNames) {
createInstitutionItem(institutions, inst);
}
} else {
final EntityName inst = insitutionNames.iterator().next();
createInstitutionItem(navigation, inst);
}
// final TreeItem user = this.widgetFactory.treeItemLocalized(
// navigation,
// "org.sebserver.activities.user");
// ActivitySelection.set(user, Activity.USERS.createSelection());
//
// final TreeItem configs = this.widgetFactory.treeItemLocalized(
// navigation,
// "org.sebserver.activities.sebconfigs");
// ActivitySelection.set(configs, Activity.SEB_CONFIGS.createSelection());
//
// final TreeItem config = this.widgetFactory.treeItemLocalized(
// configs,
// "org.sebserver.activities.sebconfig");
// ActivitySelection.set(config, Activity.SEB_CONFIG.createSelection());
//
// final TreeItem configTemplates = this.widgetFactory.treeItemLocalized(
// configs,
// "org.sebserver.activities.sebconfig.templates");
// ActivitySelection.set(configTemplates, Activity.SEB_CONFIG_TEMPLATES.createSelection());
//
// final TreeItem exams = this.widgetFactory.treeItemLocalized(
// navigation,
// "org.sebserver.activities.exam");
// ActivitySelection.set(exams, Activity.EXAMS.createSelection());
//
// final TreeItem monitoring = this.widgetFactory.treeItemLocalized(
// navigation,
// "org.sebserver.activities.monitoring");
// ActivitySelection.set(monitoring, Activity.MONITORING.createSelection());
//
// final TreeItem runningExams = this.widgetFactory.treeItemLocalized(
// monitoring,
// "org.sebserver.activities.runningExams");
// ActivitySelection.set(runningExams, Activity.RUNNING_EXAMS.createSelection()
// .withExpandFunction(this::runningExamExpand));
// runningExams.setItemCount(1);
//
// final TreeItem logs = this.widgetFactory.treeItemLocalized(
// monitoring,
// "org.sebserver.activities.logs");
// ActivitySelection.set(logs, Activity.LOGS.createSelection());
navigation.addListener(SWT.Expand, this::handleExpand);
navigation.addListener(SWT.Selection, event -> handleSelection(pageContext, event));
navigation.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ActionEventListener() {
@Override
public void notify(final ActionEvent event) {
final ActivityActionHandler aah =
ActivitiesPane.this.activityActionHandler.get(event.actionDefinition);
if (aah != null) {
aah.notifyAction(event, navigation, pageContext);
}
}
});
// page-selection on (re)load
final MainPageState mainPageState = MainPageState.get();
if (mainPageState.activitySelection == null) {
mainPageState.activitySelection = ActivitySelection.get(navigation.getItem(0));
}
pageContext.publishPageEvent(
new ActivitySelectionEvent(mainPageState.activitySelection));
}
// private void runningExamExpand(final TreeItem item) {
// item.removeAll();
// final List<EntityName> runningExamNames = this.restService
// .sebServerCall(GetRunningExamNames.class)
// .onError(t -> {
// throw new RuntimeException(t);
// });
//
// if (runningExamNames != null) {
// for (final EntityName runningExamName : runningExamNames) {
// final TreeItem runningExams = this.widgetFactory.treeItemLocalized(
// item,
// runningExamName.name);
// ActivitySelection.set(runningExams, Activity.RUNNING_EXAM.createSelection(runningExamName));
// }
// }
// }
private void handleExpand(final Event event) {
final TreeItem treeItem = (TreeItem) event.item;
System.out.println("opened: " + treeItem);
final ActivitySelection activity = ActivitySelection.get(treeItem);
if (activity != null) {
activity.processExpand(treeItem);
}
}
private void handleSelection(final PageContext composerCtx, final Event event) {
final TreeItem treeItem = (TreeItem) event.item;
System.out.println("selected: " + treeItem);
final MainPageState mainPageState = MainPageState.get();
final ActivitySelection activitySelection = ActivitySelection.get(treeItem);
if (mainPageState.activitySelection == null) {
mainPageState.activitySelection = Activity.NONE.createSelection();
}
if (!mainPageState.activitySelection.equals(activitySelection)) {
mainPageState.activitySelection = activitySelection;
composerCtx.publishPageEvent(
new ActivitySelectionEvent(mainPageState.activitySelection));
}
}
static TreeItem createInstitutionItem(final Tree parent, final EntityName idAndName) {
final TreeItem institution = new TreeItem(parent, SWT.NONE);
createInstitutionItem(idAndName, institution);
return institution;
}
static TreeItem createInstitutionItem(final TreeItem parent, final EntityName idAndName) {
final TreeItem institution = new TreeItem(parent, SWT.NONE);
createInstitutionItem(idAndName, institution);
return institution;
}
static void createInstitutionItem(final EntityName idAndName, final TreeItem institution) {
institution.setText(idAndName.name);
ActivitySelection.inject(institution, Activity.INSTITUTION_NODE.createSelection(idAndName));
}
static final TreeItem findItemByActivity(
final TreeItem[] items,
final Activity activity,
final String objectId) {
if (items == null) {
return null;
}
for (final TreeItem item : items) {
final ActivitySelection activitySelection = ActivitySelection.get(item);
final String id = activitySelection.getObjectIdentifier();
if (activitySelection != null && activitySelection.activity == activity &&
(id == null || (objectId != null && objectId.equals(id)))) {
return item;
}
final TreeItem _item = findItemByActivity(item.getItems(), activity, objectId);
if (_item != null) {
return _item;
}
}
return null;
}
static final TreeItem findItemByActivity(final TreeItem[] items, final Activity activity) {
return findItemByActivity(items, activity, null);
}
static final void expand(final TreeItem item) {
if (item == null) {
return;
}
item.setExpanded(true);
expand(item.getParentItem());
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 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.activity;
import org.eclipse.swt.widgets.Tree;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
public interface ActivityActionHandler {
public ActionDefinition handlesAction();
void notifyAction(
final ActionEvent event,
final Tree navigation,
final PageContext composerCtx);
}

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2018 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.activity;
import java.util.function.Consumer;
import org.eclipse.swt.widgets.TreeItem;
import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.action.ActionPane;
import ch.ethz.seb.sebserver.gui.service.page.impl.TODOTemplate;
public class ActivitySelection {
public static final Consumer<TreeItem> EMPTY_FUNCTION = ti -> {
};
public static final Consumer<TreeItem> COLLAPSE_NONE_EMPTY = ti -> {
ti.removeAll();
ti.setItemCount(1);
};
public enum Activity {
NONE(TODOTemplate.class, TODOTemplate.class, (String) null),
INSTITUTION_ROOT(
TODOTemplate.class,
ActionPane.class,
new LocTextKey("sebserver.activities.inst")),
INSTITUTION_NODE(
TODOTemplate.class,
ActionPane.class,
AttributeKeys.INSTITUTION_ID),
//
// USERS(UserAccountsForm.class, ActionPane.class),
//
// EXAMS(ExamsListPage.class, ActionPane.class),
// SEB_CONFIGS(SEBConfigurationForm.class, ActionPane.class),
// SEB_CONFIG(SEBConfigurationPage.class, ActionPane.class),
// SEB_CONFIG_TEMPLATES(TODOTemplate.class, ActionPane.class),
// MONITORING(MonitoringForm.class, ActionPane.class),
// RUNNING_EXAMS(RunningExamForm.class, ActionPane.class),
// RUNNING_EXAM(RunningExamPage.class, ActionPane.class, AttributeKeys.EXAM_ID),
// LOGS(TODOTemplate.class, ActionPane.class),
;
public final LocTextKey title;
public final Class<? extends TemplateComposer> contentPaneComposer;
public final Class<? extends TemplateComposer> actionPaneComposer;
public final String objectIdentifierAttribute;
private Activity(
final Class<? extends TemplateComposer> objectPaneComposer,
final Class<? extends TemplateComposer> selectionPaneComposer,
final LocTextKey title) {
this.title = title;
this.contentPaneComposer = objectPaneComposer;
this.actionPaneComposer = selectionPaneComposer;
this.objectIdentifierAttribute = null;
}
private Activity(
final Class<? extends TemplateComposer> objectPaneComposer,
final Class<? extends TemplateComposer> selectionPaneComposer,
final String objectIdentifierAttribute) {
this.title = null;
this.contentPaneComposer = objectPaneComposer;
this.actionPaneComposer = selectionPaneComposer;
this.objectIdentifierAttribute = objectIdentifierAttribute;
}
public final ActivitySelection createSelection() {
return new ActivitySelection(this);
}
public final ActivitySelection createSelection(final EntityName entityName) {
return new ActivitySelection(this, entityName);
}
}
private static final String ATTR_ACTIVITY_SELECTION = "ACTIVITY_SELECTION";
public final Activity activity;
public final EntityName entityName;
Consumer<TreeItem> expandFunction = EMPTY_FUNCTION;
ActivitySelection(final Activity activity) {
this(activity, null);
}
ActivitySelection(final Activity activity, final EntityName entityName) {
this.activity = activity;
this.entityName = entityName;
this.expandFunction = EMPTY_FUNCTION;
}
public ActivitySelection withExpandFunction(final Consumer<TreeItem> expandFunction) {
if (expandFunction == null) {
this.expandFunction = EMPTY_FUNCTION;
}
this.expandFunction = expandFunction;
return this;
}
public String getObjectIdentifier() {
if (this.entityName == null) {
return null;
}
return this.entityName.modelId;
}
public void processExpand(final TreeItem item) {
this.expandFunction.accept(item);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.activity == null) ? 0 : this.activity.hashCode());
result = prime * result + ((this.entityName == null) ? 0 : this.entityName.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final ActivitySelection other = (ActivitySelection) obj;
if (this.activity != other.activity)
return false;
if (this.entityName == null) {
if (other.entityName != null)
return false;
} else if (!this.entityName.equals(other.entityName))
return false;
return true;
}
public static ActivitySelection get(final TreeItem item) {
return (ActivitySelection) item.getData(ATTR_ACTIVITY_SELECTION);
}
public static void inject(final TreeItem item, final ActivitySelection selection) {
item.setData(ATTR_ACTIVITY_SELECTION, selection);
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 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.event;
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
public final class ActionEvent implements PageEvent {
public final ActionDefinition actionDefinition;
public final Object source;
public ActionEvent(
final ActionDefinition actionDefinition,
final Object source) {
this.actionDefinition = actionDefinition;
this.source = source;
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018 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.event;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.eclipse.swt.widgets.Widget;
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
public interface ActionEventListener extends PageEventListener<ActionEvent> {
@Override
default boolean match(final Class<? extends PageEvent> type) {
return type == ActionEvent.class;
}
static ActionEventListener of(final Consumer<ActionEvent> eventConsumer) {
return new ActionEventListener() {
@Override
public void notify(final ActionEvent event) {
eventConsumer.accept(event);
}
};
}
static ActionEventListener of(
final Predicate<ActionEvent> predicate,
final Consumer<ActionEvent> eventConsumer) {
return new ActionEventListener() {
@Override
public void notify(final ActionEvent event) {
if (predicate.test(event)) {
eventConsumer.accept(event);
}
}
};
}
static ActionEventListener of(
final ActionDefinition actionDefinition,
final Consumer<ActionEvent> eventConsumer) {
return new ActionEventListener() {
@Override
public void notify(final ActionEvent event) {
if (event.actionDefinition == actionDefinition) {
eventConsumer.accept(event);
}
}
};
}
static void injectListener(
final Widget widget,
final ActionDefinition actionDefinition,
final Consumer<ActionEvent> eventConsumer) {
widget.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,
of(actionDefinition, eventConsumer));
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2018 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.event;
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
public class ActionPublishEvent implements PageEvent {
public final ActionDefinition actionDefinition;
public final Runnable run;
public final String confirmationMessage;
public final String successMessage;
public ActionPublishEvent(
final ActionDefinition actionDefinition,
final Runnable run) {
this(actionDefinition, run, null, null);
}
public ActionPublishEvent(
final ActionDefinition actionDefinition,
final Runnable run,
final String confirmationMessage) {
this(actionDefinition, run, confirmationMessage, null);
}
public ActionPublishEvent(
final ActionDefinition actionDefinition,
final Runnable run,
final String confirmationMessage,
final String successMessage) {
this.actionDefinition = actionDefinition;
this.run = run;
this.confirmationMessage = confirmationMessage;
this.successMessage = successMessage;
}
@Override
public String toString() {
return "ActionPublishEvent [actionDefinition=" + this.actionDefinition + ", confirmationMessage="
+ this.confirmationMessage + ", successMessage=" + this.successMessage + "]";
}
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2018 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.event;
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
public interface ActionPublishEventListener extends PageEventListener<ActionPublishEvent> {
@Override
default boolean match(final Class<? extends PageEvent> type) {
return type == ActionPublishEvent.class;
}
}

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2018 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.event;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
public class ActivitySelectionEvent implements PageEvent {
public final ActivitySelection selection;
public ActivitySelectionEvent(final ActivitySelection selection) {
this.selection = selection;
}
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2018 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.event;
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
public interface ActivitySelectionListener extends PageEventListener<ActivitySelectionEvent> {
@Override
default boolean match(final Class<? extends PageEvent> eventType) {
return eventType == ActivitySelectionEvent.class;
}
}

View file

@ -0,0 +1,21 @@
/*
* 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.gui.service.page.event;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
public final class LogoutEvent implements PageEvent {
public final PageContext pageContext;
public LogoutEvent(final PageContext pageContext) {
this.pageContext = pageContext;
}
}

View file

@ -0,0 +1,20 @@
/*
* 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.gui.service.page.event;
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
public interface LogoutEventListener extends PageEventListener<LogoutEvent> {
@Override
default boolean match(final Class<? extends PageEvent> eventType) {
return eventType == LogoutEvent.class;
}
}

View file

@ -0,0 +1,13 @@
/*
* 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.gui.service.page.event;
public interface PageEvent {
}

View file

@ -0,0 +1,186 @@
/*
* 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.gui.service.page.impl;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.page.ComposerService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageDefinition;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
@Lazy
@Service
@GuiProfile
public class ComposerServiceImpl implements ComposerService {
private static final Logger log = LoggerFactory.getLogger(ComposerServiceImpl.class);
// TODO configurable
private final Class<? extends PageDefinition> loginPageType = DefaultLoginPage.class;
private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class;
final AuthorizationContextHolder authorizationContextHolder;
private final I18nSupport i18nSupport;
private final Map<String, TemplateComposer> composer;
private final Map<String, PageDefinition> pages;
public ComposerServiceImpl(
final AuthorizationContextHolder authorizationContextHolder,
final I18nSupport i18nSupport,
final Collection<TemplateComposer> composer,
final Collection<PageDefinition> pageDefinitions) {
this.authorizationContextHolder = authorizationContextHolder;
this.i18nSupport = i18nSupport;
this.composer = composer
.stream()
.collect(Collectors.toMap(
comp -> comp.getClass().getName(),
Function.identity()));
this.pages = pageDefinitions
.stream()
.collect(Collectors.toMap(
page -> page.getClass().getName(),
Function.identity()));
}
@Override
public PageDefinition mainPage() {
return this.pages.get(this.mainPageType.getName());
}
@Override
public PageDefinition loginPage() {
return this.pages.get(this.loginPageType.getName());
}
@Override
public boolean validate(final String composerName, final PageContext pageContext) {
if (!this.composer.containsKey(composerName)) {
return false;
}
return this.composer
.get(composerName)
.validate(pageContext);
}
@Override
public void compose(
final Class<? extends TemplateComposer> composerType,
final PageContext pageContext) {
compose(composerType.getName(), pageContext);
}
@Override
public void compose(
final String name,
final PageContext pageContext) {
if (!this.composer.containsKey(name)) {
log.error("No TemplateComposer with name: " + name + " found. Check Spring confiuration and beans");
return;
}
final TemplateComposer composer = this.composer.get(name);
if (composer.validate(pageContext)) {
clear(pageContext.getParent());
try {
composer.compose(pageContext);
} catch (final Exception e) {
log.warn("Failed to compose: {}, pageContext: {}", name, pageContext, e);
}
try {
pageContext.getParent().layout();
} catch (final Exception e) {
log.warn("Failed to layout new composition: {}, pageContext: {}", name, pageContext, e);
}
} else {
log.error(
"Invalid or missing mandatory attributes to handle compose request of ViewComposer: {} pageContext: {}",
name,
pageContext);
}
}
@Override
public void composePage(
final PageDefinition pageDefinition,
final Composite root) {
compose(
pageDefinition.composer(),
pageDefinition.applyPageContext(createPageContext(root)));
}
@Override
public void composePage(
final Class<? extends PageDefinition> pageType,
final Composite root) {
final String pageName = pageType.getName();
if (!this.pages.containsKey(pageName)) {
log.error("Unknown page with name: {}", pageName);
return;
}
final PageDefinition pageDefinition = this.pages.get(pageName);
compose(
pageDefinition.composer(),
pageDefinition.applyPageContext(createPageContext(root)));
}
@Override
public void loadLoginPage(final Composite parent) {
composePage(this.loginPageType, parent);
}
@Override
public void loadMainPage(final Composite parent) {
composePage(this.mainPageType, parent);
}
private PageContext createPageContext(final Composite root) {
return new PageContextImpl(
this.i18nSupport, this, root, root, null);
}
private void clear(final Composite parent) {
if (parent == null) {
return;
}
for (final Control control : parent.getChildren()) {
control.dispose();
}
}
}

View file

@ -0,0 +1,37 @@
/*
* 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.gui.service.page.impl;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
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;
/** Default login page works with the DefaultPageLayout and the
* SEBLogin template */
@Lazy
@Component
public class DefaultLoginPage implements PageDefinition {
@Override
public Class<? extends TemplateComposer> composer() {
return DefaultPageLayout.class;
}
@Override
public PageContext applyPageContext(final PageContext pageContext) {
return pageContext.withAttr(
AttributeKeys.ATTR_PAGE_TEMPLATE_COMPOSER_NAME,
SEBLogin.class.getName());
}
}

View file

@ -0,0 +1,37 @@
/*
* 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.gui.service.page.impl;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
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;
/** Default main page works with the DefaultPageLayout and the
* SEBMainPage template */
@Lazy
@Component
public class DefaultMainPage implements PageDefinition {
@Override
public Class<? extends TemplateComposer> composer() {
return DefaultPageLayout.class;
}
@Override
public PageContext applyPageContext(final PageContext pageContext) {
return pageContext.withAttr(
AttributeKeys.ATTR_PAGE_TEMPLATE_COMPOSER_NAME,
SEBMainPage.class.getName());
}
}

View file

@ -0,0 +1,275 @@
/*
* Copyright (c) 2018 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.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.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
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.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@Lazy
@Component
public class DefaultPageLayout implements TemplateComposer {
private final WidgetFactory widgetFactory;
private final PolyglotPageService polyglotPageService;
private final AuthorizationContextHolder authorizationContextHolder;
private final String sebServerVersion;
public DefaultPageLayout(
final WidgetFactory widgetFactory,
final PolyglotPageService polyglotPageService,
final AuthorizationContextHolder authorizationContextHolder,
@Value("${sebserver.version}") final String sebServerVersion) {
this.widgetFactory = widgetFactory;
this.polyglotPageService = polyglotPageService;
this.authorizationContextHolder = authorizationContextHolder;
this.sebServerVersion = sebServerVersion;
}
@Override
public boolean validate(final PageContext pageContext) {
return pageContext.hasAttribute(AttributeKeys.ATTR_PAGE_TEMPLATE_COMPOSER_NAME);
}
@Override
public void compose(final PageContext pageContext) {
final GridLayout skeletonLayout = new GridLayout();
skeletonLayout.marginBottom = 0;
skeletonLayout.marginLeft = 0;
skeletonLayout.marginRight = 0;
skeletonLayout.marginTop = 0;
skeletonLayout.marginHeight = 0;
skeletonLayout.marginWidth = 0;
skeletonLayout.verticalSpacing = 0;
skeletonLayout.horizontalSpacing = 0;
pageContext.getParent().setLayout(skeletonLayout);
composeHeader(pageContext);
composeLogoBar(pageContext);
composeContent(pageContext);
composeFooter(pageContext);
this.polyglotPageService.setDefaultPageLocale(pageContext.getRoot());
}
private void composeHeader(final PageContext pageContext) {
final Composite header = new Composite(pageContext.getParent(), SWT.NONE);
final GridLayout gridLayout = new GridLayout();
gridLayout.marginRight = 50;
gridLayout.marginLeft = 50;
header.setLayout(gridLayout);
final GridData headerCell = new GridData(SWT.FILL, SWT.TOP, true, false);
headerCell.minimumHeight = 40;
headerCell.heightHint = 40;
header.setLayoutData(headerCell);
header.setData(RWT.CUSTOM_VARIANT, "header");
final Composite headerRight = new Composite(header, SWT.NONE);
headerRight.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, true));
final GridLayout headerRightGrid = new GridLayout(2, false);
headerRightGrid.marginHeight = 0;
headerRightGrid.marginWidth = 0;
headerRightGrid.horizontalSpacing = 20;
headerRight.setLayout(headerRightGrid);
headerRight.setData(RWT.CUSTOM_VARIANT, "header");
if (this.authorizationContextHolder.getAuthorizationContext().isLoggedIn()) {
final Label username = new Label(headerRight, SWT.NONE);
username.setData(RWT.CUSTOM_VARIANT, "header");
username.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, true));
username.setText(this.authorizationContextHolder
.getAuthorizationContext()
.getLoggedInUser()
.get(pageContext::logoutOnError).username);
final Button logout = this.widgetFactory.buttonLocalized(headerRight, "sebserver.logout");
logout.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, true));
logout.setData(RWT.CUSTOM_VARIANT, "header");
logout.addListener(SWT.Selection, event -> {
final boolean logoutSuccessful = this.authorizationContextHolder
.getAuthorizationContext()
.logout();
if (!logoutSuccessful) {
// TODO error handling
}
MainPageState.clear();
// forward to login page with success message
pageContext.forwardToLoginPage(
pageContext.withAttr(AttributeKeys.LGOUT_SUCCESS, "true"));
});
}
}
private void composeLogoBar(final PageContext pageContext) {
final Composite logoBar = new Composite(pageContext.getParent(), SWT.NONE);
final GridData logoBarCell = new GridData(SWT.FILL, SWT.TOP, false, false);
logoBarCell.minimumHeight = 80;
logoBarCell.heightHint = 80;
logoBar.setLayoutData(logoBarCell);
logoBar.setData(RWT.CUSTOM_VARIANT, "logo");
final GridLayout logoBarLayout = new GridLayout(2, false);
logoBarLayout.horizontalSpacing = 0;
logoBarLayout.marginHeight = 0;
logoBar.setLayout(logoBarLayout);
final Composite logo = new Composite(logoBar, SWT.NONE);
final GridData logoCell = new GridData(SWT.LEFT, SWT.CENTER, true, true);
logoCell.minimumHeight = 80;
logoCell.heightHint = 80;
logoCell.minimumWidth = 400;
logoCell.horizontalIndent = 50;
logo.setLayoutData(logoCell);
logo.setData(RWT.CUSTOM_VARIANT, "bgLogo");
final Composite langSupport = new Composite(logoBar, SWT.NONE);
final GridData langSupportCell = new GridData(SWT.RIGHT, SWT.CENTER, false, false);
langSupportCell.heightHint = 20;
logoCell.horizontalIndent = 50;
langSupport.setLayoutData(langSupportCell);
langSupport.setData(RWT.CUSTOM_VARIANT, "logo");
final RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
rowLayout.spacing = 7;
rowLayout.marginRight = 70;
langSupport.setLayout(rowLayout);
this.widgetFactory.createLanguageSelector(pageContext.copyOf(langSupport));
// for (final Locale locale : this.i18nSupport.supportedLanguages()) {
// final LanguageSelection languageSelection = new LanguageSelection(langSupport, locale);
// languageSelection.updateLocale(this.i18nSupport);
// languageSelection.addListener(SWT.MouseDown, event -> {
// this.polyglotPageService.setPageLocale(pageContext.root, languageSelection.locale);
//
// });
// }
}
private void composeContent(final PageContext pageContext) {
final Composite contentBackground = new Composite(pageContext.getParent(), SWT.NONE);
contentBackground.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
contentBackground.setData(RWT.CUSTOM_VARIANT, "bgContent");
final GridLayout innerGrid = new GridLayout();
innerGrid.marginLeft = 50;
innerGrid.marginRight = 50;
innerGrid.marginHeight = 0;
innerGrid.marginWidth = 0;
contentBackground.setLayout(innerGrid);
final Composite content = new Composite(contentBackground, SWT.NONE);
content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
content.setData(RWT.CUSTOM_VARIANT, "content");
final GridLayout contentGrid = new GridLayout();
contentGrid.marginHeight = 0;
contentGrid.marginWidth = 0;
content.setLayout(contentGrid);
final Composite contentInner = new Composite(content, SWT.NONE);
contentInner.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
final GridLayout gridLayout = new GridLayout();
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
contentInner.setLayout(gridLayout);
final String contentComposerName = pageContext.getAttribute(
AttributeKeys.ATTR_PAGE_TEMPLATE_COMPOSER_NAME);
pageContext.composerService().compose(
contentComposerName,
pageContext.copyOf(contentInner));
}
private void composeFooter(final PageContext pageContext) {
final Composite footerBar = new Composite(pageContext.getParent(), SWT.NONE);
final GridData footerCell = new GridData(SWT.FILL, SWT.BOTTOM, false, false);
footerCell.minimumHeight = 30;
footerCell.heightHint = 30;
footerBar.setLayoutData(footerCell);
footerBar.setData(RWT.CUSTOM_VARIANT, "bgFooter");
final GridLayout innerBarGrid = new GridLayout();
innerBarGrid.marginHeight = 0;
innerBarGrid.marginWidth = 0;
innerBarGrid.marginLeft = 50;
innerBarGrid.marginRight = 50;
footerBar.setLayout(innerBarGrid);
final Composite footer = new Composite(footerBar, SWT.NONE);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
footer.setLayoutData(gridData);
final GridLayout footerGrid = new GridLayout(2, false);
footerGrid.marginHeight = 0;
footerGrid.marginWidth = 0;
footerGrid.horizontalSpacing = 0;
footer.setLayout(footerGrid);
footer.setData(RWT.CUSTOM_VARIANT, "footer");
final Composite footerLeft = new Composite(footer, SWT.NONE);
footerLeft.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true));
footerLeft.setData(RWT.CUSTOM_VARIANT, "footer");
RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
rowLayout.marginLeft = 20;
rowLayout.spacing = 20;
footerLeft.setLayout(rowLayout);
final Composite footerRight = new Composite(footer, SWT.NONE);
footerRight.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, true));
footerRight.setData(RWT.CUSTOM_VARIANT, "footer");
rowLayout = new RowLayout(SWT.HORIZONTAL);
rowLayout.marginRight = 20;
footerRight.setLayout(rowLayout);
this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.imprint"));
this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.about"));
this.widgetFactory.labelLocalized(
footerRight,
"footer",
new LocTextKey("sebserver.overall.version", this.sebServerVersion));
}
// private final class LanguageSelection extends Label implements Polyglot {
//
// private static final long serialVersionUID = 8110167162843383940L;
// private final Locale locale;
//
// public LanguageSelection(final Composite parent, final Locale locale) {
// super(parent, SWT.NONE);
// this.locale = locale;
// super.setData(RWT.CUSTOM_VARIANT, "header");
// super.setText("| " + locale.getLanguage().toUpperCase());
// }
//
// @Override
// public void updateLocale(final I18nSupport i18nSupport) {
// super.setVisible(
// !i18nSupport.getCurrentLocale()
// .getLanguage()
// .equals(this.locale.getLanguage()));
// }
// }
}

View file

@ -0,0 +1,49 @@
/*
* 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.gui.service.page.impl;
import javax.servlet.http.HttpSession;
import org.eclipse.rap.rwt.RWT;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity;
public final class MainPageState {
public ActivitySelection activitySelection = Activity.NONE.createSelection();
private MainPageState() {
}
public static MainPageState get() {
try {
final HttpSession httpSession = RWT
.getUISession()
.getHttpSession();
MainPageState mainPageState = (MainPageState) httpSession.getAttribute(SEBMainPage.ATTR_MAIN_PAGE_STATE);
if (mainPageState == null) {
mainPageState = new MainPageState();
httpSession.setAttribute(SEBMainPage.ATTR_MAIN_PAGE_STATE, mainPageState);
}
return mainPageState;
} catch (final Exception e) {
SEBMainPage.log.error("Unexpected error while trying to get MainPageState from user-session");
}
return null;
}
public static void clear() {
final MainPageState mainPageState = get();
mainPageState.activitySelection = Activity.NONE.createSelection();
}
}

View file

@ -0,0 +1,275 @@
/*
* 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.gui.service.page.impl;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.rap.rwt.widgets.DialogCallback;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessageError;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.page.ComposerService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageDefinition;
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent;
import ch.ethz.seb.sebserver.gui.service.widget.Message;
public class PageContextImpl implements PageContext {
private static final Logger log = LoggerFactory.getLogger(PageContextImpl.class);
private final I18nSupport i18nSupport;
private final ComposerService composerService;
private final Composite root;
private final Composite parent;
private final Map<String, String> attributes;
PageContextImpl(
final I18nSupport i18nSupport,
final ComposerService composerService,
final Composite root,
final Composite parent,
final Map<String, String> attributes) {
this.i18nSupport = i18nSupport;
this.composerService = composerService;
this.root = root;
this.parent = parent;
this.attributes = Utils.immutableMapOf(attributes);
}
@Override
public Shell getShell() {
if (this.root == null) {
return null;
}
return this.root.getShell();
}
@Override
public ComposerService composerService() {
return this.composerService;
}
@Override
public Composite getRoot() {
return this.root;
}
@Override
public Composite getParent() {
return this.parent;
}
@Override
public PageContext copyOf(final Composite parent) {
return new PageContextImpl(
this.i18nSupport,
this.composerService,
this.root,
parent,
this.attributes);
}
@Override
public PageContext copyOfAttributes(final PageContext otherContext) {
final Map<String, String> attrs = new HashMap<>();
attrs.putAll(this.attributes);
attrs.putAll(((PageContextImpl) otherContext).attributes);
return new PageContextImpl(
this.i18nSupport,
this.composerService,
this.root,
this.parent,
attrs);
}
@Override
public PageContext withAttr(final String key, final String value) {
final Map<String, String> attrs = new HashMap<>();
attrs.putAll(this.attributes);
attrs.put(key, value);
return new PageContextImpl(
this.i18nSupport,
this.composerService,
this.root,
this.parent, attrs);
}
@Override
public String getAttribute(final String name) {
return this.attributes.get(name);
}
@Override
public String getAttribute(final String name, final String def) {
if (this.attributes.containsKey(name)) {
return this.attributes.get(name);
} else {
return def;
}
}
@Override
public boolean hasAttribute(final String name) {
return this.attributes.containsKey(name);
}
@Override
@SuppressWarnings("unchecked")
public <T extends PageEvent> void publishPageEvent(final T event) {
final Class<? extends PageEvent> typeClass = event.getClass();
final List<PageEventListener<T>> listeners = new ArrayList<>();
ComposerService.traversePageTree(
this.root,
c -> {
final PageEventListener<?> listener =
(PageEventListener<?>) c.getData(PageEventListener.LISTENER_ATTRIBUTE_KEY);
return listener != null && listener.match(typeClass);
},
c -> listeners.add(((PageEventListener<T>) c.getData(PageEventListener.LISTENER_ATTRIBUTE_KEY))));
if (listeners.isEmpty()) {
return;
}
listeners.stream()
.sorted(LISTENER_COMPARATOR)
.forEach(listener -> listener.notify(event));
}
@Override
@SuppressWarnings("serial")
public void applyConfirmDialog(final String confirmMessage, final Runnable onOK) {
final Message messageBox = new Message(
this.root.getShell(),
this.i18nSupport.getText("org.sebserver.dialog.confirm.title"),
this.i18nSupport.getText(confirmMessage),
SWT.OK | SWT.CANCEL);
messageBox.open(new DialogCallback() {
@Override
public void dialogClosed(final int returnCode) {
if (returnCode == SWT.OK) {
try {
onOK.run();
} catch (final Throwable t) {
log.error(
"Unexpected on confirm callback execution. This should not happen, plase secure the given onOK Runnable",
t);
}
}
}
});
}
// public void applyValidationErrorDialog(final Collection<FieldValidationError> validationErrors) {
// final Message messageBox = new Message(
// this.root.getShell(),
// this.i18nSupport.getText("org.sebserver.dialog.validationErrors.title"),
// this.i18nSupport.getText(confirmMessage),
// SWT.OK);
// }
@Override
public void forwardToPage(
final PageDefinition pageDefinition,
final PageContext pageContext) {
this.composerService.compose(
pageDefinition.composer(),
pageDefinition.applyPageContext(pageContext.copyOf(pageContext.getRoot())));
}
@Override
public void forwardToMainPage(final PageContext pageContext) {
forwardToPage(this.composerService.mainPage(), pageContext);
}
@Override
public void forwardToLoginPage(final PageContext pageContext) {
forwardToPage(this.composerService.loginPage(), pageContext);
}
@Override
public void notifyError(final String errorMessage, final Throwable error) {
if (error instanceof APIMessageError) {
final List<APIMessage> errorMessages = ((APIMessageError) error).getErrorMessages();
final MessageBox messageBox = new Message(
getShell(),
this.i18nSupport.getText("sebserver.error.unexpected"),
APIMessage.toHTML(errorMessages),
SWT.ERROR);
messageBox.setMarkupEnabled(true);
messageBox.open(null);
return;
}
final MessageBox messageBox = new Message(
getShell(),
this.i18nSupport.getText("sebserver.error.unexpected"),
error.toString(),
SWT.ERROR);
messageBox.open(null);
}
@Override
public void notifyError(final Throwable error) {
notifyError(error.getMessage(), error);
}
@Override
public <T> T logoutOnError(final Throwable error) {
// just to be sure we leave a clean and proper authorizationContext
try {
((ComposerServiceImpl) this.composerService).authorizationContextHolder
.getAuthorizationContext()
.logout();
} catch (final Exception e) {
log.info("Cleanup logout failed: {}", e.getMessage());
}
MainPageState.clear();
forwardToLoginPage(this.withAttr(
AttributeKeys.AUTHORIZATION_FAILURE,
error.getMessage()));
return null;
}
@Override
public String toString() {
return "PageContextImpl [root=" + this.root + ", parent=" + this.parent + ", attributes=" + this.attributes
+ "]";
}
private static final Comparator<PageEventListener<?>> LISTENER_COMPARATOR =
new Comparator<>() {
@Override
public int compare(final PageEventListener<?> o1, final PageEventListener<?> o2) {
final int x = o1.priority();
final int y = o2.priority();
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
};
}

View file

@ -0,0 +1,147 @@
/*
* 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.gui.service.page.impl;
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.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.service.i18n.I18nSupport;
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.TemplateComposer;
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.widget.Message;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class SEBLogin implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(SEBLogin.class);
private final AuthorizationContextHolder authorizationContextHolder;
private final WidgetFactory widgetFactory;
private final I18nSupport i18nSupport;
public SEBLogin(
final AuthorizationContextHolder authorizationContextHolder,
final WidgetFactory widgetFactory,
final I18nSupport i18nSupport) {
this.authorizationContextHolder = authorizationContextHolder;
this.widgetFactory = widgetFactory;
this.i18nSupport = i18nSupport;
}
@Override
public void compose(final PageContext pageContext) {
final Composite parent = pageContext.getParent();
if (pageContext.hasAttribute((AttributeKeys.LGOUT_SUCCESS))) {
final MessageBox logoutSuccess = new Message(
pageContext.getShell(),
this.i18nSupport.getText("org.sebserver.logout"),
this.i18nSupport.getText("org.sebserver.logout.success.message"),
SWT.ICON_INFORMATION);
logoutSuccess.open(null);
}
final Composite loginGroup = new Composite(parent, SWT.NONE);
final GridLayout rowLayout = new GridLayout();
rowLayout.marginWidth = 20;
rowLayout.marginRight = 100;
loginGroup.setLayout(rowLayout);
loginGroup.setData(RWT.CUSTOM_VARIANT, "login");
final Label name = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.username");
name.setLayoutData(new GridData(300, -1));
name.setAlignment(SWT.BOTTOM);
final Text loginName = new Text(loginGroup, SWT.LEFT | SWT.BORDER);
loginName.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
GridData gridData = new GridData(SWT.FILL, SWT.TOP, false, false);
gridData.verticalIndent = 10;
final Label pwd = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.pwd");
pwd.setLayoutData(gridData);
final Text loginPassword = new Text(loginGroup, SWT.LEFT | SWT.PASSWORD | SWT.BORDER);
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
.getAuthorizationContext(RWT.getUISession().getHttpSession());
button.addListener(SWT.Selection, event -> {
final String username = loginName.getText();
try {
final boolean loggedIn = authorizationContext.login(
username,
loginPassword.getText());
if (loggedIn) {
// Set users locale on page after successful login
this.i18nSupport.setSessionLocale(
authorizationContext
.getLoggedInUser()
.get(pageContext::logoutOnError).locale);
pageContext.forwardToMainPage(pageContext);
} else {
loginError(pageContext, "sebserver.login.failed.message");
}
} catch (final Exception e) {
log.error("Unexpected error while trying to login with user: {}", username, e);
loginError(pageContext, "Unexpected Error. Please call an Administrator");
}
});
loginName.addListener(SWT.KeyDown, event -> {
if (event.character == '\n' || event.character == '\r') {
loginPassword.setFocus();
}
});
loginPassword.addListener(SWT.KeyDown, event -> {
if (event.character == '\n' || event.character == '\r') {
button.setFocus();
}
});
}
private void loginError(
final PageContext pageContext,
final String message) {
final MessageBox error = new Message(
pageContext.getShell(),
this.i18nSupport.getText("sebserver.login.failed.title"),
this.i18nSupport.getText(message, message),
SWT.ERROR);
error.open(null);
pageContext.logoutOnError(new RuntimeException(message));
}
}

View file

@ -0,0 +1,166 @@
/*
* 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.gui.service.page.impl;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitiesPane;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionListener;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType;
@Lazy
@Component
@GuiProfile
public class SEBMainPage implements TemplateComposer {
static final Logger log = LoggerFactory.getLogger(SEBMainPage.class);
public static final String ATTR_MAIN_PAGE_STATE = "MAIN_PAGE_STATE";
private static final int ACTIVITY_PANE_WEIGHT = 20;
private static final int CONTENT_PANE_WEIGHT = 65;
private static final int ACTION_PANE_WEIGHT = 15;
private static final int[] DEFAULT_SASH_WEIGHTS = new int[] {
ACTIVITY_PANE_WEIGHT,
CONTENT_PANE_WEIGHT,
ACTION_PANE_WEIGHT
};
private static final int[] OPENED_SASH_WEIGHTS = new int[] { 0, 100, 0 };
private final WidgetFactory widgetFactory;
public SEBMainPage(final WidgetFactory widgetFactory) {
this.widgetFactory = widgetFactory;
}
@Override
public void compose(final PageContext pageContext) {
MainPageState.clear();
final Composite parent = pageContext.getParent();
parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final SashForm mainSash = new SashForm(parent, SWT.HORIZONTAL);
final GridLayout gridLayout = new GridLayout();
mainSash.setLayout(gridLayout);
mainSash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final Composite nav = new Composite(mainSash, SWT.NONE);
nav.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final GridLayout navLayout = new GridLayout();
navLayout.marginHeight = 20;
navLayout.marginWidth = 0;
nav.setLayout(navLayout);
final Composite content = new Composite(mainSash, SWT.NONE);
content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final GridLayout contentOuterlayout = new GridLayout();
contentOuterlayout.marginHeight = 0;
contentOuterlayout.marginWidth = 0;
content.setLayout(contentOuterlayout);
final Label toggleView = this.widgetFactory.imageButton(
IconButtonType.MAXIMIZE,
content,
new LocTextKey("sebserver.mainpage.maximize.tooltip"),
event -> {
final Label ib = (Label) event.widget;
if ((Boolean) ib.getData("fullScreen")) {
mainSash.setWeights(DEFAULT_SASH_WEIGHTS);
ib.setData("fullScreen", false);
ib.setImage(WidgetFactory.IconButtonType.MAXIMIZE.getImage(ib.getDisplay()));
this.widgetFactory.injectI18n(
ib,
null,
new LocTextKey("sebserver.mainpage.maximize.tooltip"));
} else {
mainSash.setWeights(OPENED_SASH_WEIGHTS);
ib.setData("fullScreen", true);
ib.setImage(WidgetFactory.IconButtonType.MINIMIZE.getImage(ib.getDisplay()));
this.widgetFactory.injectI18n(
ib,
null,
new LocTextKey("sebserver.mainpage.minimize.tooltip"));
}
});
final GridData gridData = new GridData(SWT.RIGHT, SWT.TOP, true, false);
toggleView.setLayoutData(gridData);
toggleView.setData("fullScreen", false);
final Composite contentObjects = new Composite(content, SWT.NONE);
contentObjects.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final GridLayout contentObjectslayout = new GridLayout();
contentObjectslayout.marginHeight = 0;
contentObjectslayout.marginWidth = 0;
contentObjects.setLayout(contentObjectslayout);
contentObjects.setData(PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ActivitySelectionListener() {
@Override
public int priority() {
return 2;
}
@Override
public void notify(final ActivitySelectionEvent event) {
pageContext.composerService().compose(
event.selection.activity.contentPaneComposer,
pageContext.copyOf(contentObjects).withAttr(
event.selection.activity.objectIdentifierAttribute,
event.selection.getObjectIdentifier()));
}
});
final Composite actionPane = new Composite(mainSash, SWT.NONE);
actionPane.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final GridLayout actionPaneGrid = new GridLayout();
actionPane.setLayout(actionPaneGrid);
actionPane.setData(RWT.CUSTOM_VARIANT, "actionPane");
actionPane.setData(PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ActivitySelectionListener() {
@Override
public int priority() {
return 1;
}
@Override
public void notify(final ActivitySelectionEvent event) {
pageContext.composerService().compose(
event.selection.activity.actionPaneComposer,
pageContext.copyOf(actionPane).withAttr(
event.selection.activity.objectIdentifierAttribute,
event.selection.getObjectIdentifier()));
}
});
pageContext.composerService().compose(
ActivitiesPane.class,
pageContext.copyOf(nav));
mainSash.setWeights(DEFAULT_SASH_WEIGHTS);
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018 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.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Label;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
@Lazy
@Component
public class TODOTemplate implements TemplateComposer {
@Override
public void compose(final PageContext composerCtx) {
final Label tree = new Label(composerCtx.getParent(), SWT.NONE);
tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
tree.setText("[TODO]");
}
}

View file

@ -0,0 +1,96 @@
/*
* 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.gui.service.remote.webservice;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLContext;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.ResourceUtils;
import ch.ethz.seb.sebserver.gbl.profile.DevGuiProfile;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.profile.ProdGuiProfile;
@Configuration
@GuiProfile
public class WebserviceConnectionConfig {
/** A ClientHttpRequestFactory for development profile with no TSL SSL protocol and
* not following redirects on redirect responses.
*
* @return ClientHttpRequestFactory bean for development profiles */
@Bean
@DevGuiProfile
public ClientHttpRequestFactory clientHttpRequestFactory() {
return new SimpleClientHttpRequestFactory() {
@Override
protected void prepareConnection(final HttpURLConnection connection, final String httpMethod)
throws IOException {
super.prepareConnection(connection, httpMethod);
connection.setInstanceFollowRedirects(false);
}
};
}
/** A ClientHttpRequestFactory used in production with TSL SSL configuration.
*
* NOTE:
* environment property: sebserver.gui.truststore.pwd is expected to have the correct truststore password set
* environment property: sebserver.gui.truststore.type is expected to set to the correct type of truststore
* truststore.jks is expected to be on the classpath containing all trusted certificates for request
* to SSL secured SEB Server webservice
*
* @return ClientHttpRequestFactory with TLS / SSL configuration
* @throws IOException
* @throws FileNotFoundException
* @throws CertificateException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException */
@Bean
@ProdGuiProfile
public ClientHttpRequestFactory clientHttpRequestFactoryTLS(final Environment env) throws KeyManagementException,
NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException {
final char[] password = env
.getProperty("sebserver.gui.truststore.pwd")
.toCharArray();
final SSLContext sslContext = SSLContextBuilder
.create()
.loadTrustMaterial(ResourceUtils.getFile(
"classpath:truststore.jks"),
password)
.build();
final HttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.build();
return new HttpComponentsClientHttpRequestFactory(client);
}
}

View file

@ -28,8 +28,8 @@ import org.springframework.web.client.RestClientResponseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService.SortOrder;
@ -58,9 +58,10 @@ public abstract class RestCall<T> {
}
void init(final RestService restService, final JSONMapper jsonMapper) {
RestCall<T> init(final RestService restService, final JSONMapper jsonMapper) {
this.restService = restService;
this.jsonMapper = jsonMapper;
return this;
}
protected Result<T> exchange(final RestCallBuilder builder) {
@ -107,6 +108,10 @@ public abstract class RestCall<T> {
}
}
public RestCallBuilder newBuilder() {
return new RestCallBuilder();
}
public final class RestCallBuilder {
private final HttpHeaders httpHeaders = new HttpHeaders();
@ -117,7 +122,7 @@ public abstract class RestCall<T> {
RestCallBuilder() {
this.httpHeaders.set(
HttpHeaders.CONTENT_TYPE,
RestCall.this.contentType.getType());
RestCall.this.contentType.toString());
}
public RestCallBuilder withHeaders(final HttpHeaders headers) {
@ -165,7 +170,7 @@ public abstract class RestCall<T> {
return this;
}
public final Result<T> exchange() {
public final Result<T> call() {
return RestCall.this.exchange(this);
}

View file

@ -11,9 +11,10 @@ package ch.ethz.seb.sebserver.gui.service.remote.webservice.api;
import java.util.ArrayList;
import java.util.List;
import ch.ethz.seb.sebserver.gbl.model.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessageError;
public class RestCallError extends RuntimeException {
public class RestCallError extends RuntimeException implements APIMessageError {
private static final long serialVersionUID = -5201349295667957490L;
@ -27,6 +28,7 @@ public class RestCallError extends RuntimeException {
super(message);
}
@Override
public List<APIMessage> getErrorMessages() {
return this.errors;
}

View file

@ -8,6 +8,10 @@
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
@ -15,10 +19,10 @@ import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import ch.ethz.seb.sebserver.gbl.JSONMapper;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIBuilderSupplier;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService;
@Lazy
@Service
@ -28,17 +32,23 @@ public class RestService {
private static final Logger log = LoggerFactory.getLogger(RestService.class);
private final AuthorizationContextHolder authorizationContextHolder;
private final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier;
private final JSONMapper jsonMapper;
private final WebserviceURIService webserviceURIBuilderSupplier;
private final Map<String, RestCall<?>> calls;
public RestService(
final AuthorizationContextHolder authorizationContextHolder,
final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier,
final JSONMapper jsonMapper) {
final WebserviceURIService webserviceURIBuilderSupplier,
final JSONMapper jsonMapper,
final Collection<RestCall<?>> calls) {
this.authorizationContextHolder = authorizationContextHolder;
this.webserviceURIBuilderSupplier = webserviceURIBuilderSupplier;
this.jsonMapper = jsonMapper;
this.calls = calls
.stream()
.collect(Collectors.toMap(
call -> call.getClass().getName(),
call -> call.init(this, jsonMapper)));
}
public RestTemplate getWebserviceAPIRestTemplate() {
@ -51,15 +61,19 @@ public class RestService {
return this.webserviceURIBuilderSupplier.getBuilder();
}
@SuppressWarnings("unchecked")
public <T> RestCall<T> getRestCall(final Class<? extends RestCall<T>> type) {
try {
final RestCall<T> restCall = type.getDeclaredConstructor().newInstance();
restCall.init(this, this.jsonMapper);
return restCall;
} catch (final Exception e) {
log.error("Error while trying to create RestCall of type: {}", type, e);
return new BuildErrorCall<>(e);
return (RestCall<T>) this.calls.get(type.getName());
}
public <T> RestCall<T>.RestCallBuilder getBuilder(final Class<? extends RestCall<T>> type) {
@SuppressWarnings("unchecked")
final RestCall<T> restCall = (RestCall<T>) this.calls.get(type.getName());
if (restCall == null) {
return null;
}
return restCall.newBuilder();
}
}

View file

@ -0,0 +1,39 @@
/*
* 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.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.SEBServerRestEndpoints;
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 GetInstitutionNames extends RestCall<List<EntityName>> {
protected GetInstitutionNames() {
super(
new TypeReference<List<EntityName>>() {
},
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
SEBServerRestEndpoints.ENDPOINT_INSTITUTION + "/names");
}
}

View file

@ -34,7 +34,9 @@ public class CurrentUser {
public UserInfo get() {
if (isAvailable()) {
return this.authContext.getLoggedInUser();
return this.authContext
.getLoggedInUser()
.getOrThrow();
}
log.warn("Current user requested but no user is currently logged in");

View file

@ -22,7 +22,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
@ -41,6 +43,7 @@ import org.springframework.web.client.RestTemplate;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
@Lazy
@Component
@ -50,23 +53,23 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
private static final Logger log = LoggerFactory.getLogger(OAuth2AuthorizationContextHolder.class);
private static final String CONTEXT_HOLDER_ATTRIBUTE = "CONTEXT_HOLDER_ATTRIBUTE";
private static final String OAUTH_TOKEN_URI_PATH = "oauth/token"; // TODO to config properties?
private static final String OAUTH_REVOKE_TOKEN_URI_PATH = "/oauth/revoke-token"; // TODO to config properties?
private static final String CURRENT_USER_URI_PATH = "/user/me"; // TODO to config properties?
private final String guiClientId;
private final String guiClientSecret;
private final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier;
private final WebserviceURIService webserviceURIService;
private final ClientHttpRequestFactory clientHttpRequestFactory;
@Autowired
public OAuth2AuthorizationContextHolder(
@Value("${sebserver.gui.webservice.clientId}") final String guiClientId,
@Value("${sebserver.gui.webservice.clientSecret}") final String guiClientSecret,
final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier) {
final WebserviceURIService webserviceURIService,
final ClientHttpRequestFactory clientHttpRequestFactory) {
this.guiClientId = guiClientId;
this.guiClientSecret = guiClientSecret;
this.webserviceURIBuilderSupplier = webserviceURIBuilderSupplier;
this.webserviceURIService = webserviceURIService;
this.clientHttpRequestFactory = clientHttpRequestFactory;
}
@Override
@ -85,7 +88,8 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
context = new OAuth2AuthorizationContext(
this.guiClientId,
this.guiClientSecret,
this.webserviceURIBuilderSupplier);
this.webserviceURIService,
this.clientHttpRequestFactory);
session.setAttribute(CONTEXT_HOLDER_ATTRIBUTE, context);
}
@ -132,7 +136,7 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
private static final String GRANT_TYPE = "password";
private static final List<String> SCOPES = Collections.unmodifiableList(
Arrays.asList("web-service-api-read", "web-service-api-write"));
Arrays.asList("read", "write"));
private boolean valid = true;
@ -141,34 +145,26 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
private final String revokeTokenURI;
private final String currentUserURI;
private UserInfo loggedInUser = null;
private Result<UserInfo> loggedInUser = null;
OAuth2AuthorizationContext(
final String guiClientId,
final String guiClientSecret,
final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier) {
final WebserviceURIService webserviceURIService,
final ClientHttpRequestFactory clientHttpRequestFactory) {
this.resource = new ResourceOwnerPasswordResourceDetails();
this.resource.setAccessTokenUri(
webserviceURIBuilderSupplier
.getBuilder()
.path(OAUTH_TOKEN_URI_PATH)
.toUriString() /* restCallBuilder.withPath(OAUTH_TOKEN_URI_PATH) */);
this.resource.setAccessTokenUri(webserviceURIService.getOAuthTokenURI());
this.resource.setClientId(guiClientId);
this.resource.setClientSecret(guiClientSecret);
this.resource.setGrantType(GRANT_TYPE);
this.resource.setScope(SCOPES);
this.restTemplate = new DisposableOAuth2RestTemplate(this.resource);
this.restTemplate.setRequestFactory(clientHttpRequestFactory);
this.revokeTokenURI = webserviceURIBuilderSupplier
.getBuilder()
.path(OAUTH_REVOKE_TOKEN_URI_PATH)
.toUriString(); //restCallBuilder.withPath(OAUTH_REVOKE_TOKEN_URI_PATH);
this.currentUserURI = webserviceURIBuilderSupplier
.getBuilder()
.path(CURRENT_USER_URI_PATH)
.toUriString(); //restCallBuilder.withPath(CURRENT_USER_URI_PATH);
this.revokeTokenURI = webserviceURIService.getOAuthRevokeTokenURI();
this.currentUserURI = webserviceURIService.getCurrentUserRequestURI();
}
@Override
@ -227,7 +223,7 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
}
@Override
public UserInfo getLoggedInUser() {
public Result<UserInfo> getLoggedInUser() {
if (this.loggedInUser != null) {
return this.loggedInUser;
}
@ -237,18 +233,27 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
try {
if (isValid() && isLoggedIn()) {
final ResponseEntity<UserInfo> response =
this.restTemplate.getForEntity(this.currentUserURI, UserInfo.class);
this.loggedInUser = response.getBody();
return this.loggedInUser;
this.restTemplate
.getForEntity(this.currentUserURI, UserInfo.class);
if (response.getStatusCode() == HttpStatus.OK) {
this.loggedInUser = Result.of(response.getBody());
return this.loggedInUser;
} else {
log.error("Unexpected error response: {}", response);
return Result.ofError(new IllegalStateException(
"Http Request responded with status: " + response.getStatusCode()));
}
} else {
throw new IllegalStateException("Logged in User requested on invalid or not logged in ");
return Result.ofError(
new IllegalStateException("Logged in User requested on invalid or not logged in "));
}
} catch (final AccessDeniedException | OAuth2AccessDeniedException ade) {
log.error("Acccess denied while trying to request logged in User from API", ade);
throw ade;
return Result.ofError(ade);
} catch (final Exception e) {
log.error("Unexpected error while trying to request logged in User from API", e);
throw new RuntimeException("Unexpected error while trying to request logged in User from API", e);
return Result.ofError(
new RuntimeException("Unexpected error while trying to request logged in User from API", e));
}
}
@ -258,8 +263,9 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
return false;
}
return getLoggedInUser().roles
.contains(role.name());
return getLoggedInUser()
.getOrThrow().roles
.contains(role.name());
}
}
}

View file

@ -12,6 +12,7 @@ import org.springframework.web.client.RestTemplate;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.util.Result;
public interface SEBServerAuthorizationContext {
@ -23,7 +24,7 @@ public interface SEBServerAuthorizationContext {
boolean logout();
UserInfo getLoggedInUser();
Result<UserInfo> getLoggedInUser();
public boolean hasRole(UserRole role);

View file

@ -1,40 +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.gui.service.remote.webservice.auth;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
@Lazy
@Component
@GuiProfile
public class WebserviceURIBuilderSupplier {
private final UriComponentsBuilder webserviceURIBuilder;
public WebserviceURIBuilderSupplier(
@Value("${sebserver.gui.webservice.protocol}") final String webserviceProtocol,
@Value("${sebserver.gui.webservice.address}") final String webserviceServerAdress,
@Value("${sebserver.gui.webservice.portol}") final String webserviceServerPort,
@Value("${sebserver.gui.webservice.apipath}") final String webserviceAPIPath) {
this.webserviceURIBuilder = UriComponentsBuilder
.fromHttpUrl(webserviceProtocol + "://" + webserviceServerAdress)
.port(webserviceServerPort)
.path(webserviceAPIPath);
}
public UriComponentsBuilder getBuilder() {
return this.webserviceURIBuilder.cloneBuilder();
}
}

View file

@ -0,0 +1,63 @@
/*
* 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.gui.service.remote.webservice.auth;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
@Component
@GuiProfile
public class WebserviceURIService {
private static final String OAUTH_TOKEN_URI_PATH = "oauth/token"; // TODO to config properties?
private static final String OAUTH_REVOKE_TOKEN_URI_PATH = "/oauth/revoke-token"; // TODO to config properties?
private static final String CURRENT_USER_URI_PATH = SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me";
private final String webserviceServerAddress;
private final UriComponentsBuilder webserviceURIBuilder;
public WebserviceURIService(
@Value("${sebserver.gui.webservice.protocol}") final String webserviceProtocol,
@Value("${sebserver.gui.webservice.address}") final String webserviceServerAdress,
@Value("${sebserver.gui.webservice.port}") final String webserviceServerPort,
@Value("${sebserver.gui.webservice.apipath}") final String webserviceAPIPath) {
this.webserviceServerAddress = webserviceProtocol + "://" + webserviceServerAdress + ":" + webserviceServerPort;
this.webserviceURIBuilder = UriComponentsBuilder
.fromHttpUrl(webserviceProtocol + "://" + webserviceServerAdress)
.port(webserviceServerPort)
.path(webserviceAPIPath);
}
public UriComponentsBuilder getBuilder() {
return this.webserviceURIBuilder.cloneBuilder();
}
public String getOAuthTokenURI() {
return UriComponentsBuilder.fromHttpUrl(this.webserviceServerAddress)
.path(OAUTH_TOKEN_URI_PATH)
.toUriString();
}
public String getOAuthRevokeTokenURI() {
return UriComponentsBuilder.fromHttpUrl(this.webserviceServerAddress)
.path(OAUTH_REVOKE_TOKEN_URI_PATH)
.toUriString();
}
public String getCurrentUserRequestURI() {
return getBuilder()
.path(CURRENT_USER_URI_PATH)
.toUriString();
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018 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.widget;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
public class Message extends MessageBox {
private static final long serialVersionUID = 6973272221493264432L;
public Message(final Shell parent, final String title, final String message, final int type) {
super(parent, type);
super.setText(title);
super.setMessage(message);
}
@Override
protected void prepareOpen() {
super.prepareOpen();
final GridLayout layout = (GridLayout) super.shell.getLayout();
layout.marginTop = 10;
layout.marginBottom = 10;
super.shell.setData(RWT.CUSTOM_VARIANT, "message");
final Rectangle bounds = super.shell.getBounds();
if (bounds.width < 400) {
bounds.x = bounds.x - (400 - bounds.width) / 2;
bounds.width = 400;
super.shell.setBounds(bounds);
}
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2018 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.widget;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
public class SingleSelection extends Combo {
private static final long serialVersionUID = 6522063655406404279L;
final List<String> valueMapping;
final List<String> keyMapping;
public SingleSelection(final Composite parent, final List<Tuple<String>> mapping) {
super(parent, SWT.READ_ONLY);
this.valueMapping = mapping.stream()
.map(t -> t._2)
.collect(Collectors.toList());
this.keyMapping = mapping.stream()
.map(t -> t._1)
.collect(Collectors.toList());
super.setItems(this.valueMapping.toArray(new String[mapping.size()]));
}
public void select(final String key) {
final int selectionindex = this.keyMapping.indexOf(key);
if (selectionindex < 0) {
return;
}
super.select(selectionindex);
}
public String getSelectionValue() {
final int selectionindex = super.getSelectionIndex();
if (selectionindex < 0) {
return null;
}
return this.keyMapping.get(selectionindex);
}
}

View file

@ -0,0 +1,470 @@
/*
* Copyright (c) 2018 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.widget;
import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.POLYGLOT_TREE_ITEM_TEXT_DATA_KEY;
import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.POLYGLOT_WIDGET_FUNCTION_KEY;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
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.i18n.PolyglotPageService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
@Lazy
@Service
@GuiProfile
public class WidgetFactory {
private static final Logger log = LoggerFactory.getLogger(WidgetFactory.class);
public enum IconButtonType {
MAXIMIZE("maximize.png"),
MINIMIZE("minimize.png"),
SAVE_ACTION("saveAction.png"),
NEW_ACTION("newAction.png"),
DELETE_ACTION("deleteAction.png"),
;
private String fileName;
private ImageData image = null;
private IconButtonType(final String fileName) {
this.fileName = fileName;
}
public Image getImage(final Device device) {
if (this.image == null) {
try {
final InputStream resourceAsStream =
WidgetFactory.class.getResourceAsStream("/static/images/" + this.fileName);
this.image = new ImageData(resourceAsStream);
} catch (final Exception e) {
log.error("Failed to load resource image: {}", this.fileName, e);
}
}
return new Image(device, this.image);
}
}
private final PolyglotPageService polyglotPageService;
private final I18nSupport i18nSupport;
public WidgetFactory(final PolyglotPageService polyglotPageService) {
this.polyglotPageService = polyglotPageService;
this.i18nSupport = polyglotPageService.getI18nSupport();
}
public Button buttonLocalized(final Composite parent, final String locTextKey) {
final Button button = new Button(parent, SWT.NONE);
this.injectI18n(button, new LocTextKey(locTextKey));
return button;
}
public Button buttonLocalized(final Composite parent, final LocTextKey locTextKey) {
final Button button = new Button(parent, SWT.NONE);
this.injectI18n(button, locTextKey);
return button;
}
public Button buttonLocalized(final Composite parent, final String style, final String locTextKey) {
final Button button = new Button(parent, SWT.NONE);
this.injectI18n(button, new LocTextKey(locTextKey));
button.setData(RWT.CUSTOM_VARIANT, style);
return button;
}
public Label label(final Composite parent, final String text) {
final Label label = new Label(parent, SWT.NONE);
label.setText(text);
return label;
}
public Label labelLocalized(final Composite parent, final String locTextKey) {
final Label label = new Label(parent, SWT.NONE);
this.injectI18n(label, new LocTextKey(locTextKey));
return label;
}
public Label labelLocalized(final Composite parent, final LocTextKey locTextKey) {
final Label label = new Label(parent, SWT.NONE);
this.injectI18n(label, locTextKey);
return label;
}
public Label labelLocalized(final Composite parent, final String style, final LocTextKey locTextKey) {
final Label label = new Label(parent, SWT.NONE);
this.injectI18n(label, locTextKey);
label.setData(RWT.CUSTOM_VARIANT, style);
return label;
}
public Label labelLocalized(
final Composite parent,
final LocTextKey locTextKey,
final LocTextKey locToolTextKey) {
final Label label = new Label(parent, SWT.NONE);
this.injectI18n(label, locTextKey, locToolTextKey);
return label;
}
public Label labelLocalized(
final Composite parent,
final String style,
final LocTextKey locTextKey,
final LocTextKey locToolTextKey) {
final Label label = new Label(parent, SWT.NONE);
this.injectI18n(label, locTextKey, locToolTextKey);
label.setData(RWT.CUSTOM_VARIANT, style);
return label;
}
public Tree treeLocalized(final Composite parent, final int style) {
final Tree tree = new Tree(parent, SWT.SINGLE | SWT.FULL_SELECTION);
this.injectI18n(tree);
return tree;
}
public TreeItem treeItemLocalized(final Tree parent, final String locTextKey) {
final TreeItem item = new TreeItem(parent, SWT.NONE);
this.injectI18n(item, new LocTextKey(locTextKey));
return item;
}
public TreeItem treeItemLocalized(final Tree parent, final LocTextKey locTextKey) {
final TreeItem item = new TreeItem(parent, SWT.NONE);
this.injectI18n(item, locTextKey);
return item;
}
public TreeItem treeItemLocalized(final TreeItem parent, final String locTextKey) {
final TreeItem item = new TreeItem(parent, SWT.NONE);
this.injectI18n(item, new LocTextKey(locTextKey));
return item;
}
public TreeItem treeItemLocalized(final TreeItem parent, final LocTextKey locTextKey) {
final TreeItem item = new TreeItem(parent, SWT.NONE);
this.injectI18n(item, locTextKey);
return item;
}
public Table tableLocalized(final Composite parent) {
final Table table = new Table(parent, SWT.NONE);
this.injectI18n(table);
return table;
}
public TableColumn tableColumnLocalized(final Table table, final String locTextKey) {
final TableColumn tableColumn = new TableColumn(table, SWT.NONE);
this.injectI18n(tableColumn, new LocTextKey(locTextKey));
return tableColumn;
}
public Label labelSeparator(final Composite parent) {
final Label label = new Label(parent, SWT.SEPARATOR);
return label;
}
public Label imageButton(
final IconButtonType type,
final Composite parent,
final LocTextKey toolTip,
final Listener listener) {
final Label imageButton = labelLocalized(parent, (LocTextKey) null, toolTip);
imageButton.setData(RWT.CUSTOM_VARIANT, "imageButton");
imageButton.setImage(type.getImage(parent.getDisplay()));
if (listener != null) {
imageButton.addListener(SWT.MouseDown, listener);
}
return imageButton;
}
public Label formLabelLocalized(final Composite parent, final String locTextKey) {
final Label label = labelLocalized(parent, locTextKey);
final GridData gridData = new GridData(SWT.RIGHT, SWT.CENTER, true, false);
label.setLayoutData(gridData);
return label;
}
public Label formValueLabel(final Composite parent, final String value, final int span) {
final Label label = new Label(parent, SWT.NONE);
label.setText(value);
final GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, true, false, span, 1);
label.setLayoutData(gridData);
return label;
}
public Text formTextInput(final Composite parent, final String value) {
return formTextInput(parent, value, 1, 1);
}
public Text formTextInput(final Composite parent, final String value, final int hspan, final int vspan) {
final Text textInput = new Text(parent, SWT.LEFT | SWT.BORDER);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, hspan, vspan);
gridData.heightHint = 15;
textInput.setLayoutData(gridData);
textInput.setText(value);
return textInput;
}
public Combo formSingleSelectionLocalized(
final Composite parent,
final String selection,
final List<Tuple<String>> items) {
return formSingleSelectionLocalized(parent, selection, items, 1, 1);
}
public Combo formSingleSelectionLocalized(
final Composite parent,
final String selection,
final List<Tuple<String>> items,
final int hspan, final int vspan) {
final SingleSelection combo = singleSelectionLocalized(parent, items);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, hspan, vspan);
gridData.heightHint = 25;
combo.setLayoutData(gridData);
combo.select(selection);
return combo;
}
public void formEmpty(final Composite parent) {
formEmpty(parent, 1, 1);
}
public void formEmpty(final Composite parent, final int hspan, final int vspan) {
final Label empty = new Label(parent, SWT.LEFT);
empty.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, hspan, vspan));
empty.setText("");
}
public SingleSelection singleSelectionLocalized(
final Composite parent,
final List<Tuple<String>> items) {
final SingleSelection combo = new SingleSelection(parent, items);
this.injectI18n(combo, combo.valueMapping);
return combo;
}
public void injectI18n(final Label label, final LocTextKey locTextKey) {
injectI18n(label, locTextKey, null);
}
public void injectI18n(final Label label, final LocTextKey locTextKey, final LocTextKey locToolTipKey) {
final Consumer<Label> labelFunction = labelFunction(locTextKey, locToolTipKey, this.i18nSupport);
label.setData(POLYGLOT_WIDGET_FUNCTION_KEY, labelFunction);
labelFunction.accept(label);
}
public void injectI18n(final Button button, final LocTextKey locTextKey) {
injectI18n(button, locTextKey, null);
}
public void injectI18n(final Button button, final LocTextKey locTextKey, final LocTextKey locToolTipKey) {
final Consumer<Button> buttonFunction = buttonFunction(locTextKey, locToolTipKey, this.i18nSupport);
button.setData(POLYGLOT_WIDGET_FUNCTION_KEY, buttonFunction);
buttonFunction.accept(button);
}
public void injectI18n(final Tree tree) {
tree.setData(POLYGLOT_WIDGET_FUNCTION_KEY, treeFunction(this.i18nSupport));
}
public void injectI18n(final TreeItem treeItem, final LocTextKey locTextKey) {
treeItem.setData(POLYGLOT_TREE_ITEM_TEXT_DATA_KEY, locTextKey);
treeItem.setText(this.i18nSupport.getText(locTextKey));
}
public void injectI18n(final Table table) {
table.setData(POLYGLOT_WIDGET_FUNCTION_KEY, tableFunction(this.i18nSupport));
}
public void injectI18n(final TableColumn tableColumn, final LocTextKey locTextKey) {
tableColumn.setData(POLYGLOT_TREE_ITEM_TEXT_DATA_KEY, locTextKey);
tableColumn.setText(this.i18nSupport.getText(locTextKey));
}
public void injectI18n(final TableItem tableItem, final LocTextKey... locTextKey) {
if (locTextKey == null) {
return;
}
tableItem.setData(POLYGLOT_TREE_ITEM_TEXT_DATA_KEY, locTextKey);
for (int i = 0; i < locTextKey.length; i++) {
tableItem.setText(i, this.i18nSupport.getText(locTextKey[i]));
}
}
public void injectI18n(final Combo combo, final List<String> items) {
final Consumer<Combo> comboFunction = comboFunction(items, this.i18nSupport);
combo.setData(POLYGLOT_WIDGET_FUNCTION_KEY, comboFunction);
comboFunction.accept(combo);
}
public void createLanguageSelector(final PageContext composerCtx) {
for (final Locale locale : this.i18nSupport.supportedLanguages()) {
final Label languageSelection = new Label(composerCtx.getParent(), SWT.NONE);
languageSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY,
langSelectionLabelFunction(locale, this.i18nSupport));
languageSelection.setData(RWT.CUSTOM_VARIANT, "header");
languageSelection.setText("| " + locale.getLanguage().toUpperCase());
//languageSelection.updateLocale(this.i18nSupport);
languageSelection.addListener(SWT.MouseDown, event -> {
this.polyglotPageService.setPageLocale(composerCtx.getRoot(), locale);
});
}
}
private static Consumer<Tree> treeFunction(final I18nSupport i18nSupport) {
return tree -> updateLocale(tree.getItems(), i18nSupport);
}
private static Consumer<Table> tableFunction(final I18nSupport i18nSupport) {
return table -> {
updateLocale(table.getColumns(), i18nSupport);
updateLocale(table.getItems(), i18nSupport);
};
}
private static final Consumer<Label> langSelectionLabelFunction(
final Locale locale,
final I18nSupport i18nSupport) {
return label -> label.setVisible(
!i18nSupport.getCurrentLocale()
.getLanguage()
.equals(locale.getLanguage()));
}
private static final Consumer<Label> labelFunction(
final LocTextKey locTextKey,
final LocTextKey locToolTipKey,
final I18nSupport i18nSupport) {
return label -> {
if (locTextKey != null) {
label.setText(i18nSupport.getText(locTextKey));
}
if (locToolTipKey != null) {
label.setToolTipText(i18nSupport.getText(locToolTipKey));
}
};
}
private static final Consumer<Combo> comboFunction(
final List<String> items,
final I18nSupport i18nSupport) {
return combo -> {
int i = 0;
final Iterator<String> iterator = items.iterator();
while (iterator.hasNext()) {
combo.setItem(i, i18nSupport.getText(iterator.next()));
i++;
}
};
}
private static final Consumer<Button> buttonFunction(
final LocTextKey locTextKey,
final LocTextKey locToolTipKey,
final I18nSupport i18nSupport) {
return button -> {
if (locTextKey != null) {
button.setText(i18nSupport.getText(locTextKey));
}
if (locToolTipKey != null) {
button.setToolTipText(i18nSupport.getText(locToolTipKey));
}
};
}
private static final void updateLocale(final TreeItem[] items, final I18nSupport i18nSupport) {
if (items == null) {
return;
}
for (final TreeItem childItem : items) {
final LocTextKey locTextKey = (LocTextKey) childItem.getData(POLYGLOT_TREE_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
childItem.setText(i18nSupport.getText(locTextKey));
}
updateLocale(childItem.getItems(), i18nSupport);
}
}
private static void updateLocale(final TableItem[] items, final I18nSupport i18nSupport) {
if (items == null) {
return;
}
for (final TableItem childItem : items) {
final LocTextKey[] locTextKey = (LocTextKey[]) childItem.getData(POLYGLOT_TREE_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
for (int i = 0; i < locTextKey.length; i++) {
if (locTextKey[i] != null) {
childItem.setText(i, i18nSupport.getText(locTextKey[i]));
}
}
}
}
}
private static void updateLocale(final TableColumn[] columns, final I18nSupport i18nSupport) {
if (columns == null) {
return;
}
for (final TableColumn childItem : columns) {
final LocTextKey locTextKey = (LocTextKey) childItem.getData(POLYGLOT_TREE_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
childItem.setText(i18nSupport.getText(locTextKey));
}
}
}
}

View file

@ -43,19 +43,19 @@ public interface BulkActionSupportDAO<T extends Entity> {
return (this instanceof ActivatableEntityDAO)
? ((ActivatableEntityDAO<?, ?>) this).setActive(all, true)
.map(BulkActionSupportDAO::transformResult)
.getOrHandleError(error -> handleBulkActionError(error, all))
.get(error -> handleBulkActionError(error, all))
: Collections.emptyList();
case DEACTIVATE:
return (this instanceof ActivatableEntityDAO)
? ((ActivatableEntityDAO<?, ?>) this).setActive(all, false)
.map(BulkActionSupportDAO::transformResult)
.getOrHandleError(error -> handleBulkActionError(error, all))
.get(error -> handleBulkActionError(error, all))
: Collections.emptyList();
case HARD_DELETE:
return (this instanceof EntityDAO)
? ((EntityDAO<?, ?>) this).delete(all)
.map(BulkActionSupportDAO::transformResult)
.getOrHandleError(error -> handleBulkActionError(error, all))
.get(error -> handleBulkActionError(error, all))
: Collections.emptyList();
}

View file

@ -19,7 +19,7 @@ import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName;
import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.ModelIdAware;
import ch.ethz.seb.sebserver.gbl.util.Result;
@ -66,12 +66,12 @@ public interface EntityDAO<T extends Entity, M extends ModelIdAware> {
Result<Collection<T>> loadEntities(Collection<EntityKey> keys);
@Transactional(readOnly = true)
default Result<Collection<EntityKeyAndName>> loadEntityNames(final Collection<EntityKey> keys) {
default Result<Collection<EntityName>> loadEntityNames(final Collection<EntityKey> keys) {
return Result.tryCatch(() -> {
return loadEntities(keys)
.getOrThrow()
.stream()
.map(entity -> new EntityKeyAndName(
.map(entity -> new EntityName(
entity.entityType(),
entity.getModelId(),
entity.getName()))

View file

@ -11,7 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import org.joda.time.DateTime;
import org.springframework.util.MultiValueMap;
import ch.ethz.seb.sebserver.gbl.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;

View file

@ -32,8 +32,8 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.model.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.model.APIMessage.ErrorMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;

View file

@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@ -219,6 +220,7 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter
log.warn("Unauthorized Request: {} : Redirect to login after unauthorized request",
request.getRequestURI());
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.sendRedirect(this.redirect);
}
}

View file

@ -31,8 +31,8 @@ public class WebServiceUserDetails implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
return this.userDAO.sebServerUserByUsername(username)
.getOrHandleError(t -> {
throw new UsernameNotFoundException("No User with name: " + username + " found", t);
.get(error -> {
throw new UsernameNotFoundException("No User with name: " + username + " found", error);
});
}

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
@ -26,8 +27,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import ch.ethz.seb.sebserver.gbl.model.APIMessage;
import ch.ethz.seb.sebserver.gbl.model.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationException;
@ -47,7 +48,8 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
final WebRequest request) {
log.error("Unexpected generic error catched at the API endpoint: ", ex);
return new ResponseEntity<>(APIMessage.ErrorMessage.GENERIC.of(ex.getMessage()), status);
final List<APIMessage> errors = Arrays.asList(APIMessage.ErrorMessage.GENERIC.of(ex.getMessage()));
return new ResponseEntity<>(errors, status);
}
@Override

View file

@ -28,11 +28,11 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName;
import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Page;
@ -124,7 +124,7 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt
method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Collection<EntityKeyAndName> getNames(
public Collection<EntityName> getNames(
@RequestParam(
name = Entity.FILTER_ATTR_INSTITUTION,
required = true,

View file

@ -30,8 +30,9 @@ 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.gbl.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
@ -58,7 +59,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
@WebServiceProfile
@RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_EXAM_ADMINISTRATION)
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_EXAM_ADMINISTRATION)
public class ExamAdministrationController extends ActivatableEntityController<Exam, Exam> {
private final ExamDAO examDAO;

View file

@ -13,7 +13,8 @@ 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.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport;
@ -27,7 +28,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
@WebServiceProfile
@RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_INSTITUTION)
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
public class InstitutionController extends ActivatableEntityController<Institution, Institution> {
private final InstitutionDAO institutionDAO;

View file

@ -21,7 +21,8 @@ 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.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@ -37,7 +38,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
@WebServiceProfile
@RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_LMS_SETUP)
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_LMS_SETUP)
public class LmsSetupController extends ActivatableEntityController<LmsSetup, LmsSetup> {
private final LmsAPIService lmsAPIService;

View file

@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
@ -27,7 +28,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
@WebServiceProfile
@RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_QUIZ_IMPORT)
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_QUIZ_IMPORT)
public class QuizImportController {
private final int defaultPageSize;

View file

@ -14,7 +14,8 @@ 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.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@ -30,7 +31,7 @@ import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint;
@WebServiceProfile
@RestController
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_USER_ACCOUNT)
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
public class UserAccountController extends ActivatableEntityController<UserInfo, UserMod> {
private final ApplicationEventPublisher applicationEventPublisher;

View file

@ -17,6 +17,7 @@ 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.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
@ -32,7 +33,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
@WebServiceProfile
@RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_USER_ACTIVITY_LOG)
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG)
public class UserActivityLogController {
private final UserActivityLogDAO userActivityLogDAO;

View file

@ -1,2 +1,4 @@
spring.application.name=SEB Server
spring.profiles.active=dev
spring.profiles.active=dev
sebserver.version=1.0 beta

View file

@ -0,0 +1,31 @@
################################
# Overall
################################
sebserver.overall.version=SEB Server Version : {0}
sebserver.overall.imprint=Imprint
sebserver.overall.about=About
################################
# Login Page
################################
sebserver.login.username=User Name
sebserver.login.pwd=Password
sebserver.login.login=Sign In
sebserver.login.failed.title=Login Failed
sebserver.login.failed.message=Access Denied: Wrong username or password
################################
# Main Page
################################
sebserver.logout=Logout
sebserver.mainpage.maximize.tooltip=Maximize
sebserver.mainpage.minimize.tooltip=Minimize
sebserver.activitiespane.title=Activities
sebserver.actionpane.title=Actions
sebserver.activities.inst=Institution
sebserver.error.unexpected=Unexpected Error

View file

@ -84,7 +84,7 @@ Composite.content {
margin: 0 0 0 0;
}
Composite.selectionPane {
Composite.actionPane {
background-color: #D3D9DB;
}

View file

@ -14,7 +14,7 @@ import java.io.IOException;
import org.junit.Test;
import ch.ethz.seb.sebserver.gbl.JSONMapper;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;

View file

@ -75,8 +75,8 @@ public class ResultTest {
final Result<String> resultOf = Result.of("ONE");
final Result<String> resultOfError = Result.ofError(new RuntimeException("Some Error"));
assertEquals("ONE", resultOf.getOrElse("TWO"));
assertEquals("TWO", resultOfError.getOrElse("TWO"));
assertEquals("ONE", resultOf.getOr("TWO"));
assertEquals("TWO", resultOfError.getOr("TWO"));
assertEquals("ONE", resultOf.getOrElse(() -> "TWO"));
assertEquals("TWO", resultOfError.getOrElse(() -> "TWO"));
@ -87,8 +87,8 @@ public class ResultTest {
final Result<String> resultOf = Result.of("ONE");
final Result<String> resultOfError = Result.ofError(new RuntimeException("Some Error"));
assertEquals("ONE", resultOf.getOrHandleError(t -> t.getMessage()));
assertEquals("Some Error", resultOfError.getOrHandleError(t -> t.getMessage()));
assertEquals("ONE", resultOf.get(t -> t.getMessage()));
assertEquals("Some Error", resultOfError.get(t -> t.getMessage()));
assertEquals("ONE", resultOf.getOrThrowRuntime("Should not be thrown"));
try {

View file

@ -41,7 +41,7 @@ import org.springframework.web.context.WebApplicationContext;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.SEBServer;
import ch.ethz.seb.sebserver.gbl.JSONMapper;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
@RunWith(SpringRunner.class)
@SpringBootTest(

View file

@ -19,11 +19,11 @@ import org.springframework.test.context.jdbc.Sql;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.model.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.webservice.weblayer.api.RestAPI;
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" })
public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
@ -32,7 +32,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
public void getInstitutions() throws Exception {
Page<Institution> institutions = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Page<Institution>>() {
});
@ -44,7 +44,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
institutions = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withAttribute("active", "true")
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Page<Institution>>() {
@ -57,7 +57,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
institutions = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withAttribute("active", "false")
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Page<Institution>>() {
@ -70,7 +70,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// institutional admin sees only his institution
institutions = new RestAPITestHelper()
.withAccessToken(getAdminInstitution1Access())
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Page<Institution>>() {
});
@ -82,7 +82,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// Institutional admin tries to get data from other institution
final APIMessage errorMessage = new RestAPITestHelper()
.withAccessToken(getAdminInstitution1Access())
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withAttribute("institutionId", "2") // try to hack
.withExpectedStatus(HttpStatus.FORBIDDEN)
.getAsObject(new TypeReference<APIMessage>() {
@ -96,7 +96,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
public void getInstitutionById() throws Exception {
Institution institution = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_INSTITUTION + "/1")
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION + "/1")
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Institution>() {
});
@ -107,7 +107,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// a seb-admin is also able to get an institution that is not the one he self belongs to
institution = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_INSTITUTION + "/2")
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION + "/2")
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Institution>() {
});
@ -118,7 +118,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// but a institutional-admin is not able to get an institution that is not the one he self belongs to
new RestAPITestHelper()
.withAccessToken(getAdminInstitution1Access())
.withPath(RestAPI.ENDPOINT_INSTITUTION + "/2")
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION + "/2")
.withExpectedStatus(HttpStatus.FORBIDDEN)
.getAsString();
}
@ -128,7 +128,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// create new institution with seb-admin
Institution institution = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withMethod(HttpMethod.POST)
.withAttribute("name", "new institution")
.withAttribute("urlSuffix", "new_inst")
@ -144,7 +144,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// an institutional admin should not be allowed to create a new institution
APIMessage errorMessage = new RestAPITestHelper()
.withAccessToken(getAdminInstitution1Access())
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withMethod(HttpMethod.POST)
.withAttribute("name", "new institution")
.withAttribute("urlSuffix", "new_inst")
@ -156,7 +156,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// and name for institution must be unique
errorMessage = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withMethod(HttpMethod.POST)
.withAttribute("name", "new institution")
.withAttribute("urlSuffix", "new_inst")
@ -171,7 +171,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// and predefined id should be ignored
institution = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withMethod(HttpMethod.POST)
.withAttribute("id", "123")
.withAttribute("name", "newer institution")
@ -191,7 +191,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
final String sebAdminAccess = getSebAdminAccess();
Institution institution = new RestAPITestHelper()
.withAccessToken(sebAdminAccess)
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withMethod(HttpMethod.POST)
.withAttribute("name", "testInstitution")
.withExpectedStatus(HttpStatus.OK)
@ -205,7 +205,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// get
institution = new RestAPITestHelper()
.withAccessToken(sebAdminAccess)
.withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withMethod(HttpMethod.GET)
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Institution>() {
@ -219,7 +219,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// modify
institution = new RestAPITestHelper()
.withAccessToken(sebAdminAccess)
.withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withMethod(HttpMethod.PUT)
.withBodyJson(new Institution(null, "testInstitution", "testSuffix", null, null))
.withExpectedStatus(HttpStatus.OK)
@ -234,7 +234,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// activate
EntityProcessingReport report = new RestAPITestHelper()
.withAccessToken(sebAdminAccess)
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withPath("/").withPath(String.valueOf(institution.id)).withPath("/active")
.withMethod(HttpMethod.POST)
.withExpectedStatus(HttpStatus.OK)
@ -250,7 +250,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// get
institution = new RestAPITestHelper()
.withAccessToken(sebAdminAccess)
.withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withMethod(HttpMethod.GET)
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Institution>() {
@ -262,7 +262,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// deactivate
report = new RestAPITestHelper()
.withAccessToken(sebAdminAccess)
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withPath("/").withPath(String.valueOf(institution.id)).withPath("/inactive")
.withMethod(HttpMethod.POST)
.withExpectedStatus(HttpStatus.OK)
@ -278,7 +278,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// get
institution = new RestAPITestHelper()
.withAccessToken(sebAdminAccess)
.withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withMethod(HttpMethod.GET)
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Institution>() {
@ -290,7 +290,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// delete
report = new RestAPITestHelper()
.withAccessToken(sebAdminAccess)
.withPath(RestAPI.ENDPOINT_INSTITUTION)
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION)
.withPath("/").withPath(String.valueOf(institution.id))
.withMethod(HttpMethod.DELETE)
.withExpectedStatus(HttpStatus.OK)
@ -307,7 +307,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// get
final APIMessage error = new RestAPITestHelper()
.withAccessToken(sebAdminAccess)
.withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id))
.withMethod(HttpMethod.GET)
.withExpectedStatus(HttpStatus.NOT_FOUND)
.getAsObject(new TypeReference<APIMessage>() {

View file

@ -31,11 +31,12 @@ import org.springframework.test.context.jdbc.Sql;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName;
import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
@ -43,7 +44,6 @@ 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.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
import ch.ethz.seb.sebserver.webservice.weblayer.api.RestAPI;
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" })
public class UserAPITest extends AdministrationAPIIntegrationTester {
@ -52,7 +52,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void getMyUserInfo() throws Exception {
String contentAsString = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
.withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me")
.withExpectedStatus(HttpStatus.OK)
.getAsString();
@ -70,7 +70,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
contentAsString = new RestAPITestHelper()
.withAccessToken(getAdminInstitution1Access())
.withPath(RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
.withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me")
.withExpectedStatus(HttpStatus.OK)
.getAsString();
@ -90,7 +90,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
@Test
public void getUserInfoWithUUID() throws Exception {
final String sebAdminAccessToken = getSebAdminAccess();
String contentAsString = this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user2")
String contentAsString = this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminAccessToken))
.andExpect(status().isOk())
@ -109,7 +109,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
contentAsString);
final String adminInstitution2AccessToken = getAdminInstitution2Access();
contentAsString = this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user1")
contentAsString = this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user1")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + adminInstitution2AccessToken))
.andExpect(status().isForbidden())
@ -127,7 +127,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void institutionalAdminNotAllowedToSeeUsersOfOtherInstitution() throws Exception {
new RestAPITestHelper()
.withAccessToken(getAdminInstitution1Access())
.withPath(RestAPI.ENDPOINT_USER_ACCOUNT + "?institutionId=2")
.withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?institutionId=2")
.withExpectedStatus(HttpStatus.FORBIDDEN)
.getAsString();
}
@ -136,7 +136,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void getAllUserInfoNoFilter() throws Exception {
Page<UserInfo> userInfos = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(RestAPI.ENDPOINT_USER_ACCOUNT)
.withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Page<UserInfo>>() {
});
@ -150,7 +150,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
userInfos = new RestAPITestHelper()
.withAccessToken(getAdminInstitution2Access())
.withPath(RestAPI.ENDPOINT_USER_ACCOUNT)
.withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
.withAttribute("institutionId", "2")
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Page<UserInfo>>() {
@ -167,7 +167,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
//.. and without inactive, if active flag is set to true
userInfos = new RestAPITestHelper()
.withAccessToken(getAdminInstitution2Access())
.withPath(RestAPI.ENDPOINT_USER_ACCOUNT)
.withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
.withAttribute(Entity.FILTER_ATTR_INSTITUTION, "2")
.withAttribute(Entity.FILTER_ATTR_ACTIVE, "true")
.withExpectedStatus(HttpStatus.OK)
@ -183,7 +183,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
//.. and only inactive, if active flag is set to false
userInfos = new RestAPITestHelper()
.withAccessToken(getAdminInstitution2Access())
.withPath(RestAPI.ENDPOINT_USER_ACCOUNT)
.withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
.withAttribute(Entity.FILTER_ATTR_INSTITUTION, "2")
.withAttribute(Entity.FILTER_ATTR_ACTIVE, "false")
.withExpectedStatus(HttpStatus.OK)
@ -203,7 +203,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final String token = getSebAdminAccess();
final Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT)
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -225,7 +225,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final String token = getSebAdminAccess();
final Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?institutionId=2")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?institutionId=2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -244,7 +244,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void getPageNoFilterNoPageAttributesDescendingOrder() throws Exception {
final String token = getSebAdminAccess();
final Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?sort=-")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?sort=-")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -266,7 +266,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// first page default sort order
Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT
+ "?page_number=1&page_size=3&institutionId=2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
@ -284,7 +284,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// second page default sort order
userInfos = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT
+ "?page_number=2&page_size=3&institutionId=2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
@ -303,7 +303,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// invalid page number should refer to last page
userInfos = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT
+ "?page_number=3&page_size=3&institutionId=2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
@ -322,7 +322,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// first page descending sort order
userInfos = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT
+ "?page_number=1&page_size=3&sort=-&institutionId=2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
@ -350,7 +350,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void getAllUserInfo() throws Exception {
final String token = getSebAdminAccess();
final Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT)
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -366,7 +366,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void getAllUserInfoWithOnlyActive() throws Exception {
final String token = getSebAdminAccess();
final Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=true&institutionId=2")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?active=true&institutionId=2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -385,7 +385,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// expecting none for SEBAdmins institution
final String token = getSebAdminAccess();
Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=false")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?active=false")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -399,7 +399,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// expecting one for institution 2
userInfos = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=false&institutionId=2")
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?active=false&institutionId=2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -416,7 +416,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void getAllUserInfoWithSearchUsernameLike() throws Exception {
final String token = getSebAdminAccess();
final Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?username=exam")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?username=exam")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -432,7 +432,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
@Test
public void testOwnerGet() throws Exception {
final String examAdminToken1 = getExamAdmin1();
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me")
.header("Authorization", "Bearer " + examAdminToken1))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
@ -442,7 +442,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void createUserTest() throws Exception {
final String token = getSebAdminAccess();
final UserInfo createdUser = this.jsonMapper.readValue(
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT)
this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param(Domain.USER.ATTR_NAME, "NewTestUser")
@ -461,7 +461,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// get newly created user and check equality
final UserInfo createdUserGet = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + createdUser.uuid)
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + createdUser.uuid)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -477,7 +477,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final Page<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG
+ "?user=user1&activity_types=CREATE")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -557,7 +557,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void modifyUserWithPUTMethod() throws Exception {
final String token = getSebAdminAccess();
final UserInfo user = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user7")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user7")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -586,7 +586,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final String modifyUserJson = this.jsonMapper.writeValueAsString(modifyUser);
UserInfo modifiedUserResult = this.jsonMapper.readValue(
this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + user.getUuid())
this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + user.getUuid())
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifyUserJson))
@ -604,7 +604,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// double check by getting the user by UUID
modifiedUserResult = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUserResult.uuid)
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUserResult.uuid)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -657,7 +657,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void testOwnerModifyPossibleForExamAdmin() throws Exception {
final String examAdminToken1 = getExamAdmin1();
final UserInfo examAdmin = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me")
.header("Authorization", "Bearer " + examAdminToken1))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
@ -667,7 +667,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final UserMod modifiedUser = new UserMod(examAdmin, null, null);
final String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser);
this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid)
this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid)
.header("Authorization", "Bearer " + examAdminToken1)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
@ -679,7 +679,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void institutionalAdminTryToCreateOrModifyUserForOtherInstituionNotPossible() throws Exception {
final String token = getAdminInstitution1Access();
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT)
this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param(Domain.USER.ATTR_INSTITUTION_ID, "2")
@ -695,7 +695,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name())));
final UserMod newUser = new UserMod(userInfo, "12345678", "12345678");
final String newUserJson = this.jsonMapper.writeValueAsString(newUser);
this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/NewTestUser")
this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/NewTestUser")
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(newUserJson))
@ -707,7 +707,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void unauthorizedAdminTryToCreateUserNotPossible() throws Exception {
final String token = getExamAdmin1();
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT)
this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT)
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param(Domain.USER.ATTR_INSTITUTION_ID, "2")
@ -723,7 +723,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name())));
final UserMod newUser = new UserMod(userInfo, "12345678", "12345678");
final String newUserJson = this.jsonMapper.writeValueAsString(newUser);
this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/NewTestUser")
this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/NewTestUser")
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(newUserJson))
@ -739,7 +739,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// a SEB Server Admin now changes the password of ExamAdmin1
final String sebAdminToken = getSebAdminAccess();
final UserInfo examAdmin1 = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user4")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -753,7 +753,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
"newPassword");
final String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser);
this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid)
this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid)
.header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
@ -769,14 +769,14 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
}
// it should also not be possible to use an old token again after password change
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me")
.header("Authorization", "Bearer " + examAdminToken1))
.andExpect(status().isUnauthorized())
.andReturn().getResponse().getContentAsString();
// but it should be possible to get a new access token and request again
final String examAdminToken2 = obtainAccessToken("examAdmin1", "newPassword");
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me")
.header("Authorization", "Bearer " + examAdminToken2))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
@ -786,7 +786,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
public void modifyUserPasswordInvalidPasswords() throws Exception {
final String sebAdminToken = getSebAdminAccess();
final UserInfo examAdmin1 = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user4")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -802,7 +802,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser);
List<APIMessage> messages = this.jsonMapper.readValue(
this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid)
this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid)
.header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
@ -824,7 +824,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser);
messages = this.jsonMapper.readValue(
this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid)
this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid)
.header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
@ -843,7 +843,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS);
// only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account
final String examAdminToken = getExamAdmin1();
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4/inactive")
this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user4/inactive")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + examAdminToken))
.andExpect(status().isForbidden());
@ -851,7 +851,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// With SEB Administrator it should work
final String sebAdminToken = getSebAdminAccess();
final EntityProcessingReport report = this.jsonMapper.readValue(
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4/inactive")
this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user4/inactive")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -868,7 +868,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// get user and check activity
final EntityKey key = report.source.iterator().next();
final UserInfo user = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + key.modelId)
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + key.modelId)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -883,7 +883,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final Page<UserActivityLog> userLogs = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/?user=user1&from=" + timeNow)
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "/?user=user1&from=" + timeNow)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -903,7 +903,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS);
// only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account
final String examAdminToken = getExamAdmin1();
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user6/active")
this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user6/active")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + examAdminToken))
.andExpect(status().isForbidden());
@ -911,7 +911,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// With SEB Administrator it should work
final String sebAdminToken = getSebAdminAccess();
final EntityProcessingReport report = this.jsonMapper.readValue(
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user6/active")
this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user6/active")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -928,7 +928,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// get user and check activity
final EntityKey key = report.source.iterator().next();
final UserInfo user = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + key.modelId)
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + key.modelId)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -942,7 +942,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// check also user activity log
final Page<UserActivityLog> userLogs = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user1&from=" + timeNow)
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user1&from=" + timeNow)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
@ -962,7 +962,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// all active for the own institution
Page<UserInfo> usersPage = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/all/active")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/active")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -976,7 +976,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// all inactive of the own institution
usersPage = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/all/inactive")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/inactive")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -990,7 +990,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// all active of institution 2
usersPage = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/all/active?institutionId=2")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/active?institutionId=2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -1005,7 +1005,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// all inactive of institution 2
usersPage = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/all/inactive?institutionId=2")
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/inactive?institutionId=2")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -1026,7 +1026,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
Collection<UserInfo> users = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/list?ids=user1,user2,user6,user7")
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/list?ids=user1,user2,user6,user7")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
@ -1043,7 +1043,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
users = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/list?ids=user1,user2,user6,user7")
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/list?ids=user1,user2,user6,user7")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + instAdminToken))
.andExpect(status().isOk())
@ -1061,14 +1061,14 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final String sebAdminToken = getSebAdminAccess();
// for SEB Admin
Collection<EntityKeyAndName> names = this.jsonMapper.readValue(
Collection<EntityName> names = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/names")
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/names")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
new TypeReference<Collection<EntityKeyAndName>>() {
new TypeReference<Collection<EntityName>>() {
});
assertNotNull(names);
@ -1081,12 +1081,12 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
final String instAdminToken = getAdminInstitution2Access();
names = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/names")
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/names")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + instAdminToken))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
new TypeReference<Collection<EntityKeyAndName>>() {
new TypeReference<Collection<EntityName>>() {
});
assertNotNull(names);
@ -1099,12 +1099,12 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// for an institutional admin 2 only active
names = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/names?active=true")
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/names?active=true")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header("Authorization", "Bearer " + instAdminToken))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
new TypeReference<Collection<EntityKeyAndName>>() {
new TypeReference<Collection<EntityName>>() {
});
assertNotNull(names);

View file

@ -19,9 +19,9 @@ import org.springframework.test.context.jdbc.Sql;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.webservice.weblayer.api.RestAPI;
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" })
public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
@ -30,7 +30,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
public void getAllAsSEBAdmin() throws Exception {
final String token = getSebAdminAccess();
final Page<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG)
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
@ -47,7 +47,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
// for a user in another institution, the institution has to be defined
Page<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4&institutionId=2")
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4&institutionId=2")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
@ -59,7 +59,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
// for a user in the same institution no institution is needed
logs = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user2")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user2")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
@ -82,7 +82,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
final String token = getSebAdminAccess();
Page<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2)
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
@ -94,7 +94,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
logs = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from="
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from="
+ sec2 + "&to=" + sec4)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -108,7 +108,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
logs = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2
+ "&to=" + sec5)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -122,7 +122,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
logs = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2
+ "&to=" + sec6)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -138,7 +138,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
public void getAllAsSEBAdminForActivityType() throws Exception {
final String token = getSebAdminAccess();
Page<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?activity_types=CREATE")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?activity_types=CREATE")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
@ -151,7 +151,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
logs = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG
+ "?activity_types=CREATE,MODIFY")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -166,7 +166,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
logs = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG
+ "?institutionId=2&activity_types=CREATE,MODIFY")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -183,7 +183,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
final String token = getSebAdminAccess();
Page<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?entity_types=INSTITUTION")
.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?entity_types=INSTITUTION")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
@ -196,7 +196,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
logs = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG
+ "?entity_types=INSTITUTION,EXAM")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -210,7 +210,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
logs = this.jsonMapper.readValue(
this.mockMvc
.perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG
get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG
+ "?entity_types=INSTITUTION,EXAM&institutionId=2")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
@ -226,7 +226,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
public void getAllAsInstitutionalAdmin() throws Exception {
final String token = getAdminInstitution1Access();
final Page<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG)
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
@ -242,18 +242,18 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
String token = getExamAdmin1();
// no privilege at all
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG)
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG)
.header("Authorization", "Bearer " + token))
.andExpect(status().isForbidden());
// no privilege at all
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4")
.header("Authorization", "Bearer " + token))
.andExpect(status().isForbidden());
// no privilege to query logs of users of other institution
token = getAdminInstitution1Access();
final Page<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4")
this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),

View file

@ -31,7 +31,7 @@ import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
import ch.ethz.seb.sebserver.SEBServer;
import ch.ethz.seb.sebserver.gbl.JSONMapper;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
@RunWith(SpringRunner.class)
@SpringBootTest(