Repository: avenga/couper
Pull request #217 (2021-04-28 14:01:44)

saml refinement: NameIDFormat


Patch:
diff --git a/eval/lib/saml.go b/eval/lib/saml.go
index 8a6bcf3c..93fe6329 100644
--- a/eval/lib/saml.go
+++ b/eval/lib/saml.go
@@ -13,7 +13,10 @@ import (
 	cs "github.com/avenga/couper/config/saml"
 )
 
-const FnSamlSsoUrl = "saml_sso_url"
+const (
+	FnSamlSsoUrl            = "saml_sso_url"
+	NameIdFormatUnspecified = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+)
 
 func NewSamlSsoUrlFunction(samlConfigs []*cs.SAML) function.Function {
 	samls := make(map[string]*cs.SAML)
@@ -55,10 +58,7 @@ func NewSamlSsoUrlFunction(samlConfigs []*cs.SAML) function.Function {
 				}
 			}
 
-			nameIDFormat := ""
-			if len(metadata.IDPSSODescriptor.NameIDFormats) > 0 {
-				nameIDFormat = metadata.IDPSSODescriptor.NameIDFormats[0].Value
-			}
+			nameIDFormat := getNameIDFormat(metadata.IDPSSODescriptor.NameIDFormats)
 
 			sp := &saml2.SAMLServiceProvider{
 				AssertionConsumerServiceURL: saml.SpAcsUrl,
@@ -79,3 +79,22 @@ func NewSamlSsoUrlFunction(samlConfigs []*cs.SAML) function.Function {
 		},
 	})
 }
+
+func getNameIDFormat(supportedNameIDFormats []types.NameIDFormat) string {
+	nameIDFormat := ""
+	if isSupportedNameIDFormat(supportedNameIDFormats, NameIdFormatUnspecified) {
+		nameIDFormat = NameIdFormatUnspecified
+	} else if len(supportedNameIDFormats) > 0 {
+		nameIDFormat = supportedNameIDFormats[0].Value
+	}
+	return nameIDFormat
+}
+
+func isSupportedNameIDFormat(supportedNameIDFormats []types.NameIDFormat, nameIDFormat string) bool {
+	for _, n := range supportedNameIDFormats {
+		if n.Value == nameIDFormat {
+			return true
+		}
+	}
+	return false
+}


Test patch:
diff --git a/eval/lib/saml_internal_test.go b/eval/lib/saml_internal_test.go
new file mode 100644
index 00000000..d8577e18
--- /dev/null
+++ b/eval/lib/saml_internal_test.go
@@ -0,0 +1,58 @@
+package lib
+
+import (
+	"testing"
+
+	"github.com/russellhaering/gosaml2/types"
+)
+
+func Test_getNameIDFormat(t *testing.T) {
+	tests := []struct {
+		name             string
+		supportedFormats []types.NameIDFormat
+		wantFormat       string
+	}{
+		{
+			"only unspecified",
+			[]types.NameIDFormat{
+				{Value: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"},
+			},
+			"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
+		},
+		{
+			"unspecified 1st",
+			[]types.NameIDFormat{
+				{Value: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"},
+				{Value: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"},
+				{Value: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"},
+			},
+			"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
+		},
+		{
+			"unspecified 2nd",
+			[]types.NameIDFormat{
+				{Value: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"},
+				{Value: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"},
+				{Value: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"},
+			},
+			"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
+		},
+		{
+			"no unspecified",
+			[]types.NameIDFormat{
+				{Value: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"},
+				{Value: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"},
+				{Value: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"},
+			},
+			"urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			format := getNameIDFormat(tt.supportedFormats)
+			if format != tt.wantFormat {
+				t.Errorf("Expected format %q, got: %#v", tt.wantFormat, format)
+			}
+		})
+	}
+}