Repository: smallstep/crypto
Pull request #58 (2022-08-18 16:54:22)

Add TemplateData.SetWebhooks method
### Description
Adds methods to sshutil.TemplateData and x509util.TemplateData to set arbitrary data under the key `Webhooks`.

💔Thank you!



Patch:
diff --git a/sshutil/templates.go b/sshutil/templates.go
index 48d4871..de1f898 100644
--- a/sshutil/templates.go
+++ b/sshutil/templates.go
@@ -15,6 +15,7 @@ const (
 	CertificateRequestKey = "CR"
 	AuthorizationCrtKey   = "AuthorizationCrt"
 	AuthorizationChainKey = "AuthorizationChain"
+	WebhooksKey           = "Webhooks"
 )
 
 // TemplateError represents an error in a template produced by the fail
@@ -167,6 +168,17 @@ func (t TemplateData) SetCertificateRequest(cr CertificateRequest) {
 	t.SetInsecure(CertificateRequestKey, cr)
 }
 
+// SetWebhook sets the given webhook response in the webhooks template data.
+func (t TemplateData) SetWebhook(webhookName string, data interface{}) {
+	if webhooksMap, ok := t[WebhooksKey].(map[string]interface{}); ok {
+		webhooksMap[webhookName] = data
+	} else {
+		t[WebhooksKey] = map[string]interface{}{
+			webhookName: data,
+		}
+	}
+}
+
 // DefaultTemplate is the default template for an SSH certificate.
 const DefaultTemplate = `{
 	"type": {{ toJson .Type }},
diff --git a/x509util/templates.go b/x509util/templates.go
index 335f417..059cf7f 100644
--- a/x509util/templates.go
+++ b/x509util/templates.go
@@ -16,6 +16,7 @@ const (
 	CertificateRequestKey = "CR"
 	AuthorizationCrtKey   = "AuthorizationCrt"
 	AuthorizationChainKey = "AuthorizationChain"
+	WebhooksKey           = "Webhooks"
 )
 
 // TemplateError represents an error in a template produced by the fail
@@ -125,6 +126,17 @@ func (t TemplateData) SetCertificateRequest(cr *x509.CertificateRequest) {
 	t.SetInsecure(CertificateRequestKey, newCertificateRequest(cr))
 }
 
+// SetWebhook sets the given webhook response in the webhooks template data.
+func (t TemplateData) SetWebhook(webhookName string, data interface{}) {
+	if webhooksMap, ok := t[WebhooksKey].(map[string]interface{}); ok {
+		webhooksMap[webhookName] = data
+	} else {
+		t[WebhooksKey] = map[string]interface{}{
+			webhookName: data,
+		}
+	}
+}
+
 // DefaultLeafTemplate is the default template used to generate a leaf
 // certificate.
 const DefaultLeafTemplate = `{


Test patch:
diff --git a/sshutil/templates_test.go b/sshutil/templates_test.go
index f4be069..66fb5b5 100644
--- a/sshutil/templates_test.go
+++ b/sshutil/templates_test.go
@@ -554,6 +554,31 @@ func TestTemplateData_SetCertificateRequest(t *testing.T) {
 	}
 }
 
+func TestTemplateData_SetWebhook(t *testing.T) {
+	type args struct {
+		name string
+		v    interface{}
+	}
+	tests := []struct {
+		name string
+		td   TemplateData
+		args args
+		want TemplateData
+	}{
+		{"empty", TemplateData{}, args{"foo", "bar"}, TemplateData{WebhooksKey: map[string]interface{}{"foo": "bar"}}},
+		{"overwrite", TemplateData{WebhooksKey: map[string]interface{}{"foo": "bar"}}, args{"foo", "zar"}, TemplateData{WebhooksKey: map[string]interface{}{"foo": "zar"}}},
+		{"existing", TemplateData{WebhooksKey: map[string]interface{}{"foo": "bar"}}, args{"bar", "foo"}, TemplateData{WebhooksKey: map[string]interface{}{"foo": "bar", "bar": "foo"}}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.td.SetWebhook(tt.args.name, tt.args.v)
+			if !reflect.DeepEqual(tt.td, tt.want) {
+				t.Errorf("TemplateData.SetWebhook() = %v, want %v", tt.td, tt.want)
+			}
+		})
+	}
+}
+
 func TestValidateTemplate(t *testing.T) {
 	tests := []struct {
 		name    string
diff --git a/x509util/templates_test.go b/x509util/templates_test.go
index 92c34ad..a3d1653 100644
--- a/x509util/templates_test.go
+++ b/x509util/templates_test.go
@@ -381,6 +381,31 @@ func TestTemplateData_SetCertificateRequest(t *testing.T) {
 	}
 }
 
+func TestTemplateData_SetWebhook(t *testing.T) {
+	type args struct {
+		name string
+		v    interface{}
+	}
+	tests := []struct {
+		name string
+		td   TemplateData
+		args args
+		want TemplateData
+	}{
+		{"empty", TemplateData{}, args{"foo", "bar"}, TemplateData{WebhooksKey: map[string]interface{}{"foo": "bar"}}},
+		{"overwrite", TemplateData{WebhooksKey: map[string]interface{}{"foo": "bar"}}, args{"foo", "zar"}, TemplateData{WebhooksKey: map[string]interface{}{"foo": "zar"}}},
+		{"existing", TemplateData{WebhooksKey: map[string]interface{}{"foo": "bar"}}, args{"bar", "foo"}, TemplateData{WebhooksKey: map[string]interface{}{"foo": "bar", "bar": "foo"}}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.td.SetWebhook(tt.args.name, tt.args.v)
+			if !reflect.DeepEqual(tt.td, tt.want) {
+				t.Errorf("TemplateData.SetWebhook() = %v, want %v", tt.td, tt.want)
+			}
+		})
+	}
+}
+
 func TestValidateTemplate(t *testing.T) {
 	tests := []struct {
 		name    string