mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 13:41:21 +00:00
Merge pull request #63 from glyptodon/config-tokenization
GUAC-340: Implement credential tokens.
This commit is contained in:
@@ -112,6 +112,14 @@
|
|||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JUnit -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.10</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@@ -28,6 +28,8 @@ 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.protocol.GuacamoleConfiguration;
|
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
|
||||||
|
import org.glyptodon.guacamole.token.StandardTokens;
|
||||||
|
import org.glyptodon.guacamole.token.TokenFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides means of retrieving a set of named GuacamoleConfigurations for a
|
* Provides means of retrieving a set of named GuacamoleConfigurations for a
|
||||||
@@ -72,6 +74,14 @@ public abstract class SimpleAuthenticationProvider
|
|||||||
if (configs == null)
|
if (configs == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
// Build credential TokenFilter
|
||||||
|
TokenFilter tokenFilter = new TokenFilter();
|
||||||
|
StandardTokens.addStandardTokens(tokenFilter, credentials);
|
||||||
|
|
||||||
|
// Filter each configuration
|
||||||
|
for (GuacamoleConfiguration config : configs.values())
|
||||||
|
tokenFilter.filterValues(config.getParameters());
|
||||||
|
|
||||||
// Return user context restricted to authorized configs
|
// Return user context restricted to authorized configs
|
||||||
return new SimpleUserContext(credentials.getUsername(), configs);
|
return new SimpleUserContext(credentials.getUsername(), configs);
|
||||||
|
|
||||||
|
@@ -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.token;
|
||||||
|
|
||||||
|
import org.glyptodon.guacamole.net.auth.Credentials;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class which provides access to standardized token names, as well as
|
||||||
|
* facilities for generating those tokens from common objects.
|
||||||
|
*
|
||||||
|
* @author Michael Jumper
|
||||||
|
*/
|
||||||
|
public class StandardTokens {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the username token added via addStandardTokens().
|
||||||
|
*/
|
||||||
|
private static final String USERNAME_TOKEN = "GUAC_USERNAME";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the password token added via addStandardTokens().
|
||||||
|
*/
|
||||||
|
private static final String PASSWORD_TOKEN = "GUAC_PASSWORD";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This utility class should not be instantiated.
|
||||||
|
*/
|
||||||
|
private StandardTokens() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the standard username (GUAC_USERNAME) and password (GUAC_PASSWORD)
|
||||||
|
* tokens to the given TokenFilter using the values from the given
|
||||||
|
* Credentials object. If either the username or password are not set
|
||||||
|
* within the given credentials, the corresponding token(s) will remain
|
||||||
|
* unset.
|
||||||
|
*
|
||||||
|
* @param filter
|
||||||
|
* The TokenFilter to add standard username/password tokens to.
|
||||||
|
*
|
||||||
|
* @param credentials
|
||||||
|
* The Credentials containing the username/password to add.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static void addStandardTokens(TokenFilter filter, Credentials credentials) {
|
||||||
|
|
||||||
|
// Add username token
|
||||||
|
String username = credentials.getUsername();
|
||||||
|
if (username != null)
|
||||||
|
filter.setToken(USERNAME_TOKEN, username);
|
||||||
|
|
||||||
|
// Add password token
|
||||||
|
String password = credentials.getPassword();
|
||||||
|
if (password != null)
|
||||||
|
filter.setToken(PASSWORD_TOKEN, password);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,234 @@
|
|||||||
|
/*
|
||||||
|
* 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.token;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filtering object which replaces tokens of the form "${TOKEN_NAME}" with
|
||||||
|
* their corresponding values. Unknown tokens are not replaced. If TOKEN_NAME
|
||||||
|
* is a valid token, the literal value "${TOKEN_NAME}" can be included by using
|
||||||
|
* "$${TOKEN_NAME}".
|
||||||
|
*
|
||||||
|
* @author Michael Jumper
|
||||||
|
*/
|
||||||
|
public class TokenFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular expression which matches individual tokens, with additional
|
||||||
|
* capturing groups for convenient retrieval of leading text, the possible
|
||||||
|
* escape character preceding the token, the name of the token, and the
|
||||||
|
* entire token itself.
|
||||||
|
*/
|
||||||
|
private final Pattern tokenPattern = Pattern.compile("(.*?)(^|.)(\\$\\{([A-Za-z0-9_]*)\\})");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index of the capturing group within tokenPattern which matches
|
||||||
|
* non-token text preceding a possible token.
|
||||||
|
*/
|
||||||
|
private static final int LEADING_TEXT_GROUP = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index of the capturing group within tokenPattern which matches the
|
||||||
|
* character immediately preceding a possible token, possibly denoting that
|
||||||
|
* the token should instead be interpreted as a literal.
|
||||||
|
*/
|
||||||
|
private static final int ESCAPE_CHAR_GROUP = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index of the capturing group within tokenPattern which matches the
|
||||||
|
* entire token, including the leading "${" and terminating "}" strings.
|
||||||
|
*/
|
||||||
|
private static final int TOKEN_GROUP = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index of the capturing group within tokenPattern which matches only
|
||||||
|
* the token name contained within the "${" and "}" strings.
|
||||||
|
*/
|
||||||
|
private static final int TOKEN_NAME_GROUP = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The values of all known tokens.
|
||||||
|
*/
|
||||||
|
private final Map<String, String> tokenValues = new HashMap<String, String>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the token having the given name to the given value. Any existing
|
||||||
|
* value for that token is replaced.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* The name of the token to set.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The value to set the token to.
|
||||||
|
*/
|
||||||
|
public void setToken(String name, String value) {
|
||||||
|
tokenValues.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the token with the given name, or null if no such
|
||||||
|
* token has been set.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* The name of the token to return.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The value of the token with the given name, or null if no such
|
||||||
|
* token exists.
|
||||||
|
*/
|
||||||
|
public String getToken(String name) {
|
||||||
|
return tokenValues.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the value of the token with the given name. If no such token
|
||||||
|
* exists, this function has no effect.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* The name of the token whose value should be removed.
|
||||||
|
*/
|
||||||
|
public void unsetToken(String name) {
|
||||||
|
tokenValues.remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of all tokens, with each key being a token name, and each
|
||||||
|
* value being the corresponding token value. Changes to this map will
|
||||||
|
* directly affect the tokens associated with this filter.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A map of all token names and their corresponding values.
|
||||||
|
*/
|
||||||
|
public Map<String, String> getTokens() {
|
||||||
|
return tokenValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces all current token values with the contents of the given map,
|
||||||
|
* where each map key represents a token name, and each map value
|
||||||
|
* represents a token value.
|
||||||
|
*
|
||||||
|
* @param tokens
|
||||||
|
* A map containing the token names and corresponding values to
|
||||||
|
* assign.
|
||||||
|
*/
|
||||||
|
public void setTokens(Map<String, String> tokens) {
|
||||||
|
tokenValues.clear();
|
||||||
|
tokenValues.putAll(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the given string, replacing any tokens with their corresponding
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* @param input
|
||||||
|
* The string to filter.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A copy of the input string, with any tokens replaced with their
|
||||||
|
* corresponding values.
|
||||||
|
*/
|
||||||
|
public String filter(String input) {
|
||||||
|
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
Matcher tokenMatcher = tokenPattern.matcher(input);
|
||||||
|
|
||||||
|
// Track last regex match
|
||||||
|
int endOfLastMatch = 0;
|
||||||
|
|
||||||
|
// For each possible token
|
||||||
|
while (tokenMatcher.find()) {
|
||||||
|
|
||||||
|
// Pull possible leading text and first char before possible token
|
||||||
|
String literal = tokenMatcher.group(LEADING_TEXT_GROUP);
|
||||||
|
String escape = tokenMatcher.group(ESCAPE_CHAR_GROUP);
|
||||||
|
|
||||||
|
// Append leading non-token text
|
||||||
|
output.append(literal);
|
||||||
|
|
||||||
|
// If char before token is '$', the token itself is escaped
|
||||||
|
if ("$".equals(escape)) {
|
||||||
|
String notToken = tokenMatcher.group(TOKEN_GROUP);
|
||||||
|
output.append(notToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If char is not '$', interpret as a token
|
||||||
|
else {
|
||||||
|
|
||||||
|
// The char before the token, if any, is a literal
|
||||||
|
output.append(escape);
|
||||||
|
|
||||||
|
// Pull token value
|
||||||
|
String tokenName = tokenMatcher.group(TOKEN_NAME_GROUP);
|
||||||
|
String tokenValue = getToken(tokenName);
|
||||||
|
|
||||||
|
// If token is unknown, interpret as literal
|
||||||
|
if (tokenValue == null) {
|
||||||
|
String notToken = tokenMatcher.group(TOKEN_GROUP);
|
||||||
|
output.append(notToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, substitute value
|
||||||
|
else
|
||||||
|
output.append(tokenValue);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last regex match
|
||||||
|
endOfLastMatch = tokenMatcher.end();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append any remaining non-token text
|
||||||
|
output.append(input.substring(endOfLastMatch));
|
||||||
|
|
||||||
|
return output.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an arbitrary map containing String values, replace each non-null
|
||||||
|
* value with the corresponding filtered value.
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
* The map whose values should be filtered.
|
||||||
|
*/
|
||||||
|
public void filterValues(Map<?, String> map) {
|
||||||
|
|
||||||
|
// For each map entry
|
||||||
|
for (Map.Entry<?, String> entry : map.entrySet()) {
|
||||||
|
|
||||||
|
// If value is non-null, filter value through this TokenFilter
|
||||||
|
String value = entry.getValue();
|
||||||
|
if (value != null)
|
||||||
|
entry.setValue(filter(value));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* 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.token;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.Test;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test which verifies the filtering functionality of TokenFilter.
|
||||||
|
*
|
||||||
|
* @author Michael Jumper
|
||||||
|
*/
|
||||||
|
public class TokenFilterTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that token replacement via filter() functions as specified.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFilter() {
|
||||||
|
|
||||||
|
// Create token filter
|
||||||
|
TokenFilter tokenFilter = new TokenFilter();
|
||||||
|
tokenFilter.setToken("TOKEN_A", "value-of-a");
|
||||||
|
tokenFilter.setToken("TOKEN_B", "value-of-b");
|
||||||
|
|
||||||
|
// Test basic substitution and escaping
|
||||||
|
assertEquals(
|
||||||
|
"$${NOPE}hellovalue-of-aworldvalue-of-b${NOT_A_TOKEN}",
|
||||||
|
tokenFilter.filter("$$${NOPE}hello${TOKEN_A}world${TOKEN_B}$${NOT_A_TOKEN}")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Unknown tokens must be interpreted as literals
|
||||||
|
assertEquals(
|
||||||
|
"${NOPE}hellovalue-of-aworld${TOKEN_C}",
|
||||||
|
tokenFilter.filter("${NOPE}hello${TOKEN_A}world${TOKEN_C}")
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that token replacement via filterValues() functions as
|
||||||
|
* specified.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFilterValues() {
|
||||||
|
|
||||||
|
// Create token filter
|
||||||
|
TokenFilter tokenFilter = new TokenFilter();
|
||||||
|
tokenFilter.setToken("TOKEN_A", "value-of-a");
|
||||||
|
tokenFilter.setToken("TOKEN_B", "value-of-b");
|
||||||
|
|
||||||
|
// Create test map
|
||||||
|
Map<Integer, String> map = new HashMap<Integer, String>();
|
||||||
|
map.put(1, "$$${NOPE}hello${TOKEN_A}world${TOKEN_B}$${NOT_A_TOKEN}");
|
||||||
|
map.put(2, "${NOPE}hello${TOKEN_A}world${TOKEN_C}");
|
||||||
|
map.put(3, null);
|
||||||
|
|
||||||
|
// Filter map values
|
||||||
|
tokenFilter.filterValues(map);
|
||||||
|
|
||||||
|
// Filter should not affect size of map
|
||||||
|
assertEquals(3, map.size());
|
||||||
|
|
||||||
|
// Filtered value 1
|
||||||
|
assertEquals(
|
||||||
|
"$${NOPE}hellovalue-of-aworldvalue-of-b${NOT_A_TOKEN}",
|
||||||
|
map.get(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Filtered value 2
|
||||||
|
assertEquals(
|
||||||
|
"${NOPE}hellovalue-of-aworld${TOKEN_C}",
|
||||||
|
map.get(2)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Null values are not filtered
|
||||||
|
assertNull(map.get(3));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -63,6 +63,51 @@ angular.module('manage').directive('guacConnectionParameter', [function connecti
|
|||||||
var $q = $injector.get('$q');
|
var $q = $injector.get('$q');
|
||||||
var translationStringService = $injector.get('translationStringService');
|
var translationStringService = $injector.get('translationStringService');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type to use for password input fields. By default, password
|
||||||
|
* input fields have type 'password', and are thus masked.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
* @default 'password'
|
||||||
|
*/
|
||||||
|
$scope.passwordInputType = 'password';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string which describes the action the next call to
|
||||||
|
* togglePassword() will have.
|
||||||
|
*
|
||||||
|
* @return {String}
|
||||||
|
* A string which describes the action the next call to
|
||||||
|
* togglePassword() will have.
|
||||||
|
*/
|
||||||
|
$scope.getTogglePasswordHelpText = function getTogglePasswordHelpText() {
|
||||||
|
|
||||||
|
// If password is hidden, togglePassword() will show the password
|
||||||
|
if ($scope.passwordInputType === 'password')
|
||||||
|
return 'MANAGE.HELP_SHOW_PASSWORD';
|
||||||
|
|
||||||
|
// If password is shown, togglePassword() will hide the password
|
||||||
|
return 'MANAGE.HELP_HIDE_PASSWORD';
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles visibility of the parameter contents, if this parameter
|
||||||
|
* is a password parameter. Initially, password contents are
|
||||||
|
* masked (invisible).
|
||||||
|
*/
|
||||||
|
$scope.togglePassword = function togglePassword() {
|
||||||
|
|
||||||
|
// If password is hidden, show the password
|
||||||
|
if ($scope.passwordInputType === 'password')
|
||||||
|
$scope.passwordInputType = 'text';
|
||||||
|
|
||||||
|
// If password is shown, hide the password
|
||||||
|
else
|
||||||
|
$scope.passwordInputType = 'password';
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deferred load of the parameter definition, pending availability
|
* Deferred load of the parameter definition, pending availability
|
||||||
* of the protocol definition as a whole.
|
* of the protocol definition as a whole.
|
||||||
|
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Do not stretch connection parameters to fit available area */
|
||||||
|
.connection-parameter input[type=text],
|
||||||
|
.connection-parameter input[type=password],
|
||||||
|
.connection-parameter input[type=number] {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep toggle-password icon on same line */
|
||||||
|
.connection-parameter .password-field {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic 1x1em icon/button */
|
||||||
|
.connection-parameter .password-field .icon.toggle-password {
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 1em;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon for unmasking passwords */
|
||||||
|
.connection-parameter .password-field input[type=password] ~ .icon.toggle-password {
|
||||||
|
background-image: url('images/action-icons/guac-show-pass.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon for masking passwords */
|
||||||
|
.connection-parameter .password-field input[type=text] ~ .icon.toggle-password {
|
||||||
|
background-image: url('images/action-icons/guac-hide-pass.png');
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
<span class="connectionParameter">
|
<div class="connection-parameter">
|
||||||
<!--
|
<!--
|
||||||
Copyright 2014 Glyptodon LLC.
|
Copyright 2014 Glyptodon LLC.
|
||||||
|
|
||||||
@@ -25,13 +25,18 @@
|
|||||||
<input ng-show="parameter.type === 'TEXT'" type="text" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
<input ng-show="parameter.type === 'TEXT'" type="text" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
||||||
<input ng-show="parameter.type === 'NUMERIC'" type="number" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
<input ng-show="parameter.type === 'NUMERIC'" type="number" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
||||||
<input ng-show="parameter.type === 'USERNAME'" type="text" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
<input ng-show="parameter.type === 'USERNAME'" type="text" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
||||||
<input ng-show="parameter.type === 'PASSWORD'" type="password" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
|
||||||
<input ng-show="parameter.type === 'BOOLEAN'" type="checkbox" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
<input ng-show="parameter.type === 'BOOLEAN'" type="checkbox" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
|
||||||
|
|
||||||
|
<!-- Password parameter -->
|
||||||
|
<div ng-show="parameter.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 parameter -->
|
<!-- Multiline parameter -->
|
||||||
<textarea ng-show="parameter.type === 'MULTILINE'" ng-model="typedValue" autocorrect="off" autocapitalize="off"></textarea>
|
<textarea ng-show="parameter.type === 'MULTILINE'" ng-model="typedValue" autocorrect="off" autocapitalize="off"></textarea>
|
||||||
|
|
||||||
<!-- Enumerated parameter -->
|
<!-- Enumerated parameter -->
|
||||||
<select ng-show="parameter.type === 'ENUM'" ng-model="typedValue" ng-options="option.value as getProtocolParameterOption(protocol.name, parameter.name, option.value) | translate for option in parameter.options | orderBy: value"></select>
|
<select ng-show="parameter.type === 'ENUM'" ng-model="typedValue" ng-options="option.value as getProtocolParameterOption(protocol.name, parameter.name, option.value) | translate for option in parameter.options | orderBy: value"></select>
|
||||||
|
|
||||||
</span>
|
</div>
|
BIN
guacamole/src/main/webapp/images/action-icons/guac-hide-pass.png
Normal file
BIN
guacamole/src/main/webapp/images/action-icons/guac-hide-pass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 721 B |
BIN
guacamole/src/main/webapp/images/action-icons/guac-show-pass.png
Normal file
BIN
guacamole/src/main/webapp/images/action-icons/guac-show-pass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 709 B |
@@ -135,8 +135,10 @@
|
|||||||
|
|
||||||
"DIALOG_HEADER_ERROR" : "Error",
|
"DIALOG_HEADER_ERROR" : "Error",
|
||||||
|
|
||||||
"HELP_CONNECTIONS" : "Click or tap on a connection below to manage that connection. Depending on your access level, connections can be added and deleted, and their properties (protocol, hostname, port, etc.) can be changed.",
|
"HELP_CONNECTIONS" : "Click or tap on a connection below to manage that connection. Depending on your access level, connections can be added and deleted, and their properties (protocol, hostname, port, etc.) can be changed.",
|
||||||
"HELP_USERS" : "Click or tap on a user below to manage that user. Depending on your access level, users can be added and deleted, and their passwords can be changed.",
|
"HELP_SHOW_PASSWORD" : "Click to show password",
|
||||||
|
"HELP_HIDE_PASSWORD" : "Click to hide password",
|
||||||
|
"HELP_USERS" : "Click or tap on a user below to manage that user. Depending on your access level, users can be added and deleted, and their passwords can be changed.",
|
||||||
|
|
||||||
"SECTION_HEADER_ADMINISTRATION" : "Administration",
|
"SECTION_HEADER_ADMINISTRATION" : "Administration",
|
||||||
"SECTION_HEADER_CONNECTIONS" : "Connections",
|
"SECTION_HEADER_CONNECTIONS" : "Connections",
|
||||||
|
Reference in New Issue
Block a user