diff --git a/api/api_test.go b/api/api_test.go index d9e125e6..eb4de053 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -514,7 +514,7 @@ type mockAuthority struct { getRoots func() ([]*x509.Certificate, error) getFederation func() ([]*x509.Certificate, error) getSSHKeys func() (*authority.SSHKeys, error) - getSSHConfig func(typ string) ([]templates.Output, error) + getSSHConfig func(typ string, data map[string]string) ([]templates.Output, error) } // TODO: remove once Authorize is deprecated. @@ -627,9 +627,9 @@ func (m *mockAuthority) GetSSHKeys() (*authority.SSHKeys, error) { return m.ret1.(*authority.SSHKeys), m.err } -func (m *mockAuthority) GetSSHConfig(typ string) ([]templates.Output, error) { +func (m *mockAuthority) GetSSHConfig(typ string, data map[string]string) ([]templates.Output, error) { if m.getSSHConfig != nil { - return m.getSSHConfig(typ) + return m.getSSHConfig(typ, data) } return m.ret1.([]templates.Output), m.err } diff --git a/api/ssh.go b/api/ssh.go index 14c3b7c9..f8142356 100644 --- a/api/ssh.go +++ b/api/ssh.go @@ -18,7 +18,7 @@ type SSHAuthority interface { SignSSH(key ssh.PublicKey, opts provisioner.SSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) SignSSHAddUser(key ssh.PublicKey, cert *ssh.Certificate) (*ssh.Certificate, error) GetSSHKeys() (*authority.SSHKeys, error) - GetSSHConfig(typ string) ([]templates.Output, error) + GetSSHConfig(typ string, data map[string]string) ([]templates.Output, error) } // SignSSHRequest is the request body of an SSH certificate request. @@ -146,7 +146,8 @@ type Template = templates.Output // SSHConfigRequest is the request body used to get the SSH configuration // templates. type SSHConfigRequest struct { - Type string `json:"type"` + Type string `json:"type"` + Data map[string]string `json:"data"` } // Validate checks the values of the SSHConfigurationRequest. @@ -272,7 +273,7 @@ func (h *caHandler) SSHConfig(w http.ResponseWriter, r *http.Request) { return } - ts, err := h.Authority.GetSSHConfig(body.Type) + ts, err := h.Authority.GetSSHConfig(body.Type, body.Data) if err != nil { WriteError(w, InternalServerError(err)) return diff --git a/authority/authority.go b/authority/authority.go index 1e4c3466..b3f5fb94 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -158,8 +158,8 @@ func (a *Authority) init() error { // Configure protected template variables: if t := a.config.Templates; t != nil { - if t.Variables == nil { - t.Variables = make(map[string]interface{}) + if t.Data == nil { + t.Data = make(map[string]interface{}) } var vars templates.Step if a.config.SSH != nil { @@ -170,7 +170,7 @@ func (a *Authority) init() error { vars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey() } } - t.Variables["Step"] = vars + t.Data["Step"] = vars } // JWT numeric dates are seconds. diff --git a/authority/ssh.go b/authority/ssh.go index 68b52c9d..22cdd0f4 100644 --- a/authority/ssh.go +++ b/authority/ssh.go @@ -6,10 +6,9 @@ import ( "net/http" "strings" - "github.com/smallstep/certificates/templates" - "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/templates" "github.com/smallstep/cli/crypto/randutil" "golang.org/x/crypto/ssh" ) @@ -51,7 +50,7 @@ func (a *Authority) GetSSHKeys() (*SSHKeys, error) { } // GetSSHConfig returns rendered templates for clients (user) or servers (host). -func (a *Authority) GetSSHConfig(typ string) ([]templates.Output, error) { +func (a *Authority) GetSSHConfig(typ string, data map[string]string) ([]templates.Output, error) { if a.sshCAUserCertSignKey == nil && a.sshCAHostCertSignKey == nil { return nil, &apiError{ err: errors.New("getSSHConfig: ssh is not configured"), @@ -76,10 +75,23 @@ func (a *Authority) GetSSHConfig(typ string) ([]templates.Output, error) { } } - // Render templates. + // Merge user and default data + var mergedData map[string]interface{} + + if len(data) == 0 { + mergedData = a.config.Templates.Data + } else { + mergedData = make(map[string]interface{}, len(a.config.Templates.Data)+1) + mergedData["User"] = data + for k, v := range a.config.Templates.Data { + mergedData[k] = v + } + } + + // Render templates output := []templates.Output{} for _, t := range ts { - o, err := t.Output(a.config.Templates.Variables) + o, err := t.Output(mergedData) if err != nil { return nil, err } diff --git a/templates/templates.go b/templates/templates.go index 740ddc1c..1ff5cdf2 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -2,7 +2,6 @@ package templates import ( "bytes" - "fmt" "io/ioutil" "text/template" @@ -31,8 +30,8 @@ type Output struct { // Templates is a collection of templates and variables. type Templates struct { - SSH *SSHTemplates `json:"ssh,omitempty"` - Variables map[string]interface{} `json:"variables,omitempty"` + SSH *SSHTemplates `json:"ssh,omitempty"` + Data map[string]interface{} `json:"data,omitempty"` } // Validate returns an error if a template is not valid. @@ -46,10 +45,13 @@ func (t *Templates) Validate() (err error) { return } - // Do not allow "Step" - if t.Variables != nil { - if _, ok := t.Variables["Step"]; ok { - return errors.New("templates variables cannot contain 'step' as a property") + // Do not allow "Step" and "User" + if t.Data != nil { + if _, ok := t.Data["Step"]; ok { + return errors.New("templates variables cannot contain 'Step' as a property") + } + if _, ok := t.Data["User"]; ok { + return errors.New("templates variables cannot contain 'User' as a property") } } return nil @@ -156,7 +158,6 @@ func (t *Template) Render(data interface{}) ([]byte, error) { } buf := new(bytes.Buffer) - fmt.Println(data) if err := t.Execute(buf, data); err != nil { return nil, errors.Wrapf(err, "error executing %s", t.TemplatePath) }