1111 * See the License for the specific language governing permissions and
1212 * limitations under the License.
1313 */
14- package io .streamnative .pulsar .handlers .mqtt .identitypool ;
15-
16- import static io .streamnative .pulsar .handlers .mqtt .identitypool .ExpressionCompiler .DN ;
17- import static io .streamnative .pulsar .handlers .mqtt .identitypool .ExpressionCompiler .DN_KEYS ;
18- import static io .streamnative .pulsar .handlers .mqtt .identitypool .ExpressionCompiler .SAN ;
19- import static io .streamnative .pulsar .handlers .mqtt .identitypool .ExpressionCompiler .SHA1 ;
20- import static io .streamnative .pulsar .handlers .mqtt .identitypool .ExpressionCompiler .SNID ;
14+ package io .streamnative .pulsar .handlers .mqtt .authentication ;
15+
16+ import static io .streamnative .pulsar .handlers .mqtt .authentication .ExpressionCompiler .DN ;
17+ import static io .streamnative .pulsar .handlers .mqtt .authentication .ExpressionCompiler .SAN ;
18+ import static io .streamnative .pulsar .handlers .mqtt .authentication .ExpressionCompiler .SHA1 ;
19+ import static io .streamnative .pulsar .handlers .mqtt .authentication .ExpressionCompiler .SNID ;
20+ import com .fasterxml .jackson .core .JsonProcessingException ;
21+ import com .fasterxml .jackson .databind .ObjectMapper ;
2122import com .google .common .annotations .VisibleForTesting ;
2223import com .google .common .base .Joiner ;
2324import io .streamnative .oidc .broker .common .OIDCPoolResources ;
4344import lombok .extern .slf4j .Slf4j ;
4445import org .apache .commons .codec .binary .Hex ;
4546import org .apache .commons .lang .StringUtils ;
47+ import org .apache .commons .lang .text .StrBuilder ;
4648import org .apache .pulsar .broker .ServiceConfiguration ;
4749import org .apache .pulsar .broker .authentication .AuthenticationDataSource ;
4850import org .apache .pulsar .broker .authentication .AuthenticationProvider ;
4951import org .apache .pulsar .broker .authentication .metrics .AuthenticationMetrics ;
52+ import org .apache .pulsar .common .util .ObjectMapperFactory ;
5053import org .apache .pulsar .metadata .api .MetadataStore ;
5154import org .apache .pulsar .metadata .api .MetadataStoreConfig ;
5255import org .apache .pulsar .metadata .api .MetadataStoreException ;
@@ -64,6 +67,8 @@ public class AuthenticationProviderMTls implements AuthenticationProvider {
6467 @ VisibleForTesting
6568 private OIDCPoolResources poolResources ;
6669
70+ private final ObjectMapper objectMapper = ObjectMapperFactory .create ();
71+
6772 @ Getter
6873 @ VisibleForTesting
6974 private final ConcurrentHashMap <String , ExpressionCompiler > poolMap = new ConcurrentHashMap <>();
@@ -212,7 +217,7 @@ public String authenticate(AuthenticationDataSource authData) throws Authenticat
212217 final X509Certificate certificate = (X509Certificate ) certs [0 ];
213218
214219 // parse DN
215- Map <String , Object > params ;
220+ Map <String , String > params ;
216221 try {
217222 String subject = certificate .getSubjectX500Principal ().getName ();
218223 params = parseDN (subject );
@@ -228,20 +233,26 @@ public String authenticate(AuthenticationDataSource authData) throws Authenticat
228233 // parse SHA1
229234 params .put (SHA1 , parseSHA1FingerPrint (certificate ));
230235
231- String principal = matchPool (params );
232- if (principal .isEmpty ()) {
236+ String poolName = matchPool (params );
237+ if (poolName .isEmpty ()) {
233238 errorCode = ErrorCode .NO_MATCH_POOL ;
234239 throw new AuthenticationException ("No matched identity pool from the client certificate" );
235240 }
241+ AuthRequest authRequest = new AuthRequest (poolName , params );
242+ String authRequestJson = objectMapper .writeValueAsString (authRequest );
236243 metrics .recordSuccess ();
237- return principal ;
244+ return authRequestJson ;
238245 } catch (AuthenticationException e ) {
239246 metrics .recordFailure (errorCode );
240247 throw e ;
248+ } catch (JsonProcessingException e ) {
249+ log .error ("Failed to serialize the auth request" , e );
250+ metrics .recordFailure (errorCode );
251+ throw new AuthenticationException (e .getMessage ());
241252 }
242253 }
243254
244- public String matchPool (Map <String , Object > params ) throws AuthenticationException {
255+ public String matchPool (Map <String , String > params ) throws AuthenticationException {
245256 List <String > principals = new ArrayList <>();
246257 poolMap .forEach ((poolName , compiler ) -> {
247258 Boolean matched = false ;
@@ -284,32 +295,38 @@ static String parseSHA1FingerPrint(X509Certificate certificate) {
284295 }
285296 }
286297
287- static Map <String , Object > parseDN (String dn ) throws InvalidNameException {
288- Map <String , Object > params = new HashMap <>();
298+ static Map <String , String > parseDN (String dn ) throws InvalidNameException {
299+ Map <String , String > params = new HashMap <>();
289300 if (StringUtils .isEmpty (dn )) {
290301 return params ;
291302 }
292303 params .put (DN , dn );
293304 LdapName ldapName = new LdapName (dn );
294305 for (Rdn rdn : ldapName .getRdns ()) {
295306 String rdnType = rdn .getType ().toUpperCase ();
296- if (DN_KEYS .contains (rdnType )) {
297- String value = Rdn .escapeValue (rdn .getValue ());
298- value = value .replace ("\r " , "\\ 0D" );
299- value = value .replace ("\n " , "\\ 0A" );
300- params .put (rdnType , value );
301- }
307+ String value = Rdn .escapeValue (rdn .getValue ());
308+ value = value .replace ("\r " , "\\ 0D" );
309+ value = value .replace ("\n " , "\\ 0A" );
310+ params .put (rdnType , value );
302311 }
303312
304313 return params ;
305314 }
306315
307- static void parseSAN (X509Certificate certificate , @ NotNull Map <String , Object > map ) {
316+ static void parseSAN (X509Certificate certificate , @ NotNull Map <String , String > map ) {
308317 try {
318+ // byte[] extensionValue = certificate.getExtensionValue("2.5.29.17");
319+ // TODO How to get the original extension name
309320 Collection <List <?>> subjectAlternativeNames = certificate .getSubjectAlternativeNames ();
310321 if (subjectAlternativeNames != null ) {
311322 List <String > formattedSANList = subjectAlternativeNames .stream ()
312- .map (list -> getSanName ((int ) list .get (0 )) + ":" + list .get (1 ))
323+ .map (list -> {
324+ String sanName = getSanName ((int ) list .get (0 ));
325+ String sanValue = (String ) list .get (1 );
326+ map .put (sanName , sanValue );
327+ sanName = mapSANNames (sanName , sanValue , map );
328+ return sanName + ":" + sanValue ;
329+ })
313330 .collect (Collectors .toList ());
314331 String formattedSAN = String .join ("," , formattedSANList );
315332 map .put (SAN , formattedSAN );
@@ -319,10 +336,27 @@ static void parseSAN(X509Certificate certificate, @NotNull Map<String, Object> m
319336 }
320337 }
321338
339+ static String mapSANNames (String sanName , String sanValue , @ NotNull Map <String , String > map ) {
340+ String newSanName = sanName ;
341+ // "RFC822NAME:aaa" -> "EMAIL:aaa,DEVICE_ID:aaa,RFC822NAME:aaa"
342+ if (sanName .equals ("DNS" )) {
343+ StrBuilder strBuilder = new StrBuilder ();
344+ strBuilder .append ("EMAIL:" ).append (sanValue ).append ("," );
345+ map .put ("EMAIL" , sanValue );
346+
347+ // strBuilder.append("DEVICE_ID:").append(sanValue).append(",");
348+ // map.put("DEVICE_ID", sanValue);
349+
350+ strBuilder .append (sanName );
351+ newSanName = strBuilder .toString ();
352+ }
353+ return newSanName ;
354+ }
355+
322356 private static String getSanName (int type ) {
323357 return switch (type ) {
324358 case 0 -> "OTHERNAME" ;
325- case 1 -> "EMAIL " ;
359+ case 1 -> "RFC822NAME " ;
326360 case 2 -> "DNS" ;
327361 case 3 -> "X400" ;
328362 case 4 -> "DIR" ;
0 commit comments