From 458d851d1cb41bd588dc1973c8810d9909d19c80 Mon Sep 17 00:00:00 2001 From: Radu Cotescu Date: Mon, 8 Dec 2014 17:10:28 +0200 Subject: [PATCH] fix #29: Failure to connect to an AEM instance listening to HTTPS connections only * added option in project settings to allow the use of self-signed certificates if needed --- sly/nls/en-gb/strings.js | 3 +- sly/nls/ro/strings.js | 3 +- sly/nls/root/strings.js | 3 +- sly/node/PackMgr.js | 64 +++++++++----------- sly/node/SlyDomain.js | 63 ++++++++++--------- sly/node/VaultSyncManager.js | 18 +++--- sly/preferences/Preferences.js | 29 +++++++-- sly/preferences/project-settings-dialog.html | 26 ++++++++ 8 files changed, 130 insertions(+), 79 deletions(-) diff --git a/sly/nls/en-gb/strings.js b/sly/nls/en-gb/strings.js index 59d76dd..ac3afed 100644 --- a/sly/nls/en-gb/strings.js +++ b/sly/nls/en-gb/strings.js @@ -36,6 +36,7 @@ define({ PROJECT_SETTING_SERVER_URL_ERROR_INVALID_CHAR : 'Special characters like \'{0}\' must be %-encoded.', PROJECT_SETTING_REMOTE_USER_ERROR_EMPTY : 'Please provide a username.', PROJECT_SETTING_REMOTE_USER_PASSWORD_ERROR_EMPTY : 'Please provide a password.', + PROJECT_SETTING_ACCEPT_SELF_SIGNED_CERTIFICATES : 'Accept self-signed certificates for HTTPS', // Synchronisation indicator tooltip SYNC_FULL : 'All selected files were synced successfully.', @@ -53,4 +54,4 @@ define({ SYNC_STATUS_EXCLUDED_VLT : 'vlt file or file excluded by .vltignore pattern', SYNC_STATUS_DELETED_FROM_REMOTE : 'removed - the file was deleted on the server' -}); \ No newline at end of file +}); diff --git a/sly/nls/ro/strings.js b/sly/nls/ro/strings.js index 3961956..f116653 100644 --- a/sly/nls/ro/strings.js +++ b/sly/nls/ro/strings.js @@ -36,6 +36,7 @@ define({ PROJECT_SETTING_SERVER_URL_ERROR_INVALID_CHAR : 'Caracterele speciale precum \'{0}\' trebuie să fie %-codificate.', PROJECT_SETTING_REMOTE_USER_ERROR_EMPTY : 'Vă rugăm să introduceți utilizatorul.', PROJECT_SETTING_REMOTE_USER_PASSWORD_ERROR_EMPTY : 'Vă rugăm să introduceți parola.', + PROJECT_SETTING_ACCEPT_SELF_SIGNED_CERTIFICATES : 'Acceptați certificate care nu sunt semnate de CA', // Synchronisation indicator tooltip SYNC_FULL : 'Toate fișierele selectate au fost sincronizate cu succes.', @@ -53,4 +54,4 @@ define({ SYNC_STATUS_EXCLUDED_VLT : 'fișier vlt sau fișier exclus de către .vltignore', SYNC_STATUS_DELETED_FROM_REMOTE : 'șters - fișierul a fost șters pe server' -}); \ No newline at end of file +}); diff --git a/sly/nls/root/strings.js b/sly/nls/root/strings.js index 20e910f..9b597ab 100644 --- a/sly/nls/root/strings.js +++ b/sly/nls/root/strings.js @@ -36,6 +36,7 @@ define({ PROJECT_SETTING_SERVER_URL_ERROR_INVALID_CHAR : 'Special characters like \'{0}\' must be %-encoded.', PROJECT_SETTING_REMOTE_USER_ERROR_EMPTY : 'Please provide a username.', PROJECT_SETTING_REMOTE_USER_PASSWORD_ERROR_EMPTY : 'Please provide a password.', + PROJECT_SETTING_ACCEPT_SELF_SIGNED_CERTIFICATES : 'Accept self-signed certificates for HTTPS', // Synchronisation indicator tooltip SYNC_FULL : 'All selected files were synced successfully.', @@ -53,4 +54,4 @@ define({ SYNC_STATUS_EXCLUDED_VLT : 'vlt file or file excluded by .vltignore pattern', SYNC_STATUS_DELETED_FROM_REMOTE : 'removed - the file was deleted on the server' -}); \ No newline at end of file +}); diff --git a/sly/node/PackMgr.js b/sly/node/PackMgr.js index 09b462e..9c8e66b 100644 --- a/sly/node/PackMgr.js +++ b/sly/node/PackMgr.js @@ -23,23 +23,19 @@ node:true, eqeqeq:true, strict:true, undef:true, bitwise:true, immed:true, maxle /** * Uploads the ZIP content package from packageFilePath to the AEM instance from serverURL. * @param {String} serverURL the server's URL (e.g. http://localhost:4502) + * @param {boolean} acceptSelfSigned boolean flag to indicate if self-signed certificates should be acceppted for HTTPs connections * @param {String} user a user allowed to manage content packages * @param {String} password the user's password * @param {String} packageFilePath the path to the content package on the file system * @returns {promise|Q.promise} a promise */ - function uploadPackage(serverURL, user, password, packageFilePath) { + function uploadPackage(serverURL, acceptSelfSigned, user, password, packageFilePath) { var deferred = Q.defer(), packageStream = Fs.createReadStream(packageFilePath), uri = serverURL + URL + '?cmd=upload'; var r = Request.post( uri, - { - auth: { - user: user, - pass: password - } - }, + _createRequestOptions(user, password, acceptSelfSigned), function(err, httpResponse, body) { if (!_errorDetected(err, httpResponse, serverURL, 200, deferred)) { var response = JSON.parse(body); @@ -61,22 +57,18 @@ node:true, eqeqeq:true, strict:true, undef:true, bitwise:true, immed:true, maxle /** * Installs a previously uploaded content package on the AEM instance from serverURL. * @param {String} serverURL the server's URL (e.g. http://localhost:4502) + * @oaram {boolean} acceptSelfSigned boolean flag to indicate if self-signed certificates should be acceppted for HTTPs connections * @param {String} user a user allowed to manage content packages * @param {String} password the user's password * @param {String} packageName the full content package name (e.g. 'group/name-version.zip') * @returns {promise|Q.promise} a promise */ - function installPackage(serverURL, user, password, packageName) { + function installPackage(serverURL, acceptSelfSigned, user, password, packageName) { var deferred = Q.defer(), uri = serverURL + URL + '/etc/packages/' + packageName + '?cmd=install'; Request.post( uri, - { - auth: { - user: user, - pass: password - } - }, + _createRequestOptions(user, password, acceptSelfSigned), function (err, httpResponse, body) { if (!_errorDetected(err, httpResponse, serverURL, 200, deferred)) { var response = JSON.parse(body); @@ -94,22 +86,18 @@ node:true, eqeqeq:true, strict:true, undef:true, bitwise:true, immed:true, maxle /** * Builds a previously uploaded content package on the AEM instance from serverURL. * @param {String} serverURL the server's URL (e.g. http://localhost:4502) + * @oaram {boolean} acceptSelfSigned boolean flag to indicate if self-signed certificates should be acceppted for HTTPs connections * @param {String} user a user allowed to manage content packages * @param {String} password the user's password * @param {String} packageName the full content package name (e.g. 'group/name-version.zip * @returns {promise|Q.promise} a promise */ - function buildPackage(serverURL, user, password, packageName) { + function buildPackage(serverURL, acceptSelfSigned, user, password, packageName) { var deferred = Q.defer(), uri = serverURL + URL + '/etc/packages/' + packageName + '?cmd=build'; Request.post( uri, - { - auth: { - user: user, - pass: password - } - }, + _createRequestOptions(user, password, acceptSelfSigned), function(err, httpResponse, body) { if (!_errorDetected(err, httpResponse, serverURL, 200, deferred)) { var response = JSON.parse(body); @@ -127,22 +115,18 @@ node:true, eqeqeq:true, strict:true, undef:true, bitwise:true, immed:true, maxle /** * Deletes a previously uploaded content package on the AEM instance from serverURL. * @param {String} serverURL the server's URL (e.g. http://localhost:4502) + * @oaram {boolean} acceptSelfSigned boolean flag to indicate if self-signed certificates should be acceppted for HTTPs connections * @param {String} user a user allowed to manage content packages * @param {String} password the user's password * @param {String} packageName the full content package name (e.g. 'group/name-version.zip) * @returns {promise|Q.promise} a promise */ - function deletePackage(serverURL, user, password, packageName) { + function deletePackage(serverURL, acceptSelfSigned, user, password, packageName) { var deferred = Q.defer(), uri = serverURL + URL + '/etc/packages/' + packageName + '?cmd=delete'; Request.post( uri, - { - auth: { - user: user, - pass: password - } - }, + _createRequestOptions(user, password, acceptSelfSigned), function(err, httpResponse, body) { if (!_errorDetected(err, httpResponse, serverURL, 200, deferred)) { var response = JSON.parse(body); @@ -161,6 +145,7 @@ node:true, eqeqeq:true, strict:true, undef:true, bitwise:true, immed:true, maxle * Downloads package packageName from the AEM instance available at serverURL to the outputFolder * folder. * @param {String} serverURL the server's URL (e.g. http://localhost:4502) + * @oaram {boolean} acceptSelfSigned boolean flag to indicate if self-signed certificates should be acceppted for HTTPs connections * @param {String} user a user allowed to manage content packages * @param {String} password the user's password * @param {String} packageName the full content package name (e.g. 'group/name-version.zip) @@ -168,7 +153,7 @@ node:true, eqeqeq:true, strict:true, undef:true, bitwise:true, immed:true, maxle * @param {String} [outputFileName] the file name under which to save the downloaded package * @returns {promise|Q.promise} a promise */ - function downloadPackage(serverURL, user, password, packageName, outputFolder, outputFileName) { + function downloadPackage(serverURL, acceptSelfSigned, user, password, packageName, outputFolder, outputFileName) { var deferred = Q.defer(), uri = serverURL + '/etc/packages/' + packageName, mkdirp = Q.denodeify(Fs.mkdirp); @@ -180,12 +165,7 @@ node:true, eqeqeq:true, strict:true, undef:true, bitwise:true, immed:true, maxle function() { var r = Request.get( uri, - { - auth: { - user: user, - pass: password - } - }, + _createRequestOptions(user, password, acceptSelfSigned), function(err, httpResponse) { _errorDetected(err, httpResponse, serverURL, 200, deferred); } @@ -223,9 +203,21 @@ node:true, eqeqeq:true, strict:true, undef:true, bitwise:true, immed:true, maxle return false; } + function _createRequestOptions(user, password, acceptSelfSigned) { + return { + auth: { + user: user, + pass: password + }, + agentOptions: { + rejectUnauthorized: !acceptSelfSigned + } + } + } + exports.uploadPackage = uploadPackage; exports.installPackage = installPackage; exports.buildPackage = buildPackage; exports.deletePackage = deletePackage; exports.downloadPackage = downloadPackage; -}()); \ No newline at end of file +}()); diff --git a/sly/node/SlyDomain.js b/sly/node/SlyDomain.js index 2f97191..7db65b3 100644 --- a/sly/node/SlyDomain.js +++ b/sly/node/SlyDomain.js @@ -6,16 +6,17 @@ ******************************************************************************/ (function () { 'use strict'; - var Fs = require('fs'), - Path = require('path'), - Request = require('request'), + var Fs = require('fs'), + Path = require('path'), + Request = require('request'), VaultSyncManager = require('./VaultSyncManager'), _remote, + _acceptSelfSignedCertificates = false, _remoteUser, _remotePassword, JCR_ROOT = 'jcr_root', VAULT_ROOT = Path.sep + JCR_ROOT + Path.sep; - + function _extractRemotePath(path) { var index = path.indexOf(VAULT_ROOT); if (index > 0) { @@ -24,12 +25,13 @@ return null; } - function setRemote(remote, remoteUser, remotePassword) { - _remote = remote; - _remoteUser = remoteUser; + function setRemote(remote, remoteUser, remotePassword, acceptSelfSignedCertificates) { + _remote = remote; + _remoteUser = remoteUser; _remotePassword = remotePassword; + _acceptSelfSignedCertificates = acceptSelfSignedCertificates || false; } - + function _internPost(path, formData, callback) { var r = Request.post( _remote + path, @@ -39,7 +41,7 @@ pass: _remotePassword } }, - function(err, httpResponse) { + function (err, httpResponse) { if (err) { if (err.code === 'ECONNREFUSED') { callback('Connection to server ' + _remote + ' was refused.'); @@ -73,8 +75,8 @@ function postFile(parentPath, filePath, callback) { var formData = { - '_charset_' : 'utf-8', - '*' : Fs.createReadStream(filePath) + '_charset_': 'utf-8', + '*': Fs.createReadStream(filePath) }; if (parentPath.indexOf('/install', parentPath.length - '/install'.length) !== -1) { // in that case we ensure node type is fulfilled in case the folder needs to be created @@ -98,50 +100,57 @@ } function pushVault(path, filterFile, callback) { - return VaultSyncManager.sync(_remote, _remoteUser, _remotePassword, path, filterFile, VaultSyncManager.PUSH).then( - function(fileSyncStatus) { + return VaultSyncManager.sync(_remote, _acceptSelfSignedCertificates, _remoteUser, _remotePassword, path, filterFile, VaultSyncManager.PUSH).then( + function (fileSyncStatus) { callback(null, fileSyncStatus); }, - function(err) { + function (err) { callback(err.message); } ); } function pullVault(path, filterFile, callback) { - return VaultSyncManager.sync(_remote, _remoteUser, _remotePassword, path, filterFile, VaultSyncManager.PULL).then( - function(fileSyncStatus) { + return VaultSyncManager.sync(_remote, _acceptSelfSignedCertificates, _remoteUser, _remotePassword, path, filterFile, VaultSyncManager.PULL).then( + function (fileSyncStatus) { callback(null, fileSyncStatus); }, - function(err) { + function (err) { callback(err.message); } ); } - + /** - * Initializes the test domain with several test commands. + * Initializes the domain. * @param {DomainManager} domainManager The DomainManager for the server */ function init(domainManager) { if (!domainManager.hasDomain('sly')) { - domainManager.registerDomain('sly', { major: 0, minor: 1}); + domainManager.registerDomain('sly', {major: 0, minor: 1}); } - + domainManager.registerCommand('sly', 'setRemote', setRemote, true, 'set remote server configuration', - [{name: 'remote', type: 'string', description: 'scheme, host & port string, e.g. http://localhost:4502'}, + [ + {name: 'remote', type: 'string', description: 'scheme, host & port string, e.g. http://localhost:4502'}, {name: 'remoteUser', type: 'string', description: 'remote username'}, - {name: 'remotePassword', type: 'string', description: 'remote user password'}], + {name: 'remotePassword', type: 'string', description: 'remote user password'}, + { + name: 'acceptSelfSignedCertificates', + type: 'boolean', + description: 'indicate whether to accept self-signed certificates for HTTPS connections' + } + ], []); - + domainManager.registerCommand('sly', 'postFile', postFile, true, 'post a file to the remote', [{name: 'parentPath', type: 'string', description: 'path of the remote parent'}, {name: 'filePath', type: 'string', description: 'path of the file'}], []); domainManager.registerCommand('sly', 'syncChildProcess', syncChildProcess, true, 'execute a child process', - [ {name: 'command', type: 'string', description: 'execute an alternative command for synchronizing files'}, + [{name: 'command', type: 'string', description: 'execute an alternative command for synchronizing files'}, {name: 'path', type: 'string', description: 'path of the document/folder'} - ], + ], []); domainManager.registerCommand( 'sly', @@ -171,4 +180,4 @@ exports.init = init; -}()); \ No newline at end of file +}()); diff --git a/sly/node/VaultSyncManager.js b/sly/node/VaultSyncManager.js index 7b34c75..0ab1770 100644 --- a/sly/node/VaultSyncManager.js +++ b/sly/node/VaultSyncManager.js @@ -650,6 +650,7 @@ * Synchronises the supplied file-system path path with an AEM server. * * @param {String} server the URL of the AEM server (e.g. http://localhost:4502/contextpath) + * @param {boolean} acceptSelfSignedCert flag to indicate if self-signed certificates should be accepted or not * @param {String} user the user used for synchronisation * @param {String} password the user's password * @param {String} path the path on the file-system for which to perform the synchronisation operation @@ -657,7 +658,7 @@ * @param {String} action the synchronisation operation type (VaultSyncManager.PULL or VaultSyncManager.PUSH) * @returns {promise|Q.promise} a promise resolved when the synchronisation operation completed successfully */ - function sync(server, user, password, path, filterFile, action) { + function sync(server, acceptSelfSignedCert, user, password, path, filterFile, action) { var deferred = Q.defer(); path = Path.resolve(path); if (isPathInJCRCheckout(path)) { @@ -670,6 +671,7 @@ fileSyncStatus = {}, remotePath = getRemotePath(path), pathsFromRemote = {}, + acceptSelfSigned = acceptSelfSignedCert || false, vaultIgnore; parseFilterXML(filterFile).then( function (_filters) { @@ -748,11 +750,11 @@ } ).then( function (zipFileName) { - return PackMgr.uploadPackage(server, user, password, zipFileName).then( + return PackMgr.uploadPackage(server, acceptSelfSigned, user, password, zipFileName).then( function () { - return PackMgr.installPackage(server, user, password, fullPackageName).then( + return PackMgr.installPackage(server, acceptSelfSigned, user, password, fullPackageName).then( function () { - return PackMgr.deletePackage(server, user, password, fullPackageName); + return PackMgr.deletePackage(server, acceptSelfSigned, user, password, fullPackageName); } ); } @@ -773,10 +775,10 @@ return createContentPackageArchive(tempFolder, 'pkg').then( function (_zipFileName) { zipFileName = _zipFileName; - return PackMgr.uploadPackage(server, user, password, zipFileName) + return PackMgr.uploadPackage(server, acceptSelfSigned, user, password, zipFileName) .then( function () { - return PackMgr.buildPackage(server, user, password, fullPackageName); + return PackMgr.buildPackage(server, acceptSelfSigned, user, password, fullPackageName); } ); } @@ -804,10 +806,10 @@ ).then( function () { return PackMgr.downloadPackage( - server, user, password, fullPackageName, tempFolder, 'pkg.zip' + server, acceptSelfSigned, user, password, fullPackageName, tempFolder, 'pkg.zip' ).then( function (downloadedPackage) { - return PackMgr.deletePackage(server, user, password, + return PackMgr.deletePackage(server, acceptSelfSigned, user, password, fullPackageName).then( function () { return extractContentPackageArchive(tempFolder, diff --git a/sly/preferences/Preferences.js b/sly/preferences/Preferences.js index e72f424..d84a56b 100644 --- a/sly/preferences/Preferences.js +++ b/sly/preferences/Preferences.js @@ -39,25 +39,26 @@ define(function (require, exports, module) { return StringUtils.format(Strings.PROJECT_SETTING_SERVER_URL_ERROR_INVALID_CHAR, url[index]); } return ''; - } + }; validators.remoteUser = function(user) { if (user === '') { return Strings.PROJECT_SETTING_REMOTE_USER_ERROR_EMPTY; } return ''; - } + }; validators.remoteUserPassword = function(password) { if (password === '') { return Strings.PROJECT_SETTING_REMOTE_USER_PASSWORD_ERROR_EMPTY; } return ''; - } + }; scopes.serverUrl = 'project'; scopes.remoteUser = 'project'; scopes.remoteUserPassword = 'project'; + scopes.acceptSelfSignedCertificates = 'project'; scopes.syncedLanguages = 'user'; @@ -77,6 +78,10 @@ define(function (require, exports, module) { return get('serverUrl'); } + function acceptSelfSignedCertificates() { + return get('acceptSelfSignedCertificates'); + } + function getRemoteUser() { return get('remoteUser'); } @@ -90,7 +95,7 @@ define(function (require, exports, module) { } function load(SLYDictionary) { - SlyDomain.exec('setRemote', getRemote(), getRemoteUser(), getRemotePassword()); + SlyDomain.exec('setRemote', getRemote(), getRemoteUser(), getRemotePassword(), acceptSelfSignedCertificates()); } function openProjectPreferences(errorMessage) { @@ -102,6 +107,7 @@ define(function (require, exports, module) { serverUrl : getRemote(), remoteUser : getRemoteUser(), remoteUserPassword : getRemotePassword(), + acceptSelfSignedCertificates: acceptSelfSignedCertificates(), errorMessage : errorMessage }; dialog = Dialogs.showModalDialogUsingTemplate(Mustache.render(ProjectSettingsDialogTemplate, templateVars)); @@ -109,6 +115,18 @@ define(function (require, exports, module) { dialog.done(function(id) { if (id === Dialogs.DIALOG_BTN_OK) { formData = dialog.getElement().find('form').serializeArray(); + var i, + found = false; + for (i = 0; i < formData.length; i++) { + var entry = formData[i]; + if (entry.name === 'acceptSelfSignedCertificates' && entry.value === 'on') { + formData[i] = {name: 'acceptSelfSignedCertificates', value: true}; + found = true; + } + } + if (!found) { + formData.push({name: 'acceptSelfSignedCertificates', value: false}); + } var validationResult = _validatePreferencesForm(formData); if (validationResult === '') { formData.forEach(function(entry) { @@ -154,7 +172,7 @@ define(function (require, exports, module) { slyPreferences.on( 'change', function (err, data) { - SlyDomain.exec('setRemote', getRemote(), getRemoteUser(), getRemotePassword()); + SlyDomain.exec('setRemote', getRemote(), getRemoteUser(), getRemotePassword(), acceptSelfSignedCertificates()); } ); @@ -164,5 +182,6 @@ define(function (require, exports, module) { exports.getSyncedLanguages = getSyncedLanguages; exports.getRemoteUser = getRemoteUser; exports.getRemotePassword = getRemotePassword; + exports.acceptSelfSignedCertificates = acceptSelfSignedCertificates; exports.openProjectPreferences = openProjectPreferences; }); diff --git a/sly/preferences/project-settings-dialog.html b/sly/preferences/project-settings-dialog.html index b49553c..a741d3c 100644 --- a/sly/preferences/project-settings-dialog.html +++ b/sly/preferences/project-settings-dialog.html @@ -19,6 +19,11 @@

{{Strings.PROJECT_SETTINGS}}

+ + {{Strings.PROJECT_SETTING_ACCEPT_SELF_SIGNED_CERTIFICATES}}: + + {{Strings.PROJECT_SETTING_REMOTE_USER}}: {{Strings.PROJECT_SETTINGS}} +