Skip to content
Merged
5 changes: 4 additions & 1 deletion docs/_docs/datasources/repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ for information on Package URL and the various ways it is used throughout Depend

### Authentication

For each repository a Username and Password can be specified to perform Basic Authentication.
To use Bearer Token authentication, leave the Username field empty and fill out the Token in the Password field.

#### GitHub

For GitHub repositories (`github.com` per default), the username should be the GitHub account's username,
and the password should be a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)
(PAT) with public access (no additional scopes).
(PAT) with public access (no additional scopes).
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,6 @@
*/
package org.dependencytrack.persistence;

import alpine.common.logging.Logger;
import alpine.persistence.PaginatedResult;
import alpine.resources.AlpineRequest;
import alpine.security.crypto.DataEncryption;
import org.apache.commons.lang3.StringUtils;
import org.dependencytrack.model.Repository;
import org.dependencytrack.model.RepositoryMetaComponent;
import org.dependencytrack.model.RepositoryType;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -37,6 +26,19 @@
import java.util.Map;
import java.util.UUID;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.apache.commons.lang3.StringUtils;
import org.dependencytrack.model.Repository;
import org.dependencytrack.model.RepositoryMetaComponent;
import org.dependencytrack.model.RepositoryType;

import alpine.common.logging.Logger;
import alpine.persistence.PaginatedResult;
import alpine.resources.AlpineRequest;
import alpine.security.crypto.DataEncryption;

public class RepositoryQueryManager extends QueryManager implements IQueryManager {
private static final Logger LOGGER = Logger.getLogger(RepositoryQueryManager.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,10 @@ protected CloseableHttpResponse processHttpRequest(String url) throws IOExceptio
URIBuilder uriBuilder = new URIBuilder(url);
final HttpUriRequest request = new HttpGet(uriBuilder.build().toString());
request.addHeader("accept", "application/json");
if (username != null || password != null) {
if (!StringUtils.isEmpty(username)) { // for some reason there is a testcase for password being null
request.addHeader("Authorization", HttpUtil.basicAuthHeaderValue(username, password));
} else if (!StringUtils.isEmpty(password)) {
request.addHeader("Authorization", "Bearer " + password);
}
return HttpClientPool.getClient().execute(request);
} catch (URISyntaxException ex) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/dependencytrack/util/HttpUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
*/
package org.dependencytrack.util;

import static org.apache.http.HttpHeaders.AUTHORIZATION;

import java.util.Base64;
import java.util.Objects;

import static org.apache.http.HttpHeaders.AUTHORIZATION;

public final class HttpUtil {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
*/
package org.dependencytrack.resources.v1;

import alpine.server.filters.ApiFilter;
import alpine.server.filters.AuthenticationFilter;
import java.util.Date;
import java.util.List;

import org.dependencytrack.JerseyTestRule;
import org.dependencytrack.ResourceTest;
import org.dependencytrack.model.Repository;
Expand All @@ -33,13 +34,13 @@
import org.junit.ClassRule;
import org.junit.Test;

import alpine.server.filters.ApiFilter;
import alpine.server.filters.AuthenticationFilter;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.Date;
import java.util.List;

public class RepositoryResourceTest extends ResourceTest {

Expand Down Expand Up @@ -173,7 +174,7 @@ public void getRepositoryMetaUntrackedComponentTest() {


@Test
public void createRepositoryTest() {
public void createRepositoryTestWithBasicAuth() {
Repository repository = new Repository();
repository.setAuthenticationRequired(true);
repository.setEnabled(true);
Expand Down Expand Up @@ -203,6 +204,40 @@ public void createRepositoryTest() {
Assert.assertTrue(json.getJsonObject(13).getBoolean("enabled"));
}

@Test
public void createRepositoryTestWithBearerAuth() {
//Password field gets ignored during json serialization, so create the json ourselves
String repo = """
{
"identifier":"test2",
"url":"https://www.foobar2.com",
"internal":true,
"authenticationRequired":true,
"password":"letoken",
"enabled":true,
"type":"MAVEN"
}
""";

Response response = jersey.target(V1_REPOSITORY).request().header(X_API_KEY, apiKey)
.put(Entity.entity(repo, MediaType.APPLICATION_JSON));
Assert.assertEquals(201, response.getStatus());

response = jersey.target(V1_REPOSITORY).request().header(X_API_KEY, apiKey).get(Response.class);
Assert.assertEquals(200, response.getStatus(), 0);
Assert.assertEquals(String.valueOf(18), response.getHeaderString(TOTAL_COUNT_HEADER));
JsonArray json = parseJsonArray(response);
Assert.assertNotNull(json);
Assert.assertEquals(18, json.size());
Assert.assertEquals("MAVEN", json.getJsonObject(13).getString("type"));
Assert.assertEquals("test2", json.getJsonObject(13).getString("identifier"));
Assert.assertEquals("https://www.foobar2.com", json.getJsonObject(13).getString("url"));
Assert.assertTrue(json.getJsonObject(13).getInt("resolutionOrder") > 0);
Assert.assertTrue(json.getJsonObject(13).getBoolean("authenticationRequired"));
Assert.assertFalse(json.getJsonObject(13).containsKey("username"));
Assert.assertTrue(json.getJsonObject(13).getBoolean("enabled"));
}

@Test
public void createNonInternalRepositoryTest() {
Repository repository = new Repository();
Expand Down Expand Up @@ -262,7 +297,6 @@ public void createRepositoryAuthFalseTest() {
Assert.assertTrue(json.getJsonObject(13).getInt("resolutionOrder") > 0);
Assert.assertFalse(json.getJsonObject(13).getBoolean("authenticationRequired"));
Assert.assertTrue(json.getJsonObject(13).getBoolean("enabled"));

}

@Test
Expand Down Expand Up @@ -298,6 +332,5 @@ public void updateRepositoryTest() throws Exception {
}
}
}

}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.dependencytrack.tasks;

import alpine.event.framework.EventService;
import com.github.packageurl.PackageURL;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.http.Body;
import com.github.tomakehurst.wiremock.http.ContentTypeHeader;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import static com.github.tomakehurst.wiremock.client.WireMock.containing;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import org.dependencytrack.PersistenceCapableTest;
import org.dependencytrack.event.RepositoryMetaEvent;
import org.dependencytrack.model.Component;
Expand All @@ -19,12 +19,14 @@
import org.junit.Rule;
import org.junit.Test;

import jakarta.ws.rs.core.MediaType;
import java.util.List;
import com.github.packageurl.PackageURL;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.http.Body;
import com.github.tomakehurst.wiremock.http.ContentTypeHeader;
import com.github.tomakehurst.wiremock.junit.WireMockRule;

import static com.github.tomakehurst.wiremock.client.WireMock.containing;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
import alpine.event.framework.EventService;
import jakarta.ws.rs.core.MediaType;

public class RepoMetaAnalysisTaskTest extends PersistenceCapableTest {

Expand Down Expand Up @@ -70,7 +72,7 @@ public void informTestNullPassword() throws Exception {
</versions>
<lastUpdated>20210213164433</lastUpdated>
</versioning>
</metadata>
</metadata>
""".getBytes(), new ContentTypeHeader(MediaType.APPLICATION_JSON))
)
.withHeader("X-CheckSum-MD5", "md5hash")
Expand All @@ -94,7 +96,7 @@ public void informTestNullPassword() throws Exception {

@Test
public void informTestNullUserName() throws Exception {
WireMock.stubFor(WireMock.get(WireMock.anyUrl()).withHeader("Authorization", containing("Basic"))
WireMock.stubFor(WireMock.get(WireMock.anyUrl()).withHeader("Authorization", containing("Bearer"))
.willReturn(WireMock.aResponse()
.withStatus(200)
.withResponseBody(Body.ofBinaryOrText("""
Expand All @@ -116,7 +118,7 @@ public void informTestNullUserName() throws Exception {
</versions>
<lastUpdated>20210213164433</lastUpdated>
</versioning>
</metadata>
</metadata>
""".getBytes(), new ContentTypeHeader(MediaType.APPLICATION_JSON))
)
.withHeader("X-CheckSum-MD5", "md5hash")
Expand Down Expand Up @@ -162,7 +164,7 @@ public void informTestNullUserNameAndPassword() throws Exception {
</versions>
<lastUpdated>20210213164433</lastUpdated>
</versioning>
</metadata>
</metadata>
""".getBytes(), new ContentTypeHeader(MediaType.APPLICATION_JSON))
)
.withHeader("X-CheckSum-MD5", "md5hash")
Expand All @@ -186,7 +188,7 @@ public void informTestNullUserNameAndPassword() throws Exception {

@Test
public void informTestUserNameAndPassword() throws Exception {
WireMock.stubFor(WireMock.get(WireMock.anyUrl())
WireMock.stubFor(WireMock.get(WireMock.anyUrl()).withHeader("Authorization", containing("Basic"))
.willReturn(WireMock.aResponse()
.withStatus(200)
.withResponseBody(Body.ofBinaryOrText("""
Expand All @@ -208,7 +210,7 @@ public void informTestUserNameAndPassword() throws Exception {
</versions>
<lastUpdated>20210213164433</lastUpdated>
</versioning>
</metadata>
</metadata>
""".getBytes(), new ContentTypeHeader(MediaType.APPLICATION_JSON))
)
.withHeader("X-CheckSum-MD5", "md5hash")
Expand All @@ -229,4 +231,51 @@ public void informTestUserNameAndPassword() throws Exception {
qm.getPersistenceManager().refresh(metaComponent);
assertThat(metaComponent.getLatestVersion()).isEqualTo("4.13.2");
}

@Test
public void informTestBearerToken() throws Exception {
WireMock.stubFor(WireMock.get(WireMock.anyUrl()).withHeader("Authorization", containing("Bearer"))
.willReturn(WireMock.aResponse()
.withStatus(200)
.withResponseBody(Body.ofBinaryOrText("""
<metadata>
<groupId>test4</groupId>
<artifactId>test4</artifactId>
<versioning>
<latest>5.13.2</latest>
<release>5.13.2</release>
<versions>
<version>5.13-beta-1</version>
<version>5.13-beta-2</version>
<version>5.13-beta-3</version>
<version>5.13-rc-1</version>
<version>5.13-rc-2</version>
<version>5.13</version>
<version>5.13.1</version>
<version>5.13.2</version>
</versions>
<lastUpdated>20210213164433</lastUpdated>
</versioning>
</metadata>
""".getBytes(), new ContentTypeHeader(MediaType.APPLICATION_JSON))
)
.withHeader("X-CheckSum-MD5", "md5hash")
.withHeader("X-Checksum-SHA1", "sha1hash")
.withHeader("X-Checksum-SHA512", "sha512hash")
.withHeader("X-Checksum-SHA256", "sha256hash")
.withHeader("Last-Modified", "Thu, 07 Jul 2022 14:00:00 GMT")));
EventService.getInstance().subscribe(RepositoryMetaEvent.class, RepositoryMetaAnalyzerTask.class);
Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false);
Component component = new Component();
component.setProject(project);
component.setName("test3");
component.setPurl(new PackageURL("pkg:maven/test4/test4@5.12"));
qm.createComponent(component, false);
qm.createRepository(RepositoryType.MAVEN, "test", wireMockRule.baseUrl(), true, false, true, null, "testPassword");
new RepositoryMetaAnalyzerTask().inform(new RepositoryMetaEvent(List.of(component)));
RepositoryMetaComponent metaComponent = qm.getRepositoryMetaComponent(RepositoryType.MAVEN, "test4", "test4");
qm.getPersistenceManager().refresh(metaComponent);
assertThat(metaComponent.getLatestVersion()).isEqualTo("5.13.2");
}

}
Loading
Loading