1package ldap_test 2 3import ( 4 "reflect" 5 "testing" 6 7 "gopkg.in/ldap.v2" 8) 9 10func TestSuccessfulDNParsing(t *testing.T) { 11 testcases := map[string]ldap.DN{ 12 "": ldap.DN{[]*ldap.RelativeDN{}}, 13 "cn=Jim\\2C \\22Hasse Hö\\22 Hansson!,dc=dummy,dc=com": ldap.DN{[]*ldap.RelativeDN{ 14 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"cn", "Jim, \"Hasse Hö\" Hansson!"}}}, 15 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"dc", "dummy"}}}, 16 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"dc", "com"}}}}}, 17 "UID=jsmith,DC=example,DC=net": ldap.DN{[]*ldap.RelativeDN{ 18 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"UID", "jsmith"}}}, 19 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "example"}}}, 20 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}}, 21 "OU=Sales+CN=J. Smith,DC=example,DC=net": ldap.DN{[]*ldap.RelativeDN{ 22 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{ 23 &ldap.AttributeTypeAndValue{"OU", "Sales"}, 24 &ldap.AttributeTypeAndValue{"CN", "J. Smith"}}}, 25 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "example"}}}, 26 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}}, 27 "1.3.6.1.4.1.1466.0=#04024869": ldap.DN{[]*ldap.RelativeDN{ 28 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"}}}}}, 29 "1.3.6.1.4.1.1466.0=#04024869,DC=net": ldap.DN{[]*ldap.RelativeDN{ 30 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"}}}, 31 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}}, 32 "CN=Lu\\C4\\8Di\\C4\\87": ldap.DN{[]*ldap.RelativeDN{ 33 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}}, 34 " CN = Lu\\C4\\8Di\\C4\\87 ": ldap.DN{[]*ldap.RelativeDN{ 35 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}}, 36 ` A = 1 , B = 2 `: ldap.DN{[]*ldap.RelativeDN{ 37 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"A", "1"}}}, 38 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"B", "2"}}}}}, 39 ` A = 1 + B = 2 `: ldap.DN{[]*ldap.RelativeDN{ 40 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{ 41 &ldap.AttributeTypeAndValue{"A", "1"}, 42 &ldap.AttributeTypeAndValue{"B", "2"}}}}}, 43 ` \ \ A\ \ = \ \ 1\ \ , \ \ B\ \ = \ \ 2\ \ `: ldap.DN{[]*ldap.RelativeDN{ 44 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{" A ", " 1 "}}}, 45 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{" B ", " 2 "}}}}}, 46 ` \ \ A\ \ = \ \ 1\ \ + \ \ B\ \ = \ \ 2\ \ `: ldap.DN{[]*ldap.RelativeDN{ 47 &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{ 48 &ldap.AttributeTypeAndValue{" A ", " 1 "}, 49 &ldap.AttributeTypeAndValue{" B ", " 2 "}}}}}, 50 } 51 52 for test, answer := range testcases { 53 dn, err := ldap.ParseDN(test) 54 if err != nil { 55 t.Errorf(err.Error()) 56 continue 57 } 58 if !reflect.DeepEqual(dn, &answer) { 59 t.Errorf("Parsed DN %s is not equal to the expected structure", test) 60 t.Logf("Expected:") 61 for _, rdn := range answer.RDNs { 62 for _, attribs := range rdn.Attributes { 63 t.Logf("#%v\n", attribs) 64 } 65 } 66 t.Logf("Actual:") 67 for _, rdn := range dn.RDNs { 68 for _, attribs := range rdn.Attributes { 69 t.Logf("#%v\n", attribs) 70 } 71 } 72 } 73 } 74} 75 76func TestErrorDNParsing(t *testing.T) { 77 testcases := map[string]string{ 78 "*": "DN ended with incomplete type, value pair", 79 "cn=Jim\\0Test": "Failed to decode escaped character: encoding/hex: invalid byte: U+0054 'T'", 80 "cn=Jim\\0": "Got corrupted escaped character", 81 "DC=example,=net": "DN ended with incomplete type, value pair", 82 "1=#0402486": "Failed to decode BER encoding: encoding/hex: odd length hex string", 83 "test,DC=example,DC=com": "incomplete type, value pair", 84 "=test,DC=example,DC=com": "incomplete type, value pair", 85 } 86 87 for test, answer := range testcases { 88 _, err := ldap.ParseDN(test) 89 if err == nil { 90 t.Errorf("Expected %s to fail parsing but succeeded\n", test) 91 } else if err.Error() != answer { 92 t.Errorf("Unexpected error on %s:\n%s\nvs.\n%s\n", test, answer, err.Error()) 93 } 94 } 95} 96 97func TestDNEqual(t *testing.T) { 98 testcases := []struct { 99 A string 100 B string 101 Equal bool 102 }{ 103 // Exact match 104 {"", "", true}, 105 {"o=A", "o=A", true}, 106 {"o=A", "o=B", false}, 107 108 {"o=A,o=B", "o=A,o=B", true}, 109 {"o=A,o=B", "o=A,o=C", false}, 110 111 {"o=A+o=B", "o=A+o=B", true}, 112 {"o=A+o=B", "o=A+o=C", false}, 113 114 // Case mismatch in type is ignored 115 {"o=A", "O=A", true}, 116 {"o=A,o=B", "o=A,O=B", true}, 117 {"o=A+o=B", "o=A+O=B", true}, 118 119 // Case mismatch in value is significant 120 {"o=a", "O=A", false}, 121 {"o=a,o=B", "o=A,O=B", false}, 122 {"o=a+o=B", "o=A+O=B", false}, 123 124 // Multi-valued RDN order mismatch is ignored 125 {"o=A+o=B", "O=B+o=A", true}, 126 // Number of RDN attributes is significant 127 {"o=A+o=B", "O=B+o=A+O=B", false}, 128 129 // Missing values are significant 130 {"o=A+o=B", "O=B+o=A+O=C", false}, // missing values matter 131 {"o=A+o=B+o=C", "O=B+o=A", false}, // missing values matter 132 133 // Whitespace tests 134 // Matching 135 { 136 "cn=John Doe, ou=People, dc=sun.com", 137 "cn=John Doe, ou=People, dc=sun.com", 138 true, 139 }, 140 // Difference in leading/trailing chars is ignored 141 { 142 "cn=John Doe, ou=People, dc=sun.com", 143 "cn=John Doe,ou=People,dc=sun.com", 144 true, 145 }, 146 // Difference in values is significant 147 { 148 "cn=John Doe, ou=People, dc=sun.com", 149 "cn=John Doe, ou=People, dc=sun.com", 150 false, 151 }, 152 } 153 154 for i, tc := range testcases { 155 a, err := ldap.ParseDN(tc.A) 156 if err != nil { 157 t.Errorf("%d: %v", i, err) 158 continue 159 } 160 b, err := ldap.ParseDN(tc.B) 161 if err != nil { 162 t.Errorf("%d: %v", i, err) 163 continue 164 } 165 if expected, actual := tc.Equal, a.Equal(b); expected != actual { 166 t.Errorf("%d: when comparing '%s' and '%s' expected %v, got %v", i, tc.A, tc.B, expected, actual) 167 continue 168 } 169 if expected, actual := tc.Equal, b.Equal(a); expected != actual { 170 t.Errorf("%d: when comparing '%s' and '%s' expected %v, got %v", i, tc.A, tc.B, expected, actual) 171 continue 172 } 173 } 174} 175 176func TestDNAncestor(t *testing.T) { 177 testcases := []struct { 178 A string 179 B string 180 Ancestor bool 181 }{ 182 // Exact match returns false 183 {"", "", false}, 184 {"o=A", "o=A", false}, 185 {"o=A,o=B", "o=A,o=B", false}, 186 {"o=A+o=B", "o=A+o=B", false}, 187 188 // Mismatch 189 {"ou=C,ou=B,o=A", "ou=E,ou=D,ou=B,o=A", false}, 190 191 // Descendant 192 {"ou=C,ou=B,o=A", "ou=E,ou=C,ou=B,o=A", true}, 193 } 194 195 for i, tc := range testcases { 196 a, err := ldap.ParseDN(tc.A) 197 if err != nil { 198 t.Errorf("%d: %v", i, err) 199 continue 200 } 201 b, err := ldap.ParseDN(tc.B) 202 if err != nil { 203 t.Errorf("%d: %v", i, err) 204 continue 205 } 206 if expected, actual := tc.Ancestor, a.AncestorOf(b); expected != actual { 207 t.Errorf("%d: when comparing '%s' and '%s' expected %v, got %v", i, tc.A, tc.B, expected, actual) 208 continue 209 } 210 } 211} 212