@@ -295,6 +295,22 @@ describe('PKIAuthenticationProvider', () => {
295295 expect ( mockOptions . client . callAsInternalUser ) . not . toHaveBeenCalled ( ) ;
296296 } ) ;
297297
298+ it ( 'does not exchange peer certificate to access token for Ajax requests.' , async ( ) => {
299+ const request = httpServerMock . createKibanaRequest ( {
300+ headers : { 'kbn-xsrf' : 'xsrf' } ,
301+ socket : getMockSocket ( {
302+ authorized : true ,
303+ peerCertificate : getMockPeerCertificate ( [ '2A:7A:C2:DD' , '3B:8B:D3:EE' ] ) ,
304+ } ) ,
305+ } ) ;
306+ await expect ( provider . authenticate ( request ) ) . resolves . toEqual (
307+ AuthenticationResult . notHandled ( )
308+ ) ;
309+
310+ expect ( mockOptions . client . asScoped ) . not . toHaveBeenCalled ( ) ;
311+ expect ( mockOptions . client . callAsInternalUser ) . not . toHaveBeenCalled ( ) ;
312+ } ) ;
313+
298314 it ( 'fails with non-401 error if state is available, peer is authorized, but certificate is not available.' , async ( ) => {
299315 const request = httpServerMock . createKibanaRequest ( {
300316 socket : getMockSocket ( { authorized : true } ) ,
@@ -383,14 +399,7 @@ describe('PKIAuthenticationProvider', () => {
383399 } ) ;
384400
385401 it ( 'gets a new access token even if existing token is expired.' , async ( ) => {
386- const user = mockAuthenticatedUser ( ) ;
387- const request = httpServerMock . createKibanaRequest ( {
388- socket : getMockSocket ( {
389- authorized : true ,
390- peerCertificate : getMockPeerCertificate ( [ '2A:7A:C2:DD' , '3B:8B:D3:EE' ] ) ,
391- } ) ,
392- } ) ;
393- const state = { accessToken : 'existing-token' , peerCertificateFingerprint256 : '2A:7A:C2:DD' } ;
402+ const user = mockAuthenticatedUser ( { authentication_provider : { type : 'pki' , name : 'pki' } } ) ;
394403
395404 const mockScopedClusterClient = elasticsearchServiceMock . createLegacyScopedClusterClient ( ) ;
396405 mockScopedClusterClient . callAsCurrentUser
@@ -399,55 +408,102 @@ describe('PKIAuthenticationProvider', () => {
399408 LegacyElasticsearchErrorHelpers . decorateNotAuthorizedError ( new Error ( ) )
400409 )
401410 // In response to a call with a new token.
411+ . mockResolvedValueOnce ( user ) // In response to call with an expired token.
412+ . mockRejectedValueOnce (
413+ LegacyElasticsearchErrorHelpers . decorateNotAuthorizedError ( new Error ( ) )
414+ )
415+ // In response to a call with a new token.
416+ . mockResolvedValueOnce ( user ) // In response to call with an expired token.
417+ . mockRejectedValueOnce (
418+ LegacyElasticsearchErrorHelpers . decorateNotAuthorizedError ( new Error ( ) )
419+ )
420+ // In response to a call with a new token.
402421 . mockResolvedValueOnce ( user ) ;
403422 mockOptions . client . asScoped . mockReturnValue ( mockScopedClusterClient ) ;
404423 mockOptions . client . callAsInternalUser . mockResolvedValue ( { access_token : 'access-token' } ) ;
405424
406- await expect ( provider . authenticate ( request , state ) ) . resolves . toEqual (
407- AuthenticationResult . succeeded (
408- { ...user , authentication_provider : { type : 'pki' , name : 'pki' } } ,
409- {
410- authHeaders : { authorization : 'Bearer access-token' } ,
411- state : { accessToken : 'access-token' , peerCertificateFingerprint256 : '2A:7A:C2:DD' } ,
412- }
413- )
425+ const nonAjaxRequest = httpServerMock . createKibanaRequest ( {
426+ socket : getMockSocket ( {
427+ authorized : true ,
428+ peerCertificate : getMockPeerCertificate ( [ '2A:7A:C2:DD' , '3B:8B:D3:EE' ] ) ,
429+ } ) ,
430+ } ) ;
431+ const nonAjaxState = {
432+ accessToken : 'existing-token' ,
433+ peerCertificateFingerprint256 : '2A:7A:C2:DD' ,
434+ } ;
435+ await expect ( provider . authenticate ( nonAjaxRequest , nonAjaxState ) ) . resolves . toEqual (
436+ AuthenticationResult . succeeded ( user , {
437+ authHeaders : { authorization : 'Bearer access-token' } ,
438+ state : { accessToken : 'access-token' , peerCertificateFingerprint256 : '2A:7A:C2:DD' } ,
439+ } )
414440 ) ;
415441
416- expect ( mockOptions . client . callAsInternalUser ) . toHaveBeenCalledTimes ( 1 ) ;
417- expect ( mockOptions . client . callAsInternalUser ) . toHaveBeenCalledWith ( 'shield.delegatePKI' , {
418- body : {
419- x509_certificate_chain : [
420- 'fingerprint:2A:7A:C2:DD:base64' ,
421- 'fingerprint:3B:8B:D3:EE:base64' ,
422- ] ,
423- } ,
442+ const ajaxRequest = httpServerMock . createKibanaRequest ( {
443+ headers : { 'kbn-xsrf' : 'xsrf' } ,
444+ socket : getMockSocket ( {
445+ authorized : true ,
446+ peerCertificate : getMockPeerCertificate ( [ '3A:7A:C2:DD' , '3B:8B:D3:EE' ] ) ,
447+ } ) ,
424448 } ) ;
449+ const ajaxState = {
450+ accessToken : 'existing-token' ,
451+ peerCertificateFingerprint256 : '3A:7A:C2:DD' ,
452+ } ;
453+ await expect ( provider . authenticate ( ajaxRequest , ajaxState ) ) . resolves . toEqual (
454+ AuthenticationResult . succeeded ( user , {
455+ authHeaders : { authorization : 'Bearer access-token' } ,
456+ state : { accessToken : 'access-token' , peerCertificateFingerprint256 : '3A:7A:C2:DD' } ,
457+ } )
458+ ) ;
425459
426- expect ( request . headers ) . not . toHaveProperty ( 'authorization' ) ;
427- } ) ;
428-
429- it ( 'does not exchange peer certificate to a new access token even if existing token is expired and request does not require authentication.' , async ( ) => {
430- const request = httpServerMock . createKibanaRequest ( {
460+ const optionalAuthRequest = httpServerMock . createKibanaRequest ( {
431461 routeAuthRequired : false ,
432462 socket : getMockSocket ( {
433463 authorized : true ,
434- peerCertificate : getMockPeerCertificate ( [ '2A :7A:C2:DD' , '3B:8B:D3:EE' ] ) ,
464+ peerCertificate : getMockPeerCertificate ( [ '4A :7A:C2:DD' , '3B:8B:D3:EE' ] ) ,
435465 } ) ,
436466 } ) ;
437- const state = { accessToken : 'existing-token' , peerCertificateFingerprint256 : '2A:7A:C2:DD' } ;
438-
439- const mockScopedClusterClient = elasticsearchServiceMock . createLegacyScopedClusterClient ( ) ;
440- mockScopedClusterClient . callAsCurrentUser . mockRejectedValueOnce (
441- LegacyElasticsearchErrorHelpers . decorateNotAuthorizedError ( new Error ( ) )
467+ const optionalAuthState = {
468+ accessToken : 'existing-token' ,
469+ peerCertificateFingerprint256 : '4A:7A:C2:DD' ,
470+ } ;
471+ await expect ( provider . authenticate ( optionalAuthRequest , optionalAuthState ) ) . resolves . toEqual (
472+ AuthenticationResult . succeeded ( user , {
473+ authHeaders : { authorization : 'Bearer access-token' } ,
474+ state : { accessToken : 'access-token' , peerCertificateFingerprint256 : '4A:7A:C2:DD' } ,
475+ } )
442476 ) ;
443- mockOptions . client . asScoped . mockReturnValue ( mockScopedClusterClient ) ;
444477
445- await expect ( provider . authenticate ( request , state ) ) . resolves . toEqual (
446- AuthenticationResult . notHandled ( )
447- ) ;
478+ expect ( mockOptions . client . callAsInternalUser ) . toHaveBeenCalledTimes ( 3 ) ;
479+ expect ( mockOptions . client . callAsInternalUser ) . toHaveBeenCalledWith ( 'shield.delegatePKI' , {
480+ body : {
481+ x509_certificate_chain : [
482+ 'fingerprint:2A:7A:C2:DD:base64' ,
483+ 'fingerprint:3B:8B:D3:EE:base64' ,
484+ ] ,
485+ } ,
486+ } ) ;
487+ expect ( mockOptions . client . callAsInternalUser ) . toHaveBeenCalledWith ( 'shield.delegatePKI' , {
488+ body : {
489+ x509_certificate_chain : [
490+ 'fingerprint:3A:7A:C2:DD:base64' ,
491+ 'fingerprint:3B:8B:D3:EE:base64' ,
492+ ] ,
493+ } ,
494+ } ) ;
495+ expect ( mockOptions . client . callAsInternalUser ) . toHaveBeenCalledWith ( 'shield.delegatePKI' , {
496+ body : {
497+ x509_certificate_chain : [
498+ 'fingerprint:4A:7A:C2:DD:base64' ,
499+ 'fingerprint:3B:8B:D3:EE:base64' ,
500+ ] ,
501+ } ,
502+ } ) ;
448503
449- expect ( mockOptions . client . callAsInternalUser ) . not . toHaveBeenCalled ( ) ;
450- expect ( request . headers ) . not . toHaveProperty ( 'authorization' ) ;
504+ expect ( nonAjaxRequest . headers ) . not . toHaveProperty ( 'authorization' ) ;
505+ expect ( ajaxRequest . headers ) . not . toHaveProperty ( 'authorization' ) ;
506+ expect ( optionalAuthRequest . headers ) . not . toHaveProperty ( 'authorization' ) ;
451507 } ) ;
452508
453509 it ( 'fails with 401 if existing token is expired, but certificate is not present.' , async ( ) => {
0 commit comments