mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-926: Also accept user and group identifiers to post in a seperate request.
This commit is contained in:
@@ -33,6 +33,7 @@ angular.module('import').factory('connectionCSVService',
|
||||
|
||||
// Required types
|
||||
const ParseError = $injector.get('ParseError');
|
||||
const ProtoConnection = $injector.get('ProtoConnection');
|
||||
const TranslatableMessage = $injector.get('TranslatableMessage');
|
||||
|
||||
// Required services
|
||||
@@ -43,7 +44,7 @@ angular.module('import').factory('connectionCSVService',
|
||||
const service = {};
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves to an object detailing the connection
|
||||
* Returns a promise that resolves to a object detailing the connection
|
||||
* attributes for the current data source, as well as the connection
|
||||
* paremeters for every protocol, for the current data source.
|
||||
*
|
||||
@@ -96,12 +97,66 @@ angular.module('import').factory('connectionCSVService',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a raw user-provided, semicolon-seperated list of identifiers into
|
||||
* an array of identifiers. If identifiers contain semicolons, they can be
|
||||
* escaped with backslashes, and backslashes can also be escaped using other
|
||||
* backslashes.
|
||||
*
|
||||
* @param {type} rawIdentifiers
|
||||
* The raw string value as fetched from the CSV.
|
||||
*
|
||||
* @returns {Array.<String>}
|
||||
* An array of identifier values.
|
||||
*/
|
||||
function splitIdentifiers(rawIdentifiers) {
|
||||
|
||||
// Keep track of whether a backslash was seen
|
||||
let escaped = false;
|
||||
|
||||
return _.reduce(rawIdentifiers, (identifiers, ch) => {
|
||||
|
||||
// The current identifier will be the last one in the final list
|
||||
let identifier = identifiers[identifiers.length - 1];
|
||||
|
||||
// If a semicolon is seen, set the "escaped" flag and continue
|
||||
// to the next character
|
||||
if (!escaped && ch == '\\') {
|
||||
escaped = true;
|
||||
return identifiers;
|
||||
}
|
||||
|
||||
// End the current identifier and start a new one if there's an
|
||||
// unescaped semicolon
|
||||
else if (!escaped && ch == ';') {
|
||||
identifiers.push('');
|
||||
return identifiers;
|
||||
}
|
||||
|
||||
// In all other cases, just append to the identifier
|
||||
else {
|
||||
identifier += ch;
|
||||
escaped = false;
|
||||
}
|
||||
|
||||
// Save the updated identifier to the list
|
||||
identifiers[identifiers.length - 1] = identifier;
|
||||
|
||||
return identifiers;
|
||||
|
||||
}, [''])
|
||||
|
||||
// Filter out any 0-length (empty) identifiers
|
||||
.filter(identifier => identifier.length);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a CSV header row, create and return a promise that will resolve to
|
||||
* a function that can take a CSV data row and return a connection object.
|
||||
* If an error occurs while parsing a particular row, the resolved function
|
||||
* will throw a ParseError describing the failure.
|
||||
* a function that can take a CSV data row and return a ProtoConnection
|
||||
* object. If an error occurs while parsing a particular row, the resolved
|
||||
* function will throw a ParseError describing the failure.
|
||||
*
|
||||
* The provided CSV must contain columns for name and protocol. Optionally,
|
||||
* the parentIdentifier of the target parent connection group, or a connection
|
||||
@@ -120,14 +175,17 @@ angular.module('import').factory('connectionCSVService',
|
||||
* required.
|
||||
*
|
||||
* This returned object will be very similar to the Connection type, with
|
||||
* the exception that a human-readable "group" field may be present.
|
||||
* the exception that a human-readable "group" field may be present, in
|
||||
* addition to "user" and "userGroup" fields containing arrays of user and
|
||||
* user group identifiers for whom read access should be granted to this
|
||||
* connection.
|
||||
*
|
||||
* If a failure occurs while attempting to create the transformer function,
|
||||
* the promise will be rejected with a ParseError describing the failure.
|
||||
*
|
||||
* @returns {Promise.<Function.<String[], Object>>}
|
||||
* A promise that will resolve to a function that translates a CSV data
|
||||
* row (array of strings) to a connection object.
|
||||
* row (array of strings) to a ProtoConnection object.
|
||||
*/
|
||||
service.getCSVTransformer = function getCSVTransformer(headerRow) {
|
||||
|
||||
@@ -149,8 +207,12 @@ angular.module('import').factory('connectionCSVService',
|
||||
protocolGetter: undefined,
|
||||
|
||||
// Callbacks for a parent group ID or group path
|
||||
groupGetter: _.noop,
|
||||
parentIdentifierGetter: _.noop,
|
||||
groupGetter: undefined,
|
||||
parentIdentifierGetter: undefined,
|
||||
|
||||
// Callbacks for user and user group identifiers
|
||||
usersGetter: () => [],
|
||||
userGroupsGetter: () => [],
|
||||
|
||||
// Callbacks that will generate either connection attributes or
|
||||
// parameters. These callbacks will return a {type, name, value}
|
||||
@@ -188,6 +250,11 @@ angular.module('import').factory('connectionCSVService',
|
||||
|
||||
// A callback that returns the field at the current index
|
||||
const fetchFieldAtIndex = row => row[index];
|
||||
|
||||
// A callback that splits identifier lists by semicolon
|
||||
// characters into a javascript list of identifiers
|
||||
const identifierListCallback = row =>
|
||||
splitIdentifiers(fetchFieldAtIndex(row));
|
||||
|
||||
// Set up the name callback
|
||||
if (header == 'name')
|
||||
@@ -203,7 +270,18 @@ angular.module('import').factory('connectionCSVService',
|
||||
|
||||
// Set up the group parent ID callback
|
||||
else if (header == 'parentIdentifier')
|
||||
transformConfig.parentIdentifierGetter = fetchFieldAtIndex;
|
||||
transformConfig.parentIdentifierGetter = (
|
||||
identifierListCallback);
|
||||
|
||||
// Set the user identifiers callback
|
||||
else if (header == 'users')
|
||||
transformConfig.usersGetter = (
|
||||
identifierListCallback);
|
||||
|
||||
// Set the user group identifiers callback
|
||||
else if (header == 'groups')
|
||||
transformConfig.userGroupsGetter = (
|
||||
identifierListCallback);
|
||||
|
||||
// At this point, any other header might refer to a connection
|
||||
// parameter or to an attribute
|
||||
@@ -282,47 +360,61 @@ angular.module('import').factory('connectionCSVService',
|
||||
return { type, name, value };
|
||||
});
|
||||
});
|
||||
|
||||
const {
|
||||
nameGetter, protocolGetter,
|
||||
parentIdentifierGetter, groupGetter,
|
||||
usersGetter, userGroupsGetter,
|
||||
parameterOrAttributeGetters
|
||||
} = transformConfig;
|
||||
|
||||
// Fail if the name wasn't provided
|
||||
if (!transformConfig.nameGetter)
|
||||
if (!nameGetter)
|
||||
return deferred.reject(new ParseError({
|
||||
message: 'The connection name must be provided',
|
||||
key: 'CONNECTION_IMPORT.ERROR_REQUIRED_NAME'
|
||||
}));
|
||||
|
||||
// Fail if the protocol wasn't provided
|
||||
if (!transformConfig.protocolGetter)
|
||||
if (!protocolGetter)
|
||||
return deferred.reject(new ParseError({
|
||||
message: 'The connection protocol must be provided',
|
||||
key: 'CONNECTION_IMPORT.ERROR_REQUIRED_PROTOCOL'
|
||||
}));
|
||||
|
||||
// If both are specified, the parent group is ambigious
|
||||
if (parentIdentifierGetter && groupGetter)
|
||||
throw new ParseError({
|
||||
message: 'Only one of group or parentIdentifier can be set',
|
||||
key: 'CONNECTION_IMPORT.ERROR_AMBIGUOUS_PARENT_GROUP'
|
||||
});
|
||||
|
||||
// The function to transform a CSV row into a connection object
|
||||
deferred.resolve(function transformCSVRow(row) {
|
||||
|
||||
const {
|
||||
nameGetter, protocolGetter,
|
||||
parentIdentifierGetter, groupGetter,
|
||||
parameterOrAttributeGetters
|
||||
} = transformConfig;
|
||||
|
||||
// Set name and protocol
|
||||
// Get name and protocol
|
||||
const name = nameGetter(row);
|
||||
const protocol = protocolGetter(row);
|
||||
|
||||
// Get any users or user groups who should be granted access
|
||||
const users = usersGetter(row);
|
||||
const groups = userGroupsGetter(row);
|
||||
|
||||
// Set the parent group ID and/or group path
|
||||
// Get the parent group ID and/or group path
|
||||
const group = groupGetter && groupGetter(row);
|
||||
const parentIdentifier = (
|
||||
parentIdentifierGetter && parentIdentifierGetter(row));
|
||||
|
||||
return {
|
||||
return new ProtoConnection({
|
||||
|
||||
// Simple fields that are not protocol-specific
|
||||
// Fields that are not protocol-specific
|
||||
...{
|
||||
name,
|
||||
protocol,
|
||||
parentIdentifier,
|
||||
group
|
||||
group,
|
||||
users,
|
||||
groups
|
||||
},
|
||||
|
||||
// Fields that might potentially be either attributes or
|
||||
@@ -339,7 +431,7 @@ angular.module('import').factory('connectionCSVService',
|
||||
|
||||
}, {parameters: {}, attributes: {}})
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@@ -33,6 +33,7 @@ angular.module('import').factory('connectionParseService',
|
||||
const Connection = $injector.get('Connection');
|
||||
const DirectoryPatch = $injector.get('DirectoryPatch');
|
||||
const ParseError = $injector.get('ParseError');
|
||||
const ParseResult = $injector.get('ParseResult');
|
||||
const TranslatableMessage = $injector.get('TranslatableMessage');
|
||||
|
||||
// Required services
|
||||
@@ -41,8 +42,81 @@ angular.module('import').factory('connectionParseService',
|
||||
const schemaService = $injector.get('schemaService');
|
||||
const connectionCSVService = $injector.get('connectionCSVService');
|
||||
const connectionGroupService = $injector.get('connectionGroupService');
|
||||
const userService = $injector.get('userService');
|
||||
const userGroupService = $injector.get('userGroupService');
|
||||
|
||||
const service = {};
|
||||
|
||||
/**
|
||||
* Resolves to an object whose keys are all valid identifiers in the current
|
||||
* data source. The provided `requestFunction` should resolve to such an
|
||||
* object when provided with the current data source.
|
||||
*
|
||||
* @param {Function.<String<Object.<String, *>>} requestFunction
|
||||
* A function that, given a data source, will return a promise resolving
|
||||
* to an object with keys that are unique identifiers for entities in
|
||||
* that data source.
|
||||
*
|
||||
* @returns {Promise.<Function[String[], String[]>>}
|
||||
* A promise that will resolve to an function that, given an array of
|
||||
* identifiers, will return all identifiers that do not exist as keys in
|
||||
* the object returned by `requestFunction`, i.e. all identifiers that
|
||||
* do not exist in the current data source.
|
||||
*/
|
||||
function getIdentifierMap(requestFunction) {
|
||||
|
||||
// The current data source to which all the identifiers will belong
|
||||
const dataSource = $routeParams.dataSource;
|
||||
|
||||
// Make the request and return the response, which should be an object
|
||||
// whose keys are all valid identifiers for the current data source
|
||||
return requestFunction(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a promise that resolves to a function that takes an array of
|
||||
* identifiers, returning a ParseError describing the invalid identifiers
|
||||
* from the list. The provided `requestFunction` should resolve to such an
|
||||
* object whose keys are all valid identifiers when provided with the
|
||||
* current data source.
|
||||
*
|
||||
* @param {Function.<String<Object.<String, *>>} requestFunction
|
||||
* A function that, given a data source, will return a promise resolving
|
||||
* to an object with keys that are unique identifiers for entities in
|
||||
* that data source.
|
||||
*
|
||||
* @returns {Promise.<Function.<String[], ParseError?>>}
|
||||
* A promise that will resolve to a function to check the validity of
|
||||
* each identifier in a provided array, returning a ParseError
|
||||
* describing the problem if any are not valid.
|
||||
*/
|
||||
function getInvalidIdentifierErrorChecker(requestFunction) {
|
||||
|
||||
// Fetch all the valid user identifiers in the system, and
|
||||
return getIdentifierMap(requestFunction).then(validIdentifiers =>
|
||||
|
||||
// The resolved function that takes a list of user group identifiers
|
||||
allIdentifiers => {
|
||||
|
||||
// Filter to only include invalid identifiers
|
||||
const invalidIdentifiers = _.filter(allIdentifiers,
|
||||
identifier => !validIdentifiers[identifier]);
|
||||
|
||||
if (invalidIdentifiers.length) {
|
||||
|
||||
// Quote and comma-seperate for display
|
||||
const identifierList = invalidIdentifiers.map(
|
||||
identifier => '"' + identifier + '"').join(', ');
|
||||
|
||||
return new ParseError({
|
||||
message: 'Invalid User Group Identifiers: ' + identifierList,
|
||||
key: 'CONNECTION_IMPORT.ERROR_INVALID_USER_GROUP_IDENTIFIERS',
|
||||
variables: { IDENTIFIER_LIST: identifierList }
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform basic checks, common to all file types - namely that the parsed
|
||||
@@ -127,9 +201,9 @@ angular.module('import').factory('connectionParseService',
|
||||
|
||||
/**
|
||||
* Returns a promise that will resolve to a transformer function that will
|
||||
* take an object that may a "group" field, replacing it if present with a
|
||||
* "parentIdentifier". If both a "group" and "parentIdentifier" field are
|
||||
* present on the provided object, or if no group exists at the specified
|
||||
* take an object that may contain a "group" field, replacing it if present
|
||||
* with a "parentIdentifier". If both a "group" and "parentIdentifier" field
|
||||
* are present on the provided object, or if no group exists at the specified
|
||||
* path, the function will throw a ParseError describing the failure.
|
||||
*
|
||||
* @returns {Promise.<Function<Object, Object>>}
|
||||
@@ -170,32 +244,109 @@ angular.module('import').factory('connectionParseService',
|
||||
});
|
||||
}
|
||||
|
||||
// Translate a given javascript object to a full-fledged Connection
|
||||
const connectionTransformer = connection => new Connection(connection);
|
||||
/**
|
||||
* Convert a provided ProtoConnection array into a ParseResult. Any provided
|
||||
* transform functions will be run on each entry in `connectionData` before
|
||||
* any other processing is done.
|
||||
*
|
||||
* @param {*[]} connectionData
|
||||
* An arbitrary array of data. This must evaluate to a ProtoConnection
|
||||
* object after being run through all functions in `transformFunctions`.
|
||||
*
|
||||
* @param {Function[]} transformFunctions
|
||||
* An array of transformation functions to run on each entry in
|
||||
* `connection` data.
|
||||
*
|
||||
* @return {Promise.<Object>}
|
||||
* A promise resolving to ParseResult object representing the result of
|
||||
* parsing all provided connection data.
|
||||
*/
|
||||
function parseConnectionData(connectionData, transformFunctions) {
|
||||
|
||||
// Check that the provided connection data array is not empty
|
||||
const checkError = performBasicChecks(connectionData);
|
||||
if (checkError) {
|
||||
const deferred = $q.defer();
|
||||
deferred.reject(checkError);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Translate a Connection object to a patch requesting the creation of said
|
||||
// Connection
|
||||
const patchTransformer = connection => new DirectoryPatch({
|
||||
op: 'add',
|
||||
path: '/',
|
||||
value: connection
|
||||
});
|
||||
return $q.all({
|
||||
groupTransformer : getGroupTransformer(),
|
||||
invalidUserIdErrorDetector : getInvalidIdentifierErrorChecker(
|
||||
userService.getUsers),
|
||||
invalidGroupIDErrorDetector : getInvalidIdentifierErrorChecker(
|
||||
userGroupService.getUserGroups),
|
||||
})
|
||||
|
||||
// Transform the rows from the CSV file to an array of API patches
|
||||
// and lists of user and group identifiers
|
||||
.then(({groupTransformer,
|
||||
invalidUserIdErrorDetector, invalidGroupIDErrorDetector}) =>
|
||||
connectionData.reduce((parseResult, data) => {
|
||||
|
||||
const { patches, identifiers, users, groups } = parseResult;
|
||||
|
||||
// Run the array data through each provided transform
|
||||
let connectionObject = data;
|
||||
_.forEach(transformFunctions, transform => {
|
||||
connectionObject = transform(connectionObject);
|
||||
});
|
||||
|
||||
// All errors found while parsing this connection
|
||||
const connectionErrors = [];
|
||||
parseResult.errors.push(connectionErrors);
|
||||
|
||||
// Translate the group on the object to a parentIdentifier
|
||||
try {
|
||||
connectionObject = groupTransformer(connectionObject);
|
||||
}
|
||||
|
||||
// If there was a problem with the group or parentIdentifier
|
||||
catch (error) {
|
||||
connectionErrors.push(error);
|
||||
}
|
||||
|
||||
// Push any errors for invalid user or user group identifiers
|
||||
const pushError = error => error && connectionErrors.push(error);
|
||||
pushError(invalidUserIdErrorDetector(connectionObject.users));
|
||||
pushError(invalidGroupIDErrorDetector(connectionObject.userGroups));
|
||||
|
||||
// Add the user and group identifiers for this connection
|
||||
users.push(connectionObject.users);
|
||||
groups.push(connectionObject.groups);
|
||||
|
||||
// Translate to a full-fledged Connection
|
||||
const connection = new Connection(connectionObject);
|
||||
|
||||
// Finally, add a patch for creating the connection
|
||||
patches.push(new DirectoryPatch({
|
||||
op: 'add',
|
||||
path: '/',
|
||||
value: connection
|
||||
}));
|
||||
|
||||
// If there are any errors for this connection fail the whole batch
|
||||
if (connectionErrors.length)
|
||||
parseResult.hasErrors = true;
|
||||
|
||||
return parseResult;
|
||||
|
||||
}, new ParseResult()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a provided CSV representation of a connection list into a JSON
|
||||
* string to be submitted to the PATCH REST endpoint. The returned JSON
|
||||
* string will contain a PATCH operation to create each connection in the
|
||||
* provided list.
|
||||
*
|
||||
* TODO: Describe disambiguation suffixes, e.g. hostname (parameter), and
|
||||
* that we will accept without the suffix if it's unambigous. (or not? how about not?)
|
||||
* object to be submitted to the PATCH REST endpoint, as well as a list of
|
||||
* objects containing lists of user and user group identifiers to be granted
|
||||
* to each connection.
|
||||
*
|
||||
* @param {String} csvData
|
||||
* The JSON-encoded connection list to convert to a PATCH request body.
|
||||
* The CSV-encoded connection list to process.
|
||||
*
|
||||
* @return {Promise.<Connection[]>}
|
||||
* A promise resolving to an array of Connection objects, one for each
|
||||
* connection in the provided CSV.
|
||||
* @return {Promise.<Object>}
|
||||
* A promise resolving to ParseResult object representing the result of
|
||||
* parsing all provided connection data.
|
||||
*/
|
||||
service.parseCSV = function parseCSV(csvData) {
|
||||
|
||||
@@ -211,134 +362,69 @@ angular.module('import').factory('connectionParseService',
|
||||
catch(error) {
|
||||
console.error(error);
|
||||
const deferred = $q.defer();
|
||||
deferred.reject(error);
|
||||
deferred.reject(new ParseError({ message: error.message }));
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// The header row - an array of string header values
|
||||
const header = parsedData.length ? parsedData[0] : [];
|
||||
|
||||
// Slice off the header row to get the data rows
|
||||
const connectionData = parsedData.slice(1);
|
||||
|
||||
// Check that the provided CSV is not empty (the parser always
|
||||
// returns an array)
|
||||
const checkError = performBasicChecks(connectionData);
|
||||
if (checkError) {
|
||||
const deferred = $q.defer();
|
||||
deferred.reject(checkError);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// The header row - an array of string header values
|
||||
const header = parsedData[0];
|
||||
// Generate the CSV transform function, and apply it to every row
|
||||
// before applying all the rest of the standard transforms
|
||||
return connectionCSVService.getCSVTransformer(header).then(
|
||||
csvTransformer =>
|
||||
|
||||
return $q.all({
|
||||
csvTransformer : connectionCSVService.getCSVTransformer(header),
|
||||
groupTransformer : getGroupTransformer()
|
||||
})
|
||||
|
||||
// Transform the rows from the CSV file to an array of API patches
|
||||
.then(({csvTransformer, groupTransformer}) => connectionData.map(
|
||||
dataRow => {
|
||||
|
||||
// Translate the raw CSV data to a javascript object
|
||||
let connectionObject = csvTransformer(dataRow);
|
||||
|
||||
// Translate the group on the object to a parentIdentifier
|
||||
connectionObject = groupTransformer(connectionObject);
|
||||
|
||||
// Translate to a full-fledged Connection
|
||||
const connection = connectionTransformer(connectionObject);
|
||||
|
||||
// Finally, translate to a patch for creating the connection
|
||||
return patchTransformer(connection);
|
||||
|
||||
}));
|
||||
// Apply the CSV transform to every row
|
||||
parseConnectionData(connectionData, [csvTransformer]));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a provided YAML representation of a connection list into a JSON
|
||||
* string to be submitted to the PATCH REST endpoint. The returned JSON
|
||||
* string will contain a PATCH operation to create each connection in the
|
||||
* provided list.
|
||||
* object to be submitted to the PATCH REST endpoint, as well as a list of
|
||||
* objects containing lists of user and user group identifiers to be granted
|
||||
* to each connection.
|
||||
*
|
||||
* @param {String} yamlData
|
||||
* The YAML-encoded connection list to convert to a PATCH request body.
|
||||
* The YAML-encoded connection list to process.
|
||||
*
|
||||
* @return {Promise.<Connection[]>}
|
||||
* A promise resolving to an array of Connection objects, one for each
|
||||
* connection in the provided YAML.
|
||||
* @return {Promise.<Object>}
|
||||
* A promise resolving to ParseResult object representing the result of
|
||||
* parsing all provided connection data.
|
||||
*/
|
||||
service.parseYAML = function parseYAML(yamlData) {
|
||||
|
||||
// Parse from YAML into a javascript array
|
||||
const parsedData = parseYAMLData(yamlData);
|
||||
const connectionData = parseYAMLData(yamlData);
|
||||
|
||||
// Check that the data is the correct format, and not empty
|
||||
const checkError = performBasicChecks(connectionData);
|
||||
if (checkError) {
|
||||
const deferred = $q.defer();
|
||||
deferred.reject(checkError);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Transform the data from the YAML file to an array of API patches
|
||||
return getGroupTransformer().then(
|
||||
groupTransformer => parsedData.map(connectionObject => {
|
||||
|
||||
// Translate the group on the object to a parentIdentifier
|
||||
connectionObject = groupTransformer(connectionObject);
|
||||
|
||||
// Translate to a full-fledged Connection
|
||||
const connection = connectionTransformer(connectionObject);
|
||||
|
||||
// Finally, translate to a patch for creating the connection
|
||||
return patchTransformer(connection);
|
||||
|
||||
}));
|
||||
|
||||
// Produce a ParseResult
|
||||
return parseConnectionData(connectionData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a provided JSON representation of a connection list into a JSON
|
||||
* string to be submitted to the PATCH REST endpoint. The returned JSON
|
||||
* string will contain a PATCH operation to create each connection in the
|
||||
* provided list.
|
||||
* Convert a provided JSON-encoded representation of a connection list into
|
||||
* an array of patches to be submitted to the PATCH REST endpoint, as well
|
||||
* as a list of objects containing lists of user and user group identifiers
|
||||
* to be granted to each connection.
|
||||
*
|
||||
* @param {String} jsonData
|
||||
* The JSON-encoded connection list to convert to a PATCH request body.
|
||||
* The JSON-encoded connection list to process.
|
||||
*
|
||||
* @return {Promise.<Connection[]>}
|
||||
* A promise resolving to an array of Connection objects, one for each
|
||||
* connection in the provided JSON.
|
||||
* @return {Promise.<Object>}
|
||||
* A promise resolving to ParseResult object representing the result of
|
||||
* parsing all provided connection data.
|
||||
*/
|
||||
service.parseJSON = function parseJSON(jsonData) {
|
||||
|
||||
// Parse from JSON into a javascript array
|
||||
const parsedData = JSON.parse(yamlData);
|
||||
const connectionData = JSON.parse(jsonData);
|
||||
|
||||
// Check that the data is the correct format, and not empty
|
||||
const checkError = performBasicChecks(connectionData);
|
||||
if (checkError) {
|
||||
const deferred = $q.defer();
|
||||
deferred.reject(checkError);
|
||||
return deferred.promise;
|
||||
}
|
||||
// Produce a ParseResult
|
||||
return parseConnectionData(connectionData);
|
||||
|
||||
// Transform the data from the YAML file to an array of API patches
|
||||
return getGroupTransformer().then(
|
||||
groupTransformer => parsedData.map(connectionObject => {
|
||||
|
||||
// Translate the group on the object to a parentIdentifier
|
||||
connectionObject = groupTransformer(connectionObject);
|
||||
|
||||
// Translate to a full-fledged Connection
|
||||
const connection = connectionTransformer(connectionObject);
|
||||
|
||||
// Finally, translate to a patch for creating the connection
|
||||
return patchTransformer(connection);
|
||||
|
||||
}));
|
||||
|
||||
};
|
||||
|
||||
return service;
|
||||
|
@@ -31,7 +31,7 @@ angular.module('import').factory('ParseError', [function defineParseError() {
|
||||
* The object whose properties should be copied within the new
|
||||
* ParseError.
|
||||
*/
|
||||
var ParseError = function ParseError(template) {
|
||||
const ParseError = function ParseError(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Service which defines the ParseResult class.
|
||||
*/
|
||||
angular.module('import').factory('ParseResult', [function defineParseResult() {
|
||||
|
||||
/**
|
||||
* The result of parsing a connection import file - containing a list of
|
||||
* API patches ready to be submitted to the PATCH REST API for batch
|
||||
* connection creation, a set of users and user groups to grant access to
|
||||
* each connection, and any errors that may have occured while parsing
|
||||
* each connection.
|
||||
*
|
||||
* @constructor
|
||||
* @param {ParseResult|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* ParseResult.
|
||||
*/
|
||||
const ParseResult = function ParseResult(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* An array of patches, ready to be submitted to the PATCH REST API for
|
||||
* batch connection creation.
|
||||
*
|
||||
* @type {DirectoryPatch[]}
|
||||
*/
|
||||
this.patches = template.patches || [];
|
||||
|
||||
/**
|
||||
* A list of user identifiers that should be granted read access to the
|
||||
* the corresponding connection (at the same array index).
|
||||
*
|
||||
* @type {String[]}
|
||||
*/
|
||||
this.users = template.users || [];
|
||||
|
||||
/**
|
||||
* A list of user group identifiers that should be granted read access
|
||||
* to the corresponding connection (at the same array index).
|
||||
*
|
||||
* @type {String[]}
|
||||
*/
|
||||
this.groups = template.groups || [];
|
||||
|
||||
/**
|
||||
* An array of errors encountered while parsing the corresponding
|
||||
* connection (at the same array index). Each connection should have a
|
||||
* an array of errors. If empty, no errors occured for this connection.
|
||||
*
|
||||
* @type {ParseError[][]}
|
||||
*/
|
||||
this.errors = template.errors || [];
|
||||
|
||||
/**
|
||||
* True if any errors were encountered while parsing the connections
|
||||
* represented by this ParseResult. This should always be true if there
|
||||
* are a non-zero number of elements in the errors list for any
|
||||
* connection, or false otherwise.
|
||||
*/
|
||||
this.hasErrors = template.hasErrors || false;
|
||||
|
||||
};
|
||||
|
||||
return ParseResult;
|
||||
|
||||
}]);
|
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Service which defines the Connection class.
|
||||
*/
|
||||
angular.module('import').factory('ProtoConnection', [
|
||||
function defineProtoConnection() {
|
||||
|
||||
/**
|
||||
* A representation of a connection to be imported, as parsed from an
|
||||
* user-supplied import file.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Connection|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* Connection.
|
||||
*/
|
||||
var ProtoConnection = function ProtoConnection(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The unique identifier of the connection group that contains this
|
||||
* connection.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.parentIdentifier = template.parentIdentifier;
|
||||
|
||||
/**
|
||||
* The path to the connection group that contains this connection,
|
||||
* written as e.g. "ROOT/parent/child/group".
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.group = template.group;
|
||||
|
||||
/**
|
||||
* The human-readable name of this connection, which is not necessarily
|
||||
* unique.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.name = template.name;
|
||||
|
||||
/**
|
||||
* The name of the protocol associated with this connection, such as
|
||||
* "vnc" or "rdp".
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.protocol = template.protocol;
|
||||
|
||||
/**
|
||||
* Connection configuration parameters, as dictated by the protocol in
|
||||
* use, arranged as name/value pairs. This information may not be
|
||||
* available until directly queried. If this information is
|
||||
* unavailable, this property will be null or undefined.
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
this.parameters = template.parameters || {};
|
||||
|
||||
/**
|
||||
* Arbitrary name/value pairs which further describe this connection.
|
||||
* The semantics and validity of these attributes are dictated by the
|
||||
* extension which defines them.
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
this.attributes = template.attributes || {};
|
||||
|
||||
/**
|
||||
* The identifiers of all users who should be granted read access to
|
||||
* this connection.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
this.users = template.users || [];
|
||||
|
||||
/**
|
||||
* The identifiers of all user groups who should be granted read access
|
||||
* to this connection.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
this.groups = template.groups || [];
|
||||
|
||||
};
|
||||
|
||||
return ProtoConnection;
|
||||
|
||||
}]);
|
@@ -200,6 +200,10 @@
|
||||
"ERROR_INVALID_GROUP": "No group matching \"{GROUP}\" found",
|
||||
"ERROR_INVALID_FILE_TYPE":
|
||||
"Invalid import file type \"{TYPE}\"",
|
||||
"ERROR_INVALID_USER_IDENTIFIERS":
|
||||
"Users not found: {IDENTIFIER_LIST}",
|
||||
"ERROR_INVALID_USER_GROUP_IDENTIFIERS":
|
||||
"User Groups not found: {IDENTIFIER_LIST}",
|
||||
"ERROR_NO_FILE_SUPPLIED": "Please select a file to import",
|
||||
"ERROR_AMBIGUOUS_PARENT_GROUP":
|
||||
"Both group and parentIdentifier may be not specified at the same time",
|
||||
|
Reference in New Issue
Block a user