mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-02 19:45:51 +01:00
Extract token generation
This commit is contained in:
@@ -11,26 +11,24 @@ 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;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
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 LfsAccessTokenFactory tokenFactory;
|
||||
private final GitRepositoryContextResolver gitRepositoryContextResolver;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final String baseUrl;
|
||||
|
||||
@Inject
|
||||
public LFSAuthCommand(AccessTokenBuilderFactory tokenBuilderFactory, GitRepositoryContextResolver gitRepositoryContextResolver, ScmConfiguration configuration) {
|
||||
this.tokenBuilderFactory = tokenBuilderFactory;
|
||||
public LFSAuthCommand(LfsAccessTokenFactory tokenFactory, GitRepositoryContextResolver gitRepositoryContextResolver, ScmConfiguration configuration) {
|
||||
this.tokenFactory = tokenFactory;
|
||||
this.gitRepositoryContextResolver = gitRepositoryContextResolver;
|
||||
|
||||
objectMapper = new ObjectMapper();
|
||||
@@ -39,11 +37,25 @@ public class LFSAuthCommand implements CommandInterpreterFactory {
|
||||
|
||||
@Override
|
||||
public Optional<CommandInterpreter> canHandle(String command) {
|
||||
return command.startsWith("git-lfs-authenticate") ? Optional.of(new CommandInterpreter() {
|
||||
if (command.startsWith("git-lfs-authenticate")) {
|
||||
return Optional.of(new LfsAuthCommandInterpreter(command));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private class LfsAuthCommandInterpreter implements CommandInterpreter {
|
||||
|
||||
private final String command;
|
||||
|
||||
public LfsAuthCommandInterpreter(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getParsedArgs() {
|
||||
// we are interested only in the 'repo' argument, so we discard the rest
|
||||
return new String[] {command.split("\\s+")[1]};
|
||||
return new String[]{command.split("\\s+")[1]};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,10 +69,10 @@ public class LFSAuthCommand implements CommandInterpreterFactory {
|
||||
}
|
||||
|
||||
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());
|
||||
AccessToken accessToken = tokenFactory.getReadAccessToken(repository);
|
||||
|
||||
return new ExpiringAction(url, accessToken);
|
||||
}
|
||||
@@ -69,6 +81,5 @@ public class LFSAuthCommand implements CommandInterpreterFactory {
|
||||
public RepositoryContextResolver getRepositoryContextResolver() {
|
||||
return gitRepositoryContextResolver;
|
||||
}
|
||||
}) : Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package sonia.scm.web.lfs;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
import sonia.scm.security.AccessToken;
|
||||
import sonia.scm.security.AccessTokenBuilderFactory;
|
||||
import sonia.scm.security.Scope;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class LfsAccessTokenFactory {
|
||||
|
||||
private final AccessTokenBuilderFactory tokenBuilderFactory;
|
||||
|
||||
@Inject
|
||||
LfsAccessTokenFactory(AccessTokenBuilderFactory tokenBuilderFactory) {
|
||||
this.tokenBuilderFactory = tokenBuilderFactory;
|
||||
}
|
||||
|
||||
AccessToken getReadAccessToken(Repository repository) {
|
||||
return createToken(
|
||||
Scope.valueOf(
|
||||
RepositoryPermissions.read(repository).asShiroString(),
|
||||
RepositoryPermissions.pull(repository).asShiroString()));
|
||||
}
|
||||
|
||||
AccessToken getWriteAccessToken(Repository repository) {
|
||||
return createToken(
|
||||
Scope.valueOf(
|
||||
RepositoryPermissions.read(repository).asShiroString(),
|
||||
RepositoryPermissions.pull(repository).asShiroString(),
|
||||
RepositoryPermissions.push(repository).asShiroString()));
|
||||
}
|
||||
|
||||
private AccessToken createToken(Scope scope) {
|
||||
return tokenBuilderFactory
|
||||
.create()
|
||||
.expiresIn(5, TimeUnit.MINUTES)
|
||||
.scope(scope)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,11 @@ import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
|
||||
import org.eclipse.jgit.lfs.server.LargeFileRepository;
|
||||
import org.eclipse.jgit.lfs.server.Response;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
import sonia.scm.security.AccessToken;
|
||||
import sonia.scm.security.AccessTokenBuilderFactory;
|
||||
import sonia.scm.security.Scope;
|
||||
import sonia.scm.store.Blob;
|
||||
import sonia.scm.store.BlobStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* This LargeFileRepository is used for jGit-Servlet implementation. Under the jgit LFS Servlet hood, the
|
||||
@@ -24,7 +20,7 @@ import java.util.concurrent.TimeUnit;
|
||||
public class ScmBlobLfsRepository implements LargeFileRepository {
|
||||
|
||||
private final BlobStore blobStore;
|
||||
private final AccessTokenBuilderFactory tokenBuilderFactory;
|
||||
private final LfsAccessTokenFactory tokenFactory;
|
||||
|
||||
/**
|
||||
* This URI is used to determine the actual URI for Upload / Download. Must be full URI (or rewritable by reverse
|
||||
@@ -33,6 +29,10 @@ public class ScmBlobLfsRepository implements LargeFileRepository {
|
||||
private final String baseUri;
|
||||
private final Repository repository;
|
||||
|
||||
/**
|
||||
* A {@link ScmBlobLfsRepository} is created for either download or upload, not both. Therefore we can cache the
|
||||
* access token and do not have to create them anew for each action.
|
||||
*/
|
||||
private AccessToken accessToken;
|
||||
|
||||
/**
|
||||
@@ -40,27 +40,31 @@ public class ScmBlobLfsRepository implements LargeFileRepository {
|
||||
*
|
||||
* @param repository The current scm repository this LFS repository is used for.
|
||||
* @param blobStore The SCM Blobstore used for this @{@link LargeFileRepository}.
|
||||
* @param tokenBuilderFactory The token builder used to create short lived access tokens.
|
||||
* @param tokenFactory The token builder for subsequent LFS requests.
|
||||
* @param baseUri This URI is used to determine the actual URI for Upload / Download. Must be full URI (or
|
||||
*/
|
||||
|
||||
public ScmBlobLfsRepository(Repository repository, BlobStore blobStore, AccessTokenBuilderFactory tokenBuilderFactory, String baseUri) {
|
||||
public ScmBlobLfsRepository(Repository repository, BlobStore blobStore, LfsAccessTokenFactory tokenFactory, String baseUri) {
|
||||
this.repository = repository;
|
||||
this.blobStore = blobStore;
|
||||
this.tokenBuilderFactory = tokenBuilderFactory;
|
||||
this.tokenFactory = tokenFactory;
|
||||
this.baseUri = baseUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response.Action getDownloadAction(AnyLongObjectId id) {
|
||||
|
||||
return getAction(id, Scope.valueOf(RepositoryPermissions.read(repository).asShiroString(), RepositoryPermissions.pull(repository).asShiroString()));
|
||||
if (accessToken == null) {
|
||||
accessToken = tokenFactory.getReadAccessToken(repository);
|
||||
}
|
||||
return getAction(id, accessToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response.Action getUploadAction(AnyLongObjectId id, long size) {
|
||||
|
||||
return getAction(id, Scope.valueOf(RepositoryPermissions.read(repository).asShiroString(), RepositoryPermissions.pull(repository).asShiroString(), RepositoryPermissions.push(repository).asShiroString()));
|
||||
if (accessToken == null) {
|
||||
accessToken = tokenFactory.getWriteAccessToken(repository);
|
||||
}
|
||||
return getAction(id, accessToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,23 +93,11 @@ public class ScmBlobLfsRepository implements LargeFileRepository {
|
||||
/**
|
||||
* Constructs the Download / Upload actions to be supplied to the client.
|
||||
*/
|
||||
private Response.Action getAction(AnyLongObjectId id, Scope scope) {
|
||||
private Response.Action getAction(AnyLongObjectId id, AccessToken token) {
|
||||
|
||||
//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.
|
||||
|
||||
return new ExpiringAction(baseUri + id.getName(), getAccessToken(scope));
|
||||
return new ExpiringAction(baseUri + id.getName(), token);
|
||||
}
|
||||
|
||||
private AccessToken getAccessToken(Scope scope) {
|
||||
if (accessToken == null) {
|
||||
accessToken = tokenBuilderFactory
|
||||
.create()
|
||||
.expiresIn(5, TimeUnit.MINUTES)
|
||||
.scope(scope)
|
||||
.build();
|
||||
}
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,12 +4,10 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import org.eclipse.jgit.lfs.server.LargeFileRepository;
|
||||
import org.eclipse.jgit.lfs.server.LfsProtocolServlet;
|
||||
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.LfsAccessTokenFactory;
|
||||
import sonia.scm.web.lfs.LfsBlobStoreFactory;
|
||||
import sonia.scm.web.lfs.ScmBlobLfsRepository;
|
||||
|
||||
@@ -28,15 +26,13 @@ import javax.servlet.http.HttpServletRequest;
|
||||
@Singleton
|
||||
public class LfsServletFactory {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LfsServletFactory.class);
|
||||
|
||||
private final LfsBlobStoreFactory lfsBlobStoreFactory;
|
||||
private final AccessTokenBuilderFactory tokenBuilderFactory;
|
||||
private final LfsAccessTokenFactory tokenFactory;
|
||||
|
||||
@Inject
|
||||
public LfsServletFactory(LfsBlobStoreFactory lfsBlobStoreFactory, AccessTokenBuilderFactory tokenBuilderFactory) {
|
||||
public LfsServletFactory(LfsBlobStoreFactory lfsBlobStoreFactory, LfsAccessTokenFactory tokenFactory) {
|
||||
this.lfsBlobStoreFactory = lfsBlobStoreFactory;
|
||||
this.tokenBuilderFactory = tokenBuilderFactory;
|
||||
this.tokenFactory = tokenFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +46,7 @@ public class LfsServletFactory {
|
||||
BlobStore blobStore = lfsBlobStoreFactory.getLfsBlobStore(repository);
|
||||
String baseUri = buildBaseUri(repository, request);
|
||||
|
||||
LargeFileRepository largeFileRepository = new ScmBlobLfsRepository(repository, blobStore, tokenBuilderFactory, baseUri);
|
||||
LargeFileRepository largeFileRepository = new ScmBlobLfsRepository(repository, blobStore, tokenFactory, baseUri);
|
||||
return new ScmLfsProtocolServlet(largeFileRepository);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user