--[[----------------------------------------------------------------------------

WFCatP2PIdentityManagerTest.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2020 Adobe Inc.
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.


------------------------------------------------------------------------------]]

local WFRxTestCase = require 'WFRxTestCase'

local KSData = require "KSData"
local KSJsonEncoder = require "KSJsonEncoder"
local KSKeychainService = require "KSKeychainService"
local KSIdGenerator = require "KSIdGenerator"

local WFPlatformInfo = require 'WFPlatformInfo'
local WFFileUtils = require "WFFileUtils"

local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFCatSpecMock = require "WFCatSpecMock"
local WFMockHttpRequest = require "WFMockHttpRequest"

local WFCrypto = require "WFCrypto"
local WFTestCertificateAuthority = require "WFTestCertificateAuthority"

local WFCatP2PIdentityManager = require "WFCatP2PIdentityManager"

--==============================================================================

local kUserId = KSIdGenerator.uuidInLowercaseHex( )
local catalogId = KSIdGenerator.uuidInLowercaseHex( )

local catalogHash = KSData.make( catalogId ):digest( 'SHA224' )

local TEST_TIMEOUT = 240

--------------------------------------------------------------------------------

local WFCatP2PIdentityManagerTest = {}

---------------------------------------------------------------------------------

local testKey = [[-----BEGIN PRIVATE KEY-----
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
C9Jum34bjyKph2h3mXQAXXpT/m4ymS0=
-----END PRIVATE KEY-----
]]

local testPassword = '_m&+Q]4Db7&0HvR}k_lmP5Av^PtDM-9l2u<Gi)dgyJknkG$p9~QxP}}xigA]&++J'

--------------------------------------------------------------------------------

local function createWFCatalogForTestWithSpec( rxTest, wfCatalogSpec )

	local indexSpecScript

	if WFPlatformInfo.isMobile() then
		indexSpecScript = 'LrmCatalogIndexDefinitions.lua'
	end

	return WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, wfCatalogSpec, nil, indexSpecScript )

end


--------------------------------------------------------------------------------

local function certificateResponse( csr, options )

	local adobeTestCaCert = WFTestCertificateAuthority.certificate()

	local validSeconds = 3600 * 24 * 90

	local testCert = adobeTestCaCert:sign( csr, WFTestCertificateAuthority.privateKey(), validSeconds )

	return KSJsonEncoder.prettyPrint( {
		Certificate = testCert.pem,
		CertificateChain = WFTestCertificateAuthority.certificate().pem
	} )

end

--------------------------------------------------------------------------------

local function generateTestCertificate( optValidSeconds )

	local validSeconds = optValidSeconds or ( 3600 * 24 * 90 ) -- 90 days

	local csr = WFCrypto.generateCSR( catalogHash, testKey )

	local adobeTestCaCert = WFTestCertificateAuthority.certificate()

	local testCert = adobeTestCaCert:sign( csr, WFTestCertificateAuthority.privateKey(), validSeconds )

	return testCert

end

--------------------------------------------------------------------------------

function WFCatP2PIdentityManagerTest.setUp()

	WFFileUtils.delete( WFCatP2PIdentityManager.defaultIdentityFileName( catalogHash ) )

	KSKeychainService.delete( catalogHash )

end

--------------------------------------------------------------------------------

function WFCatP2PIdentityManagerTest.tearDown()

	WFFileUtils.delete( WFCatP2PIdentityManager.defaultIdentityFileName( catalogHash ) )

end

--------------------------------------------------------------------------------

function WFCatP2PIdentityManagerTest.rxTestGetIdentity( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local catalogSpec = WFCatSpecMock( catalogId, kUserId, rxTest )
	local wfCatalog = createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	catalogSpec:expectHttpRequest(
		WFMockHttpRequest()
			:post( WFCatP2PIdentityManager.kCertificateServiceUrl )
			:validateRequestData( function( str )
					return str:find( "-----BEGIN CERTIFICATE REQUEST-----" )
				end )
			:respond( '200', certificateResponse )
		)

	local requestIdentity = WFCatP2PIdentityManager.getOrRequestIdentity( wfCatalog, catalogHash )
		:map( function( identityFile, password, caFileName )

			local caCertificate = WFCrypto.readPEM( caFileName )

			local cert, _ = WFCrypto.readP12( identityFile, password )
			return cert.isValidNow and caCertificate.pem == WFTestCertificateAuthority.certificate().pem

		end )

	rxTest:expectSingleValue( requestIdentity, true )

end

--------------------------------------------------------------------------------

function WFCatP2PIdentityManagerTest.rxTestGetIdentityExisting( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	-- write an identity with sufficient validity where the
	-- WFCatP2PIdentityManager expects it to execute a different codepath

	local fileName = WFCatP2PIdentityManager.defaultIdentityFileName( catalogHash )
	local certificate = generateTestCertificate()

	certificate:writeP12( fileName, testKey, testPassword )

	rxTest:expectOnlyCompleted( KSKeychainService.write( catalogHash, testPassword ) )

	local catalogSpec = WFCatSpecMock( catalogId, kUserId, rxTest )
	local wfCatalog = createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	local requestIdentity = WFCatP2PIdentityManager.getOrRequestIdentity( wfCatalog, catalogHash )
		:map( function( identityFile, password, caFileName)

			local caCertificate = WFCrypto.readPEM( caFileName )

			local cert, _ = WFCrypto.readP12( identityFile, password )
			return cert.isValidNow and caCertificate.pem == WFTestCertificateAuthority.certificate().pem

		end )

	rxTest:expectSingleValue( requestIdentity, true )

end

--------------------------------------------------------------------------------

function WFCatP2PIdentityManagerTest.rxTestGetIdentityExistingReset( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	-- write an identity with sufficient validity where the
	-- WFCatP2PIdentityManager expects it to execute a different codepath

	local fileName = WFCatP2PIdentityManager.defaultIdentityFileName( catalogHash )
	local certificate = generateTestCertificate()

	certificate:writeP12( fileName, testKey, testPassword )

	rxTest:expectOnlyCompleted( KSKeychainService.write( catalogHash, testPassword ) )

	local catalogSpec = WFCatSpecMock( catalogId, kUserId, rxTest )
	local wfCatalog = createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	catalogSpec:expectHttpRequest(
		WFMockHttpRequest()
			:post( WFCatP2PIdentityManager.kCertificateServiceUrl )
			:validateRequestData( function( str )
					return str:find( "-----BEGIN CERTIFICATE REQUEST-----" )
				end )
			:respond( '200', certificateResponse )
		)

	local requestIdentity = WFCatP2PIdentityManager.getOrRequestIdentity( wfCatalog, catalogHash, { reset = true })
		:map( function( identityFile, password, caFileName)

			local caCertificate = WFCrypto.readPEM( caFileName )

			local cert, _ = WFCrypto.readP12( identityFile, password )
			return cert.isValidNow and caCertificate.pem == WFTestCertificateAuthority.certificate().pem

		end )

	rxTest:expectSingleValue( requestIdentity, true )

end

--------------------------------------------------------------------------------

function WFCatP2PIdentityManagerTest.rxTestGetIdentityExpiresSoon( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	-- write an identity that expires soon (now) to make sure a new certificate is generated

	local fileName = WFCatP2PIdentityManager.defaultIdentityFileName( catalogHash )

	local certificate = generateTestCertificate( 0 )

	certificate:writeP12( fileName, testKey, testPassword )

	rxTest:expectOnlyCompleted( KSKeychainService.write( catalogHash, testPassword ) )

	local catalogSpec = WFCatSpecMock( catalogId, kUserId, rxTest )
	local wfCatalog = createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	catalogSpec:expectHttpRequest(
		WFMockHttpRequest()
			:post( WFCatP2PIdentityManager.kCertificateServiceUrl )
			:validateRequestData( function( str )
					return str:find( "-----BEGIN CERTIFICATE REQUEST-----" )
				end )
			:respond( '200', certificateResponse )
		)
		
	local requestIdentity = WFCatP2PIdentityManager.getOrRequestIdentity( wfCatalog, catalogHash )
		:map( function( identityFile, password, caFileName )

			local caCertificate = WFCrypto.readPEM( caFileName )

			local cert, _ = WFCrypto.readP12( identityFile, password )
			return cert.isValidNow and caCertificate.pem == WFTestCertificateAuthority.certificate().pem

		end )

	rxTest:expectSingleValue( requestIdentity, true )

end

--------------------------------------------------------------------------------

function WFCatP2PIdentityManagerTest.rxTestGetIdentityInvalid( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	-- write an invalid identity (empty file) to make sure a valid certificate is generated

	local fileName = WFCatP2PIdentityManager.defaultIdentityFileName( catalogHash )

	KSData.saveToFile( KSData.make( 'not a p12' ), fileName )

	rxTest:finally( function()
		WFFileUtils.delete( fileName )
	end )
	
	rxTest:expectEqual( 'file', WFFileUtils.exists( fileName ) )

	rxTest:expectOnlyCompleted( KSKeychainService.write( catalogHash, testPassword ) )

	local catalogSpec = WFCatSpecMock( catalogId, kUserId, rxTest )
	local wfCatalog = createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	catalogSpec:expectHttpRequest(
		WFMockHttpRequest()
			:post( WFCatP2PIdentityManager.kCertificateServiceUrl )
			:validateRequestData( function( str )
					return str:find( "-----BEGIN CERTIFICATE REQUEST-----" )
				end )
			:respond( '200', certificateResponse )
		)
		
	local requestIdentity = WFCatP2PIdentityManager.getOrRequestIdentity( wfCatalog, catalogHash )
		:map( function( identityFile, password, caFileName )

			local caCertificate = WFCrypto.readPEM( caFileName )

			local cert, _ = WFCrypto.readP12( identityFile, password )
			return cert.isValidNow and caCertificate.pem == WFTestCertificateAuthority.certificate().pem

		end )

	rxTest:expectSingleValue( requestIdentity, true )

end

--------------------------------------------------------------------------------

function WFCatP2PIdentityManagerTest.rxTestGetIdentityServerFailed( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local catalogSpec = WFCatSpecMock( catalogId, kUserId, rxTest )
	local wfCatalog = createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	catalogSpec:expectHttpRequest(
		WFMockHttpRequest()
			:post( WFCatP2PIdentityManager.kCertificateServiceUrl )
			:validateRequestData( function( str )
					return str:find( "-----BEGIN CERTIFICATE REQUEST-----" )
				end )
			:respond( '500', 'the server made a boo' )
		)

	local requestIdentity = WFCatP2PIdentityManager.getOrRequestIdentity( wfCatalog, catalogHash )

	rxTest:expectOnlyError( requestIdentity, 'the server made a boo', { status_code = '500' } )

end

--------------------------------------------------------------------------------

return WFRxTestCase.wrapSuite( 'WFCatP2PIdentityManagerTest', WFCatP2PIdentityManagerTest )