diff --git a/docs/reference/ocm_credential-handling.md b/docs/reference/ocm_credential-handling.md index 28e70c6c1..32dc2e39f 100644 --- a/docs/reference/ocm_credential-handling.md +++ b/docs/reference/ocm_credential-handling.md @@ -167,7 +167,7 @@ The following credential consumer types are used/supported: - password: the basic auth password - - NpmRegistry: NPM repository + - NpmRegistry: NPM registry It matches the NpmRegistry consumer type and additionally acts like the hostpath type. diff --git a/docs/reference/ocm_get_credentials.md b/docs/reference/ocm_get_credentials.md index c97d24bb0..1a7205d39 100644 --- a/docs/reference/ocm_get_credentials.md +++ b/docs/reference/ocm_get_credentials.md @@ -93,7 +93,7 @@ Matchers exist for the following usage contexts or consumer types: - password: the basic auth password - - NpmRegistry: NPM repository + - NpmRegistry: NPM registry It matches the NpmRegistry consumer type and additionally acts like the hostpath type. diff --git a/pkg/contexts/credentials/builtin/npm/identity/identity.go b/pkg/contexts/credentials/builtin/npm/identity/identity.go index 308cf8d29..dd90ef107 100644 --- a/pkg/contexts/credentials/builtin/npm/identity/identity.go +++ b/pkg/contexts/credentials/builtin/npm/identity/identity.go @@ -1,11 +1,8 @@ package identity import ( - "path" + "net/url" - . "net/url" - - "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" "github.com/open-component-model/ocm/pkg/contexts/credentials/identity/hostpath" "github.com/open-component-model/ocm/pkg/listformat" @@ -37,31 +34,35 @@ func init() { ATTR_TOKEN, "the token attribute. May exist after login at any npm registry. Check your .npmrc file!", }) - cpi.RegisterStandardIdentity(CONSUMER_TYPE, hostpath.IdentityMatcher(CONSUMER_TYPE), `NPM repository + cpi.RegisterStandardIdentity(CONSUMER_TYPE, hostpath.IdentityMatcher(CONSUMER_TYPE), `NPM registry It matches the `+CONSUMER_TYPE+` consumer type and additionally acts like the `+hostpath.IDENTITY_TYPE+` type.`, attrs) } -func GetConsumerId(rawURL string, pkgName string) cpi.ConsumerIdentity { - url, err := Parse(rawURL) +var identityMatcher = hostpath.IdentityMatcher(CONSUMER_TYPE) + +func IdentityMatcher(pattern, cur, id cpi.ConsumerIdentity) bool { + return identityMatcher(pattern, cur, id) +} + +func GetConsumerId(rawURL, groupId string) (cpi.ConsumerIdentity, error) { + _url, err := url.JoinPath(rawURL, groupId) if err != nil { - return nil + return nil, err } - - url.Path = path.Join(url.Path, pkgName) - return hostpath.GetConsumerIdentity(CONSUMER_TYPE, url.String()) + return hostpath.GetConsumerIdentity(CONSUMER_TYPE, _url), nil } -func GetCredentials(ctx cpi.ContextProvider, repoUrl string, pkgName string) common.Properties { - id := GetConsumerId(repoUrl, pkgName) - if id == nil { - return nil +func GetCredentials(ctx cpi.ContextProvider, repoUrl string, pkgName string) (cpi.Credentials, error) { + id, err := GetConsumerId(repoUrl, pkgName) + if err != nil { + return nil, err } - credentials, err := cpi.CredentialsForConsumer(ctx.CredentialsContext(), id) - if credentials == nil || err != nil { - return nil + if id == nil { + logging.DynamicLogger(REALM).Debug("No consumer identity found.", "url", repoUrl, "groupId", pkgName) + return nil, nil } - return credentials.Properties() + return cpi.CredentialsForConsumer(ctx.CredentialsContext(), id) } diff --git a/pkg/contexts/credentials/repositories/npm/config_test.go b/pkg/contexts/credentials/repositories/npm/config_test.go index e2c2fcfea..d0ed58df8 100644 --- a/pkg/contexts/credentials/repositories/npm/config_test.go +++ b/pkg/contexts/credentials/repositories/npm/config_test.go @@ -3,18 +3,17 @@ package npm_test import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - . "github.com/open-component-model/ocm/pkg/testutils" "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/contexts/credentials" "github.com/open-component-model/ocm/pkg/contexts/credentials/builtin/npm/identity" "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/npm" + . "github.com/open-component-model/ocm/pkg/testutils" ) var _ = Describe("Config deserialization Test Environment", func() { It("read .npmrc", func() { ctx := credentials.New() - repo := Must(npm.NewRepository(ctx, "testdata/.npmrc")) Expect(Must(repo.LookupCredentials("registry.npmjs.org")).Properties()).To(Equal(common.Properties{identity.ATTR_TOKEN: "npm_TOKEN"})) Expect(Must(repo.LookupCredentials("npm.registry.acme.com/api/npm")).Properties()).To(Equal(common.Properties{identity.ATTR_TOKEN: "bearer_TOKEN"})) @@ -22,12 +21,9 @@ var _ = Describe("Config deserialization Test Environment", func() { It("propagates credentials", func() { ctx := credentials.New() - spec := npm.NewRepositorySpec("testdata/.npmrc") - _ = Must(ctx.RepositoryForSpec(spec)) - id := identity.GetConsumerId("registry.npmjs.org", "pkg") - + id := Must(identity.GetConsumerId("registry.npmjs.org", "pkg")) creds := Must(credentials.CredentialsForConsumer(ctx, id)) Expect(creds).NotTo(BeNil()) Expect(creds.GetProperty(identity.ATTR_TOKEN)).To(Equal("npm_TOKEN")) @@ -39,5 +35,4 @@ var _ = Describe("Config deserialization Test Environment", func() { Expect(t).NotTo(BeNil()) Expect(t.Description()).NotTo(Equal("")) }) - }) diff --git a/pkg/contexts/credentials/repositories/npm/provider.go b/pkg/contexts/credentials/repositories/npm/provider.go index 2268ff5fa..a60520437 100644 --- a/pkg/contexts/credentials/repositories/npm/provider.go +++ b/pkg/contexts/credentials/repositories/npm/provider.go @@ -35,8 +35,12 @@ func (p *ConsumerProvider) get(requested cpi.ConsumerIdentity, currentFound cpi. var creds cpi.CredentialsSource for key, value := range all { - id := npm.GetConsumerId("https://"+key, "") - + id, err := npm.GetConsumerId("https://"+key, "") + if err != nil { + log := logging.Context().Logger(npm.REALM) + log.LogError(err, "Failed to get consumer id", "key", key, "value", value) + return nil, nil + } if m(requested, currentFound, id) { creds = newCredentials(value) currentFound = id diff --git a/pkg/contexts/credentials/repositories/npm/repository.go b/pkg/contexts/credentials/repositories/npm/repository.go index 87c4d326c..0cd48ffa4 100644 --- a/pkg/contexts/credentials/repositories/npm/repository.go +++ b/pkg/contexts/credentials/repositories/npm/repository.go @@ -65,7 +65,7 @@ func (r *Repository) Read(force bool) error { } if r.path == "" { - return fmt.Errorf("npmrc path not provided") + return errors.New("npmrc path not provided") } cfg, path, err := readNpmConfigFile(r.path) if err != nil { diff --git a/pkg/contexts/ocm/accessmethods/npm/method.go b/pkg/contexts/ocm/accessmethods/npm/method.go index fcf60cc67..7d70ecb21 100644 --- a/pkg/contexts/ocm/accessmethods/npm/method.go +++ b/pkg/contexts/ocm/accessmethods/npm/method.go @@ -86,7 +86,7 @@ func (a *AccessSpec) AccessMethod(c accspeccpi.ComponentVersionAccess) (accspecc } func (a *AccessSpec) GetInexpensiveContentVersionIdentity(access accspeccpi.ComponentVersionAccess) string { - meta, err := a.getPackageMeta(access.GetContext()) + meta, err := a.GetPackageVersion(access.GetContext()) if err != nil { return "" } @@ -96,36 +96,49 @@ func (a *AccessSpec) GetInexpensiveContentVersionIdentity(access accspeccpi.Comp return "" } -// PackageUrl returns the URL of the NPM package (Registry/Package/Version). +// PackageUrl returns the URL of the NPM package (Registry/Package). func (a *AccessSpec) PackageUrl() string { - return a.Registry + path.Join("/", a.Package, a.Version) + return strings.TrimSuffix(a.Registry, "/") + path.Join("/", a.Package) } -func (a *AccessSpec) getPackageMeta(ctx accspeccpi.Context) (*meta, error) { +// PackageVersionUrl returns the URL of the NPM package-version (Registry/Package/Version). +func (a *AccessSpec) PackageVersionUrl() string { + return strings.TrimSuffix(a.Registry, "/") + path.Join("/", a.Package, a.Version) +} + +func (a *AccessSpec) GetPackageVersion(ctx accspeccpi.Context) (*npm.Version, error) { r, err := reader(a, vfsattr.Get(ctx), ctx) if err != nil { return nil, err } - buf := &bytes.Buffer{} - _, err = io.Copy(buf, io.LimitReader(r, 200000)) + defer r.Close() + buf, err := io.ReadAll(r) if err != nil { - return nil, errors.Wrapf(err, "cannot get version metadata for %s", a.PackageUrl()) + return nil, errors.Wrapf(err, "cannot get version metadata for %s", a.PackageVersionUrl()) } - - var metadata meta - - err = json.Unmarshal(buf.Bytes(), &metadata) - if err != nil { - return nil, errors.Wrapf(err, "cannot unmarshal version metadata for %s", a.PackageUrl()) + var version npm.Version + err = json.Unmarshal(buf, &version) + if err != nil || version.Dist.Tarball == "" { + // ugly fallback as workaround for https://github.com/sonatype/nexus-public/issues/224 + var project npm.Project + err = json.Unmarshal(buf, &project) // parse the complete project + if err != nil { + return nil, errors.Wrapf(err, "cannot unmarshal version metadata for %s", a.PackageVersionUrl()) + } + v, ok := project.Version[a.Version] // and pick only the specified version + if !ok { + return nil, errors.Newf("version '%s' doesn't exist", a.Version) + } + version = v } - return &metadata, nil + return &version, nil } //////////////////////////////////////////////////////////////////////////////// func newMethod(c accspeccpi.ComponentVersionAccess, a *AccessSpec) (accspeccpi.AccessMethodImpl, error) { factory := func() (blobaccess.BlobAccess, error) { - meta, err := a.getPackageMeta(c.GetContext()) + meta, err := a.GetPackageVersion(c.GetContext()) if err != nil { return nil, err } @@ -133,6 +146,20 @@ func newMethod(c accspeccpi.ComponentVersionAccess, a *AccessSpec) (accspeccpi.A f := func() (io.ReadCloser, error) { return reader(a, vfsattr.Get(c.GetContext()), c.GetContext(), meta.Dist.Tarball) } + if meta.Dist.Integrity != "" { + tf := f + f = func() (io.ReadCloser, error) { + r, err := tf() + if err != nil { + return nil, err + } + digest, err := iotools.DecodeBase64ToHex(meta.Dist.Integrity) + if err != nil { + return nil, err + } + return iotools.VerifyingReaderWithHash(r, crypto.SHA512, digest), nil + } + } if meta.Dist.Shasum != "" { tf := f f = func() (io.ReadCloser, error) { @@ -149,15 +176,8 @@ func newMethod(c accspeccpi.ComponentVersionAccess, a *AccessSpec) (accspeccpi.A return accspeccpi.NewDefaultMethodImpl(c, a, "", mime.MIME_TGZ, factory), nil } -type meta struct { - Dist struct { - Shasum string `json:"shasum"` - Tarball string `json:"tarball"` - } `json:"dist"` -} - func reader(a *AccessSpec, fs vfs.FileSystem, ctx cpi.ContextProvider, tar ...string) (io.ReadCloser, error) { - url := a.PackageUrl() + url := a.PackageVersionUrl() if len(tar) > 0 { url = tar[0] } @@ -170,12 +190,38 @@ func reader(a *AccessSpec, fs vfs.FileSystem, ctx cpi.ContextProvider, tar ...st if err != nil { return nil, err } - npm.Authorize(req, ctx, a.Registry, a.Package) + err = npm.BasicAuth(req, ctx, a.Registry, a.Package) + if err != nil { + return nil, err + } c := &http.Client{} resp, err := c.Do(req) if err != nil { return nil, err } + defer resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { + // maybe it's stupid Nexus - https://github.com/sonatype/nexus-public/issues/224? + url = a.PackageUrl() + req, err = http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil) + if err != nil { + return nil, err + } + err = npm.BasicAuth(req, ctx, a.Registry, a.Package) + if err != nil { + return nil, err + } + + // close body before overwriting to close any pending connections + resp.Body.Close() + resp, err = c.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + } + if resp.StatusCode != http.StatusOK { defer resp.Body.Close() buf := &bytes.Buffer{} @@ -185,5 +231,9 @@ func reader(a *AccessSpec, fs vfs.FileSystem, ctx cpi.ContextProvider, tar ...st } return nil, errors.Newf("version meta data request %s provides %s: %s", url, resp.Status, buf.String()) } - return resp.Body, nil + content, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return io.NopCloser(bytes.NewBuffer(content)), nil } diff --git a/pkg/contexts/ocm/accessmethods/npm/method_test.go b/pkg/contexts/ocm/accessmethods/npm/method_test.go index dc4dbd251..97c8ec763 100644 --- a/pkg/contexts/ocm/accessmethods/npm/method_test.go +++ b/pkg/contexts/ocm/accessmethods/npm/method_test.go @@ -5,15 +5,15 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - . "github.com/open-component-model/ocm/pkg/env" - . "github.com/open-component-model/ocm/pkg/env/builder" - . "github.com/open-component-model/ocm/pkg/testutils" "github.com/open-component-model/ocm/pkg/contexts/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/npm" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + . "github.com/open-component-model/ocm/pkg/env" + . "github.com/open-component-model/ocm/pkg/env/builder" "github.com/open-component-model/ocm/pkg/iotools" "github.com/open-component-model/ocm/pkg/mime" + . "github.com/open-component-model/ocm/pkg/testutils" ) const NPMPATH = "/testdata/registry" @@ -62,4 +62,20 @@ var _ = Describe("Method", func() { _, err := m.Reader() Expect(err).To(MatchError(ContainSubstring("SHA-1 digest mismatch: expected 44a77645201d1a8fc5213ace787c220eabbd0967, found 34a77645201d1a8fc5213ace787c220eabbd0967"))) }) + + It("PackageUrl()", func() { + packageUrl := "https://registry.npmjs.org/yargs" + acc := npm.New("https://registry.npmjs.org", "yargs", "17.7.1") + Expect(acc.PackageUrl()).To(Equal(packageUrl)) + acc = npm.New("https://registry.npmjs.org/", "yargs", "17.7.1") + Expect(acc.PackageUrl()).To(Equal(packageUrl)) + }) + + It("PackageVersionUrl()", func() { + packageVersionUrl := "https://registry.npmjs.org/yargs/17.7.1" + acc := npm.New("https://registry.npmjs.org", "yargs", "17.7.1") + Expect(acc.PackageVersionUrl()).To(Equal(packageVersionUrl)) + acc = npm.New("https://registry.npmjs.org/", "yargs", "17.7.1") + Expect(acc.PackageVersionUrl()).To(Equal(packageVersionUrl)) + }) }) diff --git a/pkg/contexts/ocm/blobhandler/handlers/generic/npm/blobhandler.go b/pkg/contexts/ocm/blobhandler/handlers/generic/npm/blobhandler.go index 76032ffca..526b1f3dc 100644 --- a/pkg/contexts/ocm/blobhandler/handlers/generic/npm/blobhandler.go +++ b/pkg/contexts/ocm/blobhandler/handlers/generic/npm/blobhandler.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" + crds "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/npm" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/logging" @@ -64,14 +65,8 @@ func (b *artifactHandler) StoreBlob(blob cpi.BlobAccess, _ string, _ string, _ c log = log.WithValues("package", pkg.Name, "version", pkg.Version) log.Debug("identified") - token, err := npmLogin.BearerToken(ctx.GetContext(), b.spec.Url, pkg.Name) - if err != nil { - // we assume, it's not possible to publish anonymous - without token - return nil, err - } - // check if package exists - exists, err := packageExists(b.spec.Url, *pkg, token) + exists, err := packageExists(b.spec.Url, *pkg, ctx.GetContext()) if err != nil { return nil, err } @@ -104,8 +99,11 @@ func (b *artifactHandler) StoreBlob(blob cpi.BlobAccess, _ string, _ string, _ c if err != nil { return nil, err } - req.Header.Set("authorization", "Bearer "+token) - req.Header.Set("content-type", "application/json") + err = npmLogin.Authorize(req, ctx.GetContext(), b.spec.Url, pkg.Name) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") // send PUT request - upload tgz client := http.Client{} @@ -127,13 +125,16 @@ func (b *artifactHandler) StoreBlob(blob cpi.BlobAccess, _ string, _ string, _ c } // Check if package already exists in npm registry. If it does, checks if it's the same. -func packageExists(repoUrl string, pkg Package, token string) (bool, error) { +func packageExists(repoUrl string, pkg Package, ctx crds.ContextProvider) (bool, error) { client := http.Client{} req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, repoUrl+"/"+url.PathEscape(pkg.Name)+"/"+url.PathEscape(pkg.Version), nil) if err != nil { return false, err } - req.Header.Set("authorization", "Bearer "+token) + err = npmLogin.Authorize(req, ctx, repoUrl, pkg.Name) + if err != nil { + return false, err + } resp, err := client.Do(req) if err != nil { return false, err diff --git a/pkg/iotools/digests.go b/pkg/iotools/digests.go new file mode 100644 index 000000000..ab5298849 --- /dev/null +++ b/pkg/iotools/digests.go @@ -0,0 +1,21 @@ +package iotools + +import ( + "encoding/base64" + "encoding/hex" + "regexp" +) + +// Regular expression matching: e.g. 'sha512-', 'SHA-1:', 'Sha-256:', 'sHA42-',. +var re = regexp.MustCompile(`(?i)^sha(\d+-|\-\d+:)`) + +// DecodeBase64ToHex decodes a base64 encoded string and returns the hex representation. +// Any prefix like 'sha512-' or 'SHA-256:' or 'Sha1-' is removed. +func DecodeBase64ToHex(b64encoded string) (string, error) { + b64encoded = re.ReplaceAllString(b64encoded, "") + digest, err := base64.StdEncoding.DecodeString(b64encoded) + if err != nil { + return "", err + } + return hex.EncodeToString(digest), nil +} diff --git a/pkg/iotools/digests_test.go b/pkg/iotools/digests_test.go new file mode 100644 index 000000000..980472223 --- /dev/null +++ b/pkg/iotools/digests_test.go @@ -0,0 +1,39 @@ +package iotools_test + +import ( + "crypto" + "encoding/base64" + "encoding/hex" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/open-component-model/ocm/pkg/iotools" +) + +var _ = Describe("digests.go tests", func() { + It("DecodeBase64ToHex", func() { + hx, err := iotools.DecodeBase64ToHex("04cXMnFlKzgudf//lH/VqGUtFkplvGv0BCmPREEJsVYTJrxyiBFlsOiZIrjPENBkHWPnK6kOG53VTiqtsILNgw==") + Expect(err).To(BeNil()) + Expect(hx).To(Equal("d387173271652b382e75ffff947fd5a8652d164a65bc6bf404298f444109b1561326bc72881165b0e89922b8cf10d0641d63e72ba90e1b9dd54e2aadb082cd83")) + + hx, err = iotools.DecodeBase64ToHex("sha512-04cXMnFlKzgudf//lH/VqGUtFkplvGv0BCmPREEJsVYTJrxyiBFlsOiZIrjPENBkHWPnK6kOG53VTiqtsILNgw==") + Expect(err).To(BeNil()) + Expect(hx).To(Equal("d387173271652b382e75ffff947fd5a8652d164a65bc6bf404298f444109b1561326bc72881165b0e89922b8cf10d0641d63e72ba90e1b9dd54e2aadb082cd83")) + + hx, err = iotools.DecodeBase64ToHex("SHA512-04cXMnFlKzgudf//lH/VqGUtFkplvGv0BCmPREEJsVYTJrxyiBFlsOiZIrjPENBkHWPnK6kOG53VTiqtsILNgw==") + Expect(err).To(BeNil()) + Expect(hx).To(Equal("d387173271652b382e75ffff947fd5a8652d164a65bc6bf404298f444109b1561326bc72881165b0e89922b8cf10d0641d63e72ba90e1b9dd54e2aadb082cd83")) + + hx, err = iotools.DecodeBase64ToHex("Sha-512:04cXMnFlKzgudf//lH/VqGUtFkplvGv0BCmPREEJsVYTJrxyiBFlsOiZIrjPENBkHWPnK6kOG53VTiqtsILNgw==") + Expect(err).To(BeNil()) + Expect(hx).To(Equal("d387173271652b382e75ffff947fd5a8652d164a65bc6bf404298f444109b1561326bc72881165b0e89922b8cf10d0641d63e72ba90e1b9dd54e2aadb082cd83")) + + s1 := crypto.SHA1.New() + s1.Write([]byte("hello")) + sum := s1.Sum(nil) + hx, err = iotools.DecodeBase64ToHex("sHa-1:" + base64.StdEncoding.EncodeToString(sum)) + Expect(err).To(BeNil()) + Expect(hx).To(Equal(hex.EncodeToString(sum))) + }) +}) diff --git a/pkg/npm/login.go b/pkg/npm/login.go index 17f45b029..955750cdf 100644 --- a/pkg/npm/login.go +++ b/pkg/npm/login.go @@ -54,46 +54,71 @@ func Login(registry string, username string, password string, email string) (str return token.Token, nil } +func GetCredentials(ctx cpi.ContextProvider, repoUrl string, pkgName string) (string, string, error) { + credentials, err := identity.GetCredentials(ctx, repoUrl, pkgName) + if err != nil { + return "", "", err + } + if credentials == nil { + return "", "", fmt.Errorf("no credentials found for %s. Couldn't access '%s'", repoUrl, pkgName) + } + return credentials.GetProperty(identity.ATTR_USERNAME), credentials.GetProperty(identity.ATTR_PASSWORD), nil +} + // BearerToken retrieves the bearer token for the given repository URL and package name. // Either it's setup in the credentials or it will login to the registry and retrieve it. func BearerToken(ctx cpi.ContextProvider, repoUrl string, pkgName string) (string, error) { // get credentials and TODO cache it - cred := identity.GetCredentials(ctx, repoUrl, pkgName) - if cred == nil { - return "", fmt.Errorf("no credentials found for %s. Couldn't upload '%s'", repoUrl, pkgName) + credentials, err := identity.GetCredentials(ctx, repoUrl, pkgName) + if err != nil { + return "", err + } + if credentials == nil { + return "", fmt.Errorf("no credentials found for %s. Couldn't access '%s'", repoUrl, pkgName) } log := logging.Context().Logger(identity.REALM) log.Debug("found credentials") // check if token exists, if not login and retrieve token - token := cred[identity.ATTR_TOKEN] + token := credentials.GetProperty(identity.ATTR_TOKEN) if token != "" { log.Debug("token found, skipping login") return token, nil } // use user+pass+mail from credentials to login and retrieve bearer token - username := cred[identity.ATTR_USERNAME] - password := cred[identity.ATTR_PASSWORD] - email := cred[identity.ATTR_EMAIL] + username := credentials.GetProperty(identity.ATTR_USERNAME) + password := credentials.GetProperty(identity.ATTR_PASSWORD) + email := credentials.GetProperty(identity.ATTR_EMAIL) if username == "" || password == "" || email == "" { return "", fmt.Errorf("credentials for %s are invalid. Username, password or email missing! Couldn't upload '%s'", repoUrl, pkgName) } - log = log.WithValues("user", username, "repo", repoUrl) - log.Debug("login") + log.Debug("login", "user", username, "repo", repoUrl) // TODO: check different kinds of .npmrc content - return Login(repoUrl, username, password, email) + token, err = Login(repoUrl, username, password, email) + return token, err } // Authorize the given request with the bearer token for the given repository URL and package name. // If the token is empty (login failed or credentials not found), it will not be set. -func Authorize(req *http.Request, ctx cpi.ContextProvider, repoUrl string, pkgName string) { +func Authorize(req *http.Request, ctx cpi.ContextProvider, repoUrl string, pkgName string) error { token, err := BearerToken(ctx, repoUrl, pkgName) if err != nil { - log := logging.Context().Logger(identity.REALM) - log.Debug("Couldn't authorize", "error", err.Error(), "repo", repoUrl, "package", pkgName) + return err } else if token != "" { - req.Header.Set("authorization", "Bearer "+token) + req.Header.Set("Authorization", "Bearer "+token) + } + return nil +} + +func BasicAuth(req *http.Request, ctx cpi.ContextProvider, repoUrl string, pkgName string) error { + username, password, err := GetCredentials(ctx, repoUrl, pkgName) + if err != nil { + return err + } + if username != "" && password != "" { + req.SetBasicAuth(username, password) } + return nil } diff --git a/pkg/npm/structs.go b/pkg/npm/structs.go new file mode 100644 index 000000000..e3c52c6af --- /dev/null +++ b/pkg/npm/structs.go @@ -0,0 +1,18 @@ +package npm + +type Dist struct { + Integrity string `json:"integrity"` + Shasum string `json:"shasum"` + Tarball string `json:"tarball"` +} + +type Version struct { + Name string `json:"name"` + Version string `json:"version"` + Dist Dist `json:"dist"` +} + +type Project struct { + Name string `json:"name"` + Version map[string]Version `json:"versions"` +}