mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-926: Improve error handling for invalid, missing connection data.
This commit is contained in:
@@ -322,10 +322,32 @@ angular.module('import').factory('connectionCSVService',
|
|||||||
// of the header
|
// of the header
|
||||||
const value = fetchFieldAtIndex(row);
|
const value = fetchFieldAtIndex(row);
|
||||||
|
|
||||||
|
// If no value is provided, do not check the validity
|
||||||
|
// of the parameter/attribute. Doing so would prevent
|
||||||
|
// the import of a list of mixed protocol types, where
|
||||||
|
// fields are only populated for protocols for which
|
||||||
|
// they are valid parameters. If a value IS provided,
|
||||||
|
// it must be a valid parameter or attribute for the
|
||||||
|
// current protocol, which will be checked below.
|
||||||
|
if (!value)
|
||||||
|
return {};
|
||||||
|
|
||||||
// The protocol may determine whether a field is
|
// The protocol may determine whether a field is
|
||||||
// a parameter or an attribute (or both)
|
// a parameter or an attribute (or both)
|
||||||
const protocol = transformConfig.protocolGetter(row);
|
const protocol = transformConfig.protocolGetter(row);
|
||||||
|
|
||||||
|
// Any errors encountered while processing this row
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
// Before checking whether it's an attribute or protocol,
|
||||||
|
// make sure this is a valid protocol to start
|
||||||
|
if (!protocolParameters[protocol])
|
||||||
|
|
||||||
|
// If the protocol is invalid, do not throw an error
|
||||||
|
// here - this will be handled further downstream
|
||||||
|
// by non-CSV-specific error handling
|
||||||
|
return {};
|
||||||
|
|
||||||
// Determine if the field refers to an attribute or a
|
// Determine if the field refers to an attribute or a
|
||||||
// parameter (or both, which is an error)
|
// parameter (or both, which is an error)
|
||||||
const isAttribute = !!attributes[name];
|
const isAttribute = !!attributes[name];
|
||||||
@@ -336,24 +358,24 @@ angular.module('import').factory('connectionCSVService',
|
|||||||
// parameter with the provided name, it's impossible to
|
// parameter with the provided name, it's impossible to
|
||||||
// figure out which this should be
|
// figure out which this should be
|
||||||
if (isAttribute && isParameter)
|
if (isAttribute && isParameter)
|
||||||
throw new ParseError({
|
errors.push(new ParseError({
|
||||||
message: 'Ambiguous CSV Header: ' + header,
|
message: 'Ambiguous CSV Header: ' + header,
|
||||||
key: 'IMPORT.ERROR_AMBIGUOUS_CSV_HEADER',
|
key: 'IMPORT.ERROR_AMBIGUOUS_CSV_HEADER',
|
||||||
variables: { HEADER: header }
|
variables: { HEADER: header }
|
||||||
});
|
}));
|
||||||
|
|
||||||
// It's neither an attribute or a parameter
|
// It's neither an attribute or a parameter
|
||||||
else if (!isAttribute && !isParameter)
|
else if (!isAttribute && !isParameter)
|
||||||
throw new ParseError({
|
errors.push(new ParseError({
|
||||||
message: 'Invalid CSV Header: ' + header,
|
message: 'Invalid CSV Header: ' + header,
|
||||||
key: 'IMPORT.ERROR_INVALID_CSV_HEADER',
|
key: 'IMPORT.ERROR_INVALID_CSV_HEADER',
|
||||||
variables: { HEADER: header }
|
variables: { HEADER: header }
|
||||||
});
|
}));
|
||||||
|
|
||||||
// Choose the appropriate type
|
// Choose the appropriate type
|
||||||
const type = isAttribute ? 'attributes' : 'parameters';
|
const type = isAttribute ? 'attributes' : 'parameters';
|
||||||
|
|
||||||
return { type, name, value };
|
return { type, name, value, errors };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -364,19 +386,20 @@ angular.module('import').factory('connectionCSVService',
|
|||||||
parameterOrAttributeGetters
|
parameterOrAttributeGetters
|
||||||
} = transformConfig;
|
} = transformConfig;
|
||||||
|
|
||||||
// Fail if the name wasn't provided
|
// Fail if the name wasn't provided. Note that this is a file-level
|
||||||
|
// error, not specific to any connection.
|
||||||
if (!nameGetter)
|
if (!nameGetter)
|
||||||
return deferred.reject(new ParseError({
|
throw new ParseError({
|
||||||
message: 'The connection name must be provided',
|
message: 'The connection name must be provided',
|
||||||
key: 'IMPORT.ERROR_REQUIRED_NAME'
|
key: 'IMPORT.ERROR_REQUIRED_NAME_FILE'
|
||||||
}));
|
});
|
||||||
|
|
||||||
// Fail if the protocol wasn't provided
|
// Fail if the protocol wasn't provided
|
||||||
if (!protocolGetter)
|
if (!protocolGetter)
|
||||||
return deferred.reject(new ParseError({
|
throw new ParseError({
|
||||||
message: 'The connection protocol must be provided',
|
message: 'The connection protocol must be provided',
|
||||||
key: 'IMPORT.ERROR_REQUIRED_PROTOCOL'
|
key: 'IMPORT.ERROR_REQUIRED_PROTOCOL_FILE'
|
||||||
}));
|
});
|
||||||
|
|
||||||
// The function to transform a CSV row into a connection object
|
// The function to transform a CSV row into a connection object
|
||||||
deferred.resolve(function transformCSVRow(row) {
|
deferred.resolve(function transformCSVRow(row) {
|
||||||
@@ -409,14 +432,20 @@ angular.module('import').factory('connectionCSVService',
|
|||||||
...parameterOrAttributeGetters.reduce((values, getter) => {
|
...parameterOrAttributeGetters.reduce((values, getter) => {
|
||||||
|
|
||||||
// Determine the type, name, and value
|
// Determine the type, name, and value
|
||||||
const { type, name, value } = getter(row);
|
const { type, name, value, errors } = getter(row);
|
||||||
|
|
||||||
// Set the value and continue on to the next attribute
|
// Set the value if available
|
||||||
// or parameter
|
if (type && name && value)
|
||||||
values[type][name] = value;
|
values[type][name] = value;
|
||||||
|
|
||||||
|
// If there were errors
|
||||||
|
if (errors && errors.length)
|
||||||
|
values.errors = [...values.errors, ...errors];
|
||||||
|
|
||||||
|
// Continue on to the next attribute or parameter
|
||||||
return values;
|
return values;
|
||||||
|
|
||||||
}, {parameters: {}, attributes: {}})
|
}, {parameters: {}, attributes: {}, errors: []})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -271,6 +271,13 @@ angular.module('import').factory('connectionParseService',
|
|||||||
// to be translated into an absolute path starting at the root
|
// to be translated into an absolute path starting at the root
|
||||||
group = connection.group;
|
group = connection.group;
|
||||||
|
|
||||||
|
// If the provided group isn't a string, it can never be valid
|
||||||
|
if (typeof group !== 'string')
|
||||||
|
throw new ParseError({
|
||||||
|
message: 'Invalid group type - must be a string',
|
||||||
|
key: 'IMPORT.ERROR_INVALID_GROUP_TYPE'
|
||||||
|
});
|
||||||
|
|
||||||
// Allow the group to start with a leading slash instead instead
|
// Allow the group to start with a leading slash instead instead
|
||||||
// of explicitly requiring the root connection group
|
// of explicitly requiring the root connection group
|
||||||
if (group.startsWith('/'))
|
if (group.startsWith('/'))
|
||||||
@@ -351,6 +358,84 @@ 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 {Promise.<Object.<String, Boolean>>}
|
||||||
|
* A promise that resolves to a set of all valid protocols.
|
||||||
|
*/
|
||||||
|
function getValidProtocols() {
|
||||||
|
|
||||||
|
// The current data source - the one that the connections will be
|
||||||
|
// imported into
|
||||||
|
const dataSource = $routeParams.dataSource;
|
||||||
|
|
||||||
|
// Fetch the protocols and convert to a set of valid protocol names
|
||||||
|
return schemaService.getProtocols(dataSource).then(
|
||||||
|
protocols => _.mapValues(protocols, () => true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
function getFieldErrors(connection, protocols) {
|
||||||
|
const connectionErrors = [];
|
||||||
|
|
||||||
|
// 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'
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 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 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 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 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'
|
||||||
|
}));
|
||||||
|
|
||||||
|
return connectionErrors;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a provided connection array into a ParseResult. Any provided
|
* Convert a provided connection array into a ParseResult. Any provided
|
||||||
* transform functions will be run on each entry in `connectionData` before
|
* transform functions will be run on each entry in `connectionData` before
|
||||||
@@ -384,8 +469,12 @@ angular.module('import').factory('connectionParseService',
|
|||||||
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
|
|
||||||
// Get the group transformer to apply to each connection
|
// Get the tree transformer and valid protocol set
|
||||||
return getTreeTransformer(importConfig).then(treeTransformer =>
|
return $q.all({
|
||||||
|
treeTransformer : getTreeTransformer(importConfig),
|
||||||
|
protocols : getValidProtocols()
|
||||||
|
})
|
||||||
|
.then(({treeTransformer, protocols}) =>
|
||||||
connectionData.reduce((parseResult, data) => {
|
connectionData.reduce((parseResult, data) => {
|
||||||
|
|
||||||
const { patches, users, groups, groupPaths } = parseResult;
|
const { patches, users, groups, groupPaths } = parseResult;
|
||||||
@@ -396,8 +485,13 @@ angular.module('import').factory('connectionParseService',
|
|||||||
connectionObject = transform(connectionObject);
|
connectionObject = transform(connectionObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
// All errors found while parsing this connection
|
// All errors encountered while running the connection through the
|
||||||
const connectionErrors = [];
|
// provided transform, starting with those encountered during
|
||||||
|
// the provided transforms, and any errors from missing fields
|
||||||
|
const connectionErrors = [
|
||||||
|
..._.get(connectionObject, 'errors', []),
|
||||||
|
...getFieldErrors(connectionObject, protocols)
|
||||||
|
];
|
||||||
|
|
||||||
// Determine the connection's place in the connection group tree
|
// Determine the connection's place in the connection group tree
|
||||||
try {
|
try {
|
||||||
|
@@ -114,6 +114,13 @@ angular.module('import').factory('ImportConnection', ['$injector',
|
|||||||
*/
|
*/
|
||||||
this.importMode = template.importMode || ImportConnection.ImportMode.CREATE;
|
this.importMode = template.importMode || ImportConnection.ImportMode.CREATE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any errors specific to this connection encountered while parsing.
|
||||||
|
*
|
||||||
|
* @type ParseError[]
|
||||||
|
*/
|
||||||
|
this.errors = template.errors || [];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -211,13 +211,19 @@
|
|||||||
"ERROR_INVALID_MIME_TYPE" : "Unsupported file type: \"{TYPE}\"",
|
"ERROR_INVALID_MIME_TYPE" : "Unsupported file type: \"{TYPE}\"",
|
||||||
"ERROR_INVALID_GROUP" : "No group matching \"{GROUP}\" found",
|
"ERROR_INVALID_GROUP" : "No group matching \"{GROUP}\" found",
|
||||||
"ERROR_INVALID_GROUP_IDENTIFIER" : "No connection group with identifier \"{IDENTIFIER}\" found",
|
"ERROR_INVALID_GROUP_IDENTIFIER" : "No connection group with identifier \"{IDENTIFIER}\" found",
|
||||||
|
"ERROR_INVALID_GROUP_TYPE" : "Invalid group - must be a string.",
|
||||||
|
"ERROR_INVALID_PROTOCOL" : "Invalid protocol \"{PROTOCOL}\"",
|
||||||
|
"ERROR_INVALID_USER_GROUPS_TYPE" : "Invalid user groups - must be an array of user group identifiers.",
|
||||||
|
"ERROR_INVALID_USERS_TYPE" : "Invalid users - must be an array of user identifiers.",
|
||||||
"ERROR_NO_FILE_SUPPLIED" : "Please select a file to import",
|
"ERROR_NO_FILE_SUPPLIED" : "Please select a file to import",
|
||||||
"ERROR_PARSE_FAILURE_CSV" : "Please make sure your file is valid CSV. Parsing failed with error \"{ERROR}\". ",
|
"ERROR_PARSE_FAILURE_CSV" : "Please make sure your file is valid CSV. Parsing failed with error \"{ERROR}\". ",
|
||||||
"ERROR_PARSE_FAILURE_JSON" : "Please make sure your file is valid JSON. Parsing failed with error \"{ERROR}\". ",
|
"ERROR_PARSE_FAILURE_JSON" : "Please make sure your file is valid JSON. Parsing failed with error \"{ERROR}\". ",
|
||||||
"ERROR_PARSE_FAILURE_YAML" : "Please make sure your file is valid YAML. Parsing failed with error \"{ERROR}\". ",
|
"ERROR_PARSE_FAILURE_YAML" : "Please make sure your file is valid YAML. Parsing failed with error \"{ERROR}\". ",
|
||||||
"ERROR_REJECT_UPDATE_CONNECTION" : "Connection \"{NAME}\" already exists at \"{PATH}\"",
|
"ERROR_REJECT_UPDATE_CONNECTION" : "Connection \"{NAME}\" already exists at \"{PATH}\"",
|
||||||
"ERROR_REQUIRED_NAME" : "No connection name found in the provided file",
|
"ERROR_REQUIRED_NAME_CONNECTION" : "The connection name is required",
|
||||||
"ERROR_REQUIRED_PROTOCOL" : "No connection protocol found in the provided file",
|
"ERROR_REQUIRED_PROTOCOL_CONNECTION" : "The connection protocol is required",
|
||||||
|
"ERROR_REQUIRED_NAME_FILE" : "No connection name found in the provided file",
|
||||||
|
"ERROR_REQUIRED_PROTOCOL_FILE" : "No connection protocol found in the provided file",
|
||||||
|
|
||||||
"FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
|
"FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user