DocumentTemplateGitHttpConfig.java
package com.taxonomy.templates;
import io.github.carstenartur.jgit.storage.hibernate.RepositoryAccessDeniedException;
import io.github.carstenartur.jgit.storage.hibernate.RepositoryAccessOperation;
import io.github.carstenartur.jgit.storage.hibernate.RepositoryAccessRequest;
import io.github.carstenartur.jgit.storage.hibernate.SecuredHibernateRepositoryFactory;
import io.github.carstenartur.jgit.storage.hibernate.smarthttp.SecuredSmartHttp;
import jakarta.persistence.EntityManagerFactory;
import jakarta.servlet.http.HttpServletRequest;
import org.eclipse.jgit.http.server.GitServlet;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.hibernate.SessionFactory;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Read-only standard Git transport over the existing canonical template repository.
* Authentication and CSRF stay with the existing /api/admin/** security boundary.
* No second server, mirror, credential store or Git protocol implementation is introduced.
*/
@Configuration(proxyBeanMethods = false)
public class DocumentTemplateGitHttpConfig {
@Bean
ServletRegistrationBean<GitServlet> documentTemplateGitServlet(
EntityManagerFactory entityManagerFactory) {
var factory = new SecuredHibernateRepositoryFactory<String>(
entityManagerFactory.unwrap(SessionFactory.class),
DocumentTemplateGitHttpConfig::requireTemplateRead);
// The library's two-argument factory disables receive-pack and dumb HTTP.
GitServlet servlet = SecuredSmartHttp.servlet(
factory, DocumentTemplateGitHttpConfig::requireAdministrator);
var registration = new ServletRegistrationBean<>(servlet, "/api/admin/git/*");
registration.setName("documentTemplateGitServlet");
registration.setLoadOnStartup(1);
return registration;
}
static String requireAdministrator(HttpServletRequest request)
throws ServiceNotAuthorizedException, ServiceMayNotContinueException {
var principal = request.getUserPrincipal();
if (principal == null || principal.getName() == null || principal.getName().isBlank()) {
throw new ServiceNotAuthorizedException();
}
if (!request.isUserInRole("ADMIN") && !request.isUserInRole("ROLE_ADMIN")) {
throw new ServiceMayNotContinueException("Administrator access required", 403);
}
return principal.getName();
}
static void requireTemplateRead(String principal, RepositoryAccessRequest request) {
if (principal == null || principal.isBlank()
|| !DocumentTemplateGitRepository.REPOSITORY_NAME.equals(request.repositoryName().value())
|| (request.operation() != RepositoryAccessOperation.DISCOVER
&& request.operation() != RepositoryAccessOperation.READ)) {
throw new RepositoryAccessDeniedException(
request, "TEMPLATE_GIT_READ_ONLY", "template-git-http", 1);
}
}
}