diff --git a/app-backend/src/datastore/DataStoreImpl.js b/app-backend/src/datastore/DataStoreImpl.js
index 91fc0b9..dbc8185 100644
--- a/app-backend/src/datastore/DataStoreImpl.js
+++ b/app-backend/src/datastore/DataStoreImpl.js
@@ -2,6 +2,7 @@
* Created by kasra on 27/06/17.
*/
const Configstore = require('configstore');
+const uuid = require('uuid/v4');
const isValidPath = require('is-valid-path');
const fs = require('fs-extra');
const ApiStatusCodes = require('../api/ApiStatusCodes');
@@ -392,11 +393,39 @@ class DataStore {
});
}
- updateAppDefinitionInDb(appName, instanceCount, envVars, volumes, nodeId, notExposeAsWebApp, ports) {
+ updateAppDefinitionInDb(appName, instanceCount, envVars, volumes, nodeId, notExposeAsWebApp, ports, appPushWebhook, authenticator) {
const self = this;
- return this.getAppDefinition(appName)
- .then(function (app) {
+ let app;
+
+ return Promise.resolve()
+ .then(function () {
+
+ return self.getAppDefinition(appName);
+
+ })
+ .then(function (appObj) {
+
+ app = appObj;
+
+ })
+ .then(function () {
+
+ if (appPushWebhook.repoInfo && appPushWebhook.repoInfo.repo && appPushWebhook.repoInfo.branch
+ && appPushWebhook.repoInfo.user && appPushWebhook.repoInfo.password) {
+ return authenticator
+ .getAppPushWebhookDatastore({
+ repo: appPushWebhook.repoInfo.repo,
+ branch: appPushWebhook.repoInfo.branch,
+ user: appPushWebhook.repoInfo.user,
+ password: appPushWebhook.repoInfo.password
+ })
+ }
+
+ return null;
+
+ })
+ .then(function (appPushWebhookRepoInfo) {
instanceCount = Number(instanceCount);
@@ -404,6 +433,22 @@ class DataStore {
app.notExposeAsWebApp = !!notExposeAsWebApp;
app.nodeId = nodeId;
+ if (appPushWebhookRepoInfo) {
+
+ app.appPushWebhook = app.appPushWebhook || {};
+
+ if (!app.appPushWebhook.tokenVersion) {
+ app.appPushWebhook.tokenVersion = uuid();
+ }
+
+ app.appPushWebhook.repoInfo = appPushWebhookRepoInfo;
+ }
+ else {
+
+ app.appPushWebhook = {};
+
+ }
+
if (ports) {
function isPortValid(portNumber) {
@@ -471,6 +516,21 @@ class DataStore {
}
}
+ })
+ .then(function () {
+
+ if (app.appPushWebhook.repoInfo) {
+ return authenticator
+ .getAppPushWebhookToken(appName, app.appPushWebhook.tokenVersion)
+ }
+
+ })
+ .then(function (pushWebhookToken) {
+
+ if (pushWebhookToken) {
+ app.appPushWebhook.pushWebhookToken = pushWebhookToken;
+ }
+
self.data.set(APP_DEFINITIONS + '.' + appName, app);
});
@@ -687,6 +747,7 @@ class DataStore {
envVars: [],
volumes: [],
ports: [],
+ appPushWebhook: {}, // tokenVersion, repoInfo, pushWebhookToken
versions: []
};
diff --git a/app-backend/src/routes/AppDefinitionRouter.js b/app-backend/src/routes/AppDefinitionRouter.js
index 5e7d300..eb4ec6c 100644
--- a/app-backend/src/routes/AppDefinitionRouter.js
+++ b/app-backend/src/routes/AppDefinitionRouter.js
@@ -4,6 +4,7 @@ const router = express.Router();
const BaseApi = require('../api/BaseApi');
const ApiStatusCodes = require('../api/ApiStatusCodes');
const Logger = require('../utils/Logger');
+const Authenticator = require('../user/Authenticator');
// Get a list of oneclickspps
router.get('/oneclickapps', function (req, res, next) {
@@ -36,19 +37,39 @@ router.get('/', function (req, res, next) {
let dataStore = res.locals.user.dataStore;
let serviceManager = res.locals.user.serviceManager;
+ let appsArray = [];
dataStore.getAppDefinitions()
.then(function (apps) {
- let appsArray = [];
+ let promises = [];
Object.keys(apps).forEach(function (key, index) {
let app = apps[key];
app.appName = key;
app.isAppBuilding = serviceManager.isAppBuilding(key);
+ app.appPushWebhook = app.appPushWebhook || {};
+
+ let repoInfoEncrypted = app.appPushWebhook ? app.appPushWebhook.repoInfo : null;
+ if (repoInfoEncrypted) {
+ promises.push(
+ Authenticator.get(dataStore.getNameSpace())
+ .decodeAppPushWebhookDatastore(repoInfoEncrypted)
+ .then(function (decryptedData) {
+ app.appPushWebhook.repoInfo = decryptedData;
+ }));
+ }
+ else {
+ app.appPushWebhook.repoInfo = {};
+ }
appsArray.push(app);
});
+ return Promise.all(promises);
+
+ })
+ .then(function () {
+
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, "App definitions are retrieved.");
baseApi.data = appsArray;
baseApi.rootDomain = dataStore.getRootDomain();
@@ -299,6 +320,7 @@ router.post('/update/', function (req, res, next) {
let appName = req.body.appName;
let nodeId = req.body.nodeId;
let notExposeAsWebApp = req.body.notExposeAsWebApp;
+ let appPushWebhook = req.body.appPushWebhook || {};
let envVars = req.body.envVars || [];
let volumes = req.body.volumes || [];
let ports = req.body.ports || [];
@@ -306,7 +328,7 @@ router.post('/update/', function (req, res, next) {
Logger.d('Updating app started: ' + appName);
- serviceManager.updateAppDefinition(appName, Number(instanceCount), envVars, volumes, nodeId, notExposeAsWebApp, ports)
+ serviceManager.updateAppDefinition(appName, Number(instanceCount), envVars, volumes, nodeId, notExposeAsWebApp, ports, appPushWebhook)
.then(function () {
Logger.d('AppName is updated: ' + appName);
diff --git a/app-backend/src/user/Authenticator.js b/app-backend/src/user/Authenticator.js
index 3681a99..4d65a53 100644
--- a/app-backend/src/user/Authenticator.js
+++ b/app-backend/src/user/Authenticator.js
@@ -10,8 +10,9 @@ const DataStoreProvider = require('../datastore/DataStoreProvider');
const captainDefaultPassword = EnvVar.DEFAULT_PASSWORD || 'captain42';
-const COOKIE_AUTH_PREFIX = 'cookie-';
-
+const COOKIE_AUTH_SUFFIX = 'cookie-';
+const WEBHOOK_APP_PUSH_SUFFIX = '-webhook-app-push';
+const WEBHOOK_APP_PUSH_DATASTORE_SUFFIX = "-webhook-app-datastore";
class Authenticator {
@@ -72,7 +73,7 @@ class Authenticator {
}
getAuthTokenForCookies(password) {
- return this.getAuthToken(password, COOKIE_AUTH_PREFIX);
+ return this.getAuthToken(password, COOKIE_AUTH_SUFFIX);
}
getAuthToken(password, keySuffix) {
@@ -101,7 +102,7 @@ class Authenticator {
}
decodeAuthTokenFromCookies(token) {
- return this.decodeAuthToken(token, COOKIE_AUTH_PREFIX);
+ return this.decodeAuthToken(token, COOKIE_AUTH_SUFFIX);
}
decodeAuthToken(token, keySuffix) {
@@ -135,6 +136,77 @@ class Authenticator {
});
}
+
+ getAppPushWebhookDatastore(dataToSave) {
+ const self = this;
+
+ return self.getGenericToken(dataToSave, WEBHOOK_APP_PUSH_DATASTORE_SUFFIX)
+ }
+
+ decodeAppPushWebhookDatastore(token) {
+ const self = this;
+
+ return self.decodeGenericToken(token, WEBHOOK_APP_PUSH_DATASTORE_SUFFIX)
+ }
+
+ getAppPushWebhookToken(appName, tokenVersion) {
+ const self = this;
+
+ if (!appName) {
+ throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'App name are required for webhook token..');
+ }
+
+ return self.getGenericToken({
+ tokenVersion: tokenVersion,
+ appName: appName
+ }, WEBHOOK_APP_PUSH_SUFFIX)
+ }
+
+ decodeAppPushWebhookToken(token) {
+ const self = this;
+
+ return self.decodeGenericToken(token, WEBHOOK_APP_PUSH_SUFFIX)
+ }
+
+ getGenericToken(obj, keySuffix) {
+
+ const self = this;
+ obj.namespace = self.namespace;
+
+ return Promise.resolve()
+ .then(function () {
+ return jwt.sign({
+ data: obj
+ }, self.encryptionKey + (keySuffix ? keySuffix : ''));
+ })
+ }
+
+ decodeGenericToken(token, keySuffix) {
+ const self = this;
+
+ return new Promise(function (resolve, reject) {
+
+ jwt.verify(token, self.encryptionKey + (keySuffix ? keySuffix : ''), function (err, rawDecoded) {
+ if (err) {
+ Logger.d(err);
+ reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_AUTH_TOKEN_INVALID, 'Token corrupted'));
+ return;
+ }
+
+ let decodedData = rawDecoded.data;
+
+ if (decodedData.namespace !== self.namespace) {
+ reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_AUTH_TOKEN_INVALID,
+ 'token does not match the namespace'));
+ return;
+ }
+
+ resolve(decodedData);
+ });
+
+ });
+ }
+
}
const authenticatorCache = {};
diff --git a/app-backend/src/user/ServiceManager.js b/app-backend/src/user/ServiceManager.js
index fb68479..4f44dee 100644
--- a/app-backend/src/user/ServiceManager.js
+++ b/app-backend/src/user/ServiceManager.js
@@ -6,6 +6,7 @@ const path = require('path');
const CaptainManager = require('./CaptainManager');
const ApiStatusCodes = require('../api/ApiStatusCodes');
const TemplateHelper = require('./TemplateHelper');
+const Authenticator = require('./Authenticator');
const uuid = require('uuid/v4');
const SOURCE_FOLDER_NAME = 'src';
@@ -630,7 +631,7 @@ class ServiceManager {
});
}
- updateAppDefinition(appName, instanceCount, envVars, volumes, nodeId, notExposeAsWebApp, ports) {
+ updateAppDefinition(appName, instanceCount, envVars, volumes, nodeId, notExposeAsWebApp, ports, appPushWebhook) {
const self = this;
const dataStore = this.dataStore;
@@ -721,7 +722,8 @@ class ServiceManager {
})
.then(function () {
- return dataStore.updateAppDefinitionInDb(appName, instanceCount, envVars, volumes, nodeId, notExposeAsWebApp, ports);
+ return dataStore.updateAppDefinitionInDb(appName, instanceCount, envVars, volumes, nodeId,
+ notExposeAsWebApp, ports, appPushWebhook, Authenticator.get(dataStore.getNameSpace()));
})
.then(function () {
diff --git a/app-frontend/src/js/captain/apiManager.js b/app-frontend/src/js/captain/apiManager.js
index 6388868..73f05f1 100644
--- a/app-frontend/src/js/captain/apiManager.js
+++ b/app-frontend/src/js/captain/apiManager.js
@@ -382,6 +382,7 @@
var volumes = appDefinition.volumes;
var ports = appDefinition.ports;
var nodeId = appDefinition.nodeId;
+ var appPushWebhook = appDefinition.appPushWebhook;
$http
.post(BASE_API + 'user/appDefinitions/update', {
@@ -390,6 +391,7 @@
notExposeAsWebApp: notExposeAsWebApp,
volumes: volumes,
ports: ports,
+ appPushWebhook: appPushWebhook,
nodeId: nodeId,
envVars: envVars
}, createConfig())
diff --git a/app-frontend/src/js/controllers/app-definitions-ctrl.js b/app-frontend/src/js/controllers/app-definitions-ctrl.js
index 4d40342..22c2269 100644
--- a/app-frontend/src/js/controllers/app-definitions-ctrl.js
+++ b/app-frontend/src/js/controllers/app-definitions-ctrl.js
@@ -6,6 +6,7 @@ angular.module('RDash')
function AppDefinitionCtrl($scope, $cookieStore, $rootScope, pageDefinitions,
apiManager, captainToast, $uibModal, $state, $location) {
+ $scope.rootDomainWithProtocol = window.location.protocol;
$scope.loadingState = {};
$scope.loadingState.enabled = true;
$scope.search = {};
@@ -36,6 +37,7 @@ function AppDefinitionCtrl($scope, $cookieStore, $rootScope, pageDefinitions,
}
$scope.rootDomain = data.rootDomain;
+ $scope.rootDomainWithProtocol = window.location.protocol + '//' + data.rootDomain;
});
@@ -257,6 +259,7 @@ function AppDefinitionCtrl($scope, $cookieStore, $rootScope, pageDefinitions,
ports: app.ports,
nodeId: app.nodeId,
notExposeAsWebApp: app.notExposeAsWebApp,
+ appPushWebhook: app.appPushWebhook,
volumes: app.volumes
}
diff --git a/app-frontend/src/templates/apps.html b/app-frontend/src/templates/apps.html
index 68aeee1..abcae73 100644
--- a/app-frontend/src/templates/apps.html
+++ b/app-frontend/src/templates/apps.html
@@ -236,6 +236,48 @@