GUACAMOLE-220: Merge extract common base for management UI.

This commit is contained in:
Nick Couchman
2018-05-04 04:25:59 -04:00
20 changed files with 2064 additions and 1681 deletions

View File

@@ -24,23 +24,24 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
function manageConnectionController($scope, $injector) {
// Required types
var Connection = $injector.get('Connection');
var ConnectionGroup = $injector.get('ConnectionGroup');
var HistoryEntryWrapper = $injector.get('HistoryEntryWrapper');
var PermissionSet = $injector.get('PermissionSet');
var Connection = $injector.get('Connection');
var ConnectionGroup = $injector.get('ConnectionGroup');
var HistoryEntryWrapper = $injector.get('HistoryEntryWrapper');
var ManagementPermissions = $injector.get('ManagementPermissions');
var PermissionSet = $injector.get('PermissionSet');
var Protocol = $injector.get('Protocol');
// Required services
var $location = $injector.get('$location');
var $q = $injector.get('$q');
var $routeParams = $injector.get('$routeParams');
var $translate = $injector.get('$translate');
var authenticationService = $injector.get('authenticationService');
var guacNotification = $injector.get('guacNotification');
var connectionService = $injector.get('connectionService');
var connectionGroupService = $injector.get('connectionGroupService');
var permissionService = $injector.get('permissionService');
var requestService = $injector.get('requestService');
var schemaService = $injector.get('schemaService');
var translationStringService = $injector.get('translationStringService');
/**
* The unique identifier of the data source containing the connection being
@@ -108,36 +109,15 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
* @type HistoryEntryWrapper[]
*/
$scope.historyEntryWrappers = null;
/**
* Whether the user can save the connection being edited. This could be
* updating an existing connection, or creating a new connection.
*
* @type Boolean
*/
$scope.canSaveConnection = null;
/**
* Whether the user can delete the connection being edited.
*
* @type Boolean
*/
$scope.canDeleteConnection = null;
/**
* Whether the user can clone the connection being edited.
*
* @type Boolean
*/
$scope.canCloneConnection = null;
/**
* All permissions associated with the current user, or null if the user's
* permissions have not yet been loaded.
* The management-related actions that the current user may perform on the
* connection currently being created/modified, or null if the current
* user's permissions have not yet been loaded.
*
* @type PermissionSet
* @type ManagementPermissions
*/
$scope.permissions = null;
$scope.managementPermissions = null;
/**
* All available connection attributes. This is only the set of attribute
@@ -157,205 +137,178 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
*/
$scope.isLoaded = function isLoaded() {
return $scope.protocols !== null
&& $scope.rootGroup !== null
&& $scope.connection !== null
&& $scope.parameters !== null
&& $scope.historyDateFormat !== null
&& $scope.historyEntryWrappers !== null
&& $scope.permissions !== null
&& $scope.attributes !== null
&& $scope.canSaveConnection !== null
&& $scope.canDeleteConnection !== null
&& $scope.canCloneConnection !== null;
return $scope.protocols !== null
&& $scope.rootGroup !== null
&& $scope.connection !== null
&& $scope.parameters !== null
&& $scope.historyDateFormat !== null
&& $scope.historyEntryWrappers !== null
&& $scope.managementPermissions !== null
&& $scope.attributes !== null;
};
// Pull connection attribute schema
schemaService.getConnectionAttributes($scope.selectedDataSource)
.then(function attributesReceived(attributes) {
$scope.attributes = attributes;
}, requestService.WARN);
/**
* Loads the data associated with the connection having the given
* identifier, preparing the interface for making modifications to that
* existing connection.
*
* @param {String} dataSource
* The unique identifier of the data source containing the connection to
* load.
*
* @param {String} identifier
* The identifier of the connection to load.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared for
* editing the given connection.
*/
var loadExistingConnection = function loadExistingConnection(dataSource, identifier) {
return $q.all({
connection : connectionService.getConnection(dataSource, identifier),
historyEntries : connectionService.getConnectionHistory(dataSource, identifier),
parameters : connectionService.getConnectionParameters(dataSource, identifier)
})
.then(function connectionDataRetrieved(values) {
// Pull connection group hierarchy
connectionGroupService.getConnectionGroupTree(
$scope.selectedDataSource,
ConnectionGroup.ROOT_IDENTIFIER,
[PermissionSet.ObjectPermissionType.ADMINISTER]
)
.then(function connectionGroupReceived(rootGroup) {
$scope.rootGroup = rootGroup;
}, requestService.WARN);
// Query the user's permissions for the current connection
permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
.then(function permissionsReceived(permissions) {
$scope.permissions = permissions;
// Check if the connection is new or if the user has UPDATE permission
$scope.canSaveConnection =
!identifier
|| PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
// Check if connection is not new and the user has DELETE permission
$scope.canDeleteConnection =
!!identifier && (
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
);
// Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions
$scope.canCloneConnection =
!!identifier && (
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || (
PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)
&& PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION)
)
);
}, requestService.WARN);
// Get protocol metadata
schemaService.getProtocols($scope.selectedDataSource)
.then(function protocolsReceived(protocols) {
$scope.protocols = protocols;
}, requestService.WARN);
// Get history date format
$translate('MANAGE_CONNECTION.FORMAT_HISTORY_START').then(function historyDateFormatReceived(historyDateFormat) {
$scope.historyDateFormat = historyDateFormat;
}, angular.noop);
// If we are editing an existing connection, pull its data
if (identifier) {
// Pull data from existing connection
connectionService.getConnection($scope.selectedDataSource, identifier)
.then(function connectionRetrieved(connection) {
$scope.connection = connection;
}, requestService.WARN);
// Pull connection history
connectionService.getConnectionHistory($scope.selectedDataSource, identifier)
.then(function historyReceived(historyEntries) {
$scope.connection = values.connection;
$scope.parameters = values.parameters;
// Wrap all history entries for sake of display
$scope.historyEntryWrappers = [];
historyEntries.forEach(function wrapHistoryEntry(historyEntry) {
$scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry));
angular.forEach(values.historyEntries, function wrapHistoryEntry(historyEntry) {
$scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry));
});
}, requestService.WARN);
});
};
// Pull connection parameters
connectionService.getConnectionParameters($scope.selectedDataSource, identifier)
.then(function parametersReceived(parameters) {
$scope.parameters = parameters;
}, requestService.WARN);
}
// If we are cloning an existing connection, pull its data instead
else if (cloneSourceIdentifier) {
/**
* Loads the data associated with the connection having the given
* identifier, preparing the interface for cloning that existing
* connection.
*
* @param {String} dataSource
* The unique identifier of the data source containing the connection
* to be cloned.
*
* @param {String} identifier
* The identifier of the connection being cloned.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared for
* cloning the given connection.
*/
var loadClonedConnection = function loadClonedConnection(dataSource, identifier) {
return $q.all({
connection : connectionService.getConnection(dataSource, identifier),
parameters : connectionService.getConnectionParameters(dataSource, identifier)
})
.then(function connectionDataRetrieved(values) {
$scope.connection = values.connection;
$scope.parameters = values.parameters;
// Pull data from cloned connection
connectionService.getConnection($scope.selectedDataSource, cloneSourceIdentifier)
.then(function connectionRetrieved(connection) {
$scope.connection = connection;
// Clear the identifier field because this connection is new
delete $scope.connection.identifier;
}, requestService.WARN);
// Do not pull connection history
$scope.historyEntryWrappers = [];
// Pull connection parameters from cloned connection
connectionService.getConnectionParameters($scope.selectedDataSource, cloneSourceIdentifier)
.then(function parametersReceived(parameters) {
$scope.parameters = parameters;
}, requestService.WARN);
}
// Cloned connections have no history
$scope.historyEntryWrappers = [];
// If we are creating a new connection, populate skeleton connection data
else {
});
};
/**
* Loads skeleton connection data, preparing the interface for creating a
* new connection.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared for
* creating a new connection.
*/
var loadSkeletonConnection = function loadSkeletonConnection() {
// Use skeleton connection object with no associated permissions,
// history, or parameters
$scope.connection = new Connection({
protocol : 'vnc',
parentIdentifier : $location.search().parent
});
$scope.historyEntryWrappers = [];
$scope.parameters = {};
}
/**
* Returns whether the current user can change/set all connection
* attributes for the connection being edited, regardless of whether those
* attributes are already explicitly associated with that connection.
*
* @returns {Boolean}
* true if the current user can change all attributes for the
* connection being edited, regardless of whether those attributes are
* already explicitly associated with that connection, false otherwise.
*/
$scope.canChangeAllAttributes = function canChangeAllAttributes() {
// All attributes can be set if we are creating the connection
return !identifier;
return $q.resolve();
};
/**
* Returns the translation string namespace for the protocol having the
* given name. The namespace will be of the form:
* Loads the data required for performing the management task requested
* through the route parameters given at load time, automatically preparing
* the interface for editing an existing connection, cloning an existing
* connection, or creating an entirely new connection.
*
* <code>PROTOCOL_NAME</code>
*
* where <code>NAME</code> is the protocol name transformed via
* translationStringService.canonicalize().
*
* @param {String} protocolName
* The name of the protocol.
*
* @returns {String}
* The translation namespace for the protocol specified, or null if no
* namespace could be generated.
* @returns {Promise}
* A promise which is resolved when the interface has been prepared
* for performing the requested management task.
*/
$scope.getNamespace = function getNamespace(protocolName) {
var loadRequestedConnection = function loadRequestedConnection() {
// Do not generate a namespace if no protocol is selected
if (!protocolName)
return null;
// If we are editing an existing connection, pull its data
if (identifier)
return loadExistingConnection($scope.selectedDataSource, identifier);
return 'PROTOCOL_' + translationStringService.canonicalize(protocolName);
// If we are cloning an existing connection, pull its data instead
if (cloneSourceIdentifier)
return loadClonedConnection($scope.selectedDataSource, cloneSourceIdentifier);
// If we are creating a new connection, populate skeleton connection data
return loadSkeletonConnection();
};
/**
* Given the internal name of a protocol, produces the translation string
* for the localized version of that protocol's name. The translation
* string will be of the form:
*
* <code>NAMESPACE.NAME<code>
*
* where <code>NAMESPACE</code> is the namespace generated from
* $scope.getNamespace().
*
* @param {String} protocolName
* The name of the protocol.
*
* @returns {String}
* The translation string which produces the localized name of the
* protocol specified.
*/
$scope.getProtocolName = function getProtocolName(protocolName) {
return $scope.getNamespace(protocolName) + '.NAME';
};
// Populate interface with requested data
$q.all({
connectionData : loadRequestedConnection(),
attributes : schemaService.getConnectionAttributes($scope.selectedDataSource),
permissions : permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()),
protocols : schemaService.getProtocols($scope.selectedDataSource),
rootGroup : connectionGroupService.getConnectionGroupTree($scope.selectedDataSource, ConnectionGroup.ROOT_IDENTIFIER, [PermissionSet.ObjectPermissionType.ADMINISTER])
})
.then(function dataRetrieved(values) {
$scope.attributes = values.attributes;
$scope.protocols = values.protocols;
$scope.rootGroup = values.rootGroup;
$scope.managementPermissions = ManagementPermissions.fromPermissionSet(
values.permissions,
PermissionSet.SystemPermissionType.CREATE_CONNECTION,
PermissionSet.hasConnectionPermission,
identifier);
}, requestService.WARN);
// Get history date format
$translate('MANAGE_CONNECTION.FORMAT_HISTORY_START').then(function historyDateFormatReceived(historyDateFormat) {
$scope.historyDateFormat = historyDateFormat;
}, angular.noop);
/**
* Cancels all pending edits, returning to the management page.
* @borrows Protocol.getNamespace
*/
$scope.cancel = function cancel() {
$scope.getNamespace = Protocol.getNamespace;
/**
* @borrows Protocol.getName
*/
$scope.getProtocolName = Protocol.getName;
/**
* Cancels all pending edits, returning to the main list of connections
* within the selected data source.
*/
$scope.returnToConnectionList = function returnToConnectionList() {
$location.url('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
};
@@ -368,76 +321,33 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
};
/**
* Saves the connection, creating a new connection or updating the existing
* connection.
* Saves the current connection, creating a new connection or updating the
* existing connection, returning a promise which is resolved if the save
* operation succeeds and rejected if the save operation fails.
*
* @returns {Promise}
* A promise which is resolved if the save operation succeeds and is
* rejected with an {@link Error} if the save operation fails.
*/
$scope.saveConnection = function saveConnection() {
$scope.connection.parameters = $scope.parameters;
// Save the connection
connectionService.saveConnection($scope.selectedDataSource, $scope.connection)
.then(function savedConnection() {
$location.url('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
}, guacNotification.SHOW_REQUEST_ERROR);
};
/**
* An action to be provided along with the object sent to showStatus which
* immediately deletes the current connection.
*/
var DELETE_ACTION = {
name : "MANAGE_CONNECTION.ACTION_DELETE",
className : "danger",
// Handle action
callback : function deleteCallback() {
deleteConnectionImmediately();
guacNotification.showStatus(false);
}
};
/**
* An action to be provided along with the object sent to showStatus which
* closes the currently-shown status dialog.
*/
var CANCEL_ACTION = {
name : "MANAGE_CONNECTION.ACTION_CANCEL",
// Handle action
callback : function cancelCallback() {
guacNotification.showStatus(false);
}
};
/**
* Immediately deletes the current connection, without prompting the user
* for confirmation.
*/
var deleteConnectionImmediately = function deleteConnectionImmediately() {
// Delete the connection
connectionService.deleteConnection($scope.selectedDataSource, $scope.connection)
.then(function deletedConnection() {
$location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
}, guacNotification.SHOW_REQUEST_ERROR);
return connectionService.saveConnection($scope.selectedDataSource, $scope.connection);
};
/**
* Deletes the connection, prompting the user first to confirm that
* deletion is desired.
* Deletes the current connection, returning a promise which is resolved if
* the delete operation succeeds and rejected if the delete operation fails.
*
* @returns {Promise}
* A promise which is resolved if the delete operation succeeds and is
* rejected with an {@link Error} if the delete operation fails.
*/
$scope.deleteConnection = function deleteConnection() {
// Confirm deletion request
guacNotification.showStatus({
'title' : 'MANAGE_CONNECTION.DIALOG_HEADER_CONFIRM_DELETE',
'text' : {
key : 'MANAGE_CONNECTION.TEXT_CONFIRM_DELETE'
},
'actions' : [ DELETE_ACTION, CANCEL_ACTION]
});
return connectionService.deleteConnection($scope.selectedDataSource, $scope.connection);
};
}]);

View File

@@ -24,15 +24,16 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
function manageConnectionGroupController($scope, $injector) {
// Required types
var ConnectionGroup = $injector.get('ConnectionGroup');
var PermissionSet = $injector.get('PermissionSet');
var ConnectionGroup = $injector.get('ConnectionGroup');
var ManagementPermissions = $injector.get('ManagementPermissions');
var PermissionSet = $injector.get('PermissionSet');
// Required services
var $location = $injector.get('$location');
var $q = $injector.get('$q');
var $routeParams = $injector.get('$routeParams');
var authenticationService = $injector.get('authenticationService');
var connectionGroupService = $injector.get('connectionGroupService');
var guacNotification = $injector.get('guacNotification');
var permissionService = $injector.get('permissionService');
var requestService = $injector.get('requestService');
var schemaService = $injector.get('schemaService');
@@ -45,6 +46,15 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
*/
$scope.selectedDataSource = $routeParams.dataSource;
/**
* The identifier of the original connection group from which this
* connection group is being cloned. Only valid if this is a new
* connection group.
*
* @type String
*/
var cloneSourceIdentifier = $location.search().clone;
/**
* The identifier of the connection group being edited. If a new connection
* group is being created, this will not be defined.
@@ -53,6 +63,23 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
*/
var identifier = $routeParams.id;
/**
* Available connection group types, as translation string / internal value
* pairs.
*
* @type Object[]
*/
$scope.types = [
{
label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_ORGANIZATIONAL",
value: ConnectionGroup.Type.ORGANIZATIONAL
},
{
label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_BALANCING",
value : ConnectionGroup.Type.BALANCING
}
];
/**
* The root connection group of the connection group hierarchy.
*
@@ -68,26 +95,13 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
$scope.connectionGroup = null;
/**
* Whether the user has UPDATE permission for the current connection group.
*
* @type Boolean
*/
$scope.hasUpdatePermission = null;
/**
* Whether the user has DELETE permission for the current connection group.
*
* @type Boolean
*/
$scope.hasDeletePermission = null;
/**
* All permissions associated with the current user, or null if the user's
* permissions have not yet been loaded.
* The management-related actions that the current user may perform on the
* connection group currently being created/modified, or null if the current
* user's permissions have not yet been loaded.
*
* @type PermissionSet
* @type ManagementPermissions
*/
$scope.permissions = null;
$scope.managementPermissions = null;
/**
* All available connection group attributes. This is only the set of
@@ -109,177 +123,172 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
return $scope.rootGroup !== null
&& $scope.connectionGroup !== null
&& $scope.permissions !== null
&& $scope.attributes !== null
&& $scope.canSaveConnectionGroup !== null
&& $scope.canDeleteConnectionGroup !== null;
&& $scope.managementPermissions !== null
&& $scope.attributes !== null;
};
// Pull connection group attribute schema
schemaService.getConnectionGroupAttributes($scope.selectedDataSource)
.then(function attributesReceived(attributes) {
$scope.attributes = attributes;
}, requestService.WARN);
// Query the user's permissions for the current connection group
permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
.then(function permissionsReceived(permissions) {
$scope.permissions = permissions;
// Check if the connection group is new or if the user has UPDATE permission
$scope.canSaveConnectionGroup =
!identifier
|| PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
// Check if connection group is not new and the user has DELETE permission
$scope.canDeleteConnectionGroup =
!!identifier && (
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
);
}, requestService.WARN);
// Pull connection group hierarchy
connectionGroupService.getConnectionGroupTree(
$scope.selectedDataSource,
ConnectionGroup.ROOT_IDENTIFIER,
[PermissionSet.ObjectPermissionType.ADMINISTER]
)
.then(function connectionGroupReceived(rootGroup) {
$scope.rootGroup = rootGroup;
}, requestService.WARN);
// If we are editing an existing connection group, pull its data
if (identifier) {
connectionGroupService.getConnectionGroup($scope.selectedDataSource, identifier)
/**
* Loads the data associated with the connection group having the given
* identifier, preparing the interface for making modifications to that
* existing connection group.
*
* @param {String} dataSource
* The unique identifier of the data source containing the connection
* group to load.
*
* @param {String} identifier
* The identifier of the connection group to load.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared for
* editing the given connection group.
*/
var loadExistingConnectionGroup = function loadExistingConnectionGroup(dataSource, identifier) {
return connectionGroupService.getConnectionGroup(
dataSource,
identifier
)
.then(function connectionGroupReceived(connectionGroup) {
$scope.connectionGroup = connectionGroup;
}, requestService.WARN);
}
});
};
// If we are creating a new connection group, populate skeleton connection group data
else
/**
* Loads the data associated with the connection group having the given
* identifier, preparing the interface for cloning that existing
* connection group.
*
* @param {String} dataSource
* The unique identifier of the data source containing the connection
* group to be cloned.
*
* @param {String} identifier
* The identifier of the connection group being cloned.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared for
* cloning the given connection group.
*/
var loadClonedConnectionGroup = function loadClonedConnectionGroup(dataSource, identifier) {
return connectionGroupService.getConnectionGroup(
dataSource,
identifier
)
.then(function connectionGroupReceived(connectionGroup) {
$scope.connectionGroup = connectionGroup;
delete $scope.connectionGroup.identifier;
});
};
/**
* Loads skeleton connection group data, preparing the interface for
* creating a new connection group.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared for
* creating a new connection group.
*/
var loadSkeletonConnectionGroup = function loadSkeletonConnectionGroup() {
// Use skeleton connection group object with specified parent
$scope.connectionGroup = new ConnectionGroup({
parentIdentifier : $location.search().parent
});
/**
* Available connection group types, as translation string / internal value
* pairs.
*
* @type Object[]
*/
$scope.types = [
{
label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_ORGANIZATIONAL",
value: ConnectionGroup.Type.ORGANIZATIONAL
},
{
label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_BALANCING",
value : ConnectionGroup.Type.BALANCING
}
];
/**
* Returns whether the current user can change/set all connection group
* attributes for the connection group being edited, regardless of whether
* those attributes are already explicitly associated with that connection
* group.
*
* @returns {Boolean}
* true if the current user can change all attributes for the
* connection group being edited, regardless of whether those
* attributes are already explicitly associated with that connection
* group, false otherwise.
*/
$scope.canChangeAllAttributes = function canChangeAllAttributes() {
// All attributes can be set if we are creating the connection group
return !identifier;
return $q.resolve();
};
/**
* Cancels all pending edits, returning to the management page.
* Loads the data required for performing the management task requested
* through the route parameters given at load time, automatically preparing
* the interface for editing an existing connection group, cloning an
* existing connection group, or creating an entirely new connection group.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared
* for performing the requested management task.
*/
$scope.cancel = function cancel() {
var loadRequestedConnectionGroup = function loadRequestedConnectionGroup() {
// If we are editing an existing connection group, pull its data
if (identifier)
return loadExistingConnectionGroup($scope.selectedDataSource, identifier);
// If we are cloning an existing connection group, pull its data
// instead
if (cloneSourceIdentifier)
return loadClonedConnectionGroup($scope.selectedDataSource, cloneSourceIdentifier);
// If we are creating a new connection group, populate skeleton
// connection group data
return loadSkeletonConnectionGroup();
};
// Query the user's permissions for the current connection group
$q.all({
connectionGroupData : loadRequestedConnectionGroup(),
attributes : schemaService.getConnectionGroupAttributes($scope.selectedDataSource),
permissions : permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()),
rootGroup : connectionGroupService.getConnectionGroupTree($scope.selectedDataSource, ConnectionGroup.ROOT_IDENTIFIER, [PermissionSet.ObjectPermissionType.ADMINISTER])
})
.then(function connectionGroupDataRetrieved(values) {
$scope.attributes = values.attributes;
$scope.rootGroup = values.rootGroup;
$scope.managementPermissions = ManagementPermissions.fromPermissionSet(
values.permissions,
PermissionSet.SystemPermissionType.CREATE_CONNECTION,
PermissionSet.hasConnectionPermission,
identifier);
}, requestService.WARN);
/**
* Cancels all pending edits, returning to the main list of connections
* within the selected data source.
*/
$scope.returnToConnectionList = function returnToConnectionList() {
$location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
};
/**
* Saves the connection group, creating a new connection group or updating
* the existing connection group.
* Cancels all pending edits, opening an edit page for a new connection
* group which is prepopulated with the data from the connection group
* currently being edited.
*/
$scope.cloneConnectionGroup = function cloneConnectionGRoup() {
$location.path('/manage/' + encodeURIComponent($scope.selectedDataSource) + '/connectionGroups').search('clone', identifier);
};
/**
* Saves the current connection group, creating a new connection group or
* updating the existing connection group, returning a promise which is
* resolved if the save operation succeeds and rejected if the save
* operation fails.
*
* @returns {Promise}
* A promise which is resolved if the save operation succeeds and is
* rejected with an {@link Error} if the save operation fails.
*/
$scope.saveConnectionGroup = function saveConnectionGroup() {
// Save the connection
connectionGroupService.saveConnectionGroup($scope.selectedDataSource, $scope.connectionGroup)
.then(function savedConnectionGroup() {
$location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
}, guacNotification.SHOW_REQUEST_ERROR);
return connectionGroupService.saveConnectionGroup($scope.selectedDataSource, $scope.connectionGroup);
};
/**
* An action to be provided along with the object sent to showStatus which
* immediately deletes the current connection group.
*/
var DELETE_ACTION = {
name : "MANAGE_CONNECTION_GROUP.ACTION_DELETE",
className : "danger",
// Handle action
callback : function deleteCallback() {
deleteConnectionGroupImmediately();
guacNotification.showStatus(false);
}
};
/**
* An action to be provided along with the object sent to showStatus which
* closes the currently-shown status dialog.
*/
var CANCEL_ACTION = {
name : "MANAGE_CONNECTION_GROUP.ACTION_CANCEL",
// Handle action
callback : function cancelCallback() {
guacNotification.showStatus(false);
}
};
/**
* Immediately deletes the current connection group, without prompting the
* user for confirmation.
*/
var deleteConnectionGroupImmediately = function deleteConnectionGroupImmediately() {
// Delete the connection group
connectionGroupService.deleteConnectionGroup($scope.selectedDataSource, $scope.connectionGroup)
.then(function deletedConnectionGroup() {
$location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
}, guacNotification.SHOW_REQUEST_ERROR);
};
/**
* Deletes the connection group, prompting the user first to confirm that
* deletion is desired.
* Deletes the current connection group, returning a promise which is
* resolved if the delete operation succeeds and rejected if the delete
* operation fails.
*
* @returns {Promise}
* A promise which is resolved if the delete operation succeeds and is
* rejected with an {@link Error} if the delete operation fails.
*/
$scope.deleteConnectionGroup = function deleteConnectionGroup() {
// Confirm deletion request
guacNotification.showStatus({
'title' : 'MANAGE_CONNECTION_GROUP.DIALOG_HEADER_CONFIRM_DELETE',
'text' : {
key : 'MANAGE_CONNECTION_GROUP.TEXT_CONFIRM_DELETE'
},
'actions' : [ DELETE_ACTION, CANCEL_ACTION]
});
return connectionGroupService.deleteConnectionGroup($scope.selectedDataSource, $scope.connectionGroup);
};
}]);

View File

@@ -24,32 +24,21 @@ angular.module('manage').controller('manageSharingProfileController', ['$scope',
function manageSharingProfileController($scope, $injector) {
// Required types
var SharingProfile = $injector.get('SharingProfile');
var PermissionSet = $injector.get('PermissionSet');
var ManagementPermissions = $injector.get('ManagementPermissions');
var SharingProfile = $injector.get('SharingProfile');
var PermissionSet = $injector.get('PermissionSet');
var Protocol = $injector.get('Protocol');
// Required services
var $location = $injector.get('$location');
var $q = $injector.get('$q');
var $routeParams = $injector.get('$routeParams');
var authenticationService = $injector.get('authenticationService');
var connectionService = $injector.get('connectionService');
var guacNotification = $injector.get('guacNotification');
var permissionService = $injector.get('permissionService');
var requestService = $injector.get('requestService');
var schemaService = $injector.get('schemaService');
var sharingProfileService = $injector.get('sharingProfileService');
var translationStringService = $injector.get('translationStringService');
/**
* An action to be provided along with the object sent to showStatus which
* closes the currently-shown status dialog, effectively canceling the
* operation which was pending user confirmation.
*/
var CANCEL_ACTION = {
name : "MANAGE_SHARING_PROFILE.ACTION_CANCEL",
callback : function cancelCallback() {
guacNotification.showStatus(false);
}
};
/**
* The unique identifier of the data source containing the sharing profile
@@ -98,34 +87,13 @@ angular.module('manage').controller('manageSharingProfileController', ['$scope',
$scope.parameters = null;
/**
* Whether the user can save the sharing profile being edited. This could be
* updating an existing sharing profile, or creating a new sharing profile.
* The management-related actions that the current user may perform on the
* sharing profile currently being created/modified, or null if the current
* user's permissions have not yet been loaded.
*
* @type Boolean
* @type ManagementPermissions
*/
$scope.canSaveSharingProfile = null;
/**
* Whether the user can delete the sharing profile being edited.
*
* @type Boolean
*/
$scope.canDeleteSharingProfile = null;
/**
* Whether the user can clone the sharing profile being edited.
*
* @type Boolean
*/
$scope.canCloneSharingProfile = null;
/**
* All permissions associated with the current user, or null if the user's
* permissions have not yet been loaded.
*
* @type PermissionSet
*/
$scope.permissions = null;
$scope.managementPermissions = null;
/**
* All available sharing profile attributes. This is only the set of
@@ -149,176 +117,178 @@ angular.module('manage').controller('manageSharingProfileController', ['$scope',
&& $scope.sharingProfile !== null
&& $scope.primaryConnection !== null
&& $scope.parameters !== null
&& $scope.permissions !== null
&& $scope.attributes !== null
&& $scope.canSaveSharingProfile !== null
&& $scope.canDeleteSharingProfile !== null
&& $scope.canCloneSharingProfile !== null;
&& $scope.managementPermissions !== null
&& $scope.attributes !== null;
};
// Pull sharing profile attribute schema
schemaService.getSharingProfileAttributes($scope.selectedDataSource)
.then(function attributesReceived(attributes) {
$scope.attributes = attributes;
}, requestService.WARN);
/**
* Loads the data associated with the sharing profile having the given
* identifier, preparing the interface for making modifications to that
* existing sharing profile.
*
* @param {String} dataSource
* The unique identifier of the data source containing the sharing
* profile to load.
*
* @param {String} identifier
* The identifier of the sharing profile to load.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared for
* editing the given sharing profile.
*/
var loadExistingSharingProfile = function loadExistingSharingProfile(dataSource, identifier) {
return $q.all({
sharingProfile : sharingProfileService.getSharingProfile(dataSource, identifier),
parameters : sharingProfileService.getSharingProfileParameters(dataSource, identifier)
})
.then(function sharingProfileDataRetrieved(values) {
// Query the user's permissions for the current sharing profile
permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
.then(function permissionsReceived(permissions) {
$scope.sharingProfile = values.sharingProfile;
$scope.parameters = values.parameters;
$scope.permissions = permissions;
// Load connection object for associated primary connection
return connectionService.getConnection(
dataSource,
values.sharingProfile.primaryConnectionIdentifier
)
.then(function connectionRetrieved(connection) {
$scope.primaryConnection = connection;
});
// The sharing profile can be saved if it is new or if the user has
// UPDATE permission for that sharing profile (either explicitly or by
// virtue of being an administrator)
$scope.canSaveSharingProfile =
!identifier
|| PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasSharingProfilePermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
});
};
// The sharing profile can be saved only if it exists and the user has
// DELETE permission (either explicitly or by virtue of being an
// administrator)
$scope.canDeleteSharingProfile =
!!identifier && (
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasSharingProfilePermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
);
/**
* Loads the data associated with the sharing profile having the given
* identifier, preparing the interface for cloning that existing
* sharing profile.
*
* @param {String} dataSource
* The unique identifier of the data source containing the sharing
* profile to be cloned.
*
* @param {String} identifier
* The identifier of the sharing profile being cloned.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared for
* cloning the given sharing profile.
*/
var loadClonedSharingProfile = function loadClonedSharingProfile(dataSource, identifier) {
return $q.all({
sharingProfile : sharingProfileService.getSharingProfile(dataSource, identifier),
parameters : sharingProfileService.getSharingProfileParameters(dataSource, identifier)
})
.then(function sharingProfileDataRetrieved(values) {
// The sharing profile can be cloned only if it exists, the user has
// UPDATE permission on the sharing profile being cloned (is able to
// read parameters), and the user can create new sharing profiles
$scope.canCloneSharingProfile =
!!identifier && (
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || (
PermissionSet.hasSharingProfilePermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)
&& PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_SHARING_PROFILE)
)
);
}, requestService.WARN);
// Get protocol metadata
schemaService.getProtocols($scope.selectedDataSource)
.then(function protocolsReceived(protocols) {
$scope.protocols = protocols;
}, requestService.WARN);
// If we are editing an existing sharing profile, pull its data
if (identifier) {
// Pull data from existing sharing profile
sharingProfileService.getSharingProfile($scope.selectedDataSource, identifier)
.then(function sharingProfileRetrieved(sharingProfile) {
$scope.sharingProfile = sharingProfile;
}, requestService.WARN);
// Pull sharing profile parameters
sharingProfileService.getSharingProfileParameters($scope.selectedDataSource, identifier)
.then(function parametersReceived(parameters) {
$scope.parameters = parameters;
}, requestService.WARN);
}
// If we are cloning an existing sharing profile, pull its data instead
else if (cloneSourceIdentifier) {
// Pull data from cloned sharing profile
sharingProfileService.getSharingProfile($scope.selectedDataSource, cloneSourceIdentifier)
.then(function sharingProfileRetrieved(sharingProfile) {
// Store data of sharing profile being cloned
$scope.sharingProfile = sharingProfile;
$scope.sharingProfile = values.sharingProfile;
$scope.parameters = values.parameters;
// Clear the identifier field because this sharing profile is new
delete $scope.sharingProfile.identifier;
}, requestService.WARN);
// Load connection object for associated primary connection
return connectionService.getConnection(
dataSource,
values.sharingProfile.primaryConnectionIdentifier
)
.then(function connectionRetrieved(connection) {
$scope.primaryConnection = connection;
});
// Pull sharing profile parameters from cloned sharing profile
sharingProfileService.getSharingProfileParameters($scope.selectedDataSource, cloneSourceIdentifier)
.then(function parametersReceived(parameters) {
$scope.parameters = parameters;
}, requestService.WARN);
});
};
}
// If we are creating a new sharing profile, populate skeleton sharing
// profile data
else {
/**
* Loads skeleton sharing profile data, preparing the interface for
* creating a new sharing profile.
*
* @param {String} dataSource
* The unique identifier of the data source containing the sharing
* profile to be created.
*
* @returns {Promise}
* A promise which is resolved when the interface has been prepared for
* creating a new sharing profile.
*/
var loadSkeletonSharingProfile = function loadSkeletonSharingProfile(dataSource) {
// Use skeleton sharing profile object with no associated parameters
$scope.sharingProfile = new SharingProfile({
primaryConnectionIdentifier : $location.search().parent
});
$scope.parameters = {};
}
// Populate primary connection once its identifier is known
$scope.$watch('sharingProfile.primaryConnectionIdentifier',
function retrievePrimaryConnection(identifier) {
if (identifier) {
connectionService.getConnection($scope.selectedDataSource, identifier)
.then(function connectionRetrieved(connection) {
$scope.primaryConnection = connection;
}, requestService.WARN);
}
});
/**
* Returns whether the current user can change/set all sharing profile
* attributes for the sharing profile being edited, regardless of whether
* those attributes are already explicitly associated with that sharing
* profile.
*
* @returns {Boolean}
* true if the current user can change all attributes for the sharing
* profile being edited, regardless of whether those attributes are
* already explicitly associated with that sharing profile, false
* otherwise.
*/
$scope.canChangeAllAttributes = function canChangeAllAttributes() {
// All attributes can be set if we are creating the sharing profile
return !identifier;
// Load connection object for associated primary connection
return connectionService.getConnection(
dataSource,
$scope.sharingProfile.primaryConnectionIdentifier
)
.then(function connectionRetrieved(connection) {
$scope.primaryConnection = connection;
});
};
/**
* Returns the translation string namespace for the protocol having the
* given name. The namespace will be of the form:
* Loads the data required for performing the management task requested
* through the route parameters given at load time, automatically preparing
* the interface for editing an existing sharing profile, cloning an
* existing sharing profile, or creating an entirely new sharing profile.
*
* <code>PROTOCOL_NAME</code>
*
* where <code>NAME</code> is the protocol name transformed via
* translationStringService.canonicalize().
*
* @param {String} protocolName
* The name of the protocol.
*
* @returns {String}
* The translation namespace for the protocol specified, or null if no
* namespace could be generated.
* @returns {Promise}
* A promise which is resolved when the interface has been prepared
* for performing the requested management task.
*/
$scope.getNamespace = function getNamespace(protocolName) {
var loadRequestedSharingProfile = function loadRequestedSharingProfile() {
// Do not generate a namespace if no protocol is selected
if (!protocolName)
return null;
// If we are editing an existing sharing profile, pull its data
if (identifier)
return loadExistingSharingProfile($scope.selectedDataSource, identifier);
return 'PROTOCOL_' + translationStringService.canonicalize(protocolName);
// If we are cloning an existing sharing profile, pull its data instead
if (cloneSourceIdentifier)
return loadClonedSharingProfile($scope.selectedDataSource, cloneSourceIdentifier);
// If we are creating a new sharing profile, populate skeleton sharing
// profile data
return loadSkeletonSharingProfile($scope.selectedDataSource);
};
// Query the user's permissions for the current sharing profile
$q.all({
sharingProfileData : loadRequestedSharingProfile(),
attributes : schemaService.getSharingProfileAttributes($scope.selectedDataSource),
protocols : schemaService.getProtocols($scope.selectedDataSource),
permissions : permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
})
.then(function sharingProfileDataRetrieved(values) {
$scope.attributes = values.attributes;
$scope.protocols = values.protocols;
$scope.managementPermissions = ManagementPermissions.fromPermissionSet(
values.permissions,
PermissionSet.SystemPermissionType.CREATE_CONNECTION,
PermissionSet.hasConnectionPermission,
identifier);
}, requestService.WARN);
/**
* Cancels all pending edits, returning to the management page.
* @borrows Protocol.getNamespace
*/
$scope.cancel = function cancel() {
$scope.getNamespace = Protocol.getNamespace;
/**
* Cancels all pending edits, returning to the main list of connections
* within the selected data source.
*/
$scope.returnToConnectionList = function returnToConnectionList() {
$location.url('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
};
@@ -332,64 +302,35 @@ angular.module('manage').controller('manageSharingProfileController', ['$scope',
};
/**
* Saves the sharing profile, creating a new sharing profile or updating
* the existing sharing profile.
* Saves the current sharing profile, creating a new sharing profile or
* updating the existing sharing profile, returning a promise which is
* resolved if the save operation succeeds and rejected if the save
* operation fails.
*
* @returns {Promise}
* A promise which is resolved if the save operation succeeds and is
* rejected with an {@link Error} if the save operation fails.
*/
$scope.saveSharingProfile = function saveSharingProfile() {
$scope.sharingProfile.parameters = $scope.parameters;
// Save the sharing profile
sharingProfileService.saveSharingProfile($scope.selectedDataSource, $scope.sharingProfile)
.then(function savedSharingProfile() {
$location.url('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
}, guacNotification.SHOW_REQUEST_ERROR);
return sharingProfileService.saveSharingProfile($scope.selectedDataSource, $scope.sharingProfile);
};
/**
* An action to be provided along with the object sent to showStatus which
* immediately deletes the current sharing profile.
*/
var DELETE_ACTION = {
name : "MANAGE_SHARING_PROFILE.ACTION_DELETE",
className : "danger",
// Handle action
callback : function deleteCallback() {
deleteSharingProfileImmediately();
guacNotification.showStatus(false);
}
};
/**
* Immediately deletes the current sharing profile, without prompting the
* user for confirmation.
*/
var deleteSharingProfileImmediately = function deleteSharingProfileImmediately() {
// Delete the sharing profile
sharingProfileService.deleteSharingProfile($scope.selectedDataSource, $scope.sharingProfile)
.then(function deletedSharingProfile() {
$location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
}, guacNotification.SHOW_REQUEST_ERROR);
};
/**
* Deletes the sharing profile, prompting the user first to confirm that
* deletion is desired.
* Deletes the current sharing profile, returning a promise which is
* resolved if the delete operation succeeds and rejected if the delete
* operation fails.
*
* @returns {Promise}
* A promise which is resolved if the delete operation succeeds and is
* rejected with an {@link Error} if the delete operation fails.
*/
$scope.deleteSharingProfile = function deleteSharingProfile() {
// Confirm deletion request
guacNotification.showStatus({
'title' : 'MANAGE_SHARING_PROFILE.DIALOG_HEADER_CONFIRM_DELETE',
'text' : {
'key' : 'MANAGE_SHARING_PROFILE.TEXT_CONFIRM_DELETE'
},
'actions' : [ DELETE_ACTION, CANCEL_ACTION]
});
return sharingProfileService.deleteSharingProfile($scope.selectedDataSource, $scope.sharingProfile);
};
}]);

View File

@@ -0,0 +1,407 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* A directive for manipulating the connection permissions granted within a
* given {@link PermissionFlagSet}, tracking the specific permissions added or
* removed within a separate pair of {@link PermissionSet} objects.
*/
angular.module('manage').directive('connectionPermissionEditor', ['$injector',
function connectionPermissionEditor($injector) {
// Required types
var ConnectionGroup = $injector.get('ConnectionGroup');
var GroupListItem = $injector.get('GroupListItem');
var PermissionSet = $injector.get('PermissionSet');
// Required services
var connectionGroupService = $injector.get('connectionGroupService');
var dataSourceService = $injector.get('dataSourceService');
var requestService = $injector.get('requestService');
var directive = {
// Element only
restrict: 'E',
replace: true,
scope: {
/**
* The unique identifier of the data source associated with the
* permissions being manipulated.
*
* @type String
*/
dataSource : '=',
/**
* The current state of the permissions being manipulated. This
* {@link PemissionFlagSet} will be modified as changes are made
* through this permission editor.
*
* @type PermissionFlagSet
*/
permissionFlags : '=',
/**
* The set of permissions that have been added, relative to the
* initial state of the permissions being manipulated.
*
* @type PermissionSet
*/
permissionsAdded : '=',
/**
* The set of permissions that have been removed, relative to the
* initial state of the permissions being manipulated.
*
* @type PermissionSet
*/
permissionsRemoved : '='
},
templateUrl: 'app/manage/templates/connectionPermissionEditor.html'
};
directive.controller = ['$scope', function connectionPermissionEditorController($scope) {
/**
* Array of all connection properties that are filterable.
*
* @type String[]
*/
$scope.filteredConnectionProperties = [
'name',
'protocol'
];
/**
* Array of all connection group properties that are filterable.
*
* @type String[]
*/
$scope.filteredConnectionGroupProperties = [
'name'
];
/**
* A map of data source identifiers to the root connection groups within
* thost data sources. As only one data source is applicable to any
* particular permission set being edited/created, this will only
* contain a single key.
*
* @type Object.<String, GroupListItem>
*/
$scope.rootGroups = null;
// Retrieve all connections for which we have ADMINISTER permission
dataSourceService.apply(
connectionGroupService.getConnectionGroupTree,
[$scope.dataSource],
ConnectionGroup.ROOT_IDENTIFIER,
[PermissionSet.ObjectPermissionType.ADMINISTER]
)
.then(function connectionGroupReceived(rootGroups) {
// Convert all received ConnectionGroup objects into GroupListItems
$scope.rootGroups = {};
angular.forEach(rootGroups, function addGroupListItem(rootGroup, dataSource) {
$scope.rootGroups[dataSource] = GroupListItem.fromConnectionGroup(dataSource, rootGroup);
});
}, requestService.WARN);
/**
* Expands all items within the tree descending from the given
* GroupListItem which have at least one descendant for which explicit
* READ permission is granted. The expanded state of all other items is
* left untouched.
*
* @param {GroupListItem} item
* The GroupListItem which should be conditionally expanded
* depending on whether READ permission is granted for any of its
* descendants.
*
* @param {PemissionFlagSet} flags
* The set of permissions which should be used to determine whether
* the given item and its descendants are expanded.
*/
var expandReadable = function expandReadable(item, flags) {
// If the current item is expandable and has defined children,
// determine whether it should be expanded
if (item.expandable && item.children) {
angular.forEach(item.children, function expandReadableChild(child) {
// Determine whether the permission set contains READ
// permission for the current child object
var readable = false;
switch (child.type) {
case GroupListItem.Type.CONNECTION:
readable = flags.connectionPermissions.READ[child.identifier];
break;
case GroupListItem.Type.CONNECTION_GROUP:
readable = flags.connectionGroupPermissions.READ[child.identifier];
break;
case GroupListItem.Type.SHARING_PROFILE:
readable = flags.sharingProfilePermissions.READ[child.identifier];
break;
}
// The parent should be expanded by default if the child is
// expanded by default OR the permission set contains READ
// permission on the child
item.expanded |= expandReadable(child, flags) || readable;
});
}
return item.expanded;
};
// Update default expanded state whenever connection groups and
// associated permissions change
$scope.$watchGroup(['rootGroups', 'permissionFlags'], function updateDefaultExpandedStates() {
if (!$scope.rootGroups || !$scope.permissionFlags)
return;
angular.forEach($scope.rootGroups, function updateExpandedStates(rootGroup) {
// Automatically expand all objects with any descendants for
// which the permission set contains READ permission
expandReadable(rootGroup, $scope.permissionFlags);
});
});
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the addition of the given connection permission.
*
* @param {String} identifier
* The identifier of the connection to add READ permission for.
*/
var addConnectionPermission = function addConnectionPermission(identifier) {
// If permission was previously removed, simply un-remove it
if (PermissionSet.hasConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
PermissionSet.removeConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
// Otherwise, explicitly add the permission
else
PermissionSet.addConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
};
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the removal of the given connection permission.
*
* @param {String} identifier
* The identifier of the connection to remove READ permission for.
*/
var removeConnectionPermission = function removeConnectionPermission(identifier) {
// If permission was previously added, simply un-add it
if (PermissionSet.hasConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier))
PermissionSet.removeConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
// Otherwise, explicitly remove the permission
else
PermissionSet.addConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
};
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the addition of the given connection group permission.
*
* @param {String} identifier
* The identifier of the connection group to add READ permission
* for.
*/
var addConnectionGroupPermission = function addConnectionGroupPermission(identifier) {
// If permission was previously removed, simply un-remove it
if (PermissionSet.hasConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
PermissionSet.removeConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
// Otherwise, explicitly add the permission
else
PermissionSet.addConnectionGroupPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
};
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the removal of the given connection group permission.
*
* @param {String} identifier
* The identifier of the connection group to remove READ permission
* for.
*/
var removeConnectionGroupPermission = function removeConnectionGroupPermission(identifier) {
// If permission was previously added, simply un-add it
if (PermissionSet.hasConnectionGroupPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier))
PermissionSet.removeConnectionGroupPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
// Otherwise, explicitly remove the permission
else
PermissionSet.addConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
};
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the addition of the given sharing profile permission.
*
* @param {String} identifier
* The identifier of the sharing profile to add READ permission for.
*/
var addSharingProfilePermission = function addSharingProfilePermission(identifier) {
// If permission was previously removed, simply un-remove it
if (PermissionSet.hasSharingProfilePermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
PermissionSet.removeSharingProfilePermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
// Otherwise, explicitly add the permission
else
PermissionSet.addSharingProfilePermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
};
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the removal of the given sharing profile permission.
*
* @param {String} identifier
* The identifier of the sharing profile to remove READ permission
* for.
*/
var removeSharingProfilePermission = function removeSharingProfilePermission(identifier) {
// If permission was previously added, simply un-add it
if (PermissionSet.hasSharingProfilePermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier))
PermissionSet.removeSharingProfilePermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
// Otherwise, explicitly remove the permission
else
PermissionSet.addSharingProfilePermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
};
// Expose permission query and modification functions to group list template
$scope.groupListContext = {
/**
* Returns the PermissionFlagSet that contains the current state of
* granted permissions.
*
* @returns {PermissionFlagSet}
* The PermissionFlagSet describing the current state of granted
* permissions for the permission set being edited.
*/
getPermissionFlags : function getPermissionFlags() {
return $scope.permissionFlags;
},
/**
* Notifies the controller that a change has been made to the given
* connection permission for the permission set being edited. This
* only applies to READ permissions.
*
* @param {String} identifier
* The identifier of the connection affected by the changed
* permission.
*/
connectionPermissionChanged : function connectionPermissionChanged(identifier) {
// Determine current permission setting
var granted = $scope.permissionFlags.connectionPermissions.READ[identifier];
// Add/remove permission depending on flag state
if (granted)
addConnectionPermission(identifier);
else
removeConnectionPermission(identifier);
},
/**
* Notifies the controller that a change has been made to the given
* connection group permission for the permission set being edited.
* This only applies to READ permissions.
*
* @param {String} identifier
* The identifier of the connection group affected by the
* changed permission.
*/
connectionGroupPermissionChanged : function connectionGroupPermissionChanged(identifier) {
// Determine current permission setting
var granted = $scope.permissionFlags.connectionGroupPermissions.READ[identifier];
// Add/remove permission depending on flag state
if (granted)
addConnectionGroupPermission(identifier);
else
removeConnectionGroupPermission(identifier);
},
/**
* Notifies the controller that a change has been made to the given
* sharing profile permission for the permission set being edited.
* This only applies to READ permissions.
*
* @param {String} identifier
* The identifier of the sharing profile affected by the changed
* permission.
*/
sharingProfilePermissionChanged : function sharingProfilePermissionChanged(identifier) {
// Determine current permission setting
var granted = $scope.permissionFlags.sharingProfilePermissions.READ[identifier];
// Add/remove permission depending on flag state
if (granted)
addSharingProfilePermission(identifier);
else
removeSharingProfilePermission(identifier);
}
};
}];
return directive;
}]);

View File

@@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Directive which displays a set of tabs pointing to the same object within
* different data sources, such as user accounts which span multiple data
* sources.
*/
angular.module('manage').directive('dataSourceTabs', ['$injector',
function dataSourceTabs($injector) {
// Required types
var PageDefinition = $injector.get('PageDefinition');
// Required services
var translationStringService = $injector.get('translationStringService');
var directive = {
restrict : 'E',
replace : true,
templateUrl : 'app/manage/templates/dataSourceTabs.html',
scope : {
/**
* The permissions which dictate the management actions available
* to the current user.
*
* @type Object.<String, ManagementPermissions>
*/
permissions : '=',
/**
* A function which returns the URL of the object within a given
* data source. The relevant data source will be made available to
* the Angular expression defining this function as the
* "dataSource" variable. No other values will be made available,
* including values from the scope.
*
* @type Function
*/
url : '&'
}
};
directive.controller = ['$scope', function dataSourceTabsController($scope) {
/**
* The set of pages which each manage the same object within different
* data sources.
*
* @type PageDefinition[]
*/
$scope.pages = null;
// Keep pages synchronized with permissions
$scope.$watch('permissions', function permissionsChanged(permissions) {
$scope.pages = [];
angular.forEach(permissions, function addDataSourcePage(managementPermissions, dataSource) {
// Determine whether data source contains this object
var exists = !!managementPermissions.identifier;
// Data source is not relevant if the associated object does not
// exist and cannot be created
var readOnly = !managementPermissions.canSaveObject;
if (!exists && readOnly)
return;
// Determine class name based on read-only / linked status
var className;
if (readOnly) className = 'read-only';
else if (exists) className = 'linked';
else className = 'unlinked';
// Add page entry
$scope.pages.push(new PageDefinition({
name : translationStringService.canonicalize('DATA_SOURCE_' + dataSource) + '.NAME',
url : $scope.url({ dataSource : dataSource }),
className : className
}));
});
});
}];
return directive;
}]);

View File

@@ -0,0 +1,201 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Directive which displays a set of object management buttons (save, delete,
* clone, etc.) representing the actions available to the current user in
* context of the object being edited/created.
*/
angular.module('manage').directive('managementButtons', ['$injector',
function managementButtons($injector) {
// Required services
var guacNotification = $injector.get('guacNotification');
var directive = {
restrict : 'E',
replace : true,
templateUrl : 'app/manage/templates/managementButtons.html',
scope : {
/**
* The translation namespace associated with all applicable
* translation strings. This directive requires at least the
* following translation strings within the given namespace:
*
* - ACTION_CANCEL
* - ACTION_CLONE
* - ACTION_DELETE
* - ACTION_SAVE
* - DIALOG_HEADER_CONFIRM_DELETE
* - TEXT_CONFIRM_DELETE
*
* @type String
*/
namespace : '@',
/**
* The permissions which dictate the management actions available
* to the current user.
*
* @type ManagementPermissions
*/
permissions : '=',
/**
* The function to invoke to save the arbitrary object being edited
* if the current user has permission to do so. The provided
* function MUST return a promise which is resolved if the save
* operation succeeds and is rejected with an {@link Error} if the
* save operation fails.
*
* @type Function
*/
save : '&',
/**
* The function to invoke when the current user chooses to clone
* the object being edited. The provided function MUST perform the
* actions necessary to produce an interface which will clone the
* object.
*
* @type Function
*/
clone : '&',
/**
* The function to invoke to delete the arbitrary object being edited
* if the current user has permission to do so. The provided
* function MUST return a promise which is resolved if the delete
* operation succeeds and is rejected with an {@link Error} if the
* delete operation fails.
*
* @type Function
*/
delete : '&',
/**
* The function to invoke when the current user chooses to cancel
* the edit in progress, or when a save/delete operation has
* succeeded. The provided function MUST perform the actions
* necessary to return the user to a reasonable starting point.
*
* @type Function
*/
return : '&'
}
};
directive.controller = ['$scope', function managementButtonsController($scope) {
/**
* An action to be provided along with the object sent to showStatus which
* immediately deletes the current connection.
*/
var DELETE_ACTION = {
name : $scope.namespace + '.ACTION_DELETE',
className : 'danger',
callback : function deleteCallback() {
deleteObjectImmediately();
guacNotification.showStatus(false);
}
};
/**
* An action to be provided along with the object sent to showStatus which
* closes the currently-shown status dialog.
*/
var CANCEL_ACTION = {
name : $scope.namespace + '.ACTION_CANCEL',
callback : function cancelCallback() {
guacNotification.showStatus(false);
}
};
/**
* Invokes the provided return function to navigate the user back to
* the page they started from.
*/
var navigateBack = function navigateBack() {
$scope['return']($scope.$parent);
};
/**
* Invokes the provided delete function, immediately deleting the
* current object without prompting the user for confirmation. If
* deletion is successful, the user is navigated back to the page they
* started from. If the deletion fails, an error notification is
* displayed.
*/
var deleteObjectImmediately = function deleteObjectImmediately() {
$scope['delete']($scope.$parent).then(navigateBack, guacNotification.SHOW_REQUEST_ERROR);
};
/**
* Cancels all pending edits, returning to the page the user started
* from.
*/
$scope.cancel = navigateBack;
/**
* Cancels all pending edits, invoking the provided clone function to
* open an edit page for a new object which is prepopulated with the
* data from the current object.
*/
$scope.cloneObject = function cloneObject () {
$scope.clone($scope.$parent);
};
/**
* Invokes the provided save function to save the current object. If
* saving is successful, the user is navigated back to the page they
* started from. If saving fails, an error notification is displayed.
*/
$scope.saveObject = function saveObject() {
$scope.save($scope.$parent).then(navigateBack, guacNotification.SHOW_REQUEST_ERROR);
};
/**
* Deletes the current object, prompting the user first to confirm that
* deletion is desired. If the user confirms that deletion is desired,
* the object is deleted through invoking the provided delete function.
* The user is automatically navigated back to the page they started
* from or given an error notification depending on whether deletion
* succeeds.
*/
$scope.deleteObject = function deleteObject() {
// Confirm deletion request
guacNotification.showStatus({
title : $scope.namespace + '.DIALOG_HEADER_CONFIRM_DELETE',
text : { key : $scope.namespace + '.TEXT_CONFIRM_DELETE' },
actions : [ DELETE_ACTION, CANCEL_ACTION]
});
};
}];
return directive;
}]);

View File

@@ -0,0 +1,308 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* A directive for manipulating the system permissions granted within a given
* {@link PermissionFlagSet}, tracking the specific permissions added or
* removed within a separate pair of {@link PermissionSet} objects. Optionally,
* the permission for a particular user to update themselves (change their own
* password/attributes) may also be manipulated.
*/
angular.module('manage').directive('systemPermissionEditor', ['$injector',
function systemPermissionEditor($injector) {
// Required services
var authenticationService = $injector.get('authenticationService');
var dataSourceService = $injector.get('dataSourceService');
var permissionService = $injector.get('permissionService');
var requestService = $injector.get('requestService');
// Required types
var PermissionSet = $injector.get('PermissionSet');
var directive = {
// Element only
restrict: 'E',
replace: true,
scope: {
/**
* The unique identifier of the data source associated with the
* permissions being manipulated.
*
* @type String
*/
dataSource : '=',
/**
* The username of the user whose self-update permission (whether
* the user has permission to update their own user account) should
* be additionally controlled by this editor. If no such user
* permissions should be controlled, this should be left undefined.
*
* @type String
*/
username : '=',
/**
* The current state of the permissions being manipulated. This
* {@link PemissionFlagSet} will be modified as changes are made
* through this permission editor.
*
* @type PermissionFlagSet
*/
permissionFlags : '=',
/**
* The set of permissions that have been added, relative to the
* initial state of the permissions being manipulated.
*
* @type PermissionSet
*/
permissionsAdded : '=',
/**
* The set of permissions that have been removed, relative to the
* initial state of the permissions being manipulated.
*
* @type PermissionSet
*/
permissionsRemoved : '='
},
templateUrl: 'app/manage/templates/systemPermissionEditor.html'
};
directive.controller = ['$scope', function systemPermissionEditorController($scope) {
/**
* The identifiers of all data sources currently available to the
* authenticated user.
*
* @type String[]
*/
var dataSources = authenticationService.getAvailableDataSources();
/**
* The username of the current, authenticated user.
*
* @type String
*/
var currentUsername = authenticationService.getCurrentUsername();
/**
* Available system permission types, as translation string / internal
* value pairs.
*
* @type Object[]
*/
$scope.systemPermissionTypes = [
{
label: "MANAGE_USER.FIELD_HEADER_ADMINISTER_SYSTEM",
value: PermissionSet.SystemPermissionType.ADMINISTER
},
{
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_USERS",
value: PermissionSet.SystemPermissionType.CREATE_USER
},
{
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTIONS",
value: PermissionSet.SystemPermissionType.CREATE_CONNECTION
},
{
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS",
value: PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP
},
{
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_SHARING_PROFILES",
value: PermissionSet.SystemPermissionType.CREATE_SHARING_PROFILE
}
];
// Query the permissions granted to the currently-authenticated user
dataSourceService.apply(
permissionService.getEffectivePermissions,
dataSources,
currentUsername
)
.then(function permissionsReceived(permissions) {
$scope.permissions = permissions;
}, requestService.WARN);
/**
* Returns whether the current user has permission to change the system
* permissions granted to users.
*
* @returns {Boolean}
* true if the current user can grant or revoke system permissions
* to the permission set being edited, false otherwise.
*/
$scope.canChangeSystemPermissions = function canChangeSystemPermissions() {
// Do not check if permissions are not yet loaded
if (!$scope.permissions)
return false;
// Only the administrator can modify system permissions
return PermissionSet.hasSystemPermission($scope.permissions[$scope.dataSource],
PermissionSet.SystemPermissionType.ADMINISTER);
};
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the addition of the given system permission.
*
* @param {String} type
* The system permission to add, as defined by
* PermissionSet.SystemPermissionType.
*/
var addSystemPermission = function addSystemPermission(type) {
// If permission was previously removed, simply un-remove it
if (PermissionSet.hasSystemPermission($scope.permissionsRemoved, type))
PermissionSet.removeSystemPermission($scope.permissionsRemoved, type);
// Otherwise, explicitly add the permission
else
PermissionSet.addSystemPermission($scope.permissionsAdded, type);
};
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the removal of the given system permission.
*
* @param {String} type
* The system permission to remove, as defined by
* PermissionSet.SystemPermissionType.
*/
var removeSystemPermission = function removeSystemPermission(type) {
// If permission was previously added, simply un-add it
if (PermissionSet.hasSystemPermission($scope.permissionsAdded, type))
PermissionSet.removeSystemPermission($scope.permissionsAdded, type);
// Otherwise, explicitly remove the permission
else
PermissionSet.addSystemPermission($scope.permissionsRemoved, type);
};
/**
* Notifies the controller that a change has been made to the given
* system permission for the permission set being edited.
*
* @param {String} type
* The system permission that was changed, as defined by
* PermissionSet.SystemPermissionType.
*/
$scope.systemPermissionChanged = function systemPermissionChanged(type) {
// Determine current permission setting
var granted = $scope.permissionFlags.systemPermissions[type];
// Add/remove permission depending on flag state
if (granted)
addSystemPermission(type);
else
removeSystemPermission(type);
};
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the addition of the given user permission.
*
* @param {String} type
* The user permission to add, as defined by
* PermissionSet.ObjectPermissionType.
*
* @param {String} identifier
* The identifier of the user affected by the permission being added.
*/
var addUserPermission = function addUserPermission(type, identifier) {
// If permission was previously removed, simply un-remove it
if (PermissionSet.hasUserPermission($scope.permissionsRemoved, type, identifier))
PermissionSet.removeUserPermission($scope.permissionsRemoved, type, identifier);
// Otherwise, explicitly add the permission
else
PermissionSet.addUserPermission($scope.permissionsAdded, type, identifier);
};
/**
* Updates the permissionsAdded and permissionsRemoved permission sets
* to reflect the removal of the given user permission.
*
* @param {String} type
* The user permission to remove, as defined by
* PermissionSet.ObjectPermissionType.
*
* @param {String} identifier
* The identifier of the user affected by the permission being
* removed.
*/
var removeUserPermission = function removeUserPermission(type, identifier) {
// If permission was previously added, simply un-add it
if (PermissionSet.hasUserPermission($scope.permissionsAdded, type, identifier))
PermissionSet.removeUserPermission($scope.permissionsAdded, type, identifier);
// Otherwise, explicitly remove the permission
else
PermissionSet.addUserPermission($scope.permissionsRemoved, type, identifier);
};
/**
* Notifies the controller that a change has been made to the given user
* permission for the permission set being edited.
*
* @param {String} type
* The user permission that was changed, as defined by
* PermissionSet.ObjectPermissionType.
*
* @param {String} identifier
* The identifier of the user affected by the changed permission.
*/
$scope.userPermissionChanged = function userPermissionChanged(type, identifier) {
// Determine current permission setting
var granted = $scope.permissionFlags.userPermissions[type][identifier];
// Add/remove permission depending on flag state
if (granted)
addUserPermission(type, identifier);
else
removeUserPermission(type, identifier);
};
}];
return directive;
}]);

View File

@@ -0,0 +1,21 @@
<div class="connection-permissions">
<div class="header">
<h2>{{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}</h2>
<guac-group-list-filter connection-groups="rootGroups"
filtered-connection-groups="filteredRootGroups"
placeholder="'MANAGE_USER.FIELD_PLACEHOLDER_FILTER' | translate"
connection-properties="filteredConnectionProperties"
connection-group-properties="filteredConnectionGroupProperties"></guac-group-list-filter>
</div>
<div class="section">
<guac-group-list
context="groupListContext"
connection-groups="filteredRootGroups"
templates="{
'connection' : 'app/manage/templates/connectionPermission.html',
'sharing-profile' : 'app/manage/templates/sharingProfilePermission.html',
'connection-group' : 'app/manage/templates/connectionGroupPermission.html'
}"
page-size="20"></guac-group-list>
</div>
</div>

View File

@@ -0,0 +1,3 @@
<div class="page-tabs">
<guac-page-list pages="pages"></guac-page-list>
</div>

View File

@@ -41,7 +41,7 @@
<!-- Connection attributes section -->
<div class="attributes">
<guac-form namespace="'CONNECTION_ATTRIBUTES'" content="attributes"
model="connection.attributes" model-only="!canChangeAllAttributes()"></guac-form>
model="connection.attributes" model-only="!managementPermissions.canChangeAllAttributes"></guac-form>
</div>
<!-- Connection parameters -->
@@ -53,12 +53,13 @@
</div>
<!-- Form action buttons -->
<div class="action-buttons">
<button ng-show="canSaveConnection" ng-click="saveConnection()">{{'MANAGE_CONNECTION.ACTION_SAVE' | translate}}</button>
<button ng-show="canCloneConnection" ng-click="cloneConnection()">{{'MANAGE_CONNECTION.ACTION_CLONE' | translate}}</button>
<button ng-click="cancel()">{{'MANAGE_CONNECTION.ACTION_CANCEL' | translate}}</button>
<button ng-show="canDeleteConnection" ng-click="deleteConnection()" class="danger">{{'MANAGE_CONNECTION.ACTION_DELETE' | translate}}</button>
</div>
<management-buttons namespace="MANAGE_CONNECTION"
permissions="managementPermissions"
save="saveConnection()"
delete="deleteConnection()"
clone="cloneConnection()"
return="returnToConnectionList()">
</management-buttons>
<!-- Connection history -->
<h2 class="header">{{'MANAGE_CONNECTION.SECTION_HEADER_HISTORY' | translate}}</h2>

View File

@@ -41,14 +41,16 @@
<!-- Connection group attributes section -->
<div class="attributes">
<guac-form namespace="'CONNECTION_GROUP_ATTRIBUTES'" content="attributes"
model="connectionGroup.attributes" model-only="!canChangeAllAttributes()"></guac-form>
model="connectionGroup.attributes" model-only="!managementPermissions.canChangeAllAttributes"></guac-form>
</div>
<!-- Form action buttons -->
<div class="action-buttons">
<button ng-show="canSaveConnectionGroup" ng-click="saveConnectionGroup()">{{'MANAGE_CONNECTION_GROUP.ACTION_SAVE' | translate}}</button>
<button ng-click="cancel()">{{'MANAGE_CONNECTION_GROUP.ACTION_CANCEL' | translate}}</button>
<button ng-show="canDeleteConnectionGroup" ng-click="deleteConnectionGroup()" class="danger">{{'MANAGE_CONNECTION_GROUP.ACTION_DELETE' | translate}}</button>
</div>
<management-buttons namespace="MANAGE_CONNECTION_GROUP"
permissions="managementPermissions"
save="saveConnectionGroup()"
delete="deleteConnectionGroup()"
clone="cloneConnectionGroup()"
return="returnToConnectionList()">
</management-buttons>
</div>

View File

@@ -22,7 +22,7 @@
<!-- Sharing profile attributes section -->
<div class="attributes">
<guac-form namespace="'SHARING_PROFILE_ATTRIBUTES'" content="attributes"
model="sharingProfile.attributes" model-only="!canChangeAllAttributes()"></guac-form>
model="sharingProfile.attributes" model-only="!managementPermissions.canChangeAllAttributes"></guac-form>
</div>
<!-- Sharing profile parameters -->
@@ -34,11 +34,12 @@
</div>
<!-- Form action buttons -->
<div class="action-buttons">
<button ng-show="canSaveSharingProfile" ng-click="saveSharingProfile()">{{'MANAGE_SHARING_PROFILE.ACTION_SAVE' | translate}}</button>
<button ng-show="canCloneSharingProfile" ng-click="cloneSharingProfile()">{{'MANAGE_SHARING_PROFILE.ACTION_CLONE' | translate}}</button>
<button ng-click="cancel()">{{'MANAGE_SHARING_PROFILE.ACTION_CANCEL' | translate}}</button>
<button ng-show="canDeleteSharingProfile" ng-click="deleteSharingProfile()" class="danger">{{'MANAGE_SHARING_PROFILE.ACTION_DELETE' | translate}}</button>
</div>
<management-buttons namespace="MANAGE_SHARING_PROFILE"
permissions="managementPermissions"
save="saveSharingProfile()"
delete="deleteSharingProfile()"
clone="cloneSharingProfile()"
return="returnToConnectionList()">
</management-buttons>
</div>

View File

@@ -6,17 +6,18 @@
<h2>{{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}</h2>
<guac-user-menu></guac-user-menu>
</div>
<div class="page-tabs">
<guac-page-list pages="accountPages"></guac-page-list>
</div>
<data-data-source-tabs ng-hide="cloneSourceUsername"
permissions="managementPermissions"
url="getUserURL(dataSource)">
</data-data-source-tabs>
<!-- Warn if user is read-only -->
<div class="section" ng-show="isReadOnly()">
<div class="section" ng-hide="managementPermissions[dataSource].canSaveObject">
<p class="notice read-only">{{'MANAGE_USER.INFO_READ_ONLY' | translate}}</p>
</div>
<!-- Sections applicable to non-read-only users -->
<div ng-show="!isReadOnly()">
<div ng-show="managementPermissions[dataSource].canSaveObject">
<!-- User password section -->
<div class="section">
@@ -40,61 +41,37 @@
</div>
<!-- User attributes section -->
<div class="attributes" ng-show="canChangeAttributes()">
<div class="attributes" ng-show="managementPermissions[dataSource].canChangeAttributes">
<guac-form namespace="'USER_ATTRIBUTES'" content="attributes"
model="user.attributes" model-only="!canChangeAllAttributes()"></guac-form>
model="user.attributes"
model-only="!managementPermissions[dataSource].canChangeAllAttributes"></guac-form>
</div>
<!-- System permissions section -->
<div class="system-permissions" ng-show="canChangePermissions()">
<h2 class="header">{{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}</h2>
<div class="section">
<table class="properties">
<tr ng-repeat="systemPermissionType in systemPermissionTypes"
ng-show="canChangeSystemPermissions()">
<th>{{systemPermissionType.label | translate}}</th>
<td><input type="checkbox" ng-model="permissionFlags.systemPermissions[systemPermissionType.value]"
ng-change="systemPermissionChanged(systemPermissionType.value)"/></td>
</tr>
<tr>
<th>{{'MANAGE_USER.FIELD_HEADER_CHANGE_OWN_PASSWORD' | translate}}</th>
<td><input type="checkbox" ng-model="permissionFlags.userPermissions.UPDATE[selfUsername]"
ng-change="userPermissionChanged('UPDATE', selfUsername)"/></td>
</tr>
</table>
</div>
</div>
<system-permission-editor ng-show="managementPermissions[dataSource].canChangePermissions"
username="selfUsername"
data-data-source="dataSource"
permission-flags="permissionFlags"
permissions-added="permissionsAdded"
permissions-removed="permissionsRemoved">
</system-permission-editor>
<!-- Connection permissions section -->
<div class="connection-permissions" ng-show="canChangePermissions()">
<div class="header">
<h2>{{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}</h2>
<guac-group-list-filter connection-groups="rootGroups"
filtered-connection-groups="filteredRootGroups"
placeholder="'MANAGE_USER.FIELD_PLACEHOLDER_FILTER' | translate"
connection-properties="filteredConnectionProperties"
connection-group-properties="filteredConnectionGroupProperties"></guac-group-list-filter>
</div>
<div class="section">
<guac-group-list
context="groupListContext"
connection-groups="filteredRootGroups"
templates="{
'connection' : 'app/manage/templates/connectionPermission.html',
'sharing-profile' : 'app/manage/templates/sharingProfilePermission.html',
'connection-group' : 'app/manage/templates/connectionGroupPermission.html'
}"
page-size="20"/>
</div>
</div>
<connection-permission-editor ng-show="managementPermissions[dataSource].canChangePermissions"
data-data-source="dataSource"
permission-flags="permissionFlags"
permissions-added="permissionsAdded"
permissions-removed="permissionsRemoved">
</connection-permission-editor>
<!-- Form action buttons -->
<div class="action-buttons">
<button ng-show="canSaveUser()" ng-click="saveUser()">{{'MANAGE_USER.ACTION_SAVE' | translate}}</button>
<button ng-show="canCloneUser()" ng-click="cloneUser()">{{'MANAGE_USER.ACTION_CLONE' | translate}}</button>
<button ng-click="cancel()">{{'MANAGE_USER.ACTION_CANCEL' | translate}}</button>
<button ng-show="canDeleteUser()" ng-click="deleteUser()" class="danger">{{'MANAGE_USER.ACTION_DELETE' | translate}}</button>
</div>
<management-buttons namespace="MANAGE_USER"
permissions="managementPermissions[dataSource]"
save="saveUser()"
delete="deleteUser()"
clone="cloneUser()"
return="returnToUserList()">
</management-buttons>
</div>

View File

@@ -0,0 +1,6 @@
<div class="action-buttons">
<button ng-show="permissions.canSaveObject" ng-click="saveObject()">{{namespace + '.ACTION_SAVE' | translate}}</button>
<button ng-show="permissions.canCloneObject" ng-click="cloneObject()">{{namespace + '.ACTION_CLONE' | translate}}</button>
<button ng-click="cancel()">{{namespace + '.ACTION_CANCEL' | translate}}</button>
<button ng-show="permissions.canDeleteObject" ng-click="deleteObject()" class="danger">{{namespace + '.ACTION_DELETE' | translate}}</button>
</div>

View File

@@ -0,0 +1,18 @@
<div class="system-permissions">
<h2 class="header">{{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}</h2>
<div class="section">
<table class="properties">
<tr ng-repeat="systemPermissionType in systemPermissionTypes"
ng-show="canChangeSystemPermissions()">
<th>{{systemPermissionType.label | translate}}</th>
<td><input type="checkbox" ng-model="permissionFlags.systemPermissions[systemPermissionType.value]"
ng-change="systemPermissionChanged(systemPermissionType.value)"/></td>
</tr>
<tr ng-show="username">
<th>{{'MANAGE_USER.FIELD_HEADER_CHANGE_OWN_PASSWORD' | translate}}</th>
<td><input type="checkbox" ng-model="permissionFlags.userPermissions.UPDATE[username]"
ng-change="userPermissionChanged('UPDATE', username)"/></td>
</tr>
</table>
</div>
</div>

View File

@@ -0,0 +1,182 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* A service for defining the ManagementPermissions class.
*/
angular.module('manage').factory('ManagementPermissions', ['$injector',
function defineManagementPermissions($injector) {
// Required types
var PermissionSet = $injector.get('PermissionSet');
/**
* Higher-level representation of the management-related permissions
* available to the current user on a particular, arbitrary object.
*
* @constructor
* @param {ManagementPermissions|Object} template
* An object whose properties should be copied into the new
* ManagementPermissions object.
*/
var ManagementPermissions = function ManagementPermissions(template) {
/**
* The identifier of the associated object, or null if the object does
* not yet exist.
*
* @type String
*/
this.identifier = template.identifier || null;
/**
* Whether the user can save the associated object. This could be
* updating an existing object, or creating a new object.
*
* @type Boolean
*/
this.canSaveObject = template.canSaveObject;
/**
* Whether the user can clone the associated object.
*
* @type Boolean
*/
this.canCloneObject = template.canCloneObject;
/**
* Whether the user can delete the associated object.
*
* @type Boolean
*/
this.canDeleteObject = template.canDeleteObject;
/**
* Whether the user can change attributes which are currently
* associated with the object.
*
* @type Boolean
*/
this.canChangeAttributes = template.canChangeAttributes;
/**
* Whether the user can change absolutely all attributes associated
* with the object, including those which are not already present.
*
* @type Boolean
*/
this.canChangeAllAttributes = template.canChangeAllAttributes;
/**
* Whether the user can change permissions which are assigned to the
* associated object, if the object is capable of being assigned
* permissions.
*
* @type Boolean
*/
this.canChangePermissions = template.canChangePermissions;
};
/**
* Creates a new {@link ManagementPermissions} which defines the high-level
* actions the current user may take for the given object.
*
* @param {PermissionSet} permissions
* The effective permissions granted to the current user within the
* data source associated with the object being managed.
*
* @param {String} createPermission
* The system permission required to create objects of the same type as
* the object being managed, as defined by
* {@link PermissionSet.SystemPermissionTypes}.
*
* @param {Function} hasObjectPermission
* The function to invoke to test whether a {@link PermissionSet}
* contains a particular object permission. The parameters accepted
* by this function must be identical to those accepted by
* {@link PermissionSet.hasUserPermission()},
* {@link PermissionSet.hasConnectionPermission()}, etc.
*
* @param {String} [identifier]
* The identifier of the object being managed. If the object does not
* yet exist, this parameter should be omitted or set to null.
*
* @returns {ManagementPermissions}
* A new {@link ManagementPermissions} which defines the high-level
* actions the current user may take for the given object.
*/
ManagementPermissions.fromPermissionSet = function fromPermissionSet(
permissions, createPermission, hasObjectPermission, identifier) {
var isAdmin = PermissionSet.hasSystemPermission(permissions,
PermissionSet.SystemPermissionType.ADMINISTER);
var canCreate = PermissionSet.hasSystemPermission(permissions, createPermission);
var canAdminister = hasObjectPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER, identifier);
var canUpdate = hasObjectPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
var canDelete = hasObjectPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier);
var exists = !!identifier;
return new ManagementPermissions({
identifier : identifier,
// A user can save (create or update) an object if they are a
// system-level administrator, OR the object does not yet exist and
// the user has explicit permission to create such objects, OR the
// object does already exist and the user has explicit UPDATE
// permission on the object
canSaveObject : isAdmin || (!exists && canCreate) || canUpdate,
// A user can clone an object only if the object exists, and
// only if they are a system-level administrator OR they have
// explicit permission to create such objects
canCloneObject : exists && (isAdmin || canCreate),
// A user can delete an object only if the object exists, and
// only if they are a system-level administrator OR they have
// explicit DELETE permission on the object
canDeleteObject : exists && (isAdmin || canDelete),
// Attributes in general (with or without existing values) can only
// be changed if the object is being created, OR the user is a
// system-level administrator, OR the user has explicit UPDATE
// permission on the object
canChangeAttributes : !exists || isAdmin || canUpdate,
// A user can change the attributes of an object which are not
// explicitly defined on that object when the object is being
// created
canChangeAllAttributes : !exists,
// A user can change the system permissions related to an object
// if they are a system-level admin, OR they are creating the
// object, OR they have explicit ADMINISTER permission on the
// existing object
canChangePermissions : isAdmin || !exists || canAdminister
});
};
return ManagementPermissions;
}]);

View File

@@ -22,5 +22,6 @@
* Guacamole web application.
*/
angular.module('rest', [
'auth'
'auth',
'locale'
]);

View File

@@ -20,8 +20,11 @@
/**
* Service which defines the Protocol class.
*/
angular.module('rest').factory('Protocol', [function defineProtocol() {
angular.module('rest').factory('Protocol', ['$injector', function defineProtocol($injector) {
// Required services
var translationStringService = $injector.get('translationStringService');
/**
* The object returned by REST API calls when representing the data
* associated with a supported remote desktop protocol.
@@ -64,6 +67,53 @@ angular.module('rest').factory('Protocol', [function defineProtocol() {
};
/**
* Returns the translation string namespace for the protocol having the
* given name. The namespace will be of the form:
*
* <code>PROTOCOL_NAME</code>
*
* where <code>NAME</code> is the protocol name transformed via
* translationStringService.canonicalize().
*
* @param {String} protocolName
* The name of the protocol.
*
* @returns {String}
* The translation namespace for the protocol specified, or null if no
* namespace could be generated.
*/
Protocol.getNamespace = function getNamespace(protocolName) {
// Do not generate a namespace if no protocol is selected
if (!protocolName)
return null;
return 'PROTOCOL_' + translationStringService.canonicalize(protocolName);
};
/**
* Given the internal name of a protocol, produces the translation string
* for the localized version of that protocol's name. The translation
* string will be of the form:
*
* <code>NAMESPACE.NAME<code>
*
* where <code>NAMESPACE</code> is the namespace generated from
* $scope.getNamespace().
*
* @param {String} protocolName
* The name of the protocol.
*
* @returns {String}
* The translation string which produces the localized name of the
* protocol specified.
*/
Protocol.getName = function getProtocolName(protocolName) {
return Protocol.getNamespace(protocolName) + '.NAME';
};
return Protocol;
}]);

View File

@@ -234,6 +234,7 @@
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
"ACTION_CLONE" : "@:APP.ACTION_CLONE",
"ACTION_DELETE" : "@:APP.ACTION_DELETE",
"ACTION_SAVE" : "@:APP.ACTION_SAVE",