Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions agent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
<groupId>com.walmartlabs.concord</groupId>
<artifactId>concord-common</artifactId>
</dependency>
<dependency>
<groupId>com.walmartlabs.concord</groupId>
<artifactId>concord-github-app-installation</artifactId>
</dependency>
<dependency>
<groupId>com.walmartlabs.concord</groupId>
<artifactId>concord-client2</artifactId>
Expand Down Expand Up @@ -143,6 +147,11 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<!-- dependency tree fix for mvnd -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.walmartlabs.concord.agent;

/*-
* *****
* Concord
* -----
* Copyright (C) 2017 - 2025 Walmart Inc.
* -----
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =====
*/

import com.typesafe.config.Config;
import com.walmartlabs.concord.agent.remote.ApiClientFactory;
import com.walmartlabs.concord.common.AuthTokenProvider;
import com.walmartlabs.concord.common.ExternalAuthToken;
import com.walmartlabs.concord.github.appinstallation.GitHubAppInstallation;
import com.walmartlabs.concord.sdk.Secret;

import javax.annotation.Nullable;
import javax.inject.Inject;
import java.net.URI;
import java.util.List;
import java.util.Optional;

public class AgentAuthTokenProvider implements AuthTokenProvider {
private final List<AuthTokenProvider> authTokenProviders;

@Inject
public AgentAuthTokenProvider(GitHubAppInstallation githubProvider,
OauthTokenProvider oauthTokenProvider) {

this.authTokenProviders = List.of(
githubProvider,
oauthTokenProvider
);
}

@Override
public boolean supports(URI repo, @Nullable Secret secret) {
return authTokenProviders.stream()
.anyMatch(p -> p.supports(repo, secret));
}

public Optional<ExternalAuthToken> getToken(URI repo, @Nullable Secret secret) {
for (var k : authTokenProviders) {
if (k.supports(repo, secret)) {
return k.getToken(repo, secret);
}
}

return Optional.empty();
}

public static class ConcordServerTokenProvider implements AuthTokenProvider {
private final ApiClientFactory apiClientFactory;
private final Config config;

@Inject
public ConcordServerTokenProvider(ApiClientFactory apiClientFactory, Config config) {
this.apiClientFactory = apiClientFactory;
this.config = config;
}


@Override
public boolean supports(URI repo, @Nullable Secret secret) {
// TODO implement
return false;
}

@Override
public Optional<ExternalAuthToken> getToken(URI repo, @Nullable Secret secret) {
// TODO implement
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
import com.walmartlabs.concord.agent.remote.ApiClientFactory;
import com.walmartlabs.concord.agent.remote.QueueClientProvider;
import com.walmartlabs.concord.common.ObjectMapperProvider;
import com.walmartlabs.concord.common.cfg.OauthTokenConfig;
import com.walmartlabs.concord.config.ConfigModule;
import com.walmartlabs.concord.github.appinstallation.cfg.GitHubAppInstallationConfig;
import com.walmartlabs.concord.server.queueclient.QueueClient;

import javax.inject.Named;
Expand Down Expand Up @@ -59,6 +61,10 @@ public void configure(Binder binder) {
binder.bind(DockerConfiguration.class).in(SINGLETON);
binder.bind(RuntimeConfiguration.class).asEagerSingleton();
binder.bind(GitConfiguration.class).in(SINGLETON);
binder.bind(OauthTokenConfig.class).to(GitConfiguration.class).in(SINGLETON);
binder.bind(GitHubConfiguration.class).in(SINGLETON);
binder.bind(GitHubAppInstallationConfig.class).to(GitHubConfiguration.class).in(SINGLETON);
binder.bind(AgentAuthTokenProvider.ConcordServerTokenProvider.class).in(SINGLETON);
binder.bind(ImportConfiguration.class).in(SINGLETON);
binder.bind(PreForkConfiguration.class).in(SINGLETON);
binder.bind(RepositoryCacheConfiguration.class).in(SINGLETON);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ public RepositoryManager(SecretClient secretClient,
GitConfiguration gitCfg,
RepositoryCacheConfiguration cacheCfg,
ObjectMapper objectMapper,
DependencyManager dependencyManager) throws IOException {
DependencyManager dependencyManager,
AgentAuthTokenProvider agentAuthTokenProvider) throws IOException {

this.secretClient = secretClient;
this.gitCfg = gitCfg;

GitClientConfiguration clientCfg = GitClientConfiguration.builder()
.oauthToken(gitCfg.getToken())
.oauthToken(gitCfg.getOauthToken())
.defaultOperationTimeout(gitCfg.getDefaultOperationTimeout())
.fetchTimeout(gitCfg.getFetchTimeout())
.httpLowSpeedLimit(gitCfg.getHttpLowSpeedLimit())
Expand All @@ -66,9 +67,10 @@ public RepositoryManager(SecretClient secretClient,
.sshTimeoutRetryCount(gitCfg.getSshTimeoutRetryCount())
.build();

List<RepositoryProvider> providers = Arrays.asList(new MavenRepositoryProvider(dependencyManager), new GitCliRepositoryProvider(clientCfg));
this.providers = new RepositoryProviders(providers);

this.providers = new RepositoryProviders(List.of(
new MavenRepositoryProvider(dependencyManager),
new GitCliRepositoryProvider(clientCfg, agentAuthTokenProvider)
));
this.repositoryCache = new RepositoryCache(cacheCfg.getCacheDir(),
cacheCfg.getInfoDir(),
cacheCfg.getLockTimeout(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,21 @@
*/

import com.typesafe.config.Config;
import com.walmartlabs.concord.common.cfg.MappingAuthConfig;
import com.walmartlabs.concord.common.cfg.OauthTokenConfig;

import javax.inject.Inject;
import java.time.Duration;
import java.util.List;
import java.util.Optional;

import static com.walmartlabs.concord.agent.cfg.Utils.getStringOrDefault;

public class GitConfiguration {
public class GitConfiguration implements OauthTokenConfig {

private final String token;
private final String oauthUsername;
private final String oauthUrlPattern;
private final boolean shallowClone;
private final boolean checkAlreadyFetched;
private final Duration defaultOperationTimeout;
Expand All @@ -39,10 +45,13 @@ public class GitConfiguration {
private final Duration sshTimeout;
private final int sshTimeoutRetryCount;
private final boolean skip;
private final List<? extends Config> authConfigs;

@Inject
public GitConfiguration(Config cfg) {
this.token = getStringOrDefault(cfg, "git.oauth", () -> null);
this.oauthUsername = getStringOrDefault(cfg, "git.oauthUsername", () -> null);
this.oauthUrlPattern = getStringOrDefault(cfg, "git.oauthUrlPattern", () -> null);
this.shallowClone = cfg.getBoolean("git.shallowClone");
this.checkAlreadyFetched = cfg.getBoolean("git.checkAlreadyFetched");
this.defaultOperationTimeout = cfg.getDuration("git.defaultOperationTimeout");
Expand All @@ -52,10 +61,22 @@ public GitConfiguration(Config cfg) {
this.sshTimeout = cfg.getDuration("git.sshTimeout");
this.sshTimeoutRetryCount = cfg.getInt("git.sshTimeoutRetryCount");
this.skip = cfg.getBoolean("git.skip");
this.authConfigs = cfg.getConfigList("git.systemAuth");
}

public String getToken() {
return token;
@Override
public Optional<String> getOauthToken() {
return Optional.ofNullable(token);
}

@Override
public Optional<String> getOauthUsername() {
return Optional.ofNullable(oauthUsername);
}

@Override
public Optional<String> getOauthUrlPattern() {
return Optional.ofNullable(oauthUrlPattern);
}

public boolean isShallowClone() {
Expand Down Expand Up @@ -93,4 +114,46 @@ public int getSshTimeoutRetryCount() {
public boolean isSkip() {
return skip;
}

public List<MappingAuthConfig> getSystemAuth() {
return authConfigs.stream()
.map(o -> {
AuthSource type = AuthSource.valueOf(o.getString("type").toUpperCase());

return (AuthConfig) switch (type) {
case OAUTH_TOKEN -> OauthConfig.from(o);
};
})
.map(AuthConfig::toGitAuth)
.toList();

}

enum AuthSource {
OAUTH_TOKEN
}

public interface AuthConfig {
MappingAuthConfig toGitAuth();
}

public record OauthConfig(String id, String urlPattern, String token) implements AuthConfig {

static OauthConfig from(Config cfg) {
return new OauthConfig(
getStringOrDefault(cfg, "id", () -> "system-oauth-token"),
cfg.getString("urlPattern"),
cfg.getString("token")
);
}

@Override
public MappingAuthConfig.OauthAuthConfig toGitAuth() {
return MappingAuthConfig.OauthAuthConfig.builder()
.id(this.id())
.urlPattern(MappingAuthConfig.assertBaseUrlPattern(this.urlPattern()))
.token(this.token())
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.walmartlabs.concord.agent.cfg;

/*-
* *****
* Concord
* -----
* Copyright (C) 2017 - 2025 Walmart Inc.
* -----
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =====
*/

import com.walmartlabs.concord.common.cfg.MappingAuthConfig;
import com.walmartlabs.concord.github.appinstallation.cfg.GitHubAppInstallationConfig;

import javax.inject.Inject;
import java.time.Duration;
import java.util.List;

public class GitHubConfiguration implements GitHubAppInstallationConfig {

private static final String CFG_APP_INSTALLATION = "github.appInstallation";

private final GitHubAppInstallationConfig appInstallation;

@Inject
public GitHubConfiguration(com.typesafe.config.Config config) {
if (config.hasPath(CFG_APP_INSTALLATION)) {
var raw = config.getConfig(CFG_APP_INSTALLATION);
this.appInstallation = GitHubAppInstallationConfig.fromConfig(raw);
} else {
this.appInstallation = GitHubAppInstallationConfig.builder()
.authConfigs(List.of())
.build();
}
}

@Override
public List<MappingAuthConfig> getAuthConfigs() {
return appInstallation.getAuthConfigs();
}

@Override
public Duration getSystemAuthCacheDuration() {
return appInstallation.getSystemAuthCacheDuration();
}

@Override
public long getSystemAuthCacheMaxWeight() {
return appInstallation.getSystemAuthCacheMaxWeight();
}

}
45 changes: 44 additions & 1 deletion agent/src/main/resources/concord-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,26 @@ concord-agent {
# if true, skip Git fetch, use workspace state only
skip = false

# GitHub auth token to use when cloning repositories without explicitly configured authentication
# GitHub auth token to use when cloning repositories without explicitly
# configured authentication. Deprecated in favor of systemAuth list of
# tokens or service-specific app config (e.g. github)
# oauth = "..."

# specific username to use for auth
# oauthUsername = ""

# regex to match against git server's hostname + port + path so oauth
# token isn't used for and unexpected host
# oauthUrlPattern = ""

# List of system-provided auth token configs
# {
# "token" = "...",
# "username" = "...", # optional, username to send with auth token
# "urlPattern" = "..." # required, regex to match against target git host + port + path
# }
systemAuth = []

# use GIT's shallow clone
shallowClone = true

Expand All @@ -212,6 +229,32 @@ concord-agent {
sshTimeout = "10 minutes"
}

# github app settings. While this works on the agent, it's preferable to
# get auth token from concord-server via externalTokenProvider
github {
# App installation settings. Multiple auth (private key) definitions are supported,
# as each is matched to a particular url pattern.
appInstallation {
# {
# type = "GITHUB_APP_INSTALLATION",
# urlPattern = "github.com", # regex
# username = "...", # optional, defaults to "x-access-token"
# apiUrl = "https://api.github.com", # github api url, usually *not* the same as the repo url host/path
# clientId = "...",
# privateKey = "/path/to/pk.pem"
# }
# or static oauth config. Not exactly a "GitHub App", but can do some
# API interactions and cloning. Less preferred to actual app.
# {
# type = "OAUTH_TOKEN",
# token = "...",
# username = "...", # optional, usually not necessary
# urlPattern = "..." # regex to match against git server's hostname + port + path
# }
auth = []
}
}

imports {
# base git url for imports
src = ""
Expand Down
Loading