@echo off

rem ##############################################################################
rem #
rem # Copyright (c) 2013 VMware, Inc. All rights reserved.
rem # -- VMware Confidential
rem #
rem ##############################################################################

rem ---------------------------------------------

rem - IS steps
rem # 0. Do all the validations.
rem # 1. Generate the pfx keystore.
rem # 2. Run unregister-sso.bat
rem # 3. Check whether new certificate is already registered with SSO
rem # 4. Stop IS
rem # 5. Create a backup of original certs/keystores.
rem # 6. Update the cert, key, pfx
rem # 7. Run register-sso.bat
rem # 8. Update IS solution user role
rem # 9. Start IS

set _IS_CERT_CHAIN=%is_cert_chain%
set _IS_PRIVATE_KEY_NEW=%is_private_key_new%

rem Fetch from win-registry
set _IS_INSTALLATION_DIR=
set _LS_URL=

set _IS_SC_EXE=%windir%\system32\sc.exe
if NOT EXIST "%_IS_SC_EXE%" (
	set last_error=sc.exe is not present in the system32 directory.
	goto endError
)

set _IS_REG_EXE=%windir%\system32\reg.exe
if NOT EXIST "%_IS_REG_EXE%" (
	set last_error=reg.exe is not present in the system32 directory.
	goto endError
)

set _IS_SVC_NAME=vimQueryService

set _IS_REG_PATH="HKLM\SOFTWARE\VMware, Inc.\VMware Infrastructure\Inventory Service"
call :GetInvSvcInfoFromWinReg %_IS_REG_PATH% > nul 2>&1
if "%_IS_INSTALLATION_DIR%"=="" (
	set last_error=Failed to load the path from the registry. Inventory Service might not be installed.
	call "%~dp0common" %last_error%
	exit /B 2
)

rem chop off the last backslash if one is present
if "%_IS_INSTALLATION_DIR:~-1%" == "\" set "_IS_INSTALLATION_DIR=%_IS_INSTALLATION_DIR:~0,-1%"
call "%~dp0common" The Inventory Service is installed at "%_IS_INSTALLATION_DIR%"

set _IS_DATASERVICE_PROP=%_IS_INSTALLATION_DIR%\lib\server\config\dataservice.properties
for /F "tokens=2 delims=\=" %%i IN ('findstr /B "dataservice.sso.solutionUser" "%_IS_DATASERVICE_PROP%"') do SET _IS_SOLUTION_USER=%%i

if NOT EXIST "%_IS_DATASERVICE_PROP%" (
	set last_error="%_IS_DATASERVICE_PROP%" does not exist
	goto endError
)

rem Fetch _IS_SSL_DIR from dataservice.properties
set _IS_SSL_DIR=
for /F "tokens=2 delims=\=" %%i IN ('findstr /B "dataservice.keystore.path" "%_IS_DATASERVICE_PROP%"') do SET _IS_SSL_DIR=%%i
rem remove prefix "file:" and trailing file name - "rui.pfx"
set _IS_SSL_DIR=%_IS_SSL_DIR:file:=%
set _IS_SSL_DIR=%_IS_SSL_DIR:/rui.pfx=%

rem remove trailing / if present ...
if "%_IS_SSL_DIR:~-1%" == "/" set "_IS_SSL_DIR=%_IS_SSL_DIR:~0,-1%"

rem now, _IS_SSL_DIR has the path with all forward slashes '/'
rem For windows, revert to backward slashes ...
set _IS_SSL_DIR=%_IS_SSL_DIR:/=\%
set _IS_CERT_ORIG=%_IS_SSL_DIR%\rui.crt
set _IS_KEY_ORIG=%_IS_SSL_DIR%\rui.key
set _IS_KEYSTORE_ORIG=%_IS_SSL_DIR%\rui.pfx
set _IS_SSO_REGTOOL=%_IS_INSTALLATION_DIR%\sso\regTool.cmd
set _IS_SCRIPTS_DIR=%_IS_INSTALLATION_DIR%\scripts
set _IS_REGISTER_SSO_SCRIPT=%_IS_SCRIPTS_DIR%\register-sso.bat
set _IS_UNREGISTER_SSO_SCRIPT=%_IS_SCRIPTS_DIR%\unregister-sso.bat
set "_IS_OPENSSL_FULL_PATH=%~dp0openssl\openssl.exe"
set "_IS_ROLLBACK_PATH=%ROLLBACK_BACKUP_FOLDER%"
rem Remove trailing slash
if "%_IS_ROLLBACK_PATH:~-1%" == "\" set "_IS_ROLLBACK_PATH=%_IS_ROLLBACK_PATH:~0,-1%"
call "%~dp0common" Rollback path is "%_IS_ROLLBACK_PATH%"

if NOT EXIST "%_IS_ROLLBACK_PATH%" (
	mkdir "%_IS_ROLLBACK_PATH%"
	if NOT EXIST "%_IS_ROLLBACK_PATH%" (
		set last_error=Specified rollback directory "%_IS_ROLLBACK_PATH%" does not exist.
		goto endError
	)
)

set "_IS_ROLLBACK_PATH=%_IS_ROLLBACK_PATH%\IS"
if NOT EXIST "%_IS_ROLLBACK_PATH%" (
	mkdir "%_IS_ROLLBACK_PATH%"
)
call "%~dp0common" Rollback path is "%_IS_ROLLBACK_PATH%"
if NOT EXIST "%_IS_ROLLBACK_PATH%" (
	set last_error=Inventory Service rollback directory "%_IS_ROLLBACK_PATH%" does not exist.
	goto endError
)

rem - Use a tmp file location to generate new IS keystore using OPENSSL
set "_IS_CURR_KEYSTORE_PATH=%_IS_ROLLBACK_PATH%\rui_tmp.pfx"

set _IS_ROLLBACK_CRT=%_IS_ROLLBACK_PATH%\rui.crt
set _IS_ROLLBACK_KEY=%_IS_ROLLBACK_PATH%\rui.key
set _IS_ROLLBACK_PFX=%_IS_ROLLBACK_PATH%\rui.pfx

if "%1"=="-rollback" (
	call "%~dp0common" Rolling back previous changes from "%_IS_ROLLBACK_PATH%"
	IF NOT EXIST "%_IS_ROLLBACK_CRT%" (
		set last_error=Backup file "%_IS_ROLLBACK_CRT%" does not exist, so cannot rollback
		goto endError
	)
	IF NOT EXIST "%_IS_ROLLBACK_KEY%" (
		set last_error=Backup file "%_IS_ROLLBACK_KEY%" does not exist, so cannot rollback
		goto endError
	)
	IF NOT EXIST "%_IS_ROLLBACK_PFX%" (
		set last_error=Backup file "%_IS_ROLLBACK_PFX%" does not exist, so cannot rollback
		goto endError
	)
	goto revertFilesAndUpdateSSO
)

set _RET_VAL=0
call:fileExistsCheck "%_IS_CERT_CHAIN%" _RET_VAL
call:fileExistsCheck "%_IS_PRIVATE_KEY_NEW%" _RET_VAL
call:fileExistsCheck "%_IS_SSL_DIR%" _RET_VAL
call:fileExistsCheck "%_IS_CERT_ORIG%" _RET_VAL
call:fileExistsCheck "%_IS_KEY_ORIG%" _RET_VAL
call:fileExistsCheck "%_IS_KEYSTORE_ORIG%" _RET_VAL
call:fileExistsCheck "%_IS_INSTALLATION_DIR%" _RET_VAL
call:fileExistsCheck "%_IS_SSO_REGTOOL%" _RET_VAL
call:fileExistsCheck "%_IS_SCRIPTS_DIR%" _RET_VAL
call:fileExistsCheck "%_IS_REGISTER_SSO_SCRIPT%" _RET_VAL
call:fileExistsCheck "%_IS_UNREGISTER_SSO_SCRIPT%" _RET_VAL
call:fileExistsCheck "%_IS_OPENSSL_FULL_PATH%" _RET_VAL

set _ERR_VAL=1
if "%_RET_VAL%" == "%_ERR_VAL%" (
	goto endError
)

rem Invoke openssl.exe to generate IS keystore
for /F "tokens=2 delims=\=" %%i IN ('findstr /B "dataservice.keystore.password" "%_IS_DATASERVICE_PROP%"') do SET _IS_KEYSTORE_PWD=%%i
"%_IS_OPENSSL_FULL_PATH%" pkcs12 -export -out "%_IS_CURR_KEYSTORE_PATH%" -in "%_IS_CERT_CHAIN%" -inkey "%_IS_PRIVATE_KEY_NEW%" -name rui -passout pass:%_IS_KEYSTORE_PWD% > nul 2> nul
if ERRORLEVEL 1 (
	set last_error=openssl.exe Cannot generate Inventory Service rui.pfx - errorlevel is %ERRORLEVEL%
	call "%~dp0common" curr path is - "%_IS_CURR_KEYSTORE_PATH%"
	goto endError
)

IF NOT EXIST "%_IS_CURR_KEYSTORE_PATH%" (
	set last_error=Cannot find the generated Inventory Service keystore "%_IS_CURR_KEYSTORE_PATH%"
	goto endError
)

rem Unregister from SSO only if it is registered with it ...
call "%~dp0common" Determining whether Inventory Service is registered with Single Sign-On ...
rem Verify whether the IS solution user is already registered with SSO
rem This is a bit hacky today - use the IS solution user and a garbage DN and ask the question to SSO whether IS solution user already registered?
call "%_IS_SSO_REGTOOL%" validateUsername -d %_LS_URL% -u "%sso_admin_user%" -p "%sso_admin_password%" -su %_IS_SOLUTION_USER% -dn GARBAGE_VALUE
rem returns 0 if IS is not registered with SSO
rem returns 7 if IS is already registered with SSO, else throw error
rem for any other return code, throw error
if "%ERRORLEVEL%"=="0" (
	rem IS is not registered with SSO
	goto skipUnregisterFromSsoDuringRollback
)
if NOT "%ERRORLEVEL%"=="7" (
	rem throw an error
	set last_error=Cannot determine if Inventory Service is registered with Single Sign-On - errorlevel is %ERRORLEVEL%
	goto endError
)

call "%~dp0common" Unregistering Inventory Service from Single Sign-On ...
call "%_IS_UNREGISTER_SSO_SCRIPT%" %_LS_URL% "%sso_admin_user%" "%sso_admin_password%"
rem returns 0 for success, else error
if NOT "%ERRORLEVEL%"=="0" (
	set last_error=Cannot unregister Inventory Service from Single Sign-On - errorlevel is %ERRORLEVEL%
	goto endError
)

:skipUnregisterFromSso
call "%~dp0common" Determining if the new Inventory Service certificate is already registered with Single Sign-On ...
rem Verify whether the new IS cert is already registered with SSO
rem This is a bit hacky today - use the new cert + and invalid username and ask the question to SSO - Have you seen this cert below?
call "%_IS_SSO_REGTOOL%" validateUsername -d %_LS_URL% -u "%sso_admin_user%" -p "%sso_admin_password%" -su GARBAGE_VALUE -sr "%_IS_CERT_CHAIN%"
rem returns 12 if the new certificate is already registered with SSO
if "%ERRORLEVEL%"=="12" (
	set last_error=New Inventory Service certificate is already registered with Single Sign-On. Use a unique certificate - errorlevel is %ERRORLEVEL%
	goto endErrorReRegisterWithSSO
)

call "%~dp0common" Determining the Inventory Service service status ...
%_IS_SC_EXE% query %_IS_SVC_NAME% | FIND /i "RUNNING" > nul
IF ERRORLEVEL 1 (
	rem looks like IS is not running
	goto skipInvSvcStop
)

call "%~dp0common" Stopping Inventory Service
net STOP %_IS_SVC_NAME%
if ERRORLEVEL 1 (
	set last_error=Cannot stop Inventory Service - errorlevel is %ERRORLEVEL%
	goto endError
)

:skipInvSvcStop
rem - copy the existing certs/ssl info to rollback folder
copy /Y "%_IS_CERT_ORIG%" "%_IS_ROLLBACK_CRT%"
if ERRORLEVEL 1 (
	set last_error=Cannot create a backup of original Inventory Service SSL certificate - errorlevel is %ERRORLEVEL%
	goto endError
)
copy /Y "%_IS_KEY_ORIG%" "%_IS_ROLLBACK_KEY%"
if ERRORLEVEL 1 (
	set last_error=Cannot create a backup of the original Inventory Service SSL private key - errorlevel is %ERRORLEVEL%
	goto endError
)
copy /Y "%_IS_KEYSTORE_ORIG%" "%_IS_ROLLBACK_PFX%"
if ERRORLEVEL 1 (
	set last_error=Cannot create a backup of the original Inventory Service SSL keystore - errorlevel is %ERRORLEVEL%
	goto endError
)

rem now copy new certs to this location
copy /Y "%_IS_CERT_CHAIN%" "%_IS_CERT_ORIG%"
if ERRORLEVEL 1 (
	set last_error=Cannot copy the new Inventory Service SSL certificate - errorlevel is %ERRORLEVEL%
	goto endErrorUndoCopyAndStartInvSvc
)
copy /Y "%_IS_PRIVATE_KEY_NEW%" "%_IS_KEY_ORIG%"
if ERRORLEVEL 1 (
	set last_error=Cannot copy the new Inventory Service SSL private key - errorlevel is %ERRORLEVEL%
	goto endErrorUndoCopyAndStartInvSvc
)
copy /Y "%_IS_CURR_KEYSTORE_PATH%" "%_IS_KEYSTORE_ORIG%"
if ERRORLEVEL 1 (
	set last_error=Cannot copy the new Inventory Service SSL keystore - errorlevel is %ERRORLEVEL%
	goto endErrorUndoCopyAndStartInvSvc
)

call "%~dp0common" Registering Inventory Service with Single Sign-On ...
call "%_IS_REGISTER_SSO_SCRIPT%" %_LS_URL% "%sso_admin_user%" "%sso_admin_password%"
rem returns 0 for success, else error
if NOT "%ERRORLEVEL%"=="0" (
	rem - not sure if a rollback is possible at this stage!!!
	set last_error=Cannot register Inventory Service with Single Sign-On - errorlevel is %ERRORLEVEL%
	goto endError
)

rem - Update IS solution user role to RegularUser - read privilege
call "%_IS_SSO_REGTOOL%" assignUserRole -d %_LS_URL% -u "%sso_admin_user%" -p "%sso_admin_password%" -su %_IS_SOLUTION_USER% -r read
rem returns 0 for success, else error
if NOT "%ERRORLEVEL%"=="0" (
	rem - not sure if a rollback is possible at this stage!!!
	set last_error=Cannot update the Inventory Service solution user role - errorlevel is %ERRORLEVEL%
	goto endError
)

call "%~dp0common" Starting Inventory Service
net START %_IS_SVC_NAME%
if ERRORLEVEL 1 (
	rem - not sure if a rollback is possible at this stage!!!
	set last_error=Cannot start Inventory Service - errorlevel is %ERRORLEVEL%
	goto endError
)

call "%~dp0common" Successfully updated the Inventory Service SSL certificate
set _IS_KEYSTORE_PWD=
exit /B 0

:GetInvSvcInfoFromWinReg
call "%~dp0common" GetInvSvcInfoFromWinReg called with "%1"
set _IS_INSTALLATION_DIR=
rem fetch _IS_INSTALLATION_DIR
for /F "tokens=1,2*" %%i in ('%_IS_REG_EXE% query %1 /v "InstallPath"') DO (
	if "%%i"=="InstallPath" (
		SET "_IS_INSTALLATION_DIR=%%k"
	)
)
set _LS_URL=
rem fetch _LS_URL
for /F "tokens=1,2*" %%i in ('%_IS_REG_EXE% query %1 /v "LookupServiceUrl"') DO (
	if "%%i"=="LookupServiceUrl" (
		SET "_LS_URL=%%k"
	)
)

exit /B 0

:revertFilesAndUpdateSSO

rem - We bailout on failure during a rollback.
rem - We never rollback a rollback!

rem - Rollback steps -
rem # 1. Run unregister-sso.bat
rem # 2. Stop IS
rem # 3. Copy the backup cert+key+keystore back to original location.
rem # 4. Run register-sso.bat
rem # 5. Update IS solution user role
rem # 6. Start IS

rem Unregister from SSO only if it is registered with it ...
call "%~dp0common" Determining if Inventory Service is registered with Single Sign-On ...
rem Verify whether the IS solution user is already registered with SSO
rem This is a bit hacky today - use the IS solution user and a garbage DN and ask the question to SSO whether IS solution user already registered?
call "%_IS_SSO_REGTOOL%" validateUsername -d %_LS_URL% -u "%sso_admin_user%" -p "%sso_admin_password%" -su %_IS_SOLUTION_USER% -dn GARBAGE_VALUE
rem returns 0 if IS is not registered with SSO
rem returns 7 if IS is already registered with SSO, else throw error
rem for any other return code, throw error
if "%ERRORLEVEL%"=="0" (
	rem IS is not registered with SSO
	goto skipUnregisterFromSsoDuringRollback
)
if NOT "%ERRORLEVEL%"=="7" (
	rem throw an error
	set last_error=Cannot determine whether Inventory Service is registered with Single Sign-On - errorlevel is %ERRORLEVEL%
	goto endError
)

call "%~dp0common" Unregistering Inventory Service from SSO ...
call "%_IS_UNREGISTER_SSO_SCRIPT%" %_LS_URL% "%sso_admin_user%" "%sso_admin_password%"
rem returns 0 for success, else error
if NOT "%ERRORLEVEL%"=="0" (
	set last_error=Cannot unregister Inventory Service from Single Sign-On - errorlevel is %ERRORLEVEL%
	goto endError
)

:skipUnregisterFromSsoDuringRollback
call "%~dp0common" Determining the Inventory Service service status ...
%_IS_SC_EXE% query %_IS_SVC_NAME% | FIND /i "RUNNING" > nul
IF ERRORLEVEL 1 (
	rem looks like IS is not running
	goto skipInvSvcStopDuringRollback
)

call "%~dp0common" Stopping Inventory Service
net STOP %_IS_SVC_NAME%
if ERRORLEVEL 1 (
	set last_error=Cannot stop Inventory Service - errorlevel is %ERRORLEVEL%
	goto endError
)

:skipInvSvcStopDuringRollback
copy /Y "%_IS_ROLLBACK_CRT%" "%_IS_CERT_ORIG%"
copy /Y "%_IS_ROLLBACK_KEY%" "%_IS_KEY_ORIG%"
copy /Y "%_IS_ROLLBACK_PFX%" "%_IS_KEYSTORE_ORIG%"

call "%~dp0common" Registering Inventory Service with SSO ...
call "%_IS_REGISTER_SSO_SCRIPT%" %_LS_URL% "%sso_admin_user%" "%sso_admin_password%"
rem returns 0 for success, else error
if NOT "%ERRORLEVEL%"=="0" (
	rem - not sure if a rollback is possible at this stage!!!
	set last_error=Cannot register Inventory Service with Single Sign-On - errorlevel is %ERRORLEVEL%
	goto endError
)

rem - Update IS solution user role to RegularUser - read privilege
call "%_IS_SSO_REGTOOL%" assignUserRole -d %_LS_URL% -u "%sso_admin_user%" -p "%sso_admin_password%" -su %_IS_SOLUTION_USER% -r read
rem returns 0 for success, else error
if NOT "%ERRORLEVEL%"=="0" (
	rem - not sure if a rollback is possible at this stage!!!
	set last_error=Cannot update the Inventory Service solution user role - errorlevel is %ERRORLEVEL%
	goto endError
)

call "%~dp0common" Starting Inventory Service
net START %_IS_SVC_NAME%
if ERRORLEVEL 1 (
	rem - not sure if a rollback is possible at this stage!!!
	set last_error=Cannot start Inventory Service - errorlevel is %ERRORLEVEL%
	goto endError
)

call "%~dp0common" Inventory Service Rollback SSL completed successfully
set _IS_KEYSTORE_PWD=
call "%~dp0common" The vCenter Server trust to Inventory Service needs to be updated so invoke the operation (Update vCenter Server Trust to Inventory Service) 1>&2
exit /B 0

:endErrorUndoCopyAndStartInvSvc
copy /Y "%_IS_ROLLBACK_CRT%" "%_IS_CERT_ORIG%"
if ERRORLEVEL 1 (
	set last_error=Cannot restore the original Inventory Service SSL certificate - errorlevel is %ERRORLEVEL%
	goto endError
)
copy /Y "%_IS_ROLLBACK_KEY%" "%_IS_KEY_ORIG%"
if ERRORLEVEL 1 (
	set last_error=Cannot restore the original Inventory Service SSL private key - errorlevel is %ERRORLEVEL%
	goto endError
)
copy /Y "%_IS_ROLLBACK_PFX%" "%_IS_KEYSTORE_ORIG%"
if ERRORLEVEL 1 (
	set last_error=Cannot restore the original Inventory Service PFX keystore - errorlevel is %ERRORLEVEL%
	goto endError
)

net START %_IS_SVC_NAME%

:endErrorReRegisterWithSSO
call "%~dp0common" To perform a clean rollback, registering Inventory Service with Single Sign-On ...
call "%_IS_REGISTER_SSO_SCRIPT%" %_LS_URL% "%sso_admin_user%" "%sso_admin_password%"
rem returns 0 for success, else error
if NOT "%ERRORLEVEL%"=="0" (
	rem - not sure if a rollback is possible at this stage, bailing out
	set last_error=During the Inventory Service rollback, cannot re-register Inventory Service with Single Sign-On - errorlevel is %ERRORLEVEL%
	goto endError
)

rem - Update IS solution user role to RegularUser - read privilege
call "%_IS_SSO_REGTOOL%" assignUserRole -d %_LS_URL% -u "%sso_admin_user%" -p "%sso_admin_password%" -su %_IS_SOLUTION_USER% -r read
rem returns 0 for success, else error
if NOT "%ERRORLEVEL%"=="0" (
	rem - not sure if a rollback is possible at this stage, bailing out
	set last_error=During Inventory Service rollback, cannot update Inventory Service solution user role - errorlevel is %ERRORLEVEL%
)

:endError
call "%~dp0common" "%last_error%"
call "%~dp0common" Exiting Inventory Service update SSL certificate due to errors
set _IS_KEYSTORE_PWD=
exit /B 1

:fileExistsCheck
IF NOT EXIST "%~1" (
	set last_error=The following file or folder - "%~1" - does not exist
	call "%~dp0common" The following file or folder - "%~1" - does not exist
	set %~2=1
)

exit /B 0
rem ---------------------------------------------