Merge branch 'develop' into feature/import_git_from_url

This commit is contained in:
Eduard Heimbuch
2020-12-01 12:18:13 +01:00
84 changed files with 2721 additions and 3009 deletions

View File

@@ -29,15 +29,12 @@ import de.otto.edison.hal.Links;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
import org.mapstruct.ObjectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.Branch;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.Contributor;
import sonia.scm.repository.Person;
import sonia.scm.repository.Repository;
import sonia.scm.repository.Signature;
import sonia.scm.repository.Tags;
import sonia.scm.repository.api.Command;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
@@ -47,7 +44,6 @@ import sonia.scm.security.gpg.RawGpgKey;
import sonia.scm.web.EdisonHalAppender;
import javax.inject.Inject;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
@@ -60,8 +56,6 @@ import static de.otto.edison.hal.Links.linkingTo;
@Mapper
public abstract class DefaultChangesetToChangesetDtoMapper extends HalAppenderMapper implements InstantAttributeMapper, ChangesetToChangesetDtoMapper {
private static Logger LOG = LoggerFactory.getLogger(DefaultChangesetToChangesetDtoMapper.class);
@Inject
private RepositoryServiceFactory serviceFactory;
@@ -120,22 +114,12 @@ public abstract class DefaultChangesetToChangesetDtoMapper extends HalAppenderMa
try (RepositoryService repositoryService = serviceFactory.create(repository)) {
if (repositoryService.isSupported(Command.TAGS)) {
Tags tags = null;
try {
tags = repositoryService.getTagsCommand().getTags();
} catch (IOException e) {
LOG.error("Error while retrieving tags from repository", e);
}
if (tags != null) {
embeddedBuilder.with("tags", tagCollectionToDtoMapper.getTagDtoList(namespace, name,
getListOfObjects(source.getTags(), tags::getTagByName)));
}
embeddedBuilder.with("tags", tagCollectionToDtoMapper.getMinimalEmbeddedTagDtoList(namespace, name, source.getTags()));
}
if (repositoryService.isSupported(Command.BRANCHES)) {
embeddedBuilder.with("branches", branchCollectionToDtoMapper.getBranchDtoList(repository,
getListOfObjects(source.getBranches(), branchName -> Branch.normalBranch(branchName, source.getId()))));
}
if (repositoryService.isSupported(Command.DIFF_RESULT)) {
linksBuilder.single(link("diffParsed", resourceLinks.diff().parsed(namespace, name, source.getId())));
}

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.api.v2.resources;
import com.google.inject.Inject;
@@ -58,6 +58,17 @@ public class TagCollectionToDtoMapper {
return tags.stream().map(tag -> tagToTagDtoMapper.map(tag, new NamespaceAndName(namespace, name))).collect(toList());
}
public List<TagDto> getMinimalEmbeddedTagDtoList(String namespace, String name, Collection<String> tags) {
return tags.stream()
.map(tag -> {
Links links = linkingTo().self(resourceLinks.tag().self(namespace, name, tag)).build();
TagDto dto = new TagDto(links);
dto.setName(tag);
return dto;
})
.collect(toList());
}
private Links createLinks(String namespace, String name) {
return
linkingTo()

View File

@@ -46,6 +46,10 @@ public class TagDto extends HalRepresentation {
@JsonInclude(JsonInclude.Include.NON_NULL)
private Instant date;
TagDto(Links links) {
super(links);
}
TagDto(Links links, Embedded embedded) {
super(links, embedded);
}

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.filter;
//~--- non-JDK imports --------------------------------------------------------
@@ -34,6 +34,7 @@ import org.apache.shiro.subject.Subject;
import org.slf4j.MDC;
import sonia.scm.SCMContext;
import sonia.scm.TransactionId;
import sonia.scm.security.DefaultKeyGenerator;
import sonia.scm.web.filter.HttpFilter;
@@ -72,9 +73,6 @@ public class MDCFilter extends HttpFilter
@VisibleForTesting
static final String MDC_USERNAME = "username";
@VisibleForTesting
static final String MDC_TRANSACTION_ID = "transaction_id";
//~--- methods --------------------------------------------------------------
/**
@@ -98,7 +96,7 @@ public class MDCFilter extends HttpFilter
MDC.put(MDC_CLIENT_HOST, request.getRemoteHost());
MDC.put(MDC_REQUEST_METHOD, request.getMethod());
MDC.put(MDC_REQUEST_URI, request.getRequestURI());
MDC.put(MDC_TRANSACTION_ID, TRANSACTION_KEY_GENERATOR.createKey());
TransactionId.set(TRANSACTION_KEY_GENERATOR.createKey());
try
{
@@ -111,7 +109,7 @@ public class MDCFilter extends HttpFilter
MDC.remove(MDC_CLIENT_HOST);
MDC.remove(MDC_REQUEST_METHOD);
MDC.remove(MDC_REQUEST_URI);
MDC.remove(MDC_TRANSACTION_ID);
TransactionId.clear();
}
}

View File

@@ -36,6 +36,9 @@ import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.crypto.hash.DefaultHashService;
import org.apache.shiro.guice.web.ShiroWebModule;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SubjectDAO;
import org.apache.shiro.realm.Realm;
import org.slf4j.Logger;
@@ -114,14 +117,24 @@ public class ScmSecurityModule extends ShiroWebModule
}
// bind constant
bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to(
"/index.html");
bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to("/index.html");
// disable access to mustache resources
addFilterChain("/**.mustache", filterConfig(ROLES, "nobody"));
// disable session
disableSession();
}
private void disableSession() {
addFilterChain("/**", NO_SESSION_CREATION);
bindConstant().annotatedWith(Names.named("shiro.sessionStorageEnabled")).to(false);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();
sessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);
bind(SubjectDAO.class).toInstance(subjectDAO);
}
/**

View File

@@ -24,12 +24,14 @@
package sonia.scm.security;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.Subject;
@@ -56,6 +58,12 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
*/
private static final Logger LOG = LoggerFactory.getLogger(JwtAccessTokenBuilder.class);
@VisibleForTesting
static final long DEFAULT_REFRESHABLE = 12L;
@VisibleForTesting
static final TimeUnit DEFAULT_REFRESHABLE_UNIT = TimeUnit.HOURS;
private final KeyGenerator keyGenerator;
private final SecureKeyResolver keyResolver;
private final Clock clock;
@@ -64,8 +72,8 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
private String issuer;
private long expiresIn = 1;
private TimeUnit expiresInUnit = TimeUnit.HOURS;
private long refreshableFor = 12;
private TimeUnit refreshableForUnit = TimeUnit.HOURS;
private long refreshableFor = DEFAULT_REFRESHABLE;
private TimeUnit refreshableForUnit = DEFAULT_REFRESHABLE_UNIT;
private Instant refreshExpiration;
private String parentKeyId;
private Scope scope = Scope.empty();
@@ -87,8 +95,6 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
@Override
public JwtAccessTokenBuilder custom(String key, Object value) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(key), "null or empty value not allowed");
Preconditions.checkArgument(value != null, "null or empty value not allowed");
this.custom.put(key, value);
return this;
}
@@ -183,8 +189,8 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
if (refreshableFor > 0) {
long refreshExpiration = refreshableForUnit.toMillis(refreshableFor);
claims.put(JwtAccessToken.REFRESHABLE_UNTIL_CLAIM_KEY, new Date(now.toEpochMilli() + refreshExpiration).getTime());
long re = refreshableForUnit.toMillis(refreshableFor);
claims.put(JwtAccessToken.REFRESHABLE_UNTIL_CLAIM_KEY, Date.from(now.plusMillis(re)));
} else if (refreshExpiration != null) {
claims.put(JwtAccessToken.REFRESHABLE_UNTIL_CLAIM_KEY, Date.from(refreshExpiration));
}
@@ -198,10 +204,11 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
claims.setIssuer(issuer);
}
// sign token and create compact version
String compact = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS256, key.getBytes())
.signWith(Keys.hmacShaKeyFor(key.getBytes()), SignatureAlgorithm.HS256)
.compact();
return new JwtAccessToken(claims, compact);