Repository: smallstep/crypto
Pull request #32 (2021-12-29 23:44:49)

Certificates in templates
### Description

This PR adds fixed keys and methods to add certificates and certificate chains to the templates. If a provisioner adds those methods they will be accessible from the template like `{{ .AuthorizationCrt }}` or `{{ .AuthorizationChain }}`.


Patch:
diff --git a/sshutil/templates.go b/sshutil/templates.go
index 84a05ec..b6adc70 100644
--- a/sshutil/templates.go
+++ b/sshutil/templates.go
@@ -11,6 +11,8 @@ const (
 	InsecureKey           = "Insecure"
 	UserKey               = "User"
 	CertificateRequestKey = "CR"
+	AuthorizationCrtKey   = "AuthorizationCrt"
+	AuthorizationChainKey = "AuthorizationChain"
 )
 
 // TemplateError represents an error in a template produced by the fail
@@ -134,6 +136,18 @@ func (t TemplateData) SetUserData(v interface{}) {
 	t.SetInsecure(UserKey, v)
 }
 
+// SetAuthorizationCertificate sets the given certificate in the template. This
+// certificate is generally present in a token header.
+func (t TemplateData) SetAuthorizationCertificate(crt interface{}) {
+	t.Set(AuthorizationCrtKey, crt)
+}
+
+// SetAuthorizationCertificateChain sets a the given certificate chain in the
+// template. These certificates are generally present in a token header.
+func (t TemplateData) SetAuthorizationCertificateChain(chain interface{}) {
+	t.Set(AuthorizationChainKey, chain)
+}
+
 // SetCertificateRequest sets the simulated ssh certificate request the insecure
 // template data.
 func (t TemplateData) SetCertificateRequest(cr CertificateRequest) {
diff --git a/x509util/templates.go b/x509util/templates.go
index bdfce60..4845700 100644
--- a/x509util/templates.go
+++ b/x509util/templates.go
@@ -12,6 +12,8 @@ const (
 	InsecureKey           = "Insecure"
 	UserKey               = "User"
 	CertificateRequestKey = "CR"
+	AuthorizationCrtKey   = "AuthorizationCrt"
+	AuthorizationChainKey = "AuthorizationChain"
 )
 
 // TemplateError represents an error in a template produced by the fail
@@ -87,6 +89,18 @@ func (t TemplateData) SetUserData(v interface{}) {
 	t.SetInsecure(UserKey, v)
 }
 
+// SetAuthorizationCertificate sets the given certificate in the template. This certificate
+// is generally present in a token header.
+func (t TemplateData) SetAuthorizationCertificate(crt interface{}) {
+	t.Set(AuthorizationCrtKey, crt)
+}
+
+// SetAuthorizationCertificateChain sets a the given certificate chain in the
+// template. These certificates are generally present in a token header.
+func (t TemplateData) SetAuthorizationCertificateChain(chain interface{}) {
+	t.Set(AuthorizationChainKey, chain)
+}
+
 // SetCertificateRequest sets the given certificate request in the insecure
 // template data.
 func (t TemplateData) SetCertificateRequest(cr *x509.CertificateRequest) {


Test patch:
diff --git a/sshutil/templates_test.go b/sshutil/templates_test.go
index ac4152b..8981d8e 100644
--- a/sshutil/templates_test.go
+++ b/sshutil/templates_test.go
@@ -441,6 +441,80 @@ func TestTemplateData_SetUserData(t *testing.T) {
 	}
 }
 
+func TestTemplateData_SetAuthorizationCertificate(t *testing.T) {
+	crt1 := Certificate{Key: mustGeneratePublicKey(t)}
+	crt2 := Certificate{Key: mustGeneratePublicKey(t)}
+	type args struct {
+		crt Certificate
+	}
+	tests := []struct {
+		name string
+		t    TemplateData
+		args args
+		want TemplateData
+	}{
+		{"ok", TemplateData{}, args{crt1}, TemplateData{
+			AuthorizationCrtKey: crt1,
+		}},
+		{"overwrite", TemplateData{
+			AuthorizationCrtKey: crt1,
+			InsecureKey: TemplateData{
+				UserKey: "data",
+			},
+		}, args{crt2}, TemplateData{
+			AuthorizationCrtKey: crt2,
+			InsecureKey: TemplateData{
+				UserKey: "data",
+			},
+		}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.t.SetAuthorizationCertificate(tt.args.crt)
+			if !reflect.DeepEqual(tt.t, tt.want) {
+				t.Errorf("TemplateData.SetCertificate() = %v, want %v", tt.t, tt.want)
+			}
+		})
+	}
+}
+
+func TestTemplateData_SetAuthorizationCertificateChain(t *testing.T) {
+	crt1 := Certificate{Key: mustGeneratePublicKey(t)}
+	crt2 := Certificate{Key: mustGeneratePublicKey(t)}
+	type args struct {
+		crt []interface{}
+	}
+	tests := []struct {
+		name string
+		t    TemplateData
+		args args
+		want TemplateData
+	}{
+		{"ok", TemplateData{}, args{[]interface{}{crt1, crt2}}, TemplateData{
+			AuthorizationChainKey: []interface{}{crt1, crt2},
+		}},
+		{"overwrite", TemplateData{
+			AuthorizationChainKey: []interface{}{crt1, crt2},
+			InsecureKey: TemplateData{
+				UserKey: "data",
+			},
+		}, args{[]interface{}{crt1}}, TemplateData{
+			AuthorizationChainKey: []interface{}{crt1},
+			InsecureKey: TemplateData{
+				UserKey: "data",
+			},
+		}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.t.SetAuthorizationCertificateChain(tt.args.crt)
+			if !reflect.DeepEqual(tt.t, tt.want) {
+				t.Errorf("TemplateData.SetCertificate() = %v, want %v", tt.t, tt.want)
+			}
+		})
+	}
+}
+
 func TestTemplateData_SetCertificateRequest(t *testing.T) {
 	cr1 := CertificateRequest{Key: mustGeneratePublicKey(t)}
 	cr2 := CertificateRequest{Key: mustGeneratePublicKey(t)}
diff --git a/x509util/templates_test.go b/x509util/templates_test.go
index c5f66a9..a957654 100644
--- a/x509util/templates_test.go
+++ b/x509util/templates_test.go
@@ -226,6 +226,80 @@ func TestTemplateData_SetUserData(t *testing.T) {
 	}
 }
 
+func TestTemplateData_SetAuthorizationCertificate(t *testing.T) {
+	crt1 := Certificate{DNSNames: []string{"crt1"}}
+	crt2 := Certificate{DNSNames: []string{"crt2"}}
+	type args struct {
+		crt Certificate
+	}
+	tests := []struct {
+		name string
+		t    TemplateData
+		args args
+		want TemplateData
+	}{
+		{"ok", TemplateData{}, args{crt1}, TemplateData{
+			AuthorizationCrtKey: crt1,
+		}},
+		{"overwrite", TemplateData{
+			AuthorizationCrtKey: crt1,
+			InsecureKey: TemplateData{
+				UserKey: "data",
+			},
+		}, args{crt2}, TemplateData{
+			AuthorizationCrtKey: crt2,
+			InsecureKey: TemplateData{
+				UserKey: "data",
+			},
+		}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.t.SetAuthorizationCertificate(tt.args.crt)
+			if !reflect.DeepEqual(tt.t, tt.want) {
+				t.Errorf("TemplateData.SetCertificate() = %v, want %v", tt.t, tt.want)
+			}
+		})
+	}
+}
+
+func TestTemplateData_SetAuthorizationCertificateChain(t *testing.T) {
+	crt1 := Certificate{DNSNames: []string{"crt1"}}
+	crt2 := Certificate{DNSNames: []string{"crt2"}}
+	type args struct {
+		crt []interface{}
+	}
+	tests := []struct {
+		name string
+		t    TemplateData
+		args args
+		want TemplateData
+	}{
+		{"ok", TemplateData{}, args{[]interface{}{crt1, crt2}}, TemplateData{
+			AuthorizationChainKey: []interface{}{crt1, crt2},
+		}},
+		{"overwrite", TemplateData{
+			AuthorizationChainKey: []interface{}{crt1, crt2},
+			InsecureKey: TemplateData{
+				UserKey: "data",
+			},
+		}, args{[]interface{}{crt1}}, TemplateData{
+			AuthorizationChainKey: []interface{}{crt1},
+			InsecureKey: TemplateData{
+				UserKey: "data",
+			},
+		}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.t.SetAuthorizationCertificateChain(tt.args.crt)
+			if !reflect.DeepEqual(tt.t, tt.want) {
+				t.Errorf("TemplateData.SetCertificate() = %v, want %v", tt.t, tt.want)
+			}
+		})
+	}
+}
+
 func TestTemplateData_SetCertificateRequest(t *testing.T) {
 	cr := &x509.CertificateRequest{
 		DNSNames: []string{"foo", "bar"},