This commit is contained in:
Rene Pfeuffer
2019-10-18 10:22:45 +02:00
parent 9f48875d57
commit 6cdb81d263
3 changed files with 47 additions and 12 deletions

View File

@@ -2,12 +2,14 @@ package sonia.scm;
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;
import sonia.scm.repository.Repository;
import sonia.scm.security.AccessToken;
import sonia.scm.security.AccessTokenBuilderFactory;
@@ -21,14 +23,20 @@ import java.util.Date;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static java.lang.String.format;
@Extension
public class LFSAuthCommand implements CommandInterpreterFactory {
private final AccessTokenBuilderFactory tokenBuilderFactory;
private final GitRepositoryContextResolver gitRepositoryContextResolver;
private final ScmConfiguration configuration;
@Inject
public LFSAuthCommand(AccessTokenBuilderFactory tokenBuilderFactory) {
public LFSAuthCommand(AccessTokenBuilderFactory tokenBuilderFactory, GitRepositoryContextResolver gitRepositoryContextResolver, ScmConfiguration configuration) {
this.tokenBuilderFactory = tokenBuilderFactory;
this.gitRepositoryContextResolver = gitRepositoryContextResolver;
this.configuration = configuration;
}
@Override
@@ -36,7 +44,7 @@ public class LFSAuthCommand implements CommandInterpreterFactory {
return command.startsWith("git-lfs-authenticate") ? Optional.of(new CommandInterpreter() {
@Override
public String[] getParsedArgs() {
return new String[0];
return new String[] {command.split("\\s+")[1]};
}
@Override
@@ -44,10 +52,13 @@ public class LFSAuthCommand implements CommandInterpreterFactory {
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("http://localhost:8081/scm/repo/scmadmin/lfs.git/info/lfs/", new LfsAuthHeader(accessToken.compact()), Instant.now().plus(5, ChronoUnit.MINUTES));
LfsAuthResponse response = new LfsAuthResponse(url, new LfsAuthHeader(accessToken.compact()), Instant.now().plus(5, ChronoUnit.MINUTES));
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
objectMapper.writeValue(buffer, response);
context.getOutputStream().write(buffer.toString().getBytes());
@@ -56,7 +67,7 @@ public class LFSAuthCommand implements CommandInterpreterFactory {
@Override
public RepositoryContextResolver getRepositoryContextResolver() {
return args -> new RepositoryContext(null, null);
return gitRepositoryContextResolver;
}
}) : Optional.empty();
}

View File

@@ -1,12 +1,21 @@
package sonia.scm.web.lfs;
import org.eclipse.jgit.lfs.Protocol;
import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
import org.eclipse.jgit.lfs.server.LargeFileRepository;
import org.eclipse.jgit.lfs.server.Response;
import sonia.scm.security.AccessToken;
import sonia.scm.security.AccessTokenBuilderFactory;
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;
/**
* This LargeFileRepository is used for jGit-Servlet implementation. Under the jgit LFS Servlet hood, the
@@ -18,6 +27,7 @@ import java.io.IOException;
public class ScmBlobLfsRepository implements LargeFileRepository {
private final BlobStore blobStore;
private final AccessTokenBuilderFactory tokenBuilderFactory;
/**
* This URI is used to determine the actual URI for Upload / Download. Must be full URI (or rewritable by reverse
@@ -28,14 +38,15 @@ public class ScmBlobLfsRepository implements LargeFileRepository {
/**
* Creates a {@link ScmBlobLfsRepository} for the provided repository.
*
* @param blobStore The SCM Blobstore used for this @{@link LargeFileRepository}.
* @param baseUri This URI is used to determine the actual URI for Upload / Download. Must be full URI (or
* rewritable by reverse proxy).
* @param blobStore The SCM Blobstore used for this @{@link LargeFileRepository}.
* @param tokenBuilderFactory
* @param baseUri This URI is used to determine the actual URI for Upload / Download. Must be full URI (or
*/
public ScmBlobLfsRepository(BlobStore blobStore, String baseUri) {
public ScmBlobLfsRepository(BlobStore blobStore, AccessTokenBuilderFactory tokenBuilderFactory, String baseUri) {
this.blobStore = blobStore;
this.tokenBuilderFactory = tokenBuilderFactory;
this.baseUri = baseUri;
}
@@ -82,9 +93,19 @@ 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.
Response.Action a = new Response.Action();
ExpiringAction a = new ExpiringAction();
a.href = baseUri + id.getName();
AccessToken accessToken = tokenBuilderFactory.create().expiresIn(5, TimeUnit.MINUTES).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;
}
private static class ExpiringAction extends Response.Action {
public String expires_at;
}
}

View File

@@ -7,6 +7,7 @@ import org.eclipse.jgit.lfs.server.fs.FileLfsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.Repository;
import sonia.scm.security.AccessTokenBuilderFactory;
import sonia.scm.store.BlobStore;
import sonia.scm.util.HttpUtil;
import sonia.scm.web.lfs.LfsBlobStoreFactory;
@@ -30,10 +31,12 @@ public class LfsServletFactory {
private static final Logger logger = LoggerFactory.getLogger(LfsServletFactory.class);
private final LfsBlobStoreFactory lfsBlobStoreFactory;
private final AccessTokenBuilderFactory tokenBuilderFactory;
@Inject
public LfsServletFactory(LfsBlobStoreFactory lfsBlobStoreFactory) {
public LfsServletFactory(LfsBlobStoreFactory lfsBlobStoreFactory, AccessTokenBuilderFactory tokenBuilderFactory) {
this.lfsBlobStoreFactory = lfsBlobStoreFactory;
this.tokenBuilderFactory = tokenBuilderFactory;
}
/**
@@ -47,7 +50,7 @@ public class LfsServletFactory {
BlobStore blobStore = lfsBlobStoreFactory.getLfsBlobStore(repository);
String baseUri = buildBaseUri(repository, request);
LargeFileRepository largeFileRepository = new ScmBlobLfsRepository(blobStore, baseUri);
LargeFileRepository largeFileRepository = new ScmBlobLfsRepository(blobStore, tokenBuilderFactory, baseUri);
return new ScmLfsProtocolServlet(largeFileRepository);
}