@@ -18,14 +18,14 @@ func TestIdentityMapElement(t *testing.T) {
1818 exactMatch := func (sysName , dbname string ) element {
1919 return element {
2020 dbUser : dbname ,
21- pattern : regexp .MustCompile ("^" + regexp .QuoteMeta (sysName ) + "$" ),
21+ pattern : regexp .MustCompile ("(?i) ^" + regexp .QuoteMeta (sysName ) + "$" ),
2222 substituteAt : - 1 ,
2323 }
2424 }
2525 regexMatch := func (sysName , dbName string ) element {
2626 return element {
2727 dbUser : dbName ,
28- pattern : regexp .MustCompile (sysName ),
28+ pattern : regexp .MustCompile ("(?i)" + sysName ),
2929 substituteAt : strings .Index (dbName , `\1` ),
3030 }
3131 }
@@ -65,6 +65,33 @@ func TestIdentityMapElement(t *testing.T) {
65656666 expected : "" ,
6767 },
68+ // Case-insensitive exact match tests.
69+ {
70+ elt : exactMatch ("CaRlItO" , "carl" ),
71+ principal : "carlito" ,
72+ expected : "carl" ,
73+ },
74+ {
75+ elt : exactMatch ("carlito" , "carl" ),
76+ principal : "CARLITO" ,
77+ expected : "carl" ,
78+ },
79+ {
80+ elt : exactMatch ("CaRlItO" , "carl" ),
81+ principal : "CaRlItO" ,
82+ expected : "carl" ,
83+ },
84+ // Case-insensitive regex match tests.
85+ {
86+ elt : regexMatch ("^(.*)@CockroachLabs.com$" , `\1` ),
87+ 88+ expected : "carl" ,
89+ },
90+ {
91+ elt : regexMatch ("^(.*)@cockroachlabs.com$" , `\1` ),
92+ 93+ expected : "Carl" ,
94+ },
6895 }
6996
7097 for idx , tc := range tcs {
130157 _ , mapFound , _ = m .Map ("bar" , "something" )
131158 a .True (mapFound )
132159}
160+
161+ func TestIdentityMapCaseInsensitive (t * testing.T ) {
162+ a := assert .New (t )
163+ data := `
164+ # Test case-insensitive matching for both literal and regex patterns.
165+ # This is important because certificate CNs are normalized to lowercase
166+ # before being passed to the user map.
167+ certmap AbC123DeF456 cert_user
168+ certmap /^([A-Z0-9]+)@example.com$ user_\1
169+ emailmap /^(.*)@COMPANY.COM$ \1
170+ `
171+
172+ m , err := From (strings .NewReader (data ))
173+ if ! a .NoError (err ) {
174+ return
175+ }
176+
177+ // Test literal pattern matching is case-insensitive.
178+ elts , _ , err := m .Map ("certmap" , "abc123def456" )
179+ if a .NoError (err ) && a .Len (elts , 1 ) {
180+ a .Equal ("cert_user" , elts [0 ].Normalized ())
181+ }
182+
183+ // Test uppercase version also matches.
184+ elts , _ , err = m .Map ("certmap" , "ABC123DEF456" )
185+ if a .NoError (err ) && a .Len (elts , 1 ) {
186+ a .Equal ("cert_user" , elts [0 ].Normalized ())
187+ }
188+
189+ // Test mixed case also matches.
190+ elts , _ , err = m .Map ("certmap" , "AbC123dEf456" )
191+ if a .NoError (err ) && a .Len (elts , 1 ) {
192+ a .Equal ("cert_user" , elts [0 ].Normalized ())
193+ }
194+
195+ // Test regex pattern matching is case-insensitive with substitution.
196+ elts ,
_ ,
err = m .
Map (
"certmap" ,
"[email protected] " )
197+ if a .NoError (err ) && a .Len (elts , 1 ) {
198+ a .Equal ("user_abc123" , elts [0 ].Normalized ())
199+ }
200+
201+ // Test uppercase email also matches. Note that the captured group is lowercased
202+ // during SQL username normalization.
203+ elts ,
_ ,
err = m .
Map (
"certmap" ,
"[email protected] " )
204+ if a .NoError (err ) && a .Len (elts , 1 ) {
205+ a .Equal ("user_abc123" , elts [0 ].Normalized ())
206+ }
207+
208+ // Test pattern with uppercase domain matches lowercase input.
209+ elts ,
_ ,
err = m .
Map (
"emailmap" ,
"[email protected] " )
210+ if a .NoError (err ) && a .Len (elts , 1 ) {
211+ a .Equal ("john.doe" , elts [0 ].Normalized ())
212+ }
213+
214+ // Test pattern with uppercase domain matches uppercase input.
215+ // The captured username is normalized to lowercase.
216+ elts ,
_ ,
err = m .
Map (
"emailmap" ,
"[email protected] " )
217+ if a .NoError (err ) && a .Len (elts , 1 ) {
218+ a .Equal ("jane.doe" , elts [0 ].Normalized ())
219+ }
220+ }
221+
222+ func TestIdentityMapCaseInsensitiveDeduplication (t * testing.T ) {
223+ a := assert .New (t )
224+ data := `
225+ # Test that deduplication works correctly with case-insensitive usernames.
226+ # If multiple rules produce usernames that normalize to the same value,
227+ # only the first one should be returned.
228+ 229+ testmap /^(.*)@cockroachlabs.com$ \1
230+ `
231+
232+ m , err := From (strings .NewReader (data ))
233+ if ! a .NoError (err ) {
234+ return
235+ }
236+
237+ // When we pass in [email protected] (note the different casing): 238+ // - First rule matches and produces: carl
239+ // - Second rule matches and produces: Carl (which normalizes to carl)
240+ // We should only get one result since they normalize to the same username.
241+ elts ,
_ ,
err := m .
Map (
"testmap" ,
"[email protected] " )
242+ if a .NoError (err ) && a .Len (elts , 1 ) {
243+ a .Equal ("carl" , elts [0 ].Normalized ())
244+ }
245+
246+ // Also test with lowercase input to verify both rules match but deduplicate.
247+ elts ,
_ ,
err = m .
Map (
"testmap" ,
"[email protected] " )
248+ if a .NoError (err ) && a .Len (elts , 1 ) {
249+ a .Equal ("carl" , elts [0 ].Normalized ())
250+ }
251+
252+ // Test with a completely uppercase input.
253+ elts ,
_ ,
err = m .
Map (
"testmap" ,
"[email protected] " )
254+ if a .NoError (err ) && a .Len (elts , 1 ) {
255+ a .Equal ("carl" , elts [0 ].Normalized ())
256+ }
257+ }
0 commit comments