diff --git a/dashboard/src/app/index.module.ts b/dashboard/src/app/index.module.ts
index edea2597f7f..bae894fcd17 100644
--- a/dashboard/src/app/index.module.ts
+++ b/dashboard/src/app/index.module.ts
@@ -115,6 +115,7 @@ initModule.run(['$rootScope', '$location', '$routeParams', 'routingRedirect', '$
$rootScope.showIDE = false;
workspaceDetailsService.addSection('Projects', '', 'icon-ic_inbox_24px');
+ workspaceDetailsService.addSection('SSH', '', 'icon-ic_vpn_key_24px');
// here only to create instances of these components
cheIdeFetcher;
diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.controller.ts b/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.controller.ts
new file mode 100644
index 00000000000..40cafb85a6c
--- /dev/null
+++ b/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.controller.ts
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015-2016 Codenvy, S.A.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Codenvy, S.A. - initial API and implementation
+ */
+import {CheWorkspace} from '../../../../components/api/che-workspace.factory';
+import {CheNotification} from '../../../../components/notification/che-notification.factory';
+import {CheSsh} from '../../../../components/api/che-ssh.factory';
+'use strict';
+
+/**
+ * @ngdoc controller
+ * @name workspace.details.controller:WorkspaceDetailsSSHCtrl
+ * @description This class is handling the controller for details of workspace : section ssh
+ * @author Florent Benoit
+ */
+export class WorkspaceDetailsSshCtrl {
+
+ /**
+ * Workspace.
+ */
+ private cheWorkspace: CheWorkspace;
+
+ /**
+ * SSH.
+ */
+ private cheSsh: CheSsh;
+
+ /**
+ * Notification.
+ */
+ private cheNotification: CheNotification;
+
+ /**
+ * Material Design Dialog Service
+ */
+ private $mdDialog: ng.material.IDialogService;
+
+ /**
+ * Angular Log service.
+ */
+ private $log: ng.ILogService;
+
+ /**
+ * Angular Q service.
+ */
+ private $q: ng.IQService;
+
+ private $timeout : ng.ITimeoutService;
+
+ private namespace : string;
+ private workspaceName : string;
+ private workspaceKey : string;
+ private workspace : any;
+ private workspaceId: string;
+
+ private sshKeyPair : any;
+
+ /**
+ * True if one machine has ssh agent enabled.
+ */
+ private hasSSHAgents: boolean;
+
+ private machineSshAgents : Array<{agentEnabled : boolean, name: string}>;
+
+ /**
+ * Default constructor that is using resource
+ * @ngInject for Dependency injection
+ */
+ constructor($route : ng.route.IRouteService, cheSsh: CheSsh, cheWorkspace: CheWorkspace, cheNotification, $mdDialog : ng.material.IDialogService, $log : ng.ILogService, $q : ng.IQService, $timeout : ng.ITimeoutService) {
+ this.cheWorkspace = cheWorkspace;
+ this.cheSsh = cheSsh;
+ this.cheNotification = cheNotification;
+ this.$mdDialog = $mdDialog;
+ this.$log = $log;
+ this.$q = $q;
+
+ this.machineSshAgents = new Array<>();
+ this.namespace = $route.current.params.namespace;
+ this.workspaceName = $route.current.params.workspaceName;
+ this.workspaceKey = this.namespace + ':' + this.workspaceName;
+
+ this.updateData();
+
+ }
+
+
+ updateData() {
+ this.hasSSHAgents = false;
+ this.workspace = this.cheWorkspace.getWorkspaceByName(this.namespace, this.workspaceName);
+ this.workspaceId = this.workspace.id;
+
+ // get ssh key
+ this.cheSsh.fetchKey('workspace', this.workspaceId).finally(() => {
+ this.sshKeyPair = this.cheSsh.getKey('workspace', this.workspaceId);
+
+ });
+
+ let defaultEnv : string = this.workspace.config.defaultEnv;
+ let machines : any = this.workspace.config.environments[defaultEnv].machines;
+ let machineNames : Array = Object.keys(machines);
+ this.machineSshAgents.length = 0;
+ machineNames.forEach((machineName) => {
+ let enabled : boolean = machines[machineName].agents.indexOf('org.eclipse.che.ssh') >= 0;
+ let machineAgent = {agentEnabled : enabled, name: machineName};
+ this.machineSshAgents.push(machineAgent);
+ if (enabled) {
+ this.hasSSHAgents = true;
+ }
+ });
+
+ }
+
+ /**
+ * Remove the default workspace keypair
+ */
+ removeDefaultKey() {
+ this.cheSsh.removeKey('workspace', this.workspaceId).then(
+ () => {this.$timeout( this.updateData(), 3000)}
+ );
+ }
+
+ /**
+ * Generate a new default workspace keypair
+ */
+ generateDefaultKey() {
+ this.cheSsh.generateKey('workspace', this.workspaceId).then(() => {this.$timeout( this.updateData(), 3000)});
+ }
+
+
+}
diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.directive.ts b/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.directive.ts
new file mode 100644
index 00000000000..8c0a85c7bb1
--- /dev/null
+++ b/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.directive.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015-2016 Codenvy, S.A.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Codenvy, S.A. - initial API and implementation
+ */
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name workspaces.details.directive:workspaceDetailsSSH
+ * @restrict E
+ * @element
+ *
+ * @description
+ * ` for displaying workspace ssh entry.
+ *
+ * @usage
+ *
+ *
+ * @author Florent Benoit
+ */
+export class WorkspaceDetailsSsh {
+
+
+ restrict: string = 'E';
+ templateUrl: string = 'app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.html';
+
+ controller: string = 'WorkspaceDetailsSshCtrl';
+ controllerAs: string = 'workspaceDetailsSshCtrl';
+ bindToController: boolean = true;
+
+
+ /**
+ * Default constructor that is using resource
+ * @ngInject for Dependency injection
+ */
+ constructor () {
+
+ }
+
+}
diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.html b/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.html
new file mode 100644
index 00000000000..06f1cc2faed
--- /dev/null
+++ b/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+
Workspace is not running. SSH connections are unavailable.
+
+
+
+
SSH keys are configured but there is no SSH agent enabled on machines. SSH connections are unavailable.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This workspace does not have a generated SSH key. We can generate a new one here or you can add your own in the IDE.
+ Note: Workspace is currently in RUNNING state. It will require a restart of the workspace in order to be taken into account.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{machine.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.styl b/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.styl
new file mode 100644
index 00000000000..80ee2a27724
--- /dev/null
+++ b/dashboard/src/app/workspaces/workspace-details/workspace-ssh/workspace-details-ssh.styl
@@ -0,0 +1,47 @@
+.workspace-ssh-content
+ margin 0
+ button
+ margin 0
+
+.workspace-ssh-content
+ .che-text-info-mobile span, .che-text-info-desktop span
+ border-width 0
+ padding-bottom 20px
+
+ .che-text-info-mobile span label, .che-text-info-desktop span label
+ max-width 500px
+ word-wrap break-word
+ font-family Fixed, monospace
+ color $disabled-color
+
+ .che-text-info .copy-clipboard
+ margin-top 50px
+ margin-left 20px
+
+ .workspace-ssh-content-notice
+ padding-top 20px
+
+ .che-list-item-content *:not(.tooltip)
+ overflow visible
+
+ .che-list-header md-item
+ border none
+
+ .che-list-header-content > *
+ margin: 0
+
+ .che-list
+ margin: 0
+
+ .workspace-ssh-content-invalid-notification
+ background-color $invalid-color
+ color $error-color
+ padding: 5px
+ margin: 10px 0 10px 0
+
+ .fa
+ font-size 18px
+ padding-right 5px
+
+.workspace-ssh-content .workspace-ssh-content-private .che-text-info-desktop span label
+ white-space pre
diff --git a/dashboard/src/app/workspaces/workspaces-config.ts b/dashboard/src/app/workspaces/workspaces-config.ts
index 8a363da4bab..7fe526ac172 100644
--- a/dashboard/src/app/workspaces/workspaces-config.ts
+++ b/dashboard/src/app/workspaces/workspaces-config.ts
@@ -21,6 +21,8 @@ import {UsageChart} from './list-workspaces/workspace-item/usage-chart.directive
import {WorkspaceItemCtrl} from './list-workspaces/workspace-item/workspace-item.controller';
import {WorkspaceEditModeOverlay} from './workspace-edit-mode/workspace-edit-mode-overlay.directive';
import {WorkspaceEditModeToolbarButton} from './workspace-edit-mode/workspace-edit-mode-toolbar-button.directive';
+import {WorkspaceDetailsSsh} from './workspace-details/workspace-ssh/workspace-details-ssh.directive';
+import {WorkspaceDetailsSshCtrl} from './workspace-details/workspace-ssh/workspace-details-ssh.controller';
import {WorkspaceDetailsProjectsCtrl} from './workspace-details/workspace-projects/workspace-details-projects.controller';
import {WorkspaceDetailsService} from './workspace-details/workspace-details.service';
import {ExportWorkspaceController} from './workspace-details/export-workspace/export-workspace.controller';
@@ -83,6 +85,9 @@ export class WorkspacesConfig {
new CreateProjectStackLibrarySelectedStackFilter(register);
+ register.controller('WorkspaceDetailsSshCtrl', WorkspaceDetailsSshCtrl);
+ register.directive('workspaceDetailsSsh', WorkspaceDetailsSsh);
+
register.controller('ListWorkspacesCtrl', ListWorkspacesCtrl);
register.controller('WorkspaceDetailsController', WorkspaceDetailsController);
register.controller('WorkspaceStacksController', WorkspaceStacksController);
diff --git a/dashboard/src/components/api/che-api-config.ts b/dashboard/src/components/api/che-api-config.ts
index e6bde6c75b3..8a1a69d9117 100644
--- a/dashboard/src/components/api/che-api-config.ts
+++ b/dashboard/src/components/api/che-api-config.ts
@@ -30,6 +30,7 @@ import {CheRemote} from './remote/che-remote.factory';
import {CheOAuthProvider} from './che-o-auth-provider.factory';
import {CheEnvironmentRegistry} from './environment/che-environment-registry.factory';
import {CheAgent} from './che-agent.factory';
+import {CheSsh} from './che-ssh.factory';
import {CheNamespaceRegistry} from './namespace/che-namespace-registry.factory';
export class ApiConfig {
@@ -54,6 +55,7 @@ export class ApiConfig {
register.factory('cheOAuthProvider', CheOAuthProvider);
register.factory('cheEnvironmentRegistry', CheEnvironmentRegistry);
register.factory('cheAgent', CheAgent);
+ register.factory('cheSsh', CheSsh);
register.factory('cheNamespaceRegistry', CheNamespaceRegistry);
}
}
diff --git a/dashboard/src/components/api/che-api.factory.ts b/dashboard/src/components/api/che-api.factory.ts
index 5dd504d41ae..dcbe9271172 100644
--- a/dashboard/src/components/api/che-api.factory.ts
+++ b/dashboard/src/components/api/che-api.factory.ts
@@ -8,6 +8,8 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*/
+
+import {CheSsh} from './che-ssh.factory';
'use strict';
@@ -18,12 +20,15 @@
*/
export class CheAPI {
+
+ private cheSsh : CheSsh;
+
/**
* Default constructor that is using resource
* @ngInject for Dependency injection
*/
constructor(cheWorkspace, cheProfile, chePreferences, cheProjectTemplate, cheWebsocket, cheService,
- cheAdminPlugins, cheAdminService, cheRecipe, cheRecipeTemplate, cheStack, cheOAuthProvider, cheAgent) {
+ cheAdminPlugins, cheAdminService, cheRecipe, cheRecipeTemplate, cheStack, cheOAuthProvider, cheAgent, cheSsh : CheSsh) {
this.cheWorkspace = cheWorkspace;
this.cheProfile = cheProfile;
this.chePreferences = chePreferences;
@@ -37,6 +42,7 @@ export class CheAPI {
this.cheStack = cheStack;
this.cheOAuthProvider = cheOAuthProvider;
this.cheAgent = cheAgent;
+ this.cheSsh = cheSsh;
}
@@ -145,4 +151,12 @@ export class CheAPI {
return this.cheAgent;
}
+ /**
+ * Gets Che ssh API
+ * @returns {CheSsh}
+ */
+ getSsh() {
+ return this.cheSsh;
+ }
+
}
diff --git a/dashboard/src/components/api/che-ssh.factory.ts b/dashboard/src/components/api/che-ssh.factory.ts
new file mode 100644
index 00000000000..ef237d1fe42
--- /dev/null
+++ b/dashboard/src/components/api/che-ssh.factory.ts
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2015-2016 Codenvy, S.A.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Codenvy, S.A. - initial API and implementation
+ */
+'use strict';
+
+/**
+ * This class is handling the ssh keys
+ * @author Florent Benoit
+ */
+export class CheSsh {
+
+ /**
+ * Angular Resource service.
+ */
+ private $resource: ng.resource.IResourceService;
+
+ /**
+ * Angular Promise service.
+ */
+ private $q: ng.IQService;
+
+ /**
+ * Remote API for SSH.
+ */
+ private remoteSshAPI: ng.resource.IResourceClass>;
+ remoteSshAPI: { getKeyPair: Function; removeKey : Function, generateKey: Function};
+
+ private sshKeyPairs : Map;
+
+ /**
+ * Default constructor that is using resource
+ * @ngInject for Dependency injection
+ */
+ constructor($resource : ng.resource.IResourceService, $q : ng.IQService) {
+
+ // keep resource
+ this.$resource = $resource;
+ this.$q = $q;
+
+
+ this.sshKeyPairs = new Map();
+
+ // remote call
+ this.remoteSshAPI = this.$resource('/api/ssh', {}, {
+ getKeyPair: { method: 'GET', url: '/api/ssh/:serviceId/:nameId'},
+ removeKey: { method: 'DELETE', url: '/api/ssh/:serviceId/:nameId'},
+ generateKey: { method: 'POST', url: '/api/ssh/generate'},
+ });
+ }
+
+ /**
+ * Fetch the keyPair
+ */
+ fetchKey(serviceId: string, nameId: string) {
+ var defer = this.$q.defer();
+ let promise = this.remoteSshAPI.getKeyPair({serviceId: serviceId, nameId: nameId}).$promise;
+
+ promise.then((sshKeyPair) => {
+ this.sshKeyPairs.set(serviceId + '/' + nameId, sshKeyPair);
+ defer.resolve();
+ }, (error) => {
+ if (error.status != 304) {
+ this.sshKeyPairs.delete(serviceId + '/' + nameId);
+ defer.reject(error);
+ } else {
+ defer.resolve();
+ }
+ });
+
+ return defer.promise;
+ }
+
+ /**
+ * Get ssh keypair
+ * @returns
+ */
+ getKey(serviceId: string, nameId: string) {
+ return this.sshKeyPairs.get(serviceId + '/' + nameId);
+ }
+
+ removeKey(serviceId: string, nameId: string) {
+ return this.remoteSshAPI.removeKey({serviceId: serviceId, nameId: nameId}).$promise;
+ }
+
+ generateKey(serviceId: string, nameId: string) {
+ return this.remoteSshAPI.generateKey({}, {service: serviceId, name: nameId}).$promise;
+ }
+
+}