@@ -34,11 +34,15 @@ import (
3434 "deps.dev/util/semver"
3535 "github.com/google/osv-scalibr/log"
3636 "golang.org/x/net/html/charset"
37+ "golang.org/x/oauth2/google"
3738)
3839
3940// mavenCentral holds the URL of Maven Central Repository.
4041const mavenCentral = "https://repo.maven.apache.org/maven2"
4142
43+ // artifactRegistryScheme defines the scheme for Google Artifact Registry.
44+ const artifactRegistryScheme = "artifactregistry"
45+
4246var errAPIFailed = errors .New ("API query failed" )
4347
4448// MavenRegistryAPIClient defines a client to fetch metadata from a Maven registry.
@@ -47,6 +51,7 @@ type MavenRegistryAPIClient struct {
4751 registries []MavenRegistry // Additional registries specified to fetch projects
4852 registryAuths map [string ]* HTTPAuthentication // Authentication for the registries keyed by registry ID. From settings.xml
4953 localRegistry string // The local directory that holds Maven manifests
54+ googleClient * http.Client // A client for authenticating with Google services, used for Artifact Registry.
5055
5156 // Cache fields
5257 mu * sync.Mutex
@@ -71,7 +76,7 @@ type MavenRegistry struct {
7176}
7277
7378// NewMavenRegistryAPIClient returns a new MavenRegistryAPIClient.
74- func NewMavenRegistryAPIClient (registry MavenRegistry , localRegistry string ) (* MavenRegistryAPIClient , error ) {
79+ func NewMavenRegistryAPIClient (ctx context. Context , registry MavenRegistry , localRegistry string ) (* MavenRegistryAPIClient , error ) {
7580 if registry .URL == "" {
7681 registry .URL = mavenCentral
7782 registry .ID = "central"
@@ -90,14 +95,18 @@ func NewMavenRegistryAPIClient(registry MavenRegistry, localRegistry string) (*M
9095 globalSettings := ParseMavenSettings (globalMavenSettingsFile ())
9196 userSettings := ParseMavenSettings (userMavenSettingsFile ())
9297
93- return & MavenRegistryAPIClient {
98+ client := & MavenRegistryAPIClient {
9499 // We assume only downloading releases is allowed on the default registry.
95100 defaultRegistry : registry ,
96101 localRegistry : localRegistry ,
97102 mu : & sync.Mutex {},
98103 responses : NewRequestCache [string , response ](),
99104 registryAuths : MakeMavenAuth (globalSettings , userSettings ),
100- }, nil
105+ }
106+ if registry .Parsed .Scheme == artifactRegistryScheme {
107+ client .createGoogleClient (ctx )
108+ }
109+ return client , nil
101110}
102111
103112// SetLocalRegistry sets the local directory that stores the downloaded Maven manifests.
@@ -114,13 +123,14 @@ func (m *MavenRegistryAPIClient) WithoutRegistries() *MavenRegistryAPIClient {
114123 cacheTimestamp : m .cacheTimestamp ,
115124 responses : m .responses ,
116125 registryAuths : m .registryAuths ,
126+ googleClient : m .googleClient ,
117127 }
118128}
119129
120130// AddRegistry adds the given registry to the list of registries if it has not been added.
121- func (m * MavenRegistryAPIClient ) AddRegistry (registry MavenRegistry ) error {
131+ func (m * MavenRegistryAPIClient ) AddRegistry (ctx context. Context , registry MavenRegistry ) error {
122132 if registry .ID == m .defaultRegistry .ID {
123- return m .updateDefaultRegistry (registry )
133+ return m .updateDefaultRegistry (ctx , registry )
124134 }
125135
126136 for _ , reg := range m .registries {
@@ -136,20 +146,42 @@ func (m *MavenRegistryAPIClient) AddRegistry(registry MavenRegistry) error {
136146
137147 registry .Parsed = u
138148 m .registries = append (m .registries , registry )
149+ if registry .Parsed .Scheme == artifactRegistryScheme {
150+ m .createGoogleClient (ctx )
151+ }
139152
140153 return nil
141154}
142155
143- func (m * MavenRegistryAPIClient ) updateDefaultRegistry (registry MavenRegistry ) error {
156+ func (m * MavenRegistryAPIClient ) updateDefaultRegistry (ctx context. Context , registry MavenRegistry ) error {
144157 u , err := url .Parse (registry .URL )
145158 if err != nil {
146159 return err
147160 }
148161 registry .Parsed = u
149162 m .defaultRegistry = registry
163+ if registry .Parsed .Scheme == artifactRegistryScheme {
164+ m .createGoogleClient (ctx )
165+ }
150166 return nil
151167}
152168
169+ // createGoogleClient creates a client for authenticating with Google services.
170+ func (m * MavenRegistryAPIClient ) createGoogleClient (ctx context.Context ) {
171+ if m .googleClient != nil {
172+ return
173+ }
174+ // This is the scope that artifact-registry-go-tools uses.
175+ // https://github.com/GoogleCloudPlatform/artifact-registry-go-tools/blob/main/pkg/auth/auth.go
176+ client , err := google .DefaultClient (ctx , "https://www.googleapis.com/auth/cloud-platform" )
177+ if err != nil {
178+ // We don't return an error here so that we can fall back to a regular http client.
179+ log .Warnf ("failed to create Google default client, Artifact Registry access will be unavailable: %v" , err )
180+ return
181+ }
182+ m .googleClient = client
183+ }
184+
153185// GetRegistries returns the registries added to this client.
154186func (m * MavenRegistryAPIClient ) GetRegistries () (registries []MavenRegistry ) {
155187 return m .registries
@@ -269,17 +301,28 @@ func (m *MavenRegistryAPIClient) get(ctx context.Context, auth *HTTPAuthenticati
269301 }
270302 }
271303
272- u := registry .Parsed .JoinPath (paths ... ).String ()
304+ httpClient := http .DefaultClient
305+ requestURL := * registry .Parsed
306+ isArtifactRegistry := requestURL .Scheme == artifactRegistryScheme
307+ if isArtifactRegistry {
308+ requestURL .Scheme = "https"
309+ // For Artifact Registry, use google.DefaultClient for ADC if available.
310+ if m .googleClient != nil {
311+ httpClient = m .googleClient
312+ }
313+ }
314+
315+ u := requestURL .JoinPath (paths ... ).String ()
273316 resp , err := m .responses .Get (u , func () (response , error ) {
274317 log .Infof ("Fetching response from: %s" , u )
275- resp , err := auth .Get (ctx , http . DefaultClient , u )
318+ resp , err := auth .Get (ctx , httpClient , u )
276319 if err != nil {
277320 return response {}, fmt .Errorf ("%w: Maven registry query failed: %w" , errAPIFailed , err )
278321 }
279322 defer resp .Body .Close ()
280323
281- if ! slices .Contains ([]int {http .StatusOK , http .StatusNotFound , http .StatusUnauthorized }, resp .StatusCode ) {
282- // Only cache responses with Status OK, NotFound, or Unauthorized
324+ if ! slices .Contains ([]int {http .StatusOK , http .StatusNotFound , http .StatusUnauthorized , http . StatusForbidden }, resp .StatusCode ) {
325+ // Only cache responses with Status OK, NotFound, Unauthorized, or Forbidden
283326 return response {}, fmt .Errorf ("%w: Maven registry query status: %d" , errAPIFailed , resp .StatusCode )
284327 }
285328
@@ -301,6 +344,10 @@ func (m *MavenRegistryAPIClient) get(ctx context.Context, auth *HTTPAuthenticati
301344 return err
302345 }
303346
347+ if resp .StatusCode == http .StatusForbidden && isArtifactRegistry {
348+ return fmt .Errorf ("%w: Maven registry query status: %d (Forbidden). Please check your Application Default Credentials (ADC) have permission to read from %s" , errAPIFailed , resp .StatusCode , registry .URL )
349+ }
350+
304351 if resp .StatusCode != http .StatusOK {
305352 return fmt .Errorf ("%w: Maven registry query status: %d" , errAPIFailed , resp .StatusCode )
306353 }
0 commit comments