This commit is contained in:
anhefti 2019-02-20 21:09:49 +01:00
parent 28556afae6
commit bb97e60922
49 changed files with 963 additions and 261 deletions

View file

@ -30,7 +30,7 @@
<profile> <profile>
<id>Demo</id> <id>Demo</id>
<activation> <activation>
<activeByDefault>true</activeByDefault> <activeByDefault>false</activeByDefault>
</activation> </activation>
<properties> <properties>
<java.version>1.8</java.version> <java.version>1.8</java.version>
@ -58,7 +58,7 @@
<profile> <profile>
<id>Java 11</id> <id>Java 11</id>
<activation> <activation>
<activeByDefault>false</activeByDefault> <activeByDefault>true</activeByDefault>
</activation> </activation>
<properties> <properties>
<java.version>11</java.version> <java.version>11</java.version>

View file

@ -57,6 +57,8 @@ public final class API {
public static final String DEPENDENCY_PATH_SEGMENT = "/dependency"; public static final String DEPENDENCY_PATH_SEGMENT = "/dependency";
public static final String PASSWORD_PATH_SEGMENT = "/password";
public static final String PATH_VAR_ACTIVE = MODEL_ID_VAR_PATH_SEGMENT + ACTIVE_PATH_SEGMENT; public static final String PATH_VAR_ACTIVE = MODEL_ID_VAR_PATH_SEGMENT + ACTIVE_PATH_SEGMENT;
public static final String PATH_VAR_INACTIVE = MODEL_ID_VAR_PATH_SEGMENT + INACTIVE_PATH_SEGMENT; public static final String PATH_VAR_INACTIVE = MODEL_ID_VAR_PATH_SEGMENT + INACTIVE_PATH_SEGMENT;

View file

@ -36,7 +36,7 @@ public class APIMessage implements Serializable {
ILLEGAL_API_ARGUMENT("1010", HttpStatus.BAD_REQUEST, "Illegal API request argument"), ILLEGAL_API_ARGUMENT("1010", HttpStatus.BAD_REQUEST, "Illegal API request argument"),
UNEXPECTED("1100", HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected intenral server-side error"), UNEXPECTED("1100", HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected intenral server-side error"),
FIELD_VALIDATION("1200", HttpStatus.BAD_REQUEST, "Field validation error"), FIELD_VALIDATION("1200", HttpStatus.BAD_REQUEST, "Field validation error"),
PASSWORD_MISSMATCH("1300", HttpStatus.BAD_REQUEST, "new password do not match retyped password") PASSWORD_MISMATCH("1300", HttpStatus.BAD_REQUEST, "new password do not match retyped password")
; ;

View file

@ -27,7 +27,11 @@ public interface Entity extends ModelIdAware {
@JsonIgnore @JsonIgnore
default EntityKey getEntityKey() { default EntityKey getEntityKey() {
return new EntityKey(getModelId(), entityType()); final String modelId = getModelId();
if (modelId == null) {
return null;
}
return new EntityKey(modelId, entityType());
} }
public static EntityName toName(final Entity entity) { public static EntityName toName(final Entity entity) {

View file

@ -36,13 +36,13 @@ public final class LmsSetup implements GrantEntity, Activatable {
@JsonProperty(LMS_SETUP.ATTR_ID) @JsonProperty(LMS_SETUP.ATTR_ID)
public final Long id; public final Long id;
@JsonProperty(LMS_SETUP.ATTR_INSTITUTION_ID)
@NotNull @NotNull
@JsonProperty(LMS_SETUP.ATTR_INSTITUTION_ID)
public final Long institutionId; public final Long institutionId;
@JsonProperty(LMS_SETUP.ATTR_NAME)
@NotNull(message = "lmsSetup:name:notNull") @NotNull(message = "lmsSetup:name:notNull")
@Size(min = 3, max = 255, message = "lmsSetup:name:size:{min}:{max}:${validatedValue}") @Size(min = 3, max = 255, message = "lmsSetup:name:size:{min}:{max}:${validatedValue}")
@JsonProperty(LMS_SETUP.ATTR_NAME)
public final String name; public final String name;
@JsonProperty(LMS_SETUP.ATTR_LMS_TYPE) @JsonProperty(LMS_SETUP.ATTR_LMS_TYPE)

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.model.user;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class PasswordChange {
public static final String ATTR_NAME_NEW_PASSWORD = "newPassword";
public static final String ATTR_NAME_RETYPED_NEW_PASSWORD = "retypedNewPassword";
@NotNull(message = "user:password:notNull")
@Size(min = 8, max = 255, message = "user:password:size:{min}:{max}:${validatedValue}")
@JsonProperty(ATTR_NAME_NEW_PASSWORD)
private final String newPassword;
@JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD)
private final String retypedNewPassword;
@JsonCreator
public PasswordChange(
@JsonProperty(ATTR_NAME_NEW_PASSWORD) final String newPassword,
@JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD) final String retypedNewPassword) {
this.newPassword = newPassword;
this.retypedNewPassword = retypedNewPassword;
}
public String getNewPassword() {
return this.newPassword;
}
public String getRetypedNewPassword() {
return this.retypedNewPassword;
}
public boolean newPasswordMatch() {
return this.newPassword.equals(this.retypedNewPassword);
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.model.user;
import java.util.Locale;
import java.util.Set;
import org.joda.time.DateTimeZone;
public interface UserAccount {
String getModelId();
Long getInstitutionId();
String getName();
String getUsername();
String getEmail();
Boolean getActive();
boolean isActive();
Locale getLocale();
DateTimeZone getTimeZone();
Set<String> getRoles();
String getNewPassword();
String getRetypedNewPassword();
}

View file

@ -12,10 +12,15 @@ import java.io.Serializable;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@ -32,7 +37,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;
* to and from JSON within the Jackson library. * to and from JSON within the Jackson library.
* *
* This domain model is immutable and thread-save */ * This domain model is immutable and thread-save */
public final class UserInfo implements GrantEntity, Activatable, Serializable { public final class UserInfo implements UserAccount, GrantEntity, Activatable, Serializable {
private static final long serialVersionUID = 2526446136264377808L; private static final long serialVersionUID = 2526446136264377808L;
@ -45,30 +50,39 @@ public final class UserInfo implements GrantEntity, Activatable, Serializable {
public final String uuid; public final String uuid;
/** The foreign key identifier to the institution where the User belongs to */ /** The foreign key identifier to the institution where the User belongs to */
@NotNull
@JsonProperty(USER.ATTR_INSTITUTION_ID) @JsonProperty(USER.ATTR_INSTITUTION_ID)
public final Long institutionId; public final Long institutionId;
/** Full name of the user */ /** Full name of the user */
@NotNull(message = "user:name:notNull")
@Size(min = 3, max = 255, message = "user:name:size:{min}:{max}:${validatedValue}")
@JsonProperty(USER.ATTR_NAME) @JsonProperty(USER.ATTR_NAME)
public final String name; public final String name;
/** The internal user name */ /** The internal user name */
@NotNull(message = "user:username:notNull")
@Size(min = 3, max = 255, message = "user:username:size:{min}:{max}:${validatedValue}")
@JsonProperty(USER.ATTR_USERNAME) @JsonProperty(USER.ATTR_USERNAME)
public final String username; public final String username;
/** E-mail address of the user */ /** E-mail address of the user */
@Email(message = "user:email:email:_:_:${validatedValue}")
@JsonProperty(USER.ATTR_EMAIL) @JsonProperty(USER.ATTR_EMAIL)
public final String email; public final String email;
/** Indicates whether this user is still active or not */ /** Indicates whether this user is still active or not */
@NotNull
@JsonProperty(USER.ATTR_ACTIVE) @JsonProperty(USER.ATTR_ACTIVE)
public final Boolean active; public final Boolean active;
/** The users locale */ /** The users locale */
@NotNull(message = "user:locale:notNull")
@JsonProperty(USER.ATTR_LOCALE) @JsonProperty(USER.ATTR_LOCALE)
public final Locale locale; public final Locale locale;
/** The users time zone */ /** The users time zone */
@NotNull(message = "user:timeZone:notNull")
@JsonProperty(USER.ATTR_TIMEZONE) @JsonProperty(USER.ATTR_TIMEZONE)
public final DateTimeZone timeZone; public final DateTimeZone timeZone;
@ -129,14 +143,17 @@ public final class UserInfo implements GrantEntity, Activatable, Serializable {
return this.name; return this.name;
} }
@Override
public String getUsername() { public String getUsername() {
return this.username; return this.username;
} }
@Override
public String getEmail() { public String getEmail() {
return this.email; return this.email;
} }
@Override
public Boolean getActive() { public Boolean getActive() {
return this.active; return this.active;
} }
@ -146,14 +163,17 @@ public final class UserInfo implements GrantEntity, Activatable, Serializable {
return this.active; return this.active;
} }
@Override
public Locale getLocale() { public Locale getLocale() {
return this.locale; return this.locale;
} }
@Override
public DateTimeZone getTimeZone() { public DateTimeZone getTimeZone() {
return this.timeZone; return this.timeZone;
} }
@Override
public Set<String> getRoles() { public Set<String> getRoles() {
return this.roles; return this.roles;
} }
@ -165,6 +185,18 @@ public final class UserInfo implements GrantEntity, Activatable, Serializable {
return this.roles.contains(userRole.name()); return this.roles.contains(userRole.name());
} }
@JsonIgnore
@Override
public String getNewPassword() {
return null;
}
@JsonIgnore
@Override
public String getRetypedNewPassword() {
return null;
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;

View file

@ -28,7 +28,7 @@ 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.model.Domain.USER_ROLE;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;
public final class UserMod implements GrantEntity { public final class UserMod implements UserAccount, GrantEntity {
public static final String ATTR_NAME_NEW_PASSWORD = "newPassword"; public static final String ATTR_NAME_NEW_PASSWORD = "newPassword";
public static final String ATTR_NAME_RETYPED_NEW_PASSWORD = "retypedNewPassword"; public static final String ATTR_NAME_RETYPED_NEW_PASSWORD = "retypedNewPassword";
@ -88,7 +88,6 @@ public final class UserMod implements GrantEntity {
@JsonProperty(ATTR_NAME_NEW_PASSWORD) final String newPassword, @JsonProperty(ATTR_NAME_NEW_PASSWORD) final String newPassword,
@JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD) final String retypedNewPassword, @JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD) final String retypedNewPassword,
@JsonProperty(USER.ATTR_EMAIL) final String email, @JsonProperty(USER.ATTR_EMAIL) final String email,
@JsonProperty(USER.ATTR_ACTIVE) final Boolean active,
@JsonProperty(USER.ATTR_LOCALE) final Locale locale, @JsonProperty(USER.ATTR_LOCALE) final Locale locale,
@JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone, @JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone,
@JsonProperty(USER_ROLE.REFERENCE_NAME) final Set<String> roles) { @JsonProperty(USER_ROLE.REFERENCE_NAME) final Set<String> roles) {
@ -133,6 +132,19 @@ public final class UserMod implements GrantEntity {
this.roles = postAttrMapper.getStringSet(USER_ROLE.REFERENCE_NAME); this.roles = postAttrMapper.getStringSet(USER_ROLE.REFERENCE_NAME);
} }
public UserMod(final String modelId, final Long institutionId) {
this.uuid = modelId;
this.institutionId = institutionId;
this.newPassword = null;
this.retypedNewPassword = null;
this.name = null;
this.username = null;
this.email = null;
this.locale = null;
this.timeZone = null;
this.roles = null;
}
@Override @Override
public String getModelId() { public String getModelId() {
return this.uuid; return this.uuid;
@ -153,6 +165,7 @@ public final class UserMod implements GrantEntity {
return this.uuid; return this.uuid;
} }
@Override
public String getNewPassword() { public String getNewPassword() {
return this.newPassword; return this.newPassword;
} }
@ -162,26 +175,32 @@ public final class UserMod implements GrantEntity {
return this.name; return this.name;
} }
@Override
public String getUsername() { public String getUsername() {
return this.username; return this.username;
} }
@Override
public String getEmail() { public String getEmail() {
return this.email; return this.email;
} }
@Override
public Locale getLocale() { public Locale getLocale() {
return this.locale; return this.locale;
} }
@Override
public DateTimeZone getTimeZone() { public DateTimeZone getTimeZone() {
return this.timeZone; return this.timeZone;
} }
@Override
public Set<String> getRoles() { public Set<String> getRoles() {
return this.roles; return this.roles;
} }
@Override
public String getRetypedNewPassword() { public String getRetypedNewPassword() {
return this.retypedNewPassword; return this.retypedNewPassword;
} }
@ -194,6 +213,16 @@ public final class UserMod implements GrantEntity {
return passwordChangeRequest() && this.newPassword.equals(this.retypedNewPassword); return passwordChangeRequest() && this.newPassword.equals(this.retypedNewPassword);
} }
@Override
public Boolean getActive() {
return false;
}
@Override
public boolean isActive() {
return false;
}
@Override @Override
public String toString() { public String toString() {
return "UserMod [uuid=" + this.uuid + ", institutionId=" + this.institutionId + ", name=" + this.name return "UserMod [uuid=" + this.uuid + ", institutionId=" + this.institutionId + ", name=" + this.name

View file

@ -17,11 +17,14 @@ import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Text;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
@ -65,6 +68,19 @@ public final class Form implements FormBinding {
} }
} }
@Override
public MultiValueMap<String, String> getFormAsQueryAttributes() {
final LinkedMultiValueMap<String, String> result = new LinkedMultiValueMap<>();
for (final Map.Entry<String, FormFieldAccessor> entry : this.formFields.entrySet()) {
final String value = entry.getValue().getValue();
if (StringUtils.isNoneBlank(value)) {
result.add(entry.getKey(), value);
}
}
return result;
}
public String getValue(final String name) { public String getValue(final String name) {
final FormFieldAccessor formFieldAccessor = this.formFields.get(name); final FormFieldAccessor formFieldAccessor = this.formFields.get(name);
if (formFieldAccessor != null) { if (formFieldAccessor != null) {
@ -155,7 +171,7 @@ public final class Form implements FormBinding {
.forEach(processor); .forEach(processor);
} }
public void flush() { private void flush() {
for (final Map.Entry<String, FormFieldAccessor> entry : this.formFields.entrySet()) { for (final Map.Entry<String, FormFieldAccessor> entry : this.formFields.entrySet()) {
final FormFieldAccessor accessor = entry.getValue(); final FormFieldAccessor accessor = entry.getValue();
if (accessor.control.isVisible()) { if (accessor.control.isVisible()) {

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.gui.service.form; package ch.ethz.seb.sebserver.gui.service.form;
import java.util.List; import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -186,6 +187,19 @@ public class FormBuilder {
return this; return this;
} }
public FormBuilder addImageUploadIf(
final BooleanSupplier condition,
final String name,
final String label,
final String value,
final int span) {
if (condition != null && condition.getAsBoolean()) {
return addImageUpload(name, label, value, span);
}
return this;
}
public FormBuilder addImageUpload( public FormBuilder addImageUpload(
final String name, final String name,
final String label, final String label,

View file

@ -36,7 +36,6 @@ public interface PageContext {
public static final String PAGE_TEMPLATE_COMPOSER_NAME = "ATTR_PAGE_TEMPLATE_COMPOSER_NAME"; public static final String PAGE_TEMPLATE_COMPOSER_NAME = "ATTR_PAGE_TEMPLATE_COMPOSER_NAME";
public static final String READ_ONLY = "READ_ONLY"; public static final String READ_ONLY = "READ_ONLY";
public static final String CREATE_NEW = "CREATE_NEW";
public static final String ENTITY_ID = "ENTITY_ID"; public static final String ENTITY_ID = "ENTITY_ID";
public static final String PARENT_ENTITY_ID = "PARENT_ENTITY_ID"; public static final String PARENT_ENTITY_ID = "PARENT_ENTITY_ID";
@ -106,6 +105,8 @@ public interface PageContext {
String getAttribute(String name, String def); String getAttribute(String name, String def);
boolean isReadonly();
EntityKey getEntityKey(); EntityKey getEntityKey();
EntityKey getParentEntityKey(); EntityKey getParentEntityKey();

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.gui.service.page.action; package ch.ethz.seb.sebserver.gui.service.page.action;
import java.util.Set; import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -113,4 +114,12 @@ public final class Action implements Runnable {
return this.pageContext; return this.pageContext;
} }
public PageContext publishIf(final BooleanSupplier condition) {
if (condition.getAsBoolean()) {
publish();
}
return this.pageContext;
}
} }

View file

@ -16,10 +16,14 @@ public enum ActionDefinition {
"sebserver.institution.action.new", "sebserver.institution.action.new",
ImageIcon.NEW), ImageIcon.NEW),
INSTITUTION_VIEW( INSTITUTION_VIEW_FROM_LIST(
"sebserver.institution.action.view", "sebserver.institution.action.list.view",
ImageIcon.SHOW), ImageIcon.SHOW),
INSTITUTION_MODIFY_FROM__LIST(
"sebserver.institution.action.list.modify",
ImageIcon.EDIT),
INSTITUTION_MODIFY( INSTITUTION_MODIFY(
"sebserver.institution.action.modify", "sebserver.institution.action.modify",
ImageIcon.EDIT), ImageIcon.EDIT),
@ -44,6 +48,37 @@ public enum ActionDefinition {
"sebserver.institution.action.modify", "sebserver.institution.action.modify",
ImageIcon.DELETE), ImageIcon.DELETE),
USER_ACCOUNT_NEW(
"sebserver.useraccount.action.new",
ImageIcon.NEW),
USER_ACCOUNT_VIEW(
"sebserver.useraccount.action.view",
ImageIcon.SHOW),
USER_ACCOUNT_MODIFY(
"sebserver.useraccount.action.modify",
ImageIcon.EDIT),
USER_ACCOUNT_CANCEL_MODIFY(
"sebserver.overall.action.modify.cancel",
ImageIcon.CANCEL),
USER_ACCOUNT_SAVE(
"sebserver.useraccount.action.save",
ImageIcon.SAVE),
USER_ACCOUNT_ACTIVATE(
"sebserver.useraccount.action.activate",
ImageIcon.INACTIVE),
USER_ACCOUNT_DEACTIVATE(
"sebserver.useraccount.action.deactivate",
ImageIcon.ACTIVE),
USER_ACCOUNT_DELETE(
"sebserver.useraccount.action.modify",
ImageIcon.DELETE),
; ;
public final String name; public final String name;

View file

@ -40,11 +40,11 @@ public final class InstitutionActions {
} }
public static Result<?> viewInstitution(final Action action) { public static Result<?> viewInstitution(final Action action) {
return fromInstitution(action, false); return fromSelection(action, false);
} }
public static Result<?> editInstitutionFromList(final Action action) { public static Result<?> editInstitutionFromList(final Action action) {
return fromInstitution(action, true); return fromSelection(action, true);
} }
public static Result<?> editInstitution(final Action action) { public static Result<?> editInstitution(final Action action) {
@ -55,11 +55,17 @@ public final class InstitutionActions {
} }
public static Result<?> cancelEditInstitution(final Action action) { public static Result<?> cancelEditInstitution(final Action action) {
if (action.pageContext.getEntityKey() == null) {
final ActivitySelection toList = Activity.INSTITUTION_LIST.createSelection();
action.pageContext.publishPageEvent(new ActivitySelectionEvent(toList));
return Result.of(toList);
} else {
return Result.of(goToInstitution( return Result.of(goToInstitution(
action.pageContext, action.pageContext,
action.pageContext.getAttribute(AttributeKeys.ENTITY_ID), action.pageContext.getAttribute(AttributeKeys.ENTITY_ID),
false)); false));
} }
}
public static Result<?> activateInstitution(final Action action) { public static Result<?> activateInstitution(final Action action) {
return action.restService return action.restService
@ -81,7 +87,7 @@ public final class InstitutionActions {
.map(report -> goToInstitution(action.pageContext, report.getSingleSource().modelId, false)); .map(report -> goToInstitution(action.pageContext, report.getSingleSource().modelId, false));
} }
private static Result<?> fromInstitution(final Action action, final boolean edit) { private static Result<?> fromSelection(final Action action, final boolean edit) {
final Collection<String> selection = action.selectionSupplier.get(); final Collection<String> selection = action.selectionSupplier.get();
if (selection.isEmpty()) { if (selection.isEmpty()) {
return Result.ofError(new PageMessageException("sebserver.institution.info.pleaseSelect")); return Result.ofError(new PageMessageException("sebserver.institution.info.pleaseSelect"));
@ -90,13 +96,19 @@ public final class InstitutionActions {
return Result.of(goToInstitution(action.pageContext, selection.iterator().next(), edit)); return Result.of(goToInstitution(action.pageContext, selection.iterator().next(), edit));
} }
private static ActivitySelection goToInstitution(final PageContext pageContext, final String modelId, private static ActivitySelection goToInstitution(
final PageContext pageContext,
final String modelId,
final boolean edit) { final boolean edit) {
final ActivitySelection activitySelection = Activity.INSTITUTION_FORM final ActivitySelection activitySelection = Activity.INSTITUTION_FORM
.createSelection() .createSelection()
.withEntity(new EntityKey(modelId, EntityType.INSTITUTION)) .withAttribute(AttributeKeys.READ_ONLY, String.valueOf(!edit));
.withAttribute(AttributeKeys.READ_ONLY, String.valueOf(!edit))
.withAttribute(AttributeKeys.CREATE_NEW, (modelId != null) ? "false" : "true"); if (modelId != null) {
activitySelection.withEntity(new EntityKey(modelId, EntityType.INSTITUTION));
}
pageContext.publishPageEvent(new ActivitySelectionEvent(activitySelection)); pageContext.publishPageEvent(new ActivitySelectionEvent(activitySelection));
return activitySelection; return activitySelection;
} }

View file

@ -0,0 +1,64 @@
/*
* 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.action;
import java.util.Collection;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.util.Result;
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.PageMessageException;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
public final class UserAccountActions {
public static Result<?> newUserAccount(final Action action) {
return Result.of(goToUserAccount(action.pageContext, null, true));
}
public static Result<?> viewUserAccountFromList(final Action action) {
return fromSelection(action, false);
}
public static Result<?> editUserAccountFromList(final Action action) {
return fromSelection(action, true);
}
private static Result<?> fromSelection(final Action action, final boolean edit) {
final Collection<String> selection = action.selectionSupplier.get();
if (selection.isEmpty()) {
return Result.ofError(new PageMessageException("sebserver.useraccount.info.pleaseSelect"));
}
return Result.of(goToUserAccount(action.pageContext, selection.iterator().next(), edit));
}
private static ActivitySelection goToUserAccount(
final PageContext pageContext,
final String modelId,
final boolean edit) {
final ActivitySelection activitySelection = Activity.USER_ACCOUNT_FORM
.createSelection()
.withEntity(new EntityKey(modelId, EntityType.USER))
.withAttribute(AttributeKeys.READ_ONLY, String.valueOf(!edit));
if (modelId != null) {
activitySelection.withEntity(new EntityKey(modelId, EntityType.USER));
}
pageContext.publishPageEvent(new ActivitySelectionEvent(activitySelection));
return activitySelection;
}
}

View file

@ -22,6 +22,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
@ -91,6 +92,7 @@ public class ActivitiesPane implements TemplateComposer {
navigation.setLayoutData(navigationGridData); navigation.setLayoutData(navigationGridData);
// Institution // Institution
// If current user has SEB Server Admin role, show the Institution list
if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) { if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) {
// institutions (list) as root // institutions (list) as root
final TreeItem institutions = this.widgetFactory.treeItemLocalized( final TreeItem institutions = this.widgetFactory.treeItemLocalized(
@ -99,7 +101,7 @@ public class ActivitiesPane implements TemplateComposer {
injectActivitySelection(institutions, Activity.INSTITUTION_LIST.createSelection()); injectActivitySelection(institutions, Activity.INSTITUTION_LIST.createSelection());
} else { } else {
// institution node as none root // otherwise show the form of the institution for current user
final TreeItem institutions = this.widgetFactory.treeItemLocalized( final TreeItem institutions = this.widgetFactory.treeItemLocalized(
navigation, navigation,
Activity.INSTITUTION_FORM.title); Activity.INSTITUTION_FORM.title);
@ -111,10 +113,23 @@ public class ActivitiesPane implements TemplateComposer {
} }
// User Account // User Account
// if current user has base read privilege for User Account, show list
if (this.currentUser.hasPrivilege(PrivilegeType.READ_ONLY, EntityType.USER)) {
final TreeItem userAccounts = this.widgetFactory.treeItemLocalized( final TreeItem userAccounts = this.widgetFactory.treeItemLocalized(
navigation, navigation,
Activity.USER_ACCOUNT_LIST.title); Activity.USER_ACCOUNT_LIST.title);
injectActivitySelection(userAccounts, Activity.USER_ACCOUNT_LIST.createSelection()); injectActivitySelection(userAccounts, Activity.USER_ACCOUNT_LIST.createSelection());
} else {
// otherwise show the user account form for current user
final TreeItem userAccounts = this.widgetFactory.treeItemLocalized(
navigation,
Activity.USER_ACCOUNT_FORM.title);
injectActivitySelection(
userAccounts,
Activity.USER_ACCOUNT_FORM.createSelection()
.withEntity(this.currentUser.get().getEntityKey())
.withAttribute(AttributeKeys.READ_ONLY, "true"));
}
// //
// final TreeItem configs = this.widgetFactory.treeItemLocalized( // final TreeItem configs = this.widgetFactory.treeItemLocalized(
// navigation, // navigation,

View file

@ -112,7 +112,6 @@ public class ActivitySelection {
} }
return this; return this;
} }
public ActivitySelection withParentEntity(final EntityKey parentEntityKey) { public ActivitySelection withParentEntity(final EntityKey parentEntityKey) {

View file

@ -9,10 +9,8 @@
package ch.ethz.seb.sebserver.gui.service.page.content; package ch.ethz.seb.sebserver.gui.service.page.content;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.apache.commons.lang3.BooleanUtils;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -31,7 +29,6 @@ import ch.ethz.seb.sebserver.gui.service.form.FormHandle;
import ch.ethz.seb.sebserver.gui.service.form.PageFormService; import ch.ethz.seb.sebserver.gui.service.form.PageFormService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; 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.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.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.page.action.InstitutionActions; import ch.ethz.seb.sebserver.gui.service.page.action.InstitutionActions;
@ -68,44 +65,45 @@ public class InstitutionForm implements TemplateComposer {
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Compose Institutoion Form"); log.debug("Compose Institutoion Form within PageContext: {}", pageContext);
} }
final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory(); final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory();
final boolean readonly = BooleanUtils.toBoolean( final EntityKey entityKey = pageContext.getEntityKey();
pageContext.getAttribute(AttributeKeys.READ_ONLY, "true"));
final boolean createNew = BooleanUtils.toBoolean(
pageContext.getAttribute(AttributeKeys.CREATE_NEW, "false"));
// get data or create new and handle error // get data or create new and handle error
Institution institution = null; final Institution institution = (entityKey == null)
PageContext formContext = pageContext; ? new Institution(null, null, null, null, false)
: this.restService
if (createNew) {
institution = this.restService
.getBuilder(NewInstitution.class)
.withQueryParam(Domain.INSTITUTION.ATTR_NAME, "[NEW-" + UUID.randomUUID() + "]")
.call()
.get(pageContext::notifyError);
formContext = pageContext.withEntityKey(institution.getEntityKey());
} else {
final String instId = pageContext.getAttribute(AttributeKeys.ENTITY_ID);
institution = this.restService
.getBuilder(GetInstitution.class) .getBuilder(GetInstitution.class)
.withURIVariable(API.PARAM_MODEL_ID, instId) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call() .call()
.get(pageContext::notifyError); .get(pageContext::notifyError);
}
if (institution == null) { if (institution == null) {
// TODO should here be a forward to institution list page for SEB Admin? log.error("Failed to get Institution. "
+ "Error was notified to the User. "
+ "See previous logs for more infomation");
return; return;
} }
// new PageContext with actual EntityKey
final PageContext formContext = pageContext;
pageContext.withEntityKey(institution.getEntityKey());
if (log.isDebugEnabled()) {
log.debug("Institution Form for Institution {}", institution.name);
}
// the default page layout with interactive title // the default page layout with interactive title
final LocTextKey titleKey = new LocTextKey(
(entityKey != null)
? "sebserver.institution.form.title"
: "sebserver.institution.form.title.new",
institution.name);
final Composite content = widgetFactory.defaultPageLayout( final Composite content = widgetFactory.defaultPageLayout(
formContext.getParent(), formContext.getParent(),
new LocTextKey("sebserver.institution.form.title", institution.name), titleKey,
ActionDefinition.INSTITUTION_SAVE, ActionDefinition.INSTITUTION_SAVE,
title -> event -> { title -> event -> {
final Entity entity = (Entity) event.source; final Entity entity = (Entity) event.source;
@ -118,7 +116,7 @@ public class InstitutionForm implements TemplateComposer {
// The Institution form // The Institution form
final FormHandle<Institution> formHandle = this.pageFormService.getBuilder( final FormHandle<Institution> formHandle = this.pageFormService.getBuilder(
formContext.copyOf(content), 4) formContext.copyOf(content), 4)
.readonly(readonly) .readonly(pageContext.isReadonly())
.putStaticValue("id", institution.getModelId()) .putStaticValue("id", institution.getModelId())
.addTextField( .addTextField(
Domain.INSTITUTION.ATTR_NAME, Domain.INSTITUTION.ATTR_NAME,
@ -130,36 +128,37 @@ public class InstitutionForm implements TemplateComposer {
"sebserver.institution.form.urlSuffix", "sebserver.institution.form.urlSuffix",
institution.urlSuffix, 2) institution.urlSuffix, 2)
.addEmptyCell() .addEmptyCell()
.addImageUpload( .addImageUploadIf(() -> entityKey != null,
Domain.INSTITUTION.ATTR_LOGO_IMAGE, Domain.INSTITUTION.ATTR_LOGO_IMAGE,
"sebserver.institution.form.logoImage", "sebserver.institution.form.logoImage",
institution.logoImage, 2) institution.logoImage, 2)
.buildFor( .buildFor((entityKey == null)
this.restService.getRestCall(SaveInstitution.class), ? this.restService.getRestCall(NewInstitution.class)
: this.restService.getRestCall(SaveInstitution.class),
InstitutionActions.postSaveAdapter(pageContext)); InstitutionActions.postSaveAdapter(pageContext));
// propagate content actions to action-pane // propagate content actions to action-pane
if (readonly) { final boolean writeGrant = this.currentUser.hasPrivilege(PrivilegeType.WRITE, institution);
if (this.currentUser.hasPrivilege(PrivilegeType.WRITE, institution)) { final boolean modifyGrant = this.currentUser.hasPrivilege(PrivilegeType.MODIFY, institution);
if (pageContext.isReadonly()) {
formContext.createAction(ActionDefinition.INSTITUTION_NEW) formContext.createAction(ActionDefinition.INSTITUTION_NEW)
.withExec(InstitutionActions::newInstitution) .withExec(InstitutionActions::newInstitution)
.publish(); .publishIf(() -> writeGrant);
}
if (this.currentUser.hasPrivilege(PrivilegeType.MODIFY, institution)) {
formContext.createAction(ActionDefinition.INSTITUTION_MODIFY) formContext.createAction(ActionDefinition.INSTITUTION_MODIFY)
.withExec(InstitutionActions::editInstitution) .withExec(InstitutionActions::editInstitution)
.publish(); .publishIf(() -> modifyGrant);
if (!institution.isActive()) { if (!institution.isActive()) {
formContext.createAction(ActionDefinition.INSTITUTION_ACTIVATE) formContext.createAction(ActionDefinition.INSTITUTION_ACTIVATE)
.withExec(InstitutionActions::activateInstitution) .withExec(InstitutionActions::activateInstitution)
.publish(); .publishIf(() -> modifyGrant);
} else { } else {
formContext.createAction(ActionDefinition.INSTITUTION_DEACTIVATE) formContext.createAction(ActionDefinition.INSTITUTION_DEACTIVATE)
.withExec(InstitutionActions::deactivateInstitution) .withExec(InstitutionActions::deactivateInstitution)
.withConfirm(confirmDeactivation(institution)) .withConfirm(confirmDeactivation(institution))
.publish(); .publishIf(() -> modifyGrant);
}
} }
} else { } else {
formContext.createAction(ActionDefinition.INSTITUTION_SAVE) formContext.createAction(ActionDefinition.INSTITUTION_SAVE)
.withExec(formHandle::postChanges) .withExec(formHandle::postChanges)

View file

@ -8,13 +8,14 @@
package ch.ethz.seb.sebserver.gui.service.page.content; package ch.ethz.seb.sebserver.gui.service.page.content;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution; import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
@ -25,6 +26,7 @@ import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.page.action.InstitutionActions; import ch.ethz.seb.sebserver.gui.service.page.action.InstitutionActions;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutions; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutions;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.service.table.EntityTable; import ch.ethz.seb.sebserver.gui.service.table.EntityTable;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@ -34,28 +36,31 @@ import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@GuiProfile @GuiProfile
public class InstitutionList implements TemplateComposer { public class InstitutionList implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(InstitutionList.class);
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
private final RestService restService; private final RestService restService;
private final CurrentUser currentUser;
protected InstitutionList( protected InstitutionList(
final WidgetFactory widgetFactory, final WidgetFactory widgetFactory,
final RestService restService) { final RestService restService,
final CurrentUser currentUser) {
this.widgetFactory = widgetFactory; this.widgetFactory = widgetFactory;
this.restService = restService; this.restService = restService;
this.currentUser = currentUser;
} }
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
final Composite content = new Composite(pageContext.getParent(), SWT.NONE);
final GridLayout contentLayout = new GridLayout();
contentLayout.marginLeft = 10;
content.setLayout(contentLayout);
content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// title if (log.isDebugEnabled()) {
this.widgetFactory.labelLocalizedTitle( log.debug("Compose Institutoion list within PageContext: {}", pageContext);
content, }
final Composite content = this.widgetFactory.defaultPageLayout(
pageContext.getParent(),
new LocTextKey("sebserver.institution.list.title")); new LocTextKey("sebserver.institution.list.title"));
// table // table
@ -82,15 +87,15 @@ public class InstitutionList implements TemplateComposer {
// propagate content actions to action-pane // propagate content actions to action-pane
pageContext.createAction(ActionDefinition.INSTITUTION_NEW) pageContext.createAction(ActionDefinition.INSTITUTION_NEW)
.withExec(InstitutionActions::newInstitution) .withExec(InstitutionActions::newInstitution)
.publish() .publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.WRITE, EntityType.INSTITUTION))
.createAction(ActionDefinition.INSTITUTION_VIEW) .createAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST)
.withSelectionSupplier(table::getSelection) .withSelectionSupplier(table::getSelection)
.withExec(InstitutionActions::viewInstitution) .withExec(InstitutionActions::viewInstitution)
.publish() .publish()
.createAction(ActionDefinition.INSTITUTION_MODIFY) .createAction(ActionDefinition.INSTITUTION_MODIFY_FROM__LIST)
.withSelectionSupplier(table::getSelection) .withSelectionSupplier(table::getSelection)
.withExec(InstitutionActions::editInstitutionFromList) .withExec(InstitutionActions::editInstitutionFromList)
.publish(); .publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.MODIFY, EntityType.INSTITUTION));
} }

View file

@ -8,13 +8,24 @@
package ch.ethz.seb.sebserver.gui.service.page.content; package ch.ethz.seb.sebserver.gui.service.page.content;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.form.PageFormService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@Lazy @Lazy
@ -22,20 +33,48 @@ import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@GuiProfile @GuiProfile
public class UserAccountForm implements TemplateComposer { public class UserAccountForm implements TemplateComposer {
private final WidgetFactory widgetFactory; private static final Logger log = LoggerFactory.getLogger(UserAccountForm.class);
private final PageFormService pageFormService;
private final RestService restService; private final RestService restService;
private final CurrentUser currentUser;
protected UserAccountForm( protected UserAccountForm(
final WidgetFactory widgetFactory, final PageFormService pageFormService,
final RestService restService) { final RestService restService,
final CurrentUser currentUser) {
this.widgetFactory = widgetFactory; this.pageFormService = pageFormService;
this.restService = restService; this.restService = restService;
this.currentUser = currentUser;
} }
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
// TODO Auto-generated method stub
if (log.isDebugEnabled()) {
log.debug("Compose User Account Form within PageContext: {}", pageContext);
}
final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory();
final EntityKey entityKey = pageContext.getEntityKey();
// get data or create new and handle error
final UserAccount userAccount = (entityKey == null)
? new UserMod(
UUID.randomUUID().toString(),
this.currentUser.get().institutionId)
: this.restService
.getBuilder(GetUserAccount.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.get(pageContext::notifyError);
if (userAccount == null) {
log.error(
"Failed to get UserAccount. Error was notified to the User. See previous logs for more infomation");
return;
}
} }

View file

@ -9,18 +9,25 @@
package ch.ethz.seb.sebserver.gui.service.page.content; package ch.ethz.seb.sebserver.gui.service.page.content;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; 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.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.page.action.UserAccountActions;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccounts; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccounts;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition.TableFilterAttribute; import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition.TableFilterAttribute;
import ch.ethz.seb.sebserver.gui.service.table.EntityTable; import ch.ethz.seb.sebserver.gui.service.table.EntityTable;
@ -32,22 +39,33 @@ import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@GuiProfile @GuiProfile
public class UserAccountList implements TemplateComposer { public class UserAccountList implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(UserAccountList.class);
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
private final RestService restService; private final RestService restService;
private final CurrentUser currentUser;
private final int pageSize; private final int pageSize;
protected UserAccountList( protected UserAccountList(
final WidgetFactory widgetFactory, final WidgetFactory widgetFactory,
final RestService restService, final RestService restService,
final CurrentUser currentUser,
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) { @Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
this.widgetFactory = widgetFactory; this.widgetFactory = widgetFactory;
this.restService = restService; this.restService = restService;
this.currentUser = currentUser;
this.pageSize = (pageSize != null) ? pageSize : 20; this.pageSize = (pageSize != null) ? pageSize : 20;
} }
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
if (log.isDebugEnabled()) {
log.debug("Compose User Account list within PageContext: {}", pageContext);
}
// content page layout with title
final Composite content = this.widgetFactory.defaultPageLayout( final Composite content = this.widgetFactory.defaultPageLayout(
pageContext.getParent(), pageContext.getParent(),
new LocTextKey("sebserver.useraccount.list.title")); new LocTextKey("sebserver.useraccount.list.title"));
@ -87,6 +105,18 @@ public class UserAccountList implements TemplateComposer {
true)) true))
.compose(content); .compose(content);
// propagate content actions to action-pane
pageContext.createAction(ActionDefinition.USER_ACCOUNT_NEW)
.withExec(UserAccountActions::newUserAccount)
.publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.WRITE, EntityType.USER))
.createAction(ActionDefinition.USER_ACCOUNT_VIEW)
.withSelectionSupplier(table::getSelection)
.withExec(UserAccountActions::viewUserAccountFromList)
.publish()
.createAction(ActionDefinition.USER_ACCOUNT_MODIFY)
.withSelectionSupplier(table::getSelection)
.withExec(UserAccountActions::editUserAccountFromList)
.publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.MODIFY, EntityType.USER));
} }
private String getLocaleDisplayText(final UserInfo userInfo) { private String getLocaleDisplayText(final UserInfo userInfo) {

View file

@ -14,6 +14,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.BooleanUtils;
import org.eclipse.rap.rwt.widgets.DialogCallback; import org.eclipse.rap.rwt.widgets.DialogCallback;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
@ -168,6 +169,11 @@ public class PageContextImpl implements PageContext {
} }
} }
@Override
public boolean isReadonly() {
return BooleanUtils.toBoolean(getAttribute(AttributeKeys.READ_ONLY, "true"));
}
@Override @Override
public EntityKey getEntityKey() { public EntityKey getEntityKey() {
if (hasAttribute(AttributeKeys.ENTITY_ID) && hasAttribute(AttributeKeys.ENTITY_TYPE)) { if (hasAttribute(AttributeKeys.ENTITY_ID) && hasAttribute(AttributeKeys.ENTITY_TYPE)) {
@ -192,6 +198,9 @@ public class PageContextImpl implements PageContext {
@Override @Override
public PageContext withEntityKey(final EntityKey entityKey) { public PageContext withEntityKey(final EntityKey entityKey) {
if (entityKey == null) {
return this;
}
return withAttribute(AttributeKeys.ENTITY_ID, entityKey.modelId) return withAttribute(AttributeKeys.ENTITY_ID, entityKey.modelId)
.withAttribute(AttributeKeys.ENTITY_TYPE, entityKey.entityType.name()); .withAttribute(AttributeKeys.ENTITY_TYPE, entityKey.entityType.name());
} }
@ -254,7 +263,7 @@ public class PageContextImpl implements PageContext {
public void applyConfirmDialog(final LocTextKey confirmMessage, final Runnable onOK) { public void applyConfirmDialog(final LocTextKey confirmMessage, final Runnable onOK) {
final Message messageBox = new Message( final Message messageBox = new Message(
this.root.getShell(), this.root.getShell(),
this.i18nSupport.getText("org.sebserver.dialog.confirm.title"), this.i18nSupport.getText("sebserver.dialog.confirm.title"),
this.i18nSupport.getText(confirmMessage), this.i18nSupport.getText(confirmMessage),
SWT.OK | SWT.CANCEL); SWT.OK | SWT.CANCEL);
messageBox.setMarkupEnabled(true); messageBox.setMarkupEnabled(true);

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api; package ch.ethz.seb.sebserver.gui.service.remote.webservice.api;
import org.springframework.util.MultiValueMap;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
public interface FormBinding { public interface FormBinding {
@ -16,4 +18,6 @@ public interface FormBinding {
String getFormAsJson(); String getFormAsJson();
MultiValueMap<String, String> getFormAsQueryAttributes();
} }

View file

@ -32,6 +32,7 @@ import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService.SortOrder; import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService.SortOrder;
@ -123,14 +124,14 @@ public abstract class RestCall<T> {
return new RestCallBuilder(); return new RestCallBuilder();
} }
public final class RestCallBuilder { public class RestCallBuilder {
private final HttpHeaders httpHeaders = new HttpHeaders(); private final HttpHeaders httpHeaders = new HttpHeaders();
private String body = null; private String body = null;
private final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(); private final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
private final Map<String, String> uriVariables = new HashMap<>(); private final Map<String, String> uriVariables = new HashMap<>();
RestCallBuilder() { protected RestCallBuilder() {
this.httpHeaders.set( this.httpHeaders.set(
HttpHeaders.CONTENT_TYPE, HttpHeaders.CONTENT_TYPE,
RestCall.this.contentType.toString()); RestCall.this.contentType.toString());
@ -192,8 +193,13 @@ public abstract class RestCall<T> {
} }
public RestCallBuilder withFormBinding(final FormBinding formBinding) { public RestCallBuilder withFormBinding(final FormBinding formBinding) {
final EntityKey entityKey = formBinding.entityKey();
if (entityKey != null) {
return withURIVariable(API.PARAM_MODEL_ID, formBinding.entityKey().modelId) return withURIVariable(API.PARAM_MODEL_ID, formBinding.entityKey().modelId)
.withBody(formBinding.getFormAsJson()); .withBody(formBinding.getFormAsJson());
} else {
return withQueryParams(formBinding.getFormAsQueryAttributes());
}
} }
public RestCallBuilder onlyActive(final boolean active) { public RestCallBuilder onlyActive(final boolean active) {
@ -205,14 +211,14 @@ public abstract class RestCall<T> {
return RestCall.this.exchange(this); return RestCall.this.exchange(this);
} }
String buildURI() { protected String buildURI() {
return RestCall.this.restService.getWebserviceURIBuilder() return RestCall.this.restService.getWebserviceURIBuilder()
.path(RestCall.this.path) .path(RestCall.this.path)
.queryParams(this.queryParams) .queryParams(this.queryParams)
.toUriString(); .toUriString();
} }
HttpEntity<?> buildRequestEntity() { protected HttpEntity<?> buildRequestEntity() {
if (this.body != null) { if (this.body != null) {
return new HttpEntity<>(this.body, this.httpHeaders); return new HttpEntity<>(this.body, this.httpHeaders);
} else { } else {

View file

@ -31,7 +31,7 @@ public class SaveInstitution extends RestCall<Institution> {
}, },
HttpMethod.PUT, HttpMethod.PUT,
MediaType.APPLICATION_JSON_UTF8, MediaType.APPLICATION_JSON_UTF8,
API.INSTITUTION_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT); API.INSTITUTION_ENDPOINT);
} }
} }

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.remote.webservice.api.useraccount;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class ActivateUserAccount extends RestCall<EntityProcessingReport> {
protected ActivateUserAccount() {
super(
new TypeReference<EntityProcessingReport>() {
},
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.USER_ACCOUNT_ENDPOINT + API.PATH_VAR_ACTIVE);
}
}

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.remote.webservice.api.useraccount;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class DeactivateUserAccount extends RestCall<EntityProcessingReport> {
protected DeactivateUserAccount() {
super(
new TypeReference<EntityProcessingReport>() {
},
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.USER_ACCOUNT_ENDPOINT + API.PATH_VAR_INACTIVE);
}
}

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.remote.webservice.api.useraccount;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class NewUserAccount extends RestCall<UserInfo> {
protected NewUserAccount() {
super(
new TypeReference<UserInfo>() {
},
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.USER_ACCOUNT_ENDPOINT);
}
}

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.remote.webservice.api.useraccount;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class SaveUserAccount extends RestCall<UserInfo> {
protected SaveUserAccount() {
super(
new TypeReference<UserInfo>() {
},
HttpMethod.PUT,
MediaType.APPLICATION_JSON_UTF8,
API.USER_ACCOUNT_ENDPOINT);
}
}

View file

@ -15,6 +15,8 @@ import java.nio.charset.StandardCharsets;
import java.util.Base64; import java.util.Base64;
import org.apache.commons.codec.binary.Base64InputStream; import org.apache.commons.codec.binary.Base64InputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.fileupload.FileDetails; import org.eclipse.rap.fileupload.FileDetails;
import org.eclipse.rap.fileupload.FileUploadHandler; import org.eclipse.rap.fileupload.FileUploadHandler;
import org.eclipse.rap.fileupload.FileUploadReceiver; import org.eclipse.rap.fileupload.FileUploadReceiver;
@ -29,7 +31,6 @@ import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils;
import ch.ethz.seb.sebserver.gui.service.push.ServerPushContext; import ch.ethz.seb.sebserver.gui.service.push.ServerPushContext;
import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
@ -68,7 +69,7 @@ public class ImageUpload extends Composite {
final String contentType = details.getContentType(); final String contentType = details.getContentType();
if (contentType != null && contentType.startsWith("image")) { if (contentType != null && contentType.startsWith("image")) {
ImageUpload.this.imageBase64 = Base64.getEncoder() ImageUpload.this.imageBase64 = Base64.getEncoder()
.encodeToString(StreamUtils.copyToByteArray(stream)); .encodeToString(IOUtils.toByteArray(stream));
} }
} catch (final Exception e) { } catch (final Exception e) {
log.error("Error while trying to upload image", e); log.error("Error while trying to upload image", e);
@ -116,7 +117,7 @@ public class ImageUpload extends Composite {
} }
public void setImageBase64(final String imageBase64) { public void setImageBase64(final String imageBase64) {
if (imageBase64 == null) { if (StringUtils.isBlank(imageBase64)) {
return; return;
} }

View file

@ -73,11 +73,10 @@ public interface EntityDAO<T extends Entity, M extends ModelIdAware> {
/** Use this to save/modify an entity. /** Use this to save/modify an entity.
* *
* @param modelId the model id of the entity to save
* @param data entity instance containing all data that should be saved * @param data entity instance containing all data that should be saved
* @return A Result of the entity instance where the successfully saved/modified entity data is available or a * @return A Result of the entity instance where the successfully saved/modified entity data is available or a
* reported exception on error case */ * reported exception on error case */
Result<T> save(String modelId, M data); Result<T> save(T data);
/** Use this to delete a set Entity by a Collection of EntityKey /** Use this to delete a set Entity by a Collection of EntityKey
* *

View file

@ -17,30 +17,69 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser;
public interface UserActivityLogDAO public interface UserActivityLogDAO extends
extends EntityDAO<UserActivityLog, UserActivityLog>, UserRelatedEntityDAO<UserActivityLog> { EntityDAO<UserActivityLog, UserActivityLog>,
UserRelatedEntityDAO<UserActivityLog> {
/** All activity types */
enum ActivityType { enum ActivityType {
CREATE, CREATE,
IMPORT, IMPORT,
MODIFY, MODIFY,
DEACTIVATE, DEACTIVATE,
ACTIVATE, ACTIVATE,
ARCHIVE,
DELETE DELETE
} }
/** Create a user activity log entry for the current user of activity type CREATE
*
* @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */
public <E extends Entity> Result<E> logCreate(E entity);
/** Create a user activity log entry for the current user of activity type IMPORT
*
* @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */
public <E extends Entity> Result<E> logImport(E entity);
/** Create a user activity log entry for the current user of activity type MODIFY
*
* @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */
public <E extends Entity> Result<E> logModify(E entity);
/** Create a user activity log entry for the current user of activity type ACTIVATE
*
* @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */
public <E extends Entity> Result<E> logActivate(E entity);
/** Create a user activity log entry for the current user of activity type DEACTIVATE
*
* @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */
public <E extends Entity> Result<E> logDeactivate(E entity);
/** Create a user activity log entry for the current user of activity type DELETE
*
* @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */
public <E extends Entity> Result<E> logDelete(E entity);
/** Creates a user activity log entry for the current user. /** Creates a user activity log entry for the current user.
* *
* @param activityType the activity type * @param activityType the activity type
* @param entity the Entity * @param entity the Entity
* @param message an optional message */ * @param message an optional message
* @return Result of the Entity or referring to an Error id happened */
<E extends Entity> Result<E> log(ActivityType activityType, E entity, String message); <E extends Entity> Result<E> log(ActivityType activityType, E entity, String message);
/** Creates a user activity log entry for the current user. /** Creates a user activity log entry for the current user.
* *
* @param actionType the action type * @param actionType the action type
* @param entity the Entity */ * @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */
<E extends Entity> Result<E> log(ActivityType activityType, E entity); <E extends Entity> Result<E> log(ActivityType activityType, E entity);
/** Creates a user activity log entry for the current user. /** Creates a user activity log entry for the current user.
@ -54,7 +93,8 @@ public interface UserActivityLogDAO
* *
* @param activityType the activity type * @param activityType the activity type
* @param entityType the EntityType * @param entityType the EntityType
* @param message the message */ * @param message the message
* @return Result of the Entity or referring to an Error id happened */
<T> Result<T> log(ActivityType activityType, EntityType entityType, String entityId, String message, T data); <T> Result<T> log(ActivityType activityType, EntityType entityType, String entityId, String message, T data);
/** Creates a user activity log entry. /** Creates a user activity log entry.
@ -62,7 +102,8 @@ public interface UserActivityLogDAO
* @param user for specified SEBServerUser instance * @param user for specified SEBServerUser instance
* @param activityType the activity type * @param activityType the activity type
* @param entity the Entity * @param entity the Entity
* @param message an optional message */ * @param message an optional message
* @return Result of the Entity or referring to an Error id happened */
<E extends Entity> Result<E> log( <E extends Entity> Result<E> log(
SEBServerUser user, SEBServerUser user,
ActivityType activityType, ActivityType activityType,
@ -74,7 +115,8 @@ public interface UserActivityLogDAO
* @param user for specified SEBServerUser instance * @param user for specified SEBServerUser instance
* @param activityType the activity type * @param activityType the activity type
* @param entityType the entity type * @param entityType the entity type
* @param entityId the entity id (primary key or UUID) */ * @param entityId the entity id (primary key or UUID)
* @return Result of the Entity or referring to an Error id happened */
default <E extends Entity> Result<E> log( default <E extends Entity> Result<E> log(
final SEBServerUser user, final SEBServerUser user,
final ActivityType activityType, final ActivityType activityType,

View file

@ -22,11 +22,11 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSuppor
* within SEBServerUser. */ * within SEBServerUser. */
public interface UserDAO extends ActivatableEntityDAO<UserInfo, UserMod>, BulkActionSupportDAO<UserInfo> { public interface UserDAO extends ActivatableEntityDAO<UserInfo, UserMod>, BulkActionSupportDAO<UserInfo> {
/** Use this to get the user id (PK) from a given UUID. /** Use this to get the user id (PK) from a given modelId (users UUID).
* *
* @param uuid The UUID of the user * @param uuid The UUID of the user
* @return the user id (PK) from a given UUID. */ * @return the user id (PK) from a given UUID. */
Result<Long> pkForModelId(String uuid); Result<Long> pkForModelId(String modelId);
/** Use this to get UserInfo by users username /** Use this to get UserInfo by users username
* *
@ -34,20 +34,20 @@ public interface UserDAO extends ActivatableEntityDAO<UserInfo, UserMod>, BulkAc
* @return a Result of UserInfo data from user with the specified username. Or an exception result on error case */ * @return a Result of UserInfo data from user with the specified username. Or an exception result on error case */
Result<UserInfo> byUsername(String username); Result<UserInfo> byUsername(String username);
/** Set given password as new password for specified user account.
*
* @param modelId the model id of the user account to change the password
* @param newPassword the new verified password that is encrypted and stored as the new password for the user
* account
* @return a Result of user account information. Or an exception result on error case */
Result<UserInfo> changePassword(String modelId, String newPassword);
/** Use this to get the SEBServerUser principal for a given username. /** Use this to get the SEBServerUser principal for a given username.
* *
* @param username The username of the user to get SEBServerUser from * @param username The username of the user to get SEBServerUser from
* @return a Result of SEBServerUser for specified username. Or an exception result on error case */ * @return a Result of SEBServerUser for specified username. Or an exception result on error case */
Result<SEBServerUser> sebServerUserByUsername(String username); Result<SEBServerUser> sebServerUserByUsername(String username);
// /** Use this to get a Collection of filtered UserInfo. The filter criteria
// * from given UserFilter instance will be translated to SQL query and
// * the filtering happens on data-base level
// *
// * @param filter The UserFilter instance containing all filter criteria
// * @return a Result of Collection of filtered UserInfo. Or an exception result on error case */
// Result<Collection<UserInfo>> allMatching(final UserFilter filter);
/** Use this to get a Collection containing EntityKey's of all entities that belongs to a given User. /** Use this to get a Collection containing EntityKey's of all entities that belongs to a given User.
* *
* @param uuid The UUID of the user * @param uuid The UUID of the user

View file

@ -157,12 +157,11 @@ public class ExamDAOImpl implements ExamDAO {
@Override @Override
@Transactional @Transactional
public Result<Exam> save(final String modelId, final Exam exam) { public Result<Exam> save(final Exam exam) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final Long pk = Long.parseLong(modelId);
final ExamRecord examRecord = new ExamRecord( final ExamRecord examRecord = new ExamRecord(
pk, exam.id,
null, null, null, null, null, null, null, null,
(exam.supporter != null) (exam.supporter != null)
? StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR) ? StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR)
@ -173,7 +172,7 @@ public class ExamDAOImpl implements ExamDAO {
BooleanUtils.toIntegerObject(exam.active)); BooleanUtils.toIntegerObject(exam.active));
this.examRecordMapper.updateByPrimaryKeySelective(examRecord); this.examRecordMapper.updateByPrimaryKeySelective(examRecord);
return this.examRecordMapper.selectByPrimaryKey(pk); return this.examRecordMapper.selectByPrimaryKey(exam.id);
}) })
.flatMap(this::toDomainModel) .flatMap(this::toDomainModel)
.onErrorDo(TransactionHandler::rollback); .onErrorDo(TransactionHandler::rollback);

View file

@ -122,12 +122,11 @@ public class IndicatorDAOImpl implements IndicatorDAO {
@Override @Override
@Transactional @Transactional
public Result<Indicator> save(final String modelId, final Indicator modified) { public Result<Indicator> save(final Indicator modified) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final Long pk = Long.parseLong(modelId);
final IndicatorRecord newRecord = new IndicatorRecord( final IndicatorRecord newRecord = new IndicatorRecord(
pk, modified.id,
null, null,
modified.type.name(), modified.type.name(),
modified.name, modified.name,
@ -137,7 +136,7 @@ public class IndicatorDAOImpl implements IndicatorDAO {
// update also the thresholds // update also the thresholds
this.thresholdRecordMapper.deleteByExample() this.thresholdRecordMapper.deleteByExample()
.where(ThresholdRecordDynamicSqlSupport.indicatorId, isEqualTo(pk)) .where(ThresholdRecordDynamicSqlSupport.indicatorId, isEqualTo(modified.id))
.build() .build()
.execute(); .execute();
@ -145,12 +144,12 @@ public class IndicatorDAOImpl implements IndicatorDAO {
.stream() .stream()
.map(threshold -> new ThresholdRecord( .map(threshold -> new ThresholdRecord(
null, null,
pk, modified.id,
new BigDecimal(threshold.value), new BigDecimal(threshold.value),
threshold.color)) threshold.color))
.forEach(this.thresholdRecordMapper::insert); .forEach(this.thresholdRecordMapper::insert);
return this.indicatorRecordMapper.selectByPrimaryKey(pk); return this.indicatorRecordMapper.selectByPrimaryKey(modified.id);
}) })
.flatMap(this::toDomainModel) .flatMap(this::toDomainModel)
.onErrorDo(TransactionHandler::rollback); .onErrorDo(TransactionHandler::rollback);

View file

@ -26,8 +26,8 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution; import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@ -129,7 +129,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
@Override @Override
@Transactional @Transactional
public Result<Institution> save(final String modelId, final Institution institution) { public Result<Institution> save(final Institution institution) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final Long count = this.institutionRecordMapper.countByExample() final Long count = this.institutionRecordMapper.countByExample()
@ -142,16 +142,15 @@ public class InstitutionDAOImpl implements InstitutionDAO {
throw new FieldValidationException("name", "institution:name:exists"); throw new FieldValidationException("name", "institution:name:exists");
} }
final Long pk = Long.parseLong(modelId);
final InstitutionRecord newRecord = new InstitutionRecord( final InstitutionRecord newRecord = new InstitutionRecord(
pk, institution.id,
institution.name, institution.name,
institution.urlSuffix, institution.urlSuffix,
null, null,
institution.logoImage); institution.logoImage);
this.institutionRecordMapper.updateByPrimaryKeySelective(newRecord); this.institutionRecordMapper.updateByPrimaryKeySelective(newRecord);
return this.institutionRecordMapper.selectByPrimaryKey(pk); return this.institutionRecordMapper.selectByPrimaryKey(institution.id);
}) })
.flatMap(InstitutionDAOImpl::toDomainModel) .flatMap(InstitutionDAOImpl::toDomainModel)
.onErrorDo(TransactionHandler::rollback); .onErrorDo(TransactionHandler::rollback);

View file

@ -134,12 +134,11 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
@Override @Override
@Transactional @Transactional
public Result<LmsSetup> save(final String modelId, final LmsSetup lmsSetup) { public Result<LmsSetup> save(final LmsSetup lmsSetup) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final Long pk = Long.parseLong(modelId);
final LmsSetupRecord newRecord = new LmsSetupRecord( final LmsSetupRecord newRecord = new LmsSetupRecord(
pk, lmsSetup.id,
lmsSetup.institutionId, lmsSetup.institutionId,
lmsSetup.name, lmsSetup.name,
(lmsSetup.lmsType != null) ? lmsSetup.lmsType.name() : null, (lmsSetup.lmsType != null) ? lmsSetup.lmsType.name() : null,
@ -156,7 +155,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
null); null);
this.lmsSetupRecordMapper.updateByPrimaryKeySelective(newRecord); this.lmsSetupRecordMapper.updateByPrimaryKeySelective(newRecord);
return this.lmsSetupRecordMapper.selectByPrimaryKey(pk); return this.lmsSetupRecordMapper.selectByPrimaryKey(lmsSetup.id);
}) })
.flatMap(LmsSetupDAOImpl::toDomainModel) .flatMap(LmsSetupDAOImpl::toDomainModel)
.onErrorDo(TransactionHandler::rollback); .onErrorDo(TransactionHandler::rollback);

View file

@ -65,6 +65,42 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
return EntityType.USER_ACTIVITY_LOG; return EntityType.USER_ACTIVITY_LOG;
} }
@Override
@Transactional
public <E extends Entity> Result<E> logCreate(final E entity) {
return log(ActivityType.CREATE, entity);
}
@Override
@Transactional
public <E extends Entity> Result<E> logImport(final E entity) {
return log(ActivityType.IMPORT, entity);
}
@Override
@Transactional
public <E extends Entity> Result<E> logModify(final E entity) {
return log(ActivityType.MODIFY, entity);
}
@Override
@Transactional
public <E extends Entity> Result<E> logActivate(final E entity) {
return log(ActivityType.ACTIVATE, entity);
}
@Override
@Transactional
public <E extends Entity> Result<E> logDeactivate(final E entity) {
return log(ActivityType.DEACTIVATE, entity);
}
@Override
@Transactional
public <E extends Entity> Result<E> logDelete(final E entity) {
return log(ActivityType.DELETE, entity);
}
@Override @Override
@Transactional @Transactional
public <E extends Entity> Result<E> log( public <E extends Entity> Result<E> log(
@ -288,7 +324,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
@Override @Override
@Transactional @Transactional
public Result<UserActivityLog> save(final String modelId, final UserActivityLog modified) { public Result<UserActivityLog> save(final UserActivityLog modified) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return Result.ofTODO(); return Result.ofTODO();
} }

View file

@ -32,9 +32,9 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.WebSecurityConfig; import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; 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.UserMod;
@ -180,7 +180,7 @@ public class UserDaoImpl implements UserDAO {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
if (!userMod.newPasswordMatch()) { if (!userMod.newPasswordMatch()) {
throw new APIMessageException(ErrorMessage.PASSWORD_MISSMATCH); throw new APIMessageException(ErrorMessage.PASSWORD_MISMATCH);
} }
final UserRecord recordToSave = new UserRecord( final UserRecord recordToSave = new UserRecord(
@ -209,28 +209,46 @@ public class UserDaoImpl implements UserDAO {
@Override @Override
@Transactional @Transactional
public Result<UserInfo> save(final String modelId, final UserMod userMod) { public Result<UserInfo> changePassword(final String modelId, final String newPassword) {
return recordByUUID(modelId) return recordByUUID(modelId)
.map(record -> { .map(record -> {
final boolean changePWD = userMod.passwordChangeRequest();
if (changePWD && !userMod.newPasswordMatch()) {
throw new APIMessageException(ErrorMessage.PASSWORD_MISSMATCH);
}
final UserRecord newRecord = new UserRecord( final UserRecord newRecord = new UserRecord(
record.getId(), record.getId(),
null, null,
null, null,
userMod.name, null,
userMod.username, null,
(changePWD) ? this.userPasswordEncoder.encode(userMod.getNewPassword()) : null, this.userPasswordEncoder.encode(newPassword),
userMod.email, null,
userMod.locale.toLanguageTag(), null,
userMod.timeZone.getID(), null,
null);
this.userRecordMapper.updateByPrimaryKeySelective(newRecord);
return this.userRecordMapper.selectByPrimaryKey(record.getId());
})
.flatMap(this::toDomainModel)
.onErrorDo(TransactionHandler::rollback);
}
@Override
@Transactional
public Result<UserInfo> save(final UserInfo userInfo) {
return recordByUUID(userInfo.uuid)
.map(record -> {
final UserRecord newRecord = new UserRecord(
record.getId(),
null,
null,
userInfo.name,
userInfo.username,
null,
userInfo.email,
userInfo.locale.toLanguageTag(),
userInfo.timeZone.getID(),
null); null);
this.userRecordMapper.updateByPrimaryKeySelective(newRecord); this.userRecordMapper.updateByPrimaryKeySelective(newRecord);
updateRolesForUser(record.getId(), userMod.roles); updateRolesForUser(record.getId(), userInfo.roles);
return this.userRecordMapper.selectByPrimaryKey(record.getId()); return this.userRecordMapper.selectByPrimaryKey(record.getId());
}) })
.flatMap(this::toDomainModel) .flatMap(this::toDomainModel)

View file

@ -47,7 +47,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionServic
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
public abstract class EntityController<T extends GrantEntity, M extends GrantEntity> { public abstract class EntityController<T extends GrantEntity, M extends GrantEntity> {
@ -250,8 +249,8 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt
return this.beanValidationService.validateBean(requestModel) return this.beanValidationService.validateBean(requestModel)
.flatMap(this.entityDAO::createNew) .flatMap(this.entityDAO::createNew)
.flatMap(entity -> this.userActivityLogDAO.log(ActivityType.CREATE, entity)) .flatMap(this.userActivityLogDAO::logCreate)
.flatMap(entity -> this.notifySaved(requestModel, entity)) .flatMap(this::notifyCreated)
.getOrThrow(); .getOrThrow();
} }
@ -260,19 +259,16 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt
// **************** // ****************
@RequestMapping( @RequestMapping(
path = "/{modelId}",
method = RequestMethod.PUT, method = RequestMethod.PUT,
consumes = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
public T savePut( public T savePut(@Valid @RequestBody final T modifyData) {
@PathVariable final String modelId,
@Valid @RequestBody final M modifyData) {
return this.beanValidationService.validateBean(modifyData) return this.authorization.checkModify(modifyData)
.flatMap(this.authorization::checkModify) .flatMap(this::validForSave)
.flatMap(m -> this.entityDAO.save(modelId, m)) .flatMap(this.entityDAO::save)
.flatMap(e -> this.userActivityLogDAO.log(ActivityType.MODIFY, e)) .flatMap(this.userActivityLogDAO::logModify)
.flatMap(e -> notifySaved(modifyData, e)) .flatMap(this::notifySaved)
.getOrThrow(); .getOrThrow();
} }
@ -339,7 +335,19 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt
this.authorization::hasReadonlyGrant); this.authorization::hasReadonlyGrant);
} }
protected Result<T> notifySaved(final M modifyData, final T entity) { protected Result<T> notifyCreated(final T entity) {
return Result.of(entity);
}
protected Result<T> validForSave(final T entity) {
if (entity.getModelId() != null) {
return Result.of(entity);
} else {
return Result.ofError(new IllegalAPIArgumentException("Missing model identifier"));
}
}
protected Result<T> notifySaved(final T entity) {
return Result.of(entity); return Result.of(entity);
} }

View file

@ -223,7 +223,7 @@ public class ExamAdministrationController extends ActivatableEntityController<Ex
.getOrThrow(); .getOrThrow();
return this.indicatorDAO return this.indicatorDAO
.save(id, indicator) .save(indicator)
.getOrThrow(); .getOrThrow();
} }

View file

@ -73,11 +73,7 @@ public class InstitutionController extends ActivatableEntityController<Instituti
@Override @Override
protected Institution createNew(final POSTMapper postParams) { protected Institution createNew(final POSTMapper postParams) {
final Institution institution = new Institution(null, postParams); return new Institution(null, postParams);
if (this.institutionDAO.exists(institution.name)) {
throw new IllegalAPIArgumentException("institution:name:unique:" + institution.name);
}
return institution;
} }
} }

View file

@ -8,14 +8,22 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api; package ch.ethz.seb.sebserver.webservice.weblayer.api;
import javax.validation.Valid;
import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.SqlTable;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod; import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@ -25,6 +33,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint; import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint;
@ -35,9 +44,10 @@ import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint;
public class UserAccountController extends ActivatableEntityController<UserInfo, UserMod> { public class UserAccountController extends ActivatableEntityController<UserInfo, UserMod> {
private final ApplicationEventPublisher applicationEventPublisher; private final ApplicationEventPublisher applicationEventPublisher;
private final UserDAO userDAO;
public UserAccountController( public UserAccountController(
final UserDAO userDao, final UserDAO userDAO,
final AuthorizationService authorization, final AuthorizationService authorization,
final UserActivityLogDAO userActivityLogDAO, final UserActivityLogDAO userActivityLogDAO,
final PaginationService paginationService, final PaginationService paginationService,
@ -47,11 +57,12 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
super(authorization, super(authorization,
bulkActionService, bulkActionService,
userDao, userDAO,
userActivityLogDAO, userActivityLogDAO,
paginationService, paginationService,
beanValidationService); beanValidationService);
this.applicationEventPublisher = applicationEventPublisher; this.applicationEventPublisher = applicationEventPublisher;
this.userDAO = userDAO;
} }
@RequestMapping(path = "/me", method = RequestMethod.GET) @RequestMapping(path = "/me", method = RequestMethod.GET)
@ -72,19 +83,39 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
return UserRecordDynamicSqlSupport.userRecord; return UserRecordDynamicSqlSupport.userRecord;
} }
@Override
protected Result<UserInfo> notifySaved(final UserMod userData, final UserInfo userInfo) {
// handle password change; revoke access tokens if password has changed
if (userData.passwordChangeRequest() && userData.newPasswordMatch()) {
this.applicationEventPublisher.publishEvent(
new RevokeTokenEndpoint.RevokeTokenEvent(this, userInfo.username));
}
return Result.of(userInfo);
}
@Override @Override
protected UserMod createNew(final POSTMapper postParams) { protected UserMod createNew(final POSTMapper postParams) {
return new UserMod(null, postParams); return new UserMod(null, postParams);
} }
@RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.PASSWORD_PATH_SEGMENT,
method = RequestMethod.PUT,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public UserInfo changePassword(
@PathVariable final String modelId,
@Valid @RequestBody final PasswordChange passwordChange) {
if (!passwordChange.newPasswordMatch()) {
throw new APIMessageException(ErrorMessage.PASSWORD_MISMATCH);
}
return this.userDAO.byModelId(modelId)
.flatMap(this.authorization::checkWrite)
.flatMap(e -> this.userDAO.changePassword(modelId, passwordChange.getNewPassword()))
.flatMap(this::revokeAccessToken)
.flatMap(e -> this.userActivityLogDAO.log(ActivityType.MODIFY, e))
.getOrThrow();
}
private Result<UserInfo> revokeAccessToken(final UserInfo userInfo) {
return Result.tryCatch(() -> {
this.applicationEventPublisher.publishEvent(
new RevokeTokenEndpoint.RevokeTokenEvent(userInfo, userInfo.username));
return userInfo;
});
}
} }

View file

@ -13,6 +13,18 @@ sebserver.overall.action.modify.cancel.confirm=Are you sure to cancel? Modificat
sebserver.overall.action.filter=Apply Filter sebserver.overall.action.filter=Apply Filter
sebserver.overall.action.filter.clear=Clear Filter Criteria sebserver.overall.action.filter.clear=Clear Filter Criteria
################################
# Form validation and messages
################################
sebserver.form.validation.fieldError.size=The size must be between {3} and {4}
sebserver.form.validation.fieldError.name=Name is mandatory and must have a size between 3 and 255 character
sebserver.form.validation.fieldError.urlSuffix=URL Suffix must have a size between 3 and 255 character
sebserver.form.validation.fieldError.notNull=This field is mandatory
sebserver.error.unexpected=Unexpected Error
sebserver.page.message=Information
sebserver.dialog.confirm.title=Confirmation
################################ ################################
# Login Page # Login Page
################################ ################################
@ -51,15 +63,17 @@ sebserver.institution.list.column.urlSuffix=URL Suffix
sebserver.institution.list.column.active=Active sebserver.institution.list.column.active=Active
sebserver.institution.action.new=New Institution sebserver.institution.action.new=New Institution
sebserver.institution.action.view=View Institution sebserver.institution.action.list.view=View Selected
sebserver.institution.action.modify=Edit Institution sebserver.institution.action.modify=Edit Institution
sebserver.institution.action.list.modify=Edit Selected
sebserver.institution.action.save=Save Institution sebserver.institution.action.save=Save Institution
sebserver.institution.action.activate=Active sebserver.institution.action.activate=Active
sebserver.institution.action.deactivate=Active sebserver.institution.action.deactivate=Active
sebserver.institution.action.delete=Delete Institution sebserver.institution.action.delete=Delete Institution
sebserver.institution.info.pleaseSelect=Please Select an Institution from the Table first. sebserver.institution.info.pleaseSelect=Please Select an Institution first.
sebserver.institution.form.title=Institution ({0}) sebserver.institution.form.title.new=New Institution
sebserver.institution.form.title=Institution : {0}
sebserver.institution.form.name=Name sebserver.institution.form.name=Name
sebserver.institution.form.urlSuffix=URL Suffix sebserver.institution.form.urlSuffix=URL Suffix
@ -78,14 +92,15 @@ sebserver.useraccount.list.column.email=Mail
sebserver.useraccount.list.column.language=Language sebserver.useraccount.list.column.language=Language
sebserver.useraccount.list.column.active=Active sebserver.useraccount.list.column.active=Active
sebserver.useraccount.action.new=New User Account
sebserver.useraccount.action.view=View Selected
sebserver.useraccount.action.modify=Edit Selected
sebserver.useraccount.action.save=Save User Account
sebserver.useraccount.action.activate=Active
sebserver.useraccount.action.deactivate=Active
sebserver.useraccount.action.delete=Delete User Account
sebserver.useraccount.info.pleaseSelect=Please Select an User Account first.
################################
# Form validation and messages
################################
sebserver.form.validation.fieldError.size=The size must be between {3} and {4}
sebserver.form.validation.fieldError.name=Name is mandatory and must have a size between 3 and 255 character
sebserver.form.validation.fieldError.urlSuffix=URL Suffix must have a size between 3 and 255 character
sebserver.error.unexpected=Unexpected Error
sebserver.page.message=Information

View file

@ -169,7 +169,7 @@ Text.error {
color: #4a4a4a; color: #4a4a4a;
background-repeat: repeat; background-repeat: repeat;
background-position: left top; background-position: left top;
background-color: #A8322D; background-color: #82BE1E;
background-image: none; background-image: none;
text-shadow: none; text-shadow: none;
box-shadow: none; box-shadow: none;
@ -247,7 +247,7 @@ Shell-Titlebar.message {
background-color: #1f407a; background-color: #1f407a;
background-gradient-color: #1f407a; background-gradient-color: #1f407a;
color: white; color: white;
background-image: gradient( linear, left top, left bottom, from( #0069B4 ), to( #0069B4 ) ); background-image: gradient( linear, left top, left bottom, from( #1f407a ), to( #1f407a ) );
padding: 2px 5px 2px; padding: 2px 5px 2px;
margin: 0px; margin: 0px;
height: 22px; height: 22px;

View file

@ -187,7 +187,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
assertNotNull(errorMessage); assertNotNull(errorMessage);
assertTrue(errorMessage.size() > 0); assertTrue(errorMessage.size() > 0);
assertEquals("1010", errorMessage.get(0).messageCode); assertEquals("1200", errorMessage.get(0).messageCode);
// and predefined id should be ignored // and predefined id should be ignored
institution = new RestAPITestHelper() institution = new RestAPITestHelper()
@ -241,10 +241,9 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
// modify // modify
institution = new RestAPITestHelper() institution = new RestAPITestHelper()
.withAccessToken(sebAdminAccess) .withAccessToken(sebAdminAccess)
.withPath(API.INSTITUTION_ENDPOINT).withPath("/") .withPath(API.INSTITUTION_ENDPOINT)
.withPath(String.valueOf(institution.id))
.withMethod(HttpMethod.PUT) .withMethod(HttpMethod.PUT)
.withBodyJson(new Institution(null, "testInstitution", "testSuffix", null, null)) .withBodyJson(new Institution(institution.id, "testInstitution", "testSuffix", null, null))
.withExpectedStatus(HttpStatus.OK) .withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Institution>() { .getAsObject(new TypeReference<Institution>() {
}); });

View file

@ -68,7 +68,7 @@ public class LmsSetupAPITest extends AdministrationAPIIntegrationTester {
lmsSetup = new RestAPITestHelper() lmsSetup = new RestAPITestHelper()
.withAccessToken(getAdminInstitution1Access()) .withAccessToken(getAdminInstitution1Access())
.withPath(API.LMS_SETUP_ENDPOINT + "/" + lmsSetup.id) .withPath(API.LMS_SETUP_ENDPOINT)
.withMethod(HttpMethod.PUT) .withMethod(HttpMethod.PUT)
.withBodyJson(modified) .withBodyJson(modified)
.withExpectedStatus(HttpStatus.OK) .withExpectedStatus(HttpStatus.OK)

View file

@ -39,6 +39,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityName; import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; 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.UserMod;
@ -572,8 +573,8 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
assertEquals("[EXAM_SUPPORTER]", String.valueOf(user.roles)); assertEquals("[EXAM_SUPPORTER]", String.valueOf(user.roles));
// change userName, email and roles // change userName, email and roles
final UserMod modifyUser = new UserMod(new UserInfo( final UserInfo modifyUser = new UserInfo(
null, user.uuid,
user.getInstitutionId(), user.getInstitutionId(),
user.getName(), user.getName(),
"newUser1", "newUser1",
@ -581,14 +582,11 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
user.getActive(), user.getActive(),
user.getLocale(), user.getLocale(),
user.getTimeZone(), user.getTimeZone(),
Stream.of(UserRole.EXAM_ADMIN.name(), UserRole.EXAM_SUPPORTER.name()).collect(Collectors.toSet())), Stream.of(UserRole.EXAM_ADMIN.name(), UserRole.EXAM_SUPPORTER.name()).collect(Collectors.toSet()));
null, null);
final String modifyUserJson = this.jsonMapper.writeValueAsString(modifyUser); final String modifyUserJson = this.jsonMapper.writeValueAsString(modifyUser);
UserInfo modifiedUserResult = this.jsonMapper.readValue( UserInfo modifiedUserResult = this.jsonMapper.readValue(
this.mockMvc this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT)
.perform(
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + user.getUuid())
.header("Authorization", "Bearer " + token) .header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8) .contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifyUserJson)) .content(modifyUserJson))
@ -668,10 +666,9 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
new TypeReference<UserInfo>() { new TypeReference<UserInfo>() {
}); });
final UserMod modifiedUser = new UserMod(examAdmin, null, null); final String modifiedUserJson = this.jsonMapper.writeValueAsString(examAdmin);
final String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser);
this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + modifiedUser.uuid) this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT)
.header("Authorization", "Bearer " + examAdminToken1) .header("Authorization", "Bearer " + examAdminToken1)
.contentType(MediaType.APPLICATION_JSON_UTF8) .contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson)) .content(modifiedUserJson))
@ -694,12 +691,11 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
final UserInfo userInfo = new UserInfo( final UserInfo userInfo = new UserInfo(
null, 2L, "NewTestUser", "NewTestUser", "NewTestUser", 2L, "NewTestUser", "NewTestUser",
"", true, Locale.CANADA, DateTimeZone.UTC, "", true, Locale.CANADA, DateTimeZone.UTC,
new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name()))); new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name())));
final UserMod newUser = new UserMod(userInfo, "12345678", "12345678"); final String newUserJson = this.jsonMapper.writeValueAsString(userInfo);
final String newUserJson = this.jsonMapper.writeValueAsString(newUser); this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT)
this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/NewTestUser")
.header("Authorization", "Bearer " + token) .header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8) .contentType(MediaType.APPLICATION_JSON_UTF8)
.content(newUserJson)) .content(newUserJson))
@ -722,12 +718,12 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();
final UserInfo userInfo = new UserInfo( final UserInfo userInfo = new UserInfo(
null, 2L, "NewTestUser", "NewTestUser", "NewTestUser", 2L, "NewTestUser", "NewTestUser",
"", true, Locale.CANADA, DateTimeZone.UTC, "", true, Locale.CANADA, DateTimeZone.UTC,
new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name()))); new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name())));
final UserMod newUser = new UserMod(userInfo, "12345678", "12345678"); //final UserMod newUser = new UserMod(userInfo, "12345678", "12345678");
final String newUserJson = this.jsonMapper.writeValueAsString(newUser); final String newUserJson = this.jsonMapper.writeValueAsString(userInfo);
this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/NewTestUser") this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT)
.header("Authorization", "Bearer " + token) .header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8) .contentType(MediaType.APPLICATION_JSON_UTF8)
.content(newUserJson)) .content(newUserJson))
@ -751,13 +747,13 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
new TypeReference<UserInfo>() { new TypeReference<UserInfo>() {
}); });
final UserMod modifiedUser = new UserMod( final PasswordChange passwordChange = new PasswordChange(
UserInfo.of(examAdmin1),
"newPassword", "newPassword",
"newPassword"); "newPassword");
final String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser); final String modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange);
this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + modifiedUser.uuid) this.mockMvc.perform(
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + examAdmin1.uuid + API.PASSWORD_PATH_SEGMENT)
.header("Authorization", "Bearer " + sebAdminToken) .header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8) .contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson)) .content(modifiedUserJson))
@ -799,15 +795,15 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
}); });
// must be longer then 8 chars // must be longer then 8 chars
UserMod modifiedUser = new UserMod( PasswordChange passwordChange = new PasswordChange(
UserInfo.of(examAdmin1),
"new", "new",
"new"); "new");
String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser); String modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange);
List<APIMessage> messages = this.jsonMapper.readValue( List<APIMessage> messages = this.jsonMapper.readValue(
this.mockMvc.perform( this.mockMvc.perform(
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + modifiedUser.uuid) put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + examAdmin1.uuid
+ API.PASSWORD_PATH_SEGMENT)
.header("Authorization", "Bearer " + sebAdminToken) .header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8) .contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson)) .content(modifiedUserJson))
@ -822,15 +818,15 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
assertEquals("[user, password, size, 8, 255, new]", String.valueOf(messages.get(0).getAttributes())); assertEquals("[user, password, size, 8, 255, new]", String.valueOf(messages.get(0).getAttributes()));
// wrong password retype // wrong password retype
modifiedUser = new UserMod( passwordChange = new PasswordChange(
UserInfo.of(examAdmin1),
"12345678", "12345678",
"87654321"); "87654321");
modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser); modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange);
messages = this.jsonMapper.readValue( messages = this.jsonMapper.readValue(
this.mockMvc.perform( this.mockMvc.perform(
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + modifiedUser.uuid) put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + examAdmin1.uuid
+ API.PASSWORD_PATH_SEGMENT)
.header("Authorization", "Bearer " + sebAdminToken) .header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8) .contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson)) .content(modifiedUserJson))