diff --git a/scm-core/src/main/java/sonia/scm/BadRequestException.java b/scm-core/src/main/java/sonia/scm/BadRequestException.java new file mode 100644 index 0000000000..544ed75a0b --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/BadRequestException.java @@ -0,0 +1,9 @@ +package sonia.scm; + +import java.util.List; + +public abstract class BadRequestException extends ExceptionWithContext { + public BadRequestException(List context, String message) { + super(context, message); + } +} diff --git a/scm-core/src/main/java/sonia/scm/NotSupportedFeatureException.java b/scm-core/src/main/java/sonia/scm/FeatureNotSupportedException.java similarity index 88% rename from scm-core/src/main/java/sonia/scm/NotSupportedFeatureException.java rename to scm-core/src/main/java/sonia/scm/FeatureNotSupportedException.java index daf996ee6c..2d64af4318 100644 --- a/scm-core/src/main/java/sonia/scm/NotSupportedFeatureException.java +++ b/scm-core/src/main/java/sonia/scm/FeatureNotSupportedException.java @@ -40,13 +40,14 @@ import java.util.Collections; * @author Sebastian Sdorra * @version 1.6 */ -public class NotSupportedFeatureException extends ExceptionWithContext { +@SuppressWarnings("squid:MaximumInheritanceDepth") // exceptions have a deep inheritance depth themselves; therefore we accept this here +public class FeatureNotSupportedException extends BadRequestException { private static final long serialVersionUID = 256498734456613496L; private static final String CODE = "9SR8G0kmU1"; - public NotSupportedFeatureException(String feature) + public FeatureNotSupportedException(String feature) { super(Collections.emptyList(),createMessage(feature)); } diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java index a3e8a1da73..1e9cc3d374 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java @@ -38,7 +38,7 @@ package sonia.scm.repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.NotSupportedFeatureException; +import sonia.scm.FeatureNotSupportedException; import sonia.scm.SCMContextProvider; import sonia.scm.event.ScmEventBus; @@ -167,12 +167,12 @@ public abstract class AbstractRepositoryHandler * * @return * - * @throws NotSupportedFeatureException + * @throws FeatureNotSupportedException */ @Override public ImportHandler getImportHandler() { - throw new NotSupportedFeatureException("import"); + throw new FeatureNotSupportedException("import"); } /** diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java index cb19cb7f5e..aaa090827a 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java @@ -36,7 +36,7 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- import sonia.scm.Handler; -import sonia.scm.NotSupportedFeatureException; +import sonia.scm.FeatureNotSupportedException; import sonia.scm.plugin.ExtensionPoint; /** @@ -59,9 +59,9 @@ public interface RepositoryHandler * @return {@link ImportHandler} for the repository type of this handler * @since 1.12 * - * @throws NotSupportedFeatureException + * @throws FeatureNotSupportedException */ - public ImportHandler getImportHandler() throws NotSupportedFeatureException; + public ImportHandler getImportHandler() throws FeatureNotSupportedException; /** * Returns informations about the version of the RepositoryHandler. diff --git a/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java index 32b633a67c..9e7094d5bf 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java @@ -38,7 +38,7 @@ package sonia.scm.repository.api; import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.NotSupportedFeatureException; +import sonia.scm.FeatureNotSupportedException; import sonia.scm.repository.Feature; import sonia.scm.repository.spi.DiffCommand; import sonia.scm.repository.spi.DiffCommandRequest; @@ -203,7 +203,7 @@ public final class DiffCommandBuilder public DiffCommandBuilder setAncestorChangeset(String revision) { if (!supportedFeatures.contains(Feature.INCOMING_REVISION)) { - throw new NotSupportedFeatureException(Feature.INCOMING_REVISION.name()); + throw new FeatureNotSupportedException(Feature.INCOMING_REVISION.name()); } request.setAncestorChangeset(revision); diff --git a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java index 7b8e172661..917b81391f 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java @@ -39,7 +39,7 @@ import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.NotSupportedFeatureException; +import sonia.scm.FeatureNotSupportedException; import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; import sonia.scm.repository.Changeset; @@ -410,7 +410,7 @@ public final class LogCommandBuilder */ public LogCommandBuilder setAncestorChangeset(String ancestorChangeset) { if (!supportedFeatures.contains(Feature.INCOMING_REVISION)) { - throw new NotSupportedFeatureException(Feature.INCOMING_REVISION.name()); + throw new FeatureNotSupportedException(Feature.INCOMING_REVISION.name()); } request.setAncestorChangeset(ancestorChangeset); return this; diff --git a/scm-core/src/main/java/sonia/scm/security/AccessToken.java b/scm-core/src/main/java/sonia/scm/security/AccessToken.java index c2a5f4b747..ac7700b030 100644 --- a/scm-core/src/main/java/sonia/scm/security/AccessToken.java +++ b/scm-core/src/main/java/sonia/scm/security/AccessToken.java @@ -80,8 +80,20 @@ public interface AccessToken { */ Date getExpiration(); + /** + * Returns refresh expiration of token. + * + * @return refresh expiration + */ Optional getRefreshExpiration(); + /** + * Returns id of the parent key. + * + * @return parent key id + */ + Optional getParentKey(); + /** * Returns the scope of the token. The scope is able to reduce the permissions of the subject in the context of this * token. For example we could issue a token which can only be used to read a single repository. for more informations diff --git a/scm-core/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java b/scm-core/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java new file mode 100644 index 0000000000..999c693b8f --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java @@ -0,0 +1,30 @@ +package sonia.scm.security; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Generates cookies and invalidates access token cookies. + * + * @author Sebastian Sdorra + * @since 2.0.0 + */ +public interface AccessTokenCookieIssuer { + + /** + * Creates a cookie for token authentication and attaches it to the response. + * + * @param request http servlet request + * @param response http servlet response + * @param accessToken access token + */ + void authenticate(HttpServletRequest request, HttpServletResponse response, AccessToken accessToken); + /** + * Invalidates the authentication cookie. + * + * @param request http servlet request + * @param response http servlet response + */ + void invalidate(HttpServletRequest request, HttpServletResponse response); + +} diff --git a/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java b/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java index b4f0d81cd3..9c1fa590cc 100644 --- a/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java +++ b/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java @@ -164,7 +164,7 @@ public class DefaultCipherHandler implements CipherHandler { String result = null; try { - byte[] encodedInput = Base64.getDecoder().decode(value); + byte[] encodedInput = Base64.getUrlDecoder().decode(value); byte[] salt = new byte[SALT_LENGTH]; byte[] encoded = new byte[encodedInput.length - SALT_LENGTH]; @@ -221,7 +221,7 @@ public class DefaultCipherHandler implements CipherHandler { System.arraycopy(salt, 0, result, 0, SALT_LENGTH); System.arraycopy(encodedInput, 0, result, SALT_LENGTH, result.length - SALT_LENGTH); - res = new String(Base64.getEncoder().encode(result), ENCODING); + res = new String(Base64.getUrlEncoder().encode(result), ENCODING); } catch (IOException | GeneralSecurityException ex) { throw new CipherException("could not encode string", ex); } diff --git a/scm-core/src/main/java/sonia/scm/store/ConfigurationStore.java b/scm-core/src/main/java/sonia/scm/store/ConfigurationStore.java index a7f21dd304..bcdc0443ca 100644 --- a/scm-core/src/main/java/sonia/scm/store/ConfigurationStore.java +++ b/scm-core/src/main/java/sonia/scm/store/ConfigurationStore.java @@ -33,6 +33,10 @@ package sonia.scm.store; +import java.util.Optional; + +import static java.util.Optional.ofNullable; + /** * ConfigurationStore for configuration objects. Note: the default * implementation use JAXB to marshall the configuration objects. @@ -50,7 +54,17 @@ public interface ConfigurationStore * * @return configuration object from store */ - public T get(); + T get(); + + /** + * Returns the configuration object from store. + * + * + * @return configuration object from store + */ + default Optional getOptional() { + return ofNullable(get()); + } //~--- set methods ---------------------------------------------------------- @@ -60,5 +74,5 @@ public interface ConfigurationStore * * @param obejct configuration object to store */ - public void set(T obejct); + void set(T object); } diff --git a/scm-core/src/main/java/sonia/scm/store/MultiEntryStore.java b/scm-core/src/main/java/sonia/scm/store/MultiEntryStore.java index 9a35cee0e0..c1a8863758 100644 --- a/scm-core/src/main/java/sonia/scm/store/MultiEntryStore.java +++ b/scm-core/src/main/java/sonia/scm/store/MultiEntryStore.java @@ -32,6 +32,10 @@ package sonia.scm.store; +import java.util.Optional; + +import static java.util.Optional.ofNullable; + /** * Base class for {@link BlobStore} and {@link DataStore}. * @@ -67,4 +71,16 @@ public interface MultiEntryStore { * @return item with the given id */ public T get(String id); + + /** + * Returns the item with the given id from the store. + * + * + * @param id id of the item to return + * + * @return item with the given id + */ + default Optional getOptional(String id) { + return ofNullable(get(id)); + } } diff --git a/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java b/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java index caa35e0b88..b0f8117e82 100644 --- a/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java +++ b/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java @@ -1,12 +1,13 @@ package sonia.scm.user; +import sonia.scm.BadRequestException; import sonia.scm.ContextEntry; -import sonia.scm.ExceptionWithContext; -public class ChangePasswordNotAllowedException extends ExceptionWithContext { +@SuppressWarnings("squid:MaximumInheritanceDepth") // exceptions have a deep inheritance depth themselves; therefore we accept this here +public class ChangePasswordNotAllowedException extends BadRequestException { private static final String CODE = "9BR7qpDAe1"; - public static final String WRONG_USER_TYPE = "User of type %s are not allowed to change password"; + public static final String WRONG_USER_TYPE = "Users of type %s are not allowed to change password"; public ChangePasswordNotAllowedException(ContextEntry.ContextBuilder context, String type) { super(context.build(), String.format(WRONG_USER_TYPE, type)); diff --git a/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java b/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java index 93a6a7c1d1..6f1bfd9954 100644 --- a/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java +++ b/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java @@ -1,9 +1,10 @@ package sonia.scm.user; +import sonia.scm.BadRequestException; import sonia.scm.ContextEntry; -import sonia.scm.ExceptionWithContext; -public class InvalidPasswordException extends ExceptionWithContext { +@SuppressWarnings("squid:MaximumInheritanceDepth") // exceptions have a deep inheritance depth themselves; therefore we accept this here +public class InvalidPasswordException extends BadRequestException { private static final String CODE = "8YR7aawFW1"; diff --git a/scm-core/src/main/java/sonia/scm/web/AbstractRepositoryJsonEnricher.java b/scm-core/src/main/java/sonia/scm/web/AbstractRepositoryJsonEnricher.java new file mode 100644 index 0000000000..2cb4674d24 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/AbstractRepositoryJsonEnricher.java @@ -0,0 +1,40 @@ +package sonia.scm.web; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import static java.util.Collections.singletonMap; +import static sonia.scm.web.VndMediaType.REPOSITORY; +import static sonia.scm.web.VndMediaType.REPOSITORY_COLLECTION; + +public abstract class AbstractRepositoryJsonEnricher extends JsonEnricherBase { + + public AbstractRepositoryJsonEnricher(ObjectMapper objectMapper) { + super(objectMapper); + } + + @Override + public void enrich(JsonEnricherContext context) { + if (resultHasMediaType(REPOSITORY, context)) { + JsonNode repositoryNode = context.getResponseEntity(); + enrichRepositoryNode(repositoryNode); + } else if (resultHasMediaType(REPOSITORY_COLLECTION, context)) { + JsonNode repositoryCollectionNode = context.getResponseEntity().get("_embedded").withArray("repositories"); + repositoryCollectionNode.elements().forEachRemaining(this::enrichRepositoryNode); + } + } + + private void enrichRepositoryNode(JsonNode repositoryNode) { + String namespace = repositoryNode.get("namespace").asText(); + String name = repositoryNode.get("name").asText(); + + enrichRepositoryNode(repositoryNode, namespace, name); + } + + protected abstract void enrichRepositoryNode(JsonNode repositoryNode, String namespace, String name); + + protected void addLink(JsonNode repositoryNode, String linkName, String link) { + JsonNode hrefNode = createObject(singletonMap("href", value(link))); + addPropertyNode(repositoryNode.get("_links"), linkName, hrefNode); + } +} diff --git a/scm-core/src/main/java/sonia/scm/xml/XmlInstantAdapter.java b/scm-core/src/main/java/sonia/scm/xml/XmlInstantAdapter.java new file mode 100644 index 0000000000..9b8d718851 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/xml/XmlInstantAdapter.java @@ -0,0 +1,25 @@ +package sonia.scm.xml; + +import javax.xml.bind.annotation.adapters.XmlAdapter; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; + +/** + * JAXB adapter for {@link Instant} objects. + * + * @since 2.0.0 + */ +public class XmlInstantAdapter extends XmlAdapter { + + @Override + public String marshal(Instant instant) { + return DateTimeFormatter.ISO_INSTANT.format(instant); + } + + @Override + public Instant unmarshal(String text) { + TemporalAccessor parsed = DateTimeFormatter.ISO_INSTANT.parse(text); + return Instant.from(parsed); + } +} diff --git a/scm-core/src/test/java/sonia/scm/web/AbstractRepositoryJsonEnricherTest.java b/scm-core/src/test/java/sonia/scm/web/AbstractRepositoryJsonEnricherTest.java new file mode 100644 index 0000000000..2c8ef76464 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/web/AbstractRepositoryJsonEnricherTest.java @@ -0,0 +1,107 @@ +package sonia.scm.web; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.io.Resources; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.api.v2.resources.ScmPathInfoStore; + +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.net.URI; +import java.net.URL; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) +class AbstractRepositoryJsonEnricherTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private AbstractRepositoryJsonEnricher linkEnricher; + private JsonNode rootNode; + + @BeforeEach + void globalSetUp() { + ScmPathInfoStore pathInfoStore = new ScmPathInfoStore(); + pathInfoStore.set(() -> URI.create("/")); + + linkEnricher = new AbstractRepositoryJsonEnricher(objectMapper) { + @Override + protected void enrichRepositoryNode(JsonNode repositoryNode, String namespace, String name) { + addLink(repositoryNode, "new-link", "/somewhere"); + } + }; + } + + @Test + void shouldEnrichRepositories() throws IOException { + URL resource = Resources.getResource("sonia/scm/repository/repository-001.json"); + rootNode = objectMapper.readTree(resource); + + JsonEnricherContext context = new JsonEnricherContext( + URI.create("/"), + MediaType.valueOf(VndMediaType.REPOSITORY), + rootNode + ); + + linkEnricher.enrich(context); + + String configLink = context.getResponseEntity() + .get("_links") + .get("new-link") + .get("href") + .asText(); + + assertThat(configLink).isEqualTo("/somewhere"); + } + + @Test + void shouldEnrichAllRepositories() throws IOException { + URL resource = Resources.getResource("sonia/scm/repository/repository-collection-001.json"); + rootNode = objectMapper.readTree(resource); + + JsonEnricherContext context = new JsonEnricherContext( + URI.create("/"), + MediaType.valueOf(VndMediaType.REPOSITORY_COLLECTION), + rootNode + ); + + linkEnricher.enrich(context); + + context.getResponseEntity() + .get("_embedded") + .withArray("repositories") + .elements() + .forEachRemaining(node -> { + String configLink = node + .get("_links") + .get("new-link") + .get("href") + .asText(); + + assertThat(configLink).isEqualTo("/somewhere"); + }); + } + + @Test + void shouldNotModifyObjectsWithUnsupportedMediaType() throws IOException { + URL resource = Resources.getResource("sonia/scm/repository/repository-001.json"); + rootNode = objectMapper.readTree(resource); + JsonEnricherContext context = new JsonEnricherContext( + URI.create("/"), + MediaType.valueOf(VndMediaType.USER), + rootNode + ); + + linkEnricher.enrich(context); + + boolean hasNewPullRequestLink = context.getResponseEntity() + .get("_links") + .has("new-link"); + + assertThat(hasNewPullRequestLink).isFalse(); + } +} diff --git a/scm-core/src/test/java/sonia/scm/xml/XmlInstantAdapterTest.java b/scm-core/src/test/java/sonia/scm/xml/XmlInstantAdapterTest.java new file mode 100644 index 0000000000..eb1ea86aee --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/xml/XmlInstantAdapterTest.java @@ -0,0 +1,47 @@ +package sonia.scm.xml; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.TempDirectory; + +import javax.xml.bind.JAXB; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.nio.file.Path; +import java.time.Instant; + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(TempDirectory.class) +class XmlInstantAdapterTest { + + @Test + void shouldMarshalAndUnmarshalInstant(@TempDirectory.TempDir Path tempDirectory) { + Path path = tempDirectory.resolve("instant.xml"); + + Instant instant = Instant.now(); + InstantObject object = new InstantObject(instant); + JAXB.marshal(object, path.toFile()); + + InstantObject unmarshaled = JAXB.unmarshal(path.toFile(), InstantObject.class); + assertEquals(instant, unmarshaled.instant); + } + + @XmlRootElement(name = "instant-object") + @XmlAccessorType(XmlAccessType.FIELD) + public static class InstantObject { + + @XmlJavaTypeAdapter(XmlInstantAdapter.class) + private Instant instant; + + public InstantObject() { + } + + InstantObject(Instant instant) { + this.instant = instant; + } + } + +} diff --git a/scm-core/src/test/resources/sonia/scm/repository/repository-001.json b/scm-core/src/test/resources/sonia/scm/repository/repository-001.json new file mode 100644 index 0000000000..43ea136942 --- /dev/null +++ b/scm-core/src/test/resources/sonia/scm/repository/repository-001.json @@ -0,0 +1,42 @@ +{ + "creationDate": "2018-11-09T09:48:32.732Z", + "description": "Handling static webresources made easy", + "healthCheckFailures": [], + "lastModified": "2018-11-09T09:49:20.973Z", + "namespace": "scmadmin", + "name": "web-resources", + "archived": false, + "type": "git", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources" + }, + "delete": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources" + }, + "update": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources" + }, + "permissions": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/permissions/" + }, + "protocol": [ + { + "href": "http://localhost:8081/scm/repo/scmadmin/web-resources", + "name": "http" + } + ], + "tags": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/tags/" + }, + "branches": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/branches/" + }, + "changesets": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/changesets/" + }, + "sources": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/sources/" + } + } +} diff --git a/scm-core/src/test/resources/sonia/scm/repository/repository-collection-001.json b/scm-core/src/test/resources/sonia/scm/repository/repository-collection-001.json new file mode 100644 index 0000000000..f4eeb24bbc --- /dev/null +++ b/scm-core/src/test/resources/sonia/scm/repository/repository-collection-001.json @@ -0,0 +1,106 @@ +{ + "page": 0, + "pageTotal": 1, + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/repositories/?page=0&pageSize=10" + }, + "first": { + "href": "http://localhost:8081/scm/api/v2/repositories/?page=0&pageSize=10" + }, + "last": { + "href": "http://localhost:8081/scm/api/v2/repositories/?page=0&pageSize=10" + }, + "create": { + "href": "http://localhost:8081/scm/api/v2/repositories/" + } + }, + "_embedded": { + "repositories": [ + { + "creationDate": "2018-11-09T09:48:32.732Z", + "description": "Handling static webresources made easy", + "healthCheckFailures": [], + "lastModified": "2018-11-09T09:49:20.973Z", + "namespace": "scmadmin", + "name": "web-resources", + "archived": false, + "type": "git", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources" + }, + "delete": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources" + }, + "update": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources" + }, + "permissions": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/permissions/" + }, + "protocol": [ + { + "href": "http://localhost:8081/scm/repo/scmadmin/web-resources", + "name": "http" + } + ], + "tags": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/tags/" + }, + "branches": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/branches/" + }, + "changesets": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/changesets/" + }, + "sources": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/sources/" + } + } + }, + { + "creationDate": "2018-11-09T09:48:32.732Z", + "description": "Handling static webresources made easy", + "healthCheckFailures": [], + "lastModified": "2018-11-09T09:49:20.973Z", + "namespace": "scmadmin", + "name": "web-resources", + "archived": false, + "type": "git", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources" + }, + "delete": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources" + }, + "update": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources" + }, + "permissions": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/permissions/" + }, + "protocol": [ + { + "href": "http://localhost:8081/scm/repo/scmadmin/web-resources", + "name": "http" + } + ], + "tags": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/tags/" + }, + "branches": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/branches/" + }, + "changesets": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/changesets/" + }, + "sources": { + "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/sources/" + } + } + } + ] + } +} diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java index 099ab53baa..d37a150723 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java @@ -58,8 +58,6 @@ public abstract class FileBasedStoreFactory { private RepositoryLocationResolver repositoryLocationResolver; private Store store; - private File storeDirectory; - protected FileBasedStoreFactory(SCMContextProvider contextProvider , RepositoryLocationResolver repositoryLocationResolver, Store store) { this.contextProvider = contextProvider; this.repositoryLocationResolver = repositoryLocationResolver; @@ -75,17 +73,16 @@ public abstract class FileBasedStoreFactory { } protected File getStoreLocation(String name, Class type, Repository repository) { - if (storeDirectory == null) { - if (repository != null) { - LOG.debug("create store with type: {}, name: {} and repository: {}", type, name, repository.getNamespaceAndName()); - storeDirectory = this.getStoreDirectory(store, repository); - } else { - LOG.debug("create store with type: {} and name: {} ", type, name); - storeDirectory = this.getStoreDirectory(store); - } - IOUtil.mkdirs(storeDirectory); + File storeDirectory; + if (repository != null) { + LOG.debug("create store with type: {}, name: {} and repository: {}", type, name, repository.getNamespaceAndName()); + storeDirectory = this.getStoreDirectory(store, repository); + } else { + LOG.debug("create store with type: {} and name: {} ", type, name); + storeDirectory = this.getStoreDirectory(store); } - return new File(this.storeDirectory, name); + IOUtil.mkdirs(storeDirectory); + return new File(storeDirectory, name); } /** diff --git a/scm-plugins/scm-git-plugin/package.json b/scm-plugins/scm-git-plugin/package.json index b617351264..3145b6a338 100644 --- a/scm-plugins/scm-git-plugin/package.json +++ b/scm-plugins/scm-git-plugin/package.json @@ -12,6 +12,6 @@ "@scm-manager/ui-extensions": "^0.1.1" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.22" + "@scm-manager/ui-bundler": "^0.0.24" } } diff --git a/scm-plugins/scm-git-plugin/yarn.lock b/scm-plugins/scm-git-plugin/yarn.lock index 471beb513e..234ed65102 100644 --- a/scm-plugins/scm-git-plugin/yarn.lock +++ b/scm-plugins/scm-git-plugin/yarn.lock @@ -707,9 +707,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.22": - version "0.0.22" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" +"@scm-manager/ui-bundler@^0.0.24": + version "0.0.24" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-plugins/scm-hg-plugin/package.json b/scm-plugins/scm-hg-plugin/package.json index 6bccf3bd96..0638a464de 100644 --- a/scm-plugins/scm-hg-plugin/package.json +++ b/scm-plugins/scm-hg-plugin/package.json @@ -9,6 +9,6 @@ "@scm-manager/ui-extensions": "^0.1.1" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.22" + "@scm-manager/ui-bundler": "^0.0.24" } } diff --git a/scm-plugins/scm-hg-plugin/yarn.lock b/scm-plugins/scm-hg-plugin/yarn.lock index bc53e0a7ce..0666ef408d 100644 --- a/scm-plugins/scm-hg-plugin/yarn.lock +++ b/scm-plugins/scm-hg-plugin/yarn.lock @@ -641,9 +641,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.22": - version "0.0.22" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" +"@scm-manager/ui-bundler@^0.0.24": + version "0.0.24" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-plugins/scm-svn-plugin/package.json b/scm-plugins/scm-svn-plugin/package.json index 3e509172fd..e51f3b9bfd 100644 --- a/scm-plugins/scm-svn-plugin/package.json +++ b/scm-plugins/scm-svn-plugin/package.json @@ -9,6 +9,6 @@ "@scm-manager/ui-extensions": "^0.1.1" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.22" + "@scm-manager/ui-bundler": "^0.0.24" } } diff --git a/scm-plugins/scm-svn-plugin/yarn.lock b/scm-plugins/scm-svn-plugin/yarn.lock index bc53e0a7ce..0666ef408d 100644 --- a/scm-plugins/scm-svn-plugin/yarn.lock +++ b/scm-plugins/scm-svn-plugin/yarn.lock @@ -641,9 +641,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.22": - version "0.0.22" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" +"@scm-manager/ui-bundler@^0.0.24": + version "0.0.24" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationStoreFactory.java b/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationStoreFactory.java index 2c5641bfd1..2180afdca2 100644 --- a/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationStoreFactory.java +++ b/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationStoreFactory.java @@ -42,8 +42,20 @@ package sonia.scm.store; */ public class InMemoryConfigurationStoreFactory implements ConfigurationStoreFactory { + private ConfigurationStore store; + + public InMemoryConfigurationStoreFactory() { + } + + public InMemoryConfigurationStoreFactory(ConfigurationStore store) { + this.store = store; + } + @Override public ConfigurationStore getStore(TypedStoreParameters storeParameters) { + if (store != null) { + return store; + } return new InMemoryConfigurationStore<>(); } } diff --git a/scm-test/src/main/java/sonia/scm/store/InMemoryDataStore.java b/scm-test/src/main/java/sonia/scm/store/InMemoryDataStore.java new file mode 100644 index 0000000000..06198d89bf --- /dev/null +++ b/scm-test/src/main/java/sonia/scm/store/InMemoryDataStore.java @@ -0,0 +1,53 @@ +package sonia.scm.store; + +import sonia.scm.security.KeyGenerator; +import sonia.scm.security.UUIDKeyGenerator; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * In memory store implementation of {@link DataStore}. + * + * @author Sebastian Sdorra + * + * @param type of stored object + */ +public class InMemoryDataStore implements DataStore { + + private final Map store = new HashMap<>(); + private KeyGenerator generator = new UUIDKeyGenerator(); + + @Override + public String put(T item) { + String key = generator.createKey(); + store.put(key, item); + return key; + } + + @Override + public void put(String id, T item) { + store.put(id, item); + } + + @Override + public Map getAll() { + return Collections.unmodifiableMap(store); + } + + @Override + public void clear() { + store.clear(); + } + + @Override + public void remove(String id) { + store.remove(id); + } + + @Override + public T get(String id) { + return store.get(id); + } +} diff --git a/scm-test/src/main/java/sonia/scm/store/InMemoryDataStoreFactory.java b/scm-test/src/main/java/sonia/scm/store/InMemoryDataStoreFactory.java new file mode 100644 index 0000000000..b0e95e9f9c --- /dev/null +++ b/scm-test/src/main/java/sonia/scm/store/InMemoryDataStoreFactory.java @@ -0,0 +1,26 @@ +package sonia.scm.store; + +/** + * In memory configuration store factory for testing purposes. + * + * @author Sebastian Sdorra + */ +public class InMemoryDataStoreFactory implements DataStoreFactory { + + private InMemoryDataStore store; + + public InMemoryDataStoreFactory() { + } + + public InMemoryDataStoreFactory(InMemoryDataStore store) { + this.store = store; + } + + @Override + public DataStore getStore(TypedStoreParameters storeParameters) { + if (store != null) { + return store; + } + return new InMemoryDataStore<>(); + } +} diff --git a/scm-ui-components/packages/ui-components/package.json b/scm-ui-components/packages/ui-components/package.json index 880d8f3891..06e007e871 100644 --- a/scm-ui-components/packages/ui-components/package.json +++ b/scm-ui-components/packages/ui-components/package.json @@ -14,7 +14,7 @@ "eslint-fix": "eslint src --fix" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.22", + "@scm-manager/ui-bundler": "^0.0.24", "create-index": "^2.3.0", "enzyme": "^3.5.0", "enzyme-adapter-react-16": "^1.3.1", @@ -35,7 +35,8 @@ "react-i18next": "^7.11.0", "react-jss": "^8.6.1", "react-router-dom": "^4.3.1", - "react-select": "^2.1.2" + "react-select": "^2.1.2", + "diff2html": "^2.5.0" }, "browserify": { "transform": [ diff --git a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js index 960fe7db21..1b2b37bb19 100644 --- a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js +++ b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js @@ -63,8 +63,9 @@ class ConfigurationBinder { // route for global configuration, passes the current repository to component - const RepoRoute = ({ url, repository }) => { - return this.route(url + to, ); + const RepoRoute = ({url, repository}) => { + const link = repository._links[linkName].href + return this.route(url + to, ); }; // bind config route to extension point diff --git a/scm-ui-components/packages/ui-components/src/repos/Diff.js b/scm-ui-components/packages/ui-components/src/repos/Diff.js new file mode 100644 index 0000000000..0b0b31a3d4 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/Diff.js @@ -0,0 +1,36 @@ +//@flow +import React from "react"; +import { Diff2Html } from "diff2html"; + +type Props = { + diff: string, + sideBySide: boolean +}; + +class Diff extends React.Component { + + static defaultProps = { + sideBySide: false + }; + + render() { + const { diff, sideBySide } = this.props; + + const options = { + inputFormat: "diff", + outputFormat: sideBySide ? "side-by-side" : "line-by-line", + showFiles: false, + matching: "lines" + }; + + const outputHtml = Diff2Html.getPrettyHtml(diff, options); + + return ( + // eslint-disable-next-line react/no-danger +
+ ); + } + +} + +export default Diff; diff --git a/scm-ui-components/packages/ui-components/src/repos/LoadingDiff.js b/scm-ui-components/packages/ui-components/src/repos/LoadingDiff.js new file mode 100644 index 0000000000..5f6330f0e5 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/LoadingDiff.js @@ -0,0 +1,64 @@ +//@flow +import React from "react"; +import { apiClient } from "../apiclient"; +import ErrorNotification from "../ErrorNotification"; +import Loading from "../Loading"; +import Diff from "./Diff"; + +type Props = { + url: string, + sideBySide: boolean +}; + +type State = { + diff?: string, + loading: boolean, + error?: Error +}; + +class LoadingDiff extends React.Component { + + static defaultProps = { + sideBySide: false + }; + + constructor(props: Props) { + super(props); + this.state = { + loading: true + }; + } + + componentDidMount() { + const { url } = this.props; + apiClient + .get(url) + .then(response => response.text()) + .then(text => { + this.setState({ + loading: false, + diff: text + }); + }) + .catch(error => { + this.setState({ + loading: false, + error + }); + }); + } + + render() { + const { diff, loading, error } = this.state; + if (error) { + return ; + } else if (loading || !diff) { + return ; + } else { + return ; + } + } + +} + +export default LoadingDiff; diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js new file mode 100644 index 0000000000..857ff8c827 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js @@ -0,0 +1,37 @@ +//@flow +import React from "react"; +import type { Changeset } from "@scm-manager/ui-types"; +import LoadingDiff from "../LoadingDiff"; +import Notification from "../../Notification"; +import {translate} from "react-i18next"; + +type Props = { + changeset: Changeset, + + // context props + t: string => string +}; + +class ChangesetDiff extends React.Component { + + isDiffSupported(changeset: Changeset) { + return !!changeset._links.diff; + } + + createUrl(changeset: Changeset) { + return changeset._links.diff.href + "?format=GIT"; + } + + render() { + const { changeset, t } = this.props; + if (!this.isDiffSupported(changeset)) { + return {t("changesets.diff.not-supported")}; + } else { + const url = this.createUrl(changeset); + return ; + } + } + +} + +export default translate("repos")(ChangesetDiff); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js index 9651978b35..0e7a5e533d 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js @@ -7,3 +7,4 @@ export { default as ChangesetId } from "./ChangesetId"; export { default as ChangesetList } from "./ChangesetList"; export { default as ChangesetRow } from "./ChangesetRow"; export { default as ChangesetTag } from "./ChangesetTag"; +export { default as ChangesetDiff } from "./ChangesetDiff"; diff --git a/scm-ui-components/packages/ui-components/src/repos/index.js b/scm-ui-components/packages/ui-components/src/repos/index.js index 05ae15d10d..9ebd1e9c55 100644 --- a/scm-ui-components/packages/ui-components/src/repos/index.js +++ b/scm-ui-components/packages/ui-components/src/repos/index.js @@ -1,3 +1,5 @@ // @flow export * from "./changesets"; +export { default as Diff } from "./Diff"; +export { default as LoadingDiff } from "./LoadingDiff"; diff --git a/scm-ui-components/packages/ui-components/yarn.lock b/scm-ui-components/packages/ui-components/yarn.lock index 743e46f123..062bb75ab1 100644 --- a/scm-ui-components/packages/ui-components/yarn.lock +++ b/scm-ui-components/packages/ui-components/yarn.lock @@ -687,9 +687,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.22": - version "0.0.22" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" +"@scm-manager/ui-bundler@^0.0.24": + version "0.0.24" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" @@ -2444,7 +2444,16 @@ dev-ip@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0" -diff@^3.2.0: +diff2html@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/diff2html/-/diff2html-2.5.0.tgz#2d16f1a8f115354733b16b0264a594fa7db98aa2" + dependencies: + diff "^3.5.0" + hogan.js "^3.0.2" + lodash "^4.17.11" + whatwg-fetch "^3.0.0" + +diff@^3.2.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -3858,6 +3867,13 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" +hogan.js@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" + dependencies: + mkdirp "0.3.0" + nopt "1.0.10" + hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0: version "2.5.5" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" @@ -5258,7 +5274,7 @@ lodash.templatesettings@^3.0.0: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" -lodash@^4.13.1, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5: +lodash@^4.13.1, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -5518,6 +5534,10 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mkdirp@0.3.0: + version "0.3.0" + resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.1" resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -5696,6 +5716,12 @@ nomnom@~1.6.2: colors "0.5.x" underscore "~1.4.4" +nopt@1.0.10, nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + dependencies: + abbrev "1" + nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -5703,12 +5729,6 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" -nopt@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - dependencies: - abbrev "1" - normalize-package-data@^2.3.2: version "2.4.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" @@ -8040,6 +8060,10 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: dependencies: iconv-lite "0.4.24" +whatwg-fetch@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" + whatwg-mimetype@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz#a3d58ef10b76009b042d03e25591ece89b88d171" diff --git a/scm-ui-components/packages/ui-types/package.json b/scm-ui-components/packages/ui-types/package.json index 8a009d314c..4d87265379 100644 --- a/scm-ui-components/packages/ui-types/package.json +++ b/scm-ui-components/packages/ui-types/package.json @@ -14,7 +14,7 @@ "check": "flow check" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.22" + "@scm-manager/ui-bundler": "^0.0.24" }, "browserify": { "transform": [ diff --git a/scm-ui-components/packages/ui-types/yarn.lock b/scm-ui-components/packages/ui-types/yarn.lock index a19d99dfbf..ee367343ee 100644 --- a/scm-ui-components/packages/ui-types/yarn.lock +++ b/scm-ui-components/packages/ui-types/yarn.lock @@ -707,9 +707,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.22": - version "0.0.22" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" +"@scm-manager/ui-bundler@^0.0.24": + version "0.0.24" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" diff --git a/scm-ui/package.json b/scm-ui/package.json index 6fd0febd9c..2f68b75317 100644 --- a/scm-ui/package.json +++ b/scm-ui/package.json @@ -11,7 +11,7 @@ "bulma": "^0.7.1", "bulma-tooltip": "^2.0.2", "classnames": "^2.2.5", - "diff2html": "^2.4.0", + "diff2html": "^2.5.0", "font-awesome": "^4.7.0", "history": "^4.7.2", "i18next": "^11.4.0", @@ -21,7 +21,6 @@ "node-sass": "^4.9.3", "postcss-easy-import": "^3.0.0", "react": "^16.4.2", - "react-diff-view": "^1.7.0", "react-dom": "^16.4.2", "react-i18next": "^7.9.0", "react-jss": "^8.6.0", @@ -52,7 +51,7 @@ "pre-commit": "jest && flow && eslint src" }, "devDependencies": { - "@scm-manager/ui-bundler": "^0.0.22", + "@scm-manager/ui-bundler": "^0.0.24", "concat": "^1.0.3", "copyfiles": "^2.0.0", "enzyme": "^3.3.0", diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index 8296b46907..d6cdaa1d8d 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -66,6 +66,9 @@ } }, "changesets": { + "diff": { + "not-supported": "Diff of changesets is not supported by the type of repository" + }, "error-title": "Error", "error-subtitle": "Could not fetch changesets", "changeset": { diff --git a/scm-ui/src/repos/components/changesets/ChangesetDetails.js b/scm-ui/src/repos/components/changesets/ChangesetDetails.js index 2f6c4b410e..483042a779 100644 --- a/scm-ui/src/repos/components/changesets/ChangesetDetails.js +++ b/scm-ui/src/repos/components/changesets/ChangesetDetails.js @@ -9,14 +9,14 @@ import { ChangesetId, ChangesetTag, ChangesetAuthor, + ChangesetDiff, AvatarWrapper, AvatarImage, - changesets + changesets, } from "@scm-manager/ui-components"; import classNames from "classnames"; import type { Tag } from "@scm-manager/ui-types"; -import ScmDiff from "../../containers/ScmDiff"; const styles = { spacing: { @@ -78,7 +78,7 @@ class ChangesetDetails extends React.Component {

- +
); diff --git a/scm-ui/src/repos/containers/BranchSelector.js b/scm-ui/src/repos/containers/BranchSelector.js index 2183e13b69..ced1cb8f44 100644 --- a/scm-ui/src/repos/containers/BranchSelector.js +++ b/scm-ui/src/repos/containers/BranchSelector.js @@ -11,6 +11,9 @@ import classNames from "classnames"; const styles = { zeroflex: { flexGrow: 0 + }, + minWidthOfLabel: { + minWidth: "4.5rem" } }; @@ -45,7 +48,12 @@ class BranchSelector extends React.Component { return (
diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index 81de0296fc..a3f69fe70b 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -114,7 +114,7 @@ class RepositoryRoot extends React.Component { return (
-
+
{ - constructor(props: Props) { - super(props); - this.state = { diff: "" }; - } - - componentDidMount() { - const { changeset } = this.props; - const url = changeset._links.diff.href+"?format=GIT"; - apiClient - .get(url) - .then(response => response.text()) - .then(text => this.setState({ ...this.state, diff: text })) - .catch(error => this.setState({ ...this.state, error })); - } - - render() { - const options = { - inputFormat: "diff", - outputFormat: this.props.sideBySide ? "side-by-side" : "line-by-line", - showFiles: false, - matching: "lines" - }; - - const outputHtml = Diff2Html.getPrettyHtml(this.state.diff, options); - - return ( - // eslint-disable-next-line react/no-danger -
- ); - } -} - -export default ScmDiff; diff --git a/scm-ui/src/repos/sources/components/FileTree.js b/scm-ui/src/repos/sources/components/FileTree.js index 1dd11870ae..cbe03df62f 100644 --- a/scm-ui/src/repos/sources/components/FileTree.js +++ b/scm-ui/src/repos/sources/components/FileTree.js @@ -119,7 +119,9 @@ class FileTree extends React.Component { {t("sources.file-tree.lastModified")} - {t("sources.file-tree.description")} + + {t("sources.file-tree.description")} + diff --git a/scm-ui/src/repos/sources/components/FileTreeLeaf.js b/scm-ui/src/repos/sources/components/FileTreeLeaf.js index b4e2ad59ea..20905a8354 100644 --- a/scm-ui/src/repos/sources/components/FileTreeLeaf.js +++ b/scm-ui/src/repos/sources/components/FileTreeLeaf.js @@ -6,10 +6,14 @@ import FileSize from "./FileSize"; import FileIcon from "./FileIcon"; import { Link } from "react-router-dom"; import type { File } from "@scm-manager/ui-types"; +import classNames from "classnames"; const styles = { iconColumn: { width: "16px" + }, + wordBreakMinWidth: { + minWidth: "10em" } }; @@ -71,12 +75,14 @@ class FileTreeLeaf extends React.Component { return ( {this.createFileIcon(file)} - {this.createFileName(file)} + {this.createFileName(file)} {fileSize} - {file.description} + + {file.description} + ); } diff --git a/scm-ui/src/repos/sources/containers/Content.js b/scm-ui/src/repos/sources/containers/Content.js index 34d024b2a2..4565746999 100644 --- a/scm-ui/src/repos/sources/containers/Content.js +++ b/scm-ui/src/repos/sources/containers/Content.js @@ -41,6 +41,13 @@ const styles = { isVerticalCenter: { display: "flex", alignItems: "center" + }, + wordBreak: { + WebkitHyphens: "auto", + MozHyphens: "auto", + MsHyphens: "auto", + hypens: "auto", + wordBreak: "break-all", } }; @@ -93,7 +100,7 @@ class Content extends React.Component { classes.marginInHeader )} /> - {file.name} + {file.name}
{selector}
@@ -125,11 +132,11 @@ class Content extends React.Component { {t("sources.content.path")} - {file.path} + {file.path} {t("sources.content.branch")} - {revision} + {revision} {t("sources.content.size")} @@ -141,7 +148,7 @@ class Content extends React.Component { {t("sources.content.description")} - {description} + {description} diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index 4d306df6ad..8937ae3068 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -27,6 +27,14 @@ $blue: #33B2E8; padding: 0 0 0 3.8em !important; } +.is-word-break { + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + word-break: break-all; +} + .main { min-height: calc(100vh - 260px); } diff --git a/scm-ui/yarn.lock b/scm-ui/yarn.lock index 6aeb0f7703..3ddf27be96 100644 --- a/scm-ui/yarn.lock +++ b/scm-ui/yarn.lock @@ -698,9 +698,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" -"@scm-manager/ui-bundler@^0.0.22": - version "0.0.22" - resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" +"@scm-manager/ui-bundler@^0.0.24": + version "0.0.24" + resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e" dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" @@ -1925,7 +1925,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.2.5, classnames@^2.2.6: +classnames@^2.2.5: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" @@ -2549,14 +2549,14 @@ dev-ip@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0" -diff2html@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/diff2html/-/diff2html-2.4.0.tgz#de632384eefa5a7f6b0e92eafb1fa25d22dc88ab" +diff2html@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/diff2html/-/diff2html-2.5.0.tgz#2d16f1a8f115354733b16b0264a594fa7db98aa2" dependencies: diff "^3.5.0" hogan.js "^3.0.2" - lodash "^4.17.10" - whatwg-fetch "^2.0.4" + lodash "^4.17.11" + whatwg-fetch "^3.0.0" diff@^3.2.0, diff@^3.5.0: version "3.5.0" @@ -3604,10 +3604,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -gitdiff-parser@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/gitdiff-parser/-/gitdiff-parser-0.1.2.tgz#26a256e05e9c2d5016b512a96c1dacb40862b92a" - glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -5481,10 +5477,6 @@ lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" -lodash.findlastindex@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.findlastindex/-/lodash.findlastindex-4.6.0.tgz#b8375ac0f02e9b926375cdf8dc3ea814abf9c6ac" - lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" @@ -5517,10 +5509,6 @@ lodash.keys@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash.mapvalues@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" - lodash.memoize@~3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" @@ -5558,7 +5546,7 @@ lodash.templatesettings@^3.0.0: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10: +lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -5861,7 +5849,7 @@ mixin-deep@^1.2.0: mkdirp@0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.1" @@ -6919,18 +6907,6 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-diff-view@^1.7.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/react-diff-view/-/react-diff-view-1.8.1.tgz#0b9b4adcb92de6730d28177d68654dfcc2097f73" - dependencies: - classnames "^2.2.6" - gitdiff-parser "^0.1.2" - leven "^2.1.0" - lodash.escape "^4.0.1" - lodash.findlastindex "^4.6.0" - lodash.mapvalues "^4.6.0" - warning "^4.0.1" - react-dom@^16.4.2: version "16.5.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7" @@ -8730,10 +8706,6 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: dependencies: iconv-lite "0.4.24" -whatwg-fetch@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" - whatwg-fetch@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 9555ad66b5..d7846dbac5 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -79,14 +79,14 @@ import sonia.scm.repository.spi.HookEventFacade; import sonia.scm.repository.xml.XmlRepositoryDAO; import sonia.scm.schedule.QuartzScheduler; import sonia.scm.schedule.Scheduler; +import sonia.scm.security.AccessTokenCookieIssuer; import sonia.scm.security.AuthorizationChangedEventProducer; import sonia.scm.security.CipherHandler; import sonia.scm.security.CipherUtil; import sonia.scm.security.ConfigurableLoginAttemptHandler; -import sonia.scm.security.DefaultJwtAccessTokenRefreshStrategy; +import sonia.scm.security.DefaultAccessTokenCookieIssuer; import sonia.scm.security.DefaultKeyGenerator; import sonia.scm.security.DefaultSecuritySystem; -import sonia.scm.security.JwtAccessTokenRefreshStrategy; import sonia.scm.security.KeyGenerator; import sonia.scm.security.LoginAttemptHandler; import sonia.scm.security.SecuritySystem; @@ -320,6 +320,7 @@ public class ScmServletModule extends ServletModule // bind events // bind(LastModifiedUpdateListener.class); + bind(AccessTokenCookieIssuer.class).to(DefaultAccessTokenCookieIssuer.class); bind(PushStateDispatcher.class).toProvider(PushStateDispatcherProvider.class); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/BadRequestExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/BadRequestExceptionMapper.java new file mode 100644 index 0000000000..e529bc7c1a --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/BadRequestExceptionMapper.java @@ -0,0 +1,16 @@ +package sonia.scm.api.rest; + +import sonia.scm.BadRequestException; +import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper; + +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class BadRequestExceptionMapper extends ContextualExceptionMapper { + @Inject + public BadRequestExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { + super(BadRequestException.class, Response.Status.BAD_REQUEST, mapper); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java index 6bf6c8e803..64b20fc10c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java @@ -46,7 +46,7 @@ import org.apache.shiro.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.NotFoundException; -import sonia.scm.NotSupportedFeatureException; +import sonia.scm.FeatureNotSupportedException; import sonia.scm.Type; import sonia.scm.api.rest.RestActionUploadResult; import sonia.scm.api.v2.resources.RepositoryResource; @@ -394,7 +394,7 @@ public class RepositoryImportResource response = Response.ok(result).build(); } - catch (NotSupportedFeatureException ex) + catch (FeatureNotSupportedException ex) { logger .warn( @@ -609,7 +609,7 @@ public class RepositoryImportResource types.add(t); } } - catch (NotSupportedFeatureException ex) + catch (FeatureNotSupportedException ex) { if (logger.isTraceEnabled()) { @@ -711,7 +711,7 @@ public class RepositoryImportResource } } } - catch (NotSupportedFeatureException ex) + catch (FeatureNotSupportedException ex) { throw new WebApplicationException(ex, Response.Status.BAD_REQUEST); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/NotSupportedFeatureExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/NotSupportedFeatureExceptionMapper.java deleted file mode 100644 index 6a48663aa5..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/NotSupportedFeatureExceptionMapper.java +++ /dev/null @@ -1,17 +0,0 @@ -package sonia.scm.api.v2; - -import sonia.scm.NotSupportedFeatureException; -import sonia.scm.api.rest.ContextualExceptionMapper; -import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper; - -import javax.inject.Inject; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.Provider; - -@Provider -public class NotSupportedFeatureExceptionMapper extends ContextualExceptionMapper { - @Inject - public NotSupportedFeatureExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { - super(NotSupportedFeatureException.class, Response.Status.BAD_REQUEST, mapper); - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java deleted file mode 100644 index 18a6e6e75c..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java +++ /dev/null @@ -1,17 +0,0 @@ -package sonia.scm.api.v2.resources; - -import sonia.scm.api.rest.ContextualExceptionMapper; -import sonia.scm.user.ChangePasswordNotAllowedException; -import sonia.scm.user.InvalidPasswordException; - -import javax.inject.Inject; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.Provider; - -@Provider -public class ChangePasswordNotAllowedExceptionMapper extends ContextualExceptionMapper { - @Inject - public ChangePasswordNotAllowedExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { - super(ChangePasswordNotAllowedException.class, Response.Status.BAD_REQUEST, mapper); - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java deleted file mode 100644 index 7a1d311a1c..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java +++ /dev/null @@ -1,17 +0,0 @@ -package sonia.scm.api.v2.resources; - -import sonia.scm.api.rest.ContextualExceptionMapper; -import sonia.scm.user.InvalidPasswordException; - -import javax.inject.Inject; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.Provider; - -@Provider -public class InvalidPasswordExceptionMapper extends ContextualExceptionMapper { - - @Inject - public InvalidPasswordExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { - super(InvalidPasswordException.class, Response.Status.BAD_REQUEST, mapper); - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java b/scm-webapp/src/main/java/sonia/scm/security/DefaultAccessTokenCookieIssuer.java similarity index 93% rename from scm-webapp/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java rename to scm-webapp/src/main/java/sonia/scm/security/DefaultAccessTokenCookieIssuer.java index bb1473dca6..fd3f0e0d6f 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java +++ b/scm-webapp/src/main/java/sonia/scm/security/DefaultAccessTokenCookieIssuer.java @@ -51,12 +51,12 @@ import java.util.concurrent.TimeUnit; * @author Sebastian Sdorra * @since 2.0.0 */ -public final class AccessTokenCookieIssuer { +public final class DefaultAccessTokenCookieIssuer implements AccessTokenCookieIssuer { /** - * the logger for AccessTokenCookieIssuer + * the logger for DefaultAccessTokenCookieIssuer */ - private static final Logger LOG = LoggerFactory.getLogger(AccessTokenCookieIssuer.class); + private static final Logger LOG = LoggerFactory.getLogger(DefaultAccessTokenCookieIssuer.class); private final ScmConfiguration configuration; @@ -66,7 +66,7 @@ public final class AccessTokenCookieIssuer { * @param configuration scm main configuration */ @Inject - public AccessTokenCookieIssuer(ScmConfiguration configuration) { + public DefaultAccessTokenCookieIssuer(ScmConfiguration configuration) { this.configuration = configuration; } diff --git a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java index 8fb5929188..4418cb40a8 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java +++ b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java @@ -87,6 +87,7 @@ public final class JwtAccessToken implements AccessToken { return ofNullable(claims.get(REFRESHABLE_UNTIL_CLAIM_KEY, Date.class)); } + @Override public Optional getParentKey() { return ofNullable(claims.get(PARENT_TOKEN_ID_CLAIM_KEY).toString()); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AuthenticationResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AuthenticationResourceTest.java index 42428f9f77..1123dc94ce 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AuthenticationResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AuthenticationResourceTest.java @@ -18,6 +18,7 @@ import sonia.scm.security.AccessToken; import sonia.scm.security.AccessTokenBuilder; import sonia.scm.security.AccessTokenBuilderFactory; import sonia.scm.security.AccessTokenCookieIssuer; +import sonia.scm.security.DefaultAccessTokenCookieIssuer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -46,7 +47,7 @@ public class AuthenticationResourceTest { @Mock private AccessTokenBuilder accessTokenBuilder; - private AccessTokenCookieIssuer cookieIssuer = new AccessTokenCookieIssuer(mock(ScmConfiguration.class)); + private AccessTokenCookieIssuer cookieIssuer = new DefaultAccessTokenCookieIssuer(mock(ScmConfiguration.class)); private static final String AUTH_JSON_TRILLIAN = "{\n" + "\t\"cookie\": true,\n" + diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java index 9638a8aa49..fe205e88a1 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java @@ -4,9 +4,9 @@ import org.jboss.resteasy.core.Dispatcher; import org.jboss.resteasy.mock.MockDispatcherFactory; import sonia.scm.api.rest.AlreadyExistsExceptionMapper; import sonia.scm.api.rest.AuthorizationExceptionMapper; +import sonia.scm.api.rest.BadRequestExceptionMapper; import sonia.scm.api.rest.ConcurrentModificationExceptionMapper; import sonia.scm.api.v2.NotFoundExceptionMapper; -import sonia.scm.api.v2.NotSupportedFeatureExceptionMapper; public class DispatcherMock { public static Dispatcher createDispatcher(Object resource) { @@ -18,9 +18,7 @@ public class DispatcherMock { dispatcher.getProviderFactory().register(new ConcurrentModificationExceptionMapper(mapper)); dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class); dispatcher.getProviderFactory().register(new InternalRepositoryExceptionMapper(mapper)); - dispatcher.getProviderFactory().register(new ChangePasswordNotAllowedExceptionMapper(mapper)); - dispatcher.getProviderFactory().register(new InvalidPasswordExceptionMapper(mapper)); - dispatcher.getProviderFactory().register(new NotSupportedFeatureExceptionMapper(mapper)); + dispatcher.getProviderFactory().register(new BadRequestExceptionMapper(mapper)); return dispatcher; } } diff --git a/scm-webapp/src/test/java/sonia/scm/security/AccessTokenCookieIssuerTest.java b/scm-webapp/src/test/java/sonia/scm/security/DefaultAccessTokenCookieIssuerTest.java similarity index 93% rename from scm-webapp/src/test/java/sonia/scm/security/AccessTokenCookieIssuerTest.java rename to scm-webapp/src/test/java/sonia/scm/security/DefaultAccessTokenCookieIssuerTest.java index 03cf174226..9c80cfc67b 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/AccessTokenCookieIssuerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/DefaultAccessTokenCookieIssuerTest.java @@ -20,11 +20,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) -public class AccessTokenCookieIssuerTest { +public class DefaultAccessTokenCookieIssuerTest { private ScmConfiguration configuration; - private AccessTokenCookieIssuer issuer; + private DefaultAccessTokenCookieIssuer issuer; @Mock private HttpServletRequest request; @@ -41,7 +41,7 @@ public class AccessTokenCookieIssuerTest { @Before public void setUp() { configuration = new ScmConfiguration(); - issuer = new AccessTokenCookieIssuer(configuration); + issuer = new DefaultAccessTokenCookieIssuer(configuration); } @Test