mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 21:27:40 +00:00
Merge pull request #195 from glyptodon/password-expiration
GUAC-1176: Implement password expiration attribute
This commit is contained in:
@@ -37,6 +37,7 @@ import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionPermis
|
||||
import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionGroupPermissionService;
|
||||
import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionService;
|
||||
import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionService;
|
||||
import org.glyptodon.guacamole.form.BooleanField;
|
||||
import org.glyptodon.guacamole.form.Field;
|
||||
import org.glyptodon.guacamole.form.Form;
|
||||
import org.glyptodon.guacamole.net.auth.User;
|
||||
@@ -58,12 +59,19 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
||||
*/
|
||||
public static final String DISABLED_ATTRIBUTE_NAME = "disabled";
|
||||
|
||||
/**
|
||||
* The name of the attribute which controls whether a user's password is
|
||||
* expired and must be reset upon login.
|
||||
*/
|
||||
public static final String EXPIRED_ATTRIBUTE_NAME = "expired";
|
||||
|
||||
/**
|
||||
* All attributes related to restricting user accounts, within a logical
|
||||
* form.
|
||||
*/
|
||||
public static final Form ACCOUNT_RESTRICTIONS = new Form("restrictions", "Account Restrictions", Arrays.asList(
|
||||
new Field(DISABLED_ATTRIBUTE_NAME, "Disabled", "true")
|
||||
public static final Form ACCOUNT_RESTRICTIONS = new Form("restrictions", Arrays.<Field>asList(
|
||||
new BooleanField(DISABLED_ATTRIBUTE_NAME, "true"),
|
||||
new BooleanField(EXPIRED_ATTRIBUTE_NAME, "true")
|
||||
));
|
||||
|
||||
/**
|
||||
@@ -214,7 +222,10 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
||||
Map<String, String> attributes = new HashMap<String, String>();
|
||||
|
||||
// Set disabled attribute
|
||||
attributes.put("disabled", getModel().isDisabled() ? "true" : null);
|
||||
attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null);
|
||||
|
||||
// Set password expired attribute
|
||||
attributes.put(EXPIRED_ATTRIBUTE_NAME, getModel().isExpired() ? "true" : null);
|
||||
|
||||
return attributes;
|
||||
}
|
||||
@@ -223,7 +234,10 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
|
||||
// Translate disabled attribute
|
||||
getModel().setDisabled("true".equals(attributes.get("disabled")));
|
||||
getModel().setDisabled("true".equals(attributes.get(DISABLED_ATTRIBUTE_NAME)));
|
||||
|
||||
// Translate password expired attribute
|
||||
getModel().setExpired("true".equals(attributes.get(EXPIRED_ATTRIBUTE_NAME)));
|
||||
|
||||
}
|
||||
|
||||
|
@@ -26,6 +26,8 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import org.glyptodon.guacamole.GuacamoleException;
|
||||
import org.glyptodon.guacamole.net.auth.Credentials;
|
||||
import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo;
|
||||
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
|
||||
|
||||
/**
|
||||
* Service which creates new UserContext instances for valid users based on
|
||||
@@ -49,17 +51,20 @@ public class UserContextService {
|
||||
|
||||
/**
|
||||
* Authenticates the user having the given credentials, returning a new
|
||||
* UserContext instance if the credentials are valid.
|
||||
* UserContext instance only if the credentials are valid. If the
|
||||
* credentials are invalid or expired, an appropriate GuacamoleException
|
||||
* will be thrown.
|
||||
*
|
||||
* @param credentials
|
||||
* The credentials to use to produce the UserContext.
|
||||
*
|
||||
* @return
|
||||
* A new UserContext instance for the user identified by the given
|
||||
* credentials, or null if the credentials are not valid.
|
||||
* credentials.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs during authentication.
|
||||
* If an error occurs during authentication, or if the given
|
||||
* credentials are invalid or expired.
|
||||
*/
|
||||
public org.glyptodon.guacamole.net.auth.UserContext
|
||||
getUserContext(Credentials credentials)
|
||||
@@ -67,7 +72,7 @@ public class UserContextService {
|
||||
|
||||
// Authenticate user
|
||||
ModeledUser user = userService.retrieveUser(credentials);
|
||||
if (user != null && !user.getModel().isDisabled()) {
|
||||
if (user != null) {
|
||||
|
||||
// Upon successful authentication, return new user context
|
||||
UserContext context = userContextProvider.get();
|
||||
@@ -77,7 +82,7 @@ public class UserContextService {
|
||||
}
|
||||
|
||||
// Otherwise, unauthorized
|
||||
return null;
|
||||
throw new GuacamoleInvalidCredentialsException("Invalid login", CredentialsInfo.USERNAME_PASSWORD);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -48,6 +48,13 @@ public class UserModel extends ObjectModel {
|
||||
*/
|
||||
private boolean disabled;
|
||||
|
||||
/**
|
||||
* Whether the user's password is expired. If a user's password is expired,
|
||||
* it must be changed immediately upon login, and the account cannot be
|
||||
* used until this occurs.
|
||||
*/
|
||||
private boolean expired;
|
||||
|
||||
/**
|
||||
* Creates a new, empty user.
|
||||
*/
|
||||
@@ -127,4 +134,28 @@ public class UserModel extends ObjectModel {
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user's password has expired. If a user's password is
|
||||
* expired, it must be immediately changed upon login. A user account with
|
||||
* an expired password cannot be used until the password has been changed.
|
||||
*
|
||||
* @return
|
||||
* true if the user's password has expired, false otherwise.
|
||||
*/
|
||||
public boolean isExpired() {
|
||||
return expired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the user's password is expired. If a user's password is
|
||||
* expired, it must be immediately changed upon login. A user account with
|
||||
* an expired password cannot be used until the password has been changed.
|
||||
*
|
||||
* @param expired
|
||||
* true to expire the user's password, false otherwise.
|
||||
*/
|
||||
public void setExpired(boolean expired) {
|
||||
this.expired = expired;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ import com.google.inject.Provider;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.glyptodon.guacamole.net.auth.Credentials;
|
||||
import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
|
||||
import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectService;
|
||||
@@ -37,11 +38,17 @@ import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper;
|
||||
import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel;
|
||||
import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionMapper;
|
||||
import org.glyptodon.guacamole.auth.jdbc.security.PasswordEncryptionService;
|
||||
import org.glyptodon.guacamole.form.Field;
|
||||
import org.glyptodon.guacamole.form.PasswordField;
|
||||
import org.glyptodon.guacamole.net.auth.User;
|
||||
import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo;
|
||||
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
|
||||
import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
|
||||
import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet;
|
||||
import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
|
||||
import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Service which provides convenience methods for creating, retrieving, and
|
||||
@@ -51,6 +58,11 @@ import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet;
|
||||
*/
|
||||
public class UserService extends ModeledDirectoryObjectService<ModeledUser, User, UserModel> {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
|
||||
|
||||
/**
|
||||
* All user permissions which are implicitly granted to the new user upon
|
||||
* creation.
|
||||
@@ -59,7 +71,43 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
||||
ObjectPermission.Type.READ,
|
||||
ObjectPermission.Type.UPDATE
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The name of the HTTP password parameter to expect if the user is
|
||||
* changing their expired password upon login.
|
||||
*/
|
||||
private static final String NEW_PASSWORD_PARAMETER = "new-password";
|
||||
|
||||
/**
|
||||
* The password field to provide the user when their password is expired
|
||||
* and must be changed.
|
||||
*/
|
||||
private static final Field NEW_PASSWORD = new PasswordField(NEW_PASSWORD_PARAMETER);
|
||||
|
||||
/**
|
||||
* The name of the HTTP password confirmation parameter to expect if the
|
||||
* user is changing their expired password upon login.
|
||||
*/
|
||||
private static final String CONFIRM_NEW_PASSWORD_PARAMETER = "confirm-new-password";
|
||||
|
||||
/**
|
||||
* The password confirmation field to provide the user when their password
|
||||
* is expired and must be changed.
|
||||
*/
|
||||
private static final Field CONFIRM_NEW_PASSWORD = new PasswordField(CONFIRM_NEW_PASSWORD_PARAMETER);
|
||||
|
||||
/**
|
||||
* Information describing the expected credentials if a user's password is
|
||||
* expired. If a user's password is expired, it must be changed during the
|
||||
* login process.
|
||||
*/
|
||||
private static final CredentialsInfo EXPIRED_PASSWORD = new CredentialsInfo(Arrays.asList(
|
||||
CredentialsInfo.USERNAME,
|
||||
CredentialsInfo.PASSWORD,
|
||||
NEW_PASSWORD,
|
||||
CONFIRM_NEW_PASSWORD
|
||||
));
|
||||
|
||||
/**
|
||||
* Mapper for accessing users.
|
||||
*/
|
||||
@@ -213,7 +261,9 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
||||
|
||||
/**
|
||||
* Retrieves the user corresponding to the given credentials from the
|
||||
* database.
|
||||
* database. If the user account is expired, and the credentials contain
|
||||
* the necessary additional parameters to reset the user's password, the
|
||||
* password is reset.
|
||||
*
|
||||
* @param credentials
|
||||
* The credentials to use when locating the user.
|
||||
@@ -221,8 +271,12 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
||||
* @return
|
||||
* The existing ModeledUser object if the credentials given are valid,
|
||||
* null otherwise.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the provided credentials to not conform to expectations.
|
||||
*/
|
||||
public ModeledUser retrieveUser(Credentials credentials) {
|
||||
public ModeledUser retrieveUser(Credentials credentials)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Get username and password
|
||||
String username = credentials.getUsername();
|
||||
@@ -233,19 +287,55 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
||||
if (userModel == null)
|
||||
return null;
|
||||
|
||||
// If password hash matches, return the retrieved user
|
||||
byte[] hash = encryptionService.createPasswordHash(password, userModel.getPasswordSalt());
|
||||
if (Arrays.equals(hash, userModel.getPasswordHash())) {
|
||||
// If user is disabled, pretend user does not exist
|
||||
if (userModel.isDisabled())
|
||||
return null;
|
||||
|
||||
// Return corresponding user, set up cyclic reference
|
||||
ModeledUser user = getObjectInstance(null, userModel);
|
||||
user.setCurrentUser(new AuthenticatedUser(user, credentials));
|
||||
return user;
|
||||
// Verify provided password is correct
|
||||
byte[] hash = encryptionService.createPasswordHash(password, userModel.getPasswordSalt());
|
||||
if (!Arrays.equals(hash, userModel.getPasswordHash()))
|
||||
return null;
|
||||
|
||||
// Create corresponding user object, set up cyclic reference
|
||||
ModeledUser user = getObjectInstance(null, userModel);
|
||||
user.setCurrentUser(new AuthenticatedUser(user, credentials));
|
||||
|
||||
// Update password if password is expired
|
||||
if (userModel.isExpired()) {
|
||||
|
||||
// Pull new password from HTTP request
|
||||
HttpServletRequest request = credentials.getRequest();
|
||||
String newPassword = request.getParameter(NEW_PASSWORD_PARAMETER);
|
||||
String confirmNewPassword = request.getParameter(CONFIRM_NEW_PASSWORD_PARAMETER);
|
||||
|
||||
// Require new password if account is expired
|
||||
if (newPassword == null || confirmNewPassword == null) {
|
||||
logger.info("The password of user \"{}\" has expired and must be reset.", username);
|
||||
throw new GuacamoleInsufficientCredentialsException("LOGIN.INFO_PASSWORD_EXPIRED", EXPIRED_PASSWORD);
|
||||
}
|
||||
|
||||
// New password must be different from old password
|
||||
if (newPassword.equals(credentials.getPassword()))
|
||||
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_SAME");
|
||||
|
||||
// New password must not be blank
|
||||
if (newPassword.isEmpty())
|
||||
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_BLANK");
|
||||
|
||||
// Confirm that the password was entered correctly twice
|
||||
if (!newPassword.equals(confirmNewPassword))
|
||||
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_MISMATCH");
|
||||
|
||||
// Change password and reset expiration flag
|
||||
userModel.setExpired(false);
|
||||
user.setPassword(newPassword);
|
||||
userMapper.update(userModel);
|
||||
logger.info("Expired password of user \"{}\" has been reset.", username);
|
||||
|
||||
}
|
||||
|
||||
// Otherwise, the credentials do not match
|
||||
return null;
|
||||
// Return now-authenticated user
|
||||
return user;
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,25 @@
|
||||
{
|
||||
|
||||
"LOGIN" : {
|
||||
|
||||
"ERROR_PASSWORD_BLANK" : "@:APP.ERROR_PASSWORD_BLANK",
|
||||
"ERROR_PASSWORD_SAME" : "The new password must be different from the expired password.",
|
||||
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
||||
|
||||
"INFO_PASSWORD_EXPIRED" : "Your password has expired and must be reset. Please enter a new password to continue.",
|
||||
|
||||
"FIELD_HEADER_NEW_PASSWORD" : "New password",
|
||||
"FIELD_HEADER_CONFIRM_NEW_PASSWORD" : "Confirm new password"
|
||||
|
||||
},
|
||||
|
||||
"USER_ATTRIBUTES" : {
|
||||
|
||||
"FIELD_HEADER_DISABLED" : "Login disabled:",
|
||||
"FIELD_HEADER_EXPIRED" : "Password expired:",
|
||||
|
||||
"SECTION_HEADER_RESTRICTIONS" : "Account Restrictions"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,13 @@
|
||||
{
|
||||
|
||||
"LOGIN" : {
|
||||
|
||||
"ERROR_PASSWORD_BLANK" : "@:APP.ERROR_PASSWORD_BLANK",
|
||||
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
||||
|
||||
"FIELD_HEADER_NEW_PASSWORD" : "Mot de passe",
|
||||
"FIELD_HEADER_CONFIRM_NEW_PASSWORD" : "Répéter mot de passe"
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
{
|
||||
|
||||
"LOGIN" : {
|
||||
|
||||
"ERROR_PASSWORD_BLANK" : "@:APP.ERROR_PASSWORD_BLANK",
|
||||
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
||||
|
||||
"FIELD_HEADER_NEW_PASSWORD" : "Новый пароль",
|
||||
"FIELD_HEADER_CONFIRM_NEW_PASSWORD" : "Подтверждение пароля"
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -77,6 +77,7 @@ CREATE TABLE `guacamole_user` (
|
||||
`password_hash` binary(32) NOT NULL,
|
||||
`password_salt` binary(32),
|
||||
`disabled` boolean NOT NULL DEFAULT 0,
|
||||
`expired` boolean NOT NULL DEFAULT 0,
|
||||
|
||||
PRIMARY KEY (`user_id`),
|
||||
UNIQUE KEY `username` (`username`)
|
||||
|
@@ -26,3 +26,9 @@
|
||||
|
||||
ALTER TABLE guacamole_user ADD COLUMN disabled BOOLEAN NOT NULL DEFAULT 0;
|
||||
|
||||
--
|
||||
-- Add per-user password expiration flag
|
||||
--
|
||||
|
||||
ALTER TABLE guacamole_user ADD COLUMN expired BOOLEAN NOT NULL DEFAULT 0;
|
||||
|
||||
|
@@ -10,7 +10,9 @@
|
||||
],
|
||||
|
||||
"translations" : [
|
||||
"translations/en_US.json"
|
||||
"translations/en.json",
|
||||
"translations/fr.json",
|
||||
"translations/ru.json"
|
||||
]
|
||||
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@
|
||||
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
|
||||
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
|
||||
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
|
||||
<result column="expired" property="expired" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- Select all usernames -->
|
||||
@@ -59,7 +60,8 @@
|
||||
username,
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled
|
||||
disabled,
|
||||
expired
|
||||
FROM guacamole_user
|
||||
WHERE username IN
|
||||
<foreach collection="identifiers" item="identifier"
|
||||
@@ -77,7 +79,8 @@
|
||||
username,
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled
|
||||
disabled,
|
||||
expired
|
||||
FROM guacamole_user
|
||||
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
|
||||
WHERE username IN
|
||||
@@ -98,7 +101,8 @@
|
||||
username,
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled
|
||||
disabled,
|
||||
expired
|
||||
FROM guacamole_user
|
||||
WHERE
|
||||
username = #{username,jdbcType=VARCHAR}
|
||||
@@ -119,13 +123,15 @@
|
||||
username,
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled
|
||||
disabled,
|
||||
expired
|
||||
)
|
||||
VALUES (
|
||||
#{object.identifier,jdbcType=VARCHAR},
|
||||
#{object.passwordHash,jdbcType=BINARY},
|
||||
#{object.passwordSalt,jdbcType=BINARY},
|
||||
#{object.disabled,jdbcType=BOOLEAN}
|
||||
#{object.disabled,jdbcType=BOOLEAN},
|
||||
#{object.expired,jdbcType=BOOLEAN}
|
||||
)
|
||||
|
||||
</insert>
|
||||
@@ -135,7 +141,8 @@
|
||||
UPDATE guacamole_user
|
||||
SET password_hash = #{object.passwordHash,jdbcType=BINARY},
|
||||
password_salt = #{object.passwordSalt,jdbcType=BINARY},
|
||||
disabled = #{object.disabled,jdbcType=BOOLEAN}
|
||||
disabled = #{object.disabled,jdbcType=BOOLEAN},
|
||||
expired = #{object.expired,jdbcType=BOOLEAN}
|
||||
WHERE user_id = #{object.objectID,jdbcType=VARCHAR}
|
||||
</update>
|
||||
|
||||
|
@@ -118,6 +118,7 @@ CREATE TABLE guacamole_user (
|
||||
password_hash bytea NOT NULL,
|
||||
password_salt bytea,
|
||||
disabled boolean NOT NULL DEFAULT FALSE,
|
||||
expired boolean NOT NULL DEFAULT FALSE,
|
||||
|
||||
PRIMARY KEY (user_id),
|
||||
|
||||
|
@@ -26,3 +26,9 @@
|
||||
|
||||
ALTER TABLE guacamole_user ADD COLUMN disabled boolean NOT NULL DEFAULT FALSE;
|
||||
|
||||
--
|
||||
-- Add per-user password expiration flag
|
||||
--
|
||||
|
||||
ALTER TABLE guacamole_user ADD COLUMN expired boolean NOT NULL DEFAULT FALSE;
|
||||
|
||||
|
@@ -10,7 +10,9 @@
|
||||
],
|
||||
|
||||
"translations" : [
|
||||
"translations/en_US.json"
|
||||
"translations/en.json",
|
||||
"translations/fr.json",
|
||||
"translations/ru.json"
|
||||
]
|
||||
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@
|
||||
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
|
||||
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
|
||||
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
|
||||
<result column="expired" property="expired" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- Select all usernames -->
|
||||
@@ -59,7 +60,8 @@
|
||||
username,
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled
|
||||
disabled,
|
||||
expired
|
||||
FROM guacamole_user
|
||||
WHERE username IN
|
||||
<foreach collection="identifiers" item="identifier"
|
||||
@@ -77,7 +79,8 @@
|
||||
username,
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled
|
||||
disabled,
|
||||
expired
|
||||
FROM guacamole_user
|
||||
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
|
||||
WHERE username IN
|
||||
@@ -98,7 +101,8 @@
|
||||
username,
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled
|
||||
disabled,
|
||||
expired
|
||||
FROM guacamole_user
|
||||
WHERE
|
||||
username = #{username,jdbcType=VARCHAR}
|
||||
@@ -119,13 +123,15 @@
|
||||
username,
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled
|
||||
disabled,
|
||||
expired
|
||||
)
|
||||
VALUES (
|
||||
#{object.identifier,jdbcType=VARCHAR},
|
||||
#{object.passwordHash,jdbcType=BINARY},
|
||||
#{object.passwordSalt,jdbcType=BINARY},
|
||||
#{object.disabled,jdbcType=BOOLEAN}
|
||||
#{object.disabled,jdbcType=BOOLEAN},
|
||||
#{object.expired,jdbcType=BOOLEAN}
|
||||
)
|
||||
|
||||
</insert>
|
||||
@@ -135,7 +141,8 @@
|
||||
UPDATE guacamole_user
|
||||
SET password_hash = #{object.passwordHash,jdbcType=BINARY},
|
||||
password_salt = #{object.passwordSalt,jdbcType=BINARY},
|
||||
disabled = #{object.disabled,jdbcType=BOOLEAN}
|
||||
disabled = #{object.disabled,jdbcType=BOOLEAN},
|
||||
expired = #{object.expired,jdbcType=BOOLEAN}
|
||||
WHERE user_id = #{object.objectID,jdbcType=VARCHAR}
|
||||
</update>
|
||||
|
||||
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.form;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Represents a field with strictly one possible value. It is assumed that the
|
||||
* field may be blank, but that its sole non-blank value is the value provided.
|
||||
* The provided value represents "true" while all other values, including
|
||||
* having no associated value, represent "false".
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class BooleanField extends Field {
|
||||
|
||||
/**
|
||||
* Creates a new BooleanField with the given name and truth value. The
|
||||
* truth value is the value that, when assigned to this field, means that
|
||||
* this field is "true".
|
||||
*
|
||||
* @param name
|
||||
* The unique name to associate with this field.
|
||||
*
|
||||
* @param truthValue
|
||||
* The value to consider "true" for this field. All other values will
|
||||
* be considered "false".
|
||||
*/
|
||||
public BooleanField(String name, String truthValue) {
|
||||
super(name, Field.Type.BOOLEAN, Collections.singletonList(truthValue));
|
||||
}
|
||||
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Glyptodon LLC
|
||||
* Copyright (C) 2015 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -20,42 +20,29 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.glyptodon.guacamole.form;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Service which defines the FieldOption class.
|
||||
* Represents an arbitrary field with a finite, enumerated set of possible
|
||||
* values.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
angular.module('rest').factory('FieldOption', [function defineFieldOption() {
|
||||
|
||||
public class EnumField extends Field {
|
||||
|
||||
/**
|
||||
* The object returned by REST API calls when representing a single possible
|
||||
* legal value of a field.
|
||||
*
|
||||
* @constructor
|
||||
* @param {FieldOption|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* FieldOption.
|
||||
* Creates a new EnumField with the given name and possible values.
|
||||
*
|
||||
* @param name
|
||||
* The unique name to associate with this field.
|
||||
*
|
||||
* @param options
|
||||
* All possible legal options for this field.
|
||||
*/
|
||||
var FieldOption = function FieldOption(template) {
|
||||
public EnumField(String name, Collection<String> options) {
|
||||
super(name, Field.Type.ENUM, options);
|
||||
}
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* A human-readable name for this parameter value.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.title = template.title;
|
||||
|
||||
/**
|
||||
* The actual value to set the parameter to, if this option is
|
||||
* selected.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.value = template.value;
|
||||
|
||||
};
|
||||
|
||||
return FieldOption;
|
||||
|
||||
}]);
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
* Copyright (C) 2015 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,7 +27,11 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||
|
||||
/**
|
||||
* Represents an arbitrary field, such as an HTTP parameter, the parameter of a
|
||||
* remote desktop protocol, or an input field within a form.
|
||||
* remote desktop protocol, or an input field within a form. Fields are generic
|
||||
* and typed dynamically through a type string, with the semantics of the field
|
||||
* defined by the type string. The behavior of each field type is defined
|
||||
* either through the web application itself (see FormService.js) or through
|
||||
* extensions.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
@@ -35,7 +39,11 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||
public class Field {
|
||||
|
||||
/**
|
||||
* All possible types of field.
|
||||
* All types of fields which are available by default. Additional field
|
||||
* types may be defined by extensions by using a unique field type name and
|
||||
* registering that name with the form service within JavaScript.
|
||||
*
|
||||
* See FormService.js.
|
||||
*/
|
||||
public static class Type {
|
||||
|
||||
@@ -83,67 +91,51 @@ public class Field {
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* A human-readable name to be presented to the user.
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* The type of this field.
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* The value of this field, when checked. This is only applicable to
|
||||
* BOOLEAN fields.
|
||||
* A collection of all legal values of this field.
|
||||
*/
|
||||
private String value;
|
||||
private Collection<String> options;
|
||||
|
||||
/**
|
||||
* A collection of all associated field options.
|
||||
*/
|
||||
private Collection<FieldOption> options;
|
||||
|
||||
/**
|
||||
* Creates a new Parameter with no associated name, title, or type.
|
||||
* Creates a new Parameter with no associated name or type.
|
||||
*/
|
||||
public Field() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Parameter with the given name, title, and type.
|
||||
* Creates a new Field with the given name and type.
|
||||
*
|
||||
* @param name
|
||||
* The unique name to associate with this field.
|
||||
*
|
||||
* @param title
|
||||
* The human-readable title to associate with this field.
|
||||
*
|
||||
* @param type
|
||||
* The type of this field.
|
||||
*/
|
||||
public Field(String name, String title, String type) {
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
this.type = type;
|
||||
public Field(String name, String type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ENUM Parameter with the given name, title, and options.
|
||||
* Creates a new Field with the given name, type, and possible values.
|
||||
*
|
||||
* @param name
|
||||
* The unique name to associate with this field.
|
||||
*
|
||||
* @param title
|
||||
* The human-readable title to associate with this field.
|
||||
* @param type
|
||||
* The type of this field.
|
||||
*
|
||||
* @param options
|
||||
* A collection of all possible valid options for this field.
|
||||
*/
|
||||
public Field(String name, String title, Collection<FieldOption> options) {
|
||||
public Field(String name, String type, Collection<String> options) {
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
this.type = Type.ENUM;
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@@ -167,49 +159,6 @@ public class Field {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human-readable title associated with this field.
|
||||
*
|
||||
* @return
|
||||
* The human-readable title associated with this field.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title associated with this field. The title must be a human-
|
||||
* readable string which describes accurately this field.
|
||||
*
|
||||
* @param title
|
||||
* A human-readable string describing this field.
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value that should be assigned to this field if enabled. This
|
||||
* is only applicable to BOOLEAN fields.
|
||||
*
|
||||
* @return
|
||||
* The value that should be assigned to this field if enabled.
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value that should be assigned to this field if enabled. This is
|
||||
* only applicable to BOOLEAN fields.
|
||||
*
|
||||
* @param value
|
||||
* The value that should be assigned to this field if enabled.
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this field.
|
||||
*
|
||||
@@ -232,25 +181,23 @@ public class Field {
|
||||
|
||||
/**
|
||||
* Returns a mutable collection of field options. Changes to this
|
||||
* collection directly affect the available options. This is only
|
||||
* applicable to ENUM fields.
|
||||
* collection directly affect the available options.
|
||||
*
|
||||
* @return
|
||||
* A mutable collection of field options, or null if the field has no
|
||||
* options.
|
||||
*/
|
||||
public Collection<FieldOption> getOptions() {
|
||||
public Collection<String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the options available as possible values of this field. This is
|
||||
* only applicable to ENUM fields.
|
||||
* Sets the options available as possible values of this field.
|
||||
*
|
||||
* @param options
|
||||
* The options to associate with this field.
|
||||
*/
|
||||
public void setOptions(Collection<FieldOption> options) {
|
||||
public void setOptions(Collection<String> options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
|
@@ -40,41 +40,32 @@ public class Form {
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The a human-readable title describing this form.
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* All fields associated with this form.
|
||||
*/
|
||||
private Collection<Field> fields;
|
||||
|
||||
/**
|
||||
* Creates a new Form object with no associated fields. The name and title
|
||||
* of the form are left unset as null. If no form name is provided, this
|
||||
* form must not be used in the same context as another unnamed form.
|
||||
* Creates a new Form object with no associated fields. The name is left
|
||||
* unset as null. If no form name is provided, this form must not be used
|
||||
* in the same context as another unnamed form.
|
||||
*/
|
||||
public Form() {
|
||||
fields = new ArrayList<Field>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Form object having the given name and title, and
|
||||
* containing the given fields.
|
||||
* Creates a new Form object having the given name and containing the given
|
||||
* fields.
|
||||
*
|
||||
* @param name
|
||||
* A name which uniquely identifies this form.
|
||||
*
|
||||
* @param title
|
||||
* A human-readable title describing this form.
|
||||
*
|
||||
* @param fields
|
||||
* The fields to provided within the new Form.
|
||||
*/
|
||||
public Form(String name, String title, Collection<Field> fields) {
|
||||
public Form(String name, Collection<Field> fields) {
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
@@ -120,26 +111,4 @@ public class Form {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human-readable title associated with this form. A form's
|
||||
* title describes the form, but need not be unique.
|
||||
*
|
||||
* @return
|
||||
* A human-readable title describing this form.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the human-readable title associated with this form. A form's title
|
||||
* describes the form, but need not be unique.
|
||||
*
|
||||
* @param title
|
||||
* A human-readable title describing this form.
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.form;
|
||||
|
||||
/**
|
||||
* Represents a field which can contain multiple lines of text.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class MultilineField extends Field {
|
||||
|
||||
/**
|
||||
* Creates a new MultilineField with the given name.
|
||||
*
|
||||
* @param name
|
||||
* The unique name to associate with this field.
|
||||
*/
|
||||
public MultilineField(String name) {
|
||||
super(name, Field.Type.MULTILINE);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.form;
|
||||
|
||||
/**
|
||||
* Represents a field which may contain only integer values.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class NumericField extends Field {
|
||||
|
||||
/**
|
||||
* Creates a new NumericField with the given name.
|
||||
*
|
||||
* @param name
|
||||
* The unique name to associate with this field.
|
||||
*/
|
||||
public NumericField(String name) {
|
||||
super(name, Field.Type.NUMERIC);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.form;
|
||||
|
||||
/**
|
||||
* Represents a field which contains sensitive text information related to
|
||||
* authenticating a user.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class PasswordField extends Field {
|
||||
|
||||
/**
|
||||
* Creates a new PasswordField with the given name.
|
||||
*
|
||||
* @param name
|
||||
* The unique name to associate with this field.
|
||||
*/
|
||||
public PasswordField(String name) {
|
||||
super(name, Field.Type.PASSWORD);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.form;
|
||||
|
||||
/**
|
||||
* Represents a basic text field. The field may generally contain any data, but
|
||||
* may not contain multiple lines.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class TextField extends Field {
|
||||
|
||||
/**
|
||||
* Creates a new TextField with the given name.
|
||||
*
|
||||
* @param name
|
||||
* The unique name to associate with this field.
|
||||
*/
|
||||
public TextField(String name) {
|
||||
super(name, Field.Type.TEXT);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.form;
|
||||
|
||||
/**
|
||||
* Represents a text field which will contain the uniquely-identifying name of
|
||||
* a user.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class UsernameField extends Field {
|
||||
|
||||
/**
|
||||
* Creates a new UsernameField with the given name.
|
||||
*
|
||||
* @param name
|
||||
* The unique name to associate with this field.
|
||||
*/
|
||||
public UsernameField(String name) {
|
||||
super(name, Field.Type.USERNAME);
|
||||
}
|
||||
|
||||
}
|
@@ -26,6 +26,8 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import org.glyptodon.guacamole.form.Field;
|
||||
import org.glyptodon.guacamole.form.PasswordField;
|
||||
import org.glyptodon.guacamole.form.UsernameField;
|
||||
|
||||
/**
|
||||
* Information which describes a set of valid credentials.
|
||||
@@ -67,13 +69,25 @@ public class CredentialsInfo {
|
||||
*/
|
||||
public static final CredentialsInfo EMPTY = new CredentialsInfo(Collections.<Field>emptyList());
|
||||
|
||||
/**
|
||||
* A field describing the username HTTP parameter expected by Guacamole
|
||||
* during login, if usernames are being used.
|
||||
*/
|
||||
public static final Field USERNAME = new UsernameField("username");
|
||||
|
||||
/**
|
||||
* A field describing the password HTTP parameter expected by Guacamole
|
||||
* during login, if passwords are being used.
|
||||
*/
|
||||
public static final Field PASSWORD = new PasswordField("password");
|
||||
|
||||
/**
|
||||
* CredentialsInfo object which describes standard username/password
|
||||
* credentials.
|
||||
*/
|
||||
public static final CredentialsInfo USERNAME_PASSWORD = new CredentialsInfo(Arrays.asList(
|
||||
new Field("username", "username", Field.Type.USERNAME),
|
||||
new Field("password", "password", Field.Type.PASSWORD)
|
||||
USERNAME,
|
||||
PASSWORD
|
||||
));
|
||||
|
||||
}
|
||||
|
@@ -35,11 +35,6 @@ import org.glyptodon.guacamole.form.Form;
|
||||
*/
|
||||
public class ProtocolInfo {
|
||||
|
||||
/**
|
||||
* The human-readable title associated with this protocol.
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* The unique name associated with this protocol.
|
||||
*/
|
||||
@@ -51,65 +46,37 @@ public class ProtocolInfo {
|
||||
private Collection<Form> forms;
|
||||
|
||||
/**
|
||||
* Creates a new ProtocolInfo with no associated name, title, or
|
||||
* forms.
|
||||
* Creates a new ProtocolInfo with no associated name or forms.
|
||||
*/
|
||||
public ProtocolInfo() {
|
||||
this.forms = new ArrayList<Form>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ProtocolInfo having the given name and title, but without
|
||||
* any forms.
|
||||
* Creates a new ProtocolInfo having the given name, but without any forms.
|
||||
*
|
||||
* @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) {
|
||||
public ProtocolInfo(String name) {
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
this.forms = new ArrayList<Form>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ProtocolInfo having the given name, title, and forms.
|
||||
* Creates a new ProtocolInfo having the given name and forms.
|
||||
*
|
||||
* @param name
|
||||
* The unique name associated with the protocol.
|
||||
*
|
||||
* @param title
|
||||
* The human-readable title to associate with the protocol.
|
||||
*
|
||||
* @param forms
|
||||
* The forms to associate with the protocol.
|
||||
*/
|
||||
public ProtocolInfo(String name, String title, Collection<Form> forms) {
|
||||
public ProtocolInfo(String name, Collection<Form> forms) {
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
this.forms = forms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human-readable title associated with this protocol.
|
||||
*
|
||||
* @return The human-readable title associated with this protocol.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the human-readable title associated with this protocol.
|
||||
*
|
||||
* @param title The human-readable title to associate with this protocol.
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique name of this protocol. The protocol name is the
|
||||
* value required by the corresponding protocol plugin for guacd.
|
||||
|
@@ -1,251 +1,156 @@
|
||||
{
|
||||
"title" : "RDP",
|
||||
"name" : "rdp",
|
||||
"forms" : [
|
||||
|
||||
{
|
||||
"title" : "Network",
|
||||
"name" : "network",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "hostname",
|
||||
"title" : "Hostname",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "port",
|
||||
"title" : "Port",
|
||||
"type" : "NUMERIC"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Authentication",
|
||||
"name" : "authentication",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "username",
|
||||
"title" : "Username",
|
||||
"type" : "USERNAME"
|
||||
},
|
||||
{
|
||||
"name" : "password",
|
||||
"title" : "Password",
|
||||
"type" : "PASSWORD"
|
||||
},
|
||||
{
|
||||
"name" : "domain",
|
||||
"title" : "Domain",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "security",
|
||||
"title" : "Security mode",
|
||||
"type" : "ENUM",
|
||||
"options" : [
|
||||
{
|
||||
"value" : "",
|
||||
"title" : ""
|
||||
},
|
||||
{
|
||||
"value" : "rdp",
|
||||
"title" : "RDP encryption"
|
||||
},
|
||||
{
|
||||
"value" : "tls",
|
||||
"title" : "TLS encryption"
|
||||
},
|
||||
{
|
||||
"value" : "nla",
|
||||
"title" : "NLA (Network Level Authentication)"
|
||||
},
|
||||
{
|
||||
"value" : "any",
|
||||
"title" : "Any"
|
||||
}
|
||||
]
|
||||
"options" : [ "", "rdp", "tls", "nla", "any" ]
|
||||
},
|
||||
{
|
||||
"name" : "disable-auth",
|
||||
"title" : "Disable authentication",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "disable-auth",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
},
|
||||
{
|
||||
"name" : "ignore-cert",
|
||||
"title" : "Ignore server certificate",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "ignore-cert",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Basic Parameters",
|
||||
"name" : "basic-parameters",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "initial-program",
|
||||
"title" : "Initial program",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "client-name",
|
||||
"title" : "Client name",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "server-layout",
|
||||
"title" : "Keyboard layout",
|
||||
"type" : "ENUM",
|
||||
"options" : [
|
||||
{
|
||||
"value" : "",
|
||||
"title" : ""
|
||||
},
|
||||
{
|
||||
"value" : "en-us-qwerty",
|
||||
"title" : "US English (Qwerty)"
|
||||
},
|
||||
{
|
||||
"value" : "fr-fr-azerty",
|
||||
"title" : "French (Azerty)"
|
||||
},
|
||||
{
|
||||
"value" : "de-de-qwertz",
|
||||
"title" : "German (Qwertz)"
|
||||
},
|
||||
{
|
||||
"value" : "it-it-qwerty",
|
||||
"title" : "Italian (Qwerty)"
|
||||
},
|
||||
{
|
||||
"value" : "sv-se-qwerty",
|
||||
"title" : "Swedish (Qwerty)"
|
||||
},
|
||||
{
|
||||
"value" : "failsafe",
|
||||
"title" : "Unicode"
|
||||
}
|
||||
"",
|
||||
"en-us-qwerty",
|
||||
"fr-fr-azerty",
|
||||
"de-de-qwertz",
|
||||
"it-it-qwerty",
|
||||
"sv-se-qwerty",
|
||||
"failsafe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "console",
|
||||
"title" : "Administrator console",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "console",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Display",
|
||||
"name" : "display",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "width",
|
||||
"title" : "Display width",
|
||||
"type" : "NUMERIC"
|
||||
},
|
||||
{
|
||||
"name" : "height",
|
||||
"title" : "Display height",
|
||||
"type" : "NUMERIC"
|
||||
},
|
||||
{
|
||||
"name" : "dpi",
|
||||
"title" : "Display resolution (DPI)",
|
||||
"type" : "NUMERIC"
|
||||
},
|
||||
{
|
||||
"name" : "color-depth",
|
||||
"title" : "Color depth",
|
||||
"type" : "ENUM",
|
||||
"options" : [
|
||||
{
|
||||
"value" : "",
|
||||
"title" : ""
|
||||
},
|
||||
{
|
||||
"value" : "8",
|
||||
"title" : "256 color"
|
||||
},
|
||||
{
|
||||
"value" : "16",
|
||||
"title" : "Low color (16-bit)"
|
||||
},
|
||||
{
|
||||
"value" : "24",
|
||||
"title" : "True color (24-bit)"
|
||||
},
|
||||
{
|
||||
"value" : "32",
|
||||
"title" : "True color (32-bit)"
|
||||
}
|
||||
]
|
||||
"options" : [ "", "8", "16", "24", "32" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Device Redirection",
|
||||
"name" : "device-redirection",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "console-audio",
|
||||
"title" : "Support audio in console",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "console-audio",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
},
|
||||
{
|
||||
"name" : "disable-audio",
|
||||
"title" : "Disable audio",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "disable-audio",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
},
|
||||
{
|
||||
"name" : "enable-printing",
|
||||
"title" : "Enable printing",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "enable-printing",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
},
|
||||
{
|
||||
"name" : "enable-drive",
|
||||
"title" : "Enable drive",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "enable-drive",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
},
|
||||
{
|
||||
"name" : "drive-path",
|
||||
"title" : "Drive path",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "static-channels",
|
||||
"title" : "Static channel names",
|
||||
"type" : "TEXT"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "RemoteApp",
|
||||
"name" : "remoteapp",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "remote-app",
|
||||
"title" : "RemoteApp program",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "remote-app-dir",
|
||||
"title" : "RemoteApp working directory",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "remote-app-args",
|
||||
"title" : "RemoteApp parameters",
|
||||
"type" : "TEXT"
|
||||
}
|
||||
]
|
||||
|
@@ -1,140 +1,65 @@
|
||||
{
|
||||
"title" : "SSH",
|
||||
"name" : "ssh",
|
||||
"forms" : [
|
||||
|
||||
{
|
||||
"title" : "Network",
|
||||
"name" : "network",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "hostname",
|
||||
"title" : "Hostname",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "port",
|
||||
"title" : "Port",
|
||||
"type" : "NUMERIC"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Authentication",
|
||||
"name" : "authentication",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "username",
|
||||
"title" : "Username",
|
||||
"type" : "USERNAME"
|
||||
},
|
||||
{
|
||||
"name" : "password",
|
||||
"title" : "Password",
|
||||
"type" : "PASSWORD"
|
||||
},
|
||||
{
|
||||
"name" : "private-key",
|
||||
"title" : "Private key",
|
||||
"type" : "MULTILINE"
|
||||
},
|
||||
{
|
||||
"name" : "passphrase",
|
||||
"title" : "Passphrase",
|
||||
"type" : "PASSWORD"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Display",
|
||||
"name" : "display",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "font-name",
|
||||
"title" : "Font name",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "font-size",
|
||||
"title" : "Font size",
|
||||
"type" : "ENUM",
|
||||
"options" : [
|
||||
{
|
||||
"value" : "",
|
||||
"title" : ""
|
||||
},
|
||||
{
|
||||
"value" : "8",
|
||||
"title" : "8"
|
||||
},
|
||||
{
|
||||
"value" : "9",
|
||||
"title" : "9"
|
||||
},
|
||||
{
|
||||
"value" : "10",
|
||||
"title" : "10"
|
||||
},
|
||||
{
|
||||
"value" : "11",
|
||||
"title" : "11"
|
||||
},
|
||||
{
|
||||
"value" : "12",
|
||||
"title" : "12"
|
||||
},
|
||||
{
|
||||
"value" : "14",
|
||||
"title" : "14"
|
||||
},
|
||||
{
|
||||
"value" : "18",
|
||||
"title" : "18"
|
||||
},
|
||||
{
|
||||
"value" : "24",
|
||||
"title" : "24"
|
||||
},
|
||||
{
|
||||
"value" : "30",
|
||||
"title" : "30"
|
||||
},
|
||||
{
|
||||
"value" : "36",
|
||||
"title" : "36"
|
||||
},
|
||||
{
|
||||
"value" : "48",
|
||||
"title" : "48"
|
||||
},
|
||||
{
|
||||
"value" : "60",
|
||||
"title" : "60"
|
||||
},
|
||||
{
|
||||
"value" : "72",
|
||||
"title" : "72"
|
||||
},
|
||||
{
|
||||
"value" : "96",
|
||||
"title" : "96"
|
||||
}
|
||||
]
|
||||
"options" : [ "", "8", "9", "10", "11", "12", "14", "18", "24", "30", "36", "48", "60", "72", "96" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "SFTP",
|
||||
"name" : "sftp",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "enable-sftp",
|
||||
"title" : "Enable SFTP",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "enable-sftp",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -1,122 +1,50 @@
|
||||
{
|
||||
"title" : "Telnet",
|
||||
"name" : "telnet",
|
||||
"forms" : [
|
||||
|
||||
{
|
||||
"title" : "Network",
|
||||
"name" : "network",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "hostname",
|
||||
"title" : "Hostname",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "port",
|
||||
"title" : "Port",
|
||||
"type" : "NUMERIC"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Authentication",
|
||||
"name" : "authentication",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "username",
|
||||
"title" : "Username",
|
||||
"type" : "USERNAME"
|
||||
},
|
||||
{
|
||||
"name" : "password",
|
||||
"title" : "Password",
|
||||
"type" : "PASSWORD"
|
||||
},
|
||||
{
|
||||
"name" : "password-regex",
|
||||
"title" : "Password regular expression",
|
||||
"type" : "TEXT"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Display",
|
||||
"name" : "display",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "font-name",
|
||||
"title" : "Font name",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "font-size",
|
||||
"title" : "Font size",
|
||||
"type" : "ENUM",
|
||||
"options" : [
|
||||
{
|
||||
"value" : "",
|
||||
"title" : ""
|
||||
},
|
||||
{
|
||||
"value" : "8",
|
||||
"title" : "8"
|
||||
},
|
||||
{
|
||||
"value" : "9",
|
||||
"title" : "9"
|
||||
},
|
||||
{
|
||||
"value" : "10",
|
||||
"title" : "10"
|
||||
},
|
||||
{
|
||||
"value" : "11",
|
||||
"title" : "11"
|
||||
},
|
||||
{
|
||||
"value" : "12",
|
||||
"title" : "12"
|
||||
},
|
||||
{
|
||||
"value" : "14",
|
||||
"title" : "14"
|
||||
},
|
||||
{
|
||||
"value" : "18",
|
||||
"title" : "18"
|
||||
},
|
||||
{
|
||||
"value" : "24",
|
||||
"title" : "24"
|
||||
},
|
||||
{
|
||||
"value" : "30",
|
||||
"title" : "30"
|
||||
},
|
||||
{
|
||||
"value" : "36",
|
||||
"title" : "36"
|
||||
},
|
||||
{
|
||||
"value" : "48",
|
||||
"title" : "48"
|
||||
},
|
||||
{
|
||||
"value" : "60",
|
||||
"title" : "60"
|
||||
},
|
||||
{
|
||||
"value" : "72",
|
||||
"title" : "72"
|
||||
},
|
||||
{
|
||||
"value" : "96",
|
||||
"title" : "96"
|
||||
}
|
||||
]
|
||||
"options" : [ "", "8", "9", "10", "11", "12", "14", "18", "24", "30", "36", "48", "60", "72", "96" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -1,132 +1,81 @@
|
||||
{
|
||||
"title" : "VNC",
|
||||
"name" : "vnc",
|
||||
"forms" : [
|
||||
|
||||
{
|
||||
"title" : "Network",
|
||||
"name" : "network",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "hostname",
|
||||
"title" : "Hostname",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "port",
|
||||
"title" : "Port",
|
||||
"type" : "NUMERIC"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Authentication",
|
||||
"name" : "authentication",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "password",
|
||||
"title" : "Password",
|
||||
"type" : "PASSWORD"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Display",
|
||||
"name" : "display",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "read-only",
|
||||
"title" : "Read-only",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "read-only",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
},
|
||||
{
|
||||
"name" : "swap-red-blue",
|
||||
"title" : "Swap red/blue components",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "swap-red-blue",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
},
|
||||
{
|
||||
"name" : "cursor",
|
||||
"title" : "Cursor",
|
||||
"type" : "ENUM",
|
||||
"options" : [
|
||||
{
|
||||
"value" : "",
|
||||
"title" : ""
|
||||
},
|
||||
{
|
||||
"value" : "local",
|
||||
"title" : "Local"
|
||||
},
|
||||
{
|
||||
"value" : "remote",
|
||||
"title" : "Remote"
|
||||
}
|
||||
]
|
||||
"options" : [ "", "local", "remote" ]
|
||||
},
|
||||
{
|
||||
"name" : "color-depth",
|
||||
"title" : "Color depth",
|
||||
"type" : "ENUM",
|
||||
"options" : [
|
||||
{
|
||||
"value" : "",
|
||||
"title" : ""
|
||||
},
|
||||
{
|
||||
"value" : "8",
|
||||
"title" : "256 color"
|
||||
},
|
||||
{
|
||||
"value" : "16",
|
||||
"title" : "Low color (16-bit)"
|
||||
},
|
||||
{
|
||||
"value" : "24",
|
||||
"title" : "True color (24-bit)"
|
||||
},
|
||||
{
|
||||
"value" : "32",
|
||||
"title" : "True color (32-bit)"
|
||||
}
|
||||
]
|
||||
"options" : [ "", "8", "16", "24", "32" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Repeater",
|
||||
"name" : "repeater",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "dest-host",
|
||||
"title" : "Repeater destination host",
|
||||
"type" : "TEXT"
|
||||
},
|
||||
{
|
||||
"name" : "dest-port",
|
||||
"title" : "Repeater destination port",
|
||||
"type" : "NUMERIC"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"title" : "Audio",
|
||||
"name" : "audio",
|
||||
"fields" : [
|
||||
{
|
||||
"name" : "enable-audio",
|
||||
"title" : "Enable audio",
|
||||
"type" : "BOOLEAN",
|
||||
"value" : "true"
|
||||
"name" : "enable-audio",
|
||||
"type" : "BOOLEAN",
|
||||
"options" : [ "true" ]
|
||||
},
|
||||
{
|
||||
"name" : "audio-servername",
|
||||
"title" : "Audio server name",
|
||||
"type" : "TEXT"
|
||||
}
|
||||
]
|
||||
|
@@ -36,7 +36,8 @@
|
||||
* 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
|
||||
* the set of parameters originally given to authenticate() and the error that
|
||||
* rejected the credentials. The Error object provided will contain set of
|
||||
* expected credentials returned by the REST endpoint. This set of credentials
|
||||
* will be in the form of a Field array.
|
||||
*/
|
||||
@@ -154,13 +155,13 @@ angular.module('auth').factory('authenticationService', ['$injector',
|
||||
|
||||
// Request credentials if provided credentials were invalid
|
||||
if (error.type === Error.Type.INVALID_CREDENTIALS)
|
||||
$rootScope.$broadcast('guacInvalidCredentials', parameters, error.expected);
|
||||
$rootScope.$broadcast('guacInvalidCredentials', parameters, error);
|
||||
|
||||
// Request more credentials if provided credentials were not enough
|
||||
else if (error.type === Error.Type.INSUFFICIENT_CREDENTIALS)
|
||||
$rootScope.$broadcast('guacInsufficientCredentials', parameters, error.expected);
|
||||
$rootScope.$broadcast('guacInsufficientCredentials', parameters, error);
|
||||
|
||||
authenticationProcess.reject();
|
||||
authenticationProcess.reject(error);
|
||||
});
|
||||
|
||||
return authenticationProcess.promise;
|
||||
|
@@ -29,12 +29,12 @@ angular.module('form').controller('checkboxFieldController', ['$scope',
|
||||
|
||||
// Update typed value when model is changed
|
||||
$scope.$watch('model', function modelChanged(model) {
|
||||
$scope.typedValue = (model === $scope.field.value);
|
||||
$scope.typedValue = (model === $scope.field.options[0]);
|
||||
});
|
||||
|
||||
// Update string value in model when typed value is changed
|
||||
$scope.$watch('typedValue', function typedValueChanged(typedValue) {
|
||||
$scope.model = (typedValue ? $scope.field.value : '');
|
||||
$scope.model = (typedValue ? $scope.field.options[0] : '');
|
||||
});
|
||||
|
||||
}]);
|
||||
|
@@ -30,6 +30,12 @@ angular.module('form').controller('selectFieldController', ['$scope', '$injector
|
||||
// Required services
|
||||
var translationStringService = $injector.get('translationStringService');
|
||||
|
||||
// Interpret undefined/null as empty string
|
||||
$scope.$watch('model', function setModel(model) {
|
||||
if (!model && model !== '')
|
||||
$scope.model = '';
|
||||
});
|
||||
|
||||
/**
|
||||
* Produces the translation string for the given field option
|
||||
* value. The translation string will be of the form:
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div class="password-field">
|
||||
<input type="{{passwordInputType}}" ng-model="model" autocorrect="off" autocapitalize="off"/>
|
||||
<input type="{{passwordInputType}}" ng-model="model" ng-trim="false" autocorrect="off" autocapitalize="off"/>
|
||||
<div class="icon toggle-password" ng-click="togglePassword()" title="{{getTogglePasswordHelpText() | translate}}"></div>
|
||||
</div>
|
@@ -1 +1 @@
|
||||
<select ng-model="model" ng-options="option.value as getFieldOption(option.value) | translate for option in field.options | orderBy: value"></select>
|
||||
<select ng-model="model" ng-options="option as getFieldOption(option) | translate for option in field.options | orderBy: value"></select>
|
@@ -36,11 +36,30 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
||||
*/
|
||||
$scope.guacNotification = guacNotification;
|
||||
|
||||
/**
|
||||
* The message to display to the user as instructions for the login
|
||||
* process.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
$scope.loginHelpText = null;
|
||||
|
||||
/**
|
||||
* The credentials that the authentication service is has already accepted,
|
||||
* pending additional credentials, if any. If the user is logged in, or no
|
||||
* credentials have been accepted, this will be null. If credentials have
|
||||
* been accepted, this will be a map of name/value pairs corresponding to
|
||||
* the parameters submitted in a previous authentication attempt.
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
$scope.acceptedCredentials = null;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @type Field[]
|
||||
*/
|
||||
$scope.expectedCredentials = null;
|
||||
|
||||
@@ -109,21 +128,27 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
||||
};
|
||||
|
||||
// Display login screen if a whole new set of credentials is needed
|
||||
$scope.$on('guacInvalidCredentials', function loginInvalid(event, parameters, expected) {
|
||||
$scope.$on('guacInvalidCredentials', function loginInvalid(event, parameters, error) {
|
||||
$scope.page.title = 'APP.NAME';
|
||||
$scope.page.bodyClassName = '';
|
||||
$scope.expectedCredentials = expected;
|
||||
$scope.loginHelpText = null;
|
||||
$scope.acceptedCredentials = {};
|
||||
$scope.expectedCredentials = error.expected;
|
||||
});
|
||||
|
||||
// Prompt for remaining credentials if provided credentials were not enough
|
||||
$scope.$on('guacInsufficientCredentials', function loginInsufficient(event, parameters, expected) {
|
||||
$scope.$on('guacInsufficientCredentials', function loginInsufficient(event, parameters, error) {
|
||||
$scope.page.title = 'APP.NAME';
|
||||
$scope.page.bodyClassName = '';
|
||||
$scope.expectedCredentials = expected;
|
||||
$scope.loginHelpText = error.message;
|
||||
$scope.acceptedCredentials = parameters;
|
||||
$scope.expectedCredentials = error.expected;
|
||||
});
|
||||
|
||||
// Clear login screen if login was successful
|
||||
$scope.$on('guacLogin', function loginSuccessful() {
|
||||
$scope.loginHelpText = null;
|
||||
$scope.acceptedCredentials = null;
|
||||
$scope.expectedCredentials = null;
|
||||
});
|
||||
|
||||
|
@@ -35,13 +35,28 @@ angular.module('login').directive('guacLogin', [function guacLogin() {
|
||||
// Login directive scope
|
||||
directive.scope = {
|
||||
|
||||
/**
|
||||
* An optional instructional message to display within the login
|
||||
* dialog.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
helpText : '=',
|
||||
|
||||
/**
|
||||
* The login form or set of fields. This will be displayed to the user
|
||||
* to capture their credentials.
|
||||
*
|
||||
* @type Form[]|Form|Field[]|Field
|
||||
* @type Field[]
|
||||
*/
|
||||
form : '='
|
||||
form : '=',
|
||||
|
||||
/**
|
||||
* A map of all field name/value pairs that have already been provided.
|
||||
* If not null, the user will be prompted to continue their login
|
||||
* attempt using only the fields which remain.
|
||||
*/
|
||||
values : '='
|
||||
|
||||
};
|
||||
|
||||
@@ -49,21 +64,78 @@ angular.module('login').directive('guacLogin', [function guacLogin() {
|
||||
directive.controller = ['$scope', '$injector',
|
||||
function loginController($scope, $injector) {
|
||||
|
||||
// Required types
|
||||
var Error = $injector.get('Error');
|
||||
var Field = $injector.get('Field');
|
||||
|
||||
// Required services
|
||||
var $route = $injector.get('$route');
|
||||
var authenticationService = $injector.get('authenticationService');
|
||||
|
||||
/**
|
||||
* Whether an error occurred during login.
|
||||
*
|
||||
* @type Boolean
|
||||
* A description of the error that occurred during login, if any.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
$scope.loginError = false;
|
||||
|
||||
/**
|
||||
* All form values entered by the user.
|
||||
* All form values entered by the user, as parameter name/value pairs.
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
$scope.values = {};
|
||||
$scope.enteredValues = {};
|
||||
|
||||
/**
|
||||
* All form fields which have not yet been filled by the user.
|
||||
*
|
||||
* @type Field[]
|
||||
*/
|
||||
$scope.remainingFields = [];
|
||||
|
||||
/**
|
||||
* Returns whether a previous login attempt is continuing.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* true if a previous login attempt is continuing, false otherwise.
|
||||
*/
|
||||
$scope.isContinuation = function isContinuation() {
|
||||
|
||||
// The login is continuing if any parameter values are provided
|
||||
for (var name in $scope.values)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
// Ensure provided values are included within entered values, even if
|
||||
// they have no corresponding input fields
|
||||
$scope.$watch('values', function resetEnteredValues(values) {
|
||||
angular.extend($scope.enteredValues, values || {});
|
||||
});
|
||||
|
||||
// Update field information when form is changed
|
||||
$scope.$watch('form', function resetRemainingFields(fields) {
|
||||
|
||||
// If no fields are provided, then no fields remain
|
||||
if (!fields) {
|
||||
$scope.remainingFields = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter provided fields against provided values
|
||||
$scope.remainingFields = fields.filter(function isRemaining(field) {
|
||||
return !(field.name in $scope.values);
|
||||
});
|
||||
|
||||
// Set default values for all unset fields
|
||||
angular.forEach($scope.remainingFields, function setDefault(field) {
|
||||
if (!$scope.enteredValues[field.name])
|
||||
$scope.enteredValues[field.name] = '';
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Submits the currently-specified username and password to the
|
||||
@@ -71,20 +143,42 @@ angular.module('login').directive('guacLogin', [function guacLogin() {
|
||||
*/
|
||||
$scope.login = function login() {
|
||||
|
||||
// Start with cleared status
|
||||
$scope.loginError = null;
|
||||
|
||||
// Attempt login once existing session is destroyed
|
||||
authenticationService.authenticate($scope.values)
|
||||
authenticationService.authenticate($scope.enteredValues)
|
||||
|
||||
// Clear and reload upon success
|
||||
.then(function loginSuccessful() {
|
||||
$scope.loginError = false;
|
||||
$scope.values = {};
|
||||
$scope.enteredValues = {};
|
||||
$route.reload();
|
||||
})
|
||||
|
||||
// Reset upon failure
|
||||
['catch'](function loginFailed() {
|
||||
$scope.loginError = true;
|
||||
$scope.values.password = '';
|
||||
['catch'](function loginFailed(error) {
|
||||
|
||||
// Clear out passwords if the credentials were rejected for any reason
|
||||
if (error.type !== Error.Type.INSUFFICIENT_CREDENTIALS) {
|
||||
|
||||
// Flag generic error for invalid login
|
||||
if (error.type === Error.Type.INVALID_CREDENTIALS)
|
||||
$scope.loginError = 'LOGIN.ERROR_INVALID_LOGIN';
|
||||
|
||||
// Display error if anything else goes wrong
|
||||
else
|
||||
$scope.loginError = error.message;
|
||||
|
||||
// Clear all visible password fields
|
||||
angular.forEach($scope.remainingFields, function clearEnteredValueIfPassword(field) {
|
||||
|
||||
// Remove entered value only if field is a password field
|
||||
if (field.type === Field.Type.PASSWORD && field.name in $scope.enteredValues)
|
||||
$scope.enteredValues[field.name] = '';
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
@@ -86,3 +86,15 @@
|
||||
max-width: 3em;
|
||||
margin: 0.5em auto;
|
||||
}
|
||||
|
||||
.login-ui.continuation div.login-dialog {
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
box-shadow: none;
|
||||
max-width: 6in;
|
||||
}
|
||||
|
||||
.login-ui.continuation .login-dialog .logo,
|
||||
.login-ui.continuation .login-dialog .version {
|
||||
display: none;
|
||||
}
|
||||
|
@@ -38,4 +38,13 @@
|
||||
.login-ui .login-dialog .buttons input[type="submit"] {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.login-ui.continuation .login-dialog .buttons input[type="submit"] {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.login-ui.initial .login-dialog input.continue-login,
|
||||
.login-ui.continuation .login-dialog input.login {
|
||||
display: none;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div class="login-ui" ng-class="{error: loginError}" >
|
||||
<div class="login-ui" ng-class="{error: loginError, continuation: isContinuation(), initial: !isContinuation()}" >
|
||||
<!--
|
||||
Copyright 2014 Glyptodon LLC.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
-->
|
||||
|
||||
<!-- Login error message -->
|
||||
<p class="login-error">{{'LOGIN.ERROR_INVALID_LOGIN' | translate}}</p>
|
||||
<p class="login-error">{{loginError | translate}}</p>
|
||||
|
||||
<div class="login-dialog-middle">
|
||||
|
||||
@@ -34,14 +34,18 @@
|
||||
<img class="logo" src="images/guac-tricolor.png" alt=""/>
|
||||
<div class="version">{{'APP.NAME' | translate}}</div>
|
||||
|
||||
<!-- Login message/instructions -->
|
||||
<p ng-show="helpText">{{helpText | translate}}</p>
|
||||
|
||||
<!-- Login fields -->
|
||||
<div class="login-fields">
|
||||
<guac-form namespace="'LOGIN'" content="form" model="values"></guac-form>
|
||||
<guac-form namespace="'LOGIN'" content="remainingFields" model="enteredValues"></guac-form>
|
||||
</div>
|
||||
|
||||
<!-- Submit button -->
|
||||
<div class="buttons">
|
||||
<input type="submit" name="login" class="login" value="{{'LOGIN.ACTION_LOGIN' | translate}}"/>
|
||||
<input type="submit" name="login" class="continue-login" value="{{'LOGIN.ACTION_CONTINUE' | translate}}"/>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -49,4 +53,5 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@@ -46,13 +46,6 @@ angular.module('rest').factory('Field', [function defineField() {
|
||||
*/
|
||||
this.name = template.name;
|
||||
|
||||
/**
|
||||
* A human-readable name for this parameter.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.title = template.title;
|
||||
|
||||
/**
|
||||
* The type string defining which values this parameter may contain,
|
||||
* as well as what properties are applicable. Valid types are listed
|
||||
@@ -64,18 +57,9 @@ angular.module('rest').factory('Field', [function defineField() {
|
||||
this.type = template.type || Field.Type.TEXT;
|
||||
|
||||
/**
|
||||
* The value to set the parameter to, in the case of a BOOLEAN
|
||||
* parameter, to enable that parameter's effect.
|
||||
* All possible legal values for this parameter.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.value = template.value;
|
||||
|
||||
/**
|
||||
* All possible legal values for this parameter. This property is only
|
||||
* applicable to ENUM type parameters.
|
||||
*
|
||||
* @type FieldOption[]
|
||||
* @type String[]
|
||||
*/
|
||||
this.options = template.options;
|
||||
|
||||
@@ -123,7 +107,9 @@ angular.module('rest').factory('Field', [function defineField() {
|
||||
/**
|
||||
* The type string associated with parameters that may contain only a
|
||||
* single possible value, where that value enables the parameter's
|
||||
* effect.
|
||||
* effect. It is assumed that each BOOLEAN field will provide exactly
|
||||
* one possible value (option), which will be the value if that field
|
||||
* is true.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
|
@@ -40,21 +40,13 @@ angular.module('rest').factory('Form', [function defineForm() {
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The name which uniquely identifies this parameter, or null if this
|
||||
* field has no name.
|
||||
* The name which uniquely identifies this form, or null if this form
|
||||
* has no name.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.name = template.name;
|
||||
|
||||
/**
|
||||
* A human-readable name for this form, or null if this form has no
|
||||
* name.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.title = template.title;
|
||||
|
||||
/**
|
||||
* All fields contained within this form.
|
||||
*
|
||||
|
@@ -46,13 +46,6 @@ angular.module('rest').factory('Protocol', [function defineProtocol() {
|
||||
*/
|
||||
this.name = template.name;
|
||||
|
||||
/**
|
||||
* A human-readable name for this protocol.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.title = template.title;
|
||||
|
||||
/**
|
||||
* An array of forms containing all known parameters for this protocol,
|
||||
* their types, and other information.
|
||||
|
@@ -50,7 +50,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Login screen for logged-out users -->
|
||||
<guac-login ng-show="expectedCredentials" form="expectedCredentials"></guac-login>
|
||||
<guac-login ng-show="expectedCredentials"
|
||||
help-text="loginHelpText"
|
||||
form="expectedCredentials"
|
||||
values="acceptedCredentials"></guac-login>
|
||||
|
||||
<script type="text/javascript" src="app.js"></script>
|
||||
</body>
|
||||
|
@@ -7,6 +7,7 @@
|
||||
"ACTION_ACKNOWLEDGE" : "OK",
|
||||
"ACTION_CANCEL" : "Cancel",
|
||||
"ACTION_CLONE" : "Clone",
|
||||
"ACTION_CONTINUE" : "Continue",
|
||||
"ACTION_DELETE" : "Delete",
|
||||
"ACTION_DELETE_SESSIONS" : "Kill Sessions",
|
||||
"ACTION_LOGIN" : "Login",
|
||||
@@ -23,6 +24,7 @@
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "Error",
|
||||
|
||||
"ERROR_PASSWORD_BLANK" : "Your password cannot be blank.",
|
||||
"ERROR_PASSWORD_MISMATCH" : "The provided passwords do not match.",
|
||||
|
||||
"FIELD_HEADER_PASSWORD" : "Password:",
|
||||
@@ -143,7 +145,11 @@
|
||||
|
||||
"LOGIN": {
|
||||
|
||||
"ACTION_LOGIN" : "@:APP.ACTION_LOGIN",
|
||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||
"ACTION_CONTINUE" : "@:APP.ACTION_CONTINUE",
|
||||
"ACTION_LOGIN" : "@:APP.ACTION_LOGIN",
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"ERROR_INVALID_LOGIN" : "Invalid Login",
|
||||
|
||||
@@ -161,7 +167,7 @@
|
||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection",
|
||||
"DIALOG_HEADER_ERROR" : "Error",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"FIELD_HEADER_LOCATION" : "Location:",
|
||||
"FIELD_HEADER_NAME" : "Name:",
|
||||
@@ -194,7 +200,7 @@
|
||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection Group",
|
||||
"DIALOG_HEADER_ERROR" : "Error",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"FIELD_HEADER_LOCATION" : "Location:",
|
||||
"FIELD_HEADER_NAME" : "Name:",
|
||||
@@ -216,8 +222,8 @@
|
||||
"ACTION_DELETE" : "@:APP.ACTION_DELETE",
|
||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete User",
|
||||
"DIALOG_HEADER_ERROR" : "Error",
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete User",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
||||
|
||||
@@ -414,7 +420,7 @@
|
||||
"ACTION_NEW_CONNECTION" : "New Connection",
|
||||
"ACTION_NEW_CONNECTION_GROUP" : "New Group",
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "Error",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_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.",
|
||||
|
||||
@@ -432,7 +438,7 @@
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"ERROR_PASSWORD_BLANK" : "Your password cannot be blank.",
|
||||
"ERROR_PASSWORD_BLANK" : "@:APP.ERROR_PASSWORD_BLANK",
|
||||
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
||||
|
||||
"FIELD_HEADER_LANGUAGE" : "Display language:",
|
||||
@@ -469,7 +475,7 @@
|
||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||
"ACTION_NEW_USER" : "New User",
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "Error",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"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.",
|
||||
|
||||
@@ -484,7 +490,7 @@
|
||||
"ACTION_DELETE" : "Kill Sessions",
|
||||
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Kill Sessions",
|
||||
"DIALOG_HEADER_ERROR" : "Error",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"FIELD_PLACEHOLDER_FILTER" : "Filter",
|
||||
|
||||
|
@@ -23,6 +23,7 @@
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "Ошибка",
|
||||
|
||||
"ERROR_PASSWORD_BLANK" : "Пароль не может быть пустым.",
|
||||
"ERROR_PASSWORD_MISMATCH" : "Указанные пароли не совпадают.",
|
||||
|
||||
"FIELD_HEADER_PASSWORD" : "Пароль:",
|
||||
@@ -143,7 +144,10 @@
|
||||
|
||||
"LOGIN": {
|
||||
|
||||
"ACTION_LOGIN" : "@:APP.ACTION_LOGIN",
|
||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||
"ACTION_LOGIN" : "@:APP.ACTION_LOGIN",
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"ERROR_INVALID_LOGIN" : "Неверные данные для входа",
|
||||
|
||||
@@ -161,7 +165,7 @@
|
||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Удалить подключение",
|
||||
"DIALOG_HEADER_ERROR" : "Ошибка",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"FIELD_HEADER_LOCATION" : "Размещение:",
|
||||
"FIELD_HEADER_NAME" : "Название:",
|
||||
@@ -194,7 +198,7 @@
|
||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Удалить группу подключений",
|
||||
"DIALOG_HEADER_ERROR" : "Ошибка",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"FIELD_HEADER_LOCATION" : "Размещение:",
|
||||
"FIELD_HEADER_NAME" : "Название:",
|
||||
@@ -216,8 +220,8 @@
|
||||
"ACTION_DELETE" : "@:APP.ACTION_DELETE",
|
||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Удалить пользователя",
|
||||
"DIALOG_HEADER_ERROR" : "Ошибка",
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Удалить пользователя",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
||||
|
||||
@@ -393,7 +397,7 @@
|
||||
"ACTION_NEW_CONNECTION" : "Новое подключение",
|
||||
"ACTION_NEW_CONNECTION_GROUP" : "Новая группа",
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "Ошибка",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"HELP_CONNECTIONS" : "Нажмите на подключение, чтобы управлять им. В зависимости от прав доступа возможно добавление и удаление подключений, а также изменение их свойств (протокол, название сервера, порт и пр.).",
|
||||
|
||||
@@ -409,9 +413,9 @@
|
||||
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
|
||||
"ACTION_UPDATE_PASSWORD" : "@:APP.ACTION_UPDATE_PASSWORD",
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"ERROR_PASSWORD_BLANK" : "Пароль не может быть пустым.",
|
||||
"ERROR_PASSWORD_BLANK" : "@:APP.ERROR_PASSWORD_BLANK",
|
||||
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
||||
|
||||
"FIELD_HEADER_LANGUAGE" : "Язык:",
|
||||
@@ -448,7 +452,7 @@
|
||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||
"ACTION_NEW_USER" : "Новый пользователь",
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "Ошибка",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"HELP_USERS" : "Нажмите на пользователя, чтобы управлять им. В зависимости от прав доступа возможно добавление и удаление пользователей, а также изменение паролей.",
|
||||
|
||||
@@ -463,7 +467,7 @@
|
||||
"ACTION_DELETE" : "Завершить сессии",
|
||||
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Завершение сессий",
|
||||
"DIALOG_HEADER_ERROR" : "Ошибка",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"FIELD_PLACEHOLDER_FILTER" : "Фильтр",
|
||||
|
||||
|
Reference in New Issue
Block a user