diff --git a/go/cmd/bazelcredswrapper/BUILD.bazel b/go/cmd/bazelcredswrapper/BUILD.bazel new file mode 100644 index 000000000..607866663 --- /dev/null +++ b/go/cmd/bazelcredswrapper/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "bazelcredswrapper_lib", + srcs = ["main.go"], + importpath = "github.com/bazelbuild/remote-apis-sdks/go/cmd/bazelcredswrapper", + visibility = ["//visibility:private"], + deps = ["@com_github_golang_glog//:glog"], +) + +go_binary( + name = "bazelcredswrapper", + embed = [":bazelcredswrapper_lib"], + visibility = ["//visibility:public"], +) diff --git a/go/cmd/bazelcredswrapper/README.md b/go/cmd/bazelcredswrapper/README.md new file mode 100644 index 000000000..899cfa57e --- /dev/null +++ b/go/cmd/bazelcredswrapper/README.md @@ -0,0 +1,10 @@ +# Bazel Creds Interface Mapper + +## Usage +`bazelisk run //cmd/bazelcredswrapper -- --credentials_helper_path=[Path to the user's bazel-style credshelper] --uri=[URI of the credentials request]` + +This will run the user provided bazel-style credentials helper and output credentials in a format support by the `remote-apis-sdks`. For usage with `remote-apis-sdks`, we first need to build the `bazelcredswrapper` binary along with the sdks. Then whereever you set your flags to configure the sdks, you also set the following flags: + +`--credentials_helper=path/to/remote-apis-sdks/bazel-bin/go/cmd/bazelcredswrapper/bazelcredswrapper_/bazelcredswrapper` + +`--credentials_helper_args=--credentials_helper_path=[Path to user's bazel-style credshelper] --uri=[URI of the credentials request]` diff --git a/go/cmd/bazelcredswrapper/main.go b/go/cmd/bazelcredswrapper/main.go new file mode 100644 index 000000000..0f15992c2 --- /dev/null +++ b/go/cmd/bazelcredswrapper/main.go @@ -0,0 +1,86 @@ +// Binary bazelcredswrapper is used to authenticate using bazel style credentials helper with the remote-apis-sdks + +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "os" + "os/exec" + "strings" + "time" + + log "github.com/golang/glog" +) + +var ( + credsPath = flag.String("credentials_helper_path", "", "Path to the user's credentials helper binary.") + uri = flag.String("uri", "", "The URI of the credentials request.") +) + +func main() { + defer log.Flush() + flag.Parse() + log.Flush() + var err error + if *credsPath == "" { + log.Errorf("No credentials helper path provided.") + os.Exit(1) + } + uriObj := fmt.Sprintf(`{"uri":"%v"}`, *uri) + cmd := exec.Command(*credsPath, "get") + var stdin, stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + stdin.Write([]byte(uriObj)) + cmd.Stdin = &stdin + err = cmd.Run() + out := stdout.String() + if stderr.String() != "" { + log.Error(stderr.String()) + } + if err != nil { + log.Fatalf("Failed running the credentials helper: %v, with err: %v", *credsPath, err) + } + + headers, expiry := parseCredsOut(out) + // Bazel-style headers are of the form map[string][]string but we need them + // to be of the form map[string]string to match PerRPC credentials + hdrs := map[string]string{} + for k, v := range headers { + hdrs[k] = strings.Join(v, ",") + } + jsonHdrs, err := json.Marshal(hdrs) + if err != nil { + log.Error(err) + os.Exit(1) + } + fmt.Printf(`{"headers":%s, "token":"%s", "expiry":"%s"}`, jsonHdrs, + "", expiry.Format(time.UnixDate)) +} + +type CredshelperOut struct { + Headers map[string][]string `json:"headers"` + Expires string `json:"expires"` +} + +func parseCredsOut(out string) (map[string][]string, time.Time) { + var credsOut CredshelperOut + if err := json.Unmarshal([]byte(out), &credsOut); err != nil { + log.Errorf("Error while decoding credshelper output: %v", err) + os.Exit(1) + } + hdrs := credsOut.Headers + var exp time.Time + if credsOut.Expires != "" { + expiry, err := time.Parse(time.RFC3339, credsOut.Expires) + if err != nil { + log.Errorf("Failed to parse creds expiry: %v", err) + os.Exit(1) + } + exp = expiry + } + return hdrs, exp +}