mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-02 19:45:51 +01:00
Extract common functionality
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package sonia.scm.web.lfs;
|
||||
|
||||
import org.eclipse.jgit.lfs.server.Response;
|
||||
import sonia.scm.security.AccessToken;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
|
||||
@SuppressWarnings({"squid:S00116"})
|
||||
// This class is used for json serialization, only
|
||||
class ExpiringAction extends Response.Action {
|
||||
public final String expires_at;
|
||||
|
||||
ExpiringAction(String href, AccessToken accessToken) {
|
||||
this.expires_at = new SimpleDateFormat("yyyy-MM-dd'T'HH:MM:ss'Z'").format(accessToken.getExpiration());
|
||||
this.href = href;
|
||||
this.header = Collections.singletonMap("Authorization", "Bearer " + accessToken.compact());
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package sonia.scm;
|
||||
package sonia.scm.web.lfs;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.protocolcommand.CommandInterpreter;
|
||||
import sonia.scm.protocolcommand.CommandInterpreterFactory;
|
||||
import sonia.scm.protocolcommand.RepositoryContext;
|
||||
import sonia.scm.protocolcommand.RepositoryContextResolver;
|
||||
import sonia.scm.protocolcommand.ScmCommandProtocol;
|
||||
import sonia.scm.protocolcommand.git.GitRepositoryContextResolver;
|
||||
@@ -14,12 +14,7 @@ import sonia.scm.security.AccessToken;
|
||||
import sonia.scm.security.AccessTokenBuilderFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -30,13 +25,16 @@ public class LFSAuthCommand implements CommandInterpreterFactory {
|
||||
|
||||
private final AccessTokenBuilderFactory tokenBuilderFactory;
|
||||
private final GitRepositoryContextResolver gitRepositoryContextResolver;
|
||||
private final ScmConfiguration configuration;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final String baseUrl;
|
||||
|
||||
@Inject
|
||||
public LFSAuthCommand(AccessTokenBuilderFactory tokenBuilderFactory, GitRepositoryContextResolver gitRepositoryContextResolver, ScmConfiguration configuration) {
|
||||
this.tokenBuilderFactory = tokenBuilderFactory;
|
||||
this.gitRepositoryContextResolver = gitRepositoryContextResolver;
|
||||
this.configuration = configuration;
|
||||
|
||||
objectMapper = new ObjectMapper();
|
||||
baseUrl = configuration.getBaseUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,69 +42,33 @@ public class LFSAuthCommand implements CommandInterpreterFactory {
|
||||
return command.startsWith("git-lfs-authenticate") ? Optional.of(new CommandInterpreter() {
|
||||
@Override
|
||||
public String[] getParsedArgs() {
|
||||
// we are interested only in the 'repo' argument, so we discard the rest
|
||||
return new String[] {command.split("\\s+")[1]};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScmCommandProtocol getProtocolHandler() {
|
||||
return (context, repositoryContext) -> {
|
||||
AccessToken accessToken = tokenBuilderFactory.create().expiresIn(5, TimeUnit.MINUTES).build();
|
||||
|
||||
Repository repository = repositoryContext.getRepository();
|
||||
String url = format("%s/repo/%s/%s.git/info/lfs/", configuration.getBaseUrl(), repository.getNamespace(), repository.getName());
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.registerModule(new JaxbAnnotationModule());
|
||||
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:MM:ss'Z'"));
|
||||
LfsAuthResponse response = new LfsAuthResponse(url, new LfsAuthHeader(accessToken.compact()), Instant.now().plus(5, ChronoUnit.MINUTES));
|
||||
ExpiringAction response = createResponse(repositoryContext);
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
objectMapper.writeValue(buffer, response);
|
||||
context.getOutputStream().write(buffer.toString().getBytes());
|
||||
};
|
||||
}
|
||||
|
||||
private ExpiringAction createResponse(RepositoryContext repositoryContext) {
|
||||
AccessToken accessToken = tokenBuilderFactory.create().expiresIn(5, TimeUnit.MINUTES).build();
|
||||
|
||||
Repository repository = repositoryContext.getRepository();
|
||||
String url = format("%s/repo/%s/%s.git/info/lfs/", baseUrl, repository.getNamespace(), repository.getName());
|
||||
|
||||
return new ExpiringAction(url, accessToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryContextResolver getRepositoryContextResolver() {
|
||||
return gitRepositoryContextResolver;
|
||||
}
|
||||
}) : Optional.empty();
|
||||
}
|
||||
|
||||
private class LfsAuthResponse {
|
||||
private final String href;
|
||||
private final LfsAuthHeader header;
|
||||
@XmlElement(name = "expires_at")
|
||||
private final Date expiresAt;
|
||||
|
||||
public LfsAuthResponse(String href, LfsAuthHeader header, Instant expiresAt) {
|
||||
this.href = href;
|
||||
this.header = header;
|
||||
this.expiresAt = Date.from(expiresAt);
|
||||
}
|
||||
|
||||
public String getHref() {
|
||||
return href;
|
||||
}
|
||||
|
||||
public LfsAuthHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public Date getExpiresAt() {
|
||||
return expiresAt;
|
||||
}
|
||||
}
|
||||
|
||||
private class LfsAuthHeader {
|
||||
@XmlElement(name = "Authorization")
|
||||
private final String authorization;
|
||||
|
||||
public LfsAuthHeader(String authorization) {
|
||||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
public String getAuthorization() {
|
||||
return "Bearer " + authorization;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,6 @@ import sonia.scm.store.Blob;
|
||||
import sonia.scm.store.BlobStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Date;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -38,12 +33,14 @@ public class ScmBlobLfsRepository implements LargeFileRepository {
|
||||
private final String baseUri;
|
||||
private final Repository repository;
|
||||
|
||||
private AccessToken accessToken;
|
||||
|
||||
/**
|
||||
* Creates a {@link ScmBlobLfsRepository} for the provided repository.
|
||||
*
|
||||
* @param repository
|
||||
* @param repository The current scm repository this LFS repository is used for.
|
||||
* @param blobStore The SCM Blobstore used for this @{@link LargeFileRepository}.
|
||||
* @param tokenBuilderFactory
|
||||
* @param tokenBuilderFactory The token builder used to create short lived access tokens.
|
||||
* @param baseUri This URI is used to determine the actual URI for Upload / Download. Must be full URI (or
|
||||
*/
|
||||
|
||||
@@ -97,26 +94,18 @@ public class ScmBlobLfsRepository implements LargeFileRepository {
|
||||
//LFS protocol has to provide the information on where to put or get the actual content, i. e.
|
||||
//the actual URI for up- and download.
|
||||
|
||||
ExpiringAction a = new ExpiringAction();
|
||||
a.href = baseUri + id.getName();
|
||||
return new ExpiringAction(baseUri + id.getName(), getAccessToken(scope));
|
||||
}
|
||||
|
||||
AccessToken accessToken =
|
||||
tokenBuilderFactory
|
||||
private AccessToken getAccessToken(Scope scope) {
|
||||
if (accessToken == null) {
|
||||
accessToken = tokenBuilderFactory
|
||||
.create()
|
||||
.expiresIn(5, TimeUnit.MINUTES)
|
||||
.scope(scope)
|
||||
.build();
|
||||
a.header = new HashMap<>();
|
||||
a.header.put("Authorization", "Bearer " + accessToken.compact());
|
||||
Instant expire = Instant.now().plus(5, ChronoUnit.MINUTES);
|
||||
a.expires_at = new SimpleDateFormat("yyyy-MM-dd'T'HH:MM:ss'Z'").format(Date.from(expire));
|
||||
|
||||
return a;
|
||||
}
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"squid:ClassVariableVisibilityCheck", "squid:S00116"})
|
||||
// This class is used for json serialization, only
|
||||
private static class ExpiringAction extends Response.Action {
|
||||
public String expires_at;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user