Merge 1.6.1 changes to patch branch.
This commit is contained in:
commit
27be6e9f33
61 files changed
+1320
-288
No files matched your search
+4
-8
@@ -343,15 +343,13 @@ public class ModeledConnection extends ModeledChildDirectoryObject<ConnectionMod
|
|||||||
// Translate connection limit attribute
|
// Translate connection limit attribute
|
||||||
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
|
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
logger.warn("Not setting maximum connections: {}", e.getMessage());
|
logger.warn("Not setting maximum connections: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse numeric attribute.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate per-user connection limit attribute
|
// Translate per-user connection limit attribute
|
||||||
try { getModel().setMaxConnectionsPerUser(NumericField.parse(attributes.get(MAX_CONNECTIONS_PER_USER_NAME))); }
|
try { getModel().setMaxConnectionsPerUser(NumericField.parse(attributes.get(MAX_CONNECTIONS_PER_USER_NAME))); }
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
logger.warn("Not setting maximum connections per user: {}", e.getMessage());
|
logger.warn("Not setting maximum connections per user: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse numeric attribute.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate guacd hostname
|
// Translate guacd hostname
|
||||||
@@ -360,8 +358,7 @@ public class ModeledConnection extends ModeledChildDirectoryObject<ConnectionMod
|
|||||||
// Translate guacd port
|
// Translate guacd port
|
||||||
try { getModel().setProxyPort(NumericField.parse(attributes.get(GUACD_PORT_NAME))); }
|
try { getModel().setProxyPort(NumericField.parse(attributes.get(GUACD_PORT_NAME))); }
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
logger.warn("Not setting guacd port: {}", e.getMessage());
|
logger.warn("Not setting guacd port: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse numeric attribute.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate guacd encryption method
|
// Translate guacd encryption method
|
||||||
@@ -382,8 +379,7 @@ public class ModeledConnection extends ModeledChildDirectoryObject<ConnectionMod
|
|||||||
// Translate connection weight attribute
|
// Translate connection weight attribute
|
||||||
try { getModel().setConnectionWeight(NumericField.parse(attributes.get(CONNECTION_WEIGHT))); }
|
try { getModel().setConnectionWeight(NumericField.parse(attributes.get(CONNECTION_WEIGHT))); }
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
logger.warn("Not setting the connection weight: {}", e.getMessage());
|
logger.warn("Not setting the connection weight: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse numeric attribute.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate failover-only attribute
|
// Translate failover-only attribute
|
||||||
|
|||||||
+2
-4
@@ -200,15 +200,13 @@ public class ModeledConnectionGroup extends ModeledChildDirectoryObject<Connecti
|
|||||||
// Translate connection limit attribute
|
// Translate connection limit attribute
|
||||||
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
|
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
logger.warn("Not setting maximum connections: {}", e.getMessage());
|
logger.warn("Not setting maximum connections: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse numeric attribute.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate per-user connection limit attribute
|
// Translate per-user connection limit attribute
|
||||||
try { getModel().setMaxConnectionsPerUser(NumericField.parse(attributes.get(MAX_CONNECTIONS_PER_USER_NAME))); }
|
try { getModel().setMaxConnectionsPerUser(NumericField.parse(attributes.get(MAX_CONNECTIONS_PER_USER_NAME))); }
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
logger.warn("Not setting maximum connections per user: {}", e.getMessage());
|
logger.warn("Not setting maximum connections per user: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse numeric attribute.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate session affinity attribute
|
// Translate session affinity attribute
|
||||||
|
|||||||
+3
-2
@@ -778,8 +778,9 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
// If connection failed due to an upstream error, retry other
|
// If connection failed due to an upstream error, retry other
|
||||||
// connections
|
// connections
|
||||||
catch (GuacamoleUpstreamException e) {
|
catch (GuacamoleUpstreamException e) {
|
||||||
logger.info("Upstream error intercepted for connection \"{}\". Failing over to next connection in group...", connection.getIdentifier());
|
logger.info("Upstream error intercepted for connection \"{}\". "
|
||||||
logger.debug("Upstream remote desktop reported an error during connection.", e);
|
+ "Failing over to next connection in group...",
|
||||||
|
connection.getIdentifier(), e);
|
||||||
connections.remove(connection);
|
connections.remove(connection);
|
||||||
upstreamHasFailed = true;
|
upstreamHasFailed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -442,8 +442,7 @@ public class ModeledUser extends ModeledPermissions<UserModel> implements User {
|
|||||||
if (attributes.containsKey(ACCESS_WINDOW_START_ATTRIBUTE_NAME)) {
|
if (attributes.containsKey(ACCESS_WINDOW_START_ATTRIBUTE_NAME)) {
|
||||||
try { getModel().setAccessWindowStart(parseTime(attributes.get(ACCESS_WINDOW_START_ATTRIBUTE_NAME))); }
|
try { getModel().setAccessWindowStart(parseTime(attributes.get(ACCESS_WINDOW_START_ATTRIBUTE_NAME))); }
|
||||||
catch (ParseException e) {
|
catch (ParseException e) {
|
||||||
logger.warn("Not setting start time of user access window: {}", e.getMessage());
|
logger.warn("Not setting start time of user access window: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse time attribute.", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,8 +450,7 @@ public class ModeledUser extends ModeledPermissions<UserModel> implements User {
|
|||||||
if (attributes.containsKey(ACCESS_WINDOW_END_ATTRIBUTE_NAME)) {
|
if (attributes.containsKey(ACCESS_WINDOW_END_ATTRIBUTE_NAME)) {
|
||||||
try { getModel().setAccessWindowEnd(parseTime(attributes.get(ACCESS_WINDOW_END_ATTRIBUTE_NAME))); }
|
try { getModel().setAccessWindowEnd(parseTime(attributes.get(ACCESS_WINDOW_END_ATTRIBUTE_NAME))); }
|
||||||
catch (ParseException e) {
|
catch (ParseException e) {
|
||||||
logger.warn("Not setting end time of user access window: {}", e.getMessage());
|
logger.warn("Not setting end time of user access window: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse time attribute.", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,8 +458,7 @@ public class ModeledUser extends ModeledPermissions<UserModel> implements User {
|
|||||||
if (attributes.containsKey(VALID_FROM_ATTRIBUTE_NAME)) {
|
if (attributes.containsKey(VALID_FROM_ATTRIBUTE_NAME)) {
|
||||||
try { getModel().setValidFrom(parseDate(attributes.get(VALID_FROM_ATTRIBUTE_NAME))); }
|
try { getModel().setValidFrom(parseDate(attributes.get(VALID_FROM_ATTRIBUTE_NAME))); }
|
||||||
catch (ParseException e) {
|
catch (ParseException e) {
|
||||||
logger.warn("Not setting user validity start date: {}", e.getMessage());
|
logger.warn("Not setting user validity start date: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse date attribute.", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,8 +466,7 @@ public class ModeledUser extends ModeledPermissions<UserModel> implements User {
|
|||||||
if (attributes.containsKey(VALID_UNTIL_ATTRIBUTE_NAME)) {
|
if (attributes.containsKey(VALID_UNTIL_ATTRIBUTE_NAME)) {
|
||||||
try { getModel().setValidUntil(parseDate(attributes.get(VALID_UNTIL_ATTRIBUTE_NAME))); }
|
try { getModel().setValidUntil(parseDate(attributes.get(VALID_UNTIL_ATTRIBUTE_NAME))); }
|
||||||
catch (ParseException e) {
|
catch (ParseException e) {
|
||||||
logger.warn("Not setting user validity end date: {}", e.getMessage());
|
logger.warn("Not setting user validity end date: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to parse date attribute.", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-7
@@ -325,13 +325,9 @@ public class ModeledUserContext extends RestrictedObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
|
logger.warn("Unable to determine if access window enforcement is"
|
||||||
logger.warn(
|
+ " enabled for active sessions; enforcing by default: {}",
|
||||||
"Unable to determine if access window enforcement is"
|
e.getMessage(), e);
|
||||||
+ " enabled for active sessions; enforcing by default: {}"
|
|
||||||
, e.getMessage());
|
|
||||||
logger.debug("Unable to determine access window enforcement policy.", e);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A user context is valid if the associated user's account is valid
|
// A user context is valid if the associated user's account is valid
|
||||||
|
|||||||
+8
-5
@@ -78,14 +78,15 @@ public class RequestValidationService {
|
|||||||
|
|
||||||
// Deny all requests if restrictions cannot be parsed
|
// Deny all requests if restrictions cannot be parsed
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.warn("Authentication request from \"{}\" is DENIED due to parse error: {}", credentials.getRemoteAddress(), e.getMessage());
|
logger.warn("Authentication request from \"{}\" is DENIED due to "
|
||||||
logger.debug("Error parsing authentication request restrictions from guacamole.properties.", e);
|
+ "parse error: {}", credentials.getRemoteAddress(), e.getMessage(), e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All requests are allowed if no restrictions are defined
|
// All requests are allowed if no restrictions are defined
|
||||||
if (trustedNetworks.isEmpty()) {
|
if (trustedNetworks.isEmpty()) {
|
||||||
logger.debug("Authentication request from \"{}\" is ALLOWED (no restrictions).", credentials.getRemoteAddress());
|
logger.debug("Authentication request from \"{}\" is ALLOWED (no "
|
||||||
|
+ "restrictions).", credentials.getRemoteAddress());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,14 +95,16 @@ public class RequestValidationService {
|
|||||||
|
|
||||||
// Request is allowed if any subnet matches
|
// Request is allowed if any subnet matches
|
||||||
if (new IPAddressString(network).contains(new IPAddressString(credentials.getRemoteAddress()))) {
|
if (new IPAddressString(network).contains(new IPAddressString(credentials.getRemoteAddress()))) {
|
||||||
logger.debug("Authentication request from \"{}\" is ALLOWED (matched subnet).", credentials.getRemoteAddress());
|
logger.debug("Authentication request from \"{}\" is ALLOWED "
|
||||||
|
+ "(matched subnet).", credentials.getRemoteAddress());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise request is denied - no subnets matched
|
// Otherwise request is denied - no subnets matched
|
||||||
logger.debug("Authentication request from \"{}\" is DENIED (did not match subnet).", credentials.getRemoteAddress());
|
logger.debug("Authentication request from \"{}\" is DENIED (did not "
|
||||||
|
+ "match subnet).", credentials.getRemoteAddress());
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -168,22 +168,19 @@ public class UserDataService {
|
|||||||
|
|
||||||
// Fail if base64 data is not valid
|
// Fail if base64 data is not valid
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
logger.warn("Submitted data is not proper base64.");
|
logger.warn("Submitted data is not proper base64: {}", e.getMessage(), e);
|
||||||
logger.debug("Invalid base64 data.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle lack of standard UTF-8 support (should never happen)
|
// Handle lack of standard UTF-8 support (should never happen)
|
||||||
catch (UnsupportedEncodingException e) {
|
catch (UnsupportedEncodingException e) {
|
||||||
logger.error("Unexpected lack of support for UTF-8: {}", e.getMessage());
|
logger.error("Unexpected lack of support for UTF-8: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to decode base64 data as UTF-8.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if decryption or key retrieval fails for any reason
|
// Fail if decryption or key retrieval fails for any reason
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Decryption of received data failed: {}", e.getMessage());
|
logger.error("Decryption of received data failed: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to decrypt received data.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,8 +202,7 @@ public class UserDataService {
|
|||||||
|
|
||||||
// Fail UserData creation if JSON is invalid/unreadable
|
// Fail UserData creation if JSON is invalid/unreadable
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.error("Received JSON is invalid: {}", e.getMessage());
|
logger.error("Received JSON is invalid: {}", e.getMessage(), e);
|
||||||
logger.debug("Error parsing UserData JSON.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-4
@@ -322,8 +322,7 @@ public class LDAPConnectionService {
|
|||||||
ldapConnection.close();
|
ldapConnection.close();
|
||||||
logger.error("Binding with the LDAP server at \"{}\" as user "
|
logger.error("Binding with the LDAP server at \"{}\" as user "
|
||||||
+ "\"{}\" failed: {}", config.getLdapHost(), bindUser,
|
+ "\"{}\" failed: {}", config.getLdapHost(), bindUser,
|
||||||
e.getMessage());
|
e.getMessage(), e);
|
||||||
logger.debug("Unable to bind to LDAP server.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,8 +368,8 @@ public class LDAPConnectionService {
|
|||||||
}
|
}
|
||||||
catch (LdapInvalidDnException e) {
|
catch (LdapInvalidDnException e) {
|
||||||
logger.error("Credentials of existing connection cannot be used. "
|
logger.error("Credentials of existing connection cannot be used. "
|
||||||
+ "The username used (\"{}\") is not a valid DN.", username);
|
+ "The username used (\"{}\") is not a valid DN.",
|
||||||
logger.debug("Cannot bind using invalid DN.", e);
|
username, e);
|
||||||
ldapConnection.close();
|
ldapConnection.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -265,8 +265,7 @@ public class ObjectQueryService {
|
|||||||
+ "URL \"{}\".", url);
|
+ "URL \"{}\".", url);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.warn("Referral to \"{}\" could not be followed: {}", url, e.getMessage());
|
logger.warn("Referral to \"{}\" could not be followed: {}", url, e.getMessage(), e);
|
||||||
logger.debug("Failed to follow LDAP referral.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -122,7 +122,7 @@ public class ConfigurationService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.error("\"{}\" could not be read/parsed: {}", ldapServers, e.getMessage());
|
logger.error("\"{}\" could not be read/parsed: {}", ldapServers, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
+5
-10
@@ -202,8 +202,7 @@ public class ConnectionService {
|
|||||||
}
|
}
|
||||||
catch (LdapInvalidAttributeValueException e) {
|
catch (LdapInvalidAttributeValueException e) {
|
||||||
logger.error("Invalid value for {} attribute: {}",
|
logger.error("Invalid value for {} attribute: {}",
|
||||||
LDAP_ATTRIBUTE_NAME_ID, e.getMessage());
|
LDAP_ATTRIBUTE_NAME_ID, e.getMessage(), e);
|
||||||
logger.debug("LDAP exception while getting CN attribute.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,8 +222,7 @@ public class ConnectionService {
|
|||||||
config.setProtocol(protocol.getString());
|
config.setProtocol(protocol.getString());
|
||||||
}
|
}
|
||||||
catch (LdapInvalidAttributeValueException e) {
|
catch (LdapInvalidAttributeValueException e) {
|
||||||
logger.error("Invalid value of the protocol entry: {}", e.getMessage());
|
logger.error("Invalid value of the protocol entry: {}", e.getMessage(), e);
|
||||||
logger.debug("LDAP exception when getting protocol value.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,8 +232,7 @@ public class ConnectionService {
|
|||||||
proxyConfig = getProxyConfiguration(entry);
|
proxyConfig = getProxyConfiguration(entry);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Failed to retrieve proxy configuration.", e.getMessage());
|
logger.error("Failed to retrieve proxy configuration.", e.getMessage(), e);
|
||||||
logger.debug("Guacamole Exception when retrieving proxy configuration.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,8 +247,7 @@ public class ConnectionService {
|
|||||||
parameter = parameterAttribute.getString();
|
parameter = parameterAttribute.getString();
|
||||||
}
|
}
|
||||||
catch (LdapInvalidAttributeValueException e) {
|
catch (LdapInvalidAttributeValueException e) {
|
||||||
logger.warn("Parameter value not valid for {}: {}", cnName, e.getMessage());
|
logger.warn("Parameter value not valid for {}: {}", cnName, e.getMessage(), e);
|
||||||
logger.debug("LDAP exception when getting parameter value.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
parameterAttribute.remove(parameter);
|
parameterAttribute.remove(parameter);
|
||||||
@@ -394,8 +390,7 @@ public class ConnectionService {
|
|||||||
return new GuacamoleProxyConfiguration(proxyHostname, proxyPort, proxyEncryption);
|
return new GuacamoleProxyConfiguration(proxyHostname, proxyPort, proxyEncryption);
|
||||||
}
|
}
|
||||||
catch (LdapInvalidAttributeValueException e) {
|
catch (LdapInvalidAttributeValueException e) {
|
||||||
logger.error("Invalid value in proxy configuration: {}", e.getMessage());
|
logger.error("Invalid value in proxy configuration: {}", e.getMessage(), e);
|
||||||
logger.debug("LDAP exception fetching proxy attribute value.", e);
|
|
||||||
throw new GuacamoleServerException("Invalid LDAP value in proxy configuration.", e);
|
throw new GuacamoleServerException("Invalid LDAP value in proxy configuration.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-5
@@ -214,9 +214,7 @@ public class UserGroupService {
|
|||||||
}
|
}
|
||||||
catch (LdapInvalidAttributeValueException e) {
|
catch (LdapInvalidAttributeValueException e) {
|
||||||
logger.error("User group missing identifier: {}",
|
logger.error("User group missing identifier: {}",
|
||||||
e.getMessage());
|
e.getMessage(), e);
|
||||||
logger.debug("LDAP exception while getting "
|
|
||||||
+ "group identifier.", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,8 +278,7 @@ public class UserGroupService {
|
|||||||
}
|
}
|
||||||
catch (LdapInvalidAttributeValueException e) {
|
catch (LdapInvalidAttributeValueException e) {
|
||||||
logger.error("User group missing identifier: {}",
|
logger.error("User group missing identifier: {}",
|
||||||
e.getMessage());
|
e.getMessage(), e);
|
||||||
logger.debug("LDAP exception while getting group identifier.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"translations" : [
|
"translations" : [
|
||||||
"translations/de.json",
|
|
||||||
"translations/en.json",
|
"translations/en.json",
|
||||||
"translations/zh.json"
|
"translations/zh.json"
|
||||||
]
|
]
|
||||||
|
|||||||
+9
-10
@@ -94,16 +94,18 @@ public class AuthenticationProviderService {
|
|||||||
// Try to get the state attribute - if it's not there, we have a problem
|
// Try to get the state attribute - if it's not there, we have a problem
|
||||||
RadiusAttribute stateAttr = challengePacket.findAttribute(Attr_State.TYPE);
|
RadiusAttribute stateAttr = challengePacket.findAttribute(Attr_State.TYPE);
|
||||||
if (stateAttr == null) {
|
if (stateAttr == null) {
|
||||||
logger.error("Something went wrong, state attribute not present.");
|
logger.error("RADIUS server did not include the required \"{}\" "
|
||||||
logger.debug("State Attribute turned up null, which shouldn't happen in AccessChallenge.");
|
+ "attribute in its challenge packet - cannot continue.",
|
||||||
|
Attr_State.NAME);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to get the reply message so we know what to ask the user
|
// We need to get the reply message so we know what to ask the user
|
||||||
RadiusAttribute replyAttr = challengePacket.findAttribute(Attr_ReplyMessage.TYPE);
|
RadiusAttribute replyAttr = challengePacket.findAttribute(Attr_ReplyMessage.TYPE);
|
||||||
if (replyAttr == null) {
|
if (replyAttr == null) {
|
||||||
logger.error("No reply message received from the server.");
|
logger.error("RADIUS server did not include the required \"{}\" "
|
||||||
logger.debug("Expecting a Attr_ReplyMessage attribute on this packet, and did not get one.");
|
+ "attribute in its challenge packet - cannot continue.",
|
||||||
|
Attr_ReplyMessage.NAME);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,8 +164,7 @@ public class AuthenticationProviderService {
|
|||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Cannot configure RADIUS server: {}", e.getMessage());
|
logger.error("Cannot configure RADIUS server: {}", e.getMessage(), e);
|
||||||
logger.debug("Error configuring RADIUS server.", e);
|
|
||||||
throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
|
throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,13 +185,11 @@ public class AuthenticationProviderService {
|
|||||||
stateBytes);
|
stateBytes);
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
logger.warn("Illegal hexadecimal value while parsing RADIUS state string: {}", e.getMessage());
|
logger.warn("Illegal hexadecimal value while parsing RADIUS state string: {}", e.getMessage(), e);
|
||||||
logger.debug("Encountered exception while attempting to parse the hexidecimal state value.", e);
|
|
||||||
throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
|
throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Cannot configure RADIUS server: {}", e.getMessage());
|
logger.error("Cannot configure RADIUS server: {}", e.getMessage(), e);
|
||||||
logger.debug("Error configuring RADIUS server.", e);
|
|
||||||
throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
|
throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -245,18 +245,15 @@ public class RadiusConnectionService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
catch (RadiusException e) {
|
catch (RadiusException e) {
|
||||||
logger.error("Unable to complete authentication.", e.getMessage());
|
logger.error("Unable to complete authentication: {}", e.getMessage(), e);
|
||||||
logger.debug("Authentication with RADIUS failed.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (NoSuchAlgorithmException e) {
|
catch (NoSuchAlgorithmException e) {
|
||||||
logger.error("No such RADIUS algorithm: {}", e.getMessage());
|
logger.error("No such RADIUS algorithm: {}", e.getMessage(), e);
|
||||||
logger.debug("Unknown RADIUS algorithm.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (UnknownHostException e) {
|
catch (UnknownHostException e) {
|
||||||
logger.error("Could not resolve address: {}", e.getMessage());
|
logger.error("Could not resolve address: {}", e.getMessage(), e);
|
||||||
logger.debug("Exception resolving host address.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
|||||||
+1
-2
@@ -64,8 +64,7 @@ public class HostRestrictionParser {
|
|||||||
addressList.add(hostName);
|
addressList.add(hostName);
|
||||||
}
|
}
|
||||||
catch (HostNameException e) {
|
catch (HostNameException e) {
|
||||||
LOGGER.warn("Invalid host name or IP: {}", host);
|
LOGGER.warn("Invalid host name or IP: {}", host, e);
|
||||||
LOGGER.debug("HostNameException.", e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-10
@@ -122,12 +122,10 @@ public class TokenValidationService {
|
|||||||
}
|
}
|
||||||
// Log any failures to validate/parse the JWT
|
// Log any failures to validate/parse the JWT
|
||||||
catch (MalformedClaimException e) {
|
catch (MalformedClaimException e) {
|
||||||
logger.info("Rejected OpenID token with malformed claim: {}", e.getMessage());
|
logger.info("Rejected OpenID token with malformed claim: {}", e.getMessage(), e);
|
||||||
logger.debug("Malformed claim within received JWT.", e);
|
|
||||||
}
|
}
|
||||||
catch (InvalidJwtException e) {
|
catch (InvalidJwtException e) {
|
||||||
logger.info("Rejected invalid OpenID token: {}", e.getMessage());
|
logger.info("Rejected invalid OpenID token: {}", e.getMessage(), e);
|
||||||
logger.debug("Invalid JWT received.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -160,8 +158,7 @@ public class TokenValidationService {
|
|||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
catch (MalformedClaimException e) {
|
catch (MalformedClaimException e) {
|
||||||
logger.info("Rejected OpenID token with malformed claim: {}", e.getMessage());
|
logger.info("Rejected OpenID token with malformed claim: {}", e.getMessage(), e);
|
||||||
logger.debug("Malformed claim within received JWT.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn if username was not present in token, as it likely means
|
// Warn if username was not present in token, as it likely means
|
||||||
@@ -203,8 +200,7 @@ public class TokenValidationService {
|
|||||||
return Collections.unmodifiableSet(new HashSet<>(oidcGroups));
|
return Collections.unmodifiableSet(new HashSet<>(oidcGroups));
|
||||||
}
|
}
|
||||||
catch (MalformedClaimException e) {
|
catch (MalformedClaimException e) {
|
||||||
logger.info("Rejected OpenID token with malformed claim: {}", e.getMessage());
|
logger.info("Rejected OpenID token with malformed claim: {}", e.getMessage(), e);
|
||||||
logger.debug("Malformed claim within received JWT.", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,8 +258,7 @@ public class TokenValidationService {
|
|||||||
return Collections.unmodifiableMap(tokens);
|
return Collections.unmodifiableMap(tokens);
|
||||||
}
|
}
|
||||||
catch (MalformedClaimException e) {
|
catch (MalformedClaimException e) {
|
||||||
logger.info("Rejected OpenID token with malformed claim: {}", e.getMessage());
|
logger.info("Rejected OpenID token with malformed claim: {}", e.getMessage(), e);
|
||||||
logger.debug("Malformed claim within received JWT.", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -125,8 +125,7 @@ public class AssertionConsumerServiceResource extends SSOResource {
|
|||||||
|
|
||||||
// If invalid, redirect back to main page to re-attempt authentication
|
// If invalid, redirect back to main page to re-attempt authentication
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.warn("Authentication attempted with an invalid SAML response: {}", e.getMessage());
|
logger.warn("Authentication attempted with an invalid SAML response: {}", e.getMessage(), e);
|
||||||
logger.debug("Received SAML response failed validation.", e);
|
|
||||||
return Response.seeOther(guacBase).build();
|
return Response.seeOther(guacBase).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -351,16 +351,16 @@ public class SSLClientAuthenticationResource extends SSOResource {
|
|||||||
|
|
||||||
}
|
}
|
||||||
catch (GuacamoleClientException e) {
|
catch (GuacamoleClientException e) {
|
||||||
logger.warn("SSL/TLS client authentication attempt rejected: {}", e.getMessage());
|
logger.warn("SSL/TLS client authentication attempt rejected: {}",
|
||||||
logger.debug("SSL/TLS client authentication failed.", e);
|
e.getMessage(), e);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("SSL/TLS client authentication attempt could not be processed: {}", e.getMessage());
|
logger.error("SSL/TLS client authentication attempt could not be "
|
||||||
logger.debug("SSL/TLS client authentication failed.", e);
|
+ "processed: {}", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
catch (RuntimeException | Error e) {
|
catch (RuntimeException | Error e) {
|
||||||
logger.error("SSL/TLS client authentication attempt failed internally: {}", e.getMessage());
|
logger.error("SSL/TLS client authentication attempt failed "
|
||||||
logger.debug("Internal failure processing SSL/TLS client authentication attempt.", e);
|
+ "internally: {}", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sessionManager.generateInvalid();
|
return sessionManager.generateInvalid();
|
||||||
|
|||||||
+3
-7
@@ -128,8 +128,7 @@ public class UserVerificationService {
|
|||||||
// If key is not valid base32, warn but otherwise pretend the key does
|
// If key is not valid base32, warn but otherwise pretend the key does
|
||||||
// not exist
|
// not exist
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
logger.warn("TOTP key of user \"{}\" is not valid base32.", self.getIdentifier());
|
logger.warn("TOTP key of user \"{}\" is not valid base32.", self.getIdentifier(), e);
|
||||||
logger.debug("TOTP key is not valid base32.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,9 +217,7 @@ public class UserVerificationService {
|
|||||||
+ "lack permission to update their own account and the "
|
+ "lack permission to update their own account and the "
|
||||||
+ "TOTP extension was unable to obtain privileged access. "
|
+ "TOTP extension was unable to obtain privileged access. "
|
||||||
+ "TOTP will be disabled for this user.",
|
+ "TOTP will be disabled for this user.",
|
||||||
self.getIdentifier());
|
self.getIdentifier(), e);
|
||||||
logger.debug("Permission denied to set TOTP key of user "
|
|
||||||
+ "account.", e);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (GuacamoleUnsupportedException e) {
|
catch (GuacamoleUnsupportedException e) {
|
||||||
@@ -422,8 +419,7 @@ public class UserVerificationService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
catch (InvalidKeyException e) {
|
catch (InvalidKeyException e) {
|
||||||
logger.warn("User \"{}\" is associated with an invalid TOTP key.", username);
|
logger.warn("User \"{}\" is associated with an invalid TOTP key.", username, e);
|
||||||
logger.debug("TOTP key is not valid.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provided code is not valid
|
// Provided code is not valid
|
||||||
|
|||||||
+6
-7
@@ -147,8 +147,8 @@ public class HistoryConnectionRecord extends DelegatingConnectionRecord {
|
|||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.warn("Possible session recording \"{}\" could not be "
|
logger.warn("Possible session recording \"{}\" could not be "
|
||||||
+ "identified as it cannot be read: {}", file, e.getMessage());
|
+ "identified as it cannot be read: {}",
|
||||||
logger.debug("Possible session recording \"{}\" could not be read.", file, e);
|
file, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
|
||||||
@@ -161,9 +161,7 @@ public class HistoryConnectionRecord extends DelegatingConnectionRecord {
|
|||||||
|
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.warn("Unexpected error closing recording file \"{}\": {}",
|
logger.warn("Unexpected error closing recording file \"{}\": {}",
|
||||||
file, e.getMessage());
|
file, e.getMessage(), e);
|
||||||
logger.debug("Session recording file \"{}\" could not be closed.",
|
|
||||||
file, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -297,8 +295,9 @@ public class HistoryConnectionRecord extends DelegatingConnectionRecord {
|
|||||||
.array());
|
.array());
|
||||||
}
|
}
|
||||||
catch (MalformedURLException e) {
|
catch (MalformedURLException e) {
|
||||||
logger.warn("Ignoring file \"{}\" as a unique URL and UUID for that file could not be generated: {}", e.getMessage());
|
logger.warn("Ignoring file \"{}\" as a unique URL and UUID for "
|
||||||
logger.debug("URL for file \"{}\" could not be determined.", file, e);
|
+ "that file could not be generated: {}",
|
||||||
|
e.getMessage(), e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-8
@@ -155,15 +155,12 @@ public class KsmAttributeService implements VaultAttributeService {
|
|||||||
|
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
|
|
||||||
logger.warn(
|
// If the configuration can't be parsed, default to not exposing
|
||||||
"Unable to determine if KSM user attributes "
|
// the attributes
|
||||||
+ "should be exposed due to config parsing error: {}.", e.getMessage());
|
logger.warn("Unable to determine if KSM user attributes should be "
|
||||||
logger.debug(
|
+ "exposed due to config parsing error: {}.", e.getMessage(), e);
|
||||||
"Config parsing error prevented checking user attribute configuration",
|
|
||||||
e);
|
|
||||||
|
|
||||||
// If the configuration can't be parsed, default to not exposing the attributes
|
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -781,7 +781,7 @@ public class KsmClient {
|
|||||||
// Show the warning only if there is no fallback function
|
// Show the warning only if there is no fallback function
|
||||||
// and this was the last attempt
|
// and this was the last attempt
|
||||||
logger.warn("Keeper notation \"{}\" could not be resolved "
|
logger.warn("Keeper notation \"{}\" could not be resolved "
|
||||||
+ "to a record: {}", notation, e.getMessage());
|
+ "to a record: {}", notation, e.getMessage(), e);
|
||||||
|
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-8
@@ -87,13 +87,10 @@ import com.google.inject.assistedinject.AssistedInject;
|
|||||||
boolean userKsmConfigEnabled = false;
|
boolean userKsmConfigEnabled = false;
|
||||||
try {
|
try {
|
||||||
userKsmConfigEnabled = configurationService.getAllowUserConfig();
|
userKsmConfigEnabled = configurationService.getAllowUserConfig();
|
||||||
} catch (GuacamoleException e) {
|
}
|
||||||
|
catch (GuacamoleException e) {
|
||||||
logger.warn(
|
logger.warn("Disabling user KSM config due to exception: {}",
|
||||||
"Disabling user KSM config due to exception: {}"
|
e.getMessage(), e);
|
||||||
, e.getMessage());
|
|
||||||
logger.debug("Error looking up if user KSM config is enabled.", e);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If user-specific KSM configuration is not enabled, do not expose the
|
// If user-specific KSM configuration is not enabled, do not expose the
|
||||||
@@ -112,4 +109,4 @@ import com.google.inject.assistedinject.AssistedInject;
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+2
-3
@@ -328,13 +328,12 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet {
|
|||||||
// Catch any thrown guacamole exception and attempt to pass within the
|
// Catch any thrown guacamole exception and attempt to pass within the
|
||||||
// HTTP response, logging each error appropriately.
|
// HTTP response, logging each error appropriately.
|
||||||
catch (GuacamoleClientException e) {
|
catch (GuacamoleClientException e) {
|
||||||
logger.warn("HTTP tunnel request rejected: {}", e.getMessage());
|
logger.warn("HTTP tunnel request rejected: {}", e.getMessage(), e);
|
||||||
sendError(response, e.getStatus().getGuacamoleStatusCode(),
|
sendError(response, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getStatus().getHttpStatusCode(), e.getMessage());
|
e.getStatus().getHttpStatusCode(), e.getMessage());
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("HTTP tunnel request failed: {}", e.getMessage());
|
logger.error("HTTP tunnel request failed: {}", e.getMessage(), e);
|
||||||
logger.debug("Internal error in HTTP tunnel.", e);
|
|
||||||
sendError(response, e.getStatus().getGuacamoleStatusCode(),
|
sendError(response, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getStatus().getHttpStatusCode(), "Internal server error.");
|
e.getStatus().getHttpStatusCode(), "Internal server error.");
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -149,7 +149,12 @@ public abstract class GuacamoleWebSocketTunnelEndpoint extends Endpoint {
|
|||||||
// connection has changed, and synchronization need only be performed
|
// connection has changed, and synchronization need only be performed
|
||||||
// in context of the new remote.
|
// in context of the new remote.
|
||||||
synchronized (remote) {
|
synchronized (remote) {
|
||||||
remote.sendText(instruction);
|
try {
|
||||||
|
remote.sendText(instruction);
|
||||||
|
}
|
||||||
|
catch (IllegalStateException e) {
|
||||||
|
throw new IOException("WebSocket message could not be sent.", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -205,8 +210,7 @@ public abstract class GuacamoleWebSocketTunnelEndpoint extends Endpoint {
|
|||||||
|
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
|
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage(), e);
|
||||||
logger.debug("Error connecting WebSocket tunnel.", e);
|
|
||||||
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
return;
|
return;
|
||||||
@@ -265,8 +269,7 @@ public abstract class GuacamoleWebSocketTunnelEndpoint extends Endpoint {
|
|||||||
// to pass within the WebSocket connection, logging
|
// to pass within the WebSocket connection, logging
|
||||||
// each error appropriately.
|
// each error appropriately.
|
||||||
catch (GuacamoleClientException e) {
|
catch (GuacamoleClientException e) {
|
||||||
logger.info("WebSocket connection terminated: {}", e.getMessage());
|
logger.info("WebSocket connection terminated: {}", e.getMessage(), e);
|
||||||
logger.debug("WebSocket connection terminated due to client error.", e);
|
|
||||||
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
}
|
}
|
||||||
@@ -275,15 +278,14 @@ public abstract class GuacamoleWebSocketTunnelEndpoint extends Endpoint {
|
|||||||
closeConnection(session, GuacamoleStatus.SUCCESS);
|
closeConnection(session, GuacamoleStatus.SUCCESS);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
|
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage(), e);
|
||||||
logger.debug("Internal error during connection to guacd.", e);
|
|
||||||
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.debug("I/O error prevents further reads.", e);
|
logger.debug("I/O error prevents further WebSocket communication.", e);
|
||||||
closeConnection(session, GuacamoleStatus.SERVER_ERROR);
|
closeConnection(session, GuacamoleStatus.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -158,9 +158,8 @@ class DefaultEnvironment extends DelegatingEnvironment {
|
|||||||
logger.error("Defaulting to case-sensitive handling of "
|
logger.error("Defaulting to case-sensitive handling of "
|
||||||
+ "usernames and group names as the desired case "
|
+ "usernames and group names as the desired case "
|
||||||
+ "sensitivity configuration could not be read: {}",
|
+ "sensitivity configuration could not be read: {}",
|
||||||
e.getMessage());
|
e.getMessage(), e);
|
||||||
|
|
||||||
logger.debug("Error reading case sensitivity configuration.", e);
|
|
||||||
return CaseSensitivity.ENABLED;
|
return CaseSensitivity.ENABLED;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,8 +266,9 @@ public class LocalEnvironment implements Environment {
|
|||||||
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.error("Unable to read connection parameter information from \"{}\": {}", file.getAbsolutePath(), e.getMessage());
|
logger.error("Unable to read connection parameter "
|
||||||
logger.debug("Error reading protocol JSON.", e);
|
+ "information from \"{}\": {}",
|
||||||
|
file.getAbsolutePath(), e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -290,8 +291,9 @@ public class LocalEnvironment implements Environment {
|
|||||||
protocols.put(protocol, readProtocol(stream));
|
protocols.put(protocol, readProtocol(stream));
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.error("Unable to read pre-defined connection parameter information for protocol \"{}\": {}", protocol, e.getMessage());
|
logger.error("Unable to read pre-defined connection "
|
||||||
logger.debug("Error reading pre-defined protocol JSON.", e);
|
+ "parameter information for protocol \"{}\": {}",
|
||||||
|
protocol, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
src/main/frontend/dist/**/*
|
src/main/frontend/dist/**/*
|
||||||
|
src/main/resources/META-INF/services/*
|
||||||
@@ -26,6 +26,7 @@ import javax.ws.rs.ApplicationPath;
|
|||||||
import org.glassfish.hk2.api.ServiceLocator;
|
import org.glassfish.hk2.api.ServiceLocator;
|
||||||
import org.glassfish.jersey.jackson.JacksonFeature;
|
import org.glassfish.jersey.jackson.JacksonFeature;
|
||||||
import org.glassfish.jersey.server.ResourceConfig;
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.server.ServerProperties;
|
||||||
import org.jvnet.hk2.guice.bridge.api.GuiceBridge;
|
import org.jvnet.hk2.guice.bridge.api.GuiceBridge;
|
||||||
import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;
|
import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;
|
||||||
import org.slf4j.bridge.SLF4JBridgeHandler;
|
import org.slf4j.bridge.SLF4JBridgeHandler;
|
||||||
@@ -68,6 +69,10 @@ public class GuacamoleApplication extends ResourceConfig {
|
|||||||
// Automatically scan for REST resources
|
// Automatically scan for REST resources
|
||||||
packages("org.apache.guacamole.rest");
|
packages("org.apache.guacamole.rest");
|
||||||
|
|
||||||
|
// Explicitly disable WADL generation (we don't use it and Jersey warns
|
||||||
|
// of the fact that it won't generate a WADL otherwise)
|
||||||
|
property(ServerProperties.WADL_FEATURE_DISABLE, true);
|
||||||
|
|
||||||
// Use Jackson for JSON
|
// Use Jackson for JSON
|
||||||
register(JacksonFeature.class);
|
register(JacksonFeature.class);
|
||||||
|
|
||||||
|
|||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.guacamole;
|
||||||
|
|
||||||
|
import ch.qos.logback.core.joran.spi.JoranException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.servlet.ServletContainerInitializer;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import org.apache.guacamole.log.GuacamoleLogbackServiceProvider;
|
||||||
|
import org.apache.guacamole.log.LogLevel;
|
||||||
|
import org.apache.guacamole.log.ReconfigurableLoggerFactory;
|
||||||
|
import org.slf4j.ILoggerFactory;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.slf4j.helpers.Reporter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServletContainerInitializer which forces the SLF4J service provider to be
|
||||||
|
* Guacamole's built-in logging system. This is necessary as Logback also
|
||||||
|
* includes an SLF4J service provider, and there is no guarantee which provider
|
||||||
|
* will be chosen by SLF4J in the case that multiple are present.
|
||||||
|
* <p>
|
||||||
|
* SLF4J uses a "slf4j.provider" system property to allow a specific provider
|
||||||
|
* to be chosen, which this initializer sets automatically.
|
||||||
|
*/
|
||||||
|
public class GuacamoleServletContainerInitializer implements ServletContainerInitializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The log level that should be used when we have not yet loaded any
|
||||||
|
* configuration dictating otherwise.
|
||||||
|
*/
|
||||||
|
private static final LogLevel EARLY_LOG_LEVEL = LogLevel.INFO;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
|
||||||
|
|
||||||
|
// Ideally, we want to use Guacamole's built-in logging facility, but
|
||||||
|
// we shouldn't override the administrator if they have their own SLF4J
|
||||||
|
// provider that needs to be used instead (ie: for some log aggregation
|
||||||
|
// service)
|
||||||
|
if (System.getProperty(LoggerFactory.PROVIDER_PROPERTY_KEY) == null) {
|
||||||
|
|
||||||
|
System.setProperty(LoggerFactory.PROVIDER_PROPERTY_KEY,
|
||||||
|
GuacamoleLogbackServiceProvider.class.getCanonicalName());
|
||||||
|
|
||||||
|
// Avoid excessive verbosity from SLF4J unless the administrator
|
||||||
|
// has explicitly chosen SLF4J's verbosity level (the SLF4J provider
|
||||||
|
// class will otherwise be logged at startup, which is an internal
|
||||||
|
// detail and not typically needed)
|
||||||
|
if (System.getProperty(Reporter.SLF4J_INTERNAL_VERBOSITY_KEY) == null)
|
||||||
|
System.setProperty(Reporter.SLF4J_INTERNAL_VERBOSITY_KEY, "WARN");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt early initialization of Logback (before we begin parsing
|
||||||
|
// Guacamole's configuration), such that even startup messages follow
|
||||||
|
// a consistent format
|
||||||
|
try (InputStream logbackConfiguration = EARLY_LOG_LEVEL.getLogbackConfiguration()) {
|
||||||
|
ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
|
||||||
|
if (loggerFactory instanceof ReconfigurableLoggerFactory)
|
||||||
|
((ReconfigurableLoggerFactory) loggerFactory).reconfigure(logbackConfiguration);
|
||||||
|
}
|
||||||
|
catch (JoranException | IOException e) {
|
||||||
|
System.err.println("Early initialization of Logback failed - "
|
||||||
|
+ "logging format may not match until the web application "
|
||||||
|
+ "finishes starting up: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -166,8 +166,7 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener
|
|||||||
logger.info("Read configuration parameters from \"{}\".", guacProperties);
|
logger.info("Read configuration parameters from \"{}\".", guacProperties);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Unable to read guacamole.properties: {}", e.getMessage());
|
logger.error("Unable to read guacamole.properties: {}", e.getMessage(), e);
|
||||||
logger.debug("Error reading guacamole.properties.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For any values not defined in GUACAMOLE_HOME/guacamole.properties,
|
// For any values not defined in GUACAMOLE_HOME/guacamole.properties,
|
||||||
@@ -181,8 +180,7 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Unable to configure support for environment properties: {}", e.getMessage());
|
logger.error("Unable to configure support for environment properties: {}", e.getMessage(), e);
|
||||||
logger.debug("Error reading \"{}\" property from guacamole.properties.", ENABLE_ENVIRONMENT_PROPERTIES.getName(), e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For any values not defined in GUACAMOLE_HOME/guacamole.properties
|
// For any values not defined in GUACAMOLE_HOME/guacamole.properties
|
||||||
@@ -198,8 +196,7 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Unable to configure support for file environment properties: {}", e.getMessage());
|
logger.error("Unable to configure support for file environment properties: {}", e.getMessage(), e);
|
||||||
logger.debug("Error reading \"{}\" property from guacamole.properties.", ENABLE_FILE_ENVIRONMENT_PROPERTIES.getName(), e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that at least the main guacamole.properties source of
|
// Now that at least the main guacamole.properties source of
|
||||||
@@ -225,8 +222,7 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("An extension listening for application startup failed: {}", e.getMessage());
|
logger.error("An extension listening for application startup failed: {}", e.getMessage(), e);
|
||||||
logger.debug("Extension failed internally while handling the application startup event.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -301,14 +297,15 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Inform any listeners that application shutdown has completed
|
// Inform any listeners that application shutdown has completed
|
||||||
try {
|
if (listenerService != null) {
|
||||||
listenerService.handleEvent(new ApplicationShutdownEvent() {
|
try {
|
||||||
// The application shutdown event currently has no content
|
listenerService.handleEvent(new ApplicationShutdownEvent() {
|
||||||
});
|
// The application shutdown event currently has no content
|
||||||
}
|
});
|
||||||
catch (GuacamoleException e) {
|
}
|
||||||
logger.error("An extension listening for application shutdown failed: {}", e.getMessage());
|
catch (GuacamoleException e) {
|
||||||
logger.debug("Extension failed internally while handling the application shutdown event.", e);
|
logger.error("An extension listening for application shutdown failed: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,8 +320,7 @@ public class GuacamoleSession {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("An extension listening for session invalidation failed: {}", e.getMessage());
|
logger.error("An extension listening for session invalidation failed: {}", e.getMessage(), e);
|
||||||
logger.debug("Extension failed internally while handling the session invalidation event.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -51,8 +51,8 @@ public class SystemFileEnvironmentGuacamoleProperties implements GuacamoleProper
|
|||||||
return Files.asCharSource(new File(filename), StandardCharsets.UTF_8).read();
|
return Files.asCharSource(new File(filename), StandardCharsets.UTF_8).read();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.error("Property \"{}\" could not be read from file \"{}\": {}", name, filename, e.getMessage());
|
logger.error("Property \"{}\" could not be read from file \"{}\": {}", name,
|
||||||
logger.debug("Error reading property value from file.", e);
|
filename, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-8
@@ -106,13 +106,13 @@ public class FileAuthenticationProvider extends SimpleAuthenticationProvider {
|
|||||||
parser = SAXParserFactory.newInstance().newSAXParser();
|
parser = SAXParserFactory.newInstance().newSAXParser();
|
||||||
}
|
}
|
||||||
catch (ParserConfigurationException e) {
|
catch (ParserConfigurationException e) {
|
||||||
logger.error("Unable to create XML parser for reading \"{}\": {}", USER_MAPPING_FILENAME, e.getMessage());
|
logger.error("Unable to create XML parser for reading \"{}\": {}",
|
||||||
logger.debug("An instance of SAXParser could not be created.", e);
|
USER_MAPPING_FILENAME, e.getMessage(), e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (SAXException e) {
|
catch (SAXException e) {
|
||||||
logger.error("Unable to create XML parser for reading \"{}\": {}", USER_MAPPING_FILENAME, e.getMessage());
|
logger.error("Unable to create XML parser for reading \"{}\": {}",
|
||||||
logger.debug("An instance of SAXParser could not be created.", e);
|
USER_MAPPING_FILENAME, e.getMessage(), e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,15 +138,13 @@ public class FileAuthenticationProvider extends SimpleAuthenticationProvider {
|
|||||||
|
|
||||||
// If the file is unreadable, return no mapping
|
// If the file is unreadable, return no mapping
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.warn("Unable to read user mapping file \"{}\": {}", userMappingFile, e.getMessage());
|
logger.warn("Unable to read user mapping file \"{}\": {}", userMappingFile, e.getMessage(), e);
|
||||||
logger.debug("Error reading user mapping file.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file cannot be parsed, return no mapping
|
// If the file cannot be parsed, return no mapping
|
||||||
catch (SAXException e) {
|
catch (SAXException e) {
|
||||||
logger.warn("User mapping file \"{}\" is not valid: {}", userMappingFile, e.getMessage());
|
logger.warn("User mapping file \"{}\" is not valid: {}", userMappingFile, e.getMessage(), e);
|
||||||
logger.debug("Error parsing user mapping file.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -183,30 +183,33 @@ public class EventLoggingListener implements Listener {
|
|||||||
if (creds.isEmpty())
|
if (creds.isEmpty())
|
||||||
logger.debug("Empty authentication attempt (login screen "
|
logger.debug("Empty authentication attempt (login screen "
|
||||||
+ "initialization) from {} failed: {}",
|
+ "initialization) from {} failed: {}",
|
||||||
new RemoteAddress(creds), new Failure(event));
|
new RemoteAddress(creds), new Failure(event), event.getFailure());
|
||||||
else if (username == null || username.isEmpty())
|
else if (username == null || username.isEmpty())
|
||||||
logger.debug("Anonymous authentication attempt from {} failed: {}",
|
logger.debug("Anonymous authentication attempt from {} failed: {}",
|
||||||
new RemoteAddress(creds), new Failure(event));
|
new RemoteAddress(creds), new Failure(event), event.getFailure());
|
||||||
else if (event.getFailure() instanceof GuacamoleInsufficientCredentialsException) {
|
else if (event.getFailure() instanceof GuacamoleInsufficientCredentialsException) {
|
||||||
if (authProvider != null)
|
if (authProvider != null)
|
||||||
logger.debug("Authentication attempt from {} for user \"{}\" "
|
logger.debug("Authentication attempt from {} for user \"{}\" "
|
||||||
+ "requires additional credentials to continue: {} "
|
+ "requires additional credentials to continue: {} "
|
||||||
+ "(requested by \"{}\")", new RemoteAddress(creds),
|
+ "(requested by \"{}\")", new RemoteAddress(creds),
|
||||||
username, new Failure(event), authProvider.getIdentifier());
|
username, new Failure(event), authProvider.getIdentifier(),
|
||||||
|
event.getFailure());
|
||||||
else
|
else
|
||||||
logger.debug("Authentication attempt from {} for user \"{}\" "
|
logger.debug("Authentication attempt from {} for user \"{}\" "
|
||||||
+ "requires additional credentials to continue: {}",
|
+ "requires additional credentials to continue: {}",
|
||||||
new RemoteAddress(creds), username, new Failure(event));
|
new RemoteAddress(creds), username, new Failure(event),
|
||||||
|
event.getFailure());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (authProvider != null)
|
if (authProvider != null)
|
||||||
logger.warn("Authentication attempt from {} for user \"{}\" "
|
logger.warn("Authentication attempt from {} for user \"{}\" "
|
||||||
+ "failed: {} (rejected by \"{}\")", new RemoteAddress(creds),
|
+ "failed: {} (rejected by \"{}\")", new RemoteAddress(creds),
|
||||||
username, new Failure(event), authProvider.getIdentifier());
|
username, new Failure(event), authProvider.getIdentifier(),
|
||||||
|
event.getFailure());
|
||||||
else
|
else
|
||||||
logger.warn("Authentication attempt from {} for user \"{}\" "
|
logger.warn("Authentication attempt from {} for user \"{}\" "
|
||||||
+ "failed: {}", new RemoteAddress(creds), username,
|
+ "failed: {}", new RemoteAddress(creds), username,
|
||||||
new Failure(event));
|
new Failure(event), event.getFailure());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-7
@@ -40,7 +40,7 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
/**
|
/**
|
||||||
* Logger for this class.
|
* Logger for this class.
|
||||||
*/
|
*/
|
||||||
private Logger logger = LoggerFactory.getLogger(AuthenticationProviderFacade.class);
|
private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderFacade.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The underlying authentication provider, or null if the authentication
|
* The underlying authentication provider, or null if the authentication
|
||||||
@@ -86,9 +86,11 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
public AuthenticationProviderFacade(
|
public AuthenticationProviderFacade(
|
||||||
Class<? extends AuthenticationProvider> authProviderClass,
|
Class<? extends AuthenticationProvider> authProviderClass,
|
||||||
Set<String> tolerateFailures) {
|
Set<String> tolerateFailures) {
|
||||||
|
|
||||||
this.tolerateFailures = tolerateFailures;
|
this.tolerateFailures = tolerateFailures;
|
||||||
this.authProvider = ProviderFactory.newInstance("authentication provider",
|
this.authProvider = ProviderFactory.newInstance("authentication provider",
|
||||||
authProviderClass);
|
authProviderClass);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -155,15 +157,11 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
* authentication provider being skipped.
|
* authentication provider being skipped.
|
||||||
*/
|
*/
|
||||||
private void warnAuthProviderSkipped(Throwable e) {
|
private void warnAuthProviderSkipped(Throwable e) {
|
||||||
|
|
||||||
logger.warn("The \"{}\" authentication provider has been skipped due "
|
logger.warn("The \"{}\" authentication provider has been skipped due "
|
||||||
+ "to an internal error. If this is unexpected or you are the "
|
+ "to an internal error. If this is unexpected or you are the "
|
||||||
+ "developer of this authentication provider, you may wish to "
|
+ "developer of this authentication provider, you may wish to "
|
||||||
+ "enable debug-level logging: {}",
|
+ "enable debug-level logging: {}",
|
||||||
getIdentifier(), e.getMessage());
|
getIdentifier(), e.getMessage(), e);
|
||||||
|
|
||||||
logger.debug("Authentication provider skipped due to an internal failure.", e);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -299,7 +297,7 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
|
|
||||||
// Delegate to underlying auth provider
|
// Delegate to underlying auth provider
|
||||||
return authProvider.updateUserContext(context, authenticatedUser, credentials);
|
return authProvider.updateUserContext(context, authenticatedUser, credentials);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import org.apache.guacamole.GuacamoleException;
|
|||||||
* A ClassLoader implementation which finds classes within .jar files within a
|
* A ClassLoader implementation which finds classes within .jar files within a
|
||||||
* given directory.
|
* given directory.
|
||||||
*/
|
*/
|
||||||
public class DirectoryClassLoader extends URLClassLoader {
|
public class DirectoryClassLoader extends URLClassLoader implements LoggerContextProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all .jar files within the given directory as an array of URLs.
|
* Returns all .jar files within the given directory as an array of URLs.
|
||||||
@@ -105,4 +105,9 @@ public class DirectoryClassLoader extends URLClassLoader {
|
|||||||
super(getJarURLs(dir), DirectoryClassLoader.class.getClassLoader());
|
super(getJarURLs(dir), DirectoryClassLoader.class.getClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLoggerContext() {
|
||||||
|
return "lib";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -397,7 +397,7 @@ public class Extension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create isolated classloader for this extension
|
// Create isolated classloader for this extension
|
||||||
classLoader = new ExtensionClassLoader(file, temporaryFiles, parent);
|
classLoader = new ExtensionClassLoader(manifest, file, temporaryFiles, parent);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
* the given .jar. If classes are defined in both the parent and the extension
|
* the given .jar. If classes are defined in both the parent and the extension
|
||||||
* .jar, the versions defined within the extension .jar are used.
|
* .jar, the versions defined within the extension .jar are used.
|
||||||
*/
|
*/
|
||||||
public class ExtensionClassLoader extends URLClassLoader {
|
public class ExtensionClassLoader extends URLClassLoader implements LoggerContextProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger for this class.
|
* Logger for this class.
|
||||||
@@ -71,6 +71,11 @@ public class ExtensionClassLoader extends URLClassLoader {
|
|||||||
*/
|
*/
|
||||||
private final ClassLoader parent;
|
private final ClassLoader parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parsed manifest file of the extension that this class loader is for.
|
||||||
|
*/
|
||||||
|
private final ExtensionManifest manifest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the URL that refers to the given file. If the given file refers
|
* Returns the URL that refers to the given file. If the given file refers
|
||||||
* to a directory, an exception is thrown.
|
* to a directory, an exception is thrown.
|
||||||
@@ -251,6 +256,9 @@ public class ExtensionClassLoader extends URLClassLoader {
|
|||||||
* multiple times will not affect previously-returned instances of
|
* multiple times will not affect previously-returned instances of
|
||||||
* ExtensionClassLoader.
|
* ExtensionClassLoader.
|
||||||
*
|
*
|
||||||
|
* @param manifest
|
||||||
|
* The parsed manifest of the extension.
|
||||||
|
*
|
||||||
* @param extension
|
* @param extension
|
||||||
* The extension .jar file from which classes should be loaded.
|
* The extension .jar file from which classes should be loaded.
|
||||||
*
|
*
|
||||||
@@ -267,10 +275,11 @@ public class ExtensionClassLoader extends URLClassLoader {
|
|||||||
* If the given file is not actually a file, or the contents of the
|
* If the given file is not actually a file, or the contents of the
|
||||||
* file cannot be read.
|
* file cannot be read.
|
||||||
*/
|
*/
|
||||||
public ExtensionClassLoader(File extension, List<File> temporaryFiles,
|
public ExtensionClassLoader(ExtensionManifest manifest, File extension,
|
||||||
ClassLoader parent) throws GuacamoleException {
|
List<File> temporaryFiles, ClassLoader parent) throws GuacamoleException {
|
||||||
super(getExtensionURLs(extension, temporaryFiles), null);
|
super(getExtensionURLs(extension, temporaryFiles), null);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
this.manifest = manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -288,4 +297,9 @@ public class ExtensionClassLoader extends URLClassLoader {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLoggerContext() {
|
||||||
|
return "ext:" + manifest.getNamespace();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -434,8 +434,9 @@ public class ExtensionModule extends ServletModule {
|
|||||||
|
|
||||||
// Use empty set by default if property cannot be parsed
|
// Use empty set by default if property cannot be parsed
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.warn("The list of authentication providers specified via the \"{}\" property could not be parsed: {}", SKIP_IF_UNAVAILABLE.getName(), e.getMessage());
|
logger.warn("The list of authentication providers specified via "
|
||||||
logger.debug("Unable to parse \"{}\" property.", SKIP_IF_UNAVAILABLE.getName(), e);
|
+ "the \"{}\" property could not be parsed: {}",
|
||||||
|
SKIP_IF_UNAVAILABLE.getName(), e.getMessage(), e);
|
||||||
return Collections.<String>emptySet();
|
return Collections.<String>emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,8 +458,9 @@ public class ExtensionModule extends ServletModule {
|
|||||||
|
|
||||||
// Sort by filename if the desired order cannot be read
|
// Sort by filename if the desired order cannot be read
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.warn("The list of extensions specified via the \"{}\" property could not be parsed: {}", EXTENSION_PRIORITY.getName(), e.getMessage());
|
logger.warn("The list of extensions specified via the \"{}\" "
|
||||||
logger.debug("Unable to parse \"{}\" property.", EXTENSION_PRIORITY.getName(), e);
|
+ "property could not be parsed: {}",
|
||||||
|
EXTENSION_PRIORITY.getName(), e.getMessage(), e);
|
||||||
return ExtensionOrderProperty.DEFAULT_COMPARATOR;
|
return ExtensionOrderProperty.DEFAULT_COMPARATOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,8 +524,8 @@ public class ExtensionModule extends ServletModule {
|
|||||||
|
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Extension \"{}\" could not be loaded: {}", extensionFile.getName(), e.getMessage());
|
logger.error("Extension \"{}\" could not be loaded: {}",
|
||||||
logger.debug("Unable to load extension.", e);
|
extensionFile.getName(), e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,8 +125,7 @@ public class LanguageResourceService {
|
|||||||
// Warn of failure to parse
|
// Warn of failure to parse
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
parsedAllowedLanguages = null;
|
parsedAllowedLanguages = null;
|
||||||
logger.error("Unable to parse list of allowed languages: {}", e.getMessage());
|
logger.error("Unable to parse list of allowed languages: {}", e.getMessage(), e);
|
||||||
logger.debug("Error parsing list of allowed languages.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.allowedLanguages = parsedAllowedLanguages;
|
this.allowedLanguages = parsedAllowedLanguages;
|
||||||
@@ -308,8 +307,7 @@ public class LanguageResourceService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.error("Unable to merge language resource \"{}\": {}", key, e.getMessage());
|
logger.error("Unable to merge language resource \"{}\": {}", key, e.getMessage(), e);
|
||||||
logger.debug("Error merging language resource.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -437,8 +435,7 @@ public class LanguageResourceService {
|
|||||||
|
|
||||||
// Continue with next language if unable to read
|
// Continue with next language if unable to read
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.warn("Unable to read language resource \"{}\".", languageKey);
|
logger.warn("Unable to read language resource \"{}\".", languageKey, e);
|
||||||
logger.debug("Error reading language resource.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.guacamole.extension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider of general, human-readable text that describes the context of a
|
||||||
|
* logger. This is currently used to allow our various custom class loaders to
|
||||||
|
* provide contextual information, which our custom logger factory can pull from
|
||||||
|
* the call stack when loggers are created.
|
||||||
|
*/
|
||||||
|
public interface LoggerContextProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns human-readable text describing the context of events logged by
|
||||||
|
* a logger. The text should describe the general context of the source of
|
||||||
|
* the entry.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Human-readable text describing the context of events logged by any
|
||||||
|
* logger created/maintained by the class implementing this interface.
|
||||||
|
*/
|
||||||
|
String getLoggerContext();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -61,35 +61,29 @@ class ProviderFactory {
|
|||||||
catch (NoSuchMethodException e) {
|
catch (NoSuchMethodException e) {
|
||||||
logger.error("The {} extension in use is not properly defined. "
|
logger.error("The {} extension in use is not properly defined. "
|
||||||
+ "Please contact the developers of the extension or, if you "
|
+ "Please contact the developers of the extension or, if you "
|
||||||
+ "are the developer, turn on debug-level logging.", typeName);
|
+ "are the developer, turn on debug-level logging.",
|
||||||
logger.debug("{} is missing a default constructor.",
|
typeName, e);
|
||||||
providerClass.getName(), e);
|
|
||||||
}
|
}
|
||||||
catch (SecurityException e) {
|
catch (SecurityException e) {
|
||||||
logger.error("The Java security manager is preventing extensions "
|
logger.error("The Java security manager is preventing extensions "
|
||||||
+ "from being loaded. Please check the configuration of Java or your "
|
+ "from being loaded. Please check the configuration of Java or your "
|
||||||
+ "servlet container.");
|
+ "servlet container.", e);
|
||||||
logger.debug("Creation of {} disallowed by security manager.",
|
|
||||||
providerClass.getName(), e);
|
|
||||||
}
|
}
|
||||||
catch (InstantiationException e) {
|
catch (InstantiationException e) {
|
||||||
logger.error("The {} extension in use is not properly defined. "
|
logger.error("The {} extension in use is not properly defined. "
|
||||||
+ "Please contact the developers of the extension or, if you "
|
+ "Please contact the developers of the extension or, if you "
|
||||||
+ "are the developer, turn on debug-level logging.", typeName);
|
+ "are the developer, turn on debug-level logging.",
|
||||||
logger.debug("{} cannot be instantiated.", providerClass.getName(), e);
|
typeName, e);
|
||||||
}
|
}
|
||||||
catch (IllegalAccessException e) {
|
catch (IllegalAccessException e) {
|
||||||
logger.error("The {} extension in use is not properly defined. "
|
logger.error("The {} extension in use is not properly defined. "
|
||||||
+ "Please contact the developers of the extension or, if you "
|
+ "Please contact the developers of the extension or, if you "
|
||||||
+ "are the developer, turn on debug-level logging.");
|
+ "are the developer, turn on debug-level logging.", e);
|
||||||
logger.debug("Default constructor of {} is not public.", typeName, e);
|
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
logger.error("The {} extension in use is not properly defined. "
|
logger.error("The {} extension in use is not properly defined. "
|
||||||
+ "Please contact the developers of the extension or, if you "
|
+ "Please contact the developers of the extension or, if you "
|
||||||
+ "are the developer, turn on debug-level logging.", typeName);
|
+ "are the developer, turn on debug-level logging.", typeName, e);
|
||||||
logger.debug("Default constructor of {} cannot accept zero arguments.",
|
|
||||||
providerClass.getName(), e);
|
|
||||||
}
|
}
|
||||||
catch (InvocationTargetException e) {
|
catch (InvocationTargetException e) {
|
||||||
// Obtain causing error - create relatively-informative stub error if cause is unknown
|
// Obtain causing error - create relatively-informative stub error if cause is unknown
|
||||||
@@ -97,8 +91,7 @@ class ProviderFactory {
|
|||||||
if (cause == null)
|
if (cause == null)
|
||||||
cause = new GuacamoleException("Error encountered during initialization.");
|
cause = new GuacamoleException("Error encountered during initialization.");
|
||||||
|
|
||||||
logger.error("{} extension failed to start: {}", typeName, cause.getMessage());
|
logger.error("{} extension failed to start: {}", typeName, cause.getMessage(), cause);
|
||||||
logger.debug("{} instantiation failed.", providerClass.getName(), e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
|
|||||||
@@ -0,0 +1,429 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.guacamole.log;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.Marker;
|
||||||
|
import org.slf4j.MarkerFactory;
|
||||||
|
import org.slf4j.event.Level;
|
||||||
|
import org.slf4j.spi.LoggingEventBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Logger} that automatically includes arbitrary context. The additional
|
||||||
|
* context is included as a {@link Marker}, and all logging functions are
|
||||||
|
* overridden to include at least this Marker. If a logging function is given a
|
||||||
|
* Marker by the caller, it is wrapped as a reference of Marker created by the
|
||||||
|
* ContextAwareLogger's Marker.
|
||||||
|
*/
|
||||||
|
public class ContextAwareLogger implements Logger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wrapped Logger.
|
||||||
|
*/
|
||||||
|
private final Logger logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arbitrary context that should be included as a Marker in all log
|
||||||
|
* messages.
|
||||||
|
*/
|
||||||
|
private final String context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ContextAwareLogger that wraps given Logger, automatically
|
||||||
|
* including the provided context as a Marker for each logged message.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The arbitrary context to include as a Marker with each
|
||||||
|
* logged message.
|
||||||
|
*
|
||||||
|
* @param logger
|
||||||
|
* The Logger to wrap.
|
||||||
|
*/
|
||||||
|
public ContextAwareLogger(String context, Logger logger) {
|
||||||
|
this.logger = logger;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Marker containing the arbitrary context provided when this
|
||||||
|
* ContextAwareLogger was created. If Markers are provided to this function,
|
||||||
|
* they are included as references within the returned Marker.
|
||||||
|
*
|
||||||
|
* @param refs
|
||||||
|
* The Markers to include in the returned Marker as references, if any.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A Marker containing the context provided when this
|
||||||
|
* ContextAwareLogger was created, as well as any provided Marker
|
||||||
|
* references.
|
||||||
|
*/
|
||||||
|
private Marker getContextMarker(Marker... refs) {
|
||||||
|
Marker marker = MarkerFactory.getMarker(context);
|
||||||
|
Arrays.stream(refs).forEach(marker::add);
|
||||||
|
return marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return logger.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggingEventBuilder makeLoggingEventBuilder(Level level) {
|
||||||
|
return logger.makeLoggingEventBuilder(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggingEventBuilder atLevel(Level level) {
|
||||||
|
return logger.atLevel(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForLevel(Level level) {
|
||||||
|
return logger.isEnabledForLevel(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTraceEnabled() {
|
||||||
|
return logger.isTraceEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(String msg) {
|
||||||
|
logger.trace(getContextMarker(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(String format, Object arg) {
|
||||||
|
logger.trace(getContextMarker(), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(String format, Object arg1, Object arg2) {
|
||||||
|
logger.trace(getContextMarker(), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(String format, Object... arguments) {
|
||||||
|
logger.trace(getContextMarker(), format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(String msg, Throwable t) {
|
||||||
|
logger.trace(getContextMarker(), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTraceEnabled(Marker marker) {
|
||||||
|
return logger.isTraceEnabled(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggingEventBuilder atTrace() {
|
||||||
|
return logger.atTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(Marker marker, String msg) {
|
||||||
|
logger.trace(getContextMarker(marker), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(Marker marker, String format, Object arg) {
|
||||||
|
logger.trace(getContextMarker(marker), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(Marker marker, String format, Object arg1, Object arg2) {
|
||||||
|
logger.trace(getContextMarker(marker), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(Marker marker, String format, Object... argArray) {
|
||||||
|
logger.trace(getContextMarker(marker), format, argArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trace(Marker marker, String msg, Throwable t) {
|
||||||
|
logger.trace(getContextMarker(marker), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDebugEnabled() {
|
||||||
|
return logger.isDebugEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String msg) {
|
||||||
|
logger.debug(getContextMarker(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String format, Object arg) {
|
||||||
|
logger.debug(getContextMarker(), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String format, Object arg1, Object arg2) {
|
||||||
|
logger.debug(getContextMarker(), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String format, Object... arguments) {
|
||||||
|
logger.debug(getContextMarker(), format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String msg, Throwable t) {
|
||||||
|
logger.debug(getContextMarker(), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDebugEnabled(Marker marker) {
|
||||||
|
return logger.isDebugEnabled(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(Marker marker, String msg) {
|
||||||
|
logger.debug(getContextMarker(marker), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(Marker marker, String format, Object arg) {
|
||||||
|
logger.debug(getContextMarker(marker), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(Marker marker, String format, Object arg1, Object arg2) {
|
||||||
|
logger.debug(getContextMarker(marker), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(Marker marker, String format, Object... arguments) {
|
||||||
|
logger.debug(getContextMarker(marker), format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(Marker marker, String msg, Throwable t) {
|
||||||
|
logger.debug(getContextMarker(marker), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggingEventBuilder atDebug() {
|
||||||
|
return logger.atDebug();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInfoEnabled() {
|
||||||
|
return logger.isInfoEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(String msg) {
|
||||||
|
logger.info(getContextMarker(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(String format, Object arg) {
|
||||||
|
logger.info(getContextMarker(), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(String format, Object arg1, Object arg2) {
|
||||||
|
logger.info(getContextMarker(), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(String format, Object... arguments) {
|
||||||
|
logger.info(getContextMarker(), format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(String msg, Throwable t) {
|
||||||
|
logger.info(getContextMarker(), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInfoEnabled(Marker marker) {
|
||||||
|
return logger.isInfoEnabled(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(Marker marker, String msg) {
|
||||||
|
logger.info(getContextMarker(marker), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(Marker marker, String format, Object arg) {
|
||||||
|
logger.info(getContextMarker(marker), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(Marker marker, String format, Object arg1, Object arg2) {
|
||||||
|
logger.info(getContextMarker(marker), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(Marker marker, String format, Object... arguments) {
|
||||||
|
logger.info(getContextMarker(marker), format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(Marker marker, String msg, Throwable t) {
|
||||||
|
logger.info(getContextMarker(marker), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggingEventBuilder atInfo() {
|
||||||
|
return logger.atInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWarnEnabled() {
|
||||||
|
return logger.isWarnEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(String msg) {
|
||||||
|
logger.warn(getContextMarker(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(String format, Object arg) {
|
||||||
|
logger.warn(getContextMarker(), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(String format, Object... arguments) {
|
||||||
|
logger.warn(getContextMarker(), format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(String format, Object arg1, Object arg2) {
|
||||||
|
logger.warn(getContextMarker(), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(String msg, Throwable t) {
|
||||||
|
logger.warn(getContextMarker(), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWarnEnabled(Marker marker) {
|
||||||
|
return logger.isWarnEnabled(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(Marker marker, String msg) {
|
||||||
|
logger.warn(getContextMarker(marker), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(Marker marker, String format, Object arg) {
|
||||||
|
logger.warn(getContextMarker(marker), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(Marker marker, String format, Object arg1, Object arg2) {
|
||||||
|
logger.warn(getContextMarker(marker), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(Marker marker, String format, Object... arguments) {
|
||||||
|
logger.warn(getContextMarker(marker), format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(Marker marker, String msg, Throwable t) {
|
||||||
|
logger.warn(getContextMarker(marker), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggingEventBuilder atWarn() {
|
||||||
|
return logger.atWarn();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isErrorEnabled() {
|
||||||
|
return logger.isErrorEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String msg) {
|
||||||
|
logger.error(getContextMarker(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String format, Object arg) {
|
||||||
|
logger.error(getContextMarker(), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String format, Object arg1, Object arg2) {
|
||||||
|
logger.error(getContextMarker(), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String format, Object... arguments) {
|
||||||
|
logger.error(getContextMarker(), format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String msg, Throwable t) {
|
||||||
|
logger.error(getContextMarker(), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isErrorEnabled(Marker marker) {
|
||||||
|
return logger.isErrorEnabled(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Marker marker, String msg) {
|
||||||
|
logger.error(getContextMarker(marker), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Marker marker, String format, Object arg) {
|
||||||
|
logger.error(getContextMarker(marker), format, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Marker marker, String format, Object arg1, Object arg2) {
|
||||||
|
logger.error(getContextMarker(marker), format, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Marker marker, String format, Object... arguments) {
|
||||||
|
logger.error(getContextMarker(marker), format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Marker marker, String msg, Throwable t) {
|
||||||
|
logger.error(getContextMarker(marker), msg, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoggingEventBuilder atError() {
|
||||||
|
return logger.atError();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,355 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.guacamole.log;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
|
import ch.qos.logback.classic.joran.JoranConfigurator;
|
||||||
|
import ch.qos.logback.classic.util.ContextInitializer;
|
||||||
|
import ch.qos.logback.core.joran.spi.JoranException;
|
||||||
|
import ch.qos.logback.core.util.StatusPrinter;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.guacamole.extension.LoggerContextProvider;
|
||||||
|
import org.slf4j.ILoggerFactory;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
import org.slf4j.Marker;
|
||||||
|
import org.slf4j.spi.MDCAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ILoggerFactory} implementation that automatically includes additional
|
||||||
|
* {@link Marker} context based on the origin of each log message. The origin
|
||||||
|
* of each message is determined by investigating the caller classes, with
|
||||||
|
* the actual context provided by the associated classloader if it implements
|
||||||
|
* {@link LoggerContextProvider}.
|
||||||
|
*/
|
||||||
|
public class ContextAwareLoggerFactory implements ReconfigurableLoggerFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the LoggerContext maintained by this logger factory.
|
||||||
|
*/
|
||||||
|
private static final String LOGGER_CONTEXT_NAME = "guacamole";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default text that should be displayed if no other context can be
|
||||||
|
* derived from any caller.
|
||||||
|
*/
|
||||||
|
private static final String DEFAULT_CONTEXT = "app";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular expression that matches one or more newlines, regardless of style
|
||||||
|
* (Windows, Linux, etc.), preserving only the first such newline. This
|
||||||
|
* expression is intended to reliably match newlines that may be interpreted
|
||||||
|
* by the shell/viewer, regardless of the platform producing the log
|
||||||
|
* message.
|
||||||
|
* <p>
|
||||||
|
* This specific regular expression is intended for use with content that is
|
||||||
|
* being included inline (concatenated with an existing line of output).
|
||||||
|
*/
|
||||||
|
private static final String INLINE_NEWLINE_REGEX =
|
||||||
|
|
||||||
|
// Locate the right edge of the first newline sequence, whether
|
||||||
|
// that is CR, LF, CRLF, or LFCR
|
||||||
|
"(?<="
|
||||||
|
|
||||||
|
// Do not match newline sequences that are not the first
|
||||||
|
// newline in the sequence
|
||||||
|
+ "(?<![\\n\\r])"
|
||||||
|
|
||||||
|
// Match either CR, LF, CRLF, or LFCR, ensuring that CRLF
|
||||||
|
// does not get matched as just CR (same for LFCR vs. LF)
|
||||||
|
+ "("
|
||||||
|
+ "\\n(?!\\r)" // LF (with no following CR)
|
||||||
|
+ "|\\r(?!\\n)" // CR (with no following LF)
|
||||||
|
+ "|\\n\\r" // LFCR
|
||||||
|
+ "|\\r\\n" // CRLF
|
||||||
|
+ ")"
|
||||||
|
|
||||||
|
+ ")"
|
||||||
|
|
||||||
|
// Ignore (and strip) and remaining newline characters
|
||||||
|
+ "[\\n\\r]*";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identical in purpose and behavior to {@link #INLINE_NEWLINE_REGEX}, but
|
||||||
|
* intended for output that stands on its own as a block of text. This
|
||||||
|
* variation preserves formatting of a block by matching the beginning of
|
||||||
|
* all lines (regardless of whether preceded by a newline) and not matching
|
||||||
|
* the final newline.
|
||||||
|
*/
|
||||||
|
private static final String BLOCK_NEWLINE_REGEX = "(^|" + INLINE_NEWLINE_REGEX + ")(?=[^\\r\\n])";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logback encoder pattern fragment that produces a local timestamp (with
|
||||||
|
* timezone information) in roughly ISO 8601 format.
|
||||||
|
*/
|
||||||
|
private static final String LOG_PATTERN_FRAGMENT_TIMESTAMP = "%d{yyyy-MM-dd HH:mm:ss.SSS xxx}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text that should be prepended to log messages when they span multiple
|
||||||
|
* lines.
|
||||||
|
*/
|
||||||
|
private static final String CONTINUATION_PREFIX = "+ ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logback encoder pattern fragment that produces the logged message. This
|
||||||
|
* automatically prepends any newlines within the message with a "+ " prefix
|
||||||
|
* to clearly represent when a message has been split across multiple lines.
|
||||||
|
*/
|
||||||
|
private static final String LOG_PATTERN_FRAGMENT_MESSAGE_BODY = "%replace(%msg){'" + INLINE_NEWLINE_REGEX + "','" + CONTINUATION_PREFIX + "'}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logback encoder pattern that should be used for messages when the highest
|
||||||
|
* level of verbosity is not needed.
|
||||||
|
*/
|
||||||
|
private static final String LOG_PATTERN_DEFAULT = LOG_PATTERN_FRAGMENT_TIMESTAMP
|
||||||
|
+ " guacamole [%marker] %level: "
|
||||||
|
+ LOG_PATTERN_FRAGMENT_MESSAGE_BODY + "%n"
|
||||||
|
+ "%nopex";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logback encoder pattern that should be used for messages with the highest
|
||||||
|
* level of verbosity.
|
||||||
|
*/
|
||||||
|
private static final String LOG_PATTERN_VERBOSE = LOG_PATTERN_FRAGMENT_TIMESTAMP
|
||||||
|
+ " guacamole [%marker, thread:%thread] %level: %logger{36} - "
|
||||||
|
+ LOG_PATTERN_FRAGMENT_MESSAGE_BODY + "%n"
|
||||||
|
+ "%replace(%xThrowable){'" + BLOCK_NEWLINE_REGEX + "','" + CONTINUATION_PREFIX + "'}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The top-level Logback object, the LoggerContext, which is also Logback's
|
||||||
|
* LoggerFactory implementation.
|
||||||
|
*/
|
||||||
|
private final LoggerContext context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ContextAwareLoggerFactory that uses the given MDCAdapter
|
||||||
|
* for the {@link MDC} implementation.
|
||||||
|
*
|
||||||
|
* @param mdcAdapter
|
||||||
|
* The MDCAdapter that should be used to provide the MDC
|
||||||
|
* implementation.
|
||||||
|
*/
|
||||||
|
public ContextAwareLoggerFactory(MDCAdapter mdcAdapter) {
|
||||||
|
|
||||||
|
context = new LoggerContext();
|
||||||
|
context.setName(LOGGER_CONTEXT_NAME);
|
||||||
|
context.setMDCAdapter(mdcAdapter);
|
||||||
|
|
||||||
|
// Perform basic init (prior to loading any Guacamole-specific
|
||||||
|
// configuration, which is loaded by the LogModule)
|
||||||
|
try {
|
||||||
|
new ContextInitializer(context).autoConfig();
|
||||||
|
}
|
||||||
|
catch (JoranException | RuntimeException | Error e) {
|
||||||
|
System.err.println("Logging system could not be initialized: " + e.getMessage());
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the {@link StackWalker} class provided by Java9+ to retrieve all
|
||||||
|
* classes currently on the call stack for the current function, in order of
|
||||||
|
* decreasing depth. The current function (the deepest function in terms of
|
||||||
|
* call depth) is the first item in the returned list.
|
||||||
|
* <p>
|
||||||
|
* NOTE: This method is only supported by Java 9 and late and will throw a
|
||||||
|
* ClassNotFoundException otherwise..
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A List of all classes currently on the call stack, in order of
|
||||||
|
* decreasing depth.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException
|
||||||
|
* If support for StackWalker is not present (Java 8).
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<Class<?>> getCallersFromStackWalker()
|
||||||
|
throws UnsupportedOperationException {
|
||||||
|
|
||||||
|
//
|
||||||
|
// NOTE: The following is the reflection equivalent of:
|
||||||
|
//
|
||||||
|
// return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk((stack) ->
|
||||||
|
// stack.map((frame) -> frame.getDeclaringClass()).collect(Collectors.toList())
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Retrieve StackWalker definition and related classes
|
||||||
|
Class<?> stackWalkerClass = Class.forName("java.lang.StackWalker");
|
||||||
|
Class<?> stackFrameClass = Class.forName("java.lang.StackWalker$StackFrame");
|
||||||
|
Class<Enum> optionEnum = (Class<Enum>) Class.forName("java.lang.StackWalker$Option");
|
||||||
|
|
||||||
|
// Retrieve references to functions we will be needing
|
||||||
|
Method stackWalkerGetInstanceMethod = stackWalkerClass.getMethod("getInstance", optionEnum);
|
||||||
|
Method walkMethod = stackWalkerClass.getMethod("walk", Function.class);
|
||||||
|
Method getDeclaringClassMethod = stackFrameClass.getMethod("getDeclaringClass");
|
||||||
|
|
||||||
|
// Equivalent to:
|
||||||
|
// StackWalker stackWalker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
|
||||||
|
Object stackWalker = stackWalkerGetInstanceMethod.invoke(null,
|
||||||
|
Enum.valueOf(optionEnum, "RETAIN_CLASS_REFERENCE"));
|
||||||
|
|
||||||
|
// Equivalent to:
|
||||||
|
// (stack) -> stack.map((frame) -> frame.getDeclaringClass()).collect(Collectors.toList())
|
||||||
|
Function<Stream<?>, List<Class<?>>> walker =
|
||||||
|
(stack) -> stack.map((frame) -> {
|
||||||
|
try {
|
||||||
|
return (Class<?>) getDeclaringClassMethod.invoke(frame);
|
||||||
|
}
|
||||||
|
catch (ReflectiveOperationException e) {
|
||||||
|
throw new UnsupportedOperationException("getDeclaringClass() "
|
||||||
|
+ "could not be invoked for stack frame ", e);
|
||||||
|
}
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Equivalent to: stackWalker.walk(...)
|
||||||
|
return (List<Class<?>>) walkMethod.invoke(stackWalker, walker);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ReflectiveOperationException e) {
|
||||||
|
throw new UnsupportedOperationException("StackWalker is not "
|
||||||
|
+ "supported by this version of Java", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the deprecated {@link SecurityManager} to retrieve all classes
|
||||||
|
* currently on the call stack for the current function, in order of
|
||||||
|
* decreasing depth. The current function (the deepest function in terms of
|
||||||
|
* call depth) is the first item in the returned list.
|
||||||
|
* <p>
|
||||||
|
* NOTE: This is the only method that is supported by Java 8 that includes
|
||||||
|
* direct Class references.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A List of all classes currently on the call stack, in order of
|
||||||
|
* decreasing depth.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException
|
||||||
|
* If support for SecurityManager is not present (expected for future
|
||||||
|
* Java versions).
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
private List<Class<?>> getCallersFromSecurityManager() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Atempt to locate SecurityManager class and grab its
|
||||||
|
// getClassContext() method (which returns a stack trace)
|
||||||
|
Class<?> securityManagerClass = Class.forName("java.lang.SecurityManager");
|
||||||
|
Object securityManager = securityManagerClass.getDeclaredConstructor().newInstance();
|
||||||
|
Method getClassContextMethod = securityManagerClass.getDeclaredMethod("getClassContext");
|
||||||
|
|
||||||
|
// getClassContext() is protected
|
||||||
|
getClassContextMethod.setAccessible(true);
|
||||||
|
|
||||||
|
return Arrays.asList((Class<?>[]) getClassContextMethod.invoke(securityManager));
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ReflectiveOperationException e) {
|
||||||
|
throw new UnsupportedOperationException("SecurityManager is not "
|
||||||
|
+ "supported by this version of Java", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all classes currently on the call stack for the current function,
|
||||||
|
* in order of decreasing depth. The current function (the deepest function
|
||||||
|
* in terms of call depth) is the first item in the returned list.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A List of all classes currently on the call stack, in order of
|
||||||
|
* decreasing depth.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException
|
||||||
|
* If no supported mechanism exists for retrieving the call stack.
|
||||||
|
*/
|
||||||
|
private List<Class<?>> getCallers() throws UnsupportedOperationException {
|
||||||
|
try {
|
||||||
|
return getCallersFromStackWalker();
|
||||||
|
}
|
||||||
|
catch (UnsupportedOperationException e) {
|
||||||
|
return getCallersFromSecurityManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives the context of the message currently being logged based on the
|
||||||
|
* content of the call stack.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The context of the message currently being logged.
|
||||||
|
*/
|
||||||
|
private String getCallerLogContext() {
|
||||||
|
|
||||||
|
// Derive context from the most recent caller that explicitly provides
|
||||||
|
// their own logging context, if any
|
||||||
|
for (Class<?> caller : getCallers()) {
|
||||||
|
|
||||||
|
ClassLoader classloader = caller.getClassLoader();
|
||||||
|
if (classloader instanceof LoggerContextProvider)
|
||||||
|
return ((LoggerContextProvider) classloader).getLoggerContext();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use default context if no callers provide their own
|
||||||
|
return DEFAULT_CONTEXT;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Logger getLogger(String name) {
|
||||||
|
return new ContextAwareLogger(getCallerLogContext(), context.getLogger(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reconfigure(InputStream logbackConfiguration) throws JoranException {
|
||||||
|
|
||||||
|
context.reset();
|
||||||
|
|
||||||
|
// Include convenience properties for building off Guacamole's
|
||||||
|
// built-in configuration with a custom logback.xml
|
||||||
|
context.putProperty("guac_timestamp_pattern", LOG_PATTERN_FRAGMENT_TIMESTAMP);
|
||||||
|
context.putProperty("guac_message_pattern", LOG_PATTERN_FRAGMENT_MESSAGE_BODY);
|
||||||
|
context.putProperty("guac_pattern", LOG_PATTERN_DEFAULT);
|
||||||
|
context.putProperty("guac_verbose_pattern", LOG_PATTERN_VERBOSE);
|
||||||
|
|
||||||
|
// Initialize logback
|
||||||
|
JoranConfigurator configurator = new JoranConfigurator();
|
||||||
|
configurator.setContext(context);
|
||||||
|
configurator.doConfigure(logbackConfiguration);
|
||||||
|
|
||||||
|
// Dump any errors that occur during logback init
|
||||||
|
StatusPrinter.printInCaseOfErrorsOrWarnings(context);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.guacamole.log;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.util.LogbackMDCAdapter;
|
||||||
|
import org.slf4j.IMarkerFactory;
|
||||||
|
import org.slf4j.helpers.BasicMarkerFactory;
|
||||||
|
import org.slf4j.spi.MDCAdapter;
|
||||||
|
import org.slf4j.spi.SLF4JServiceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SLF4JServiceProvider that overrides the default behavior of Logback's
|
||||||
|
* service provider, forcing the use of a custom, context-aware logger factory.
|
||||||
|
*/
|
||||||
|
public class GuacamoleLogbackServiceProvider implements SLF4JServiceProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The highest version of SLF4J that this service provider can support.
|
||||||
|
* This is intentionally not actually a released version, but a reasonable
|
||||||
|
* upper bound. This value is identical to the value used by Logback.
|
||||||
|
*/
|
||||||
|
private static final String SLF4J_API_MAX_VERSION = "2.0.99";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance of the LoggerFactory maintained by this service
|
||||||
|
* provider. The custom implementation provided here uses the call stack
|
||||||
|
* and our various custom class loaders to determine the context of the
|
||||||
|
* loggers that will be created.
|
||||||
|
*/
|
||||||
|
private ContextAwareLoggerFactory loggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance of the MDCAdapter implementation that should be
|
||||||
|
* used by Logback and SLF4J.
|
||||||
|
*/
|
||||||
|
private final MDCAdapter mdcAdapter = new LogbackMDCAdapter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton MarkerFactory instance maintained by this service
|
||||||
|
* provider.
|
||||||
|
*/
|
||||||
|
private final IMarkerFactory markerFactory = new BasicMarkerFactory();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
loggerFactory = new ContextAwareLoggerFactory(mdcAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MDCAdapter getMDCAdapter() {
|
||||||
|
return mdcAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContextAwareLoggerFactory getLoggerFactory() {
|
||||||
|
return loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMarkerFactory getMarkerFactory() {
|
||||||
|
return markerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRequestedApiVersion() {
|
||||||
|
return SLF4J_API_MAX_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -56,14 +56,14 @@ public enum LogLevel {
|
|||||||
* messages, while verbose, will not affect performance.
|
* messages, while verbose, will not affect performance.
|
||||||
*/
|
*/
|
||||||
@PropertyValue("debug")
|
@PropertyValue("debug")
|
||||||
DEBUG("debug"),
|
DEBUG("debug", "${guac_verbose_pattern}"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informational messages that may be useful for debugging, but which are
|
* Informational messages that may be useful for debugging, but which are
|
||||||
* so low-level that they may affect performance.
|
* so low-level that they may affect performance.
|
||||||
*/
|
*/
|
||||||
@PropertyValue("trace")
|
@PropertyValue("trace")
|
||||||
TRACE("trace");
|
TRACE("trace", "${guac_verbose_pattern}");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format string whose sole format argument is a String containing the
|
* Format string whose sole format argument is a String containing the
|
||||||
@@ -77,7 +77,7 @@ public enum LogLevel {
|
|||||||
+ " <!-- Default appender -->\n"
|
+ " <!-- Default appender -->\n"
|
||||||
+ " <appender name=\"GUAC-DEFAULT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n"
|
+ " <appender name=\"GUAC-DEFAULT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n"
|
||||||
+ " <encoder>\n"
|
+ " <encoder>\n"
|
||||||
+ " <pattern>%%d{HH:mm:ss.SSS} [%%thread] %%-5level %%logger{36} - %%msg%%n</pattern>\n"
|
+ " <pattern>%s</pattern>\n"
|
||||||
+ " </encoder>\n"
|
+ " </encoder>\n"
|
||||||
+ " </appender>\n"
|
+ " </appender>\n"
|
||||||
+ "\n"
|
+ "\n"
|
||||||
@@ -102,9 +102,10 @@ public enum LogLevel {
|
|||||||
private final byte[] logbackConfig;
|
private final byte[] logbackConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new LogLevel with the given names. The pair of names provided
|
* Creates a new LogLevel with the given names and pattern. The pair of
|
||||||
* correspond to the name used within Guacamole's configuration and the
|
* names provided correspond to the name used within Guacamole's
|
||||||
* name used within Logback's configuration.
|
* configuration and the name used within Logback's configuration, while
|
||||||
|
* the pattern determines how Logback will convert log messages to text.
|
||||||
*
|
*
|
||||||
* @param canonicalName
|
* @param canonicalName
|
||||||
* The name that should be used for this log level when configuring
|
* The name that should be used for this log level when configuring
|
||||||
@@ -113,10 +114,14 @@ public enum LogLevel {
|
|||||||
* @param logbackLogLevel
|
* @param logbackLogLevel
|
||||||
* The name that would be provided to Logback to log at this level if
|
* The name that would be provided to Logback to log at this level if
|
||||||
* manually configuring Logback using "logback.xml".
|
* manually configuring Logback using "logback.xml".
|
||||||
|
*
|
||||||
|
* @param pattern
|
||||||
|
* The Logback encoder pattern that should be use for messages logged
|
||||||
|
* at this level.
|
||||||
*/
|
*/
|
||||||
private LogLevel(String canonicalName, String logbackLogLevel) {
|
private LogLevel(String canonicalName, String logbackLogLevel, String pattern) {
|
||||||
this.canonicalName = canonicalName;
|
this.canonicalName = canonicalName;
|
||||||
this.logbackConfig = String.format(LOGBACK_XML_TEMPLATE, logbackLogLevel).getBytes(StandardCharsets.UTF_8);
|
this.logbackConfig = String.format(LOGBACK_XML_TEMPLATE, pattern, logbackLogLevel).getBytes(StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,7 +136,27 @@ public enum LogLevel {
|
|||||||
* "logback.xml" configuration file.
|
* "logback.xml" configuration file.
|
||||||
*/
|
*/
|
||||||
private LogLevel(String logLevel) {
|
private LogLevel(String logLevel) {
|
||||||
this(logLevel, logLevel);
|
this(logLevel, logLevel, "${guac_pattern}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new LogLevel with the given name and pattern. The provided name
|
||||||
|
* corresponds to both the name used within Guacamole's configuration and
|
||||||
|
* the name used within Logback's configuration, while the pattern
|
||||||
|
* determines how Logback will convert log messages to text.
|
||||||
|
*
|
||||||
|
* @param logLevel
|
||||||
|
* The name that should be used for this log level when configuring
|
||||||
|
* Guacamole to log at this level using the "log-level" property AND
|
||||||
|
* when manually configuring Logback to log at this level using a
|
||||||
|
* "logback.xml" configuration file.
|
||||||
|
*
|
||||||
|
* @param pattern
|
||||||
|
* The Logback encoder pattern that should be use for messages logged
|
||||||
|
* at this level.
|
||||||
|
*/
|
||||||
|
private LogLevel(String logLevel, String pattern) {
|
||||||
|
this(logLevel, logLevel, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,10 +19,7 @@
|
|||||||
|
|
||||||
package org.apache.guacamole.log;
|
package org.apache.guacamole.log;
|
||||||
|
|
||||||
import ch.qos.logback.classic.LoggerContext;
|
|
||||||
import ch.qos.logback.classic.joran.JoranConfigurator;
|
|
||||||
import ch.qos.logback.core.joran.spi.JoranException;
|
import ch.qos.logback.core.joran.spi.JoranException;
|
||||||
import ch.qos.logback.core.util.StatusPrinter;
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@@ -32,6 +29,7 @@ import java.io.InputStream;
|
|||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.environment.Environment;
|
import org.apache.guacamole.environment.Environment;
|
||||||
import org.apache.guacamole.properties.EnumGuacamoleProperty;
|
import org.apache.guacamole.properties.EnumGuacamoleProperty;
|
||||||
|
import org.slf4j.ILoggerFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -97,7 +95,7 @@ public class LogModule extends AbstractModule {
|
|||||||
}
|
}
|
||||||
catch (FileNotFoundException e) {
|
catch (FileNotFoundException e) {
|
||||||
logger.info("Logback configuration could not be read "
|
logger.info("Logback configuration could not be read "
|
||||||
+ "from \"{}\": {}", logbackFile, e.getMessage());
|
+ "from \"{}\": {}", logbackFile, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +108,7 @@ public class LogModule extends AbstractModule {
|
|||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
level = LogLevel.INFO;
|
level = LogLevel.INFO;
|
||||||
logger.error("Falling back to \"{}\" log level: {}", level.getCanonicalName(), e.getMessage());
|
logger.error("Falling back to \"{}\" log level: {}", level.getCanonicalName(), e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return level.getLogbackConfiguration();
|
return level.getLogbackConfiguration();
|
||||||
@@ -122,25 +120,26 @@ public class LogModule extends AbstractModule {
|
|||||||
|
|
||||||
try (InputStream logbackConfiguration = getLogbackConfiguration()) {
|
try (InputStream logbackConfiguration = getLogbackConfiguration()) {
|
||||||
|
|
||||||
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
|
// Warn if the system is somehow preventing Guacamole from
|
||||||
context.reset();
|
// using its own logging
|
||||||
|
ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
|
||||||
|
if (!(loggerFactory instanceof ReconfigurableLoggerFactory)) {
|
||||||
|
logger.warn("Guacamole depends on its own SLF4J service "
|
||||||
|
+ "provider to configure logging, but your system has "
|
||||||
|
+ "forced a different service provider to take "
|
||||||
|
+ "priority. Guacamole's own logging configuration "
|
||||||
|
+ "may not have any effect, and logging context may "
|
||||||
|
+ "be missing.");
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize logback
|
((ReconfigurableLoggerFactory) loggerFactory).reconfigure(logbackConfiguration);
|
||||||
JoranConfigurator configurator = new JoranConfigurator();
|
|
||||||
configurator.setContext(context);
|
|
||||||
configurator.doConfigure(logbackConfiguration);
|
|
||||||
|
|
||||||
// Dump any errors that occur during logback init
|
|
||||||
StatusPrinter.printInCaseOfErrorsOrWarnings(context);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (JoranException e) {
|
catch (JoranException e) {
|
||||||
logger.error("Initialization of logback failed: {}", e.getMessage());
|
logger.error("Initialization of logback failed: {}", e.getMessage(), e);
|
||||||
logger.debug("Unable to load logback configuration.", e);
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.warn("Logback configuration file could not be cleanly closed: {}", e.getMessage());
|
logger.warn("Logback configuration file could not be cleanly closed: {}", e.getMessage(), e);
|
||||||
logger.debug("Failed to close logback configuration file.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.guacamole.log;
|
||||||
|
|
||||||
|
import ch.qos.logback.core.joran.spi.JoranException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import org.slf4j.ILoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link ILoggerFactory} that can be reconfigured as needed.
|
||||||
|
*/
|
||||||
|
public interface ReconfigurableLoggerFactory extends ILoggerFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconfigures this {@link ILoggerFactory} using the given InputStream for
|
||||||
|
* a Logback XML configuration file. It is the responsibility of the caller
|
||||||
|
* to close the provided InputStream.
|
||||||
|
*
|
||||||
|
* @param logbackConfiguration
|
||||||
|
* An InputStream that provides the desired Logback configuration in
|
||||||
|
* XML format.
|
||||||
|
*
|
||||||
|
* @throws JoranException
|
||||||
|
* If the configuration is invalid, or an error occurs that prevents
|
||||||
|
* the configuration from being read/applied.
|
||||||
|
*/
|
||||||
|
void reconfigure(InputStream logbackConfiguration) throws JoranException;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -75,7 +75,7 @@ public class RESTExceptionMapper implements ExceptionMapper<Throwable> {
|
|||||||
if (t instanceof GuacamoleUnauthorizedException) {
|
if (t instanceof GuacamoleUnauthorizedException) {
|
||||||
String token = authenticationService.getAuthenticationToken(request);
|
String token = authenticationService.getAuthenticationToken(request);
|
||||||
if (authenticationService.destroyGuacamoleSession(token))
|
if (authenticationService.destroyGuacamoleSession(token))
|
||||||
logger.debug("Implicitly invalidated session for token \"{}\"", token);
|
logger.debug("Implicitly invalidated session for token \"{}\"", token, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate GuacamoleException subclasses to HTTP error codes
|
// Translate GuacamoleException subclasses to HTTP error codes
|
||||||
@@ -84,11 +84,9 @@ public class RESTExceptionMapper implements ExceptionMapper<Throwable> {
|
|||||||
// Always log the human-readable details of GuacacamoleExceptions
|
// Always log the human-readable details of GuacacamoleExceptions
|
||||||
// for the benefit of the administrator
|
// for the benefit of the administrator
|
||||||
if (t instanceof GuacamoleClientException)
|
if (t instanceof GuacamoleClientException)
|
||||||
logger.debug("Client request rejected: {}", t.getMessage());
|
logger.debug("Client request rejected: {}", t.getMessage(), t);
|
||||||
else {
|
else
|
||||||
logger.error("Request could not be processed: {}", t.getMessage());
|
logger.error("Request could not be processed: {}", t.getMessage(), t);
|
||||||
logger.debug("Processing of request aborted by extension.", t);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response
|
return Response
|
||||||
.status(((GuacamoleException) t).getHttpStatusCode())
|
.status(((GuacamoleException) t).getHttpStatusCode())
|
||||||
@@ -100,13 +98,11 @@ public class RESTExceptionMapper implements ExceptionMapper<Throwable> {
|
|||||||
// Wrap unchecked exceptions
|
// Wrap unchecked exceptions
|
||||||
String message = t.getMessage();
|
String message = t.getMessage();
|
||||||
if (message != null)
|
if (message != null)
|
||||||
logger.error("Unexpected internal error: {}", message);
|
logger.error("Unexpected internal error: {}", message, t);
|
||||||
else
|
else
|
||||||
logger.error("An internal error occurred, but did not contain "
|
logger.error("An internal error occurred, but did not contain "
|
||||||
+ "an error message. Enable debug-level logging for "
|
+ "an error message. Enable debug-level logging for "
|
||||||
+ "details.");
|
+ "details.", t);
|
||||||
|
|
||||||
logger.debug("Unexpected error in REST endpoint.", t);
|
|
||||||
|
|
||||||
return Response
|
return Response
|
||||||
.status(Response.Status.INTERNAL_SERVER_ERROR)
|
.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
|||||||
@@ -433,12 +433,12 @@ public class AuthenticationService {
|
|||||||
// This line SHOULD be unreachable unless a bug causes
|
// This line SHOULD be unreachable unless a bug causes
|
||||||
// rethrowCause() to not actually rethrow the underlying failure
|
// rethrowCause() to not actually rethrow the underlying failure
|
||||||
Throwable cause = e.getCause();
|
Throwable cause = e.getCause();
|
||||||
if (cause != null) {
|
if (cause != null)
|
||||||
logger.warn("An underlying internal error was not correctly rethrown by rethrowCause(): {}", cause.getMessage());
|
logger.warn("An underlying internal error was not correctly "
|
||||||
logger.debug("Internal error not rethrown by rethrowCause().", cause);
|
+ "rethrown by rethrowCause(): {}", cause.getMessage(), cause);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
logger.warn("An underlying internal error was not correctly rethrown by rethrowCause().");
|
logger.warn("An underlying internal error was not correctly "
|
||||||
|
+ "rethrown by rethrowCause().");
|
||||||
|
|
||||||
throw e.getCauseAsGuacamoleException();
|
throw e.getCauseAsGuacamoleException();
|
||||||
|
|
||||||
|
|||||||
@@ -81,8 +81,7 @@ public class HashTokenSessionMap implements TokenSessionMap {
|
|||||||
sessionTimeoutValue = environment.getProperty(API_SESSION_TIMEOUT, 60);
|
sessionTimeoutValue = environment.getProperty(API_SESSION_TIMEOUT, 60);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Unable to read guacamole.properties: {}", e.getMessage());
|
logger.error("Unable to read guacamole.properties: {}", e.getMessage(), e);
|
||||||
logger.debug("Error while reading session timeout value.", e);
|
|
||||||
sessionTimeoutValue = 60;
|
sessionTimeoutValue = 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,8 +69,7 @@ public class InterceptedStreamMap<T extends Closeable> {
|
|||||||
stream.close();
|
stream.close();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.warn("Unable to close intercepted stream: {}", e.getMessage());
|
logger.warn("Unable to close intercepted stream: {}", e.getMessage(), e);
|
||||||
logger.debug("I/O error prevented closure of intercepted stream.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify waiting threads that the stream has ended
|
// Notify waiting threads that the stream has ended
|
||||||
|
|||||||
+1
-2
@@ -130,8 +130,7 @@ public class OutputStreamInterceptingFilter
|
|||||||
blob = BaseEncoding.base64().decode(data);
|
blob = BaseEncoding.base64().decode(data);
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
logger.warn("Received base64 data for intercepted stream was invalid.");
|
logger.warn("Received base64 data for intercepted stream was invalid.", e);
|
||||||
logger.debug("Decoding base64 data for intercepted stream failed.", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-6
@@ -240,8 +240,7 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
|
|||||||
tunnel = doConnect(tunnelRequest);
|
tunnel = doConnect(tunnelRequest);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
|
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage(), e);
|
||||||
logger.debug("Error connecting WebSocket tunnel.", e);
|
|
||||||
closeConnection(connection, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(connection, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
return;
|
return;
|
||||||
@@ -295,8 +294,7 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
|
|||||||
// to pass within the WebSocket connection, logging
|
// to pass within the WebSocket connection, logging
|
||||||
// each error appropriately.
|
// each error appropriately.
|
||||||
catch (GuacamoleClientException e) {
|
catch (GuacamoleClientException e) {
|
||||||
logger.info("WebSocket connection terminated: {}", e.getMessage());
|
logger.info("WebSocket connection terminated: {}", e.getMessage(), e);
|
||||||
logger.debug("WebSocket connection terminated due to client error.", e);
|
|
||||||
closeConnection(connection, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(connection, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
}
|
}
|
||||||
@@ -305,8 +303,7 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
|
|||||||
closeConnection(connection, GuacamoleStatus.SUCCESS);
|
closeConnection(connection, GuacamoleStatus.SUCCESS);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
|
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage(), e);
|
||||||
logger.debug("Internal error during connection to guacd.", e);
|
|
||||||
closeConnection(connection, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(connection, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -196,8 +196,7 @@ public abstract class GuacamoleWebSocketTunnelListener implements WebSocketListe
|
|||||||
|
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
|
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage(), e);
|
||||||
logger.debug("Error connecting WebSocket tunnel.", e);
|
|
||||||
closeConnection(session, e.getStatus().getGuacamoleStatusCode(), e.getWebSocketCode());
|
closeConnection(session, e.getStatus().getGuacamoleStatusCode(), e.getWebSocketCode());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -245,8 +244,7 @@ public abstract class GuacamoleWebSocketTunnelListener implements WebSocketListe
|
|||||||
// to pass within the WebSocket connection, logging
|
// to pass within the WebSocket connection, logging
|
||||||
// each error appropriately.
|
// each error appropriately.
|
||||||
catch (GuacamoleClientException e) {
|
catch (GuacamoleClientException e) {
|
||||||
logger.info("WebSocket connection terminated: {}", e.getMessage());
|
logger.info("WebSocket connection terminated: {}", e.getMessage(), e);
|
||||||
logger.debug("WebSocket connection terminated due to client error.", e);
|
|
||||||
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
}
|
}
|
||||||
@@ -255,8 +253,7 @@ public abstract class GuacamoleWebSocketTunnelListener implements WebSocketListe
|
|||||||
closeConnection(session, GuacamoleStatus.SUCCESS);
|
closeConnection(session, GuacamoleStatus.SUCCESS);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
|
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage(), e);
|
||||||
logger.debug("Internal error during connection to guacd.", e);
|
|
||||||
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -268,8 +268,7 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
|
|||||||
tunnel = doConnect(tunnelRequest);
|
tunnel = doConnect(tunnelRequest);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
|
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage(), e);
|
||||||
logger.debug("Error connecting WebSocket tunnel.", e);
|
|
||||||
closeConnection(outbound, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(outbound, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
return;
|
return;
|
||||||
@@ -323,8 +322,7 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
|
|||||||
// to pass within the WebSocket connection, logging
|
// to pass within the WebSocket connection, logging
|
||||||
// each error appropriately.
|
// each error appropriately.
|
||||||
catch (GuacamoleClientException e) {
|
catch (GuacamoleClientException e) {
|
||||||
logger.info("WebSocket connection terminated: {}", e.getMessage());
|
logger.info("WebSocket connection terminated: {}", e.getMessage(), e);
|
||||||
logger.debug("WebSocket connection terminated due to client error.", e);
|
|
||||||
closeConnection(outbound, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(outbound, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
}
|
}
|
||||||
@@ -333,8 +331,7 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
|
|||||||
closeConnection(outbound, GuacamoleStatus.SUCCESS);
|
closeConnection(outbound, GuacamoleStatus.SUCCESS);
|
||||||
}
|
}
|
||||||
catch (GuacamoleException e) {
|
catch (GuacamoleException e) {
|
||||||
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
|
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage(), e);
|
||||||
logger.debug("Internal error during connection to guacd.", e);
|
|
||||||
closeConnection(outbound, e.getStatus().getGuacamoleStatusCode(),
|
closeConnection(outbound, e.getStatus().getGuacamoleStatusCode(),
|
||||||
e.getWebSocketCode());
|
e.getWebSocketCode());
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
org.apache.guacamole.GuacamoleServletContainerInitializer
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.apache.guacamole.log.GuacamoleLogbackServiceProvider
|
||||||
@@ -28,6 +28,14 @@
|
|||||||
<welcome-file>index.html</welcome-file>
|
<welcome-file>index.html</welcome-file>
|
||||||
</welcome-file-list>
|
</welcome-file-list>
|
||||||
|
|
||||||
|
<!-- Ensure that our own ServletContainerInitializer is used to initialize
|
||||||
|
logging, and that Logback does not potentially initialize SLF4J first (which
|
||||||
|
would prevent our custom logging from taking effect) -->
|
||||||
|
<context-param>
|
||||||
|
<param-name>logbackDisableServletContainerInitializer</param-name>
|
||||||
|
<param-value>true</param-value>
|
||||||
|
</context-param>
|
||||||
|
|
||||||
<!-- Request that the browser always revalidate its cached copy of index.html -->
|
<!-- Request that the browser always revalidate its cached copy of index.html -->
|
||||||
<filter>
|
<filter>
|
||||||
<filter-name>cacheRevalidationFilter</filter-name>
|
<filter-name>cacheRevalidationFilter</filter-name>
|
||||||
|
|||||||
Reference in new issue
Block a user