| package auth |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "os/exec" |
| "path/filepath" |
| |
| "github.com/hashicorp/terraform/svchost" |
| ) |
| |
| type helperProgramCredentialsSource struct { |
| executable string |
| args []string |
| } |
| |
| // HelperProgramCredentialsSource returns a CredentialsSource that runs the |
| // given program with the given arguments in order to obtain credentials. |
| // |
| // The given executable path must be an absolute path; it is the caller's |
| // responsibility to validate and process a relative path or other input |
| // provided by an end-user. If the given path is not absolute, this |
| // function will panic. |
| // |
| // When credentials are requested, the program will be run in a child process |
| // with the given arguments along with two additional arguments added to the |
| // end of the list: the literal string "get", followed by the requested |
| // hostname in ASCII compatibility form (punycode form). |
| func HelperProgramCredentialsSource(executable string, args ...string) CredentialsSource { |
| if !filepath.IsAbs(executable) { |
| panic("NewCredentialsSourceHelperProgram requires absolute path to executable") |
| } |
| |
| fullArgs := make([]string, len(args)+1) |
| fullArgs[0] = executable |
| copy(fullArgs[1:], args) |
| |
| return &helperProgramCredentialsSource{ |
| executable: executable, |
| args: fullArgs, |
| } |
| } |
| |
| func (s *helperProgramCredentialsSource) ForHost(host svchost.Hostname) (HostCredentials, error) { |
| args := make([]string, len(s.args), len(s.args)+2) |
| copy(args, s.args) |
| args = append(args, "get") |
| args = append(args, string(host)) |
| |
| outBuf := bytes.Buffer{} |
| errBuf := bytes.Buffer{} |
| |
| cmd := exec.Cmd{ |
| Path: s.executable, |
| Args: args, |
| Stdin: nil, |
| Stdout: &outBuf, |
| Stderr: &errBuf, |
| } |
| err := cmd.Run() |
| if _, isExitErr := err.(*exec.ExitError); isExitErr { |
| errText := errBuf.String() |
| if errText == "" { |
| // Shouldn't happen for a well-behaved helper program |
| return nil, fmt.Errorf("error in %s, but it produced no error message", s.executable) |
| } |
| return nil, fmt.Errorf("error in %s: %s", s.executable, errText) |
| } else if err != nil { |
| return nil, fmt.Errorf("failed to run %s: %s", s.executable, err) |
| } |
| |
| var m map[string]interface{} |
| err = json.Unmarshal(outBuf.Bytes(), &m) |
| if err != nil { |
| return nil, fmt.Errorf("malformed output from %s: %s", s.executable, err) |
| } |
| |
| return HostCredentialsFromMap(m), nil |
| } |