diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java index 860405d9..2301bdb9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java @@ -512,7 +512,7 @@ public class UserDAOImpl implements UserDAO { return; } - // check same username already exists + // check same email already exists final Long otherUsersWithSameName = this.userRecordMapper .countByExample() .where(UserRecordDynamicSqlSupport.email, isEqualTo(userAccount.getEmail())) diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java index 6511cb93..fec9caa7 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java @@ -23,8 +23,6 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup; import org.apache.commons.codec.Charsets; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.BooleanUtils; @@ -37,6 +35,7 @@ import org.springframework.core.annotation.Order; import org.springframework.core.io.ClassPathResource; import org.springframework.test.context.jdbc.Sql; import org.springframework.util.StreamUtils; +import org.springframework.web.servlet.DispatcherServlet; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; @@ -56,6 +55,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.institution.Institution; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigCreationInfo; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; @@ -97,6 +97,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSe import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetupNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.NewLmsSetup; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.SaveLmsSetup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.ImportAsExam; @@ -142,7 +143,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUs import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.NewUserAccount; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.SaveUserAccount; -import org.springframework.web.servlet.DispatcherServlet; public class UseCasesIntegrationTest extends GuiIntegrationTest { @@ -427,6 +427,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertNotNull(activation); assertFalse(activation.hasError()); + // no unique email result = restService.getBuilder(NewUserAccount.class) .withFormParam(Domain.USER.ATTR_INSTITUTION_ID, instId) .withFormParam(Domain.USER.ATTR_NAME, "examSupport2") @@ -440,6 +441,23 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .withFormParam(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID()) .call(); + assertNotNull(result); + assertTrue(result.hasError()); + + // no unique email + result = restService.getBuilder(NewUserAccount.class) + .withFormParam(Domain.USER.ATTR_INSTITUTION_ID, instId) + .withFormParam(Domain.USER.ATTR_NAME, "examSupport2") + .withFormParam(Domain.USER.ATTR_USERNAME, "examSupport2") + .withFormParam(Domain.USER.ATTR_SURNAME, "examSupport2") + .withFormParam(Domain.USER.ATTR_EMAIL, "test@test1.ch") + .withFormParam(Domain.USER_ROLE.REFERENCE_NAME, UserRole.EXAM_SUPPORTER.name()) + .withFormParam(PasswordChange.ATTR_NAME_NEW_PASSWORD, "examSupport2") + .withFormParam(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "examSupport2") + .withFormParam(Domain.USER.ATTR_LANGUAGE, Locale.ENGLISH.getLanguage()) + .withFormParam(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID()) + .call(); + assertNotNull(result); assertFalse(result.hasError()); @@ -454,7 +472,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .withFormParam(Domain.USER.ATTR_NAME, "examSupport1") .withFormParam(Domain.USER.ATTR_USERNAME, "examSupport1") .withFormParam(Domain.USER.ATTR_SURNAME, "examSupport1") - .withFormParam(Domain.USER.ATTR_EMAIL, "test@test.ch") + .withFormParam(Domain.USER.ATTR_EMAIL, "test@test6.ch") .withFormParam(Domain.USER_ROLE.REFERENCE_NAME, UserRole.EXAM_SUPPORTER.name()) .withFormParam(PasswordChange.ATTR_NAME_NEW_PASSWORD, "examSupport1") .withFormParam(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "examSupport1") @@ -680,7 +698,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { new GetQuizPage()); // create new LMS Setup Mockup - Result newLMSCall = restService + final Result newLMSCall = restService .getBuilder(NewLmsSetup.class) .withFormParam(Domain.LMS_SETUP.ATTR_NAME, "Test Open edx") .withFormParam(Domain.LMS_SETUP.ATTR_LMS_TYPE, LmsType.OPEN_EDX.name()) @@ -691,18 +709,18 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertNotNull(newLMSCall); assertFalse(newLMSCall.hasError()); - LmsSetup lmsSetup = newLMSCall.get(); + final LmsSetup lmsSetup = newLMSCall.get(); assertEquals("Test Open edx", lmsSetup.name); assertFalse(lmsSetup.isActive()); // activate lms setup - LmsSetupTestResult testResult = restService + final LmsSetupTestResult testResult = restService .getBuilder(TestLmsSetup.class) .withURIVariable(API.PARAM_MODEL_ID, lmsSetup.getModelId()) .call() .getOrThrow(); - DispatcherServlet dispatcherServlet = mockMvc.getDispatcherServlet(); + final DispatcherServlet dispatcherServlet = this.mockMvc.getDispatcherServlet(); assertNotNull(testResult); assertFalse(testResult.isOk()); @@ -1772,7 +1790,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertEquals("123", newTemplValue.value); // reset template values - TemplateAttribute attribute = restService + final TemplateAttribute attribute = restService .getBuilder(ResetTemplateValues.class) .withURIVariable(API.PARAM_PARENT_MODEL_ID, String.valueOf(template.id)) .withURIVariable(API.PARAM_MODEL_ID, templateAttr.getModelId()) diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/UserAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/UserAPITest.java index 2cf8a408..9613ca43 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/UserAPITest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/UserAPITest.java @@ -1,1248 +1,1248 @@ -/* - * 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.integration.api.admin; - -import static org.junit.Assert.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import java.util.Arrays; -import java.util.Collection; -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.DateTime; -import org.joda.time.DateTimeZone; -import org.junit.Test; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.test.context.jdbc.Sql; - -import com.fasterxml.jackson.core.type.TypeReference; - -import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.APIMessage; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.Entity; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.EntityName; -import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; -import ch.ethz.seb.sebserver.gbl.model.Page; -import ch.ethz.seb.sebserver.gbl.model.institution.Institution; -import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; -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.UserLogActivityType; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; - -@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" }) -public class UserAPITest extends AdministrationAPIIntegrationTester { - - @Test - public void getMyUserInfo() throws Exception { - String contentAsString = new RestAPITestHelper() - .withAccessToken(getSebAdminAccess()) - .withPath(API.USER_ACCOUNT_ENDPOINT + "/me") - .withExpectedStatus(HttpStatus.OK) - .getAsString(); - - assertEquals( - "{\"uuid\":\"user1\"," - + "\"institutionId\":1," - + "\"creationDate\":\"2019-01-01T00:00:00.000Z\"," - + "\"name\":\"SEBAdmin\"," - + "\"surname\":\"\"," - + "\"username\":\"admin\"," - + "\"email\":\"admin@nomail.nomail\"," - + "\"active\":true," - + "\"language\":\"en\"," - + "\"timezone\":\"UTC\"," - + "\"userRoles\":[\"EXAM_ADMIN\",\"EXAM_SUPPORTER\",\"SEB_SERVER_ADMIN\",\"INSTITUTIONAL_ADMIN\"]}", - contentAsString); - - contentAsString = new RestAPITestHelper() - .withAccessToken(getAdminInstitution1Access()) - .withPath(API.USER_ACCOUNT_ENDPOINT + "/me") - .withExpectedStatus(HttpStatus.OK) - .getAsString(); - - assertEquals( - "{\"uuid\":\"user2\"," - + "\"institutionId\":1," - + "\"creationDate\":\"2019-01-01T00:00:00.000Z\"," - + "\"name\":\"Institutional1 Admin\"," - + "\"surname\":\"\"," - + "\"username\":\"inst1Admin\"," - + "\"email\":\"admin@nomail.nomail\"," - + "\"active\":true," - + "\"language\":\"en\"," - + "\"timezone\":\"UTC\"," - + "\"userRoles\":[\"INSTITUTIONAL_ADMIN\"]}", - contentAsString); - } - - @Test - public void getUserInfoWithUUID() throws Exception { - final String sebAdminAccessToken = getSebAdminAccess(); - String contentAsString = this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminAccessToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); - - assertEquals( - "{\"uuid\":\"user2\"," - + "\"institutionId\":1," - + "\"creationDate\":\"2019-01-01T00:00:00.000Z\"," - + "\"name\":\"Institutional1 Admin\"," - + "\"surname\":\"\"," - + "\"username\":\"inst1Admin\"," - + "\"email\":\"admin@nomail.nomail\"," - + "\"active\":true," - + "\"language\":\"en\"," - + "\"timezone\":\"UTC\"," - + "\"userRoles\":[\"INSTITUTIONAL_ADMIN\"]}", - contentAsString); - - final String adminInstitution2AccessToken = getAdminInstitution2Access(); - contentAsString = this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user1") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + adminInstitution2AccessToken)) - .andExpect(status().isForbidden()) - .andReturn().getResponse().getContentAsString(); - - assertEquals( - "[{\"messageCode\":\"1001\"," - + "\"systemMessage\":\"FORBIDDEN\"," - + "\"details\":\"No grant: READ on type: USER entity institution: 1 entity owner: user1 for user: user3\"," - + "\"attributes\":[]}]", - contentAsString); - } - - @Test - public void institutionalAdminNotAllowedToSeeUsersOfOtherInstitution() throws Exception { - new RestAPITestHelper() - .withAccessToken(getAdminInstitution1Access()) - .withPath(API.USER_ACCOUNT_ENDPOINT + "?institutionId=2") - .withExpectedStatus(HttpStatus.FORBIDDEN) - .getAsString(); - } - - @Test - public void getAllUserInfoNoFilter() throws Exception { - Page userInfos = new RestAPITestHelper() - .withAccessToken(getSebAdminAccess()) - .withPath(API.USER_ACCOUNT_ENDPOINT + "?institutionId=1") - .withExpectedStatus(HttpStatus.OK) - .getAsObject(new TypeReference>() { - }); - - // expecting all users for a SEBAdmin except inactive. - assertNotNull(userInfos); - assertTrue(userInfos.content.size() == 3); - assertNotNull(getUserInfo("admin", userInfos.content)); - assertNotNull(getUserInfo("inst1Admin", userInfos.content)); - assertNotNull(getUserInfo("examSupporter", userInfos.content)); - - userInfos = new RestAPITestHelper() - .withAccessToken(getAdminInstitution2Access()) - .withPath(API.USER_ACCOUNT_ENDPOINT) - .withAttribute("institutionId", "2") - .withExpectedStatus(HttpStatus.OK) - .getAsObject(new TypeReference>() { - }); - - // expecting all users of institution 2 also inactive when active flag is not set - assertNotNull(userInfos); - assertTrue(userInfos.content.size() == 4); - assertNotNull(getUserInfo("inst2Admin", userInfos.content)); - assertNotNull(getUserInfo("examAdmin1", userInfos.content)); - assertNotNull(getUserInfo("deactivatedUser", userInfos.content)); - assertNotNull(getUserInfo("user1", userInfos.content)); - - //.. and without inactive, if active flag is set to true - userInfos = new RestAPITestHelper() - .withAccessToken(getAdminInstitution2Access()) - .withPath(API.USER_ACCOUNT_ENDPOINT) - .withAttribute(Entity.FILTER_ATTR_INSTITUTION, "2") - .withAttribute(Entity.FILTER_ATTR_ACTIVE, "true") - .withExpectedStatus(HttpStatus.OK) - .getAsObject(new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.content.size() == 3); - assertNotNull(getUserInfo("inst2Admin", userInfos.content)); - assertNotNull(getUserInfo("examAdmin1", userInfos.content)); - assertNotNull(getUserInfo("user1", userInfos.content)); - - //.. and only inactive, if active flag is set to false - userInfos = new RestAPITestHelper() - .withAccessToken(getAdminInstitution2Access()) - .withPath(API.USER_ACCOUNT_ENDPOINT) - .withAttribute(Entity.FILTER_ATTR_INSTITUTION, "2") - .withAttribute(Entity.FILTER_ATTR_ACTIVE, "false") - .withExpectedStatus(HttpStatus.OK) - .getAsObject(new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.content.size() == 1); - assertNotNull(getUserInfo("deactivatedUser", userInfos.content)); - - } - - @Test - public void getPageNoFilterNoPageAttributes() throws Exception { - - // expecting all user accounts of the institution of SEBAdmin - - final String token = getSebAdminAccess(); - final Page userInfos = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?institutionId=1") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 1); - assertNotNull(userInfos.content); - assertTrue(userInfos.content.size() == 3); - assertEquals("[user1, user2, user5]", getOrderedUUIDs(userInfos.content)); - } - - @Test - public void getPageNoFilterNoPageAttributesFromOtherInstitution() throws Exception { - - // expecting all user accounts of institution 2 - - final String token = getSebAdminAccess(); - final Page userInfos = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 1); - assertNotNull(userInfos.content); - assertTrue(userInfos.content.size() == 4); - assertEquals("[user3, user4, user6, user7]", getOrderedUUIDs(userInfos.content)); - } - - @Test - public void getPageNoFilterNoPageAttributesDescendingOrder() throws Exception { - final String token = getSebAdminAccess(); - final Page userInfos = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?sort=-&institutionId=1") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 1); - assertNotNull(userInfos.content); - assertTrue(userInfos.content.size() == 3); - assertEquals("[user5, user2, user1]", getOrderedUUIDs(userInfos.content)); - } - - @Test - public void getPageOfSize3NoFilter() throws Exception { - final String token = getSebAdminAccess(); - - // first page default sort order - Page userInfos = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "?page_number=1&page_size=3&institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 2); - assertNotNull(userInfos.content); - assertTrue(userInfos.content.size() == 3); - assertEquals("[user3, user4, user6]", getOrderedUUIDs(userInfos.content)); - - // second page default sort order - userInfos = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "?page_number=2&page_size=3&institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 2); - assertNotNull(userInfos.content); - assertTrue(userInfos.pageSize == 1); - assertTrue(userInfos.content.size() == 1); - assertEquals("[user7]", getOrderedUUIDs(userInfos.content)); - - // invalid page number should refer to last page - userInfos = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "?page_number=3&page_size=3&institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 2); - assertTrue(userInfos.pageNumber == 2); - assertNotNull(userInfos.content); - assertTrue(userInfos.content.size() == 1); - assertEquals("[user7]", getOrderedUUIDs(userInfos.content)); - - // first page descending sort order - userInfos = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "?page_number=1&page_size=3&sort=-&institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 2); - assertNotNull(userInfos.content); - assertTrue(userInfos.content.size() == 3); - assertEquals("[user7, user6, user4]", getOrderedUUIDs(userInfos.content)); - } - - @Test - public void getAllUserInfo() throws Exception { - final String token = getSebAdminAccess(); - final Page userInfos = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?institutionId=1") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.content.size() == 3); - } - - @Test - public void getAllUserInfoWithOnlyActive() throws Exception { - final String token = getSebAdminAccess(); - final Page userInfos = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "?active=true&institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.content.size() == 3); - assertNull(getUserInfo("deactivatedUser", userInfos.content)); - } - - @Test - public void getAllUserInfoOnlyInactive() throws Exception { - - // expecting none for SEBAdmins institution - final String token = getSebAdminAccess(); - Page userInfos = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?active=false&institutionId=1") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.content.size() == 0); - - // expecting one for institution 2 - userInfos = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "?active=false&institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.content.size() == 1); - assertNotNull(getUserInfo("deactivatedUser", userInfos.content)); - } - - @Test - public void getAllUserInfoWithSearchUsernameLike() throws Exception { - final String token = getSebAdminAccess(); - final Page userInfos = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?username=exam&institutionId=1") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userInfos); - assertTrue(userInfos.content.size() == 1); - assertNotNull(getUserInfo("examSupporter", userInfos.content)); - } - - @Test - public void testOwnerGet() throws Exception { - final String examAdminToken1 = getExamAdmin1(); - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/me") - .header("Authorization", "Bearer " + examAdminToken1)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); - } - - @Test - public void createUserTest() throws Exception { - final String token = getSebAdminAccess(); - final UserInfo createdUser = this.jsonMapper.readValue( - this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT) - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .param(Domain.USER.ATTR_NAME, "NewTestUser") - .param(Domain.USER.ATTR_USERNAME, "NewTestUser") - .param(Domain.USER.ATTR_SURNAME, "NewTestUser") - .param(Domain.USER.ATTR_LANGUAGE, Locale.ENGLISH.toLanguageTag()) - .param(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID()) - .param(Domain.USER_ROLE.REFERENCE_NAME, UserRole.EXAM_ADMIN.name()) - .param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678") - .param(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "12345678")) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - assertNotNull(createdUser); - assertEquals("NewTestUser", createdUser.name); - - // get newly created user and check equality - final UserInfo createdUserGet = this.jsonMapper.readValue( - this.mockMvc.perform( - get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + createdUser.uuid) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - assertNotNull(createdUserGet); - assertEquals(createdUser.getEntityKey(), createdUserGet.getEntityKey()); - assertFalse(createdUserGet.isActive()); - - // check user activity log for newly created user - final Page logs = this.jsonMapper.readValue( - this.mockMvc - .perform( - get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT - + "?username=admin&activity_types=CREATE") - .header("Authorization", "Bearer " + token) - .header(HttpHeaders.CONTENT_TYPE, - MediaType.APPLICATION_FORM_URLENCODED_VALUE)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(logs); - assertTrue(1 == logs.content.size()); - final UserActivityLog userActivityLog = logs.content.iterator().next(); - assertEquals("user1", userActivityLog.userUUID); - assertEquals("USER", userActivityLog.entityType.name()); - assertEquals("CREATE", userActivityLog.activityType.name()); - assertEquals(createdUserGet.uuid, userActivityLog.entityId); - } - -// NOTE: this tests transaction rollback is working but for now only if a runtime exception is thrown on -// UserDaoImpl.updateUser after the main record (UserRecord) is stored but the new roles are not -// updated so far. -// TODO: make this test running separately in an test with UserDaoImpl mockup - -// @Test -// public void modifyUserTestTransaction() 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() { -// }); -// -// 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 modifyUser = 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 modifyUserJson = this.jsonMapper.writeValueAsString(modifyUser); -// -// final String contentAsString = this.mockMvc -// .perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/save") -// .header("Authorization", "Bearer " + token) -// .contentType(MediaType.APPLICATION_JSON_UTF8) -// .content(modifyUserJson)) -// .andReturn().getResponse().getContentAsString(); -// -// // double check by getting the user by UUID -// final UserInfo unmodifiedUserResult = this.jsonMapper.readValue( -// this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + user.uuid) -// .header("Authorization", "Bearer " + token)) -// .andExpect(status().isOk()) -// .andReturn().getResponse().getContentAsString(), -// new TypeReference() { -// }); -// -// assertNotNull(unmodifiedUserResult); -// assertEquals("User", unmodifiedUserResult.name); -// assertEquals("user1", unmodifiedUserResult.userName); -// assertEquals("user@nomail.nomail", unmodifiedUserResult.email); -// assertEquals("[EXAM_SUPPORTER]", String.valueOf(unmodifiedUserResult.roles)); -// } - - @Test - public void modifyUserWithPUTMethod() throws Exception { - final String token = getSebAdminAccess(); - final UserInfo user = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user7") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - 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 UserInfo modifyUser = new UserInfo( - user.uuid, - user.getInstitutionId(), - new DateTime(0, DateTimeZone.UTC), - user.getName(), - user.getSurname(), - "newUser1", - "newUser@nomail.nomail", - user.getActive(), - user.getLanguage(), - user.getTimeZone(), - Stream.of(UserRole.EXAM_ADMIN.name(), UserRole.EXAM_SUPPORTER.name()).collect(Collectors.toSet())); - final String modifyUserJson = this.jsonMapper.writeValueAsString(modifyUser); - - UserInfo modifiedUserResult = this.jsonMapper.readValue( - this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT) - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(modifyUserJson)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - assertNotNull(modifiedUserResult); - assertEquals(user.uuid, modifiedUserResult.uuid); - 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 UUID - modifiedUserResult = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" - + modifiedUserResult.uuid) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - 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 testModifyUserOnInactiveInstitutionNotAllowed() throws Exception { - // create new institution with seb-admin that is not active - final Institution institution = new RestAPITestHelper() - .withAccessToken(getSebAdminAccess()) - .withPath(API.INSTITUTION_ENDPOINT) - .withMethod(HttpMethod.POST) - .withAttribute("name", "new institution") - .withAttribute("urlSuffix", "new_inst") - .withAttribute("active", "false") - .withExpectedStatus(HttpStatus.OK) - .getAsObject(new TypeReference() { - }); - - assertNotNull(institution); - assertNotNull(institution.id); - assertEquals("new institution", institution.name); - - // try to create a user for this institution should not be possible - final Collection errors = this.jsonMapper.readValue( - this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT) - .header("Authorization", "Bearer " + getSebAdminAccess()) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .param(Domain.USER.ATTR_INSTITUTION_ID, String.valueOf(institution.id)) - .param(Domain.USER.ATTR_NAME, "NewTestUser") - .param(Domain.USER.ATTR_USERNAME, "NewTestUser") - .param(Domain.USER.ATTR_SURNAME, "NewTestUser") - .param(Domain.USER.ATTR_LANGUAGE, Locale.ENGLISH.toLanguageTag()) - .param(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID()) - .param(Domain.USER_ROLE.REFERENCE_NAME, UserRole.EXAM_ADMIN.name()) - .param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678") - .param(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "12345678")) - .andExpect(status().isBadRequest()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(errors); - assertTrue(errors.size() == 1); - assertEquals( - "Illegal API request argument", - errors.iterator().next().systemMessage); - assertEquals( - "User within an inactive institution cannot be created nor modified", - errors.iterator().next().details); - } - -// @Test -// public void modifyUserWithPOSTMethod() throws Exception { -// final String token = getSebAdminAccess(); -// -// final UserInfo modifiedUser = this.jsonMapper.readValue( -// this.mockMvc.perform(patch(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4") -// .header("Authorization", "Bearer " + token) -// .contentType(MediaType.APPLICATION_FORM_URLENCODED) -// .param("name", "PostModifyTest")) -// .andExpect(status().isOk()) -// .andReturn().getResponse().getContentAsString(), -// new TypeReference() { -// }); -// -// assertNotNull(modifiedUser); -// assertEquals("PostModifyTest", modifiedUser.name); -// -// // check validation -// final Collection errors = this.jsonMapper.readValue( -// this.mockMvc.perform(patch(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4") -// .header("Authorization", "Bearer " + token) -// .contentType(MediaType.APPLICATION_FORM_URLENCODED) -// .param("name", "P")) -// .andExpect(status().isBadRequest()) -// .andReturn().getResponse().getContentAsString(), -// new TypeReference>() { -// }); -// -// assertNotNull(errors); -// assertFalse(errors.isEmpty()); -// final APIMessage error = errors.iterator().next(); -// assertEquals("1200", error.messageCode); -// } - - @Test - public void testOwnerModifyPossibleForExamAdmin() throws Exception { - final String examAdminToken1 = getExamAdmin1(); - final UserInfo examAdmin = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/me") - .header("Authorization", "Bearer " + examAdminToken1)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - final String modifiedUserJson = this.jsonMapper.writeValueAsString(examAdmin); - - this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT) - .header("Authorization", "Bearer " + examAdminToken1) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(modifiedUserJson)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); - } - - @Test - public void institutionalAdminTryToCreateOrModifyUserForOtherInstituionNotPossible() throws Exception { - - final String token = getAdminInstitution1Access(); - this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT) - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .param(Domain.USER.ATTR_INSTITUTION_ID, "2") - .param(Domain.USER.ATTR_NAME, "NewTestUser") - .param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678") - .param(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "12345678")) - .andExpect(status().isForbidden()) - .andReturn().getResponse().getContentAsString(); - - final UserInfo userInfo = new UserInfo( - "NewTestUser", 2L, new DateTime(0, DateTimeZone.UTC), "NewTestUser", "", "NewTestUser", - "", true, Locale.CANADA, DateTimeZone.UTC, - new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name()))); - final String newUserJson = this.jsonMapper.writeValueAsString(userInfo); - this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT) - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(newUserJson)) - .andExpect(status().isForbidden()) - .andReturn().getResponse().getContentAsString(); - } - - @Test - public void unauthorizedAdminTryToCreateUserNotPossible() throws Exception { - - final String token = getExamAdmin1(); - this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT) - .header("Authorization", "Bearer " + token) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .param(Domain.USER.ATTR_INSTITUTION_ID, "2") - .param(Domain.USER.ATTR_NAME, "NewTestUser") - .param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678") - .param(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "12345678")) - .andExpect(status().isForbidden()) - .andReturn().getResponse().getContentAsString(); - - final UserInfo userInfo = new UserInfo( - "NewTestUser", 2L, new DateTime(0, DateTimeZone.UTC), "NewTestUser", "", "NewTestUser", - "", true, Locale.CANADA, DateTimeZone.UTC, - new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name()))); - //final UserMod newUser = new UserMod(userInfo, "12345678", "12345678"); - final String newUserJson = this.jsonMapper.writeValueAsString(userInfo); - this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT) - .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 + API.USER_ACCOUNT_ENDPOINT + "/user4") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - final PasswordChange passwordChange = new PasswordChange( - examAdmin1.uuid, - "admin", - "newPassword", - "newPassword"); - final String modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange); - - this.mockMvc.perform( - put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT) - .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 + API.USER_ACCOUNT_ENDPOINT + "/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 + API.USER_ACCOUNT_ENDPOINT + "/me") - .header("Authorization", "Bearer " + examAdminToken2)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); - } - - @Test - public void modifyUserPasswordInvalidPasswords() throws Exception { - final String sebAdminToken = getSebAdminAccess(); - final UserInfo examAdmin1 = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user4") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - // must be longer then 8 chars - PasswordChange passwordChange = new PasswordChange( - examAdmin1.uuid, - "admin", - "new", - "new"); - String modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange); - - List messages = this.jsonMapper.readValue( - this.mockMvc.perform( - put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT) - .header("Authorization", "Bearer " + sebAdminToken) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(modifiedUserJson)) - .andExpect(status().isBadRequest()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(messages); - assertTrue(1 == messages.size()); - assertEquals("1200", messages.get(0).messageCode); - assertEquals("[user, newPassword, size, 8, 255, new]", String.valueOf(messages.get(0).getAttributes())); - - // wrong password retype - passwordChange = new PasswordChange( - examAdmin1.uuid, - "admin", - "12345678", - "87654321"); - modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange); - - messages = this.jsonMapper.readValue( - this.mockMvc.perform( - put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT) - .header("Authorization", "Bearer " + sebAdminToken) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(modifiedUserJson)) - .andExpect(status().isBadRequest()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(messages); - assertTrue(1 == messages.size()); - assertEquals("1200", messages.get(0).messageCode); - } - - @Test - public void deactivateUserAccount() throws Exception { - final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.STANDARD_DATE_TIME_FORMATTER); - // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account - final String examAdminToken = getExamAdmin1(); - this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user4/inactive") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + examAdminToken)) - .andExpect(status().isForbidden()); - - // With SEB Administrator it should work - final String sebAdminToken = getSebAdminAccess(); - final EntityProcessingReport report = this.jsonMapper.readValue( - this.mockMvc - .perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user4/inactive") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - assertNotNull(report); - assertNotNull(report.source); - assertTrue(report.dependencies.isEmpty()); // TODO - assertTrue(report.errors.isEmpty()); - assertTrue(report.source.size() == 1); - - // get user and check activity - final EntityKey key = report.source.iterator().next(); - final UserInfo user = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + key.modelId) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - assertNotNull(user); - assertFalse(user.isActive()); - - // check also user activity log - final Page userLogs = this.jsonMapper.readValue( - this.mockMvc - .perform( - get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT - + "/?user=user1&from=" + timeNow) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userLogs); - assertTrue(userLogs.content.size() == 1); - final UserActivityLog userLog = userLogs.content.iterator().next(); - assertEquals(UserLogActivityType.DEACTIVATE, userLog.activityType); - assertEquals("user4", userLog.entityId); - } - - @Test - public void activateUserAccount() throws Exception { - final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.STANDARD_DATE_TIME_FORMATTER); - // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account - final String examAdminToken = getExamAdmin1(); - this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user6/active") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + examAdminToken)) - .andExpect(status().isForbidden()); - - // With SEB Administrator it should work - final String sebAdminToken = getSebAdminAccess(); - final EntityProcessingReport report = this.jsonMapper.readValue( - this.mockMvc - .perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user6/active") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - assertNotNull(report); - assertNotNull(report.source); - assertTrue(report.dependencies.isEmpty()); // TODO - assertTrue(report.errors.isEmpty()); - assertTrue(report.source.size() == 1); - - // get user and check activity - final EntityKey key = report.source.iterator().next(); - final UserInfo user = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + key.modelId) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference() { - }); - - assertNotNull(user); - assertTrue(user.isActive()); - - // check also user activity log - final Page userLogs = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT - + "?user=user1&from=" + timeNow) - .header("Authorization", "Bearer " + sebAdminToken) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(userLogs); - assertTrue(userLogs.content.size() == 1); - final UserActivityLog userLog = userLogs.content.iterator().next(); - assertEquals(UserLogActivityType.ACTIVATE, userLog.activityType); - assertEquals("user6", userLog.entityId); - } - - @Test - public void testGeneralAllActiveInactiveEndpoint() throws Exception { - final String sebAdminToken = getSebAdminAccess(); - - // all active for the own institution - Page usersPage = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/active") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(usersPage); - assertTrue(usersPage.pageSize == 3); - assertEquals("[user1, user2, user5]", getOrderedUUIDs(usersPage.content)); - - // all inactive of the own institution - usersPage = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/inactive") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(usersPage); - assertTrue(usersPage.pageSize == 0); - assertEquals("[]", getOrderedUUIDs(usersPage.content)); - - // all active of institution 2 - usersPage = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "/active?institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(usersPage); - assertTrue(usersPage.pageSize == 3); - assertEquals("[user3, user4, user7]", getOrderedUUIDs(usersPage.content)); - - // all inactive of institution 2 - usersPage = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "/inactive?institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(usersPage); - assertTrue(usersPage.pageSize == 1); - assertEquals("[user6]", getOrderedUUIDs(usersPage.content)); - } - - @Test - public void testGeneralListEndpoint() throws Exception { - final String sebAdminToken = getSebAdminAccess(); - - // for SEB Admin it should be possible to get from different institutions - Collection users = this.jsonMapper.readValue( - this.mockMvc - .perform( - get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "/list?modelIds=user1,user2,user6,user7") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(users); - assertTrue(users.size() == 4); - assertEquals("[user1, user2, user6, user7]", getOrderedUUIDs(users)); - - // for an institutional admin it should only be possible to get from own institution - final String instAdminToken = getAdminInstitution2Access(); - users = this.jsonMapper.readValue( - this.mockMvc - .perform( - get(this.endpoint + API.USER_ACCOUNT_ENDPOINT - + "/list?modelIds=user1,user2,user6,user7") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + instAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(users); - assertTrue(users.size() == 2); - assertEquals("[user6, user7]", getOrderedUUIDs(users)); - } - - @Test - public void testGeneralNamesEndpoint() throws Exception { - final String sebAdminToken = getSebAdminAccess(); - - // for SEB Admin - Collection names = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names" + "?institutionId=1") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + sebAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(names); - assertTrue(names.size() == 3); - assertEquals("[EntityName [entityType=USER, modelId=user1, name=admin ( SEBAdmin)], " + - "EntityName [entityType=USER, modelId=user2, name=inst1Admin ( Institutional1 Admin)], " + - "EntityName [entityType=USER, modelId=user5, name=examSupporter ( Exam Supporter)]]", names.toString()); - - // for an institutional admin 2 - final String instAdminToken = getAdminInstitution2Access(); - names = this.jsonMapper.readValue( - this.mockMvc - .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names" + "?institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + instAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(names); - assertTrue(names.size() == 4); - assertEquals("[EntityName [entityType=USER, modelId=user3, name=inst2Admin ( Institutional2 Admin)], " + - "EntityName [entityType=USER, modelId=user4, name=examAdmin1 ( ExamAdmin1)], " + - "EntityName [entityType=USER, modelId=user6, name=deactivatedUser ( Deactivated)], " + - "EntityName [entityType=USER, modelId=user7, name=user1 ( User)]]", names.toString()); - - // for an institutional admin 2 only active - names = this.jsonMapper.readValue( - this.mockMvc - .perform( - get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names?active=true&institutionId=2") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("Authorization", "Bearer " + instAdminToken)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(names); - assertTrue(names.size() == 3); - assertEquals("[EntityName [entityType=USER, modelId=user3, name=inst2Admin ( Institutional2 Admin)], " + - "EntityName [entityType=USER, modelId=user4, name=examAdmin1 ( ExamAdmin1)], " + - "EntityName [entityType=USER, modelId=user7, name=user1 ( User)]]", names.toString()); - } - -// @Test -// public void createWithRoleAddRoleDeleteRole() throws Exception { -// final String token = getSebAdminAccess(); -// UserInfo createdUser = this.jsonMapper.readValue( -// this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT) -// .header("Authorization", "Bearer " + token) -// .contentType(MediaType.APPLICATION_FORM_URLENCODED) -// .param(Domain.USER.ATTR_NAME, "NewTestUser") -// .param(Domain.USER.ATTR_USERNAME, "NewTestUser") -// .param(Domain.USER.ATTR_LOCALE, Locale.ENGLISH.toLanguageTag()) -// .param(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID()) -// .param(UserMod.ATTR_NAME_NEW_PASSWORD, "12345678") -// .param(UserMod.ATTR_NAME_RETYPED_NEW_PASSWORD, "12345678")) -// .andExpect(status().isOk()) -// .andReturn().getResponse().getContentAsString(), -// new TypeReference() { -// }); -// -// assertNotNull(createdUser); -// assertEquals("NewTestUser", createdUser.name); -// assertEquals("[]", String.valueOf(createdUser.roles)); -// -// // add two roles -// createdUser = this.jsonMapper.readValue( -// this.mockMvc.perform(patch(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + createdUser.uuid) -// .header("Authorization", "Bearer " + token) -// .contentType(MediaType.APPLICATION_FORM_URLENCODED) -// .param(USER_ROLE.REFERENCE_NAME, "EXAM_SUPPORTER", "EXAM_ADMIN")) -// .andExpect(status().isOk()) -// .andReturn().getResponse().getContentAsString(), -// new TypeReference() { -// }); -// -// assertNotNull(createdUser); -// assertEquals("NewTestUser", createdUser.name); -// assertEquals("[]", String.valueOf(createdUser.roles)); -// } - - private UserInfo getUserInfo(final String name, final Collection infos) { - try { - return infos - .stream() - .filter(ui -> ui.username.equals(name)) - .findFirst() - .orElseThrow(NoSuchElementException::new); - } catch (final Exception e) { - return null; - } - } - -} +/* + * 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.integration.api.admin; + +import static org.junit.Assert.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.Arrays; +import java.util.Collection; +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.DateTime; +import org.joda.time.DateTimeZone; +import org.junit.Test; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.context.jdbc.Sql; + +import com.fasterxml.jackson.core.type.TypeReference; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityName; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.model.institution.Institution; +import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; +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.UserLogActivityType; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; + +@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" }) +public class UserAPITest extends AdministrationAPIIntegrationTester { + + @Test + public void getMyUserInfo() throws Exception { + String contentAsString = new RestAPITestHelper() + .withAccessToken(getSebAdminAccess()) + .withPath(API.USER_ACCOUNT_ENDPOINT + "/me") + .withExpectedStatus(HttpStatus.OK) + .getAsString(); + + assertEquals( + "{\"uuid\":\"user1\"," + + "\"institutionId\":1," + + "\"creationDate\":\"2019-01-01T00:00:00.000Z\"," + + "\"name\":\"SEBAdmin\"," + + "\"surname\":\"\"," + + "\"username\":\"admin\"," + + "\"email\":\"admin1@nomail.nomail\"," + + "\"active\":true," + + "\"language\":\"en\"," + + "\"timezone\":\"UTC\"," + + "\"userRoles\":[\"EXAM_ADMIN\",\"EXAM_SUPPORTER\",\"SEB_SERVER_ADMIN\",\"INSTITUTIONAL_ADMIN\"]}", + contentAsString); + + contentAsString = new RestAPITestHelper() + .withAccessToken(getAdminInstitution1Access()) + .withPath(API.USER_ACCOUNT_ENDPOINT + "/me") + .withExpectedStatus(HttpStatus.OK) + .getAsString(); + + assertEquals( + "{\"uuid\":\"user2\"," + + "\"institutionId\":1," + + "\"creationDate\":\"2019-01-01T00:00:00.000Z\"," + + "\"name\":\"Institutional1 Admin\"," + + "\"surname\":\"\"," + + "\"username\":\"inst1Admin\"," + + "\"email\":\"admin2@nomail.nomail\"," + + "\"active\":true," + + "\"language\":\"en\"," + + "\"timezone\":\"UTC\"," + + "\"userRoles\":[\"INSTITUTIONAL_ADMIN\"]}", + contentAsString); + } + + @Test + public void getUserInfoWithUUID() throws Exception { + final String sebAdminAccessToken = getSebAdminAccess(); + String contentAsString = this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminAccessToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + assertEquals( + "{\"uuid\":\"user2\"," + + "\"institutionId\":1," + + "\"creationDate\":\"2019-01-01T00:00:00.000Z\"," + + "\"name\":\"Institutional1 Admin\"," + + "\"surname\":\"\"," + + "\"username\":\"inst1Admin\"," + + "\"email\":\"admin2@nomail.nomail\"," + + "\"active\":true," + + "\"language\":\"en\"," + + "\"timezone\":\"UTC\"," + + "\"userRoles\":[\"INSTITUTIONAL_ADMIN\"]}", + contentAsString); + + final String adminInstitution2AccessToken = getAdminInstitution2Access(); + contentAsString = this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user1") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + adminInstitution2AccessToken)) + .andExpect(status().isForbidden()) + .andReturn().getResponse().getContentAsString(); + + assertEquals( + "[{\"messageCode\":\"1001\"," + + "\"systemMessage\":\"FORBIDDEN\"," + + "\"details\":\"No grant: READ on type: USER entity institution: 1 entity owner: user1 for user: user3\"," + + "\"attributes\":[]}]", + contentAsString); + } + + @Test + public void institutionalAdminNotAllowedToSeeUsersOfOtherInstitution() throws Exception { + new RestAPITestHelper() + .withAccessToken(getAdminInstitution1Access()) + .withPath(API.USER_ACCOUNT_ENDPOINT + "?institutionId=2") + .withExpectedStatus(HttpStatus.FORBIDDEN) + .getAsString(); + } + + @Test + public void getAllUserInfoNoFilter() throws Exception { + Page userInfos = new RestAPITestHelper() + .withAccessToken(getSebAdminAccess()) + .withPath(API.USER_ACCOUNT_ENDPOINT + "?institutionId=1") + .withExpectedStatus(HttpStatus.OK) + .getAsObject(new TypeReference>() { + }); + + // expecting all users for a SEBAdmin except inactive. + assertNotNull(userInfos); + assertTrue(userInfos.content.size() == 3); + assertNotNull(getUserInfo("admin", userInfos.content)); + assertNotNull(getUserInfo("inst1Admin", userInfos.content)); + assertNotNull(getUserInfo("examSupporter", userInfos.content)); + + userInfos = new RestAPITestHelper() + .withAccessToken(getAdminInstitution2Access()) + .withPath(API.USER_ACCOUNT_ENDPOINT) + .withAttribute("institutionId", "2") + .withExpectedStatus(HttpStatus.OK) + .getAsObject(new TypeReference>() { + }); + + // expecting all users of institution 2 also inactive when active flag is not set + assertNotNull(userInfos); + assertTrue(userInfos.content.size() == 4); + assertNotNull(getUserInfo("inst2Admin", userInfos.content)); + assertNotNull(getUserInfo("examAdmin1", userInfos.content)); + assertNotNull(getUserInfo("deactivatedUser", userInfos.content)); + assertNotNull(getUserInfo("user1", userInfos.content)); + + //.. and without inactive, if active flag is set to true + userInfos = new RestAPITestHelper() + .withAccessToken(getAdminInstitution2Access()) + .withPath(API.USER_ACCOUNT_ENDPOINT) + .withAttribute(Entity.FILTER_ATTR_INSTITUTION, "2") + .withAttribute(Entity.FILTER_ATTR_ACTIVE, "true") + .withExpectedStatus(HttpStatus.OK) + .getAsObject(new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.content.size() == 3); + assertNotNull(getUserInfo("inst2Admin", userInfos.content)); + assertNotNull(getUserInfo("examAdmin1", userInfos.content)); + assertNotNull(getUserInfo("user1", userInfos.content)); + + //.. and only inactive, if active flag is set to false + userInfos = new RestAPITestHelper() + .withAccessToken(getAdminInstitution2Access()) + .withPath(API.USER_ACCOUNT_ENDPOINT) + .withAttribute(Entity.FILTER_ATTR_INSTITUTION, "2") + .withAttribute(Entity.FILTER_ATTR_ACTIVE, "false") + .withExpectedStatus(HttpStatus.OK) + .getAsObject(new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.content.size() == 1); + assertNotNull(getUserInfo("deactivatedUser", userInfos.content)); + + } + + @Test + public void getPageNoFilterNoPageAttributes() throws Exception { + + // expecting all user accounts of the institution of SEBAdmin + + final String token = getSebAdminAccess(); + final Page userInfos = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?institutionId=1") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.numberOfPages == 1); + assertNotNull(userInfos.content); + assertTrue(userInfos.content.size() == 3); + assertEquals("[user1, user2, user5]", getOrderedUUIDs(userInfos.content)); + } + + @Test + public void getPageNoFilterNoPageAttributesFromOtherInstitution() throws Exception { + + // expecting all user accounts of institution 2 + + final String token = getSebAdminAccess(); + final Page userInfos = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.numberOfPages == 1); + assertNotNull(userInfos.content); + assertTrue(userInfos.content.size() == 4); + assertEquals("[user3, user4, user6, user7]", getOrderedUUIDs(userInfos.content)); + } + + @Test + public void getPageNoFilterNoPageAttributesDescendingOrder() throws Exception { + final String token = getSebAdminAccess(); + final Page userInfos = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?sort=-&institutionId=1") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.numberOfPages == 1); + assertNotNull(userInfos.content); + assertTrue(userInfos.content.size() == 3); + assertEquals("[user5, user2, user1]", getOrderedUUIDs(userInfos.content)); + } + + @Test + public void getPageOfSize3NoFilter() throws Exception { + final String token = getSebAdminAccess(); + + // first page default sort order + Page userInfos = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "?page_number=1&page_size=3&institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.numberOfPages == 2); + assertNotNull(userInfos.content); + assertTrue(userInfos.content.size() == 3); + assertEquals("[user3, user4, user6]", getOrderedUUIDs(userInfos.content)); + + // second page default sort order + userInfos = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "?page_number=2&page_size=3&institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.numberOfPages == 2); + assertNotNull(userInfos.content); + assertTrue(userInfos.pageSize == 1); + assertTrue(userInfos.content.size() == 1); + assertEquals("[user7]", getOrderedUUIDs(userInfos.content)); + + // invalid page number should refer to last page + userInfos = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "?page_number=3&page_size=3&institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.numberOfPages == 2); + assertTrue(userInfos.pageNumber == 2); + assertNotNull(userInfos.content); + assertTrue(userInfos.content.size() == 1); + assertEquals("[user7]", getOrderedUUIDs(userInfos.content)); + + // first page descending sort order + userInfos = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "?page_number=1&page_size=3&sort=-&institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.numberOfPages == 2); + assertNotNull(userInfos.content); + assertTrue(userInfos.content.size() == 3); + assertEquals("[user7, user6, user4]", getOrderedUUIDs(userInfos.content)); + } + + @Test + public void getAllUserInfo() throws Exception { + final String token = getSebAdminAccess(); + final Page userInfos = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?institutionId=1") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.content.size() == 3); + } + + @Test + public void getAllUserInfoWithOnlyActive() throws Exception { + final String token = getSebAdminAccess(); + final Page userInfos = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "?active=true&institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.content.size() == 3); + assertNull(getUserInfo("deactivatedUser", userInfos.content)); + } + + @Test + public void getAllUserInfoOnlyInactive() throws Exception { + + // expecting none for SEBAdmins institution + final String token = getSebAdminAccess(); + Page userInfos = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?active=false&institutionId=1") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.content.size() == 0); + + // expecting one for institution 2 + userInfos = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "?active=false&institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.content.size() == 1); + assertNotNull(getUserInfo("deactivatedUser", userInfos.content)); + } + + @Test + public void getAllUserInfoWithSearchUsernameLike() throws Exception { + final String token = getSebAdminAccess(); + final Page userInfos = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?username=exam&institutionId=1") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.content.size() == 1); + assertNotNull(getUserInfo("examSupporter", userInfos.content)); + } + + @Test + public void testOwnerGet() throws Exception { + final String examAdminToken1 = getExamAdmin1(); + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/me") + .header("Authorization", "Bearer " + examAdminToken1)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + } + + @Test + public void createUserTest() throws Exception { + final String token = getSebAdminAccess(); + final UserInfo createdUser = this.jsonMapper.readValue( + this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param(Domain.USER.ATTR_NAME, "NewTestUser") + .param(Domain.USER.ATTR_USERNAME, "NewTestUser") + .param(Domain.USER.ATTR_SURNAME, "NewTestUser") + .param(Domain.USER.ATTR_LANGUAGE, Locale.ENGLISH.toLanguageTag()) + .param(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID()) + .param(Domain.USER_ROLE.REFERENCE_NAME, UserRole.EXAM_ADMIN.name()) + .param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678") + .param(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "12345678")) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(createdUser); + assertEquals("NewTestUser", createdUser.name); + + // get newly created user and check equality + final UserInfo createdUserGet = this.jsonMapper.readValue( + this.mockMvc.perform( + get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + createdUser.uuid) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(createdUserGet); + assertEquals(createdUser.getEntityKey(), createdUserGet.getEntityKey()); + assertFalse(createdUserGet.isActive()); + + // check user activity log for newly created user + final Page logs = this.jsonMapper.readValue( + this.mockMvc + .perform( + get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + + "?username=admin&activity_types=CREATE") + .header("Authorization", "Bearer " + token) + .header(HttpHeaders.CONTENT_TYPE, + MediaType.APPLICATION_FORM_URLENCODED_VALUE)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(logs); + assertTrue(1 == logs.content.size()); + final UserActivityLog userActivityLog = logs.content.iterator().next(); + assertEquals("user1", userActivityLog.userUUID); + assertEquals("USER", userActivityLog.entityType.name()); + assertEquals("CREATE", userActivityLog.activityType.name()); + assertEquals(createdUserGet.uuid, userActivityLog.entityId); + } + +// NOTE: this tests transaction rollback is working but for now only if a runtime exception is thrown on +// UserDaoImpl.updateUser after the main record (UserRecord) is stored but the new roles are not +// updated so far. +// TODO: make this test running separately in an test with UserDaoImpl mockup + +// @Test +// public void modifyUserTestTransaction() 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() { +// }); +// +// 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 modifyUser = 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 modifyUserJson = this.jsonMapper.writeValueAsString(modifyUser); +// +// final String contentAsString = this.mockMvc +// .perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/save") +// .header("Authorization", "Bearer " + token) +// .contentType(MediaType.APPLICATION_JSON_UTF8) +// .content(modifyUserJson)) +// .andReturn().getResponse().getContentAsString(); +// +// // double check by getting the user by UUID +// final UserInfo unmodifiedUserResult = this.jsonMapper.readValue( +// this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + user.uuid) +// .header("Authorization", "Bearer " + token)) +// .andExpect(status().isOk()) +// .andReturn().getResponse().getContentAsString(), +// new TypeReference() { +// }); +// +// assertNotNull(unmodifiedUserResult); +// assertEquals("User", unmodifiedUserResult.name); +// assertEquals("user1", unmodifiedUserResult.userName); +// assertEquals("user@nomail.nomail", unmodifiedUserResult.email); +// assertEquals("[EXAM_SUPPORTER]", String.valueOf(unmodifiedUserResult.roles)); +// } + + @Test + public void modifyUserWithPUTMethod() throws Exception { + final String token = getSebAdminAccess(); + final UserInfo user = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user7") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(user); + assertEquals("User", user.name); + assertEquals("user1", user.username); + assertEquals("user2@nomail.nomail", user.email); + assertEquals("[EXAM_SUPPORTER]", String.valueOf(user.roles)); + + // change userName, email and roles + final UserInfo modifyUser = new UserInfo( + user.uuid, + user.getInstitutionId(), + new DateTime(0, DateTimeZone.UTC), + user.getName(), + user.getSurname(), + "newUser1", + "newUser@nomail.nomail", + user.getActive(), + user.getLanguage(), + user.getTimeZone(), + Stream.of(UserRole.EXAM_ADMIN.name(), UserRole.EXAM_SUPPORTER.name()).collect(Collectors.toSet())); + final String modifyUserJson = this.jsonMapper.writeValueAsString(modifyUser); + + UserInfo modifiedUserResult = this.jsonMapper.readValue( + this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(modifyUserJson)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(modifiedUserResult); + assertEquals(user.uuid, modifiedUserResult.uuid); + 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 UUID + modifiedUserResult = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + + modifiedUserResult.uuid) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + 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 testModifyUserOnInactiveInstitutionNotAllowed() throws Exception { + // create new institution with seb-admin that is not active + final Institution institution = new RestAPITestHelper() + .withAccessToken(getSebAdminAccess()) + .withPath(API.INSTITUTION_ENDPOINT) + .withMethod(HttpMethod.POST) + .withAttribute("name", "new institution") + .withAttribute("urlSuffix", "new_inst") + .withAttribute("active", "false") + .withExpectedStatus(HttpStatus.OK) + .getAsObject(new TypeReference() { + }); + + assertNotNull(institution); + assertNotNull(institution.id); + assertEquals("new institution", institution.name); + + // try to create a user for this institution should not be possible + final Collection errors = this.jsonMapper.readValue( + this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT) + .header("Authorization", "Bearer " + getSebAdminAccess()) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param(Domain.USER.ATTR_INSTITUTION_ID, String.valueOf(institution.id)) + .param(Domain.USER.ATTR_NAME, "NewTestUser") + .param(Domain.USER.ATTR_USERNAME, "NewTestUser") + .param(Domain.USER.ATTR_SURNAME, "NewTestUser") + .param(Domain.USER.ATTR_LANGUAGE, Locale.ENGLISH.toLanguageTag()) + .param(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID()) + .param(Domain.USER_ROLE.REFERENCE_NAME, UserRole.EXAM_ADMIN.name()) + .param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678") + .param(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "12345678")) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(errors); + assertTrue(errors.size() == 1); + assertEquals( + "Illegal API request argument", + errors.iterator().next().systemMessage); + assertEquals( + "User within an inactive institution cannot be created nor modified", + errors.iterator().next().details); + } + +// @Test +// public void modifyUserWithPOSTMethod() throws Exception { +// final String token = getSebAdminAccess(); +// +// final UserInfo modifiedUser = this.jsonMapper.readValue( +// this.mockMvc.perform(patch(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4") +// .header("Authorization", "Bearer " + token) +// .contentType(MediaType.APPLICATION_FORM_URLENCODED) +// .param("name", "PostModifyTest")) +// .andExpect(status().isOk()) +// .andReturn().getResponse().getContentAsString(), +// new TypeReference() { +// }); +// +// assertNotNull(modifiedUser); +// assertEquals("PostModifyTest", modifiedUser.name); +// +// // check validation +// final Collection errors = this.jsonMapper.readValue( +// this.mockMvc.perform(patch(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4") +// .header("Authorization", "Bearer " + token) +// .contentType(MediaType.APPLICATION_FORM_URLENCODED) +// .param("name", "P")) +// .andExpect(status().isBadRequest()) +// .andReturn().getResponse().getContentAsString(), +// new TypeReference>() { +// }); +// +// assertNotNull(errors); +// assertFalse(errors.isEmpty()); +// final APIMessage error = errors.iterator().next(); +// assertEquals("1200", error.messageCode); +// } + + @Test + public void testOwnerModifyPossibleForExamAdmin() throws Exception { + final String examAdminToken1 = getExamAdmin1(); + final UserInfo examAdmin = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/me") + .header("Authorization", "Bearer " + examAdminToken1)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + final String modifiedUserJson = this.jsonMapper.writeValueAsString(examAdmin); + + this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT) + .header("Authorization", "Bearer " + examAdminToken1) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(modifiedUserJson)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + } + + @Test + public void institutionalAdminTryToCreateOrModifyUserForOtherInstituionNotPossible() throws Exception { + + final String token = getAdminInstitution1Access(); + this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param(Domain.USER.ATTR_INSTITUTION_ID, "2") + .param(Domain.USER.ATTR_NAME, "NewTestUser") + .param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678") + .param(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "12345678")) + .andExpect(status().isForbidden()) + .andReturn().getResponse().getContentAsString(); + + final UserInfo userInfo = new UserInfo( + "NewTestUser", 2L, new DateTime(0, DateTimeZone.UTC), "NewTestUser", "", "NewTestUser", + "", true, Locale.CANADA, DateTimeZone.UTC, + new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name()))); + final String newUserJson = this.jsonMapper.writeValueAsString(userInfo); + this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(newUserJson)) + .andExpect(status().isForbidden()) + .andReturn().getResponse().getContentAsString(); + } + + @Test + public void unauthorizedAdminTryToCreateUserNotPossible() throws Exception { + + final String token = getExamAdmin1(); + this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param(Domain.USER.ATTR_INSTITUTION_ID, "2") + .param(Domain.USER.ATTR_NAME, "NewTestUser") + .param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678") + .param(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, "12345678")) + .andExpect(status().isForbidden()) + .andReturn().getResponse().getContentAsString(); + + final UserInfo userInfo = new UserInfo( + "NewTestUser", 2L, new DateTime(0, DateTimeZone.UTC), "NewTestUser", "", "NewTestUser", + "", true, Locale.CANADA, DateTimeZone.UTC, + new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name()))); + //final UserMod newUser = new UserMod(userInfo, "12345678", "12345678"); + final String newUserJson = this.jsonMapper.writeValueAsString(userInfo); + this.mockMvc.perform(put(this.endpoint + API.USER_ACCOUNT_ENDPOINT) + .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 + API.USER_ACCOUNT_ENDPOINT + "/user4") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + final PasswordChange passwordChange = new PasswordChange( + examAdmin1.uuid, + "admin", + "newPassword", + "newPassword"); + final String modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange); + + this.mockMvc.perform( + put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT) + .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 + API.USER_ACCOUNT_ENDPOINT + "/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 + API.USER_ACCOUNT_ENDPOINT + "/me") + .header("Authorization", "Bearer " + examAdminToken2)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + } + + @Test + public void modifyUserPasswordInvalidPasswords() throws Exception { + final String sebAdminToken = getSebAdminAccess(); + final UserInfo examAdmin1 = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user4") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + // must be longer then 8 chars + PasswordChange passwordChange = new PasswordChange( + examAdmin1.uuid, + "admin", + "new", + "new"); + String modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange); + + List messages = this.jsonMapper.readValue( + this.mockMvc.perform( + put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT) + .header("Authorization", "Bearer " + sebAdminToken) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(modifiedUserJson)) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(messages); + assertTrue(1 == messages.size()); + assertEquals("1200", messages.get(0).messageCode); + assertEquals("[user, newPassword, size, 8, 255, new]", String.valueOf(messages.get(0).getAttributes())); + + // wrong password retype + passwordChange = new PasswordChange( + examAdmin1.uuid, + "admin", + "12345678", + "87654321"); + modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange); + + messages = this.jsonMapper.readValue( + this.mockMvc.perform( + put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT) + .header("Authorization", "Bearer " + sebAdminToken) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(modifiedUserJson)) + .andExpect(status().isBadRequest()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(messages); + assertTrue(1 == messages.size()); + assertEquals("1200", messages.get(0).messageCode); + } + + @Test + public void deactivateUserAccount() throws Exception { + final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.STANDARD_DATE_TIME_FORMATTER); + // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account + final String examAdminToken = getExamAdmin1(); + this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user4/inactive") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + examAdminToken)) + .andExpect(status().isForbidden()); + + // With SEB Administrator it should work + final String sebAdminToken = getSebAdminAccess(); + final EntityProcessingReport report = this.jsonMapper.readValue( + this.mockMvc + .perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user4/inactive") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(report); + assertNotNull(report.source); + assertTrue(report.dependencies.isEmpty()); // TODO + assertTrue(report.errors.isEmpty()); + assertTrue(report.source.size() == 1); + + // get user and check activity + final EntityKey key = report.source.iterator().next(); + final UserInfo user = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + key.modelId) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(user); + assertFalse(user.isActive()); + + // check also user activity log + final Page userLogs = this.jsonMapper.readValue( + this.mockMvc + .perform( + get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + + "/?user=user1&from=" + timeNow) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userLogs); + assertTrue(userLogs.content.size() == 1); + final UserActivityLog userLog = userLogs.content.iterator().next(); + assertEquals(UserLogActivityType.DEACTIVATE, userLog.activityType); + assertEquals("user4", userLog.entityId); + } + + @Test + public void activateUserAccount() throws Exception { + final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.STANDARD_DATE_TIME_FORMATTER); + // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account + final String examAdminToken = getExamAdmin1(); + this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user6/active") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + examAdminToken)) + .andExpect(status().isForbidden()); + + // With SEB Administrator it should work + final String sebAdminToken = getSebAdminAccess(); + final EntityProcessingReport report = this.jsonMapper.readValue( + this.mockMvc + .perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user6/active") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(report); + assertNotNull(report.source); + assertTrue(report.dependencies.isEmpty()); // TODO + assertTrue(report.errors.isEmpty()); + assertTrue(report.source.size() == 1); + + // get user and check activity + final EntityKey key = report.source.iterator().next(); + final UserInfo user = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + key.modelId) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(user); + assertTrue(user.isActive()); + + // check also user activity log + final Page userLogs = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + + "?user=user1&from=" + timeNow) + .header("Authorization", "Bearer " + sebAdminToken) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userLogs); + assertTrue(userLogs.content.size() == 1); + final UserActivityLog userLog = userLogs.content.iterator().next(); + assertEquals(UserLogActivityType.ACTIVATE, userLog.activityType); + assertEquals("user6", userLog.entityId); + } + + @Test + public void testGeneralAllActiveInactiveEndpoint() throws Exception { + final String sebAdminToken = getSebAdminAccess(); + + // all active for the own institution + Page usersPage = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/active") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(usersPage); + assertTrue(usersPage.pageSize == 3); + assertEquals("[user1, user2, user5]", getOrderedUUIDs(usersPage.content)); + + // all inactive of the own institution + usersPage = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/inactive") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(usersPage); + assertTrue(usersPage.pageSize == 0); + assertEquals("[]", getOrderedUUIDs(usersPage.content)); + + // all active of institution 2 + usersPage = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "/active?institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(usersPage); + assertTrue(usersPage.pageSize == 3); + assertEquals("[user3, user4, user7]", getOrderedUUIDs(usersPage.content)); + + // all inactive of institution 2 + usersPage = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "/inactive?institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(usersPage); + assertTrue(usersPage.pageSize == 1); + assertEquals("[user6]", getOrderedUUIDs(usersPage.content)); + } + + @Test + public void testGeneralListEndpoint() throws Exception { + final String sebAdminToken = getSebAdminAccess(); + + // for SEB Admin it should be possible to get from different institutions + Collection users = this.jsonMapper.readValue( + this.mockMvc + .perform( + get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "/list?modelIds=user1,user2,user6,user7") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(users); + assertTrue(users.size() == 4); + assertEquals("[user1, user2, user6, user7]", getOrderedUUIDs(users)); + + // for an institutional admin it should only be possible to get from own institution + final String instAdminToken = getAdminInstitution2Access(); + users = this.jsonMapper.readValue( + this.mockMvc + .perform( + get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + + "/list?modelIds=user1,user2,user6,user7") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + instAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(users); + assertTrue(users.size() == 2); + assertEquals("[user6, user7]", getOrderedUUIDs(users)); + } + + @Test + public void testGeneralNamesEndpoint() throws Exception { + final String sebAdminToken = getSebAdminAccess(); + + // for SEB Admin + Collection names = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names" + "?institutionId=1") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(names); + assertTrue(names.size() == 3); + assertEquals("[EntityName [entityType=USER, modelId=user1, name=admin ( SEBAdmin)], " + + "EntityName [entityType=USER, modelId=user2, name=inst1Admin ( Institutional1 Admin)], " + + "EntityName [entityType=USER, modelId=user5, name=examSupporter ( Exam Supporter)]]", names.toString()); + + // for an institutional admin 2 + final String instAdminToken = getAdminInstitution2Access(); + names = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names" + "?institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + instAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(names); + assertTrue(names.size() == 4); + assertEquals("[EntityName [entityType=USER, modelId=user3, name=inst2Admin ( Institutional2 Admin)], " + + "EntityName [entityType=USER, modelId=user4, name=examAdmin1 ( ExamAdmin1)], " + + "EntityName [entityType=USER, modelId=user6, name=deactivatedUser ( Deactivated)], " + + "EntityName [entityType=USER, modelId=user7, name=user1 ( User)]]", names.toString()); + + // for an institutional admin 2 only active + names = this.jsonMapper.readValue( + this.mockMvc + .perform( + get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names?active=true&institutionId=2") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header("Authorization", "Bearer " + instAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(names); + assertTrue(names.size() == 3); + assertEquals("[EntityName [entityType=USER, modelId=user3, name=inst2Admin ( Institutional2 Admin)], " + + "EntityName [entityType=USER, modelId=user4, name=examAdmin1 ( ExamAdmin1)], " + + "EntityName [entityType=USER, modelId=user7, name=user1 ( User)]]", names.toString()); + } + +// @Test +// public void createWithRoleAddRoleDeleteRole() throws Exception { +// final String token = getSebAdminAccess(); +// UserInfo createdUser = this.jsonMapper.readValue( +// this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT) +// .header("Authorization", "Bearer " + token) +// .contentType(MediaType.APPLICATION_FORM_URLENCODED) +// .param(Domain.USER.ATTR_NAME, "NewTestUser") +// .param(Domain.USER.ATTR_USERNAME, "NewTestUser") +// .param(Domain.USER.ATTR_LOCALE, Locale.ENGLISH.toLanguageTag()) +// .param(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID()) +// .param(UserMod.ATTR_NAME_NEW_PASSWORD, "12345678") +// .param(UserMod.ATTR_NAME_RETYPED_NEW_PASSWORD, "12345678")) +// .andExpect(status().isOk()) +// .andReturn().getResponse().getContentAsString(), +// new TypeReference() { +// }); +// +// assertNotNull(createdUser); +// assertEquals("NewTestUser", createdUser.name); +// assertEquals("[]", String.valueOf(createdUser.roles)); +// +// // add two roles +// createdUser = this.jsonMapper.readValue( +// this.mockMvc.perform(patch(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + createdUser.uuid) +// .header("Authorization", "Bearer " + token) +// .contentType(MediaType.APPLICATION_FORM_URLENCODED) +// .param(USER_ROLE.REFERENCE_NAME, "EXAM_SUPPORTER", "EXAM_ADMIN")) +// .andExpect(status().isOk()) +// .andReturn().getResponse().getContentAsString(), +// new TypeReference() { +// }); +// +// assertNotNull(createdUser); +// assertEquals("NewTestUser", createdUser.name); +// assertEquals("[]", String.valueOf(createdUser.roles)); +// } + + private UserInfo getUserInfo(final String name, final Collection infos) { + try { + return infos + .stream() + .filter(ui -> ui.username.equals(name)) + .findFirst() + .orElseThrow(NoSuchElementException::new); + } catch (final Exception e) { + return null; + } + } + +} diff --git a/src/test/resources/data-test.sql b/src/test/resources/data-test.sql index 86efec1d..93a9b3aa 100644 --- a/src/test/resources/data-test.sql +++ b/src/test/resources/data-test.sql @@ -5,13 +5,13 @@ INSERT INTO institution VALUES ; INSERT INTO user VALUES - (1, 1, 'user1', '2019-01-01', 'SEBAdmin', '', 'admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin@nomail.nomail', 'en', 'UTC', 1), - (2, 1, 'user2', '2019-01-01', 'Institutional1 Admin', '', 'inst1Admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin@nomail.nomail', 'en', 'UTC', 1), - (5, 1, 'user5', '2019-01-01', 'Exam Supporter', '', 'examSupporter', '', 'admin@nomail.nomail', 'en', 'UTC', 1), - (3, 2, 'user3', '2019-01-01', 'Institutional2 Admin', '', 'inst2Admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin@nomail.nomail', 'en', 'UTC', 1), - (4, 2, 'user4', '2019-01-01', 'ExamAdmin1', '', 'examAdmin1', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin@nomail.nomail', 'en', 'UTC', 1), - (6, 2, 'user6', '2019-01-01', 'Deactivated', '', 'deactivatedUser', '$2a$08$YICtiLuceLMeY4EE3TyMGuBNt6SXmAE9HZKilzS9RP00nn4OhexBm', 'user@nomail.nomail', 'en', 'UTC', 0), - (7, 2, 'user7', '2019-01-01', 'User', '', 'user1', '$2a$08$YICtiLuceLMeY4EE3TyMGuBNt6SXmAE9HZKilzS9RP00nn4OhexBm', 'user@nomail.nomail', 'en', 'UTC', 1) + (1, 1, 'user1', '2019-01-01', 'SEBAdmin', '', 'admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin1@nomail.nomail', 'en', 'UTC', 1), + (2, 1, 'user2', '2019-01-01', 'Institutional1 Admin', '', 'inst1Admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin2@nomail.nomail', 'en', 'UTC', 1), + (5, 1, 'user5', '2019-01-01', 'Exam Supporter', '', 'examSupporter', '', 'admin3@nomail.nomail', 'en', 'UTC', 1), + (3, 2, 'user3', '2019-01-01', 'Institutional2 Admin', '', 'inst2Admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin4@nomail.nomail', 'en', 'UTC', 1), + (4, 2, 'user4', '2019-01-01', 'ExamAdmin1', '', 'examAdmin1', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin5@nomail.nomail', 'en', 'UTC', 1), + (6, 2, 'user6', '2019-01-01', 'Deactivated', '', 'deactivatedUser', '$2a$08$YICtiLuceLMeY4EE3TyMGuBNt6SXmAE9HZKilzS9RP00nn4OhexBm', 'user1@nomail.nomail', 'en', 'UTC', 0), + (7, 2, 'user7', '2019-01-01', 'User', '', 'user1', '$2a$08$YICtiLuceLMeY4EE3TyMGuBNt6SXmAE9HZKilzS9RP00nn4OhexBm', 'user2@nomail.nomail', 'en', 'UTC', 1) ; INSERT INTO user_role VALUES