Skip to content

Commit c3b2f51

Browse files
authored
feat: support distinguished name state(S) (#432)
Resolves #431 Signed-off-by: Junjie Gao <[email protected]> --------- Signed-off-by: Junjie Gao <[email protected]>
1 parent 09e32d7 commit c3b2f51

File tree

3 files changed

+153
-5
lines changed

3 files changed

+153
-5
lines changed

internal/pkix/pkix.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,10 @@ func ParseDistinguishedName(name string) (map[string]string, error) {
2626
return nil, fmt.Errorf("unsupported distinguished name (DN) %q: notation does not support x509.subject identities containing \"=#\"", name)
2727
}
2828

29-
mandatoryFields := []string{"C", "ST", "O"}
3029
attrKeyValue := make(map[string]string)
3130
dn, err := ldapv3.ParseDN(name)
3231
if err != nil {
33-
return nil, fmt.Errorf("parsing distinguished name (DN) %q failed with err: %v. A valid DN must contain 'C', 'ST', and 'O' RDN attributes at a minimum, and follow RFC 4514 standard", name, err)
32+
return nil, fmt.Errorf("parsing distinguished name (DN) %q failed with err: %v. A valid DN must contain 'C', 'ST' or 'S', and 'O' RDN attributes at a minimum, and follow RFC 4514 standard", name, err)
3433
}
3534

3635
for _, rdn := range dn.RDNs {
@@ -39,6 +38,10 @@ func ParseDistinguishedName(name string) (map[string]string, error) {
3938
return nil, fmt.Errorf("distinguished name (DN) %q has multi-valued RDN attributes, remove multi-valued RDN attributes as they are not supported", name)
4039
}
4140
for _, attribute := range rdn.Attributes {
41+
// stateOrProvince name 'S' is an alias for 'ST'
42+
if attribute.Type == "S" {
43+
attribute.Type = "ST"
44+
}
4245
if attrKeyValue[attribute.Type] == "" {
4346
attrKeyValue[attribute.Type] = attribute.Value
4447
} else {
@@ -48,11 +51,13 @@ func ParseDistinguishedName(name string) (map[string]string, error) {
4851
}
4952

5053
// Verify mandatory fields are present
54+
mandatoryFields := []string{"C", "ST", "O"}
5155
for _, field := range mandatoryFields {
5256
if attrKeyValue[field] == "" {
53-
return nil, fmt.Errorf("distinguished name (DN) %q has no mandatory RDN attribute for %q, it must contain 'C', 'ST', and 'O' RDN attributes at a minimum", name, field)
57+
return nil, fmt.Errorf("distinguished name (DN) %q has no mandatory RDN attribute for %q, it must contain 'C', 'ST' or 'S', and 'O' RDN attributes at a minimum", name, field)
5458
}
5559
}
60+
5661
// No errors
5762
return attrKeyValue, nil
5863
}

internal/pkix/pkix_test.go

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright The Notary Project Authors.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package pkix
15+
16+
import "testing"
17+
18+
func TestParseDistinguishedName(t *testing.T) {
19+
// Test cases
20+
tests := []struct {
21+
name string
22+
input string
23+
wantErr bool
24+
}{
25+
{
26+
name: "valid DN",
27+
input: "C=US,ST=California,O=Notary Project",
28+
wantErr: false,
29+
},
30+
{
31+
name: "valid DN with State alias",
32+
input: "C=US,S=California,O=Notary Project",
33+
wantErr: false,
34+
},
35+
{
36+
name: "invalid DN",
37+
input: "C=US,ST=California",
38+
wantErr: true,
39+
},
40+
{
41+
name: "invalid DN without State",
42+
input: "C=US,O=Notary Project",
43+
wantErr: true,
44+
},
45+
{
46+
name: "invalid DN without State",
47+
input: "invalid",
48+
wantErr: true,
49+
},
50+
{
51+
name: "duplicate RDN attribute",
52+
input: "C=US,ST=California,O=Notary Project,S=California",
53+
wantErr: true,
54+
},
55+
{
56+
name: "unsupported DN =#",
57+
input: "C=US,ST=California,O=Notary Project=#",
58+
wantErr: true,
59+
},
60+
{
61+
name: "multi-valued RDN attributes",
62+
input: "OU=Sales+CN=J. Smith,DC=example,DC=net",
63+
wantErr: true,
64+
},
65+
}
66+
67+
// Run tests
68+
for _, tt := range tests {
69+
t.Run(tt.name, func(t *testing.T) {
70+
_, err := ParseDistinguishedName(tt.input)
71+
if tt.wantErr != (err != nil) {
72+
t.Errorf("ParseDistinguishedName() error = %v, wantErr %v", err, tt.wantErr)
73+
}
74+
})
75+
}
76+
}
77+
78+
func TestIsSubsetDN(t *testing.T) {
79+
// Test cases
80+
tests := []struct {
81+
name string
82+
dn1 map[string]string
83+
dn2 map[string]string
84+
want bool
85+
}{
86+
{
87+
name: "subset DN",
88+
dn1: map[string]string{
89+
"C": "US",
90+
"ST": "California",
91+
"O": "Notary Project",
92+
},
93+
dn2: map[string]string{
94+
"C": "US",
95+
"ST": "California",
96+
"O": "Notary Project",
97+
"L": "Los Angeles",
98+
},
99+
want: true,
100+
},
101+
{
102+
name: "not subset DN",
103+
dn1: map[string]string{
104+
"C": "US",
105+
"ST": "California",
106+
"O": "Notary Project",
107+
},
108+
dn2: map[string]string{
109+
"C": "US",
110+
"ST": "California",
111+
"O": "Notary Project 2",
112+
"L": "Los Angeles",
113+
"CN": "Notary",
114+
},
115+
want: false,
116+
},
117+
{
118+
name: "not subset DN 2",
119+
dn1: map[string]string{
120+
"C": "US",
121+
"ST": "California",
122+
"O": "Notary Project",
123+
"CN": "Notary",
124+
},
125+
dn2: map[string]string{
126+
"C": "US",
127+
"ST": "California",
128+
"O": "Notary Project",
129+
"L": "Los Angeles",
130+
},
131+
want: false,
132+
},
133+
}
134+
135+
// Run tests
136+
for _, tt := range tests {
137+
t.Run(tt.name, func(t *testing.T) {
138+
if got := IsSubsetDN(tt.dn1, tt.dn2); got != tt.want {
139+
t.Errorf("IsSubsetDN() = %v, want %v", got, tt.want)
140+
}
141+
})
142+
}
143+
}

verifier/trustpolicy/trustpolicy_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func TestValidateTrustedIdentities(t *testing.T) {
165165
// Validate x509.subject identities
166166
invalidDN := "x509.subject:,,,"
167167
err = validateTrustedIdentities("test-statement-name", []string{invalidDN})
168-
if err == nil || err.Error() != "trust policy statement \"test-statement-name\" has trusted identity \"x509.subject:,,,\" with invalid identity value: parsing distinguished name (DN) \",,,\" failed with err: incomplete type, value pair. A valid DN must contain 'C', 'ST', and 'O' RDN attributes at a minimum, and follow RFC 4514 standard" {
168+
if err == nil || err.Error() != "trust policy statement \"test-statement-name\" has trusted identity \"x509.subject:,,,\" with invalid identity value: parsing distinguished name (DN) \",,,\" failed with err: incomplete type, value pair. A valid DN must contain 'C', 'ST' or 'S', and 'O' RDN attributes at a minimum, and follow RFC 4514 standard" {
169169
t.Fatalf("invalid x509.subject identity should return error. Error : %q", err)
170170
}
171171

@@ -185,7 +185,7 @@ func TestValidateTrustedIdentities(t *testing.T) {
185185
// Validate mandatory RDNs
186186
invalidDN = "x509.subject:C=US,ST=WA"
187187
err = validateTrustedIdentities("test-statement-name", []string{invalidDN})
188-
if err == nil || err.Error() != "trust policy statement \"test-statement-name\" has trusted identity \"x509.subject:C=US,ST=WA\" with invalid identity value: distinguished name (DN) \"C=US,ST=WA\" has no mandatory RDN attribute for \"O\", it must contain 'C', 'ST', and 'O' RDN attributes at a minimum" {
188+
if err == nil || err.Error() != "trust policy statement \"test-statement-name\" has trusted identity \"x509.subject:C=US,ST=WA\" with invalid identity value: distinguished name (DN) \"C=US,ST=WA\" has no mandatory RDN attribute for \"O\", it must contain 'C', 'ST' or 'S', and 'O' RDN attributes at a minimum" {
189189
t.Fatalf("invalid x509.subject identity should return error. Error : %q", err)
190190
}
191191

0 commit comments

Comments
 (0)