diff --git a/.gitignore b/.gitignore index c2065bc..4822af4 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ out/ ### VS Code ### .vscode/ + +### Logs ### +logs/ diff --git a/CONFIGURATION.md b/CONFIGURATION.md new file mode 100644 index 0000000..cd321d8 --- /dev/null +++ b/CONFIGURATION.md @@ -0,0 +1,162 @@ +# Configuration Guide + +The Jetty-based GitProxy server supports configuration via YAML files and environment variables. This allows you to configure providers, filters, and other server settings without modifying code. + +## Configuration Files + +The server loads configuration from the following files in order: +1. `src/main/resources/git-proxy.yml` - Base configuration +2. `src/main/resources/git-proxy-local.yml` - Local overrides (merged with base) +3. Environment variables with `GITPROXY_` prefix + +Configuration from `git-proxy-local.yml` will override or extend settings from `git-proxy.yml`, and environment variables will override both. + +## Environment Variable Overrides + +You can override certain configuration values using environment variables with the `GITPROXY_` prefix: + +- `GITPROXY_SERVER_PORT`: Override the server port (e.g., `GITPROXY_SERVER_PORT=9090`) +- `GITPROXY_GITPROXY_BASEPATH`: Override the base path (e.g., `GITPROXY_GITPROXY_BASEPATH=/proxy`) + +Note: Whitelist configurations are not supported via environment variables due to their complex structure. + +## Server Configuration + +```yaml +server: + port: 8080 # HTTP port for the server +``` + +## Provider Configuration + +Providers define the Git hosting services that the proxy will forward requests to. + +### Built-in Providers + +The following providers are built-in: +- `github` - GitHub (https://github.com) +- `gitlab` - GitLab (https://gitlab.com) +- `bitbucket` - Bitbucket (https://bitbucket.org) + +### Example Provider Configuration + +```yaml +git-proxy: + base-path: "/git" # Optional base path for all servlets + providers: + github: + enabled: true + gitlab: + enabled: true + servlet-path: /custom-gitlab # Optional custom path + bitbucket: + enabled: false + # Custom provider example + internal-github: + enabled: true + servlet-path: /enterprise-github + uri: https://githubserver.example.com +``` + +### Provider Options + +- `enabled` (boolean): Enable/disable the provider +- `uri` (string): Custom URI for the provider (for self-hosted instances) +- `servlet-path` (string): Custom servlet path (default is based on provider name) +- `log-proxy` (boolean): Enable proxy logging (default: true) +- `connect-timeout` (int): Connection timeout in milliseconds (default: -1, no timeout) +- `read-timeout` (int): Read timeout in milliseconds (default: -1, no timeout) + +## Filter Configuration + +Filters control access to repositories and enforce policies. + +Note: GitHub already enforces authentication for push operations using personal access tokens (PATs). The proxy transparently forwards requests upstream and returns errors from GitHub directly, so authentication checking is handled by GitHub itself. + +### Whitelist Filters + +Control which repositories can be accessed. + +```yaml +git-proxy: + filters: + whitelists: + - enabled: true + order: 5 + operations: + - FETCH + - PUSH + providers: + - github + slugs: + - coopernetes/test-repo + - finos/git-proxy + - enabled: true + order: 10 + operations: + - PUSH + providers: + - gitlab + owners: + - finosfoundation + - enabled: true + order: 20 + operations: + - FETCH + providers: + - github + names: + - hello-world +``` + +Options: +- `enabled` (boolean): Enable/disable the filter +- `order` (int): Filter execution order +- `operations` (list): Git operations to apply filter to (PUSH, FETCH) +- `providers` (list): Provider names to apply filter to +- `slugs` (list): Repository slugs (owner/repo) to whitelist +- `owners` (list): Repository owners to whitelist +- `names` (list): Repository names to whitelist + +Note: You can use `slugs`, `owners`, and `names` together in a single whitelist entry, or separately for more granular control. + +## Running the Server + +```bash +# Build the application +./gradlew build + +# Run the application +./gradlew run + +# Or run the JAR directly +java -jar build/libs/jgit-proxy-*.jar +``` + +The server will read configuration from the YAML files and start with the configured providers and filters. + +## Example Usage + +Once the server is running, you can use it as a proxy for Git operations: + +```bash +# Clone via proxy +git clone http://localhost:8080/github.com/finos/git-proxy.git + +# Clone from GitLab via proxy +git clone http://localhost:8080/gitlab.com/coopernetes/test-repo.git + +# Clone from custom provider +git clone http://localhost:8080/debian/salsa/test-repo.git +``` + +## Logging + +The application uses SLF4J with Logback. You can configure logging levels in the YAML files: + +```yaml +logging: + level: + org.finos.gitproxy: DEBUG + org.finos.gitproxy.git: DEBUG +``` diff --git a/README.md b/README.md index fafdf23..e9deab9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,45 @@ This is a simple implementation of a git proxy in Java. This is a possible succe ## Usage To use this project, you need to have Java 17 or higher installed on your machine. You can run the project using the following command: ```shell -./gradlew bootRun +./gradlew run +``` + +## Configuration + +The Jetty-based server supports YAML-based configuration for providers and filters. Configuration files: +- `src/main/resources/application.yml` - Base configuration +- `src/main/resources/application-local.yml` - Local overrides + +See [CONFIGURATION.md](CONFIGURATION.md) for detailed configuration options. + +### Quick Configuration Example + +Configure providers and filters in `application-local.yml`: + +```yaml +server: + port: 8080 + +git-proxy: + providers: + github: + enabled: true + gitlab: + enabled: true + bitbucket: + enabled: true + filters: + whitelists: + - enabled: true + order: 5 + operations: + - FETCH + - PUSH + providers: + - github + slugs: + - coopernetes/test-repo + - finos/git-proxy ``` ## Endpoints diff --git a/build.gradle b/build.gradle index 72b4fe9..cdb52ea 100644 --- a/build.gradle +++ b/build.gradle @@ -33,9 +33,9 @@ repositories { } } -//application { -// mainClass = 'org.finos.gitproxy.GitProxyApplication' -//} +application { + mainClass = 'org.finos.gitproxy.GitProxyApplication' +} ext { jgitVersion = '7.0.0.202409031743-r' diff --git a/src/main/java/org/finos/gitproxy/GitProxyApplication.java b/src/main/java/org/finos/gitproxy/GitProxyApplication.java index 251b0ba..33163df 100644 --- a/src/main/java/org/finos/gitproxy/GitProxyApplication.java +++ b/src/main/java/org/finos/gitproxy/GitProxyApplication.java @@ -1,83 +1,91 @@ package org.finos.gitproxy; import jakarta.servlet.DispatcherType; +import java.util.Comparator; import java.util.EnumSet; import java.util.List; -import java.util.Set; +import lombok.extern.slf4j.Slf4j; import org.eclipse.jetty.ee10.servlet.FilterHolder; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.finos.gitproxy.git.HttpAuthScheme; -import org.finos.gitproxy.provider.GitHubProvider; +import org.finos.gitproxy.config.JettyConfigurationBuilder; +import org.finos.gitproxy.config.JettyConfigurationLoader; +import org.finos.gitproxy.provider.GitProxyProvider; import org.finos.gitproxy.servlet.GitProxyServlet; -import org.finos.gitproxy.servlet.filter.*; +import org.finos.gitproxy.servlet.filter.GitProxyFilter; +/** + * Main application class for the Jetty-based GitProxy server. This application reads configuration from YAML files and + * bootstraps the Jetty server with appropriate providers and filters. + */ +@Slf4j public class GitProxyApplication { public static void main(String[] args) throws Exception { + // Load configuration + JettyConfigurationLoader configLoader = new JettyConfigurationLoader(); + JettyConfigurationBuilder configBuilder = new JettyConfigurationBuilder(configLoader); + + // Build providers from configuration + List providers = configBuilder.buildProviders(); + + if (providers.isEmpty()) { + log.warn("No providers configured, server will not handle any requests"); + } + + // Setup Jetty server var threadPool = new QueuedThreadPool(); threadPool.setName("server"); - var server = new Server(threadPool); + // Configure server port + int port = configLoader.getServerPort(); var connector = new ServerConnector(server); - connector.setPort(8080); + connector.setPort(port); server.addConnector(connector); - var gitHubProvider = new GitHubProvider(""); - String urlPattern = gitHubProvider.servletMapping(); - var context = new ServletContextHandler("/", false, false); - var forceGitClientFilter = new ForceGitClientFilter(); - var forceGitClientFilterHolder = new FilterHolder(forceGitClientFilter); - forceGitClientFilterHolder.setAsyncSupported(true); - context.addFilter(forceGitClientFilterHolder, urlPattern, EnumSet.of(DispatcherType.REQUEST)); + // Setup each provider with its filters and servlet + for (GitProxyProvider provider : providers) { + log.info("Configuring provider: {} at {}", provider.getName(), provider.servletMapping()); - var parseRequestFilter = new ParseGitRequestFilter(gitHubProvider); - var parseRequestFilterHolder = new FilterHolder(parseRequestFilter); - parseRequestFilterHolder.setAsyncSupported(true); - context.addFilter(parseRequestFilterHolder, urlPattern, EnumSet.of(DispatcherType.REQUEST)); + String urlPattern = provider.servletMapping(); - var githubAuthorizedFilter = new GitHubUserAuthenticatedFilter( - 10, gitHubProvider, Set.of(HttpAuthScheme.BASIC, HttpAuthScheme.TOKEN, HttpAuthScheme.BEARER)); - var ghFilterHolder = new FilterHolder(githubAuthorizedFilter); - context.addFilter(ghFilterHolder, urlPattern, EnumSet.of(DispatcherType.REQUEST)); + // Build and add filters for this provider + List filters = configBuilder.buildFiltersForProvider(provider); - var whitelistFilters = List.of( - // new WhitelistByUrlFilter(100, gitHubProvider, List.of("coopernetes"), - // RepositoryUrlFilter.Target.OWNER), - // new WhitelistByUrlFilter( - // 101, gitHubProvider, List.of("jgit-proxy", "test-repo"), - // RepositoryUrlFilter.Target.NAME), - new WhitelistByUrlFilter( - 102, - gitHubProvider, - List.of("finos/git-proxy", "coopernetes/test-repo"), - RepositoryUrlFilter.Target.SLUG)); - var whitelistAggregateFilter = new WhitelistAggregateFilter(20, gitHubProvider, whitelistFilters); - var whitelistAggFilterHolder = new FilterHolder(whitelistAggregateFilter); - whitelistAggFilterHolder.setAsyncSupported(true); - context.addFilter(whitelistAggFilterHolder, urlPattern, EnumSet.of(DispatcherType.REQUEST)); + // Sort filters by order + filters.sort(Comparator.comparingInt(GitProxyFilter::getOrder)); - var auditFilter = new AuditLogFilter(); - var auditFilterHolder = new FilterHolder(auditFilter); - auditFilterHolder.setAsyncSupported(true); - context.addFilter(auditFilterHolder, urlPattern, EnumSet.of(DispatcherType.REQUEST)); + for (GitProxyFilter filter : filters) { + var filterHolder = new FilterHolder(filter); + filterHolder.setAsyncSupported(true); + context.addFilter(filterHolder, urlPattern, EnumSet.of(DispatcherType.REQUEST)); + log.debug( + "Added filter {} (order={}) for {}", + filter.getClass().getSimpleName(), + filter.getOrder(), + provider.getName()); + } - var proxyServlet = new GitProxyServlet(); - var proxyServletHolder = new ServletHolder(proxyServlet); - proxyServletHolder.setInitParameter("proxyTo", "https://github.com"); - proxyServletHolder.setInitParameter("prefix", "/github.com"); - proxyServletHolder.setInitParameter("hostHeader", "github.com"); - proxyServletHolder.setInitParameter("preserveHost", "false"); - context.addServlet(proxyServletHolder, urlPattern); + // Add proxy servlet for this provider + var proxyServlet = new GitProxyServlet(); + var proxyServletHolder = new ServletHolder(proxyServlet); + proxyServletHolder.setInitParameter("proxyTo", provider.getUri().toString()); + proxyServletHolder.setInitParameter("prefix", provider.servletPath()); + proxyServletHolder.setInitParameter("hostHeader", provider.getUri().getHost()); + proxyServletHolder.setInitParameter("preserveHost", "false"); + context.addServlet(proxyServletHolder, urlPattern); + log.info("Added servlet for {} proxying to {}", provider.getName(), provider.getUri()); + } server.setHandler(context); server.start(); - System.out.println("Server started at http://localhost:8080/"); + log.info("Server started at http://localhost:{}/", port); + System.out.println("Server started at http://localhost:" + port + "/"); } } diff --git a/src/main/java/org/finos/gitproxy/config/JettyConfigurationBuilder.java b/src/main/java/org/finos/gitproxy/config/JettyConfigurationBuilder.java new file mode 100644 index 0000000..5c7d6a9 --- /dev/null +++ b/src/main/java/org/finos/gitproxy/config/JettyConfigurationBuilder.java @@ -0,0 +1,211 @@ +package org.finos.gitproxy.config; + +import java.net.URI; +import java.util.*; +import lombok.extern.slf4j.Slf4j; +import org.finos.gitproxy.git.HttpOperation; +import org.finos.gitproxy.provider.*; +import org.finos.gitproxy.servlet.filter.*; + +/** + * Builder that creates GitProxy providers and filters from configuration. This class reads the parsed YAML + * configuration and instantiates the appropriate provider and filter objects for use in the Jetty server. + */ +@Slf4j +public class JettyConfigurationBuilder { + + private final JettyConfigurationLoader configLoader; + private final String basePath; + private List providers; + private Map> providerFilters; + + public JettyConfigurationBuilder(JettyConfigurationLoader configLoader) { + this.configLoader = configLoader; + this.basePath = configLoader.getBasePath(); + this.providers = new ArrayList<>(); + this.providerFilters = new HashMap<>(); + } + + /** Build all providers from configuration. */ + public List buildProviders() { + if (!providers.isEmpty()) { + return providers; + } + + Map> providersConfig = configLoader.getProviders(); + + for (Map.Entry> entry : providersConfig.entrySet()) { + String providerName = entry.getKey(); + Map providerConfig = entry.getValue(); + + boolean enabled = (Boolean) providerConfig.getOrDefault("enabled", false); + if (!enabled) { + log.info("Provider {} is disabled, skipping", providerName); + continue; + } + + GitProxyProvider provider = createProvider(providerName, providerConfig); + if (provider != null) { + providers.add(provider); + log.info("Created provider: {}", provider.getName()); + } + } + + return providers; + } + + /** Create a single provider from configuration. */ + private GitProxyProvider createProvider(String name, Map config) { + String customPath = (String) config.get("servlet-path"); + String uriString = (String) config.get("uri"); + + switch (name.toLowerCase()) { + case GitHubProvider.NAME: + if (uriString != null) { + return new GitHubProvider(URI.create(uriString), basePath, customPath); + } + return new GitHubProvider(basePath); + + case GitLabProvider.NAME: + if (uriString != null) { + return new GitLabProvider(URI.create(uriString), basePath, customPath); + } + return new GitLabProvider(basePath); + + case BitbucketProvider.NAME: + if (uriString != null) { + return new BitbucketProvider(URI.create(uriString), basePath, customPath); + } + return new BitbucketProvider(basePath); + + default: + // Generic provider + if (uriString != null) { + return GenericProxyProvider.builder() + .name(name) + .uri(URI.create(uriString)) + .basePath(basePath) + .customPath(customPath) + .build(); + } else { + log.warn("Unknown provider {} without URI, skipping", name); + return null; + } + } + } + + /** Build all filters for a specific provider from configuration. */ + public List buildFiltersForProvider(GitProxyProvider provider) { + if (providerFilters.containsKey(provider)) { + return providerFilters.get(provider); + } + + List filters = new ArrayList<>(); + Map filtersConfig = configLoader.getFilters(); + + // Add standard filters that all providers need + filters.add(new ForceGitClientFilter()); + filters.add(new ParseGitRequestFilter(provider)); + + // Build whitelist filters + filters.addAll(buildWhitelistFilters(provider, filtersConfig)); + + // Add audit filter + filters.add(new AuditLogFilter()); + + providerFilters.put(provider, filters); + return filters; + } + + /** Build whitelist filters if configured. */ + @SuppressWarnings("unchecked") + private List buildWhitelistFilters(GitProxyProvider provider, Map filtersConfig) { + List filters = new ArrayList<>(); + + Object whitelistsConfig = filtersConfig.get("whitelists"); + if (whitelistsConfig == null) { + return filters; + } + + List> whitelistsList = (List>) whitelistsConfig; + List whitelistFilters = new ArrayList<>(); + + for (Map whitelistConfig : whitelistsList) { + boolean enabled = (Boolean) whitelistConfig.getOrDefault("enabled", false); + if (!enabled) { + continue; + } + + // Check if this provider is in the list + List providerList = (List) whitelistConfig.get("providers"); + if (providerList != null && !providerList.contains(provider.getName())) { + continue; + } + + int order = (Integer) whitelistConfig.getOrDefault("order", 0); + + // Parse operations + Set operations = parseOperations((List) whitelistConfig.get("operations")); + + // Build filters for each target type + List slugs = (List) whitelistConfig.get("slugs"); + if (slugs != null && !slugs.isEmpty()) { + WhitelistByUrlFilter filter = + new WhitelistByUrlFilter(order, operations, provider, slugs, AuthorizedByUrlFilter.Target.SLUG); + whitelistFilters.add(filter); + } + + List owners = (List) whitelistConfig.get("owners"); + if (owners != null && !owners.isEmpty()) { + WhitelistByUrlFilter filter = new WhitelistByUrlFilter( + order + 1, operations, provider, owners, AuthorizedByUrlFilter.Target.OWNER); + whitelistFilters.add(filter); + } + + List names = (List) whitelistConfig.get("names"); + if (names != null && !names.isEmpty()) { + WhitelistByUrlFilter filter = new WhitelistByUrlFilter( + order + 2, operations, provider, names, AuthorizedByUrlFilter.Target.NAME); + whitelistFilters.add(filter); + } + } + + // If we have whitelist filters, wrap them in an aggregate filter + if (!whitelistFilters.isEmpty()) { + int minOrder = whitelistFilters.stream() + .mapToInt(WhitelistByUrlFilter::getOrder) + .min() + .orElse(0); + WhitelistAggregateFilter aggregateFilter = + new WhitelistAggregateFilter(minOrder, provider, whitelistFilters); + filters.add(aggregateFilter); + log.info( + "Created WhitelistAggregateFilter with {} filters for provider: {}", + whitelistFilters.size(), + provider.getName()); + } + + return filters; + } + + /** Parse operation strings to HttpOperation enums. */ + private Set parseOperations(List operationStrings) { + if (operationStrings == null || operationStrings.isEmpty()) { + return Set.of(HttpOperation.PUSH, HttpOperation.FETCH); + } + + Set operations = new HashSet<>(); + for (String op : operationStrings) { + try { + operations.add(HttpOperation.valueOf(op.toUpperCase())); + } catch (IllegalArgumentException e) { + log.warn("Unknown operation: {}", op); + } + } + return operations; + } + + public List getProviders() { + return providers; + } +} diff --git a/src/main/java/org/finos/gitproxy/config/JettyConfigurationLoader.java b/src/main/java/org/finos/gitproxy/config/JettyConfigurationLoader.java new file mode 100644 index 0000000..7bfcc24 --- /dev/null +++ b/src/main/java/org/finos/gitproxy/config/JettyConfigurationLoader.java @@ -0,0 +1,173 @@ +package org.finos.gitproxy.config; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import lombok.extern.slf4j.Slf4j; +import org.yaml.snakeyaml.Yaml; + +/** + * Configuration loader for the Jetty-based GitProxy application. This class reads configuration from YAML files + * (git-proxy.yml, git-proxy-local.yml) and environment variables, providing a structured representation of the + * configuration that can be used to bootstrap the Jetty server with appropriate providers and filters. + * + *

Environment variables with the prefix GITPROXY_ can override configuration values. For example: - + * GITPROXY_SERVER_PORT=9090 overrides server.port - GITPROXY_GITPROXY_BASEPATH=/proxy overrides git-proxy.base-path + */ +@Slf4j +public class JettyConfigurationLoader { + + private static final String DEFAULT_CONFIG = "git-proxy.yml"; + private static final String LOCAL_CONFIG = "git-proxy-local.yml"; + private static final String ENV_PREFIX = "GITPROXY_"; + + private Map config; + + public JettyConfigurationLoader() { + this.config = loadConfiguration(); + } + + /** + * Load configuration from YAML files and environment variables. Loads git-proxy.yml first, then overlays + * git-proxy-local.yml if it exists, and finally applies environment variable overrides. + */ + private Map loadConfiguration() { + Yaml yaml = new Yaml(); + Map baseConfig = new HashMap<>(); + + // Load base configuration + try (InputStream is = getClass().getClassLoader().getResourceAsStream(DEFAULT_CONFIG)) { + if (is != null) { + Map loaded = yaml.load(is); + if (loaded != null) { + baseConfig = loaded; + log.info("Loaded base configuration from {}", DEFAULT_CONFIG); + } + } else { + log.warn("Base configuration file {} not found, using defaults", DEFAULT_CONFIG); + } + } catch (IOException e) { + log.warn("Failed to load base configuration from {}: {}", DEFAULT_CONFIG, e.getMessage()); + } + + // Load local configuration and overlay + try (InputStream is = getClass().getClassLoader().getResourceAsStream(LOCAL_CONFIG)) { + if (is != null) { + Map localConfig = yaml.load(is); + if (localConfig != null) { + deepMerge(baseConfig, localConfig); + log.info("Loaded and merged local configuration from {}", LOCAL_CONFIG); + } + } else { + log.debug("Local configuration file {} not found, skipping", LOCAL_CONFIG); + } + } catch (IOException e) { + log.debug("No local configuration found at {}", LOCAL_CONFIG); + } + + // Apply environment variable overrides + applyEnvironmentOverrides(baseConfig); + + return baseConfig; + } + + /** Deep merge two maps. Values from the overlay map will overwrite or extend values in the base map. */ + @SuppressWarnings("unchecked") + private void deepMerge(Map base, Map overlay) { + for (Map.Entry entry : overlay.entrySet()) { + String key = entry.getKey(); + Object overlayValue = entry.getValue(); + + if (base.containsKey(key)) { + Object baseValue = base.get(key); + if (baseValue instanceof Map && overlayValue instanceof Map) { + deepMerge((Map) baseValue, (Map) overlayValue); + } else { + base.put(key, overlayValue); + } + } else { + base.put(key, overlayValue); + } + } + } + + /** + * Apply environment variable overrides to configuration. Supported environment variables: - GITPROXY_SERVER_PORT: + * Override server port - GITPROXY_GITPROXY_BASEPATH: Override git-proxy base path + */ + @SuppressWarnings("unchecked") + private void applyEnvironmentOverrides(Map config) { + Map env = System.getenv(); + + // Override server port + String portEnv = env.get(ENV_PREFIX + "SERVER_PORT"); + if (portEnv != null) { + try { + int port = Integer.parseInt(portEnv); + Map serverConfig = + (Map) config.computeIfAbsent("server", k -> new HashMap<>()); + serverConfig.put("port", port); + log.info("Overriding server port from environment: {}", port); + } catch (NumberFormatException e) { + log.warn("Invalid port value in GITPROXY_SERVER_PORT: {}", portEnv); + } + } + + // Override base path + String basePathEnv = env.get(ENV_PREFIX + "GITPROXY_BASEPATH"); + if (basePathEnv != null) { + Map gitProxyConfig = + (Map) config.computeIfAbsent("git-proxy", k -> new HashMap<>()); + gitProxyConfig.put("base-path", basePathEnv); + log.info("Overriding base path from environment: {}", basePathEnv); + } + + // Log other GITPROXY_ variables for visibility + env.keySet().stream() + .filter(k -> k.startsWith(ENV_PREFIX)) + .filter(k -> !k.equals(ENV_PREFIX + "SERVER_PORT") && !k.equals(ENV_PREFIX + "GITPROXY_BASEPATH")) + .forEach(k -> log.debug("Found environment variable {} (not currently supported)", k)); + } + + /** Get the git-proxy configuration section. */ + @SuppressWarnings("unchecked") + public Map getGitProxyConfig() { + return (Map) config.getOrDefault("git-proxy", new HashMap<>()); + } + + /** Get the providers configuration. */ + @SuppressWarnings("unchecked") + public Map> getProviders() { + Map gitProxyConfig = getGitProxyConfig(); + return (Map>) gitProxyConfig.getOrDefault("providers", new HashMap<>()); + } + + /** Get the filters configuration. */ + @SuppressWarnings("unchecked") + public Map getFilters() { + Map gitProxyConfig = getGitProxyConfig(); + return (Map) gitProxyConfig.getOrDefault("filters", new HashMap<>()); + } + + /** Get the base path for servlets. */ + public String getBasePath() { + Map gitProxyConfig = getGitProxyConfig(); + return (String) gitProxyConfig.getOrDefault("base-path", ""); + } + + /** Get server port configuration. */ + @SuppressWarnings("unchecked") + public int getServerPort() { + Map serverConfig = (Map) config.getOrDefault("server", new HashMap<>()); + Object port = serverConfig.get("port"); + if (port instanceof Integer) { + return (Integer) port; + } + return 8080; // default + } + + /** Get the entire configuration map. */ + public Map getConfig() { + return config; + } +} diff --git a/src/main/java/org/finos/gitproxy/servlet/GitProxyServlet.java b/src/main/java/org/finos/gitproxy/servlet/GitProxyServlet.java index e51ab34..3b4c571 100644 --- a/src/main/java/org/finos/gitproxy/servlet/GitProxyServlet.java +++ b/src/main/java/org/finos/gitproxy/servlet/GitProxyServlet.java @@ -3,14 +3,13 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; import lombok.extern.slf4j.Slf4j; import org.eclipse.jetty.client.BytesRequestContent; import org.eclipse.jetty.client.Request; import org.eclipse.jetty.ee10.proxy.AsyncProxyServlet; import org.finos.gitproxy.git.GitRequestDetails; -import java.io.IOException; - @Slf4j public class GitProxyServlet extends AsyncProxyServlet.Transparent { public static final String GIT_REQUEST_ATTRIBUTE = "org.finos.gitproxy.gitproxy.gitRequest"; diff --git a/src/main/resources/git-proxy-local.yml b/src/main/resources/git-proxy-local.yml new file mode 100644 index 0000000..be61c8b --- /dev/null +++ b/src/main/resources/git-proxy-local.yml @@ -0,0 +1,60 @@ +management: + endpoints: + web: + exposure: + include: "*" +logging: + level: + org.finos.gitproxy: DEBUG + org.finos.gitproxy.git: DEBUG +# org.eclipse.jgit.transport: DEBUG +git-proxy: + filters: + whitelists: + - enabled: true + order: 5 + operations: + - FETCH + - PUSH + providers: + - gitlab + slugs: + - coopernetes/test-repo + owners: + - finosfoundation + names: + - hello-world + - enabled: true + order: 10 + operations: + - PUSH + owners: + - finosfoundation + providers: + - gitlab + - enabled: true + order: 10 + operations: + - PUSH + slugs: + - finos/git-proxy + providers: + - github + - enabled: true + order: 20 + operations: + - FETCH + owners: + - finos + providers: + - github + - enabled: true + order: 30 + operations: + - FETCH + - PUSH + slugs: + - coopernetes/test-repo + - coopernetes/test-repo2 + providers: + - github \ No newline at end of file diff --git a/src/main/resources/git-proxy.yml b/src/main/resources/git-proxy.yml new file mode 100644 index 0000000..39ccc21 --- /dev/null +++ b/src/main/resources/git-proxy.yml @@ -0,0 +1,37 @@ +spring: + application: + name: jgit-proxy +server: + port: 8080 +management: + endpoints: + web: + exposure: + include: mappings, health, info +logging: + level: + org.finos.gitproxy: INFO +# The below is a minimal configuration needed for the app to start and is the default shipped with the jar. +# By default, the app supports proxying to GitHub, GitLab, and Bitbucket. Users should override this configuration +# to suit their needs. +git-proxy: +# base-path: "/git" + providers: + github: + enabled: true + gitlab: + enabled: true + bitbucket: + enabled: true +# internal-github: +# enabled: true +# servlet-path: /enterprise-github +# uri: https://githubserver.example.com +# internal-gitlab: +# enabled: true +# servlet-path: /external-github +# uri: https://gitlabserver.other.example.com + debian-gitlab: + enabled: true + servlet-path: /debian + uri: https://salsa.debian.org/ diff --git a/src/test/java/org/finos/gitproxy/JgitProxyApplicationTests.java b/src/test/java/org/finos/gitproxy/JgitProxyApplicationTests.java index cae6a2c..c15c14a 100644 --- a/src/test/java/org/finos/gitproxy/JgitProxyApplicationTests.java +++ b/src/test/java/org/finos/gitproxy/JgitProxyApplicationTests.java @@ -2,9 +2,10 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import spring.GitProxyServerApplication; // @Import(TestcontainersConfiguration.class) -@SpringBootTest +@SpringBootTest(classes = GitProxyServerApplication.class) class JgitProxyApplicationTests { @Test diff --git a/src/test/java/org/finos/gitproxy/config/JettyConfigurationLoaderTest.java b/src/test/java/org/finos/gitproxy/config/JettyConfigurationLoaderTest.java new file mode 100644 index 0000000..dd93065 --- /dev/null +++ b/src/test/java/org/finos/gitproxy/config/JettyConfigurationLoaderTest.java @@ -0,0 +1,42 @@ +package org.finos.gitproxy.config; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.Map; +import org.finos.gitproxy.provider.GitProxyProvider; +import org.junit.jupiter.api.Test; + +class JettyConfigurationLoaderTest { + + @Test + void testLoadDefaultConfiguration() { + JettyConfigurationLoader loader = new JettyConfigurationLoader(); + assertNotNull(loader.getConfig()); + assertNotNull(loader.getGitProxyConfig()); + } + + @Test + void testGetProviders() { + JettyConfigurationLoader loader = new JettyConfigurationLoader(); + Map> providers = loader.getProviders(); + assertNotNull(providers); + assertTrue(providers.containsKey("github")); + } + + @Test + void testGetServerPort() { + JettyConfigurationLoader loader = new JettyConfigurationLoader(); + int port = loader.getServerPort(); + assertEquals(8080, port); + } + + @Test + void testBuildProviders() { + JettyConfigurationLoader loader = new JettyConfigurationLoader(); + JettyConfigurationBuilder builder = new JettyConfigurationBuilder(loader); + List providers = builder.buildProviders(); + assertNotNull(providers); + assertTrue(providers.size() >= 1); // At least github in test config + } +} diff --git a/src/test/resources/git-proxy.yml b/src/test/resources/git-proxy.yml new file mode 100644 index 0000000..f737b1a --- /dev/null +++ b/src/test/resources/git-proxy.yml @@ -0,0 +1,8 @@ +git-proxy: + providers: + github: + enabled: true +logging: + level: + com.fasterxml.jackson: TRACE + org.springframework.boot.context.config: TRACE \ No newline at end of file