From 638365ccffe5dc3b764498f0433935aa8ef51c3e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 Apr 2015 12:36:39 -0700 Subject: [PATCH 01/16] GUAC-1161: Generalize protocol parameters into form parameters. --- .../glyptodon/guacamole/form/Parameter.java | 283 ++++++++++++++++++ .../ParameterOption.java} | 49 ++- .../guacamole/protocols/ProtocolInfo.java | 59 +++- .../protocols/ProtocolParameter.java | 192 ------------ .../xml/protocol/OptionTagHandler.java | 10 +- .../xml/protocol/ParamTagHandler.java | 28 +- .../xml/protocol/ProtocolTagHandler.java | 2 +- 7 files changed, 395 insertions(+), 228 deletions(-) create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/form/Parameter.java rename guacamole-ext/src/main/java/org/glyptodon/guacamole/{protocols/ProtocolParameterOption.java => form/ParameterOption.java} (60%) delete mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolParameter.java diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/form/Parameter.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/form/Parameter.java new file mode 100644 index 000000000..033749b88 --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/form/Parameter.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2013 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.form; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Represents an arbitrary parameter, such as an HTTP parameter, or the + * parameter of a remote desktop protocol. + * + * @author Michael Jumper + */ +public class Parameter { + + /** + * All possible types of parameter. + */ + public enum Type { + + /** + * A text parameter, accepting arbitrary values. + */ + TEXT, + + /** + * A username parameter. This parameter type generally behaves + * identically to arbitrary text parameters, but has semantic + * differences. If credential pass-through is in use, the value for this + * parameter may be automatically provided using the credentials + * originally used by the user to authenticate. + */ + USERNAME, + + /** + * A password parameter, whose value is sensitive and must be hidden. If + * credential pass-through is in use, the value for this parameter may + * be automatically provided using the credentials originally used by + * the user to authenticate. + */ + PASSWORD, + + /** + * A numeric parameter, whose value must contain only digits. + */ + NUMERIC, + + /** + * A boolean parameter, whose value is either blank or "true". + */ + BOOLEAN, + + /** + * An enumerated parameter, whose legal values are fully enumerated + * by a provided, finite list. + */ + ENUM, + + /** + * A text parameter that can span more than one line. + */ + MULTILINE + + } + + /** + * The unique name that identifies this parameter. + */ + private String name; + + /** + * A human-readable name to be presented to the user. + */ + private String title; + + /** + * The type of this parameter. + */ + private Type type; + + /** + * The value of this parameter, when checked. This is only applicable to + * BOOLEAN parameters. + */ + private String value; + + /** + * A collection of all associated parameter options. + */ + private Collection options; + + /** + * Creates a new Parameter with no associated name, title, or type. + */ + public Parameter() { + this.options = new ArrayList(); + } + + /** + * Creates a new Parameter with the given name, title, and type. + * + * @param name + * The unique name to associate with this parameter. + * + * @param title + * The human-readable title to associate with this parameter. + * + * @param type + * The type of this parameter. + */ + public Parameter(String name, String title, Type type) { + this.name = name; + this.title = title; + this.type = type; + this.options = new ArrayList(); + } + + /** + * Creates a new BOOLEAN Parameter with the given name, title, and value. + * + * @param name + * The unique name to associate with this parameter. + * + * @param title + * The human-readable title to associate with this parameter. + * + * @param value + * The value that should be assigned to this parameter if enabled. + */ + public Parameter(String name, String title, String value) { + this.name = name; + this.title = title; + this.type = Type.BOOLEAN; + this.value = value; + this.options = new ArrayList(); + } + + /** + * Creates a new ENUM Parameter with the given name, title, and options. + * + * @param name + * The unique name to associate with this parameter. + * + * @param title + * The human-readable title to associate with this parameter. + * + * @param options + * A collection of all possible valid options for this parameter. + */ + public Parameter(String name, String title, Collection options) { + this.name = name; + this.title = title; + this.type = Type.ENUM; + this.options = options; + } + + /** + * Returns the unique name associated with this parameter. + * + * @return + * The unique name associated with this parameter. + */ + public String getName() { + return name; + } + + /** + * Sets the unique name associated with this parameter. + * + * @param name + * The unique name to assign to this parameter. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the human-readable title associated with this parameter. + * + * @return + * The human-readable title associated with this parameter. + */ + public String getTitle() { + return title; + } + + /** + * Sets the title associated with this parameter. The title must be a + * human-readable string which describes accurately this parameter. + * + * @param title + * A human-readable string describing this parameter. + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * Returns the value that should be assigned to this parameter if enabled. + * This is only applicable to BOOLEAN parameters. + * + * @return + * The value that should be assigned to this parameter if enabled. + */ + public String getValue() { + return value; + } + + /** + * Sets the value that should be assigned to this parameter if enabled. + * This is only applicable to BOOLEAN parameters. + * + * @param value + * The value that should be assigned to this parameter if enabled. + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Returns the type of this parameter. + * + * @return + * The type of this parameter. + */ + public Type getType() { + return type; + } + + /** + * Sets the type of this parameter. + * + * @param type + * The type of this parameter. + */ + public void setType(Type type) { + this.type = type; + } + + /** + * Returns a mutable collection of parameter options. Changes to this + * collection directly affect the available options. This is only + * applicable to ENUM parameters. + * + * @return + * A mutable collection of parameter options. + */ + public Collection getOptions() { + return options; + } + + /** + * Sets the options available as possible values of this parameter. This + * is only applicable to ENUM parameters. + * + * @param options + * The options to associate with this parameter. + */ + public void setOptions(Collection options) { + this.options = options; + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolParameterOption.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/form/ParameterOption.java similarity index 60% rename from guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolParameterOption.java rename to guacamole-ext/src/main/java/org/glyptodon/guacamole/form/ParameterOption.java index 52d233a16..a722749bf 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolParameterOption.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/form/ParameterOption.java @@ -20,18 +20,17 @@ * THE SOFTWARE. */ -package org.glyptodon.guacamole.protocols; +package org.glyptodon.guacamole.form; /** - * Describes an available legal value for an enumerated protocol parameter. + * Describes an available legal value for an enumerated parameter. * * @author Michael Jumper */ -public class ProtocolParameterOption { +public class ParameterOption { /** - * The value that will be sent to the client plugin if this option is - * chosen. + * The value that will be assigned if this option is chosen. */ private String value; @@ -41,20 +40,40 @@ public class ProtocolParameterOption { private String title; /** - * Returns the value that will be sent to the client plugin if this option - * is chosen. + * Creates a new ParameterOption with no associated value or title. + */ + public ParameterOption() { + } + + /** + * Creates a new ParameterOption having the given value and title. * - * @return The value that will be sent if this option is chosen. + * @param value + * The value to assign if this option is chosen. + * + * @param title + * The human-readable title to associate with this option. + */ + public ParameterOption(String value, String title) { + this.value = value; + this.title = title; + } + + /** + * Returns the value that will be assigned if this option is chosen. + * + * @return + * The value that will be assigned if this option is chosen. */ public String getValue() { return value; } /** - * Sets the value that will be sent to the client plugin if this option is - * chosen. + * Sets the value that will be assigned if this option is chosen. * - * @param value The value to send if this option is chosen. + * @param value + * The value to assign if this option is chosen. */ public void setValue(String value) { this.value = value; @@ -62,7 +81,9 @@ public class ProtocolParameterOption { /** * Returns the human-readable title describing the effect of this option. - * @return The human-readable title describing the effect of this option. + * + * @return + * The human-readable title describing the effect of this option. */ public String getTitle() { return title; @@ -70,7 +91,9 @@ public class ProtocolParameterOption { /** * Sets the human-readable title describing the effect of this option. - * @param title A human-readable title describing the effect of this option. + * + * @param title + * A human-readable title describing the effect of this option. */ public void setTitle(String title) { this.title = title; diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolInfo.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolInfo.java index ad3b5e7d4..a5a9c3839 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolInfo.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolInfo.java @@ -24,6 +24,7 @@ package org.glyptodon.guacamole.protocols; import java.util.ArrayList; import java.util.Collection; +import org.glyptodon.guacamole.form.Parameter; /** * Describes a protocol and all parameters associated with it, as required by @@ -47,8 +48,49 @@ public class ProtocolInfo { /** * A collection of all associated protocol parameters. */ - private Collection parameters = - new ArrayList(); + private Collection parameters; + + /** + * Creates a new ProtocolInfo with no associated name, title, or + * parameters. + */ + public ProtocolInfo() { + this.parameters = new ArrayList(); + } + + /** + * Creates a new ProtocolInfo having the given name and title, but without + * any parameters. + * + * @param name + * The unique name associated with the protocol. + * + * @param title + * The human-readable title to associate with the protocol. + */ + public ProtocolInfo(String name, String title) { + this.name = name; + this.title = title; + this.parameters = new ArrayList(); + } + + /** + * Creates a new ProtocolInfo having the given name, title, and parameters. + * + * @param name + * The unique name associated with the protocol. + * + * @param title + * The human-readable title to associate with the protocol. + * + * @param parameters + * The parameters to associate with the protocol. + */ + public ProtocolInfo(String name, String title, Collection parameters) { + this.name = name; + this.title = title; + this.parameters = parameters; + } /** * Returns the human-readable title associated with this protocol. @@ -95,8 +137,19 @@ public class ProtocolInfo { * * @return A mutable collection of protocol parameters. */ - public Collection getParameters() { + public Collection getParameters() { return parameters; } + /** + * Sets the collection of protocol parameters associated with this + * protocol. + * + * @param parameters + * The collection of parameters to associate with this protocol. + */ + public void setParameters(Collection parameters) { + this.parameters = parameters; + } + } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolParameter.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolParameter.java deleted file mode 100644 index 40b59a497..000000000 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/protocols/ProtocolParameter.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2013 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package org.glyptodon.guacamole.protocols; - -import java.util.ArrayList; -import java.util.Collection; - -/** - * Represents a parameter of a protocol. - * - * @author Michael Jumper - */ -public class ProtocolParameter { - - /** - * All possible types of protocol parameter. - */ - public enum Type { - - /** - * A text parameter, accepting arbitrary values. - */ - TEXT, - - /** - * A username parameter. This parameter type generally behaves - * identically to arbitrary text parameters, but has semantic - * differences. If credential pass-through is in use, the value for this - * parameter may be automatically provided using the credentials - * originally used by the user to authenticate. - */ - USERNAME, - - /** - * A password parameter, whose value is sensitive and must be hidden. If - * credential pass-through is in use, the value for this parameter may - * be automatically provided using the credentials originally used by - * the user to authenticate. - */ - PASSWORD, - - /** - * A numeric parameter, whose value must contain only digits. - */ - NUMERIC, - - /** - * A boolean parameter, whose value is either blank or "true". - */ - BOOLEAN, - - /** - * An enumerated parameter, whose legal values are fully enumerated - * by a provided, finite list. - */ - ENUM, - - /** - * A text parameter that can span more than one line. - */ - MULTILINE - - } - - /** - * The unique name that identifies this parameter to the protocol plugin. - */ - private String name; - - /** - * A human-readable name to be presented to the user. - */ - private String title; - - /** - * The type of this field. - */ - private Type type; - - /** - * The value of this parameter, for boolean parameters. - */ - private String value; - - /** - * A collection of all associated parameter options. - */ - private Collection options = - new ArrayList(); - - /** - * Returns the name associated with this protocol parameter. - * @return The name associated with this protocol parameter. - */ - public String getName() { - return name; - } - - /** - * Sets the name associated with this protocol parameter. This name must - * uniquely identify this parameter among the others accepted by the - * corresponding protocol. - * - * @param name The name to assign to this protocol parameter. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Returns the title associated with this protocol parameter. - * @return The title associated with this protocol parameter. - */ - public String getTitle() { - return title; - } - - /** - * Sets the title associated with this protocol parameter. The title must - * be a human-readable string which describes accurately this parameter. - * - * @param title A human-readable string describing this parameter. - */ - public void setTitle(String title) { - this.title = title; - } - - /** - * Returns the value associated with this protocol parameter. - * @return The value associated with this protocol parameter. - */ - public String getValue() { - return value; - } - - /** - * Sets the value associated with this protocol parameter. The value must - * be a human-readable string which describes accurately this parameter. - * - * @param value A human-readable string describing this parameter. - */ - public void setValue(String value) { - this.value = value; - } - - /** - * Returns the type of this parameter. - * @return The type of this parameter. - */ - public Type getType() { - return type; - } - - /** - * Sets the type of this parameter. - * @param type The type of this parameter. - */ - public void setType(Type type) { - this.type = type; - } - - /** - * Returns a mutable collection of protocol parameter options. Changes to - * this collection directly affect the available options. - * - * @return A mutable collection of parameter options. - */ - public Collection getOptions() { - return options; - } - -} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/OptionTagHandler.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/OptionTagHandler.java index ce4299081..89743681e 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/OptionTagHandler.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/OptionTagHandler.java @@ -22,7 +22,7 @@ package org.glyptodon.guacamole.xml.protocol; -import org.glyptodon.guacamole.protocols.ProtocolParameterOption; +import org.glyptodon.guacamole.form.ParameterOption; import org.glyptodon.guacamole.xml.TagHandler; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -37,7 +37,7 @@ public class OptionTagHandler implements TagHandler { /** * The option backing this option tag. */ - private ProtocolParameterOption option = new ProtocolParameterOption(); + private ParameterOption option = new ParameterOption(); @Override public void init(Attributes attributes) throws SAXException { @@ -55,10 +55,10 @@ public class OptionTagHandler implements TagHandler { } /** - * Returns the ProtocolParameterOption backing this tag. - * @return The ProtocolParameterOption backing this tag. + * Returns the ParameterOption backing this tag. + * @return The ParameterOption backing this tag. */ - public ProtocolParameterOption asProtocolParameterOption() { + public ParameterOption asParameterOption() { return option; } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/ParamTagHandler.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/ParamTagHandler.java index cb59c04ed..f927891a6 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/ParamTagHandler.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/ParamTagHandler.java @@ -22,7 +22,7 @@ package org.glyptodon.guacamole.xml.protocol; -import org.glyptodon.guacamole.protocols.ProtocolParameter; +import org.glyptodon.guacamole.form.Parameter; import org.glyptodon.guacamole.xml.TagHandler; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -35,9 +35,9 @@ import org.xml.sax.SAXException; public class ParamTagHandler implements TagHandler { /** - * The ProtocolParameter backing this tag handler. + * The Parameter backing this tag handler. */ - private ProtocolParameter protocolParameter = new ProtocolParameter(); + private Parameter protocolParameter = new Parameter(); @Override public void init(Attributes attributes) throws SAXException { @@ -51,31 +51,31 @@ public class ParamTagHandler implements TagHandler { // Text field if ("text".equals(type)) - protocolParameter.setType(ProtocolParameter.Type.TEXT); + protocolParameter.setType(Parameter.Type.TEXT); // Numeric field else if ("numeric".equals(type)) - protocolParameter.setType(ProtocolParameter.Type.NUMERIC); + protocolParameter.setType(Parameter.Type.NUMERIC); // Username field else if ("username".equals(type)) - protocolParameter.setType(ProtocolParameter.Type.USERNAME); + protocolParameter.setType(Parameter.Type.USERNAME); // Password field else if ("password".equals(type)) - protocolParameter.setType(ProtocolParameter.Type.PASSWORD); + protocolParameter.setType(Parameter.Type.PASSWORD); // Enumerated field else if ("enum".equals(type)) - protocolParameter.setType(ProtocolParameter.Type.ENUM); + protocolParameter.setType(Parameter.Type.ENUM); // Multiline field else if ("multiline".equals(type)) - protocolParameter.setType(ProtocolParameter.Type.MULTILINE); + protocolParameter.setType(Parameter.Type.MULTILINE); // Boolean field else if ("boolean".equals(type)) { - protocolParameter.setType(ProtocolParameter.Type.BOOLEAN); + protocolParameter.setType(Parameter.Type.BOOLEAN); if(protocolParameter.getValue() == null) throw new SAXException @@ -99,7 +99,7 @@ public class ParamTagHandler implements TagHandler { // Store stub in options collection protocolParameter.getOptions().add( - tagHandler.asProtocolParameterOption()); + tagHandler.asParameterOption()); return tagHandler; } @@ -114,10 +114,10 @@ public class ParamTagHandler implements TagHandler { } /** - * Returns the ProtocolParameter backing this tag. - * @return The ProtocolParameter backing this tag. + * Returns the Parameter backing this tag. + * @return The Parameter backing this tag. */ - public ProtocolParameter asProtocolParameter() { + public Parameter asParameter() { return protocolParameter; } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/ProtocolTagHandler.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/ProtocolTagHandler.java index 562784509..38da5030f 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/ProtocolTagHandler.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/xml/protocol/ProtocolTagHandler.java @@ -56,7 +56,7 @@ public class ProtocolTagHandler implements TagHandler { ParamTagHandler tagHandler = new ParamTagHandler(); // Store stub in parameters collection - info.getParameters().add(tagHandler.asProtocolParameter()); + info.getParameters().add(tagHandler.asParameter()); return tagHandler; } From ae96de95a602c0aec7d7b9cb45feaf095c11f640 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 Apr 2015 12:56:22 -0700 Subject: [PATCH 02/16] GUAC-1161: Add CredentialsInfo and credential-specific exceptions, --- .../net/auth/credentials/CredentialsInfo.java | 79 ++++++++++++++ .../GuacamoleCredentialsException.java | 100 ++++++++++++++++++ ...amoleInsufficientCredentialsException.java | 82 ++++++++++++++ .../GuacamoleInvalidCredentialsException.java | 80 ++++++++++++++ 4 files changed, 341 insertions(+) create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/CredentialsInfo.java create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleCredentialsException.java create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleInsufficientCredentialsException.java create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleInvalidCredentialsException.java diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/CredentialsInfo.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/CredentialsInfo.java new file mode 100644 index 000000000..91a415fba --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/CredentialsInfo.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.auth.credentials; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import org.glyptodon.guacamole.form.Parameter; + +/** + * Information which describes a set of valid credentials. + * + * @author Michael Jumper + */ +public class CredentialsInfo { + + /** + * All parameters required for valid credentials. + */ + private final Collection parameters; + + /** + * Creates a new CredentialsInfo object which requires the given parameters + * for any conforming credentials. + * + * @param parameters + * The parameters to require. + */ + public CredentialsInfo(Collection parameters) { + this.parameters = parameters; + } + + /** + * Returns all parameters required for valid credentials as described by + * this object. + * + * @return + * All parameters required for valid credentials. + */ + public Collection getParameters() { + return Collections.unmodifiableCollection(parameters); + } + + /** + * CredentialsInfo object which describes empty credentials. No parameters + * are required. + */ + public static final CredentialsInfo EMPTY = new CredentialsInfo(Collections.EMPTY_LIST); + + /** + * CredentialsInfo object which describes standard username/password + * credentials. + */ + public static final CredentialsInfo USERNAME_PASSWORD = new CredentialsInfo(Arrays.asList( + new Parameter("username", "username", Parameter.Type.USERNAME), + new Parameter("password", "password", Parameter.Type.PASSWORD) + )); + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleCredentialsException.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleCredentialsException.java new file mode 100644 index 000000000..55fffea34 --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleCredentialsException.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.auth.credentials; + +import org.glyptodon.guacamole.GuacamoleSecurityException; + +/** + * A security-related exception thrown when access is denied to a user because + * of a problem related to the provided credentials. Additional information + * describing the form of valid credentials is provided. + * + * @author Michael Jumper + */ +public class GuacamoleCredentialsException extends GuacamoleSecurityException { + + /** + * Information describing the form of valid credentials. + */ + private final CredentialsInfo credentialsInfo; + + /** + * Creates a new GuacamoleInvalidCredentialsException with the given + * message, cause, and associated credential information. + * + * @param message + * A human readable description of the exception that occurred. + * + * @param cause + * The cause of this exception. + * + * @param credentialsInfo + * Information describing the form of valid credentials. + */ + public GuacamoleCredentialsException(String message, Throwable cause, + CredentialsInfo credentialsInfo) { + super(message, cause); + this.credentialsInfo = credentialsInfo; + } + + /** + * Creates a new GuacamoleInvalidCredentialsException with the given + * message and associated credential information. + * + * @param message + * A human readable description of the exception that occurred. + * + * @param credentialsInfo + * Information describing the form of valid credentials. + */ + public GuacamoleCredentialsException(String message, CredentialsInfo credentialsInfo) { + super(message); + this.credentialsInfo = credentialsInfo; + } + + /** + * Creates a new GuacamoleInvalidCredentialsException with the given cause + * and associated credential information. + * + * @param cause + * The cause of this exception. + * + * @param credentialsInfo + * Information describing the form of valid credentials. + */ + public GuacamoleCredentialsException(Throwable cause, CredentialsInfo credentialsInfo) { + super(cause); + this.credentialsInfo = credentialsInfo; + } + + /** + * Returns information describing the form of valid credentials. + * + * @return + * Information describing the form of valid credentials. + */ + public CredentialsInfo getCredentialsInfo() { + return credentialsInfo; + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleInsufficientCredentialsException.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleInsufficientCredentialsException.java new file mode 100644 index 000000000..ea8675a3b --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleInsufficientCredentialsException.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.auth.credentials; + +/** + * A security-related exception thrown when access is denied to a user because + * the provided credentials are not sufficient for authentication to succeed. + * The validity or invalidity of the given credentials is not specified, and + * more information is needed before a decision can be made. Additional + * information describing the form of valid credentials is provided. + * + * @author Michael Jumper + */ +public class GuacamoleInsufficientCredentialsException extends GuacamoleCredentialsException { + + /** + * Creates a new GuacamoleInsufficientCredentialsException with the given + * message, cause, and associated credential information. + * + * @param message + * A human readable description of the exception that occurred. + * + * @param cause + * The cause of this exception. + * + * @param credentialsInfo + * Information describing the form of valid credentials. + */ + public GuacamoleInsufficientCredentialsException(String message, Throwable cause, + CredentialsInfo credentialsInfo) { + super(message, cause, credentialsInfo); + } + + /** + * Creates a new GuacamoleInsufficientCredentialsException with the given + * message and associated credential information. + * + * @param message + * A human readable description of the exception that occurred. + * + * @param credentialsInfo + * Information describing the form of valid credentials. + */ + public GuacamoleInsufficientCredentialsException(String message, CredentialsInfo credentialsInfo) { + super(message, credentialsInfo); + } + + /** + * Creates a new GuacamoleInsufficientCredentialsException with the given + * cause and associated credential information. + * + * @param cause + * The cause of this exception. + * + * @param credentialsInfo + * Information describing the form of valid credentials. + */ + public GuacamoleInsufficientCredentialsException(Throwable cause, CredentialsInfo credentialsInfo) { + super(cause, credentialsInfo); + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleInvalidCredentialsException.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleInvalidCredentialsException.java new file mode 100644 index 000000000..903b82bef --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/credentials/GuacamoleInvalidCredentialsException.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.auth.credentials; + +/** + * A security-related exception thrown when access is denied to a user because + * the provided credentials are invalid. Additional information describing + * the form of valid credentials is provided. + * + * @author Michael Jumper + */ +public class GuacamoleInvalidCredentialsException extends GuacamoleCredentialsException { + + /** + * Creates a new GuacamoleInvalidCredentialsException with the given + * message, cause, and associated credential information. + * + * @param message + * A human readable description of the exception that occurred. + * + * @param cause + * The cause of this exception. + * + * @param credentialsInfo + * Information describing the form of valid credentials. + */ + public GuacamoleInvalidCredentialsException(String message, Throwable cause, + CredentialsInfo credentialsInfo) { + super(message, cause, credentialsInfo); + } + + /** + * Creates a new GuacamoleInvalidCredentialsException with the given + * message and associated credential information. + * + * @param message + * A human readable description of the exception that occurred. + * + * @param credentialsInfo + * Information describing the form of valid credentials. + */ + public GuacamoleInvalidCredentialsException(String message, CredentialsInfo credentialsInfo) { + super(message, credentialsInfo); + } + + /** + * Creates a new GuacamoleInvalidCredentialsException with the given cause + * and associated credential information. + * + * @param cause + * The cause of this exception. + * + * @param credentialsInfo + * Information describing the form of valid credentials. + */ + public GuacamoleInvalidCredentialsException(Throwable cause, CredentialsInfo credentialsInfo) { + super(cause, credentialsInfo); + } + +} From 2537b3d8eeda63eaa81182b6a33b672cf28a7797 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 Apr 2015 13:07:07 -0700 Subject: [PATCH 03/16] GUAC-1161: Request username/password if no user context is generated (backwards compat). --- .../net/basic/rest/auth/TokenRESTService.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java index c7f7da6e8..af7d01b9e 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java @@ -41,6 +41,8 @@ import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.UserContext; +import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; +import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; import org.glyptodon.guacamole.net.basic.GuacamoleSession; import org.glyptodon.guacamole.net.basic.rest.APIRequest; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; @@ -233,15 +235,13 @@ public class TokenRESTService { } + // Request standard username/password if no user context was produced + if (userContext == null) + throw new GuacamoleInvalidCredentialsException("Permission Denied.", + CredentialsInfo.USERNAME_PASSWORD); + } - catch(GuacamoleException e) { - logger.error("Exception caught while authenticating user.", e); - throw new HTTPException(Status.INTERNAL_SERVER_ERROR, - "Unexpected server error."); - } - - // Authentication failed. - if (userContext == null) { + catch (GuacamoleException e) { // Log authentication failures with associated usernames if (username != null) { @@ -255,10 +255,9 @@ public class TokenRESTService { logger.debug("Anonymous authentication attempt from {} failed.", getLoggableAddress(request), username); - throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); - + throw e; } - + // Update existing session, if it exists String authToken; if (existingSession != null) { From c80a203e976d0c35cc643c3acb0e8e8deed35ad3 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 Apr 2015 13:33:20 -0700 Subject: [PATCH 04/16] GUAC-1161: Include credential information in REST responses. --- .../net/basic/rest/APICredentialError.java | 102 ++++++++++++++++++ .../AuthProviderRESTExceptionWrapper.java | 74 +++++++++++-- 2 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APICredentialError.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APICredentialError.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APICredentialError.java new file mode 100644 index 000000000..f60b7028e --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APICredentialError.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.basic.rest; + +import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; + +/** + * Represents an error related to either invalid or insufficient credentials + * submitted to a REST endpoint. + * + * @author Michael Jumper + */ +public class APICredentialError extends APIError { + + /** + * The required credentials. + */ + private final CredentialsInfo info; + + /** + * The type of error that occurred. + */ + private final Type type; + + /** + * All possible types of credential errors. + */ + public enum Type { + + /** + * The credentials provided were invalid. + */ + INVALID, + + /** + * The credentials provided were not necessarily invalid, but were not + * sufficient to determine validity. + */ + INSUFFICIENT + + } + + /** + * Create a new APICredentialError with the specified error message and + * credentials information. + * + * @param type + * The type of error that occurred. + * + * @param message + * The error message. + * + * @param info + * An object which describes the required credentials. + */ + public APICredentialError(Type type, String message, CredentialsInfo info) { + super(message); + this.type = type; + this.info = info; + } + + /** + * Returns the type of error that occurred. + * + * @return + * The type of error that occurred. + */ + public Type getType() { + return type; + } + + /** + * Returns an object which describes the required credentials. + * + * @return + * An object which describes the required credentials. + */ + public CredentialsInfo getInfo() { + return info; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java index 38ec68b5d..8d344c68d 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java @@ -28,6 +28,9 @@ import org.aopalliance.intercept.MethodInvocation; import org.glyptodon.guacamole.GuacamoleClientException; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; +import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; +import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,15 +52,72 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { try { return invocation.proceed(); } - catch(GuacamoleSecurityException e) { - throw new HTTPException(Response.Status.FORBIDDEN, e.getMessage() != null ? e.getMessage() : "Permission denied."); + + // Additional credentials are needed + catch (GuacamoleInsufficientCredentialsException e) { + + // Generate default message + String message = e.getMessage(); + if (message == null) + message = "Permission denied."; + + throw new HTTPException(Response.Status.FORBIDDEN, new APICredentialError( + APICredentialError.Type.INSUFFICIENT, + message, + e.getCredentialsInfo() + )); } - catch(GuacamoleClientException e) { - throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + + // The provided credentials are wrong + catch (GuacamoleInvalidCredentialsException e) { + + // Generate default message + String message = e.getMessage(); + if (message == null) + message = "Permission denied."; + + throw new HTTPException(Response.Status.FORBIDDEN, new APICredentialError( + APICredentialError.Type.INVALID, + message, + e.getCredentialsInfo() + )); } - catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught while executing " + invocation.getMethod().getName() + ".", e); - throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + + // Generic permission denied + catch (GuacamoleSecurityException e) { + + // Generate default message + String message = e.getMessage(); + if (message == null) + message = "Permission denied."; + + throw new HTTPException(Response.Status.FORBIDDEN, message); + + } + + // Arbitrary bad requests + catch (GuacamoleClientException e) { + + // Generate default message + String message = e.getMessage(); + if (message == null) + message = "Invalid request."; + + throw new HTTPException(Response.Status.BAD_REQUEST, message); + + } + + // All other errors + catch (GuacamoleException e) { + + // Generate default message + String message = e.getMessage(); + if (message == null) + message = "Unexpected server error."; + + logger.debug("Unexpected exception in REST endpoint.", e); + throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, message); + } } From 74883ae121c9dbdf357911f31149754076a0df11 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 Apr 2015 14:43:55 -0700 Subject: [PATCH 05/16] GUAC-1161: Generalize APICredentialError into APIError. Provide consistent error responses for all REST endpoints. --- .../net/basic/rest/APICredentialError.java | 102 ------------ .../guacamole/net/basic/rest/APIError.java | 156 ++++++++++++++++-- .../AuthProviderRESTExceptionWrapper.java | 51 ++++-- .../net/basic/rest/HTTPException.java | 67 +++++--- .../net/basic/rest/auth/TokenRESTService.java | 4 +- .../net/basic/rest/user/UserRESTService.java | 13 +- .../src/main/webapp/app/rest/types/Error.js | 122 ++++++++++++++ 7 files changed, 358 insertions(+), 157 deletions(-) delete mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APICredentialError.java create mode 100644 guacamole/src/main/webapp/app/rest/types/Error.js diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APICredentialError.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APICredentialError.java deleted file mode 100644 index f60b7028e..000000000 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APICredentialError.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2015 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package org.glyptodon.guacamole.net.basic.rest; - -import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; - -/** - * Represents an error related to either invalid or insufficient credentials - * submitted to a REST endpoint. - * - * @author Michael Jumper - */ -public class APICredentialError extends APIError { - - /** - * The required credentials. - */ - private final CredentialsInfo info; - - /** - * The type of error that occurred. - */ - private final Type type; - - /** - * All possible types of credential errors. - */ - public enum Type { - - /** - * The credentials provided were invalid. - */ - INVALID, - - /** - * The credentials provided were not necessarily invalid, but were not - * sufficient to determine validity. - */ - INSUFFICIENT - - } - - /** - * Create a new APICredentialError with the specified error message and - * credentials information. - * - * @param type - * The type of error that occurred. - * - * @param message - * The error message. - * - * @param info - * An object which describes the required credentials. - */ - public APICredentialError(Type type, String message, CredentialsInfo info) { - super(message); - this.type = type; - this.info = info; - } - - /** - * Returns the type of error that occurred. - * - * @return - * The type of error that occurred. - */ - public Type getType() { - return type; - } - - /** - * Returns an object which describes the required credentials. - * - * @return - * An object which describes the required credentials. - */ - public CredentialsInfo getInfo() { - return info; - } - -} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java index f08c2e402..3c62725c8 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Glyptodon LLC + * Copyright (C) 2015 Glyptodon LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,31 +22,161 @@ package org.glyptodon.guacamole.net.basic.rest; +import java.util.Collection; +import javax.ws.rs.core.Response; +import org.glyptodon.guacamole.form.Parameter; + /** - * A simple object to represent an error to be sent from the REST API. + * Describes an error that occurred within a REST endpoint. + * * @author James Muehlner + * @author Michael Jumper */ public class APIError { - + /** * The error message. */ private final String message; /** - * Get the error message. - * @return The error message. + * All expected request parameters, if any. + */ + private final Collection expected; + + /** + * The type of error that occurred. + */ + private final Type type; + + /** + * All possible types of REST API errors. + */ + public enum Type { + + /** + * The requested operation could not be performed because the request + * itself was malformed. + */ + BAD_REQUEST(Response.Status.BAD_REQUEST), + + /** + * The credentials provided were invalid. + */ + INVALID_CREDENTIALS(Response.Status.FORBIDDEN), + + /** + * The credentials provided were not necessarily invalid, but were not + * sufficient to determine validity. + */ + INSUFFICIENT_CREDENTIALS(Response.Status.FORBIDDEN), + + /** + * An internal server error has occurred. + */ + INTERNAL_ERROR(Response.Status.INTERNAL_SERVER_ERROR), + + /** + * An object related to the request does not exist. + */ + NOT_FOUND(Response.Status.NOT_FOUND), + + /** + * Permission was denied to perform the requested operation. + */ + PERMISSION_DENIED(Response.Status.FORBIDDEN); + + /** + * The HTTP status associated with this error type. + */ + private final Response.Status status; + + /** + * Defines a new error type associated with the given HTTP status. + * + * @param status + * The HTTP status to associate with the error type. + */ + Type(Response.Status status) { + this.status = status; + } + + /** + * Returns the HTTP status associated with this error type. + * + * @return + * The HTTP status associated with this error type. + */ + public Response.Status getStatus() { + return status; + } + + } + + /** + * Create a new APIError with the specified error message. + * + * @param type + * The type of error that occurred. + * + * @param message + * The error message. + */ + public APIError(Type type, String message) { + this.type = type; + this.message = message; + this.expected = null; + } + + /** + * Create a new APIError with the specified error message and parameter + * information. + * + * @param type + * The type of error that occurred. + * + * @param message + * The error message. + * + * @param expected + * All parameters expected in the original request, or now required as + * a result of the original request. + */ + public APIError(Type type, String message, Collection expected) { + this.type = type; + this.message = message; + this.expected = expected; + } + + /** + * Returns the type of error that occurred. + * + * @return + * The type of error that occurred. + */ + public Type getType() { + return type; + } + + /** + * Returns an object which describes the required credentials. + * + * @return + * An object which describes the required credentials. + */ + public Collection getExpected() { + return expected; + } + + /** + * Returns a human-readable error message describing the error that + * occurred. + * + * @return + * A human-readable error message. */ public String getMessage() { return message; } - - /** - * Create a new APIError with the specified error message. - * @param message The error message. - */ - public APIError(String message) { - this.message = message; - } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java index 8d344c68d..741ed15df 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java @@ -27,6 +27,7 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.glyptodon.guacamole.GuacamoleClientException; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleResourceNotFoundException; import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; @@ -61,11 +62,11 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { if (message == null) message = "Permission denied."; - throw new HTTPException(Response.Status.FORBIDDEN, new APICredentialError( - APICredentialError.Type.INSUFFICIENT, - message, - e.getCredentialsInfo() - )); + throw new HTTPException( + APIError.Type.INSUFFICIENT_CREDENTIALS, + message, + e.getCredentialsInfo().getParameters() + ); } // The provided credentials are wrong @@ -76,11 +77,11 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { if (message == null) message = "Permission denied."; - throw new HTTPException(Response.Status.FORBIDDEN, new APICredentialError( - APICredentialError.Type.INVALID, - message, - e.getCredentialsInfo() - )); + throw new HTTPException( + APIError.Type.INVALID_CREDENTIALS, + message, + e.getCredentialsInfo().getParameters() + ); } // Generic permission denied @@ -91,10 +92,28 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { if (message == null) message = "Permission denied."; - throw new HTTPException(Response.Status.FORBIDDEN, message); + throw new HTTPException( + APIError.Type.PERMISSION_DENIED, + message + ); } + // Arbitrary resource not found + catch (GuacamoleResourceNotFoundException e) { + + // Generate default message + String message = e.getMessage(); + if (message == null) + message = "Not found."; + + throw new HTTPException( + APIError.Type.NOT_FOUND, + message + ); + + } + // Arbitrary bad requests catch (GuacamoleClientException e) { @@ -103,7 +122,10 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { if (message == null) message = "Invalid request."; - throw new HTTPException(Response.Status.BAD_REQUEST, message); + throw new HTTPException( + APIError.Type.BAD_REQUEST, + message + ); } @@ -116,7 +138,10 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { message = "Unexpected server error."; logger.debug("Unexpected exception in REST endpoint.", e); - throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, message); + throw new HTTPException( + APIError.Type.INTERNAL_ERROR, + message + ); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java index b4d00d540..1f00556f9 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java @@ -22,37 +22,64 @@ package org.glyptodon.guacamole.net.basic.rest; +import java.util.Collection; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; +import org.glyptodon.guacamole.form.Parameter; /** - * An exception that will result in the given HTTP Status and message or entity - * being returned from the API layer. - * + * An exception that will result in the given HTTP Status and error being + * returned from the API layer. All error messages have the same format which + * is defined by APIError. + * * @author James Muehlner + * @author Michael Jumper */ public class HTTPException extends WebApplicationException { - + /** - * Construct a new HTTPException with the given HTTP status and entity. - * - * @param status The HTTP Status to use for the response. - * @param entity The entity to use as the body of the response. + * Construct a new HTTPException with the given error. All information + * associated with this new exception will be extracted from the given + * APIError. + * + * @param error + * The error that occurred. */ - public HTTPException(Status status, Object entity) { - super(Response.status(status).entity(entity).build()); + public HTTPException(APIError error) { + super(Response.status(error.getType().getStatus()).entity(error).build()); } - + /** - * Construct a new HTTPException with the given HTTP status and message. The - * message will be wrapped in an APIError container. - * - * @param status The HTTP Status to use for the response. - * @param message The message to build the response entity with. + * Creates a new HTTPException with the given type and message. The + * corresponding APIError will be created from the provided information. + * + * @param type + * The type of error that occurred. + * + * @param message + * A human-readable message describing the error. */ - public HTTPException(Status status, String message) { - super(Response.status(status).entity(new APIError(message)).build()); + public HTTPException(APIError.Type type, String message) { + this(new APIError(type, message)); } - + + /** + * Creates a new HTTPException with the given type, message, and + * parameter information. The corresponding APIError will be created from + * the provided information. + * + * @param type + * The type of error that occurred. + * + * @param message + * A human-readable message describing the error. + * + * @param expected + * All parameters expected in the original request, or now required as + * a result of the original request. + */ + public HTTPException(APIError.Type type, String message, Collection expected) { + this(new APIError(type, message, expected)); + } + } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java index af7d01b9e..059173776 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java @@ -35,7 +35,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response.Status; import javax.xml.bind.DatatypeConverter; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; @@ -44,6 +43,7 @@ import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; import org.glyptodon.guacamole.net.basic.GuacamoleSession; +import org.glyptodon.guacamole.net.basic.rest.APIError; import org.glyptodon.guacamole.net.basic.rest.APIRequest; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.HTTPException; @@ -290,7 +290,7 @@ public class TokenRESTService { GuacamoleSession session = tokenSessionMap.remove(authToken); if (session == null) - throw new HTTPException(Status.NOT_FOUND, "No such token."); + throw new HTTPException(APIError.Type.NOT_FOUND, "No such token."); session.invalidate(); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index 85f124476..8bae0cf27 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -39,8 +39,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleResourceNotFoundException; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; @@ -53,6 +51,7 @@ import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; import org.glyptodon.guacamole.net.auth.permission.Permission; import org.glyptodon.guacamole.net.auth.permission.SystemPermission; import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; +import org.glyptodon.guacamole.net.basic.rest.APIError; import org.glyptodon.guacamole.net.basic.rest.APIPatch; import static org.glyptodon.guacamole.net.basic.rest.APIPatch.Operation.add; import static org.glyptodon.guacamole.net.basic.rest.APIPatch.Operation.remove; @@ -276,12 +275,12 @@ public class UserRESTService { // Validate data and path are sane if (!user.getUsername().equals(username)) - throw new HTTPException(Response.Status.BAD_REQUEST, + throw new HTTPException(APIError.Type.BAD_REQUEST, "Username in path does not match username provided JSON data."); // A user may not use this endpoint to modify himself if (userContext.self().getIdentifier().equals(user.getUsername())) { - throw new HTTPException(Response.Status.FORBIDDEN, + throw new HTTPException(APIError.Type.PERMISSION_DENIED, "Permission denied."); } @@ -336,7 +335,7 @@ public class UserRESTService { // Verify that the old password was correct if (authProvider.getUserContext(credentials) == null) { - throw new HTTPException(Response.Status.FORBIDDEN, + throw new HTTPException(APIError.Type.PERMISSION_DENIED, "Permission denied."); } @@ -467,7 +466,7 @@ public class UserRESTService { // Unsupported patch operation default: - throw new HTTPException(Status.BAD_REQUEST, + throw new HTTPException(APIError.Type.BAD_REQUEST, "Unsupported patch operation: \"" + operation + "\""); } @@ -586,7 +585,7 @@ public class UserRESTService { // Otherwise, the path is not supported else - throw new HTTPException(Status.BAD_REQUEST, "Unsupported patch path: \"" + path + "\""); + throw new HTTPException(APIError.Type.BAD_REQUEST, "Unsupported patch path: \"" + path + "\""); } // end for each patch operation diff --git a/guacamole/src/main/webapp/app/rest/types/Error.js b/guacamole/src/main/webapp/app/rest/types/Error.js new file mode 100644 index 000000000..fe2a599ff --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/types/Error.js @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Service which defines the Error class. + */ +angular.module('rest').factory('Error', [function defineError() { + + /** + * The object returned by REST API calls when an error occurs. + * + * @constructor + * @param {Error|Object} [template={}] + * The object whose properties should be copied within the new + * Error. + */ + var Error = function Error(template) { + + // Use empty object by default + template = template || {}; + + /** + * A human-readable message describing the error that occurred. + * + * @type String + */ + this.message = template.message; + + /** + * The type string defining which values this parameter may contain, + * as well as what properties are applicable. Valid types are listed + * within Error.Type. + * + * @type String + * @default Error.Type.INTERNAL_ERROR + */ + this.type = template.type || Error.Type.INTERNAL_ERROR; + + /** + * Any parameters which were expected in the original request, or are + * now expected as a result of the original request, if any. If no + * such information is available, this will be null. + * + * @type Field[] + */ + this.expected = template.expected; + + }; + + /** + * All valid field types. + */ + Error.Type = { + + /** + * The requested operation could not be performed because the request + * itself was malformed. + * + * @type String + */ + BAD_REQUEST : 'BAD_REQUEST', + + /** + * The credentials provided were invalid. + * + * @type String + */ + INVALID_CREDENTIALS : 'INVALID_CREDENTIALS', + + /** + * The credentials provided were not necessarily invalid, but were not + * sufficient to determine validity. + * + * @type String + */ + INSUFFICIENT_CREDENTIALS : 'INSUFFICIENT_CREDENTIALS', + + /** + * An internal server error has occurred. + * + * @type String + */ + INTERNAL_ERROR : 'INTERNAL_ERROR', + + /** + * An object related to the request does not exist. + * + * @type String + */ + NOT_FOUND : 'NOT_FOUND', + + /** + * Permission was denied to perform the requested operation. + * + * @type String + */ + PERMISSION_DENIED : 'PERMISSION_DENIED' + + }; + + return Error; + +}]); \ No newline at end of file From 1010552c69c84609049c2b38fa0e704939c3636a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 Apr 2015 14:51:29 -0700 Subject: [PATCH 06/16] GUAC-1161: Generalize HTTPException to APIException. --- .../{HTTPException.java => APIException.java} | 20 +++++++++---------- .../AuthProviderRESTExceptionWrapper.java | 12 +++++------ .../net/basic/rest/auth/TokenRESTService.java | 4 ++-- .../net/basic/rest/user/UserRESTService.java | 12 +++++------ 4 files changed, 24 insertions(+), 24 deletions(-) rename guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/{HTTPException.java => APIException.java} (78%) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIException.java similarity index 78% rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIException.java index 1f00556f9..b26f9bd90 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIException.java @@ -28,29 +28,29 @@ import javax.ws.rs.core.Response; import org.glyptodon.guacamole.form.Parameter; /** - * An exception that will result in the given HTTP Status and error being + * An exception that will result in the given error error information being * returned from the API layer. All error messages have the same format which * is defined by APIError. * * @author James Muehlner * @author Michael Jumper */ -public class HTTPException extends WebApplicationException { +public class APIException extends WebApplicationException { /** - * Construct a new HTTPException with the given error. All information + * Construct a new APIException with the given error. All information * associated with this new exception will be extracted from the given * APIError. * * @param error * The error that occurred. */ - public HTTPException(APIError error) { + public APIException(APIError error) { super(Response.status(error.getType().getStatus()).entity(error).build()); } /** - * Creates a new HTTPException with the given type and message. The + * Creates a new APIException with the given type and message. The * corresponding APIError will be created from the provided information. * * @param type @@ -59,14 +59,14 @@ public class HTTPException extends WebApplicationException { * @param message * A human-readable message describing the error. */ - public HTTPException(APIError.Type type, String message) { + public APIException(APIError.Type type, String message) { this(new APIError(type, message)); } /** - * Creates a new HTTPException with the given type, message, and - * parameter information. The corresponding APIError will be created from - * the provided information. + * Creates a new APIException with the given type, message, and parameter + * information. The corresponding APIError will be created from the + * provided information. * * @param type * The type of error that occurred. @@ -78,7 +78,7 @@ public class HTTPException extends WebApplicationException { * All parameters expected in the original request, or now required as * a result of the original request. */ - public HTTPException(APIError.Type type, String message, Collection expected) { + public APIException(APIError.Type type, String message, Collection expected) { this(new APIError(type, message, expected)); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java index 741ed15df..0878d9632 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java @@ -62,7 +62,7 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { if (message == null) message = "Permission denied."; - throw new HTTPException( + throw new APIException( APIError.Type.INSUFFICIENT_CREDENTIALS, message, e.getCredentialsInfo().getParameters() @@ -77,7 +77,7 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { if (message == null) message = "Permission denied."; - throw new HTTPException( + throw new APIException( APIError.Type.INVALID_CREDENTIALS, message, e.getCredentialsInfo().getParameters() @@ -92,7 +92,7 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { if (message == null) message = "Permission denied."; - throw new HTTPException( + throw new APIException( APIError.Type.PERMISSION_DENIED, message ); @@ -107,7 +107,7 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { if (message == null) message = "Not found."; - throw new HTTPException( + throw new APIException( APIError.Type.NOT_FOUND, message ); @@ -122,7 +122,7 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { if (message == null) message = "Invalid request."; - throw new HTTPException( + throw new APIException( APIError.Type.BAD_REQUEST, message ); @@ -138,7 +138,7 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor { message = "Unexpected server error."; logger.debug("Unexpected exception in REST endpoint.", e); - throw new HTTPException( + throw new APIException( APIError.Type.INTERNAL_ERROR, message ); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java index 059173776..3951c59e4 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java @@ -46,7 +46,7 @@ import org.glyptodon.guacamole.net.basic.GuacamoleSession; import org.glyptodon.guacamole.net.basic.rest.APIError; import org.glyptodon.guacamole.net.basic.rest.APIRequest; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; -import org.glyptodon.guacamole.net.basic.rest.HTTPException; +import org.glyptodon.guacamole.net.basic.rest.APIException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -290,7 +290,7 @@ public class TokenRESTService { GuacamoleSession session = tokenSessionMap.remove(authToken); if (session == null) - throw new HTTPException(APIError.Type.NOT_FOUND, "No such token."); + throw new APIException(APIError.Type.NOT_FOUND, "No such token."); session.invalidate(); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index 8bae0cf27..af785fc65 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -56,7 +56,7 @@ import org.glyptodon.guacamole.net.basic.rest.APIPatch; import static org.glyptodon.guacamole.net.basic.rest.APIPatch.Operation.add; import static org.glyptodon.guacamole.net.basic.rest.APIPatch.Operation.remove; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; -import org.glyptodon.guacamole.net.basic.rest.HTTPException; +import org.glyptodon.guacamole.net.basic.rest.APIException; import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.PATCH; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; @@ -275,12 +275,12 @@ public class UserRESTService { // Validate data and path are sane if (!user.getUsername().equals(username)) - throw new HTTPException(APIError.Type.BAD_REQUEST, + throw new APIException(APIError.Type.BAD_REQUEST, "Username in path does not match username provided JSON data."); // A user may not use this endpoint to modify himself if (userContext.self().getIdentifier().equals(user.getUsername())) { - throw new HTTPException(APIError.Type.PERMISSION_DENIED, + throw new APIException(APIError.Type.PERMISSION_DENIED, "Permission denied."); } @@ -335,7 +335,7 @@ public class UserRESTService { // Verify that the old password was correct if (authProvider.getUserContext(credentials) == null) { - throw new HTTPException(APIError.Type.PERMISSION_DENIED, + throw new APIException(APIError.Type.PERMISSION_DENIED, "Permission denied."); } @@ -466,7 +466,7 @@ public class UserRESTService { // Unsupported patch operation default: - throw new HTTPException(APIError.Type.BAD_REQUEST, + throw new APIException(APIError.Type.BAD_REQUEST, "Unsupported patch operation: \"" + operation + "\""); } @@ -585,7 +585,7 @@ public class UserRESTService { // Otherwise, the path is not supported else - throw new HTTPException(APIError.Type.BAD_REQUEST, "Unsupported patch path: \"" + path + "\""); + throw new APIException(APIError.Type.BAD_REQUEST, "Unsupported patch path: \"" + path + "\""); } // end for each patch operation From 999be47b205ccf2572b435d2597f842418a33263 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 Apr 2015 15:45:16 -0700 Subject: [PATCH 07/16] GUAC-1161: Reorganize authenticationInterceptor to match service pattern used elsewhere. --- .../services/authenticationInterceptor.js | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/guacamole/src/main/webapp/app/index/services/authenticationInterceptor.js b/guacamole/src/main/webapp/app/index/services/authenticationInterceptor.js index d7bfe8aee..7750243f7 100644 --- a/guacamole/src/main/webapp/app/index/services/authenticationInterceptor.js +++ b/guacamole/src/main/webapp/app/index/services/authenticationInterceptor.js @@ -20,28 +20,44 @@ * THE SOFTWARE. */ -angular.module('index').factory('authenticationInterceptor', ['$location', '$q', - function authenticationInterceptor($location, $q) { - - return { +angular.module('index').factory('authenticationInterceptor', ['$injector', + function authenticationInterceptor($injector) { - // Redirect users to login if authorization fails - responseError: function handleErrorResponse(rejection) { + // Required services + var $location = $injector.get('$location'); + var $q = $injector.get('$q'); - // Only redirect failed authentication requests - if ((rejection.status === 401 || rejection.status === 403) - && rejection.config.url === 'api/tokens') { + var service = {}; - // Only redirect if not already on login page - if ($location.path() !== '/login/') - $location.path('/login/'); + /** + * Redirect users to login if authorization fails. This is called + * automatically when this service is registered as an interceptor, as + * documented at: + * + * https://docs.angularjs.org/api/ng/service/$http#interceptors + * + * @param {HttpPromise} rejection + * The promise associated with the HTTP request that failed. + * + * @returns {Promise} + * A rejected promise containing the originally-rejected HttpPromise. + */ + service.responseError = function responseError(rejection) { - } + // Only redirect failed authentication requests + if ((rejection.status === 401 || rejection.status === 403) + && rejection.config.url === 'api/tokens') { - return $q.reject(rejection); + // Only redirect if not already on login page + if ($location.path() !== '/login/') + $location.path('/login/'); } + return $q.reject(rejection); + }; + return service; + }]); From a97145edf65e1b3527cb7817546cb5b313199dcb Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 Apr 2015 23:33:19 -0700 Subject: [PATCH 08/16] GUAC-1161: Use labels and CSS for form layout. --- .../src/main/webapp/app/form/templates/form.html | 14 ++++++-------- .../main/webapp/app/form/templates/formField.html | 2 +- .../app/manage/styles/connection-parameter.css | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/guacamole/src/main/webapp/app/form/templates/form.html b/guacamole/src/main/webapp/app/form/templates/form.html index 962ce4931..bf8c7ad97 100644 --- a/guacamole/src/main/webapp/app/form/templates/form.html +++ b/guacamole/src/main/webapp/app/form/templates/form.html @@ -25,13 +25,11 @@

{{getSectionHeader(form) | translate}}

- - - - - -
{{getFieldHeader(field) | translate}} - -
+
+ +
diff --git a/guacamole/src/main/webapp/app/form/templates/formField.html b/guacamole/src/main/webapp/app/form/templates/formField.html index ebcf3573f..5c78db80a 100644 --- a/guacamole/src/main/webapp/app/form/templates/formField.html +++ b/guacamole/src/main/webapp/app/form/templates/formField.html @@ -1,4 +1,4 @@ -
+
-