@@ -297,22 +297,42 @@ public void check(final SimpleString address,
297297 }
298298
299299 @ Override
300- public void check (final SimpleString address ,
301- final SimpleString queue ,
302- final CheckType checkType ,
303- final SecurityAuth session ) throws Exception {
304- if (securityEnabled ) {
300+ public boolean hasPermission (final SimpleString address ,
301+ final SimpleString queue ,
302+ final CheckType checkType ,
303+ final SecurityAuth session ) throws Exception {
304+ if (!securityEnabled ) {
305+ return true ;
306+ }
307+
308+ // bypass permission checks for management cluster user
309+ String user = session .getUsername ();
310+ if (managementClusterUser .equals (user ) && session .getPassword ().equals (managementClusterPassword )) {
311+ return true ;
312+ }
313+
314+ // Special case: detect authentication failure for ActiveMQSecurityManager5
315+ // This must throw an authentication exception, not an authorization exception
316+ Subject subjectForManager5 = null ;
317+ if (securityManager instanceof ActiveMQSecurityManager5 manager5 ) {
318+ subjectForManager5 = getSubjectForAuthorization (session , manager5 );
319+ /*
320+ * A user may authenticate successfully at first, but then later when their Subject is evicted from the
321+ * local cache re-authentication may fail. This could happen, for example, if the user was removed from LDAP
322+ * or the user's token expired.
323+ *
324+ * If the subject is null then authorization will *always* fail.
325+ */
326+ if (subjectForManager5 == null ) {
327+ authenticationFailed (session .getUsername (), session .getRemotingConnection ());
328+ }
329+ }
330+ try {
305331 SimpleString bareAddress = CompositeAddress .extractAddressName (address );
306332 SimpleString bareQueue = CompositeAddress .extractQueueName (queue );
307333
308334 logger .trace ("checking access permissions to {}" , bareAddress );
309335
310- // bypass permission checks for management cluster user
311- String user = session .getUsername ();
312- if (managementClusterUser .equals (user ) && session .getPassword ().equals (managementClusterPassword )) {
313- AUTHORIZATION_SUCCESS_COUNT_UPDATER .incrementAndGet (this );
314- return ;
315- }
316336
317337 Set <Role > roles = securityRepository .getMatch (bareAddress .toString ());
318338
@@ -325,27 +345,14 @@ public void check(final SimpleString address,
325345 }
326346 }
327347
328- if (checkAuthorizationCache (fqqn != null ? fqqn : bareAddress , user , checkType )) {
329- AUTHORIZATION_SUCCESS_COUNT_UPDATER .incrementAndGet (this );
330- return ;
348+ if (checkAuthorizationCache (fqqn != null ? fqqn : bareAddress , user , checkType )) {
349+ return true ;
331350 }
332351
333352 final Boolean validated ;
334353 if (securityManager instanceof ActiveMQSecurityManager5 manager5 ) {
335- Subject subject = getSubjectForAuthorization (session , manager5 );
336-
337- /*
338- * A user may authenticate successfully at first, but then later when their Subject is evicted from the
339- * local cache re-authentication may fail. This could happen, for example, if the user was removed from LDAP
340- * or the user's token expired.
341- *
342- * If the subject is null then authorization will *always* fail.
343- */
344- if (subject == null ) {
345- authenticationFailed (user , session .getRemotingConnection ());
346- }
347-
348- validated = manager5 .authorize (subject , roles , checkType , fqqn != null ? fqqn .toString () : bareAddress .toString ());
354+ // Reuse subject from earlier authentication check
355+ validated = manager5 .authorize (subjectForManager5 , roles , checkType , fqqn != null ? fqqn .toString () : bareAddress .toString ());
349356 } else if (securityManager instanceof ActiveMQSecurityManager4 manager4 ) {
350357 validated = manager4 .validateUserAndRole (user , session .getPassword (), roles , checkType , bareAddress .toString (), session .getRemotingConnection (), session .getSecurityDomain ()) != null ;
351358 } else if (securityManager instanceof ActiveMQSecurityManager3 manager3 ) {
@@ -356,52 +363,67 @@ public void check(final SimpleString address,
356363 validated = securityManager .validateUserAndRole (user , session .getPassword (), roles , checkType );
357364 }
358365
359- if (!validated ) {
360- if (notificationService != null ) {
361- TypedProperties props = new TypedProperties ();
362-
363- props .putSimpleStringProperty (ManagementHelper .HDR_ADDRESS , bareAddress );
364- props .putSimpleStringProperty (ManagementHelper .HDR_CHECK_TYPE , SimpleString .of (checkType .toString ()));
365- props .putSimpleStringProperty (ManagementHelper .HDR_USER , SimpleString .of (getCaller (user , session .getRemotingConnection ().getSubject ())));
366-
367- Notification notification = new Notification (null , CoreNotificationType .SECURITY_PERMISSION_VIOLATION , props );
368-
369- notificationService .sendNotification (notification );
370- }
371-
372- Exception ex ;
373- if (bareQueue == null ) {
374- ex = ActiveMQMessageBundle .BUNDLE .userNoPermissions (getCaller (user , session .getRemotingConnection ().getSubject ()), checkType , bareAddress );
366+ if (validated && user != null ) {
367+ // if we get here we're granted, add to the cache
368+ ConcurrentHashSet <SimpleString > set ;
369+ String key = createAuthorizationCacheKey (user , checkType );
370+ ConcurrentHashSet <SimpleString > act = getAuthorizationCacheEntry (key );
371+ if (act != null ) {
372+ set = act ;
375373 } else {
376- ex = ActiveMQMessageBundle .BUNDLE .userNoPermissionsQueue (getCaller (user , session .getRemotingConnection ().getSubject ()), checkType , bareQueue , bareAddress );
374+ set = new ConcurrentHashSet <>();
375+ putAuthorizationCacheEntry (set , key );
377376 }
378- AuditLogger .securityFailure (session .getRemotingConnection ().getSubject (), session .getRemotingConnection ().getRemoteAddress (), ex .getMessage (), ex );
379- AUTHORIZATION_FAILURE_COUNT_UPDATER .incrementAndGet (this );
380- throw ex ;
377+ set .add (Objects .requireNonNullElse (fqqn , bareAddress ));
381378 }
382379
383- // if we get here we're granted, add to the cache
380+ return validated ;
381+ } catch (Exception e ) {
382+ logger .debug ("Permission check failed" , e );
383+ return false ;
384+ }
385+ }
386+
387+ @ Override
388+ public void check (final SimpleString address ,
389+ final SimpleString queue ,
390+ final CheckType checkType ,
391+ final SecurityAuth session ) throws Exception {
392+ if (!securityEnabled ) {
393+ return ;
394+ }
384395
396+ if (hasPermission (address , queue , checkType , session )) {
385397 AUTHORIZATION_SUCCESS_COUNT_UPDATER .incrementAndGet (this );
398+ return ;
399+ }
386400
387- if (user == null ) {
388- // should get all user/pass into a subject and only cache subjects
389- // till then when subject is in play, the user may be null and
390- // we cannot cache as we don't have a unique key
391- return ;
392- }
401+ // Permission denied - handle side effects
402+ SimpleString bareAddress = CompositeAddress .extractAddressName (address );
403+ SimpleString bareQueue = CompositeAddress .extractQueueName (queue );
404+ String user = session .getUsername ();
393405
394- ConcurrentHashSet <SimpleString > set ;
395- String key = createAuthorizationCacheKey (user , checkType );
396- ConcurrentHashSet <SimpleString > act = getAuthorizationCacheEntry (key );
397- if (act != null ) {
398- set = act ;
399- } else {
400- set = new ConcurrentHashSet <>();
401- putAuthorizationCacheEntry (set , key );
402- }
403- set .add (Objects .requireNonNullElse (fqqn , bareAddress ));
406+ if (notificationService != null ) {
407+ TypedProperties props = new TypedProperties ();
408+
409+ props .putSimpleStringProperty (ManagementHelper .HDR_ADDRESS , bareAddress );
410+ props .putSimpleStringProperty (ManagementHelper .HDR_CHECK_TYPE , SimpleString .of (checkType .toString ()));
411+ props .putSimpleStringProperty (ManagementHelper .HDR_USER , SimpleString .of (getCaller (user , session .getRemotingConnection ().getSubject ())));
412+
413+ Notification notification = new Notification (null , CoreNotificationType .SECURITY_PERMISSION_VIOLATION , props );
414+
415+ notificationService .sendNotification (notification );
416+ }
417+
418+ Exception ex ;
419+ if (bareQueue == null ) {
420+ ex = ActiveMQMessageBundle .BUNDLE .userNoPermissions (getCaller (user , session .getRemotingConnection ().getSubject ()), checkType , bareAddress );
421+ } else {
422+ ex = ActiveMQMessageBundle .BUNDLE .userNoPermissionsQueue (getCaller (user , session .getRemotingConnection ().getSubject ()), checkType , bareQueue , bareAddress );
404423 }
424+ AuditLogger .securityFailure (session .getRemotingConnection ().getSubject (), session .getRemotingConnection ().getRemoteAddress (), ex .getMessage (), ex );
425+ AUTHORIZATION_FAILURE_COUNT_UPDATER .incrementAndGet (this );
426+ throw ex ;
405427 }
406428
407429 @ Override
0 commit comments