merge with branch 2.0.0-m3

This commit is contained in:
Sebastian Sdorra
2018-10-26 08:57:15 +02:00
228 changed files with 28285 additions and 10384 deletions

View File

@@ -0,0 +1,263 @@
package sonia.scm.api.v2.resources;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import org.apache.shiro.util.ThreadContext;
import org.assertj.core.util.Lists;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.Manager;
import sonia.scm.group.DefaultGroupManager;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.group.xml.XmlGroupDAO;
import sonia.scm.store.ConfigurationStore;
import sonia.scm.store.ConfigurationStoreFactory;
import sonia.scm.user.DefaultUserManager;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.user.xml.XmlUserDAO;
import sonia.scm.web.VndMediaType;
import sonia.scm.xml.XmlDatabase;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
@SubjectAware(configuration = "classpath:sonia/scm/shiro-002.ini")
@RunWith(MockitoJUnitRunner.Silent.class)
public class AutoCompleteResourceTest {
@Rule
public final ShiroRule shiroRule = new ShiroRule();
public static final String URL = "/" + AutoCompleteResource.PATH;
private final Integer defaultLimit = Manager.DEFAULT_LIMIT;
private Dispatcher dispatcher;
private XmlUserDAO userDao;
private XmlGroupDAO groupDao;
private XmlDatabase xmlDB;
private ObjectMapper jsonObjectMapper = new ObjectMapper();
@Before
public void prepareEnvironment() {
initMocks(this);
ConfigurationStoreFactory storeFactory = mock(ConfigurationStoreFactory.class);
ConfigurationStore<Object> storeConfig = mock(ConfigurationStore.class);
xmlDB = mock(XmlDatabase.class);
when(storeConfig.get()).thenReturn(xmlDB);
when(storeFactory.getStore(any(), any())).thenReturn(storeConfig);
XmlUserDAO userDao = new XmlUserDAO(storeFactory);
this.userDao = spy(userDao);
XmlGroupDAO groupDAO = new XmlGroupDAO(storeFactory);
groupDao = spy(groupDAO);
ReducedObjectModelToDtoMapperImpl mapper = new ReducedObjectModelToDtoMapperImpl();
UserManager userManager = new DefaultUserManager(this.userDao);
GroupManager groupManager = new DefaultGroupManager(groupDao);
AutoCompleteResource autoCompleteResource = new AutoCompleteResource(mapper, userManager, groupManager);
dispatcher = createDispatcher(autoCompleteResource);
}
@After
public void cleanupContext() {
ThreadContext.unbindSubject();
}
@Test
public void shouldGet400IfParameterLengthLessThan2CharsForUserSearch() throws Exception {
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "users?q=a")
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(400, response.getStatus());
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldGet400OnFailedParameterForUserSearch() throws Exception {
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "users")
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(400, response.getStatus());
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldSearchUsers() throws Exception {
ArrayList<User> users = Lists.newArrayList(createMockUser("YuCantFindMe", "ha ha"), createMockUser("user1", "User 1"), createMockUser("user2", "User 2"));
String searched = "user";
when(xmlDB.values()).thenReturn(users);
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "users?q=" + searched)
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
assertResultSize(response, 2);
assertTrue(response.getContentAsString().contains("\"id\":\"user1\""));
assertTrue(response.getContentAsString().contains("\"displayName\":\"User 1\""));
assertTrue(response.getContentAsString().contains("\"id\":\"user2\""));
assertTrue(response.getContentAsString().contains("\"displayName\":\"User 2\""));
}
@Test
@SubjectAware(username = "user_without_autocomplete_permission", password = "secret")
public void shouldGet403OnAutoCompleteUsers() throws Exception {
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "users?q=user" )
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus());
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldSearchUsersWithDefaultLimitLength() throws Exception {
List<User> userList = IntStream.range(0, 10).boxed().map(i -> createMockUser("user" + i, "User " + i)).collect(Collectors.toList());
when(xmlDB.values()).thenReturn(userList);
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "users?q=user")
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
assertResultSize(response, defaultLimit);
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldGet400OnFailedParameterForGroupSearch() throws Exception {
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "groups")
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(400, response.getStatus());
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldGet400IfParameterLengthLessThan2CharsForGroupSearch() throws Exception {
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "groups?q=a")
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(400, response.getStatus());
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldSearchGroups() throws Exception {
ArrayList<Group> groups = Lists.newArrayList(createMockGroup("YuCantFindMe"), createMockGroup("group_1"), createMockGroup("group_2"));
String searched = "group";
when(xmlDB.values()).thenReturn(groups);
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "groups?q=" + searched)
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
assertResultSize(response, 2);
assertTrue(response.getContentAsString().contains("\"id\":\"group_1\""));
assertTrue(response.getContentAsString().contains("\"displayName\":\"group_1\""));
assertTrue(response.getContentAsString().contains("\"id\":\"group_2\""));
assertTrue(response.getContentAsString().contains("\"displayName\":\"group_2\""));
}
@Test
@SubjectAware(username = "user_without_autocomplete_permission", password = "secret")
public void shouldGet403OnAutoCompleteGroups() throws Exception {
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "groups?q=user" )
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus());
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldSearchGroupsWithDefaultLimitLength() throws Exception {
List<Group> groups = IntStream.range(0, 10).boxed().map(i -> createMockGroup("group_" + i)).collect(Collectors.toList());
when(xmlDB.values()).thenReturn(groups);
MockHttpRequest request = MockHttpRequest
.get("/" + AutoCompleteResource.PATH + "groups?q=group")
.contentType(VndMediaType.AUTOCOMPLETE)
.accept(VndMediaType.AUTOCOMPLETE);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
assertResultSize(response, defaultLimit);
}
private User createMockUser(String id, String name) {
return new User(id, name, "em@l.de");
}
private Group createMockGroup(String name) {
Group group = new Group("type", name);
group.setDescription(name);
return group;
}
private void assertResultSize(MockHttpResponse response, int size) throws java.io.IOException {
ReducedObjectModelDto[] reducedObjectModelDtos = jsonObjectMapper.readValue(response.getContentAsString(), ReducedObjectModelDto[].class);
assertEquals(reducedObjectModelDtos.length, size);
}
}

View File

@@ -8,34 +8,26 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.repository.BrowserResult;
import sonia.scm.repository.FileObject;
import sonia.scm.repository.NamespaceAndName;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
public class BrowserResultToBrowserResultDtoMapperTest {
public class BrowserResultToFileObjectDtoMapperTest {
private final URI baseUri = URI.create("http://example.com/base/");
@SuppressWarnings("unused") // Is injected
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
@Mock
private FileObjectToFileObjectDtoMapper fileObjectToFileObjectDtoMapper;
@InjectMocks
private BrowserResultToBrowserResultDtoMapper mapper;
private FileObjectToFileObjectDtoMapperImpl fileObjectToFileObjectDtoMapper;
private BrowserResultToFileObjectDtoMapper mapper;
private final Subject subject = mock(Subject.class);
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
@@ -47,6 +39,7 @@ public class BrowserResultToBrowserResultDtoMapperTest {
@Before
public void init() {
initMocks(this);
mapper = new BrowserResultToFileObjectDtoMapper(fileObjectToFileObjectDtoMapper);
subjectThreadState.bind();
ThreadContext.bind(subject);
@@ -63,9 +56,6 @@ public class BrowserResultToBrowserResultDtoMapperTest {
fileObject2.setPath("/path/object/2");
fileObject2.setDescription("description of file object 2");
fileObject2.setDirectory(true);
when(fileObjectToFileObjectDtoMapper.map(any(FileObject.class), any(NamespaceAndName.class), anyString()))
.thenReturn(new FileObjectDto());
}
@After
@@ -77,7 +67,7 @@ public class BrowserResultToBrowserResultDtoMapperTest {
public void shouldMapAttributesCorrectly() {
BrowserResult browserResult = createBrowserResult();
BrowserResultDto dto = mapper.map(browserResult, new NamespaceAndName("foo", "bar"), "path");
FileObjectDto dto = mapper.map(browserResult, new NamespaceAndName("foo", "bar"));
assertEqualAttributes(browserResult, dto);
}
@@ -87,10 +77,9 @@ public class BrowserResultToBrowserResultDtoMapperTest {
BrowserResult browserResult = createBrowserResult();
NamespaceAndName namespaceAndName = new NamespaceAndName("foo", "bar");
BrowserResultDto dto = mapper.map(browserResult, namespaceAndName, "path");
FileObjectDto dto = mapper.map(browserResult, namespaceAndName);
verify(fileObjectToFileObjectDtoMapper).map(fileObject1, namespaceAndName, "Revision");
verify(fileObjectToFileObjectDtoMapper).map(fileObject2, namespaceAndName, "Revision");
assertThat(dto.getEmbedded().getItemsBy("children")).hasSize(2);
}
@Test
@@ -98,28 +87,27 @@ public class BrowserResultToBrowserResultDtoMapperTest {
BrowserResult browserResult = createBrowserResult();
NamespaceAndName namespaceAndName = new NamespaceAndName("foo", "bar");
BrowserResultDto dto = mapper.map(browserResult, namespaceAndName, "path");
FileObjectDto dto = mapper.map(browserResult, namespaceAndName);
assertThat(dto.getLinks().getLinkBy("self").get().getHref()).contains("path");
}
private BrowserResult createBrowserResult() {
BrowserResult browserResult = new BrowserResult();
browserResult.setRevision("Revision");
browserResult.setFiles(createFileObjects());
return browserResult;
return new BrowserResult("Revision", createFileObject());
}
private List<FileObject> createFileObjects() {
List<FileObject> fileObjects = new ArrayList<>();
private FileObject createFileObject() {
FileObject file = new FileObject();
file.setName("");
file.setPath("/path");
file.setDirectory(true);
fileObjects.add(fileObject1);
fileObjects.add(fileObject2);
return fileObjects;
file.addChild(fileObject1);
file.addChild(fileObject2);
return file;
}
private void assertEqualAttributes(BrowserResult browserResult, BrowserResultDto dto) {
private void assertEqualAttributes(BrowserResult browserResult, FileObjectDto dto) {
assertThat(dto.getRevision()).isEqualTo(browserResult.getRevision());
}

View File

@@ -0,0 +1,61 @@
package sonia.scm.api.v2.resources;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import sonia.scm.PageResult;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.Repository;
import java.net.URI;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ChangesetCollectionToDtoMapperTest {
public static final Repository REPOSITORY = new Repository("", "git", "space", "name");
public static final Changeset CHANGESET = new Changeset();
private final ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper = mock(ChangesetToChangesetDtoMapper.class);
private final ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, ResourceLinksMock.createMock(URI.create("/")));
@Test
public void shouldMapCollectionEntries() {
ChangesetDto expectedChangesetDto = new ChangesetDto();
when(changesetToChangesetDtoMapper.map(CHANGESET, REPOSITORY)).thenReturn(expectedChangesetDto);
CollectionDto collectionDto = changesetCollectionToDtoMapper.map(0, 1, new PageResult<>(asList(CHANGESET), 1), REPOSITORY);
assertThat(collectionDto.getEmbedded().hasItem("changesets")).isTrue();
assertThat(collectionDto.getEmbedded().getItemsBy("changesets")).containsExactly(expectedChangesetDto);
assertThat(collectionDto.getEmbedded().hasItem("branch")).isFalse();
}
@Test
public void shouldNotEmbedBranchIfNotSpecified() {
ChangesetDto expectedChangesetDto = new ChangesetDto();
when(changesetToChangesetDtoMapper.map(CHANGESET, REPOSITORY)).thenReturn(expectedChangesetDto);
CollectionDto collectionDto = changesetCollectionToDtoMapper.map(0, 1, new PageResult<>(asList(CHANGESET), 1), REPOSITORY);
assertThat(collectionDto.getEmbedded().hasItem("branch")).isFalse();
}
@Test
public void shouldEmbedBranchIfSpecified() {
ChangesetDto expectedChangesetDto = new ChangesetDto();
when(changesetToChangesetDtoMapper.map(CHANGESET, REPOSITORY)).thenReturn(expectedChangesetDto);
CollectionDto collectionDto = changesetCollectionToDtoMapper.map(0, 1, new PageResult<>(asList(CHANGESET), 1), REPOSITORY, "someBranch");
assertThat(collectionDto.getEmbedded().hasItem("branch")).isTrue();
assertThat(collectionDto.getEmbedded().getItemsBy("branch"))
.hasSize(1)
.first().matches(b -> b.getLinks().getLinkBy("self").isPresent())
.extracting(b -> b.getLinks().getLinkBy("self").get().getHref()).first().isEqualTo("/v2/repositories/space/name/branches/someBranch");
assertThat(collectionDto.getEmbedded().getItemsBy("branch"))
.first().extracting("name").first().isEqualTo("someBranch");
}
}

View File

@@ -109,8 +109,8 @@ public class ChangesetRootResourceTest extends RepositoryTestBase {
List<Changeset> changesetList = Lists.newArrayList(new Changeset(id, Date.from(creationDate).getTime(), new Person(authorName, authorEmail), commit));
when(changesetPagingResult.getChangesets()).thenReturn(changesetList);
when(changesetPagingResult.getTotal()).thenReturn(1);
when(logCommandBuilder.setPagingStart(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPagingStart(0)).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPagingLimit(10)).thenReturn(logCommandBuilder);
when(logCommandBuilder.setBranch(anyString())).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenReturn(changesetPagingResult);
MockHttpRequest request = MockHttpRequest
@@ -126,6 +126,34 @@ public class ChangesetRootResourceTest extends RepositoryTestBase {
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
}
@Test
public void shouldGetSinglePageOfChangeSets() throws Exception {
String id = "revision_123";
Instant creationDate = Instant.now();
String authorName = "name";
String authorEmail = "em@i.l";
String commit = "my branch commit";
ChangesetPagingResult changesetPagingResult = mock(ChangesetPagingResult.class);
List<Changeset> changesetList = Lists.newArrayList(new Changeset(id, Date.from(creationDate).getTime(), new Person(authorName, authorEmail), commit));
when(changesetPagingResult.getChangesets()).thenReturn(changesetList);
when(changesetPagingResult.getTotal()).thenReturn(1);
when(logCommandBuilder.setPagingStart(20)).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPagingLimit(10)).thenReturn(logCommandBuilder);
when(logCommandBuilder.setBranch(anyString())).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenReturn(changesetPagingResult);
MockHttpRequest request = MockHttpRequest
.get(CHANGESET_URL + "?page=2")
.accept(VndMediaType.CHANGESET_COLLECTION);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(200, response.getStatus());
log.info("Response :{}", response.getContentAsString());
assertTrue(response.getContentAsString().contains(String.format("\"id\":\"%s\"", id)));
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", authorName)));
assertTrue(response.getContentAsString().contains(String.format("\"mail\":\"%s\"", authorEmail)));
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
}
@Test
public void shouldGetChangeSet() throws Exception {
String id = "revision_123";
@@ -137,8 +165,6 @@ public class ChangesetRootResourceTest extends RepositoryTestBase {
List<Changeset> changesetList = Lists.newArrayList(new Changeset(id, Date.from(creationDate).getTime(), new Person(authorName, authorEmail), commit));
when(changesetPagingResult.getChangesets()).thenReturn(changesetList);
when(changesetPagingResult.getTotal()).thenReturn(1);
when(logCommandBuilder.setPagingStart(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setEndChangeset(anyString())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setStartChangeset(anyString())).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenReturn(changesetPagingResult);

View File

@@ -5,6 +5,7 @@ import org.jboss.resteasy.mock.MockDispatcherFactory;
import sonia.scm.api.rest.AlreadyExistsExceptionMapper;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.api.rest.ConcurrentModificationExceptionMapper;
import sonia.scm.api.rest.IllegalArgumentExceptionMapper;
public class DispatcherMock {
public static Dispatcher createDispatcher(Object resource) {
@@ -17,6 +18,7 @@ public class DispatcherMock {
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(ChangePasswordNotAllowedExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(InvalidPasswordExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(IllegalArgumentExceptionMapper.class);
return dispatcher;
}
}

View File

@@ -13,7 +13,7 @@ import java.util.Optional;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini")
@SubjectAware(configuration = "classpath:sonia/scm/shiro-002.ini")
public class IndexResourceTest {
@Rule
@@ -94,6 +94,26 @@ public class IndexResourceTest {
Assertions.assertThat(index.getLinks().getLinkBy("config")).matches(o -> !o.isPresent());
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldRenderAutoCompleteLinks() {
IndexDto index = indexResource.getIndex();
Assertions.assertThat(index.getLinks().getLinksBy("autocomplete"))
.extracting("name")
.containsExactlyInAnyOrder("users", "groups");
}
@Test
@SubjectAware(username = "user_without_autocomplete_permission", password = "secret")
public void userWithoutAutocompletePermissionShouldNotSeeAutoCompleteLinks() {
IndexDto index = indexResource.getIndex();
Assertions.assertThat(index.getLinks().getLinksBy("autocomplete"))
.extracting("name")
.doesNotContainSequence("users", "groups");
}
@Test
@SubjectAware(username = "dent", password = "secret")
public void shouldRenderAdminLinksIfAuthorized() {

View File

@@ -13,10 +13,12 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.user.InvalidPasswordException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;
import javax.lang.model.util.Types;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.net.URISyntaxException;
@@ -26,6 +28,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -69,7 +72,6 @@ public class MeResourceTest {
doNothing().when(userManager).modify(userCaptor.capture());
doNothing().when(userManager).delete(userCaptor.capture());
when(userManager.isTypeDefault(userCaptor.capture())).thenCallRealMethod();
when(userManager.getUserTypeChecker()).thenCallRealMethod();
when(userManager.getDefaultType()).thenReturn("xml");
MeResource meResource = new MeResource(userToDtoMapper, userManager, passwordService);
when(uriInfo.getApiRestUri()).thenReturn(URI.create("/"));
@@ -97,38 +99,40 @@ public class MeResourceTest {
public void shouldEncryptPasswordBeforeChanging() throws Exception {
String newPassword = "pwd123";
String encryptedNewPassword = "encrypted123";
String oldPassword = "notEncriptedSecret";
String encryptedOldPassword = "encryptedOld";
String oldPassword = "secret";
String content = String.format("{ \"oldPassword\": \"%s\" , \"newPassword\": \"%s\" }", oldPassword, newPassword);
MockHttpRequest request = MockHttpRequest
.put("/" + MeResource.ME_PATH_V2 + "password")
.contentType(VndMediaType.PASSWORD_CHANGE)
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
when(passwordService.encryptPassword(eq(newPassword))).thenReturn(encryptedNewPassword);
when(passwordService.encryptPassword(eq(oldPassword))).thenReturn("secret");
when(passwordService.encryptPassword(newPassword)).thenReturn(encryptedNewPassword);
when(passwordService.encryptPassword(oldPassword)).thenReturn(encryptedOldPassword);
ArgumentCaptor<String> encryptedOldPasswordCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> encryptedNewPasswordCaptor = ArgumentCaptor.forClass(String.class);
doNothing().when(userManager).changePasswordForLoggedInUser(encryptedOldPasswordCaptor.capture(), encryptedNewPasswordCaptor.capture());
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
verify(userManager).modify(any(User.class));
User updatedUser = userCaptor.getValue();
assertEquals(encryptedNewPassword, updatedUser.getPassword());
assertEquals(encryptedNewPassword, encryptedNewPasswordCaptor.getValue());
assertEquals(encryptedOldPassword, encryptedOldPasswordCaptor.getValue());
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldGet400OnChangePasswordOfUserWithNonDefaultType() throws Exception {
public void shouldGet400OnMissingOldPassword() throws Exception {
originalUser.setType("not an xml type");
String newPassword = "pwd123";
String oldPassword = "notEncriptedSecret";
String content = String.format("{ \"oldPassword\": \"%s\" , \"newPassword\": \"%s\" }", oldPassword, newPassword);
String content = String.format("{ \"newPassword\": \"%s\" }", newPassword);
MockHttpRequest request = MockHttpRequest
.put("/" + MeResource.ME_PATH_V2 + "password")
.contentType(VndMediaType.PASSWORD_CHANGE)
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
when(passwordService.encryptPassword(newPassword)).thenReturn("encrypted123");
when(passwordService.encryptPassword(eq(oldPassword))).thenReturn("secret");
dispatcher.invoke(request, response);
@@ -137,17 +141,34 @@ public class MeResourceTest {
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldGet400OnChangePasswordIfOldPasswordDoesNotMatchOriginalPassword() throws Exception {
public void shouldGet400OnMissingEmptyPassword() throws Exception {
String newPassword = "pwd123";
String oldPassword = "notEncriptedSecret";
String oldPassword = "";
String content = String.format("{ \"oldPassword\": \"%s\" , \"newPassword\": \"%s\" }", oldPassword, newPassword);
MockHttpRequest request = MockHttpRequest
.put("/" + MeResource.ME_PATH_V2 + "password")
.contentType(VndMediaType.PASSWORD_CHANGE)
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
when(passwordService.encryptPassword(newPassword)).thenReturn("encrypted123");
when(passwordService.encryptPassword(eq(oldPassword))).thenReturn("differentThanSecret");
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldMapExceptionFromManager() throws Exception {
String newPassword = "pwd123";
String oldPassword = "secret";
String content = String.format("{ \"oldPassword\": \"%s\" , \"newPassword\": \"%s\" }", oldPassword, newPassword);
MockHttpRequest request = MockHttpRequest
.put("/" + MeResource.ME_PATH_V2 + "password")
.contentType(VndMediaType.PASSWORD_CHANGE)
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
doThrow(InvalidPasswordException.class).when(userManager).changePasswordForLoggedInUser(any(), any());
dispatcher.invoke(request, response);

View File

@@ -16,6 +16,7 @@ public class ResourceLinksMock {
when(resourceLinks.user()).thenReturn(userLinks);
when(resourceLinks.me()).thenReturn(new ResourceLinks.MeLinks(uriInfo,userLinks));
when(resourceLinks.userCollection()).thenReturn(new ResourceLinks.UserCollectionLinks(uriInfo));
when(resourceLinks.autoComplete()).thenReturn(new ResourceLinks.AutoCompleteLinks(uriInfo));
when(resourceLinks.group()).thenReturn(new ResourceLinks.GroupLinks(uriInfo));
when(resourceLinks.groupCollection()).thenReturn(new ResourceLinks.GroupCollectionLinks(uriInfo));
when(resourceLinks.repository()).thenReturn(new ResourceLinks.RepositoryLinks(uriInfo));

View File

@@ -173,6 +173,30 @@ public class ResourceLinksTest {
assertEquals(BASE_URL + ConfigResource.CONFIG_PATH_V2, url);
}
@Test
public void shouldHandleSpacesInPaths() {
String url = resourceLinks.source().content("space", "name", "rev", "name with spaces");
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/name/content/rev/name%20with%20spaces", url);
}
@Test
public void shouldHandleBackslashInPaths() {
String url = resourceLinks.source().content("space", "name", "rev", "name_with_\\");
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/name/content/rev/name_with_%5C", url);
}
@Test
public void shouldHandleNewLineInPaths() {
String url = resourceLinks.source().content("space", "name", "rev", "name_with\nnew_line");
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/name/content/rev/name_with%0Anew_line", url);
}
@Test
public void shouldKeepSlashesInInPaths() {
String url = resourceLinks.source().content("space", "name", "rev", "some/dir/somewhere");
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/name/content/rev/some/dir/somewhere", url);
}
@Before
public void initUriInfo() {
initMocks(this);

View File

@@ -10,11 +10,11 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.NotFoundException;
import sonia.scm.repository.BrowserResult;
import sonia.scm.repository.FileObject;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.RevisionNotFoundException;
import sonia.scm.repository.api.BrowseCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
@@ -22,12 +22,8 @@ import sonia.scm.repository.api.RepositoryServiceFactory;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
@@ -46,30 +42,25 @@ public class SourceRootResourceTest extends RepositoryTestBase {
@Mock
private BrowseCommandBuilder browseCommandBuilder;
@Mock
private FileObjectToFileObjectDtoMapper fileObjectToFileObjectDtoMapper;
@InjectMocks
private BrowserResultToBrowserResultDtoMapper browserResultToBrowserResultDtoMapper;
private FileObjectToFileObjectDtoMapperImpl fileObjectToFileObjectDtoMapper;
private BrowserResultToFileObjectDtoMapper browserResultToFileObjectDtoMapper;
@Before
public void prepareEnvironment() throws Exception {
browserResultToFileObjectDtoMapper = new BrowserResultToFileObjectDtoMapper(fileObjectToFileObjectDtoMapper);
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
when(service.getBrowseCommand()).thenReturn(browseCommandBuilder);
FileObjectDto dto = new FileObjectDto();
dto.setName("name");
dto.setLength(1024);
when(fileObjectToFileObjectDtoMapper.map(any(FileObject.class), any(NamespaceAndName.class), anyString())).thenReturn(dto);
SourceRootResource sourceRootResource = new SourceRootResource(serviceFactory, browserResultToBrowserResultDtoMapper);
SourceRootResource sourceRootResource = new SourceRootResource(serviceFactory, browserResultToFileObjectDtoMapper);
super.sourceRootResource = Providers.of(sourceRootResource);
dispatcher = createDispatcher(getRepositoryRootResource());
}
@Test
public void shouldReturnSources() throws URISyntaxException, IOException, RevisionNotFoundException {
public void shouldReturnSources() throws URISyntaxException, IOException, NotFoundException {
BrowserResult result = createBrowserResult();
when(browseCommandBuilder.getBrowserResult()).thenReturn(result);
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources");
@@ -77,8 +68,9 @@ public class SourceRootResourceTest extends RepositoryTestBase {
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isEqualTo(200);
System.out.println(response.getContentAsString());
assertThat(response.getContentAsString()).contains("\"revision\":\"revision\"");
assertThat(response.getContentAsString()).contains("\"files\":");
assertThat(response.getContentAsString()).contains("\"children\":");
}
@Test
@@ -92,13 +84,11 @@ public class SourceRootResourceTest extends RepositoryTestBase {
}
@Test
public void shouldGetResultForSingleFile() throws URISyntaxException, IOException, RevisionNotFoundException {
BrowserResult browserResult = new BrowserResult();
browserResult.setRevision("revision");
public void shouldGetResultForSingleFile() throws URISyntaxException, IOException, NotFoundException {
FileObject fileObject = new FileObject();
fileObject.setName("File Object!");
browserResult.setFiles(Arrays.asList(fileObject));
fileObject.setPath("/");
BrowserResult browserResult = new BrowserResult("revision", fileObject);
when(browseCommandBuilder.getBrowserResult()).thenReturn(browserResult);
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources/revision/fileabc");
@@ -121,10 +111,15 @@ public class SourceRootResourceTest extends RepositoryTestBase {
}
private BrowserResult createBrowserResult() {
return new BrowserResult("revision", "tag", "branch", createFileObjects());
return new BrowserResult("revision", createFileObject());
}
private List<FileObject> createFileObjects() {
private FileObject createFileObject() {
FileObject parent = new FileObject();
parent.setName("bar");
parent.setPath("/foo/bar");
parent.setDirectory(true);
FileObject fileObject1 = new FileObject();
fileObject1.setName("FO 1");
fileObject1.setDirectory(false);
@@ -132,6 +127,7 @@ public class SourceRootResourceTest extends RepositoryTestBase {
fileObject1.setPath("/foo/bar/fo1");
fileObject1.setLength(1024L);
fileObject1.setLastModified(0L);
parent.addChild(fileObject1);
FileObject fileObject2 = new FileObject();
fileObject2.setName("FO 2");
@@ -140,7 +136,8 @@ public class SourceRootResourceTest extends RepositoryTestBase {
fileObject2.setPath("/foo/bar/fo2");
fileObject2.setLength(4096L);
fileObject2.setLastModified(1234L);
parent.addChild(fileObject2);
return Arrays.asList(fileObject1, fileObject2);
return parent;
}
}

View File

@@ -14,7 +14,9 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.NotFoundException;
import sonia.scm.PageResult;
import sonia.scm.user.ChangePasswordNotAllowedException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;
@@ -23,7 +25,6 @@ import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.MessageFormat;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
@@ -32,6 +33,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -70,7 +72,6 @@ public class UserRootResourceTest {
originalUser = createDummyUser("Neo");
when(userManager.create(userCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]);
when(userManager.isTypeDefault(userCaptor.capture())).thenCallRealMethod();
when(userManager.getUserTypeChecker()).thenCallRealMethod();
doNothing().when(userManager).modify(userCaptor.capture());
doNothing().when(userManager).delete(userCaptor.capture());
when(userManager.getDefaultType()).thenReturn("xml");
@@ -144,7 +145,7 @@ public class UserRootResourceTest {
String content = String.format("{\"newPassword\": \"%s\"}", newPassword);
MockHttpRequest request = MockHttpRequest
.put("/" + UserRootResource.USERS_PATH_V2 + "Neo/password")
.contentType(VndMediaType.PASSWORD_CHANGE)
.contentType(VndMediaType.PASSWORD_OVERWRITE)
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
when(passwordService.encryptPassword(newPassword)).thenReturn("encrypted123");
@@ -152,26 +153,61 @@ public class UserRootResourceTest {
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
verify(userManager).modify(any(User.class));
User updatedUser = userCaptor.getValue();
assertEquals("encrypted123", updatedUser.getPassword());
verify(userManager).overwritePassword("Neo", "encrypted123");
}
@Test
public void shouldGet400OnChangePasswordOfUserWithNonDefaultType() throws Exception {
public void shouldGet400OnOverwritePasswordWhenManagerThrowsNotAllowed() throws Exception {
originalUser.setType("not an xml type");
String newPassword = "pwd123";
String content = String.format("{\"newPassword\": \"%s\"}", newPassword);
MockHttpRequest request = MockHttpRequest
.put("/" + UserRootResource.USERS_PATH_V2 + "Neo/password")
.contentType(VndMediaType.PASSWORD_CHANGE)
.contentType(VndMediaType.PASSWORD_OVERWRITE)
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
doThrow(ChangePasswordNotAllowedException.class).when(userManager).overwritePassword(any(), any());
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
}
@Test
public void shouldGet404OnOverwritePasswordWhenNotFound() throws Exception {
originalUser.setType("not an xml type");
String newPassword = "pwd123";
String content = String.format("{\"newPassword\": \"%s\"}", newPassword);
MockHttpRequest request = MockHttpRequest
.put("/" + UserRootResource.USERS_PATH_V2 + "Neo/password")
.contentType(VndMediaType.PASSWORD_OVERWRITE)
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
doThrow(NotFoundException.class).when(userManager).overwritePassword(any(), any());
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_NOT_FOUND, response.getStatus());
}
@Test
public void shouldEncryptPasswordOnOverwritePassword() throws Exception {
originalUser.setType("not an xml type");
String newPassword = "pwd123";
String content = String.format("{\"newPassword\": \"%s\"}", newPassword);
MockHttpRequest request = MockHttpRequest
.put("/" + UserRootResource.USERS_PATH_V2 + "Neo/password")
.contentType(VndMediaType.PASSWORD_OVERWRITE)
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
when(passwordService.encryptPassword(newPassword)).thenReturn("encrypted123");
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
verify(userManager).overwritePassword("Neo", "encrypted123");
}
@Test

View File

@@ -65,7 +65,7 @@ public class UserToUserDtoMapperTest {
}
@Test
public void shouldGetPasswordLinkOnlyForDefaultUserType() {
public void shouldGetPasswordLinkForAdmin() {
User user = createDefaultUser();
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
when(userManager.isTypeDefault(eq(user))).thenReturn(true);
@@ -73,14 +73,15 @@ public class UserToUserDtoMapperTest {
UserDto userDto = mapper.map(user);
assertEquals("expected password link with modify permission", expectedBaseUri.resolve("abc/password").toString(), userDto.getLinks().getLinkBy("password").get().getHref());
}
when(subject.isPermitted("user:modify:abc")).thenReturn(false);
userDto = mapper.map(user);
assertEquals("expected password link on mission modify permission", expectedBaseUri.resolve("abc/password").toString(), userDto.getLinks().getLinkBy("password").get().getHref());
@Test
public void shouldGetPasswordLinkOnlyForDefaultUserType() {
User user = createDefaultUser();
when(subject.isPermitted("user:modify:abc")).thenReturn(true);
when(userManager.isTypeDefault(eq(user))).thenReturn(false);
userDto = mapper.map(user);
UserDto userDto = mapper.map(user);
assertFalse("expected no password link", userDto.getLinks().getLinkBy("password").isPresent());
}

View File

@@ -152,7 +152,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test(expected = NotFoundException.class)
public void testDeleteNotFound() throws NotFoundException {
public void testDeleteNotFound(){
manager.delete(createRepositoryWithId());
}
@@ -304,7 +304,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test
public void testModify() throws NotFoundException, AlreadyExistsException {
public void testModify() throws AlreadyExistsException {
Repository heartOfGold = createTestRepository();
heartOfGold.setDescription("prototype ship");
@@ -328,12 +328,12 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test(expected = NotFoundException.class)
public void testModifyNotFound() throws NotFoundException {
public void testModifyNotFound(){
manager.modify(createRepositoryWithId());
}
@Test
public void testRefresh() throws NotFoundException, AlreadyExistsException {
public void testRefresh() throws AlreadyExistsException {
Repository heartOfGold = createTestRepository();
String description = heartOfGold.getDescription();
@@ -354,7 +354,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test(expected = RepositoryNotFoundException.class)
public void testRefreshNotFound() throws NotFoundException {
public void testRefreshNotFound(){
manager.refresh(createRepositoryWithId());
}
@@ -495,7 +495,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
return createRepository(RepositoryTestData.createHeartOfGold());
}
private void delete(Manager<Repository> manager, Repository repository) throws NotFoundException {
private void delete(Manager<Repository> manager, Repository repository){
String id = repository.getId();

View File

@@ -1,10 +1,10 @@
/**
* Copyright (c) 2014, Sebastian Sdorra
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
@@ -13,7 +13,7 @@
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -24,9 +24,9 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* http://bitbucket.org/sdorra/scm-manager
*
*
*/
package sonia.scm.security;
@@ -57,7 +57,6 @@ import sonia.scm.repository.RepositoryTestData;
import sonia.scm.user.User;
import sonia.scm.user.UserTestData;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
@@ -70,7 +69,7 @@ import static org.mockito.Mockito.when;
/**
* Unit tests for {@link AuthorizationCollector}.
*
*
* @author Sebastian Sdorra
*/
@SuppressWarnings("unchecked")
@@ -79,28 +78,28 @@ public class DefaultAuthorizationCollectorTest {
@Mock
private Cache cache;
@Mock
private CacheManager cacheManager;
@Mock
private RepositoryDAO repositoryDAO;
@Mock
private SecuritySystem securitySystem;
private DefaultAuthorizationCollector collector;
@Rule
public ShiroRule shiro = new ShiroRule();
/**
* Set up object to test.
*/
@Before
public void setUp(){
when(cacheManager.getCache(Mockito.any(String.class))).thenReturn(cache);
collector = new DefaultAuthorizationCollector(cacheManager, repositoryDAO, securitySystem);
}
@@ -116,7 +115,7 @@ public class DefaultAuthorizationCollectorTest {
assertThat(authInfo.getStringPermissions(), nullValue());
assertThat(authInfo.getObjectPermissions(), nullValue());
}
/**
* Tests {@link AuthorizationCollector#collect()} from cache.
*/
@@ -124,16 +123,15 @@ public class DefaultAuthorizationCollectorTest {
@SubjectAware(
configuration = "classpath:sonia/scm/shiro-001.ini"
)
public void testCollectFromCache()
{
public void testCollectFromCache() {
AuthorizationInfo info = new SimpleAuthorizationInfo();
when(cache.get(anyObject())).thenReturn(info);
authenticate(UserTestData.createTrillian(), "main");
AuthorizationInfo authInfo = collector.collect();
assertSame(info, authInfo);
}
/**
* Tests {@link AuthorizationCollector#collect()} with cache.
*/
@@ -141,13 +139,13 @@ public class DefaultAuthorizationCollectorTest {
@SubjectAware(
configuration = "classpath:sonia/scm/shiro-001.ini"
)
public void testCollectWithCache(){
public void testCollectWithCache() {
authenticate(UserTestData.createTrillian(), "main");
AuthorizationInfo authInfo = collector.collect();
verify(cache).put(any(), any());
}
/**
* Tests {@link AuthorizationCollector#collect()} without permissions.
*/
@@ -155,17 +153,16 @@ public class DefaultAuthorizationCollectorTest {
@SubjectAware(
configuration = "classpath:sonia/scm/shiro-001.ini"
)
public void testCollectWithoutPermissions()
{
public void testCollectWithoutPermissions() {
authenticate(UserTestData.createTrillian(), "main");
AuthorizationInfo authInfo = collector.collect();
assertThat(authInfo.getRoles(), Matchers.contains(Role.USER));
assertThat(authInfo.getStringPermissions(), hasSize(1));
assertThat(authInfo.getStringPermissions(), contains("user:read:trillian"));
assertThat(authInfo.getStringPermissions(), hasSize(4));
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("user:autocomplete", "group:autocomplete", "user:changePassword:trillian", "user:read:trillian"));
assertThat(authInfo.getObjectPermissions(), nullValue());
}
/**
* Tests {@link AuthorizationCollector#collect()} as admin.
*/
@@ -173,18 +170,17 @@ public class DefaultAuthorizationCollectorTest {
@SubjectAware(
configuration = "classpath:sonia/scm/shiro-001.ini"
)
public void testCollectAsAdmin()
{
public void testCollectAsAdmin() {
User trillian = UserTestData.createTrillian();
trillian.setAdmin(true);
authenticate(trillian, "main");
AuthorizationInfo authInfo = collector.collect();
assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER, Role.ADMIN));
assertThat(authInfo.getObjectPermissions(), nullValue());
assertThat(authInfo.getStringPermissions(), Matchers.contains("*"));
}
/**
* Tests {@link AuthorizationCollector#collect()} with repository permissions.
*/
@@ -192,8 +188,7 @@ public class DefaultAuthorizationCollectorTest {
@SubjectAware(
configuration = "classpath:sonia/scm/shiro-001.ini"
)
public void testCollectWithRepositoryPermissions()
{
public void testCollectWithRepositoryPermissions() {
String group = "heart-of-gold-crew";
authenticate(UserTestData.createTrillian(), group);
Repository heartOfGold = RepositoryTestData.createHeartOfGold();
@@ -204,14 +199,14 @@ public class DefaultAuthorizationCollectorTest {
sonia.scm.repository.Permission permission = new sonia.scm.repository.Permission(group, PermissionType.WRITE, true);
puzzle42.setPermissions(Lists.newArrayList(permission));
when(repositoryDAO.getAll()).thenReturn(Lists.newArrayList(heartOfGold, puzzle42));
// execute and assert
AuthorizationInfo authInfo = collector.collect();
assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER));
assertThat(authInfo.getObjectPermissions(), nullValue());
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("repository:read,pull:one", "repository:read,pull,push:two", "user:read:trillian"));
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("user:autocomplete", "group:autocomplete", "user:changePassword:trillian", "repository:read,pull:one", "repository:read,pull,push:two", "user:read:trillian"));
}
/**
* Tests {@link AuthorizationCollector#collect()} with global permissions.
*/
@@ -219,20 +214,20 @@ public class DefaultAuthorizationCollectorTest {
@SubjectAware(
configuration = "classpath:sonia/scm/shiro-001.ini"
)
public void testCollectWithGlobalPermissions(){
public void testCollectWithGlobalPermissions() {
authenticate(UserTestData.createTrillian(), "main");
StoredAssignedPermission p1 = new StoredAssignedPermission("one", new AssignedPermission("one", "one:one"));
StoredAssignedPermission p2 = new StoredAssignedPermission("two", new AssignedPermission("two", "two:two"));
when(securitySystem.getPermissions(Mockito.any(Predicate.class))).thenReturn(Lists.newArrayList(p1, p2));
// execute and assert
AuthorizationInfo authInfo = collector.collect();
assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER));
assertThat(authInfo.getObjectPermissions(), nullValue());
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("one:one", "two:two", "user:read:trillian"));
assertThat(authInfo.getStringPermissions(), containsInAnyOrder("one:one", "two:two", "user:read:trillian", "user:autocomplete", "group:autocomplete", "user:changePassword:trillian"));
}
private void authenticate(User user, String group, String... groups) {
SimplePrincipalCollection spc = new SimplePrincipalCollection();
spc.add(user.getName(), "unit");
@@ -249,9 +244,9 @@ public class DefaultAuthorizationCollectorTest {
public void testInvalidateCache() {
collector.invalidateCache(AuthorizationChangedEvent.createForEveryUser());
verify(cache).clear();
collector.invalidateCache(AuthorizationChangedEvent.createForUser("dent"));
verify(cache).removeAll(Mockito.any(Predicate.class));
}
}

View File

@@ -39,9 +39,13 @@ import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.collect.Lists;
import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import sonia.scm.NotFoundException;
import sonia.scm.store.JAXBConfigurationStoreFactory;
import sonia.scm.user.xml.XmlUserDAO;
import sonia.scm.util.MockUtil;
@@ -70,6 +74,10 @@ public class DefaultUserManagerTest extends UserManagerTestBase
@Rule
public ShiroRule shiro = new ShiroRule();
private UserDAO userDAO = mock(UserDAO.class);
private User trillian;
/**
* Method description
*
@@ -82,6 +90,16 @@ public class DefaultUserManagerTest extends UserManagerTestBase
return new DefaultUserManager(createXmlUserDAO());
}
@Before
public void initDao() {
trillian = UserTestData.createTrillian();
trillian.setPassword("oldEncrypted");
userDAO = mock(UserDAO.class);
when(userDAO.getType()).thenReturn("xml");
when(userDAO.get("trillian")).thenReturn(trillian);
}
/**
* Method description
*
@@ -89,7 +107,6 @@ public class DefaultUserManagerTest extends UserManagerTestBase
@Test
public void testDefaultAccountAfterFristStart()
{
UserDAO userDAO = mock(UserDAO.class);
List<User> users = Lists.newArrayList(new User("tuser"));
when(userDAO.getAll()).thenReturn(users);
@@ -108,8 +125,6 @@ public class DefaultUserManagerTest extends UserManagerTestBase
@SuppressWarnings("unchecked")
public void testDefaultAccountCreation()
{
UserDAO userDAO = mock(UserDAO.class);
when(userDAO.getAll()).thenReturn(Collections.EMPTY_LIST);
UserManager userManager = new DefaultUserManager(userDAO);
@@ -118,6 +133,55 @@ public class DefaultUserManagerTest extends UserManagerTestBase
verify(userDAO, times(2)).add(any(User.class));
}
@Test(expected = InvalidPasswordException.class)
public void shouldFailChangePasswordForWrongOldPassword() {
UserManager userManager = new DefaultUserManager(userDAO);
userManager.changePasswordForLoggedInUser("wrongPassword", "---");
}
@Test
public void shouldSucceedChangePassword() {
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
doNothing().when(userDAO).modify(userCaptor.capture());
UserManager userManager = new DefaultUserManager(userDAO);
userManager.changePasswordForLoggedInUser("oldEncrypted", "newEncrypted");
Assertions.assertThat(userCaptor.getValue().getPassword()).isEqualTo("newEncrypted");
}
@Test(expected = ChangePasswordNotAllowedException.class)
public void shouldFailOverwritePasswordForWrongType() {
trillian.setType("wrongType");
UserManager userManager = new DefaultUserManager(userDAO);
userManager.overwritePassword("trillian", "---");
}
@Test(expected = NotFoundException.class)
public void shouldFailOverwritePasswordForMissingUser() {
UserManager userManager = new DefaultUserManager(userDAO);
userManager.overwritePassword("notExisting", "---");
}
@Test
public void shouldSucceedOverwritePassword() {
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
doNothing().when(userDAO).modify(userCaptor.capture());
UserManager userManager = new DefaultUserManager(userDAO);
userManager.overwritePassword("trillian", "newEncrypted");
Assertions.assertThat(userCaptor.getValue().getPassword()).isEqualTo("newEncrypted");
}
//~--- methods --------------------------------------------------------------
private XmlUserDAO createXmlUserDAO() {

View File

@@ -0,0 +1,255 @@
package sonia.scm.web.i18n;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockSettings;
import org.mockito.internal.creation.MockSettingsImpl;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.boot.RestartEvent;
import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager;
import sonia.scm.event.ScmEventBus;
import sonia.scm.plugin.PluginLoader;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.Silent.class)
@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini")
public class I18nServletTest {
@Rule
public ShiroRule shiro = new ShiroRule();
private static final String GIT_PLUGIN_JSON = json(
"{",
"'scm-git-plugin': {",
"'information': {",
"'clone' : 'Clone',",
"'create' : 'Create',",
"'replace' : 'Push'",
"}",
"}",
"}"
);
private static final String HG_PLUGIN_JSON = json(
"{",
"'scm-hg-plugin': {",
"'information': {",
"'clone' : 'Clone',",
"'create' : 'Create',",
"'replace' : 'Push'",
"}",
"}",
"}"
);
private static String SVN_PLUGIN_JSON = json(
"{",
"'scm-svn-plugin': {",
"'information': {",
"'checkout' : 'Checkout'",
"}",
"}",
"}"
);
private static String json(String... parts) {
return String.join("\n", parts ).replaceAll("'", "\"");
}
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Mock
PluginLoader pluginLoader;
@Mock
CacheManager cacheManager;
@Mock
ClassLoader classLoader;
I18nServlet servlet;
@Mock
private Cache cache;
private Enumeration<URL> resources;
@Before
@SuppressWarnings("unchecked")
public void init() throws IOException {
resources = Collections.enumeration(Lists.newArrayList(
createFileFromString(SVN_PLUGIN_JSON).toURL(),
createFileFromString(GIT_PLUGIN_JSON).toURL(),
createFileFromString(HG_PLUGIN_JSON).toURL()
));
when(pluginLoader.getUberClassLoader()).thenReturn(classLoader);
when(cacheManager.getCache(I18nServlet.CACHE_NAME)).thenReturn(cache);
MockSettings settings = new MockSettingsImpl<>();
settings.useConstructor(pluginLoader, cacheManager);
settings.defaultAnswer(InvocationOnMock::callRealMethod);
servlet = mock(I18nServlet.class, settings);
}
@Test
public void shouldCleanCacheOnRestartEvent() {
ScmEventBus.getInstance().register(servlet);
ScmEventBus.getInstance().post(new RestartEvent(I18nServlet.class, "Restart to reload the plugin resources"));
verify(cache).clear();
}
@Test
@SuppressWarnings("unchecked")
public void shouldFailWith404OnMissingResources() throws IOException {
String path = "/locales/de/plugins.json";
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
PrintWriter writer = mock(PrintWriter.class);
when(response.getWriter()).thenReturn(writer);
when(request.getServletPath()).thenReturn(path);
when(classLoader.getResources("locales/de/plugins.json")).thenThrow(IOException.class);
servlet.doGet(request, response);
verify(response).setStatus(404);
}
@Test
@SuppressWarnings("unchecked")
public void shouldFailWith500OnIOException() throws IOException {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
doThrow(IOException.class).when(response).getWriter();
servlet.doGet(request, response);
verify(response).setStatus(500);
}
@Test
@SuppressWarnings("unchecked")
public void inDevelopmentStageShouldNotUseCache() throws IOException {
String path = "/locales/de/plugins.json";
when(servlet.isProductionStage()).thenReturn(false);
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
File file = temporaryFolder.newFile();
PrintWriter writer = new PrintWriter(new FileOutputStream(file));
when(response.getWriter()).thenReturn(writer);
when(request.getServletPath()).thenReturn(path);
when(classLoader.getResources("locales/de/plugins.json")).thenReturn(resources);
servlet.doGet(request, response);
String json = Files.readLines(file, Charset.defaultCharset()).get(0);
assertJson(json);
verify(cache, never()).get(any());
}
@Test
@SuppressWarnings("unchecked")
public void inProductionStageShouldUseCache() throws IOException {
String path = "/locales/de/plugins.json";
when(servlet.isProductionStage()).thenReturn(true);
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
File file = temporaryFolder.newFile();
PrintWriter writer = new PrintWriter(new FileOutputStream(file));
when(response.getWriter()).thenReturn(writer);
when(request.getServletPath()).thenReturn(path);
when(classLoader.getResources("locales/de/plugins.json")).thenReturn(resources);
servlet.doGet(request, response);
String json = Files.readLines(file, Charset.defaultCharset()).get(0);
assertJson(json);
verify(cache).get(path);
verify(cache).put(eq(path), any());
}
@Test
@SuppressWarnings("unchecked")
public void inProductionStageShouldGetFromCache() throws IOException {
String path = "/locales/de/plugins.json";
when(servlet.isProductionStage()).thenReturn(true);
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
File file = temporaryFolder.newFile();
PrintWriter writer = new PrintWriter(new FileOutputStream(file));
when(response.getWriter()).thenReturn(writer);
when(request.getServletPath()).thenReturn(path);
when(classLoader.getResources("locales/de/plugins.json")).thenReturn(resources);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = objectMapper.readTree(GIT_PLUGIN_JSON);
node = servlet.merge(node, objectMapper.readTree(HG_PLUGIN_JSON));
node = servlet.merge(node, objectMapper.readTree(SVN_PLUGIN_JSON));
when(cache.get(path)).thenReturn(node);
servlet.doGet(request, response);
String json = Files.readLines(file, Charset.defaultCharset()).get(0);
verify(servlet, never()).collectJsonFile(path);
verify(cache, never()).put(eq(path), any());
verify(cache).get(path);
assertJson(json);
}
@Test
@SuppressWarnings("unchecked")
public void shouldCollectJsonFile() throws IOException {
String path = "locales/de/plugins.json";
when(classLoader.getResources(path)).thenReturn(resources);
Optional<JsonNode> jsonNodeOptional = servlet.collectJsonFile("/" + path);
assertJson(jsonNodeOptional.orElse(null));
}
public void assertJson(JsonNode actual) throws IOException {
assertJson(actual.toString());
}
public void assertJson(String actual) throws IOException {
assertThat(actual)
.isNotEmpty()
.contains(StringUtils.deleteWhitespace(GIT_PLUGIN_JSON.substring(1, GIT_PLUGIN_JSON.length() - 1)))
.contains(StringUtils.deleteWhitespace(HG_PLUGIN_JSON.substring(1, HG_PLUGIN_JSON.length() - 1)))
.contains(StringUtils.deleteWhitespace(SVN_PLUGIN_JSON.substring(1, SVN_PLUGIN_JSON.length() - 1)));
}
public File createFileFromString(String json) throws IOException {
File file = temporaryFolder.newFile();
Files.write(json.getBytes(Charsets.UTF_8), file);
return file;
}
}