mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-926: Correct parameter values in import file to correct case if possible.
This commit is contained in:
@@ -200,7 +200,8 @@ angular.module('import').factory('connectionParseService',
|
||||
/**
|
||||
* Returns a promise that will resolve to a transformer function that will
|
||||
* perform various checks and transforms relating to the connection group
|
||||
* tree heirarchy. It will:
|
||||
* tree hierarchy, pushing any errors into the resolved connection object.
|
||||
* It will:
|
||||
* - Ensure that a connection specifies either a valid group path (no path
|
||||
* defaults to ROOT), or a valid parent group identifier, but not both
|
||||
* - Ensure that this connection does not duplicate another connection
|
||||
@@ -243,19 +244,24 @@ angular.module('import').factory('connectionParseService',
|
||||
let op = DirectoryPatch.Operation.ADD;
|
||||
|
||||
// If both are specified, the parent group is ambigious
|
||||
if (providedIdentifier && connection.group)
|
||||
throw new ParseError({
|
||||
if (providedIdentifier && connection.group) {
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'Only one of group or parentIdentifier can be set',
|
||||
key: 'IMPORT.ERROR_AMBIGUOUS_PARENT_GROUP'
|
||||
});
|
||||
}));
|
||||
return connection;
|
||||
}
|
||||
|
||||
// If a parent group identifier is present, but not valid
|
||||
else if (providedIdentifier && !groupPathsByIdentifier[providedIdentifier])
|
||||
throw new ParseError({
|
||||
else if (providedIdentifier
|
||||
&& !groupPathsByIdentifier[providedIdentifier]) {
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'No group with identifier: ' + providedIdentifier,
|
||||
key: 'IMPORT.ERROR_INVALID_GROUP_IDENTIFIER',
|
||||
variables: { IDENTIFIER: providedIdentifier }
|
||||
});
|
||||
}));
|
||||
return connection;
|
||||
}
|
||||
|
||||
// If the parent identifier is valid, use it to determine the path
|
||||
else if (providedIdentifier) {
|
||||
@@ -272,11 +278,13 @@ angular.module('import').factory('connectionParseService',
|
||||
group = connection.group;
|
||||
|
||||
// If the provided group isn't a string, it can never be valid
|
||||
if (typeof group !== 'string')
|
||||
throw new ParseError({
|
||||
if (typeof group !== 'string') {
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'Invalid group type - must be a string',
|
||||
key: 'IMPORT.ERROR_INVALID_GROUP_TYPE'
|
||||
});
|
||||
}));
|
||||
return connection;
|
||||
}
|
||||
|
||||
// Allow the group to start with a leading slash instead instead
|
||||
// of explicitly requiring the root connection group
|
||||
@@ -295,12 +303,14 @@ angular.module('import').factory('connectionParseService',
|
||||
parentIdentifier = groupPathsByIdentifier[group];
|
||||
|
||||
// If the group doesn't match anything in the tree
|
||||
if (!parentIdentifier)
|
||||
throw new ParseError({
|
||||
if (!parentIdentifier) {
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'No group found named: ' + connection.group,
|
||||
key: 'IMPORT.ERROR_INVALID_GROUP',
|
||||
variables: { GROUP: connection.group }
|
||||
});
|
||||
}));
|
||||
return connection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -315,12 +325,12 @@ angular.module('import').factory('connectionParseService',
|
||||
|
||||
// Error out if this is a duplicate of a connection already in the
|
||||
// file
|
||||
if (!!_.get(connectionsInFile, path))
|
||||
throw new ParseError({
|
||||
if (!!_.get(connectionsInFile, path))
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'Duplicate connection in file: ' + path,
|
||||
key: 'IMPORT.ERROR_DUPLICATE_CONNECTION_IN_FILE',
|
||||
variables: { NAME: connection.name, PATH: group }
|
||||
});
|
||||
}));
|
||||
|
||||
// Mark the current path as already seen in the file
|
||||
_.setWith(connectionsInFile, path, connection, Object);
|
||||
@@ -329,17 +339,18 @@ angular.module('import').factory('connectionParseService',
|
||||
const existingIdentifier = _.get(connectionIdsByGroupAndName,
|
||||
[parentIdentifier, connection.name]);
|
||||
|
||||
let importMode;
|
||||
// The default behavior is to create connections if no conflict
|
||||
let importMode = ImportConnection.ImportMode.CREATE;
|
||||
let identifier;
|
||||
|
||||
// If updates to existing connections are disallowed
|
||||
if (existingIdentifier && importConfig.existingConnectionMode ===
|
||||
ConnectionImportConfig.ExistingConnectionMode.REJECT)
|
||||
throw new ParseError({
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'Rejecting update to existing connection: ' + path,
|
||||
key: 'IMPORT.ERROR_REJECT_UPDATE_CONNECTION',
|
||||
variables: { NAME: connection.name, PATH: group }
|
||||
});
|
||||
}));
|
||||
|
||||
// If the connection is being replaced, set the existing identifer
|
||||
else if (existingIdentifier) {
|
||||
@@ -347,7 +358,6 @@ angular.module('import').factory('connectionParseService',
|
||||
importMode = ImportConnection.ImportMode.REPLACE;
|
||||
}
|
||||
|
||||
// Otherwise, just create a new connection
|
||||
else
|
||||
importMode = ImportConnection.ImportMode.CREATE;
|
||||
|
||||
@@ -359,13 +369,22 @@ angular.module('import').factory('connectionParseService',
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves to a map of all valid protocols to the
|
||||
* boolean value "true", i.e. a set of all valid protocols.
|
||||
* Returns a promise that resolves to a map of all valid protocols to a map
|
||||
* of connection parameter names to a map of lower-cased and trimmed option
|
||||
* values for that parameter to the actual valid option value.
|
||||
*
|
||||
* @returns {Promise.<Object.<String, Boolean>>}
|
||||
* A promise that resolves to a set of all valid protocols.
|
||||
* This format is designed for easy retrieval of corrected parameter values
|
||||
* if the user-provided value matches a valid option except for case or
|
||||
* leading/trailing whitespace.
|
||||
*
|
||||
* If a parameter has no options (i.e. any string value is allowed), the
|
||||
* parameter name will map to a null value.
|
||||
*
|
||||
* @returns {Promise.<Object.<String, Object.<String, Object.<String, String>>>>}
|
||||
* A promise that resolves to a map of all valid protocols to parameter
|
||||
* names to valid values.
|
||||
*/
|
||||
function getValidProtocols() {
|
||||
function getProtocolParameterOptions() {
|
||||
|
||||
// The current data source - the one that the connections will be
|
||||
// imported into
|
||||
@@ -373,67 +392,109 @@ angular.module('import').factory('connectionParseService',
|
||||
|
||||
// Fetch the protocols and convert to a set of valid protocol names
|
||||
return schemaService.getProtocols(dataSource).then(
|
||||
protocols => _.mapValues(protocols, () => true));
|
||||
protocols => _.mapValues(protocols, ({connectionForms}) => {
|
||||
|
||||
const fieldMap = {};
|
||||
|
||||
// Go through all the connection forms and get the fields for each
|
||||
connectionForms.forEach(({fields}) => fields.forEach(field => {
|
||||
|
||||
const { name, options } = field;
|
||||
|
||||
// Set the value to null to indicate that there are no options
|
||||
if (!options)
|
||||
fieldMap[name] = null;
|
||||
|
||||
// Set the value to a map of lowercased/trimmed option values
|
||||
// to actual option values
|
||||
else
|
||||
fieldMap[name] = _.mapKeys(
|
||||
options, option => option.trim().toLowerCase());
|
||||
|
||||
}));
|
||||
|
||||
return fieldMap;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of field-level errors for the provided connection,
|
||||
* such as missing or invalid fields that are not dependant on the
|
||||
* connection group heirarchy.
|
||||
* Resolves to function that will perform field-level (not connection
|
||||
* hierarchy dependent) checks and transforms to a provided connection,
|
||||
* returning the transformed connection.
|
||||
*
|
||||
* @param {ImportConnection} connection
|
||||
* The connection object to check field values on.
|
||||
*
|
||||
* @param {Object.<String, Boolean>} protocols
|
||||
* A set of valid protocols, such as the one returned by
|
||||
* getValidProtocols().
|
||||
*
|
||||
* @returns {ParseError[]}
|
||||
* A list of field-level errors for the provided connection.
|
||||
* @returns {Promise.<Function.<ImportConnection, ImportConnection>>}
|
||||
* A promise resolving to a function that will apply field-level
|
||||
* transforms and checks to a provided connection, returning the
|
||||
* transformed connection.
|
||||
*/
|
||||
function getFieldErrors(connection, protocols) {
|
||||
const connectionErrors = [];
|
||||
function getFieldTransformer() {
|
||||
|
||||
// Ensure that a protocol was specified for this connection
|
||||
const protocol = connection.protocol;
|
||||
if (!protocol)
|
||||
connectionErrors.push(new ParseError({
|
||||
message: 'Missing required protocol field',
|
||||
key: 'IMPORT.ERROR_REQUIRED_PROTOCOL_CONNECTION'
|
||||
}));
|
||||
return getProtocolParameterOptions().then(protocols => connection => {
|
||||
|
||||
// Ensure that a valid protocol was specified for this connection
|
||||
if (!protocols[protocol])
|
||||
connectionErrors.push(new ParseError({
|
||||
message: 'Invalid protocol: ' + protocol,
|
||||
key: 'IMPORT.ERROR_INVALID_PROTOCOL',
|
||||
variables: { PROTOCOL: protocol }
|
||||
}));
|
||||
// Ensure that a protocol was specified for this connection
|
||||
const protocol = connection.protocol;
|
||||
if (!protocol)
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'Missing required protocol field',
|
||||
key: 'IMPORT.ERROR_REQUIRED_PROTOCOL_CONNECTION'
|
||||
}));
|
||||
|
||||
// Ensure that a name was specified for this connection
|
||||
if (!connection.name)
|
||||
connectionErrors.push(new ParseError({
|
||||
message: 'Missing required name field',
|
||||
key: 'IMPORT.ERROR_REQUIRED_NAME_CONNECTION'
|
||||
}));
|
||||
// Ensure that a valid protocol was specified for this connection
|
||||
if (!protocols[protocol])
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'Invalid protocol: ' + protocol,
|
||||
key: 'IMPORT.ERROR_INVALID_PROTOCOL',
|
||||
variables: { PROTOCOL: protocol }
|
||||
}));
|
||||
|
||||
// Ensure that the specified user list, if any, is an array
|
||||
const users = connection.users;
|
||||
if (users && !Array.isArray(users))
|
||||
connectionErrors.push(new ParseError({
|
||||
message: 'Invalid users list - must be an array',
|
||||
key: 'IMPORT.ERROR_INVALID_USERS_TYPE'
|
||||
}));
|
||||
// Ensure that a name was specified for this connection
|
||||
if (!connection.name)
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'Missing required name field',
|
||||
key: 'IMPORT.ERROR_REQUIRED_NAME_CONNECTION'
|
||||
}));
|
||||
|
||||
// Ensure that the specified user list, if any, is an array
|
||||
const groups = connection.groups;
|
||||
if (groups && !Array.isArray(groups))
|
||||
connectionErrors.push(new ParseError({
|
||||
message: 'Invalid groups list - must be an array',
|
||||
key: 'IMPORT.ERROR_INVALID_USER_GROUPS_TYPE'
|
||||
}));
|
||||
// Ensure that the specified user list, if any, is an array
|
||||
const users = connection.users;
|
||||
if (users && !Array.isArray(users))
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'Invalid users list - must be an array',
|
||||
key: 'IMPORT.ERROR_INVALID_USERS_TYPE'
|
||||
}));
|
||||
|
||||
return connectionErrors;
|
||||
// Ensure that the specified user list, if any, is an array
|
||||
const groups = connection.groups;
|
||||
if (groups && !Array.isArray(groups))
|
||||
connection.errors.push(new ParseError({
|
||||
message: 'Invalid groups list - must be an array',
|
||||
key: 'IMPORT.ERROR_INVALID_USER_GROUPS_TYPE'
|
||||
}));
|
||||
|
||||
// If the protocol is not valid, there's no point in trying to check
|
||||
// parameter case sensitivity
|
||||
if (!protocols[protocol])
|
||||
return connection;
|
||||
|
||||
_.forEach(connection.parameters, (value, name) => {
|
||||
|
||||
// Convert the provided value to the format that would match
|
||||
// the lookup object format
|
||||
const comparisonValue = value.toLowerCase().trim();
|
||||
|
||||
// The validated / corrected option value for this connection
|
||||
// parameter, if any
|
||||
const validOptionValue = _.get(
|
||||
protocols, [protocol, name, comparisonValue]);
|
||||
|
||||
// If the provided value fuzzily matches a valid option value,
|
||||
// use the valid option value instead
|
||||
if (validOptionValue)
|
||||
connection.parameters[name] = validOptionValue;
|
||||
|
||||
});
|
||||
|
||||
return connection;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -469,12 +530,12 @@ angular.module('import').factory('connectionParseService',
|
||||
|
||||
let index = 0;
|
||||
|
||||
// Get the tree transformer and valid protocol set
|
||||
// Get the tree transformer and relevant protocol information
|
||||
return $q.all({
|
||||
treeTransformer : getTreeTransformer(importConfig),
|
||||
protocols : getValidProtocols()
|
||||
fieldTransformer : getFieldTransformer(),
|
||||
treeTransformer : getTreeTransformer(importConfig),
|
||||
})
|
||||
.then(({treeTransformer, protocols}) =>
|
||||
.then(({fieldTransformer, treeTransformer}) =>
|
||||
connectionData.reduce((parseResult, data) => {
|
||||
|
||||
const { patches, users, groups, groupPaths } = parseResult;
|
||||
@@ -485,26 +546,14 @@ angular.module('import').factory('connectionParseService',
|
||||
connectionObject = transform(connectionObject);
|
||||
});
|
||||
|
||||
// All errors encountered while running the connection through the
|
||||
// provided transform, starting with those encountered during
|
||||
// the provided transforms, and any errors from missing fields
|
||||
const connectionErrors = [
|
||||
..._.get(connectionObject, 'errors', []),
|
||||
...getFieldErrors(connectionObject, protocols)
|
||||
];
|
||||
// Apply the field level transforms
|
||||
connectionObject = fieldTransformer(connectionObject);
|
||||
|
||||
// Determine the connection's place in the connection group tree
|
||||
try {
|
||||
connectionObject = treeTransformer(connectionObject);
|
||||
}
|
||||
|
||||
// If there was a problem with the connection group heirarchy
|
||||
catch (error) {
|
||||
connectionErrors.push(error);
|
||||
}
|
||||
// Apply the connection group hierarchy transforms
|
||||
connectionObject = treeTransformer(connectionObject);
|
||||
|
||||
// If there are any errors for this connection, fail the whole batch
|
||||
if (connectionErrors.length)
|
||||
if (connectionObject.errors.length)
|
||||
parseResult.hasErrors = true;
|
||||
|
||||
// The value for the patch is a full-fledged Connection
|
||||
@@ -559,7 +608,7 @@ angular.module('import').factory('connectionParseService',
|
||||
groupPaths[index] = connectionObject.group;
|
||||
|
||||
// Save the errors for this connection into the parse result
|
||||
parseResult.errors[index] = connectionErrors;
|
||||
parseResult.errors[index] = connectionObject.errors;
|
||||
|
||||
// Add this connection index to the list for each user
|
||||
_.forEach(connectionObject.users, identifier => {
|
||||
@@ -700,8 +749,10 @@ angular.module('import').factory('connectionParseService',
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Produce a ParseResult
|
||||
return parseConnectionData(importConfig, connectionData);
|
||||
// Produce a ParseResult, making sure that each record is converted to
|
||||
// the ImportConnection type before further parsing
|
||||
return parseConnectionData(importConfig, connectionData,
|
||||
[connection => new ImportConnection(connection)]);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -740,8 +791,10 @@ angular.module('import').factory('connectionParseService',
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Produce a ParseResult
|
||||
return parseConnectionData(importConfig, connectionData);
|
||||
// Produce a ParseResult, making sure that each record is converted to
|
||||
// the ImportConnection type before further parsing
|
||||
return parseConnectionData(importConfig, connectionData,
|
||||
[connection => new ImportConnection(connection)]);
|
||||
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user