Skip to content

Commit

Permalink
Maven/Gradle: Add option to use Gitlab access token for authenticatio…
Browse files Browse the repository at this point in the history
…n against maven repositories (#3403)

* Added option to define custom_header

* custom_headers added to metadata_finder for Maven

* custom_headers option added to gradle module

* Changed implementation to find host in git_sources

* Rubocop offenses cleaned

* Reverted formatting change

* Added check to only use private-token header for gitlab repos

* Gradle: Added check to only use private-token header for gitlab repos
Refactoring: extracted auth details into separate class

* Added tests for auth_details_finder classes

* Update gradle/spec/dependabot/gradle/utils/auth_details_finder_spec.rb

Co-authored-by: mo khan <[email protected]>

* Using Base64.strict_encode64 to encode auth token

* Regex update

* renaming auth_details->auth_headers to reflect what it is

* Added maven module as dependency on gradle. Code cleanup.

* Added tests with multiple defined maven repositories, and reverted change to memoize result as multiple calls will be made

* Test adjusted to only return found from one repo

Co-authored-by: Niels Wittrup Andersen <[email protected]>
Co-authored-by: mo khan <[email protected]>
  • Loading branch information
3 people authored Apr 12, 2021
1 parent deb7826 commit b0416b0
Show file tree
Hide file tree
Showing 11 changed files with 566 additions and 59 deletions.
1 change: 1 addition & 0 deletions gradle/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
source "https://rubygems.org"

gem "dependabot-common", path: "../common"
gem "dependabot-maven", path: "../maven"

gemspec
22 changes: 5 additions & 17 deletions gradle/lib/dependabot/gradle/metadata_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "dependabot/metadata_finders/base"
require "dependabot/file_fetchers/base"
require "dependabot/gradle/file_parser/repositories_finder"
require "dependabot/maven/utils/auth_headers_finder"

module Dependabot
module Gradle
Expand Down Expand Up @@ -112,7 +113,7 @@ def dependency_pom_file
"#{dependency.version}/"\
"#{artifact_id}-#{dependency.version}.pom",
idempotent: true,
**SharedHelpers.excon_defaults(headers: auth_details)
**SharedHelpers.excon_defaults(headers: auth_headers)
)

@dependency_pom_file = Nokogiri::XML(response.body)
Expand All @@ -135,7 +136,7 @@ def parent_pom_file(pom)
"#{version}/"\
"#{artifact_id}-#{version}.pom",
idempotent: true,
**SharedHelpers.excon_defaults(headers: auth_details)
**SharedHelpers.excon_defaults(headers: auth_headers)
)

Nokogiri::XML(response.body)
Expand Down Expand Up @@ -170,21 +171,8 @@ def kotlin_plugin?
plugin? && dependency.requirements.any? { |r| r.fetch(:groups).include? "kotlin" }
end

def auth_details
cred =
credentials.select { |c| c["type"] == "maven_repository" }.
find do |c|
cred_url = c.fetch("url").gsub(%r{/+$}, "")
next false unless cred_url == maven_repo_url

c.fetch("username", nil)
end

return {} unless cred

token = cred.fetch("username") + ":" + cred.fetch("password")
encoded_token = Base64.encode64(token).delete("\n")
{ "Authorization" => "Basic #{encoded_token}" }
def auth_headers
@auth_headers ||= Dependabot::Maven::Utils::AuthHeadersFinder.new(credentials).auth_headers(maven_repo_url)
end
end
end
Expand Down
27 changes: 16 additions & 11 deletions gradle/lib/dependabot/gradle/update_checker/version_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require "dependabot/gradle/update_checker"
require "dependabot/gradle/version"
require "dependabot/gradle/requirement"
require "dependabot/maven/utils/auth_headers_finder"

module Dependabot
module Gradle
Expand Down Expand Up @@ -184,10 +185,8 @@ def dependency_metadata(repository_details)
begin
response = Excon.get(
dependency_metadata_url(repository_details.fetch("url")),
user: repository_details.fetch("username"),
password: repository_details.fetch("password"),
idempotent: true,
**SharedHelpers.excon_defaults
**Dependabot::SharedHelpers.excon_defaults(headers: repository_details.fetch("auth_headers"))
)
check_response(response, repository_details.fetch("url"))
Nokogiri::XML(response.body)
Expand Down Expand Up @@ -226,10 +225,10 @@ def repositories

@repositories =
details.reject do |repo|
next if repo["password"]
next if repo["auth_headers"]

# Reject this entry if an identical one with a password exists
details.any? { |r| r["url"] == repo["url"] && r["password"] }
# Reject this entry if an identical one with non-empty auth_headers exists
details.any? { |r| r["url"] == repo["url"] && r["auth_headers"] != {} }
end
end

Expand All @@ -239,8 +238,7 @@ def credentials_repository_details
map do |cred|
{
"url" => cred.fetch("url").gsub(%r{/+$}, ""),
"username" => cred.fetch("username", nil),
"password" => cred.fetch("password", nil)
"auth_headers" => auth_headers(cred.fetch("url").gsub(%r{/+$}, ""))
}
end
end
Expand All @@ -258,16 +256,15 @@ def dependency_repository_details
target_dependency_file: target_file
).repository_urls.
map do |url|
{ "url" => url, "username" => nil, "password" => nil }
{ "url" => url, "auth_headers" => {} }
end
end.uniq
end

def plugin_repository_details
[{
"url" => GRADLE_PLUGINS_REPO,
"username" => nil,
"password" => nil
"auth_headers" => {}
}] + dependency_repository_details
end

Expand Down Expand Up @@ -333,6 +330,14 @@ def central_repo_urls
def version_class
Gradle::Version
end

def auth_headers_finder
@auth_headers_finder ||= Dependabot::Maven::Utils::AuthHeadersFinder.new(credentials)
end

def auth_headers(maven_repo_url)
auth_headers_finder.auth_headers(maven_repo_url)
end
end
end
end
Expand Down
72 changes: 72 additions & 0 deletions gradle/spec/dependabot/gradle/metadata_finder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,78 @@
end
end

context "when using a gitlab maven repository" do
let(:dependency_source) do
{ type: "maven_repo", url: "https://gitlab.com/api/v4/groups/some-group/-/packages/maven" }
end
let(:maven_url) do
"https://gitlab.com/api/v4/groups/some-group/-/packages/maven/com/google/guava/"\
"guava/23.3-jre/guava-23.3-jre.pom"
end
let(:maven_response) do
fixture("poms", "mockito-core-2.11.0.xml")
end

before do
stub_request(:get, maven_url).
to_return(status: 200, body: maven_response)
end
it { is_expected.to eq("https://github.com/mockito/mockito") }

context "with credentials" do
let(:credentials) do
[
{
"type" => "git_source",
"host" => "gitlab.com",
"username" => "x-access-token",
"password" => "token"
},
{
"type" => "maven_repository",
"url" => "https://gitlab.com/api/v4/groups/some-group/-/packages/maven"
}
]
end

before do
stub_request(:get, maven_url).to_return(status: 404)
stub_request(:get, maven_url).
with(headers: { "Private-Token" => "token" }).
to_return(status: 200, body: maven_response)
end

it { is_expected.to eq("https://github.com/mockito/mockito") }

context "that include a username and password" do
let(:credentials) do
[
{
"type" => "git_source",
"host" => "gitlab.com",
"username" => "x-access-token",
"password" => "token"
},
{
"type" => "maven_repository",
"url" => "https://gitlab.com/api/v4/groups/some-group/-/packages/maven",
"username" => "dependabot",
"password" => "dependabotPassword"
}
]
end
before do
stub_request(:get, maven_url).to_return(status: 404)
stub_request(:get, maven_url).
with(basic_auth: %w(dependabot dependabotPassword)).
to_return(status: 200, body: maven_response)
end

it { is_expected.to eq("https://github.com/mockito/mockito") }
end
end
end

context "when the Maven link resolves to a redirect" do
let(:redirect_url) do
"https://repo1.maven.org/maven2/org/mockito/mockito-core/2.11.0/"\
Expand Down
102 changes: 102 additions & 0 deletions gradle/spec/dependabot/gradle/update_checker/version_finder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,41 @@
is_expected.to eq("https://private.registry.org/repo")
end

context "that is a gitlab maven repository" do
let(:credentials) do
[
{
"type" => "maven_repository",
"url" => "https://private.registry.org/api/v4/groups/-/packages/maven/"
},
{
"type" => "git_source",
"host" => "private.registry.org",
"username" => "x-access-token",
"password" => "customToken"
}
]
end

let(:private_registry_metadata_url) do
"https://private.registry.org/api/v4/groups/-/packages/maven/"\
"com/google/guava/guava/maven-metadata.xml"
end

before do
stub_request(:get, maven_central_metadata_url).
to_return(status: 404)
stub_request(:get, private_registry_metadata_url).
with(headers: { "Private-Token" => "customToken" }).
to_return(status: 200, body: maven_central_releases)
end

its([:version]) { is_expected.to eq(version_class.new("23.6-jre")) }
its([:source_url]) do
is_expected.to eq("https://private.registry.org/api/v4/groups/-/packages/maven")
end
end

context "but no auth details" do
let(:credentials) do
[{
Expand Down Expand Up @@ -248,6 +283,73 @@
end
end

context "with multiple repositories from credentials" do
let(:credentials) do
[
{
"type" => "maven_repository",
"url" => "https://private.registry.org/repo/",
"username" => "dependabot",
"password" => "dependabotPassword"
},
{
"type" => "maven_repository",
"url" => "https://private.registry.org/repo/"
},
{
"type" => "maven_repository",
"url" => "https://private.registry.org/repo2/",
"username" => "dependabot2",
"password" => "dependabotPassword2"
},
{
"type" => "maven_repository",
"url" => "https://private.registry.org/api/v4/groups/-/packages/maven/"
},
{
"type" => "git_source",
"host" => "private.registry.org",
"username" => "x-access-token",
"password" => "customToken"
}
]
end

let(:private_registry_metadata_url) do
"https://private.registry.org/repo/"\
"com/google/guava/guava/maven-metadata.xml"
end

let(:second_repo) do
"https://private.registry.org/repo2/"\
"com/google/guava/guava/maven-metadata.xml"
end

let(:gitlab_maven_repo) do
"https://private.registry.org/api/v4/groups/-/packages/maven/"\
"com/google/guava/guava/maven-metadata.xml"
end

before do
stub_request(:get, maven_central_metadata_url).
to_return(status: 404)
stub_request(:get, second_repo).
with(basic_auth: %w(dependabot2 dependabotPassword2)).
to_return(status: 404)
stub_request(:get, gitlab_maven_repo).
with(headers: { "Private-Token" => "customToken" }).
to_return(status: 404)
stub_request(:get, private_registry_metadata_url).
with(basic_auth: %w(dependabot dependabotPassword)).
to_return(status: 200, body: maven_central_releases)
end

its([:version]) { is_expected.to eq(version_class.new("23.6-jre")) }
its([:source_url]) do
is_expected.to eq("https://private.registry.org/repo")
end
end

context "with a plugin from credentials" do
let(:dependency_requirements) do
[{
Expand Down
22 changes: 5 additions & 17 deletions maven/lib/dependabot/maven/metadata_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require "dependabot/file_fetchers/base"
require "dependabot/maven/file_parser"
require "dependabot/maven/file_parser/repositories_finder"
require "dependabot/maven/utils/auth_headers_finder"

module Dependabot
module Maven
Expand Down Expand Up @@ -104,7 +105,7 @@ def dependency_pom_file
"#{dependency.version}/"\
"#{dependency_artifact_id}-#{dependency.version}.pom",
idempotent: true,
**SharedHelpers.excon_defaults(headers: auth_details)
**SharedHelpers.excon_defaults(headers: auth_headers)
)

@dependency_pom_file = Nokogiri::XML(response.body)
Expand Down Expand Up @@ -135,7 +136,7 @@ def parent_pom_file(pom)
response = Excon.get(
substitute_properties_in_source_url(url, pom),
idempotent: true,
**SharedHelpers.excon_defaults(headers: auth_details)
**SharedHelpers.excon_defaults(headers: auth_headers)
)

Nokogiri::XML(response.body)
Expand All @@ -156,21 +157,8 @@ def maven_repo_dependency_url
"#{maven_repo_url}/#{group_id.tr('.', '/')}/#{artifact_id}"
end

def auth_details
cred =
credentials.select { |c| c["type"] == "maven_repository" }.
find do |c|
cred_url = c.fetch("url").gsub(%r{/+$}, "")
next false unless cred_url == maven_repo_url

c.fetch("username", nil)
end

return {} unless cred

token = cred.fetch("username") + ":" + cred.fetch("password")
encoded_token = Base64.encode64(token).delete("\n")
{ "Authorization" => "Basic #{encoded_token}" }
def auth_headers
@auth_headers ||= Utils::AuthHeadersFinder.new(credentials).auth_headers(maven_repo_url)
end
end
end
Expand Down
Loading

0 comments on commit b0416b0

Please sign in to comment.