[cipd] Using cipd resolve to check access for internal packages
This patch add cipd access check for internal packages. If a
package declared in manifest has attribute 'internal="true"', jiri
will use "cipd resolve" to determine if current cipd session
has correct access. Unaccessible internal packages
will not be fetched to avoid errors.
BLD-201
Change-Id: I75e3485b4f103474892ae58ba004329892730041
diff --git a/cipd/cipd.go b/cipd/cipd.go
index f34fa95..c28f465 100644
--- a/cipd/cipd.go
+++ b/cipd/cipd.go
@@ -51,6 +51,7 @@
windows-386 sha256 b8102c9a1b93915c128e7577c89fd77991ab83d52c356913e56ea505ab338735
windows-amd64 sha256 a117e3984c111c68698faf91815c4b7d374404fa82dff318aadb9f2f0582ca8d
`
+ cipdNotLoggedInStr = "Not logged in"
)
var (
@@ -244,6 +245,91 @@
return ioutil.ReadAll(resp.Body)
}
+type packageACL struct {
+ path string
+ access bool
+}
+
+func checkPackageACL(jirix *jiri.X, path, version string, c chan<- packageACL) {
+ // cipd should be already bootstrapped before this go routine.
+ // Silently return a false just in case if cipd is not found.
+ if cipdBinary == "" {
+ c <- packageACL{path: path, access: false}
+ return
+ }
+
+ args := []string{"resolve", path, "-version", version}
+ if jirix != nil {
+ jirix.Logger.Debugf("Invoke cipd with %v", args)
+ }
+ command := exec.Command(cipdBinary, args...)
+ var stdoutBuf, stderrBuf bytes.Buffer
+ command.Stdout = &stdoutBuf
+ command.Stderr = &stderrBuf
+ // Return false if cipd cannot be executed or cipd returned a non-zero
+ // return code, which usually means the package cannot be found due to
+ // access control.
+ if err := command.Run(); err != nil {
+ if jirix != nil {
+ jirix.Logger.Debugf("Error happend while executing cipd, err: %q, stderr: %q", err, stderrBuf.String())
+ }
+ c <- packageACL{path: path, access: false}
+ return
+ }
+ // cipd returned zero. Package can be accessed.
+ c <- packageACL{path: path, access: true}
+ return
+}
+
+// CheckPackageACL checks cipd's access to packages in map "pkgs". The package
+// names in "pkgs" should have trailing '/' removed before calling this
+// function.
+func CheckPackageACL(jirix *jiri.X, pkgs map[string]bool, versions map[string]string) error {
+ // Not declared as CheckPackageACL(jirix *jiri.X, pkgs map[*package.Package]bool)
+ // due to import cycles. Package jiri/package imports jiri/cipd so here we cannot
+ // import jiri/package.
+ if _, err := Bootstrap(); err != nil {
+ return err
+ }
+
+ c := make(chan packageACL)
+ for key := range pkgs {
+ go checkPackageACL(jirix, key, versions[key], c)
+ }
+
+ for i := 0; i < len(pkgs); i++ {
+ acl := <-c
+ pkgs[acl.path] = acl.access
+ }
+ return nil
+}
+
+// CheckLoggedIn checks cipd's user login information. It will return true
+// if login information is found or return false if login information is not
+// found.
+func CheckLoggedIn(jirix *jiri.X) (bool, error) {
+ cipdPath, err := Bootstrap()
+ if err != nil {
+ return false, err
+ }
+ args := []string{"auth-info"}
+ command := exec.Command(cipdPath, args...)
+ var stdoutBuf, stderrBuf bytes.Buffer
+ command.Stdout = &stdoutBuf
+ command.Stderr = &stderrBuf
+ if err := command.Run(); err != nil {
+ stdErrMsg := strings.TrimSpace(stderrBuf.String())
+ if jirix != nil {
+ jirix.Logger.Debugf("Error happend while executing cipd, err: %q, stderr: %q", err, stdErrMsg)
+ }
+ if _, ok := err.(*exec.ExitError); ok && stdErrMsg == cipdNotLoggedInStr {
+ return false, nil
+ }
+ return false, err
+ }
+ return true, nil
+}
+
// Ensure runs cipd binary's ensure funcationality over file. Fetched packages will be
// saved to projectRoot directory. Parameter timeout is in minutes.
func Ensure(jirix *jiri.X, file, projectRoot string, timeout uint) error {
diff --git a/cipd/cipd_test.go b/cipd/cipd_test.go
index fdff7ed..41cce90 100644
--- a/cipd/cipd_test.go
+++ b/cipd/cipd_test.go
@@ -17,6 +17,11 @@
// Some random valid cipd version tags from infra/tools/cipd
cipdVersionForTestA = "git_revision:00e2d8b49a4e7505d1c71f19d15c9e7c5b9245a5"
cipdVersionForTestB = "git_revision:8fac632847b1ce0de3b57d16d0f2193625f4a4f0"
+ // package path and versions for ACL tests
+ cipdPkgPathA = "gn/gn/${platform}"
+ cipdPkgVersionA = "git_revision:bdb0fd02324b120cacde634a9235405061c8ea06"
+ cipdPkgPathB = "notexist/notexist"
+ cipdPkgVersionB = "git_revision:bdb0fd02324b120cacde634a9235405061c8ea06"
)
var (
@@ -164,3 +169,30 @@
t.Errorf("failed to execute os.Stat() on fetched cipd package due to error: %v", err)
}
}
+
+func TestCheckACL(t *testing.T) {
+ cipdPath, err := Bootstrap()
+ if err != nil {
+ t.Errorf("bootstrap failed due to error: %v", err)
+ }
+ defer os.Remove(cipdPath)
+
+ pkgMap := make(map[string]bool)
+ pkgMap[cipdPkgPathA] = false
+ pkgMap[cipdPkgPathB] = false
+ versionMap := make(map[string]string)
+ versionMap[cipdPkgPathA] = cipdPkgVersionA
+ versionMap[cipdPkgPathB] = cipdPkgVersionB
+ if err := CheckPackageACL(nil, pkgMap, versionMap); err != nil {
+ t.Errorf("CheckPackageACL failed due to error: %v", err)
+ }
+
+ if !pkgMap[cipdPkgPathA] {
+ t.Errorf("pkg %q should be accessible, but it is not accessible by cipd", cipdPkgPathA)
+ }
+
+ if pkgMap[cipdPkgPathB] {
+ t.Errorf("pkg %q should not be accessible, but it is accessible by cipd", cipdPkgPathB)
+ }
+
+}
diff --git a/project/manifest.go b/project/manifest.go
index 0e5162a..232fc9c 100644
--- a/project/manifest.go
+++ b/project/manifest.go
@@ -503,8 +503,28 @@
ensureFileBuf.WriteString("$ParanoidMode CheckPresence\n")
ensureFileBuf.WriteString("\n")
- // TODO: perform ACL checks on internal projects
+ // Perform ACL checks on internal projects
+ pkgACLMap := make(map[string]bool)
+ pkgVersionMap := make(map[string]string)
for _, pkg := range pkgs {
+ pkg.Name = strings.TrimRight(pkg.Name, "/")
+ if pkg.Internal {
+ pkgACLMap[pkg.Name] = false
+ pkgVersionMap[pkg.Name] = pkg.Version
+ }
+ }
+ if len(pkgACLMap) != 0 {
+ if err := cipd.CheckPackageACL(jirix, pkgACLMap, pkgVersionMap); err != nil {
+ return err
+ }
+ }
+
+ hasSkippedPkgs := false
+ for _, pkg := range pkgs {
+ if val, ok := pkgACLMap[pkg.Name]; ok && !val {
+ hasSkippedPkgs = true
+ continue
+ }
cipdDecl, err := pkg.cipdDecl()
if err != nil {
return err
@@ -513,16 +533,25 @@
ensureFileBuf.WriteString("\n")
}
+ jirix.Logger.Debugf("Generated ensure file content:\n%v", ensureFileBuf.String())
if _, err := ensureFileBuf.WriteTo(ensureFile); err != nil {
return err
}
if err := ensureFile.Sync(); err != nil {
return err
}
- jirix.Logger.Debugf("Generated cipd ensure file at %s ", ensureFilePath)
if err := cipd.Ensure(jirix, ensureFilePath, jirix.Root, fetchTimeout); err != nil {
return err
}
+ if hasSkippedPkgs {
+ cipdLoggedIn, err := cipd.CheckLoggedIn(jirix)
+ if err != nil {
+ return err
+ }
+ if !cipdLoggedIn {
+ jirix.Logger.Warningf("Some packages are skipped by cipd due to lack of access, you might want to run \"cipd auth-login\" and try again")
+ }
+ }
return nil
}