mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
Merge pull request #153 from glyptodon/challenge-responses
GUAC-1161: Implement challenge-style responses
This commit is contained in:
@@ -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<ParameterOption> options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Parameter with no associated name, title, or type.
|
||||||
|
*/
|
||||||
|
public Parameter() {
|
||||||
|
this.options = new ArrayList<ParameterOption>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<ParameterOption>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<ParameterOption>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<ParameterOption> 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<ParameterOption> 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<ParameterOption> options) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,18 +20,17 @@
|
|||||||
* THE SOFTWARE.
|
* 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
|
* @author Michael Jumper
|
||||||
*/
|
*/
|
||||||
public class ProtocolParameterOption {
|
public class ParameterOption {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The value that will be sent to the client plugin if this option is
|
* The value that will be assigned if this option is chosen.
|
||||||
* chosen.
|
|
||||||
*/
|
*/
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
@@ -41,20 +40,40 @@ public class ProtocolParameterOption {
|
|||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value that will be sent to the client plugin if this option
|
* Creates a new ParameterOption with no associated value or title.
|
||||||
* is chosen.
|
*/
|
||||||
|
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() {
|
public String getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value that will be sent to the client plugin if this option is
|
* Sets the value that will be assigned if this option is chosen.
|
||||||
* 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) {
|
public void setValue(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@@ -62,7 +81,9 @@ public class ProtocolParameterOption {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the human-readable title describing the effect of this option.
|
* 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() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
@@ -70,7 +91,9 @@ public class ProtocolParameterOption {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the human-readable title describing the effect of this option.
|
* 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) {
|
public void setTitle(String title) {
|
||||||
this.title = title;
|
this.title = title;
|
@@ -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<Parameter> parameters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new CredentialsInfo object which requires the given parameters
|
||||||
|
* for any conforming credentials.
|
||||||
|
*
|
||||||
|
* @param parameters
|
||||||
|
* The parameters to require.
|
||||||
|
*/
|
||||||
|
public CredentialsInfo(Collection<Parameter> 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<Parameter> 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)
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -24,6 +24,7 @@ package org.glyptodon.guacamole.protocols;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import org.glyptodon.guacamole.form.Parameter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes a protocol and all parameters associated with it, as required by
|
* 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.
|
* A collection of all associated protocol parameters.
|
||||||
*/
|
*/
|
||||||
private Collection<ProtocolParameter> parameters =
|
private Collection<Parameter> parameters;
|
||||||
new ArrayList<ProtocolParameter>();
|
|
||||||
|
/**
|
||||||
|
* Creates a new ProtocolInfo with no associated name, title, or
|
||||||
|
* parameters.
|
||||||
|
*/
|
||||||
|
public ProtocolInfo() {
|
||||||
|
this.parameters = new ArrayList<Parameter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<Parameter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<Parameter> parameters) {
|
||||||
|
this.name = name;
|
||||||
|
this.title = title;
|
||||||
|
this.parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the human-readable title associated with this protocol.
|
* Returns the human-readable title associated with this protocol.
|
||||||
@@ -95,8 +137,19 @@ public class ProtocolInfo {
|
|||||||
*
|
*
|
||||||
* @return A mutable collection of protocol parameters.
|
* @return A mutable collection of protocol parameters.
|
||||||
*/
|
*/
|
||||||
public Collection<ProtocolParameter> getParameters() {
|
public Collection<Parameter> getParameters() {
|
||||||
return parameters;
|
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<Parameter> parameters) {
|
||||||
|
this.parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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<ProtocolParameterOption> options =
|
|
||||||
new ArrayList<ProtocolParameterOption>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<ProtocolParameterOption> getOptions() {
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
package org.glyptodon.guacamole.xml.protocol;
|
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.glyptodon.guacamole.xml.TagHandler;
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
@@ -37,7 +37,7 @@ public class OptionTagHandler implements TagHandler {
|
|||||||
/**
|
/**
|
||||||
* The option backing this option tag.
|
* The option backing this option tag.
|
||||||
*/
|
*/
|
||||||
private ProtocolParameterOption option = new ProtocolParameterOption();
|
private ParameterOption option = new ParameterOption();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Attributes attributes) throws SAXException {
|
public void init(Attributes attributes) throws SAXException {
|
||||||
@@ -55,10 +55,10 @@ public class OptionTagHandler implements TagHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ProtocolParameterOption backing this tag.
|
* Returns the ParameterOption backing this tag.
|
||||||
* @return The ProtocolParameterOption backing this tag.
|
* @return The ParameterOption backing this tag.
|
||||||
*/
|
*/
|
||||||
public ProtocolParameterOption asProtocolParameterOption() {
|
public ParameterOption asParameterOption() {
|
||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
package org.glyptodon.guacamole.xml.protocol;
|
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.glyptodon.guacamole.xml.TagHandler;
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
@@ -35,9 +35,9 @@ import org.xml.sax.SAXException;
|
|||||||
public class ParamTagHandler implements TagHandler {
|
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
|
@Override
|
||||||
public void init(Attributes attributes) throws SAXException {
|
public void init(Attributes attributes) throws SAXException {
|
||||||
@@ -51,31 +51,31 @@ public class ParamTagHandler implements TagHandler {
|
|||||||
|
|
||||||
// Text field
|
// Text field
|
||||||
if ("text".equals(type))
|
if ("text".equals(type))
|
||||||
protocolParameter.setType(ProtocolParameter.Type.TEXT);
|
protocolParameter.setType(Parameter.Type.TEXT);
|
||||||
|
|
||||||
// Numeric field
|
// Numeric field
|
||||||
else if ("numeric".equals(type))
|
else if ("numeric".equals(type))
|
||||||
protocolParameter.setType(ProtocolParameter.Type.NUMERIC);
|
protocolParameter.setType(Parameter.Type.NUMERIC);
|
||||||
|
|
||||||
// Username field
|
// Username field
|
||||||
else if ("username".equals(type))
|
else if ("username".equals(type))
|
||||||
protocolParameter.setType(ProtocolParameter.Type.USERNAME);
|
protocolParameter.setType(Parameter.Type.USERNAME);
|
||||||
|
|
||||||
// Password field
|
// Password field
|
||||||
else if ("password".equals(type))
|
else if ("password".equals(type))
|
||||||
protocolParameter.setType(ProtocolParameter.Type.PASSWORD);
|
protocolParameter.setType(Parameter.Type.PASSWORD);
|
||||||
|
|
||||||
// Enumerated field
|
// Enumerated field
|
||||||
else if ("enum".equals(type))
|
else if ("enum".equals(type))
|
||||||
protocolParameter.setType(ProtocolParameter.Type.ENUM);
|
protocolParameter.setType(Parameter.Type.ENUM);
|
||||||
|
|
||||||
// Multiline field
|
// Multiline field
|
||||||
else if ("multiline".equals(type))
|
else if ("multiline".equals(type))
|
||||||
protocolParameter.setType(ProtocolParameter.Type.MULTILINE);
|
protocolParameter.setType(Parameter.Type.MULTILINE);
|
||||||
|
|
||||||
// Boolean field
|
// Boolean field
|
||||||
else if ("boolean".equals(type)) {
|
else if ("boolean".equals(type)) {
|
||||||
protocolParameter.setType(ProtocolParameter.Type.BOOLEAN);
|
protocolParameter.setType(Parameter.Type.BOOLEAN);
|
||||||
|
|
||||||
if(protocolParameter.getValue() == null)
|
if(protocolParameter.getValue() == null)
|
||||||
throw new SAXException
|
throw new SAXException
|
||||||
@@ -99,7 +99,7 @@ public class ParamTagHandler implements TagHandler {
|
|||||||
|
|
||||||
// Store stub in options collection
|
// Store stub in options collection
|
||||||
protocolParameter.getOptions().add(
|
protocolParameter.getOptions().add(
|
||||||
tagHandler.asProtocolParameterOption());
|
tagHandler.asParameterOption());
|
||||||
return tagHandler;
|
return tagHandler;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -114,10 +114,10 @@ public class ParamTagHandler implements TagHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ProtocolParameter backing this tag.
|
* Returns the Parameter backing this tag.
|
||||||
* @return The ProtocolParameter backing this tag.
|
* @return The Parameter backing this tag.
|
||||||
*/
|
*/
|
||||||
public ProtocolParameter asProtocolParameter() {
|
public Parameter asParameter() {
|
||||||
return protocolParameter;
|
return protocolParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -56,7 +56,7 @@ public class ProtocolTagHandler implements TagHandler {
|
|||||||
ParamTagHandler tagHandler = new ParamTagHandler();
|
ParamTagHandler tagHandler = new ParamTagHandler();
|
||||||
|
|
||||||
// Store stub in parameters collection
|
// Store stub in parameters collection
|
||||||
info.getParameters().add(tagHandler.asProtocolParameter());
|
info.getParameters().add(tagHandler.asParameter());
|
||||||
return tagHandler;
|
return tagHandler;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -22,31 +22,161 @@
|
|||||||
|
|
||||||
package org.glyptodon.guacamole.net.basic.rest;
|
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 James Muehlner
|
||||||
|
* @author Michael Jumper
|
||||||
*/
|
*/
|
||||||
public class APIError {
|
public class APIError {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The error message.
|
* The error message.
|
||||||
*/
|
*/
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the error message.
|
* All expected request parameters, if any.
|
||||||
* @return The error message.
|
*/
|
||||||
|
private final Collection<Parameter> 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<Parameter> 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<Parameter> getExpected() {
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human-readable error message describing the error that
|
||||||
|
* occurred.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A human-readable error message.
|
||||||
*/
|
*/
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new APIError with the specified error message.
|
|
||||||
* @param message The error message.
|
|
||||||
*/
|
|
||||||
public APIError(String message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 java.util.Collection;
|
||||||
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import org.glyptodon.guacamole.form.Parameter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 APIException extends WebApplicationException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 APIException(APIError error) {
|
||||||
|
super(Response.status(error.getType().getStatus()).entity(error).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new APIException 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 APIException(APIError.Type type, String message) {
|
||||||
|
this(new APIError(type, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @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 APIException(APIError.Type type, String message, Collection<Parameter> expected) {
|
||||||
|
this(new APIError(type, message, expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -27,7 +27,11 @@ import org.aopalliance.intercept.MethodInterceptor;
|
|||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.glyptodon.guacamole.GuacamoleClientException;
|
import org.glyptodon.guacamole.GuacamoleClientException;
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
import org.glyptodon.guacamole.GuacamoleException;
|
||||||
|
import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
|
||||||
import org.glyptodon.guacamole.GuacamoleSecurityException;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -49,15 +53,96 @@ public class AuthProviderRESTExceptionWrapper implements MethodInterceptor {
|
|||||||
try {
|
try {
|
||||||
return invocation.proceed();
|
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 APIException(
|
||||||
|
APIError.Type.INSUFFICIENT_CREDENTIALS,
|
||||||
|
message,
|
||||||
|
e.getCredentialsInfo().getParameters()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
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 APIException(
|
||||||
|
APIError.Type.INVALID_CREDENTIALS,
|
||||||
|
message,
|
||||||
|
e.getCredentialsInfo().getParameters()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
catch(GuacamoleException e) {
|
|
||||||
logger.error("Unexpected GuacamoleException caught while executing " + invocation.getMethod().getName() + ".", e);
|
// Generic permission denied
|
||||||
throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error.");
|
catch (GuacamoleSecurityException e) {
|
||||||
|
|
||||||
|
// Generate default message
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message == null)
|
||||||
|
message = "Permission denied.";
|
||||||
|
|
||||||
|
throw new APIException(
|
||||||
|
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 APIException(
|
||||||
|
APIError.Type.NOT_FOUND,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arbitrary bad requests
|
||||||
|
catch (GuacamoleClientException e) {
|
||||||
|
|
||||||
|
// Generate default message
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message == null)
|
||||||
|
message = "Invalid request.";
|
||||||
|
|
||||||
|
throw new APIException(
|
||||||
|
APIError.Type.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 APIException(
|
||||||
|
APIError.Type.INTERNAL_ERROR,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 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 javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import javax.ws.rs.core.Response.Status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that will result in the given HTTP Status and message or entity
|
|
||||||
* being returned from the API layer.
|
|
||||||
*
|
|
||||||
* @author James Muehlner
|
|
||||||
*/
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
public HTTPException(Status status, Object entity) {
|
|
||||||
super(Response.status(status).entity(entity).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.
|
|
||||||
*/
|
|
||||||
public HTTPException(Status status, String message) {
|
|
||||||
super(Response.status(status).entity(new APIError(message)).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -35,16 +35,18 @@ import javax.ws.rs.Produces;
|
|||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response.Status;
|
|
||||||
import javax.xml.bind.DatatypeConverter;
|
import javax.xml.bind.DatatypeConverter;
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
import org.glyptodon.guacamole.GuacamoleException;
|
||||||
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
|
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
|
||||||
import org.glyptodon.guacamole.net.auth.Credentials;
|
import org.glyptodon.guacamole.net.auth.Credentials;
|
||||||
import org.glyptodon.guacamole.net.auth.UserContext;
|
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.GuacamoleSession;
|
||||||
|
import org.glyptodon.guacamole.net.basic.rest.APIError;
|
||||||
import org.glyptodon.guacamole.net.basic.rest.APIRequest;
|
import org.glyptodon.guacamole.net.basic.rest.APIRequest;
|
||||||
import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -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) {
|
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) {
|
|
||||||
|
|
||||||
// Log authentication failures with associated usernames
|
// Log authentication failures with associated usernames
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
@@ -255,10 +255,9 @@ public class TokenRESTService {
|
|||||||
logger.debug("Anonymous authentication attempt from {} failed.",
|
logger.debug("Anonymous authentication attempt from {} failed.",
|
||||||
getLoggableAddress(request), username);
|
getLoggableAddress(request), username);
|
||||||
|
|
||||||
throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied.");
|
throw e;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update existing session, if it exists
|
// Update existing session, if it exists
|
||||||
String authToken;
|
String authToken;
|
||||||
if (existingSession != null) {
|
if (existingSession != null) {
|
||||||
@@ -291,7 +290,7 @@ public class TokenRESTService {
|
|||||||
|
|
||||||
GuacamoleSession session = tokenSessionMap.remove(authToken);
|
GuacamoleSession session = tokenSessionMap.remove(authToken);
|
||||||
if (session == null)
|
if (session == null)
|
||||||
throw new HTTPException(Status.NOT_FOUND, "No such token.");
|
throw new APIException(APIError.Type.NOT_FOUND, "No such token.");
|
||||||
|
|
||||||
session.invalidate();
|
session.invalidate();
|
||||||
|
|
||||||
|
@@ -39,8 +39,6 @@ import javax.ws.rs.Produces;
|
|||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
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.GuacamoleException;
|
||||||
import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
|
import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
|
||||||
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
|
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
|
||||||
@@ -53,11 +51,12 @@ import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet;
|
|||||||
import org.glyptodon.guacamole.net.auth.permission.Permission;
|
import org.glyptodon.guacamole.net.auth.permission.Permission;
|
||||||
import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
|
import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
|
||||||
import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet;
|
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 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.add;
|
||||||
import static org.glyptodon.guacamole.net.basic.rest.APIPatch.Operation.remove;
|
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.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.ObjectRetrievalService;
|
||||||
import org.glyptodon.guacamole.net.basic.rest.PATCH;
|
import org.glyptodon.guacamole.net.basic.rest.PATCH;
|
||||||
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
|
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
|
||||||
@@ -276,12 +275,12 @@ public class UserRESTService {
|
|||||||
|
|
||||||
// Validate data and path are sane
|
// Validate data and path are sane
|
||||||
if (!user.getUsername().equals(username))
|
if (!user.getUsername().equals(username))
|
||||||
throw new HTTPException(Response.Status.BAD_REQUEST,
|
throw new APIException(APIError.Type.BAD_REQUEST,
|
||||||
"Username in path does not match username provided JSON data.");
|
"Username in path does not match username provided JSON data.");
|
||||||
|
|
||||||
// A user may not use this endpoint to modify himself
|
// A user may not use this endpoint to modify himself
|
||||||
if (userContext.self().getIdentifier().equals(user.getUsername())) {
|
if (userContext.self().getIdentifier().equals(user.getUsername())) {
|
||||||
throw new HTTPException(Response.Status.FORBIDDEN,
|
throw new APIException(APIError.Type.PERMISSION_DENIED,
|
||||||
"Permission denied.");
|
"Permission denied.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,7 +335,7 @@ public class UserRESTService {
|
|||||||
|
|
||||||
// Verify that the old password was correct
|
// Verify that the old password was correct
|
||||||
if (authProvider.getUserContext(credentials) == null) {
|
if (authProvider.getUserContext(credentials) == null) {
|
||||||
throw new HTTPException(Response.Status.FORBIDDEN,
|
throw new APIException(APIError.Type.PERMISSION_DENIED,
|
||||||
"Permission denied.");
|
"Permission denied.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,7 +466,7 @@ public class UserRESTService {
|
|||||||
|
|
||||||
// Unsupported patch operation
|
// Unsupported patch operation
|
||||||
default:
|
default:
|
||||||
throw new HTTPException(Status.BAD_REQUEST,
|
throw new APIException(APIError.Type.BAD_REQUEST,
|
||||||
"Unsupported patch operation: \"" + operation + "\"");
|
"Unsupported patch operation: \"" + operation + "\"");
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -586,7 +585,7 @@ public class UserRESTService {
|
|||||||
|
|
||||||
// Otherwise, the path is not supported
|
// Otherwise, the path is not supported
|
||||||
else
|
else
|
||||||
throw new HTTPException(Status.BAD_REQUEST, "Unsupported patch path: \"" + path + "\"");
|
throw new APIException(APIError.Type.BAD_REQUEST, "Unsupported patch path: \"" + path + "\"");
|
||||||
|
|
||||||
} // end for each patch operation
|
} // end for each patch operation
|
||||||
|
|
||||||
|
@@ -32,10 +32,20 @@
|
|||||||
* If a login attempt results in an existing token being replaced, 'guacLogout'
|
* If a login attempt results in an existing token being replaced, 'guacLogout'
|
||||||
* will be broadcast first for the token being replaced, followed by
|
* will be broadcast first for the token being replaced, followed by
|
||||||
* 'guacLogin' for the new token.
|
* 'guacLogin' for the new token.
|
||||||
|
*
|
||||||
|
* Failed logins may also result in guacInsufficientCredentials or
|
||||||
|
* guacInvalidCredentials events, if the provided credentials were rejected for
|
||||||
|
* being insufficient or invalid respectively. Both events will be provided
|
||||||
|
* the set of parameters originally given to authenticate() and the set of
|
||||||
|
* expected credentials returned by the REST endpoint. This set of credentials
|
||||||
|
* will be in the form of a Field array.
|
||||||
*/
|
*/
|
||||||
angular.module('auth').factory('authenticationService', ['$injector',
|
angular.module('auth').factory('authenticationService', ['$injector',
|
||||||
function authenticationService($injector) {
|
function authenticationService($injector) {
|
||||||
|
|
||||||
|
// Required types
|
||||||
|
var Error = $injector.get('Error');
|
||||||
|
|
||||||
// Required services
|
// Required services
|
||||||
var $cookieStore = $injector.get('$cookieStore');
|
var $cookieStore = $injector.get('$cookieStore');
|
||||||
var $http = $injector.get('$http');
|
var $http = $injector.get('$http');
|
||||||
@@ -140,7 +150,16 @@ angular.module('auth').factory('authenticationService', ['$injector',
|
|||||||
})
|
})
|
||||||
|
|
||||||
// If authentication fails, propogate failure to returned promise
|
// If authentication fails, propogate failure to returned promise
|
||||||
.error(function authenticationFailed() {
|
.error(function authenticationFailed(error) {
|
||||||
|
|
||||||
|
// Request credentials if provided credentials were invalid
|
||||||
|
if (error.type === Error.Type.INVALID_CREDENTIALS)
|
||||||
|
$rootScope.$broadcast('guacInvalidCredentials', parameters, error.expected);
|
||||||
|
|
||||||
|
// Request more credentials if provided credentials were not enough
|
||||||
|
else if (error.type === Error.Type.INSUFFICIENT_CREDENTIALS)
|
||||||
|
$rootScope.$broadcast('guacInsufficientCredentials', parameters, error.expected);
|
||||||
|
|
||||||
authenticationProcess.reject();
|
authenticationProcess.reject();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -113,34 +113,6 @@ angular.module('form').directive('guacForm', [function form() {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces the translation string for the header of the given
|
|
||||||
* field. The translation string will be of the form:
|
|
||||||
*
|
|
||||||
* <code>NAMESPACE.FIELD_HEADER_NAME<code>
|
|
||||||
*
|
|
||||||
* where <code>NAMESPACE</code> is the namespace provided to the
|
|
||||||
* directive and <code>NAME</code> is the field name transformed
|
|
||||||
* via translationStringService.canonicalize().
|
|
||||||
*
|
|
||||||
* @param {Field} field
|
|
||||||
* The field for which to produce the translation string.
|
|
||||||
*
|
|
||||||
* @returns {String}
|
|
||||||
* The translation string which produces the translated header
|
|
||||||
* of the field.
|
|
||||||
*/
|
|
||||||
$scope.getFieldHeader = function getFieldHeader(field) {
|
|
||||||
|
|
||||||
// If no field, or no name, then no header
|
|
||||||
if (!field || !field.name)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
|
|
||||||
+ '.FIELD_HEADER_' + translationStringService.canonicalize(field.name);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether the given object is a form, under the
|
* Determines whether the given object is a form, under the
|
||||||
* assumption that the object is either a form or a field.
|
* assumption that the object is either a form or a field.
|
||||||
|
@@ -110,6 +110,31 @@ angular.module('form').directive('guacFormField', [function formField() {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces the translation string for the header of the current
|
||||||
|
* field. The translation string will be of the form:
|
||||||
|
*
|
||||||
|
* <code>NAMESPACE.FIELD_HEADER_NAME<code>
|
||||||
|
*
|
||||||
|
* where <code>NAMESPACE</code> is the namespace provided to the
|
||||||
|
* directive and <code>NAME</code> is the field name transformed
|
||||||
|
* via translationStringService.canonicalize().
|
||||||
|
*
|
||||||
|
* @returns {String}
|
||||||
|
* The translation string which produces the translated header
|
||||||
|
* of the field.
|
||||||
|
*/
|
||||||
|
$scope.getFieldHeader = function getFieldHeader() {
|
||||||
|
|
||||||
|
// If no field, or no name, then no header
|
||||||
|
if (!$scope.field || !$scope.field.name)
|
||||||
|
return '';
|
||||||
|
|
||||||
|
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
|
||||||
|
+ '.FIELD_HEADER_' + translationStringService.canonicalize($scope.field.name);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces the translation string for the given field option
|
* Produces the translation string for the given field option
|
||||||
* value. The translation string will be of the form:
|
* value. The translation string will be of the form:
|
||||||
|
@@ -25,13 +25,9 @@
|
|||||||
<h3 ng-show="form.name">{{getSectionHeader(form) | translate}}</h3>
|
<h3 ng-show="form.name">{{getSectionHeader(form) | translate}}</h3>
|
||||||
|
|
||||||
<!-- All fields in form -->
|
<!-- All fields in form -->
|
||||||
<table class="fields">
|
<div class="fields">
|
||||||
<tr ng-repeat="field in form.fields">
|
<guac-form-field ng-repeat="field in form.fields" namespace="namespace"
|
||||||
<th>{{getFieldHeader(field) | translate}}</th>
|
field="field" model="values[field.name]"></guac-form-field>
|
||||||
<td>
|
</div>
|
||||||
<guac-form-field namespace="namespace" field="field" model="values[field.name]"></guac-form-field>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div class="form-field">
|
<label class="labeled-field" ng-class="{empty: !model}">
|
||||||
<!--
|
<!--
|
||||||
Copyright 2014 Glyptodon LLC.
|
Copyright 2014 Glyptodon LLC.
|
||||||
|
|
||||||
@@ -21,22 +21,27 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- Generic input types -->
|
<!-- Field header -->
|
||||||
<input ng-show="field.type === 'TEXT'" type="text" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
<span class="field-header">{{getFieldHeader() | translate}}</span>
|
||||||
<input ng-show="field.type === 'NUMERIC'" type="number" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
|
||||||
<input ng-show="field.type === 'USERNAME'" type="text" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
|
||||||
<input ng-show="field.type === 'BOOLEAN'" type="checkbox" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
|
||||||
|
|
||||||
<!-- Password field -->
|
<!-- Generic input types -->
|
||||||
<div ng-show="field.type === 'PASSWORD'" class="password-field">
|
<div class="form-field">
|
||||||
<input type="{{passwordInputType}}" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
<input ng-show="field.type === 'TEXT'" type="text" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
||||||
<div class="icon toggle-password" ng-click="togglePassword()" title="{{getTogglePasswordHelpText() | translate}}"></div>
|
<input ng-show="field.type === 'NUMERIC'" type="number" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
||||||
|
<input ng-show="field.type === 'USERNAME'" type="text" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
||||||
|
<input ng-show="field.type === 'BOOLEAN'" type="checkbox" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
||||||
|
|
||||||
|
<!-- Password field -->
|
||||||
|
<div ng-show="field.type === 'PASSWORD'" class="password-field">
|
||||||
|
<input type="{{passwordInputType}}" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
||||||
|
<div class="icon toggle-password" ng-click="togglePassword()" title="{{getTogglePasswordHelpText() | translate}}"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Multiline field -->
|
||||||
|
<textarea ng-show="field.type === 'MULTILINE'" ng-model="typedValue" autocorrect="off" autocapitalize="off"></textarea>
|
||||||
|
|
||||||
|
<!-- Enumerated field -->
|
||||||
|
<select ng-show="field.type === 'ENUM'" ng-model="typedValue" ng-options="option.value as getFieldOption(option.value) | translate for option in field.options | orderBy: value"></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Multiline field -->
|
</label>
|
||||||
<textarea ng-show="field.type === 'MULTILINE'" ng-model="typedValue" autocorrect="off" autocapitalize="off"></textarea>
|
|
||||||
|
|
||||||
<!-- Enumerated field -->
|
|
||||||
<select ng-show="field.type === 'ENUM'" ng-model="typedValue" ng-options="option.value as getFieldOption(option.value) | translate for option in field.options | orderBy: value"></select>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The config block for setting up the authentication interceptor.
|
|
||||||
*/
|
|
||||||
angular.module('index').config(['$httpProvider',
|
|
||||||
function indexInterceptorConfig($httpProvider) {
|
|
||||||
$httpProvider.interceptors.push('authenticationInterceptor');
|
|
||||||
}]);
|
|
||||||
|
|
||||||
|
|
@@ -39,26 +39,17 @@ angular.module('index').config(['$routeProvider', '$locationProvider',
|
|||||||
*
|
*
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
* A promise which resolves successfully only after an attempt to
|
* A promise which resolves successfully only after an attempt to
|
||||||
* re-authenticate has been made.
|
* re-authenticate has been made. If the authentication attempt fails,
|
||||||
|
* the promise will be rejected.
|
||||||
*/
|
*/
|
||||||
var updateCurrentToken = ['$injector', function updateCurrentToken($injector) {
|
var updateCurrentToken = ['$injector', function updateCurrentToken($injector) {
|
||||||
|
|
||||||
// Required services
|
// Required services
|
||||||
var $location = $injector.get('$location');
|
var $location = $injector.get('$location');
|
||||||
var $q = $injector.get('$q');
|
|
||||||
var authenticationService = $injector.get('authenticationService');
|
var authenticationService = $injector.get('authenticationService');
|
||||||
|
|
||||||
// Promise for authentication attempt
|
|
||||||
var authAttempt = $q.defer();
|
|
||||||
|
|
||||||
// Re-authenticate including any parameters in URL
|
// Re-authenticate including any parameters in URL
|
||||||
authenticationService.updateCurrentToken($location.search())
|
return authenticationService.updateCurrentToken($location.search());
|
||||||
['finally'](function authenticationAttemptComplete() {
|
|
||||||
authAttempt.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return promise that will resolve regardless of success/failure
|
|
||||||
return authAttempt.promise;
|
|
||||||
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@@ -161,15 +152,6 @@ angular.module('index').config(['$routeProvider', '$locationProvider',
|
|||||||
resolve : { updateCurrentToken: updateCurrentToken }
|
resolve : { updateCurrentToken: updateCurrentToken }
|
||||||
})
|
})
|
||||||
|
|
||||||
// Login screen
|
|
||||||
.when('/login/', {
|
|
||||||
title : 'APP.NAME',
|
|
||||||
bodyClassName : 'login',
|
|
||||||
templateUrl : 'app/login/templates/login.html',
|
|
||||||
controller : 'loginController'
|
|
||||||
// No need to update token here - the login screen ignores all auth
|
|
||||||
})
|
|
||||||
|
|
||||||
// Client view
|
// Client view
|
||||||
.when('/client/:type/:id/:params?', {
|
.when('/client/:type/:id/:params?', {
|
||||||
bodyClassName : 'client',
|
bodyClassName : 'client',
|
||||||
|
@@ -36,6 +36,14 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
*/
|
*/
|
||||||
$scope.guacNotification = guacNotification;
|
$scope.guacNotification = guacNotification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The credentials that the authentication service is currently expecting,
|
||||||
|
* if any. If the user is logged in, this will be null.
|
||||||
|
*
|
||||||
|
* @type Form[]|Form|Field[]|Field
|
||||||
|
*/
|
||||||
|
$scope.expectedCredentials = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic page-level information.
|
* Basic page-level information.
|
||||||
*/
|
*/
|
||||||
@@ -63,6 +71,10 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
// Broadcast keydown events
|
// Broadcast keydown events
|
||||||
keyboard.onkeydown = function onkeydown(keysym) {
|
keyboard.onkeydown = function onkeydown(keysym) {
|
||||||
|
|
||||||
|
// Do not handle key events if not logged in
|
||||||
|
if ($scope.expectedCredentials)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Warn of pending keydown
|
// Warn of pending keydown
|
||||||
var guacBeforeKeydownEvent = $scope.$broadcast('guacBeforeKeydown', keysym, keyboard);
|
var guacBeforeKeydownEvent = $scope.$broadcast('guacBeforeKeydown', keysym, keyboard);
|
||||||
if (guacBeforeKeydownEvent.defaultPrevented)
|
if (guacBeforeKeydownEvent.defaultPrevented)
|
||||||
@@ -77,6 +89,10 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
// Broadcast keyup events
|
// Broadcast keyup events
|
||||||
keyboard.onkeyup = function onkeyup(keysym) {
|
keyboard.onkeyup = function onkeyup(keysym) {
|
||||||
|
|
||||||
|
// Do not handle key events if not logged in
|
||||||
|
if ($scope.expectedCredentials)
|
||||||
|
return;
|
||||||
|
|
||||||
// Warn of pending keyup
|
// Warn of pending keyup
|
||||||
var guacBeforeKeydownEvent = $scope.$broadcast('guacBeforeKeyup', keysym, keyboard);
|
var guacBeforeKeydownEvent = $scope.$broadcast('guacBeforeKeyup', keysym, keyboard);
|
||||||
if (guacBeforeKeydownEvent.defaultPrevented)
|
if (guacBeforeKeydownEvent.defaultPrevented)
|
||||||
@@ -92,6 +108,25 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
keyboard.reset();
|
keyboard.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Display login screen if a whole new set of credentials is needed
|
||||||
|
$scope.$on('guacInvalidCredentials', function loginInvalid(event, parameters, expected) {
|
||||||
|
$scope.page.title = 'APP.NAME';
|
||||||
|
$scope.page.bodyClassName = '';
|
||||||
|
$scope.expectedCredentials = expected;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prompt for remaining credentials if provided credentials were not enough
|
||||||
|
$scope.$on('guacInsufficientCredentials', function loginInsufficient(event, parameters, expected) {
|
||||||
|
$scope.page.title = 'APP.NAME';
|
||||||
|
$scope.page.bodyClassName = '';
|
||||||
|
$scope.expectedCredentials = expected;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear login screen if login was successful
|
||||||
|
$scope.$on('guacLogin', function loginSuccessful() {
|
||||||
|
$scope.expectedCredentials = null;
|
||||||
|
});
|
||||||
|
|
||||||
// Update title and CSS class upon navigation
|
// Update title and CSS class upon navigation
|
||||||
$scope.$on('$routeChangeSuccess', function(event, current, previous) {
|
$scope.$on('$routeChangeSuccess', function(event, current, previous) {
|
||||||
|
|
||||||
|
@@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
angular.module('index').factory('authenticationInterceptor', ['$location', '$q',
|
|
||||||
function authenticationInterceptor($location, $q) {
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
// Redirect users to login if authorization fails
|
|
||||||
responseError: function handleErrorResponse(rejection) {
|
|
||||||
|
|
||||||
// Only redirect failed authentication requests
|
|
||||||
if ((rejection.status === 401 || rejection.status === 403)
|
|
||||||
&& rejection.config.url === 'api/tokens') {
|
|
||||||
|
|
||||||
// Only redirect if not already on login page
|
|
||||||
if ($location.path() !== '/login/')
|
|
||||||
$location.path('/login/');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $q.reject(rejection);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}]);
|
|
@@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
angular.module('login').controller('loginController', ['$scope', '$injector',
|
|
||||||
function loginController($scope, $injector) {
|
|
||||||
|
|
||||||
// Required services
|
|
||||||
var $location = $injector.get('$location');
|
|
||||||
var authenticationService = $injector.get('authenticationService');
|
|
||||||
var userPageService = $injector.get('userPageService');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether an error occurred during login.
|
|
||||||
*
|
|
||||||
* @type Boolean
|
|
||||||
*/
|
|
||||||
$scope.loginError = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the password field has focus.
|
|
||||||
*
|
|
||||||
* @type Boolean
|
|
||||||
*/
|
|
||||||
$scope.passwordFocused = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submits the currently-specified username and password to the
|
|
||||||
* authentication service, redirecting to the main view if successful.
|
|
||||||
*/
|
|
||||||
$scope.login = function login() {
|
|
||||||
|
|
||||||
// Attempt login once existing session is destroyed
|
|
||||||
authenticationService.login($scope.username, $scope.password)
|
|
||||||
|
|
||||||
// Redirect to main view upon success
|
|
||||||
.then(function loginSuccessful() {
|
|
||||||
userPageService.getHomePage()
|
|
||||||
.then(function homePageRetrieved(homePage) {
|
|
||||||
$location.url(homePage.url);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
// Reset and focus password upon failure
|
|
||||||
['catch'](function loginFailed() {
|
|
||||||
$scope.loginError = true;
|
|
||||||
$scope.passwordFocused = true;
|
|
||||||
$scope.password = '';
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}]);
|
|
96
guacamole/src/main/webapp/app/login/directives/login.js
Normal file
96
guacamole/src/main/webapp/app/login/directives/login.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A directive for displaying an arbitrary login form.
|
||||||
|
*/
|
||||||
|
angular.module('login').directive('guacLogin', [function guacLogin() {
|
||||||
|
|
||||||
|
// Login directive
|
||||||
|
var directive = {
|
||||||
|
restrict : 'E',
|
||||||
|
replace : true,
|
||||||
|
templateUrl : 'app/login/templates/login.html'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Login directive scope
|
||||||
|
directive.scope = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The login form or set of fields. This will be displayed to the user
|
||||||
|
* to capture their credentials.
|
||||||
|
*
|
||||||
|
* @type Form[]|Form|Field[]|Field
|
||||||
|
*/
|
||||||
|
form : '='
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Controller for login directive
|
||||||
|
directive.controller = ['$scope', '$injector',
|
||||||
|
function loginController($scope, $injector) {
|
||||||
|
|
||||||
|
// Required services
|
||||||
|
var $route = $injector.get('$route');
|
||||||
|
var authenticationService = $injector.get('authenticationService');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an error occurred during login.
|
||||||
|
*
|
||||||
|
* @type Boolean
|
||||||
|
*/
|
||||||
|
$scope.loginError = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All form values entered by the user.
|
||||||
|
*/
|
||||||
|
$scope.values = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits the currently-specified username and password to the
|
||||||
|
* authentication service, redirecting to the main view if successful.
|
||||||
|
*/
|
||||||
|
$scope.login = function login() {
|
||||||
|
|
||||||
|
// Attempt login once existing session is destroyed
|
||||||
|
authenticationService.authenticate($scope.values)
|
||||||
|
|
||||||
|
// Clear and reload upon success
|
||||||
|
.then(function loginSuccessful() {
|
||||||
|
$scope.loginError = false;
|
||||||
|
$scope.values = {};
|
||||||
|
$route.reload();
|
||||||
|
})
|
||||||
|
|
||||||
|
// Reset upon failure
|
||||||
|
['catch'](function loginFailed() {
|
||||||
|
$scope.loginError = true;
|
||||||
|
$scope.values.password = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}];
|
||||||
|
|
||||||
|
return directive;
|
||||||
|
|
||||||
|
}]);
|
@@ -23,4 +23,8 @@
|
|||||||
/**
|
/**
|
||||||
* The module for the login functionality.
|
* The module for the login functionality.
|
||||||
*/
|
*/
|
||||||
angular.module('login', ['element', 'navigation']);
|
angular.module('login', [
|
||||||
|
'element',
|
||||||
|
'form',
|
||||||
|
'navigation'
|
||||||
|
]);
|
||||||
|
@@ -27,6 +27,8 @@ div.login-ui {
|
|||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
display: table;
|
display: table;
|
||||||
|
background: white;
|
||||||
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.login-error {
|
p.login-error {
|
||||||
@@ -52,3 +54,35 @@ p.login-error {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
color: #964040;
|
color: #964040;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-fields .form-field .password-field .toggle-password {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fields .labeled-field {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fields .labeled-field .field-header {
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
z-index: -1;
|
||||||
|
margin: 0.5em;
|
||||||
|
font-size: 0.9em;
|
||||||
|
opacity: 0.5;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fields .labeled-field.empty input {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fields .labeled-field input:focus {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
@@ -1,26 +1,25 @@
|
|||||||
<!--
|
|
||||||
Copyright 2014 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<div class="login-ui" ng-class="{error: loginError}" >
|
<div class="login-ui" ng-class="{error: loginError}" >
|
||||||
|
<!--
|
||||||
|
Copyright 2014 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.
|
||||||
|
-->
|
||||||
|
|
||||||
<!-- Login error message -->
|
<!-- Login error message -->
|
||||||
<p class="login-error">{{'LOGIN.ERROR_INVALID_LOGIN' | translate}}</p>
|
<p class="login-error">{{'LOGIN.ERROR_INVALID_LOGIN' | translate}}</p>
|
||||||
@@ -35,10 +34,9 @@ THE SOFTWARE.
|
|||||||
<img class="logo" src="images/guac-tricolor.png" alt=""/>
|
<img class="logo" src="images/guac-tricolor.png" alt=""/>
|
||||||
<div class="version">{{'APP.NAME' | translate}}</div>
|
<div class="version">{{'APP.NAME' | translate}}</div>
|
||||||
|
|
||||||
<!-- Login fields (username + password) -->
|
<!-- Login fields -->
|
||||||
<div class="login-fields">
|
<div class="login-fields">
|
||||||
<input ng-model="username" placeholder="{{'LOGIN.FIELD_PLACEHOLDER_USERNAME' | translate}}" type="text" name="username" autofocus="autofocus" autocorrect="off" autocapitalize="off" class="username"/>
|
<guac-form namespace="'LOGIN'" content="form" model="values"></guac-form>
|
||||||
<input ng-model="password" placeholder="{{'LOGIN.FIELD_PLACEHOLDER_PASSWORD' | translate}}" type="password" name="password" class="password" guac-focus="passwordFocused"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Submit button -->
|
<!-- Submit button -->
|
||||||
|
@@ -26,3 +26,18 @@
|
|||||||
.connection-parameters input[type=number] {
|
.connection-parameters input[type=number] {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.connection-parameters .form .fields {
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connection-parameters .form .fields .labeled-field {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connection-parameters .form .fields .field-header,
|
||||||
|
.connection-parameters .form .fields .form-field {
|
||||||
|
display: table-cell;
|
||||||
|
padding: 0.125em;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
@@ -48,6 +48,7 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu()
|
|||||||
// Get required services
|
// Get required services
|
||||||
var $document = $injector.get('$document');
|
var $document = $injector.get('$document');
|
||||||
var $location = $injector.get('$location');
|
var $location = $injector.get('$location');
|
||||||
|
var $route = $injector.get('$route');
|
||||||
var authenticationService = $injector.get('authenticationService');
|
var authenticationService = $injector.get('authenticationService');
|
||||||
var userPageService = $injector.get('userPageService');
|
var userPageService = $injector.get('userPageService');
|
||||||
|
|
||||||
@@ -100,12 +101,15 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu()
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs out the current user, redirecting them to back to the login
|
* Logs out the current user, redirecting them to back to the root
|
||||||
* screen after logout completes.
|
* after logout completes.
|
||||||
*/
|
*/
|
||||||
$scope.logout = function logout() {
|
$scope.logout = function logout() {
|
||||||
authenticationService.logout()['finally'](function logoutComplete() {
|
authenticationService.logout()['finally'](function logoutComplete() {
|
||||||
$location.path('/login/');
|
if ($location.path() !== '/')
|
||||||
|
$location.url('/');
|
||||||
|
else
|
||||||
|
$route.reload();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
122
guacamole/src/main/webapp/app/rest/types/Error.js
Normal file
122
guacamole/src/main/webapp/app/rest/types/Error.js
Normal file
@@ -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;
|
||||||
|
|
||||||
|
}]);
|
@@ -34,22 +34,30 @@ THE SOFTWARE.
|
|||||||
</head>
|
</head>
|
||||||
<body ng-class="page.bodyClassName">
|
<body ng-class="page.bodyClassName">
|
||||||
|
|
||||||
<!-- Global status/error dialog -->
|
<!-- Content for logged-in users -->
|
||||||
<div ng-class="{shown: guacNotification.status}" class="status-outer">
|
<div ng-if="!expectedCredentials">
|
||||||
<div class="status-middle">
|
|
||||||
<guac-notification notification="guacNotification.status"></guac-notification>
|
<!-- Global status/error dialog -->
|
||||||
|
<div ng-class="{shown: guacNotification.status}" class="status-outer">
|
||||||
|
<div class="status-middle">
|
||||||
|
<guac-notification notification="guacNotification.status"></guac-notification>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="content" ng-view>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Notification area -->
|
||||||
|
<div id="notificationArea">
|
||||||
|
<div ng-repeat="wrapper in guacNotification.notifications">
|
||||||
|
<guac-notification notification="wrapper.notification"></guac-notification>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="content" ng-view>
|
<!-- Login screen for logged-out users -->
|
||||||
</div>
|
<guac-login ng-show="expectedCredentials" form="expectedCredentials"></guac-login>
|
||||||
|
|
||||||
<!-- Notification area -->
|
|
||||||
<div id="notificationArea">
|
|
||||||
<div ng-repeat="wrapper in guacNotification.notifications">
|
|
||||||
<guac-notification notification="wrapper.notification"></guac-notification>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="guacamole.min.js"></script>
|
<script type="text/javascript" src="guacamole.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
@@ -141,12 +141,12 @@
|
|||||||
|
|
||||||
"LOGIN": {
|
"LOGIN": {
|
||||||
|
|
||||||
"ACTION_LOGIN" : "@:APP.ACTION_LOGIN",
|
"ACTION_LOGIN" : "@:APP.ACTION_LOGIN",
|
||||||
|
|
||||||
"ERROR_INVALID_LOGIN" : "Invalid Login",
|
"ERROR_INVALID_LOGIN" : "Invalid Login",
|
||||||
|
|
||||||
"FIELD_PLACEHOLDER_USERNAME" : "Username",
|
"FIELD_HEADER_USERNAME" : "Username",
|
||||||
"FIELD_PLACEHOLDER_PASSWORD" : "Password"
|
"FIELD_HEADER_PASSWORD" : "Password"
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user