Skip to content

Commit

Permalink
fix cred names + harmonize propagation handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mandelsoft committed Feb 16, 2024
1 parent a9477ab commit b6058b0
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 93 deletions.
20 changes: 11 additions & 9 deletions pkg/contexts/credentials/repositories/dockerconfig/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
"fmt"

"github.com/open-component-model/ocm/pkg/contexts/credentials/cpi"
"github.com/open-component-model/ocm/pkg/generics"
"github.com/open-component-model/ocm/pkg/runtime"
"github.com/open-component-model/ocm/pkg/utils"
)

const (
Expand All @@ -27,19 +29,19 @@ type RepositorySpec struct {
runtime.ObjectVersionedType `json:",inline"`
DockerConfigFile string `json:"dockerConfigFile,omitempty"`
DockerConfig json.RawMessage `json:"dockerConfig,omitempty"`
PropgateConsumerIdentity bool `json:"propagateConsumerIdentity,omitempty"`
PropgateConsumerIdentity *bool `json:"propagateConsumerIdentity,omitempty"`
}

func (s RepositorySpec) WithConsumerPropagation(propagate bool) *RepositorySpec {
s.PropgateConsumerIdentity = propagate
s.PropgateConsumerIdentity = &propagate
return &s
}

// NewRepositorySpec creates a new memory RepositorySpec.
func NewRepositorySpec(path string, prop ...bool) *RepositorySpec {
p := false
for _, e := range prop {
p = p || e
var p *bool
if len(prop) > 0 {
p = generics.Pointer(utils.Optional(prop...))
}
if path == "" {
path = "~/.docker/config.json"
Expand All @@ -52,9 +54,9 @@ func NewRepositorySpec(path string, prop ...bool) *RepositorySpec {
}

func NewRepositorySpecForConfig(data []byte, prop ...bool) *RepositorySpec {
p := false
for _, e := range prop {
p = p || e
var p *bool
if len(prop) > 0 {
p = generics.Pointer(utils.Optional(prop...))
}
return &RepositorySpec{
ObjectVersionedType: runtime.NewVersionedTypedObject(Type),
Expand All @@ -73,5 +75,5 @@ func (a *RepositorySpec) Repository(ctx cpi.Context, creds cpi.Credentials) (cpi
if !ok {
return nil, fmt.Errorf("failed to assert type %T to Repositories", r)
}
return repos.GetRepository(ctx, a.DockerConfigFile, a.DockerConfig, a.PropgateConsumerIdentity)
return repos.GetRepository(ctx, a.DockerConfigFile, a.DockerConfig, utils.AsBool(a.PropgateConsumerIdentity, true))
}
10 changes: 6 additions & 4 deletions pkg/contexts/credentials/repositories/gardenerconfig/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/credentials/internal"
gardenercfgcpi "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/gardenerconfig/cpi"
"github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/gardenerconfig/identity"
"github.com/open-component-model/ocm/pkg/generics"
"github.com/open-component-model/ocm/pkg/runtime"
"github.com/open-component-model/ocm/pkg/utils"
)

const (
Expand All @@ -30,19 +32,19 @@ type RepositorySpec struct {
URL string `json:"url"`
ConfigType gardenercfgcpi.ConfigType `json:"configType"`
Cipher Cipher `json:"cipher"`
PropagateConsumerIdentity bool `json:"propagateConsumerIdentity"`
PropagateConsumerIdentity *bool `json:"propagateConsumerIdentity,omitempty"`
}

var _ cpi.ConsumerIdentityProvider = (*RepositorySpec)(nil)

// NewRepositorySpec creates a new memory RepositorySpec.
func NewRepositorySpec(url string, configType gardenercfgcpi.ConfigType, cipher Cipher, propagateConsumerIdentity bool) *RepositorySpec {
func NewRepositorySpec(url string, configType gardenercfgcpi.ConfigType, cipher Cipher, propagateConsumerIdentity ...bool) *RepositorySpec {
return &RepositorySpec{
ObjectVersionedType: runtime.NewVersionedTypedObject(Type),
URL: url,
ConfigType: configType,
Cipher: cipher,
PropagateConsumerIdentity: propagateConsumerIdentity,
PropagateConsumerIdentity: generics.Pointer(utils.OptionalDefaultedBool(true, propagateConsumerIdentity...)),
}
}

Expand All @@ -62,7 +64,7 @@ func (a *RepositorySpec) Repository(ctx cpi.Context, creds cpi.Credentials) (cpi
return nil, fmt.Errorf("unable to get key from context: %w", err)
}

return repos.GetRepository(ctx, a.URL, a.ConfigType, a.Cipher, key, a.PropagateConsumerIdentity)
return repos.GetRepository(ctx, a.URL, a.ConfigType, a.Cipher, key, utils.AsBool(a.PropagateConsumerIdentity, true))
}

func (a *RepositorySpec) GetConsumerId(uctx ...internal.UsageContext) internal.ConsumerIdentity {
Expand Down
22 changes: 22 additions & 0 deletions pkg/contexts/credentials/repositories/npm/a_usage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Open Component Model contributors.
//
// SPDX-License-Identifier: Apache-2.0

package npm

import (
"github.com/open-component-model/ocm/pkg/listformat"
)

var usage = `
This repository type can be used to access credentials stored in a file
following the NPM npmrc format (~/.npmrc). It take into account the
credentials helper section, also. If enabled, the described
credentials will be automatically assigned to appropriate consumer ids.
`

var format = `The repository specification supports the following fields:
` + listformat.FormatListElements("", listformat.StringElementDescriptionList{
"npmrcFile", "*string*: the file path to a NPM npmrc file",
"propagateConsumerIdentity", "*bool*(optional): enable consumer id propagation",
})
4 changes: 2 additions & 2 deletions pkg/contexts/credentials/repositories/npm/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func createCache(_ datacontext.Context) interface{} {
}
}

func (r *Cache) GetRepository(ctx cpi.Context, name string) (*Repository, error) {
func (r *Cache) GetRepository(ctx cpi.Context, name string, prop bool) (*Repository, error) {
var (
err error = nil
repo *Repository
Expand All @@ -24,7 +24,7 @@ func (r *Cache) GetRepository(ctx cpi.Context, name string) (*Repository, error)
repo = r.repos[name]
}
if repo == nil {
repo, err = NewRepository(ctx, name)
repo, err = NewRepository(ctx, name, prop)
if err == nil {
r.repos[name] = repo
}
Expand Down
65 changes: 5 additions & 60 deletions pkg/contexts/credentials/repositories/npm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,12 @@ package npm

import (
"bufio"
"fmt"
"os"
"strings"

"github.com/open-component-model/ocm/pkg/contexts/credentials/cpi"
"github.com/open-component-model/ocm/pkg/listformat"
"github.com/open-component-model/ocm/pkg/runtime"
)

const (
// REPOSITORY_TYPE is the type of the NPMConfig.
REPOSITORY_TYPE = "NPMConfig"
REPOSITORY_TYPE_v1 = REPOSITORY_TYPE + runtime.VersionSeparator + "v1"
)

func init() {
cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](REPOSITORY_TYPE))
cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](REPOSITORY_TYPE_v1, cpi.WithDescription(usage), cpi.WithFormatSpec(format)))
}

var usage = `
This repository type can be used to access credentials stored in a file
following the NPM npmrc format (~/.npmrc). It take into account the
credentials helper section, also. If enabled, the described
credentials will be automatically assigned to appropriate consumer ids.
`

var format = `The repository specification supports the following fields:
` + listformat.FormatListElements("", listformat.StringElementDescriptionList{
"npmrcFile", "*string*: the file path to a NPM npmrc file",
"propagateConsumerIdentity", "*bool*(optional): enable consumer id propagation",
})

type npmConfig map[string]string

// RepositorySpec describes a docker npmrc based credential repository interface.
type RepositorySpec struct {
runtime.ObjectVersionedType `json:",inline"`
NpmrcFile string `json:"npmrcFile,omitempty"`
}

// NewRepositorySpec creates a new memory RepositorySpec.
func NewRepositorySpec(path string) *RepositorySpec {
if path == "" {
path = "~/.npmrc"
}
return &RepositorySpec{
ObjectVersionedType: runtime.NewVersionedTypedObject(REPOSITORY_TYPE),
NpmrcFile: path,
}
}

func (rs *RepositorySpec) GetType() string {
return REPOSITORY_TYPE
}

func (rs *RepositorySpec) Repository(ctx cpi.Context, _ cpi.Credentials) (cpi.Repository, error) {
r := ctx.GetAttributes().GetOrCreateAttribute(".npmrc", createCache)
cache, ok := r.(*Cache)
if !ok {
return nil, fmt.Errorf("failed to assert type %T to Cache", r)
}
return cache.GetRepository(ctx, rs.NpmrcFile)
}

// readNpmConfigFile reads "~/.npmrc" file line by line, parse it and return the result as a npmConfig.
func readNpmConfigFile(path string) (npmConfig, error) {
// Open the file
Expand All @@ -89,7 +30,11 @@ func readNpmConfigFile(path string) (npmConfig, error) {
// Split the line into key and value
parts := strings.SplitN(line, ":_authToken=", 2)
if len(parts) == 2 {
cfg["https://"+parts[0]] = parts[1]
if strings.HasSuffix(parts[0], "/") {
cfg["https://"+parts[0][:len(parts[0])-1]] = parts[1]
} else {
cfg["https://"+parts[0]] = parts[1]
}
}
}

Expand Down
39 changes: 31 additions & 8 deletions pkg/contexts/credentials/repositories/npm/config_test.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
package npm
package npm_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"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() {
cfg, err := readNpmConfigFile("testdata/.npmrc")
Expect(err).To(BeNil())
Expect(cfg).ToNot(BeNil())
Expect(cfg).ToNot(BeEmpty())
Expect(cfg["https://registry.npmjs.org/"]).To(Equal("npm_TOKEN"))
Expect(cfg["https://npm.registry.acme.com/api/npm/"]).To(Equal("bearer_TOKEN"))
ctx := credentials.New()

repo := Must(npm.NewRepository(ctx, "testdata/.npmrc"))
Expect(Must(repo.LookupCredentials("https://registry.npmjs.org")).Properties()).To(Equal(common.Properties{identity.ATTR_TOKEN: "npm_TOKEN"}))
Expect(Must(repo.LookupCredentials("https://npm.registry.acme.com/api/npm")).Properties()).To(Equal(common.Properties{identity.ATTR_TOKEN: "bearer_TOKEN"}))
})

It("propagates credentials", func() {
ctx := credentials.New()

spec := npm.NewRepositorySpec("testdata/.npmrc")

_ = Must(ctx.RepositoryForSpec(spec))
id := identity.GetConsumerId("https://registry.npmjs.org", "pkg")

creds := Must(credentials.CredentialsForConsumer(ctx, id))
Expect(creds).NotTo(BeNil())
Expect(creds.GetProperty(identity.ATTR_TOKEN)).To(Equal("npm_TOKEN"))
})

It("has description", func() {
ctx := credentials.New()
t := ctx.RepositoryTypes().GetType(npm.TypeV1)
Expect(t).NotTo(BeNil())
Expect(t.Description()).NotTo(Equal(""))
})

})
28 changes: 18 additions & 10 deletions pkg/contexts/credentials/repositories/npm/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,24 @@ import (
"github.com/open-component-model/ocm/pkg/utils"
)

const PROVIDER = "ocm.software/credentialprovider/" + REPOSITORY_TYPE
const PROVIDER = "ocm.software/credentialprovider/" + Type

type Repository struct {
ctx cpi.Context
path string
npmrc npmConfig
ctx cpi.Context
path string
propagate bool
npmrc npmConfig
}

func NewRepository(ctx cpi.Context, path string) (*Repository, error) {
func NewRepository(ctx cpi.Context, path string, prop ...bool) (*Repository, error) {
return newRepository(ctx, path, utils.OptionalDefaultedBool(true, prop...))
}

func newRepository(ctx cpi.Context, path string, prop bool) (*Repository, error) {
r := &Repository{
ctx: ctx,
path: path,
ctx: ctx,
path: path,
propagate: prop,
}
err := r.Read(true)
return r, err
Expand All @@ -43,13 +49,13 @@ func (r *Repository) LookupCredentials(name string) (cpi.Credentials, error) {
return nil, err
}
if !exists {
return nil, errors.ErrNotFound("credentials", name, REPOSITORY_TYPE)
return nil, errors.ErrNotFound("credentials", name, Type)
}
return newCredentials(r.npmrc[name]), nil
}

func (r *Repository) WriteCredentials(_ string, _ cpi.Credentials) (cpi.Credentials, error) {
return nil, errors.ErrNotSupported("write", "credentials", REPOSITORY_TYPE)
return nil, errors.ErrNotSupported("write", "credentials", Type)
}

func (r *Repository) Read(force bool) error {
Expand All @@ -70,7 +76,9 @@ func (r *Repository) Read(force bool) error {
}
id := cpi.ProviderIdentity(PROVIDER + "/" + path)

r.ctx.RegisterConsumerProvider(id, &ConsumerProvider{path})
if r.propagate {
r.ctx.RegisterConsumerProvider(id, &ConsumerProvider{r.path})
}
r.npmrc = cfg
return nil
}
Expand Down
55 changes: 55 additions & 0 deletions pkg/contexts/credentials/repositories/npm/type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Open Component Model contributors.
//
// SPDX-License-Identifier: Apache-2.0

package npm

import (
"fmt"

"github.com/open-component-model/ocm/pkg/contexts/credentials/cpi"
"github.com/open-component-model/ocm/pkg/runtime"
"github.com/open-component-model/ocm/pkg/utils"
)

const (
// Type is the type of the NPMConfig.
Type = "NPMConfig"
TypeV1 = Type + runtime.VersionSeparator + "v1"
)

func init() {
cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](Type))
cpi.RegisterRepositoryType(cpi.NewRepositoryType[*RepositorySpec](TypeV1, cpi.WithDescription(usage), cpi.WithFormatSpec(format)))
}

// RepositorySpec describes a docker npmrc based credential repository interface.
type RepositorySpec struct {
runtime.ObjectVersionedType `json:",inline"`
NpmrcFile string `json:"npmrcFile,omitempty"`
PropgateConsumerIdentity *bool `json:"propagateConsumerIdentity,omitempty"`
}

// NewRepositorySpec creates a new memory RepositorySpec.
func NewRepositorySpec(path string) *RepositorySpec {
if path == "" {
path = "~/.npmrc"
}
return &RepositorySpec{
ObjectVersionedType: runtime.NewVersionedTypedObject(Type),
NpmrcFile: path,
}
}

func (rs *RepositorySpec) GetType() string {
return Type
}

func (rs *RepositorySpec) Repository(ctx cpi.Context, _ cpi.Credentials) (cpi.Repository, error) {
r := ctx.GetAttributes().GetOrCreateAttribute(".npmrc", createCache)
cache, ok := r.(*Cache)
if !ok {
return nil, fmt.Errorf("failed to assert type %T to Cache", r)
}
return cache.GetRepository(ctx, rs.NpmrcFile, utils.AsBool(rs.PropgateConsumerIdentity, true))
}

0 comments on commit b6058b0

Please sign in to comment.