SEBSERV-8 #more tests and fixes

This commit is contained in:
anhefti 2018-12-18 15:08:38 +01:00
parent b7a6bd831b
commit ad9865bfa3
12 changed files with 469 additions and 39 deletions

View file

@ -0,0 +1,13 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.model.institution;
public class Institution {
}

View file

@ -65,7 +65,7 @@ public class UserActivityLog implements Entity {
return (this.id != null) ? String.valueOf(this.id) : null;
}
public String getUserUUID() {
public String getUserUuid() {
return this.userUUID;
}

View file

@ -136,7 +136,7 @@ public final class UserInfo implements GrantEntity, Serializable {
@JsonIgnore
@Override
public String getOwnerUUID() {
return null;
return this.uuid;
}
public String getName() {

View file

@ -8,26 +8,66 @@
package ch.ethz.seb.sebserver.gbl.model.user;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
public final class UserMod {
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;
public final class UserMod implements GrantEntity {
public static final String ATTR_NAME_USER_INFO = "userInfo";
public static final String ATTR_NAME_NEW_PASSWORD = "newPassword";
public static final String ATTR_NAME_RETYPED_NEW_PASSWORD = "retypedNewPassword";
@JsonProperty(ATTR_NAME_USER_INFO)
private final UserInfo userInfo;
@Size(min = 8, max = 255, message = "userInfo: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 UserMod(
@JsonProperty("userInfo") final UserInfo userInfo,
@JsonProperty("newPassword") final String newPassword,
@JsonProperty("retypedNewPassword") final String retypedNewPassword) {
@JsonProperty(ATTR_NAME_USER_INFO) final UserInfo userInfo,
@JsonProperty(ATTR_NAME_NEW_PASSWORD) final String newPassword,
@JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD) final String retypedNewPassword) {
this.userInfo = userInfo;
this.newPassword = newPassword;
this.retypedNewPassword = retypedNewPassword;
}
@Override
@JsonIgnore
public String getId() {
return this.userInfo.getId();
}
@Override
@JsonIgnore
public EntityType entityType() {
return this.userInfo.entityType();
}
@Override
@JsonIgnore
public Long getInstitutionId() {
return this.userInfo.getInstitutionId();
}
@Override
@JsonIgnore
public String getOwnerUUID() {
return this.userInfo.getOwnerUUID();
}
public UserInfo getUserInfo() {
return this.userInfo;
}
@ -81,5 +121,4 @@ public final class UserMod {
public String toString() {
return "UserMod [userInfo=" + this.userInfo + "]";
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.util;
public class Tuple<T> {
public final T _1;
public final T _2;
public Tuple(final T _1, final T _2) {
super();
this._1 = _1;
this._2 = _2;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this._1 == null) ? 0 : this._1.hashCode());
result = prime * result + ((this._2 == null) ? 0 : this._2.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("rawtypes")
final Tuple other = (Tuple) obj;
if (this._1 == null) {
if (other._1 != null)
return false;
} else if (!this._1.equals(other._1))
return false;
if (this._2 == null) {
if (other._2 != null)
return false;
} else if (!this._2.equals(other._2))
return false;
return true;
}
@Override
public String toString() {
return "( " + this._1 + ", " + this._2 + ")";
}
}

View file

@ -85,7 +85,9 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService
.andForRole(UserRole.INSTITUTIONAL_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.WRITE)
.andForRole(UserRole.EXAM_ADMIN)
.withOwnerPrivilege(PrivilegeType.MODIFY)
.andForRole(UserRole.EXAM_SUPPORTER)
.withOwnerPrivilege(PrivilegeType.MODIFY)
.create();
// grants for user activity logs

View file

@ -28,6 +28,19 @@ public interface UserActivityLogDAO extends UserRelatedEntityDAO<UserActivityLog
DELETE
}
/** Creates a user activity log entry for the current user.
*
* @param actionType the action type
* @param entity the Entity
* @param message an optional message */
<E extends Entity> Result<E> logUserActivity(ActivityType actionType, E entity, String message);
/** Creates a user activity log entry for the current user.
*
* @param actionType the action type
* @param entity the Entity */
<E extends Entity> Result<E> logUserActivity(ActivityType actionType, E entity);
/** Creates a user activity log entry.
*
* @param user for specified SEBServerUser instance

View file

@ -62,6 +62,20 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
return EntityType.USER_ACTIVITY_LOG;
}
@Override
public <E extends Entity> Result<E> logUserActivity(
final ActivityType actionType,
final E entity,
final String message) {
return logUserActivity(this.userService.getCurrentUser(), actionType, entity, message);
}
@Override
public <E extends Entity> Result<E> logUserActivity(final ActivityType actionType, final E entity) {
return logUserActivity(this.userService.getCurrentUser(), actionType, entity, null);
}
@Override
@Transactional
public <E extends Entity> Result<E> logUserActivity(
@ -72,7 +86,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
try {
this.userLogRecordMapper.insert(new UserActivityLogRecord(
this.userLogRecordMapper.insertSelective(new UserActivityLogRecord(
null,
user.getUserInfo().uuid,
System.currentTimeMillis(),

View file

@ -25,12 +25,15 @@ import org.apache.commons.lang3.BooleanUtils;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.util.CollectionUtils;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.user.UserFilter;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
@ -57,13 +60,16 @@ public class UserDaoImpl implements UserDAO {
private final UserRecordMapper userRecordMapper;
private final RoleRecordMapper roleRecordMapper;
private final PasswordEncoder userPasswordEncoder;
public UserDaoImpl(
final UserRecordMapper userRecordMapper,
final RoleRecordMapper roleRecordMapper) {
final RoleRecordMapper roleRecordMapper,
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder userPasswordEncoder) {
this.userRecordMapper = userRecordMapper;
this.roleRecordMapper = roleRecordMapper;
this.userPasswordEncoder = userPasswordEncoder;
}
@Override
@ -215,7 +221,7 @@ public class UserDaoImpl implements UserDAO {
null,
userInfo.name,
userInfo.userName,
(changePWD) ? userMod.getNewPassword() : null,
(changePWD) ? this.userPasswordEncoder.encode(userMod.getNewPassword()) : null,
userInfo.email,
userInfo.locale.toLanguageTag(),
userInfo.timeZone.getID(),
@ -230,9 +236,6 @@ public class UserDaoImpl implements UserDAO {
private Result<UserInfo> createNewUser(final SEBServerUser principal, final UserMod userMod) {
final UserInfo userInfo = userMod.getUserInfo();
if (userInfo.institutionId == null) {
return Result.ofError(new IllegalArgumentException("The users institution cannot be null"));
}
if (!userMod.newPasswordMatch()) {
return Result.ofError(new APIMessageException(ErrorMessage.PASSWORD_MISSMATCH));
@ -244,7 +247,7 @@ public class UserDaoImpl implements UserDAO {
UUID.randomUUID().toString(),
userInfo.name,
userInfo.userName,
userMod.getNewPassword(),
this.userPasswordEncoder.encode(userMod.getNewPassword()),
userInfo.email,
userInfo.locale.toLanguageTag(),
userInfo.timeZone.getID(),
@ -345,5 +348,4 @@ public class UserDaoImpl implements UserDAO {
userInfo,
record.getPassword()));
}
}

View file

@ -13,6 +13,7 @@ import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@ -25,6 +26,7 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserFilter;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser;
@ -32,6 +34,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
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.weblayer.oauth.RevokeTokenEndpoint;
@WebServiceProfile
@RestController
@ -42,17 +45,20 @@ public class UserAccountController {
private final AuthorizationGrantService authorizationGrantService;
private final UserService userService;
private final UserActivityLogDAO userActivityLogDAO;
private final ApplicationEventPublisher applicationEventPublisher;
public UserAccountController(
final UserDAO userDao,
final AuthorizationGrantService authorizationGrantService,
final UserService userService,
final UserActivityLogDAO userActivityLogDAO) {
final UserActivityLogDAO userActivityLogDAO,
final ApplicationEventPublisher applicationEventPublisher) {
this.userDao = userDao;
this.authorizationGrantService = authorizationGrantService;
this.userService = userService;
this.userActivityLogDAO = userActivityLogDAO;
this.applicationEventPublisher = applicationEventPublisher;
}
@RequestMapping(method = RequestMethod.GET)
@ -123,7 +129,11 @@ public class UserAccountController {
@RequestBody final UserMod userData,
final Principal principal) {
return _saveUser(userData, principal, PrivilegeType.WRITE);
return _saveUser(
userData,
this.userService.extractFromPrincipal(principal),
PrivilegeType.WRITE)
.getOrThrow();
}
@RequestMapping(value = "/save", method = RequestMethod.POST)
@ -131,31 +141,33 @@ public class UserAccountController {
@RequestBody final UserMod userData,
final Principal principal) {
return _saveUser(userData, principal, PrivilegeType.MODIFY);
return _saveUser(
userData,
this.userService.extractFromPrincipal(principal),
PrivilegeType.MODIFY)
.getOrThrow();
}
private UserInfo _saveUser(
private Result<UserInfo> _saveUser(
final UserMod userData,
final Principal principal,
final PrivilegeType grantType) {
final SEBServerUser admin,
final PrivilegeType privilegeType) {
// fist check if current user has any privileges for this action
this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.USER,
grantType);
final SEBServerUser admin = this.userService.extractFromPrincipal(principal);
final ActivityType actionType = (userData.getUserInfo().uuid == null)
? ActivityType.CREATE
: ActivityType.MODIFY;
return this.userDao
.save(admin, userData)
.flatMap(userInfo -> this.userActivityLogDAO.logUserActivity(
admin,
actionType,
userInfo))
.getOrThrow();
return this.authorizationGrantService
.checkGrantOnEntity(userData, privilegeType)
.flatMap(data -> this.userDao.save(admin, data))
.flatMap(userInfo -> this.userActivityLogDAO.logUserActivity(actionType, userInfo))
.flatMap(userInfo -> {
// handle password change; revoke access tokens if password has changed
this.applicationEventPublisher.publishEvent(
new RevokeTokenEndpoint.RevokeTokenEvent(this, userInfo.userName));
return Result.of(userInfo);
});
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.http.HttpStatus;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@Controller
@WebServiceProfile
public class RevokeTokenEndpoint {
private final ConsumerTokenServices tokenServices;
private final AdminAPIClientDetails adminAPIClientDetails;
private final TokenStore tokenStore;
public RevokeTokenEndpoint(
final ConsumerTokenServices tokenServices,
final AdminAPIClientDetails adminAPIClientDetails,
final TokenStore tokenStore) {
this.tokenServices = tokenServices;
this.adminAPIClientDetails = adminAPIClientDetails;
this.tokenStore = tokenStore;
}
@RequestMapping(value = "/oauth/revoke-token", method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.OK)
public void logout(final HttpServletRequest request) {
final String authHeader = request.getHeader("Authorization");
if (authHeader != null) {
final String tokenId = authHeader.substring("Bearer".length() + 1);
this.tokenServices.revokeToken(tokenId);
}
}
@EventListener(RevokeTokenEvent.class)
private void revokeAccessToken(final RevokeTokenEvent event) {
final String clientId = this.adminAPIClientDetails.getClientId();
final Collection<OAuth2AccessToken> tokens = this.tokenStore
.findTokensByClientIdAndUserName(clientId, event.userName);
if (tokens != null) {
for (final OAuth2AccessToken token : tokens) {
this.tokenStore.removeAccessToken(token);
}
}
}
public static final class RevokeTokenEvent extends ApplicationEvent {
private static final long serialVersionUID = 5776699085388043743L;
public final String userName;
public RevokeTokenEvent(final Object source, final String userName) {
super(source);
this.userName = userName;
}
}
}

View file

@ -9,8 +9,7 @@
package ch.ethz.seb.sebserver.webservice.integration.api;
import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Arrays;
@ -19,6 +18,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.joda.time.DateTimeZone;
import org.junit.Test;
@ -105,7 +106,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
assertEquals(
"{\"messageCode\":\"1001\","
+ "\"systemMessage\":\"FORBIDDEN\","
+ "\"details\":\"No grant: READ_ONLY on type: USER entity institution: 1 entity owner: null for user: user3\","
+ "\"details\":\"No grant: READ_ONLY on type: USER entity institution: 1 entity owner: user1 for user: user3\","
+ "\"attributes\":[]}",
contentAsString);
}
@ -146,8 +147,6 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
assertNotNull(getUserInfo("admin", userInfos));
assertNotNull(getUserInfo("inst1Admin", userInfos));
assertNotNull(getUserInfo("examSupporter", userInfos));
// TODO more tests
}
@Test
@ -193,6 +192,15 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
assertNotNull(getUserInfo("examSupporter", userInfos));
}
@Test
public void testOwnerGet() throws Exception {
final String examAdminToken1 = getExamAdmin1();
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
.header("Authorization", "Bearer " + examAdminToken1))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
}
@Test
public void createUserTest() throws Exception {
final UserInfo userInfo = new UserInfo(
@ -247,6 +255,192 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
assertEquals(createdUserGet.uuid, userActivityLog.entityId);
}
@Test
public void modifyUserTest() throws Exception {
final String token = getSebAdminAccess();
final UserInfo user = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user7")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
new TypeReference<UserInfo>() {
});
assertNotNull(user);
assertEquals("User", user.name);
assertEquals("user1", user.userName);
assertEquals("user@nomail.nomail", user.email);
assertEquals("[EXAM_SUPPORTER]", String.valueOf(user.roles));
// change userName, email and roles
final UserMod modifiedUser = new UserMod(new UserInfo(
user.getUuid(),
user.getInstitutionId(),
user.getName(),
"newUser1",
"newUser@nomail.nomail",
user.getActive(),
user.getLocale(),
user.getTimeZone(),
Stream.of(UserRole.EXAM_ADMIN.name(), UserRole.EXAM_SUPPORTER.name()).collect(Collectors.toSet())),
null, null);
final String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser);
UserInfo modifiedUserResult = this.jsonMapper.readValue(
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/save")
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
new TypeReference<UserInfo>() {
});
assertNotNull(modifiedUserResult);
assertEquals("User", modifiedUserResult.name);
assertEquals("newUser1", modifiedUserResult.userName);
assertEquals("newUser@nomail.nomail", modifiedUserResult.email);
assertEquals("[EXAM_ADMIN, EXAM_SUPPORTER]", String.valueOf(modifiedUserResult.roles));
// double check by getting the user by uuis
modifiedUserResult = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUserResult.uuid)
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
new TypeReference<UserInfo>() {
});
assertNotNull(modifiedUserResult);
assertEquals("User", modifiedUserResult.name);
assertEquals("newUser1", modifiedUserResult.userName);
assertEquals("newUser@nomail.nomail", modifiedUserResult.email);
assertEquals("[EXAM_ADMIN, EXAM_SUPPORTER]", String.valueOf(modifiedUserResult.roles));
}
@Test
public void testOwnerModifyPossible() throws Exception {
final String examAdminToken1 = getExamAdmin1();
final UserInfo examAdmin = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
.header("Authorization", "Bearer " + examAdminToken1))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
new TypeReference<UserInfo>() {
});
final UserMod modifiedUser = new UserMod(examAdmin, null, null);
final String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser);
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/save")
.header("Authorization", "Bearer " + examAdminToken1)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
}
@Test
public void institutionalAdminTryToCreateOrModifyUserForOtherInstituionNotPossible() throws Exception {
final UserInfo userInfo = new UserInfo(
null, 2L, "NewTestUser", "NewTestUser",
"", true, Locale.CANADA, DateTimeZone.UTC,
new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name())));
final UserMod newUser = new UserMod(userInfo, "123", "123");
final String newUserJson = this.jsonMapper.writeValueAsString(newUser);
final String token = getAdminInstitution1Access();
this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/create")
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(newUserJson))
.andExpect(status().isForbidden())
.andReturn().getResponse().getContentAsString();
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/save")
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(newUserJson))
.andExpect(status().isForbidden())
.andReturn().getResponse().getContentAsString();
}
@Test
public void unauthorizedAdminTryToCreateUserNotPossible() throws Exception {
final UserInfo userInfo = new UserInfo(
null, 2L, "NewTestUser", "NewTestUser",
"", true, Locale.CANADA, DateTimeZone.UTC,
new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name())));
final UserMod newUser = new UserMod(userInfo, "123", "123");
final String newUserJson = this.jsonMapper.writeValueAsString(newUser);
final String token = getExamAdmin1();
this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/create")
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(newUserJson))
.andExpect(status().isForbidden())
.andReturn().getResponse().getContentAsString();
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/save")
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(newUserJson))
.andExpect(status().isForbidden())
.andReturn().getResponse().getContentAsString();
}
@Test
public void modifyUserPassword() throws Exception {
final String examAdminToken1 = getExamAdmin1();
assertNotNull(examAdminToken1);
// a SEB Server Admin now changes the password of ExamAdmin1
final String sebAdminToken = getSebAdminAccess();
final UserInfo examAdmin1 = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4")
.header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
new TypeReference<UserInfo>() {
});
final UserMod modifiedUser = new UserMod(
UserInfo.of(examAdmin1),
"newPassword",
"newPassword");
final String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser);
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/save")
.header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
// now it should not be possible to get a access token for ExamAdmin1 with the standard password
try {
getExamAdmin1();
fail("AssertionError expected here");
} catch (final AssertionError e) {
assertEquals("Status expected:<200> but was:<400>", e.getMessage());
}
// it should also not be possible to use an old token again after password change
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
.header("Authorization", "Bearer " + examAdminToken1))
.andExpect(status().isUnauthorized())
.andReturn().getResponse().getContentAsString();
// but it should be possible to get a new access token and request again
final String examAdminToken2 = obtainAccessToken("examAdmin1", "newPassword");
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me")
.header("Authorization", "Bearer " + examAdminToken2))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
}
private UserInfo getUserInfo(final String name, final Collection<UserInfo> infos) {
return infos
.stream()