Skip to content

Commit 1437d92

Browse files
authored
Revert "Revert "Migrate npm handler to OIDCRegistry (#91)" (#94)"
This reverts commit 3ff701f.
1 parent 3ff701f commit 1437d92

File tree

2 files changed

+63
-43
lines changed

2 files changed

+63
-43
lines changed

internal/handlers/npm_registry.go

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package handlers
22

33
import (
4-
"fmt"
54
"net/http"
65
"strings"
7-
"sync"
86

97
"github.com/elazarl/goproxy"
108

@@ -17,9 +15,8 @@ import (
1715
// NPMRegistryHandler handles requests to NPM registries, adding auth to
1816
// requests to registries for which we have credentials.
1917
type NPMRegistryHandler struct {
20-
credentials []npmRegistryCredentials
21-
oidcCredentials map[string]*oidc.OIDCCredential
22-
mutex sync.RWMutex
18+
credentials []npmRegistryCredentials
19+
oidcRegistry *oidc.OIDCRegistry
2320
}
2421

2522
type npmRegistryCredentials struct {
@@ -33,8 +30,8 @@ type npmRegistryCredentials struct {
3330
// NewNPMRegistryHandler returns a new NPMRegistryHandler,
3431
func NewNPMRegistryHandler(creds config.Credentials) *NPMRegistryHandler {
3532
handler := NPMRegistryHandler{
36-
credentials: []npmRegistryCredentials{},
37-
oidcCredentials: make(map[string]*oidc.OIDCCredential),
33+
credentials: []npmRegistryCredentials{},
34+
oidcRegistry: oidc.NewOIDCRegistry(),
3835
}
3936

4037
for _, cred := range creds {
@@ -44,19 +41,8 @@ func NewNPMRegistryHandler(creds config.Credentials) *NPMRegistryHandler {
4441

4542
registry := cred.GetString("registry")
4643

47-
oidcCredential, _ := oidc.CreateOIDCCredential(cred)
48-
if oidcCredential != nil {
49-
host := cred.Host()
50-
if host == "" && registry != "" {
51-
regURL, err := helpers.ParseURLLax(registry)
52-
if err == nil {
53-
host = regURL.Hostname()
54-
}
55-
}
56-
if host != "" {
57-
handler.oidcCredentials[host] = oidcCredential
58-
logging.RequestLogf(nil, "registered %s OIDC credentials for npm registry: %s", oidcCredential.Provider(), host)
59-
}
44+
// OIDC credentials are not used as static credentials.
45+
if oidcCred, _, _ := handler.oidcRegistry.Register(cred, []string{"registry", "url"}, "npm registry"); oidcCred != nil {
6046
continue
6147
}
6248

@@ -86,25 +72,8 @@ func (h *NPMRegistryHandler) HandleRequest(req *http.Request, ctx *goproxy.Proxy
8672
}
8773

8874
// Try OIDC credentials first
89-
h.mutex.RLock()
90-
oidcCred, hasOIDC := h.oidcCredentials[reqHost]
91-
h.mutex.RUnlock()
92-
93-
if hasOIDC {
94-
token, err := oidc.GetOrRefreshOIDCToken(oidcCred, req.Context())
95-
if err != nil {
96-
logging.RequestLogf(ctx, "* failed to get token via OIDC for %s: %v", reqHost, err)
97-
// Fall through to try static credentials
98-
} else {
99-
if oidcCred.Provider() == "cloudsmith" {
100-
logging.RequestLogf(ctx, "* authenticating npm registry request with OIDC API key (host: %s)", reqHost)
101-
req.Header.Set("X-Api-Key", token)
102-
} else {
103-
logging.RequestLogf(ctx, "* authenticating npm registry request with OIDC token (host: %s)", reqHost)
104-
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
105-
}
106-
return req, nil
107-
}
75+
if h.oidcRegistry.TryAuth(req, ctx) {
76+
return req, nil
10877
}
10978

11079
// Fall back to static credentials

internal/handlers/oidc_handling_test.go

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ func TestOIDCURLsAreAuthenticated(t *testing.T) {
721721
},
722722
urlMocks: []mockHttpRequest{},
723723
expectedLogLines: []string{
724-
"registered aws OIDC credentials for npm registry: npm.example.com",
724+
"registered aws OIDC credentials for npm registry: https://npm.example.com",
725725
},
726726
urlsToAuthenticate: []string{
727727
"https://npm.example.com/some-package",
@@ -743,7 +743,7 @@ func TestOIDCURLsAreAuthenticated(t *testing.T) {
743743
},
744744
urlMocks: []mockHttpRequest{},
745745
expectedLogLines: []string{
746-
"registered azure OIDC credentials for npm registry: npm.example.com",
746+
"registered azure OIDC credentials for npm registry: https://npm.example.com",
747747
},
748748
urlsToAuthenticate: []string{
749749
"https://npm.example.com/some-package",
@@ -764,7 +764,7 @@ func TestOIDCURLsAreAuthenticated(t *testing.T) {
764764
},
765765
urlMocks: []mockHttpRequest{},
766766
expectedLogLines: []string{
767-
"registered jfrog OIDC credentials for npm registry: jfrog.example.com",
767+
"registered jfrog OIDC credentials for npm registry: https://jfrog.example.com",
768768
},
769769
urlsToAuthenticate: []string{
770770
"https://jfrog.example.com/some-package",
@@ -787,7 +787,7 @@ func TestOIDCURLsAreAuthenticated(t *testing.T) {
787787
},
788788
urlMocks: []mockHttpRequest{},
789789
expectedLogLines: []string{
790-
"registered cloudsmith OIDC credentials for npm registry: cloudsmith.example.com",
790+
"registered cloudsmith OIDC credentials for npm registry: https://cloudsmith.example.com",
791791
},
792792
urlsToAuthenticate: []string{
793793
"https://cloudsmith.example.com/some-package",
@@ -1441,3 +1441,54 @@ func TestPythonOIDCSimpleSuffixStripping(t *testing.T) {
14411441
reqB = handleRequestAndClose(handler, reqB, nil)
14421442
assertHasTokenAuth(t, reqB, "Bearer", "__token_B__", "feed-B request should use token B")
14431443
}
1444+
1445+
// TestNPMOIDCSameHostDifferentPaths verifies that two npm OIDC credentials on
1446+
// the same host with different URL paths do not collide — each request is
1447+
// authenticated with the credential whose path is the longest prefix match.
1448+
func TestNPMOIDCSameHostDifferentPaths(t *testing.T) {
1449+
httpmock.Activate()
1450+
defer httpmock.DeactivateAndReset()
1451+
1452+
tenantA := "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
1453+
tenantB := "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
1454+
clientId := "87654321-4321-4321-4321-210987654321"
1455+
1456+
tokenUrl := "https://token.actions.example.com" //nolint:gosec // test URL
1457+
httpmock.RegisterResponder("GET", tokenUrl,
1458+
httpmock.NewStringResponder(200, `{"count":1,"value":"sometoken"}`))
1459+
1460+
httpmock.RegisterResponder("POST", fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenantA),
1461+
httpmock.NewStringResponder(200, `{"access_token":"__token_A__","expires_in":3600,"token_type":"Bearer"}`))
1462+
httpmock.RegisterResponder("POST", fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenantB),
1463+
httpmock.NewStringResponder(200, `{"access_token":"__token_B__","expires_in":3600,"token_type":"Bearer"}`))
1464+
1465+
t.Setenv("ACTIONS_ID_TOKEN_REQUEST_URL", tokenUrl)
1466+
t.Setenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN", "sometoken")
1467+
1468+
creds := config.Credentials{
1469+
config.Credential{
1470+
"type": "npm_registry",
1471+
"url": "https://pkgs.example.com/org/feed-A",
1472+
"tenant-id": tenantA,
1473+
"client-id": clientId,
1474+
},
1475+
config.Credential{
1476+
"type": "npm_registry",
1477+
"url": "https://pkgs.example.com/org/feed-B",
1478+
"tenant-id": tenantB,
1479+
"client-id": clientId,
1480+
},
1481+
}
1482+
1483+
handler := NewNPMRegistryHandler(creds)
1484+
1485+
// Request to feed-A path should get token A
1486+
reqA := httptest.NewRequest("GET", "https://pkgs.example.com/org/feed-A/some-package", nil)
1487+
reqA = handleRequestAndClose(handler, reqA, nil)
1488+
assertHasTokenAuth(t, reqA, "Bearer", "__token_A__", "feed-A should use token A")
1489+
1490+
// Request to feed-B path should get token B
1491+
reqB := httptest.NewRequest("GET", "https://pkgs.example.com/org/feed-B/some-package", nil)
1492+
reqB = handleRequestAndClose(handler, reqB, nil)
1493+
assertHasTokenAuth(t, reqB, "Bearer", "__token_B__", "feed-B should use token B")
1494+
}

0 commit comments

Comments
 (0)