Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

netrc #16

Merged
merged 1 commit into from
Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions carton/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func TestUnit(t *testing.T) {
suite("BuildpackDependency", testBuildpackDependency)
suite("BuildImageDependency", testBuildImageDependency)
suite("LifecycleDependency", testLifecycleDependency)
suite("Netrc", testNetrc)
suite("Package", testPackage)
suite("PackageDependency", testPackageDependency)
suite.Run(t)
Expand Down
118 changes: 118 additions & 0 deletions carton/netrc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package carton

import (
"fmt"
"io/ioutil"
"net/http"
"os"
"os/user"
"path/filepath"
"strings"
)

type Netrc []NetrcLine

type NetrcLine struct {
Machine string
Login string
Password string
}

func (n Netrc) BasicAuth(request *http.Request) (*http.Request, error) {
for _, l := range n {
if l.Machine != request.Host && l.Machine != "default" {
continue
}

request.SetBasicAuth(l.Login, l.Password)
break
}

return request, nil
}

func ParseNetrc(path string) (Netrc, error) {
b, err := ioutil.ReadFile(path)
if os.IsNotExist(err) {
return nil, nil
} else if err != nil {
return nil, fmt.Errorf("unable to open %s\n%w", path, err)
}

var (
n Netrc
l NetrcLine
m = false
)

for _, line := range strings.Split(string(b), "\n") {
if m {
if line == "" {
m = false
}
continue
}

f := strings.Fields(line)
for i := 0; i < len(f); {
switch f[i] {
case "machine":
l = NetrcLine{Machine: f[i+1]}
i += 2
case "default":
l = NetrcLine{Machine: "default"}
i += 1
case "login":
l.Login = f[i+1]
i += 2
case "password":
l.Password = f[i+1]
i += 2
case "macdef":
m = true
i += 2
}

if l.Machine != "" && l.Login != "" && l.Password != "" {
n = append(n, l)

if l.Machine == "default" {
return n, nil
}

l = NetrcLine{}
}
}
}

return n, nil
}

func NetrcPath() (string, error) {
if s, ok := os.LookupEnv("NETRC"); ok {
return s, nil
}

u, err := user.Current()
if err != nil {
return "", fmt.Errorf("unable to determine user home directory\n%w", err)
}

return filepath.Join(u.HomeDir, ".netrc"), nil
}
218 changes: 218 additions & 0 deletions carton/netrc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/*
* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package carton_test

import (
"io/ioutil"
"net/http"
"os"
"os/user"
"path/filepath"
"testing"

. "github.com/onsi/gomega"
"github.com/sclevine/spec"

"github.com/paketo-buildpacks/libpak/carton"
)

func testNetrc(t *testing.T, context spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect

path string
)

it.Before(func() {
var err error

f, err := ioutil.TempFile("", "netrc")
Expect(err).NotTo(HaveOccurred())
Expect(f.Close()).To(Succeed())
path = f.Name()
})

it.After(func() {
Expect(os.RemoveAll(path)).To(Succeed())
})

context("path", func() {
context("$NETRC", func() {
it.Before(func() {
Expect(os.Setenv("NETRC", "test-value")).To(Succeed())
})

it.After(func() {
Expect(os.Unsetenv("NETRC")).To(Succeed())
})

it("returns value from env var", func() {
Expect(carton.NetrcPath()).To(Equal("test-value"))
})
})

it("returns default", func() {
u, err := user.Current()
Expect(err).NotTo(HaveOccurred())

Expect(carton.NetrcPath()).To(Equal(filepath.Join(u.HomeDir, ".netrc")))
})
})

context("parse", func() {
it("parses one-liner", func() {
Expect(ioutil.WriteFile(path, []byte(`machine test-machine login test-login password test-password`), 0644)).To(Succeed())

Expect(carton.ParseNetrc(path)).To(Equal(carton.Netrc{
{
Machine: "test-machine",
Login: "test-login",
Password: "test-password",
},
}))
})

it("parses multi-liner", func() {
Expect(ioutil.WriteFile(path, []byte(`
machine test-machine
login test-login
password test-password
`), 0644)).To(Succeed())

Expect(carton.ParseNetrc(path)).To(Equal(carton.Netrc{
{
Machine: "test-machine",
Login: "test-login",
Password: "test-password",
},
}))
})

it("ignores macdef", func() {
Expect(ioutil.WriteFile(path, []byte(`
macdef uploadtest
cd /pub/tests
bin
put filename.tar.gz
quit

machine test-machine login test-login password test-password
`), 0644)).To(Succeed())

Expect(carton.ParseNetrc(path)).To(Equal(carton.Netrc{
{
Machine: "test-machine",
Login: "test-login",
Password: "test-password",
},
}))
})

it("ignores all after default", func() {
Expect(ioutil.WriteFile(path, []byte(`
machine test-machine-1 login test-login-1 password test-password-1

default
login test-login-2
password test-password-2

machine test-machine-3 login test-login-3 password test-password-3
`), 0644)).To(Succeed())

Expect(carton.ParseNetrc(path)).To(Equal(carton.Netrc{
{
Machine: "test-machine-1",
Login: "test-login-1",
Password: "test-password-1",
},
{
Machine: "default",
Login: "test-login-2",
Password: "test-password-2",
},
}))
})
})

context("basic auth", func() {
it("does not apply auth if no candidates", func() {
n := carton.Netrc{
{
Machine: "test-machine",
Login: "test-login",
Password: "test-password",
},
}

req, err := http.NewRequest("GET", "http://another-machine", nil)
Expect(err).NotTo(HaveOccurred())

req, err = n.BasicAuth(req)
Expect(err).NotTo(HaveOccurred())

_, _, ok := req.BasicAuth()
Expect(ok).To(BeFalse())
})

it("applies basic auth for match", func() {
n := carton.Netrc{
{
Machine: "test-machine",
Login: "test-login",
Password: "test-password",
},
}

req, err := http.NewRequest("GET", "http://test-machine", nil)
Expect(err).NotTo(HaveOccurred())

req, err = n.BasicAuth(req)
Expect(err).NotTo(HaveOccurred())

u, p, ok := req.BasicAuth()
Expect(ok).To(BeTrue())
Expect(u).To(Equal("test-login"))
Expect(p).To(Equal("test-password"))
})

it("applies basic auth for default", func() {
n := carton.Netrc{
{
Machine: "test-machine",
Login: "test-login",
Password: "test-password",
},
{
Machine: "default",
Login: "default-login",
Password: "default-password",
},
}

req, err := http.NewRequest("GET", "http://another-machine", nil)
Expect(err).NotTo(HaveOccurred())

req, err = n.BasicAuth(req)
Expect(err).NotTo(HaveOccurred())

u, p, ok := req.BasicAuth()
Expect(ok).To(BeTrue())
Expect(u).To(Equal("default-login"))
Expect(p).To(Equal("default-password"))
})
})
}
14 changes: 13 additions & 1 deletion carton/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,22 @@ func (p Package) Create(options ...Option) {
cache.DownloadPath = filepath.Join(p.Source, "dependencies")
}

np, err := NetrcPath()
if err != nil {
config.exitHandler.Error(fmt.Errorf("unable to determine netrc path\n%w", err))
return
}

n, err := ParseNetrc(np)
if err != nil {
config.exitHandler.Error(fmt.Errorf("unable to read %s as netrc\n%w", np, err))
return
}

for _, dep := range metadata.Dependencies {
logger.Headerf("Caching %s", color.BlueString("%s %s", dep.Name, dep.Version))

f, err := cache.Artifact(dep)
f, err := cache.Artifact(dep, n.BasicAuth)
if err != nil {
config.exitHandler.Error(fmt.Errorf("unable to download %s\n%w", dep.URI, err))
return
Expand Down
Loading