GUACAMOLE-5: Merge front end changes for multiple share keys.

This commit is contained in:
James Muehlner
2016-07-31 17:28:17 -07:00
23 changed files with 869 additions and 211 deletions

View File

@@ -81,4 +81,36 @@ public interface AuthenticationProviderService {
public UserContext getUserContext(AuthenticationProvider authenticationProvider, public UserContext getUserContext(AuthenticationProvider authenticationProvider,
AuthenticatedUser authenticatedUser) throws GuacamoleException; AuthenticatedUser authenticatedUser) throws GuacamoleException;
/**
* Returns an updated UserContext instance for the given
* already-authenticated user. If no changes need be made to the
* UserContext, the original UserContext will be returned.
*
* @param authenticationProvider
* The AuthenticationProvider on behalf of which the UserContext is
* being updated.
*
* @param context
* The UserContext to update.
*
* @param authenticatedUser
* The AuthenticatedUser associated with the UserContext being updated.
*
* @param credentials
* The credentials most recently submitted by the user. These
* credentials are not guaranteed to be the same as the credentials
* already associated with the AuthenticatedUser.
*
* @return
* A new UserContext instance for the user identified by the given
* credentials.
*
* @throws GuacamoleException
* If an error occurs during authentication, or if the given
* credentials are invalid or expired.
*/
public UserContext updateUserContext(AuthenticationProvider authenticationProvider,
UserContext context, AuthenticatedUser authenticatedUser,
Credentials credentials) throws GuacamoleException;
} }

View File

@@ -97,10 +97,8 @@ public abstract class InjectedAuthenticationProvider implements AuthenticationPr
public UserContext updateUserContext(UserContext context, public UserContext updateUserContext(UserContext context,
AuthenticatedUser authenticatedUser, Credentials credentials) AuthenticatedUser authenticatedUser, Credentials credentials)
throws GuacamoleException { throws GuacamoleException {
return authProviderService.updateUserContext(this, context,
// No need to update the context authenticatedUser, credentials);
return context;
} }
} }

View File

@@ -28,6 +28,7 @@ import org.apache.guacamole.auth.jdbc.user.UserService;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
@@ -82,4 +83,14 @@ public class JDBCAuthenticationProviderService implements AuthenticationProvider
} }
@Override
public UserContext updateUserContext(AuthenticationProvider authenticationProvider,
UserContext context, AuthenticatedUser authenticatedUser,
Credentials credentials) throws GuacamoleException {
// No need to update the context
return context;
}
} }

View File

@@ -25,6 +25,8 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService;
import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord;
@@ -128,11 +130,34 @@ public class ConnectionSharingService {
} }
/** /**
* Returns a SharedConnectionUser (an implementation of AuthenticatedUser) * Returns the share key contained within the given credentials. If there is
* if the given credentials contain a valid share key. The returned user * no such share key, null is returned.
* will be associated with the single shared connection to which they have *
* been granted temporary access. If the share key is invalid, or no share * @param credentials
* key is contained within the given credentials, null is returned. * The credentials from which the share key should be retrieved.
*
* @return
* The share key contained within the given credentials, or null if
* the credentials do not contain a share key.
*/
public String getShareKey(Credentials credentials) {
// Pull associated HTTP request
HttpServletRequest request = credentials.getRequest();
if (request == null)
return null;
// Retrieve the share key from the request
return request.getParameter(SHARE_KEY_NAME);
}
/**
* Returns a SharedAuthenticatedUser if the given credentials contain a
* valid share key. The returned user will be associated with the single
* shared connection to which they have been granted temporary access. If
* the share key is invalid, or no share key is contained within the given
* credentials, null is returned.
* *
* @param authProvider * @param authProvider
* The AuthenticationProvider on behalf of which the user is being * The AuthenticationProvider on behalf of which the user is being
@@ -142,31 +167,20 @@ public class ConnectionSharingService {
* The credentials which are expected to contain the share key. * The credentials which are expected to contain the share key.
* *
* @return * @return
* A SharedConnectionUser with access to a single shared connection, if * A SharedAuthenticatedUser with access to a single shared connection,
* the share key within the given credentials is valid, or null if the * if the share key within the given credentials is valid, or null if
* share key is invalid or absent. * the share key is invalid or absent.
*/ */
public SharedConnectionUser retrieveSharedConnectionUser( public SharedAuthenticatedUser retrieveSharedConnectionUser(
AuthenticationProvider authProvider, Credentials credentials) { AuthenticationProvider authProvider, Credentials credentials) {
// Pull associated HTTP request // Validate the share key
HttpServletRequest request = credentials.getRequest(); String shareKey = getShareKey(credentials);
if (request == null) if (shareKey == null || connectionMap.get(shareKey) == null)
return null; return null;
// Retrieve the share key from the request // Return temporary in-memory user
String shareKey = request.getParameter(ConnectionSharingService.SHARE_KEY_NAME); return new SharedAuthenticatedUser(authProvider, credentials, shareKey);
if (shareKey == null)
return null;
// Pull the connection definition describing the connection these
// credentials provide access to (if any)
SharedConnectionDefinition definition = connectionMap.get(shareKey);
if (definition == null)
return null;
// Return temporary in-memory user with access only to the shared connection
return new SharedConnectionUser(authProvider, definition, credentials);
} }

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.jdbc.sharing;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
/** /**
* A HashMap-based implementation of the SharedConnectionMap. * A HashMap-based implementation of the SharedConnectionMap.

View File

@@ -23,16 +23,19 @@ import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.AuthenticationProviderService; import org.apache.guacamole.auth.jdbc.AuthenticationProviderService;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedUserContext;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
/** /**
* Service which authenticates users based on share keys and provides for the * Service which authenticates users based on share keys and provides for the
* creation of corresponding. The created UserContext objects are restricted to * creation of corresponding UserContexts. The created UserContext objects are
* the connections associated with those share keys via a common * restricted to the connections associated with those share keys via a common
* ConnectionSharingService. * ConnectionSharingService.
* *
* @author Michael Jumper * @author Michael Jumper
@@ -40,10 +43,10 @@ import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsExce
public class SharedAuthenticationProviderService implements AuthenticationProviderService { public class SharedAuthenticationProviderService implements AuthenticationProviderService {
/** /**
* Provider for retrieving SharedConnectionUserContext instances. * Provider for retrieving SharedUserContext instances.
*/ */
@Inject @Inject
private Provider<SharedConnectionUserContext> sharedUserContextProvider; private Provider<SharedUserContext> sharedUserContextProvider;
/** /**
* Service for sharing active connections. * Service for sharing active connections.
@@ -66,19 +69,45 @@ public class SharedAuthenticationProviderService implements AuthenticationProvid
} }
@Override @Override
public org.apache.guacamole.net.auth.UserContext getUserContext( public SharedUserContext getUserContext(
AuthenticationProvider authenticationProvider, AuthenticationProvider authenticationProvider,
AuthenticatedUser authenticatedUser) throws GuacamoleException { AuthenticatedUser authenticatedUser) throws GuacamoleException {
// Produce sharing-specific user context if this is the user of a shared connection // Obtain a reference to a correct AuthenticatedUser which can be used
if (authenticatedUser instanceof SharedConnectionUser) { // for accessing shared connections
SharedConnectionUserContext context = sharedUserContextProvider.get(); SharedAuthenticatedUser sharedAuthenticatedUser;
context.init((SharedConnectionUser) authenticatedUser); if (authenticatedUser instanceof SharedAuthenticatedUser)
return context; sharedAuthenticatedUser = (SharedAuthenticatedUser) authenticatedUser;
} else
sharedAuthenticatedUser = new SharedAuthenticatedUser(authenticatedUser);
// No shared connections otherwise // Produce empty user context for known-authenticated user
return null; SharedUserContext context = sharedUserContextProvider.get();
context.init(authenticationProvider, sharedAuthenticatedUser);
// Add the shared connection associated with the originally-provided
// share key (if any)
String shareKey = sharedAuthenticatedUser.getShareKey();
if (shareKey != null)
context.registerShareKey(shareKey);
return context;
}
@Override
public UserContext updateUserContext(AuthenticationProvider authenticationProvider,
UserContext context, AuthenticatedUser authenticatedUser,
Credentials credentials) throws GuacamoleException {
// Retrieve the share key from the request
String shareKey = sharingService.getShareKey(credentials);
// Update the user context with the share key, if given
if (shareKey != null)
((SharedUserContext) context).registerShareKey(shareKey);
return context;
} }

View File

@@ -19,6 +19,8 @@
package org.apache.guacamole.auth.jdbc.sharing; package org.apache.guacamole.auth.jdbc.sharing;
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
/** /**
* Represents a mapping between share keys and the Guacamole connection being * Represents a mapping between share keys and the Guacamole connection being
* shared. * shared.

View File

@@ -1,93 +0,0 @@
/*
* 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.auth.jdbc.sharing;
import java.util.UUID;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
/**
* A temporary user who has authenticated using a share key and thus has
* restricted access to a single shared connection.
*
* @author Michael Jumper
*/
public class SharedConnectionUser extends RemoteAuthenticatedUser {
/**
* The single shared connection to which this user has access.
*/
private final SharedConnectionDefinition definition;
/**
* An arbitrary identifier guaranteed to be unique across users. Note that
* because Guacamole users the AuthenticatedUser's identifier as the means
* of determining overall user identity and aggregating data across
* multiple extensions, this identifier MUST NOT match the identifier of
* any possibly existing user (or else the user may unexpectedly gain
* access to another identically-named user's data).
*/
private final String identifier = UUID.randomUUID().toString();
/**
* Creates a new SharedConnectionUser with access solely to connection
* described by the given SharedConnectionDefinition.
*
* @param authenticationProvider
* The AuthenticationProvider that has authenticated the given user.
*
* @param definition
* The SharedConnectionDefinition describing the connection that this
* user should have access to, along with any associated restrictions.
*
* @param credentials
* The credentials given by the user when they authenticated.
*/
public SharedConnectionUser(AuthenticationProvider authenticationProvider,
SharedConnectionDefinition definition, Credentials credentials) {
super(authenticationProvider, credentials);
this.definition = definition;
}
@Override
public String getIdentifier() {
return identifier;
}
@Override
public void setIdentifier(String identifier) {
throw new UnsupportedOperationException("Shared connection users are immutable");
}
/**
* Returns the SharedConnectionDefinition which describes the connection
* that this user should have access to, along with any associated
* restrictions.
*
* @return
* The SharedConnectionDefinition describing the connection that this
* user should have access to, along with any associated restrictions.
*/
public SharedConnectionDefinition getSharedConnectionDefinition() {
return definition;
}
}

View File

@@ -17,17 +17,17 @@
* under the License. * under the License.
*/ */
package org.apache.guacamole.auth.jdbc.sharing; package org.apache.guacamole.auth.jdbc.sharing.connection;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; import org.apache.guacamole.auth.jdbc.sharing.connectiongroup.SharedRootConnectionGroup;
import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.net.auth.ConnectionRecord;
@@ -42,24 +42,23 @@ import org.apache.guacamole.protocol.GuacamoleConfiguration;
*/ */
public class SharedConnection implements Connection { public class SharedConnection implements Connection {
/**
* The name of the attribute which contains the username of the user that
* shared this connection.
*/
public static final String CONNECTION_OWNER = "jdbc-shared-by";
/** /**
* Service for establishing tunnels to Guacamole connections. * Service for establishing tunnels to Guacamole connections.
*/ */
@Inject @Inject
private GuacamoleTunnelService tunnelService; private GuacamoleTunnelService tunnelService;
/**
* Randomly-generated unique identifier, guaranteeing this shared connection
* does not duplicate the identifying information of the underlying
* connection being shared.
*/
private final String identifier = UUID.randomUUID().toString();
/** /**
* The user that successfully authenticated to obtain access to this * The user that successfully authenticated to obtain access to this
* SharedConnection. * SharedConnection.
*/ */
private SharedConnectionUser user; private RemoteAuthenticatedUser user;
/** /**
* The SharedConnectionDefinition dictating the connection being shared and * The SharedConnectionDefinition dictating the connection being shared and
@@ -79,14 +78,14 @@ public class SharedConnection implements Connection {
* The SharedConnectionDefinition dictating the connection being shared * The SharedConnectionDefinition dictating the connection being shared
* and any associated restrictions. * and any associated restrictions.
*/ */
public void init(SharedConnectionUser user, SharedConnectionDefinition definition) { public void init(RemoteAuthenticatedUser user, SharedConnectionDefinition definition) {
this.user = user; this.user = user;
this.definition = definition; this.definition = definition;
} }
@Override @Override
public String getIdentifier() { public String getIdentifier() {
return identifier; return definition.getShareKey();
} }
@Override @Override
@@ -96,7 +95,7 @@ public class SharedConnection implements Connection {
@Override @Override
public String getName() { public String getName() {
return definition.getSharingProfile().getName(); return definition.getActiveConnection().getConnection().getName();
} }
@Override @Override
@@ -106,7 +105,7 @@ public class SharedConnection implements Connection {
@Override @Override
public String getParentIdentifier() { public String getParentIdentifier() {
return RootConnectionGroup.IDENTIFIER; return SharedRootConnectionGroup.IDENTIFIER;
} }
@Override @Override
@@ -140,12 +139,13 @@ public class SharedConnection implements Connection {
@Override @Override
public Map<String, String> getAttributes() { public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap(); String sharedBy = definition.getActiveConnection().getUser().getIdentifier();
return Collections.<String, String>singletonMap(CONNECTION_OWNER, sharedBy);
} }
@Override @Override
public void setAttributes(Map<String, String> attributes) { public void setAttributes(Map<String, String> attributes) {
// Do nothing - no attributes supported // Do nothing - changing attributes not supported
} }
@Override @Override

View File

@@ -17,9 +17,10 @@
* under the License. * under the License.
*/ */
package org.apache.guacamole.auth.jdbc.sharing; package org.apache.guacamole.auth.jdbc.sharing.connection;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.sharing.SharedObjectManager;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;

View File

@@ -0,0 +1,158 @@
/*
* 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.auth.jdbc.sharing.connection;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionMap;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.Directory;
/**
* A Directory implementation which exposes an explicitly-registered set of
* share keys as connections. Only explicitly-registered share keys are
* accessible a SharedConnectionDirectory.
*
* @author Michael Jumper
*/
public class SharedConnectionDirectory implements Directory<Connection> {
/**
* Map of all currently-shared connections.
*/
@Inject
private SharedConnectionMap connectionMap;
/**
* Provider for retrieving SharedConnection instances.
*/
@Inject
private Provider<SharedConnection> connectionProvider;
/**
* The user associated with the UserContext that contains this directory.
*/
private RemoteAuthenticatedUser currentUser;
/**
* The set of share keys that have been explicitly registered. In general,
* only valid share keys will be present here, but invalid keys are only
* removed after an attempt to retrieve those keys has been made.
*/
private final Set<String> shareKeys =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
/**
* Creates a new SharedConnectionDirectory which exposes share keys as
* connections. Only explicitly-registered and valid share keys will be
* accessible.
*
* @param currentUser
* The user associated with the UserContext that will contain this
* directory.
*/
public void init(RemoteAuthenticatedUser currentUser) {
this.currentUser = currentUser;
}
/**
* Registers a new share key such that the connection associated with that
* share key is accessible through this directory. The share key will be
* automatically de-registered when it is no longer valid.
*
* @param shareKey
* The share key to register.
*/
public void registerShareKey(String shareKey) {
shareKeys.add(shareKey);
}
@Override
public Connection get(String identifier) throws GuacamoleException {
// Allow access only to registered share keys
if (!shareKeys.contains(identifier))
return null;
// Retrieve the connection definition associated with that share key,
// cleaning up the internally-stored share key if it's no longer valid
SharedConnectionDefinition connectionDefinition = connectionMap.get(identifier);
if (connectionDefinition == null) {
shareKeys.remove(identifier);
return null;
}
// Return a Connection which wraps that connection definition
SharedConnection connection = connectionProvider.get();
connection.init(currentUser, connectionDefinition);
return connection;
}
@Override
public Collection<Connection> getAll(Collection<String> identifiers)
throws GuacamoleException {
// Create collection with enough backing space to contain one
// connection per identifier given
Collection<Connection> matchingConnections =
new ArrayList<Connection>(identifiers.size());
// Add all connnections which exist according to get()
for (String identifier : identifiers) {
Connection connection = get(identifier);
if (connection != null)
matchingConnections.add(connection);
}
return Collections.unmodifiableCollection(matchingConnections);
}
@Override
public Set<String> getIdentifiers() throws GuacamoleException {
return Collections.unmodifiableSet(shareKeys);
}
@Override
public void add(Connection object) throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied.");
}
@Override
public void update(Connection object) throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied.");
}
@Override
public void remove(String identifier) throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied.");
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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.auth.jdbc.sharing.connectiongroup;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedUserContext;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.protocol.GuacamoleClientInformation;
/**
* A ConnectionGroup implementation which contains all connections accessible
* via a given SharedUserContext. The identifier of a SharedRootConnectionGroup
* is statically defined, and all Connections which are intended to be contained
* within an instance of SharedRootConnectionGroup MUST return that identifier
* via getParentIdentifier().
*
* @author Michael Jumper
*/
public class SharedRootConnectionGroup implements ConnectionGroup {
/**
* The identifier of the root connection group. All Connections which are
* intended to be contained within an instance of SharedRootConnectionGroup
* MUST return this identifier via getParentIdentifier().
*/
public static final String IDENTIFIER = "ROOT";
/**
* The SharedUserContext through which this connection group is accessible.
*/
private final SharedUserContext userContext;
/**
* Creates a new SharedRootConnectionGroup which contains all connections
* accessible via the given SharedUserContext. The SharedRootConnectionGroup
* is backed by the SharedUserContext, and any changes to the connections
* within the SharedUserContext are immediately reflected in the
* SharedRootConnectionGroup.
*
* @param userContext
* The SharedUserContext which should back the new
* SharedRootConnectionGroup.
*/
public SharedRootConnectionGroup(SharedUserContext userContext) {
this.userContext = userContext;
}
@Override
public String getIdentifier() {
return IDENTIFIER;
}
@Override
public void setIdentifier(String identifier) {
throw new UnsupportedOperationException("The root group is immutable.");
}
@Override
public String getName() {
return IDENTIFIER;
}
@Override
public void setName(String name) {
throw new UnsupportedOperationException("The root group is immutable.");
}
@Override
public String getParentIdentifier() {
return IDENTIFIER;
}
@Override
public void setParentIdentifier(String parentIdentifier) {
throw new UnsupportedOperationException("The root group is immutable.");
}
@Override
public GuacamoleTunnel connect(GuacamoleClientInformation info)
throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied.");
}
@Override
public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Do nothing - no attributes supported
}
@Override
public int getActiveConnections() {
return 0;
}
@Override
public void setType(Type type) {
throw new UnsupportedOperationException("The root group is immutable.");
}
@Override
public Type getType() {
return Type.ORGANIZATIONAL;
}
@Override
public Set<String> getConnectionIdentifiers() throws GuacamoleException {
Directory<Connection> connectionDirectory = userContext.getConnectionDirectory();
return connectionDirectory.getIdentifiers();
}
@Override
public Set<String> getConnectionGroupIdentifiers() throws GuacamoleException {
return Collections.<String>emptySet();
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.auth.jdbc.sharing.permission;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet;
/**
* An immutable ObjectPermissionSet which defines only READ permissions for a
* fixed set of identifiers.
*
* @author Michael Jumper
*/
public class SharedObjectPermissionSet extends SimpleObjectPermissionSet {
/**
* Returns a new Set of ObjectPermissions defining READ access for each of
* the given identifiers.
*
* @param identifiers
* The identifiers of the objects for which READ permission should be
* granted.
*
* @return
* A new Set of ObjectPermissions granting READ access for each of the
* given identifiers.
*/
private static Set<ObjectPermission> getPermissions(Collection<String> identifiers) {
// Include one READ permission for each of the given identifiers
Set<ObjectPermission> permissions = new HashSet<ObjectPermission>();
for (String identifier : identifiers)
permissions.add(new ObjectPermission(ObjectPermission.Type.READ, identifier));
return permissions;
}
/**
* Creates a new SharedObjectPermissionSet which grants read-only access to
* the objects having the given identifiers. No other permissions are
* granted.
*
* @param identifiers
* The identifiers of the objects for which READ access should be
* granted.
*/
public SharedObjectPermissionSet(Collection<String> identifiers) {
super(getPermissions(identifiers));
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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.auth.jdbc.sharing.user;
import java.util.UUID;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
/**
* Associates a user with the credentials they used to authenticate, including
* any provided share key.
*
* @author Michael Jumper
*/
public class SharedAuthenticatedUser extends RemoteAuthenticatedUser {
/**
* The username of this user.
*/
private final String identifier;
/**
* The share key which was provided by this user when they authenticated. If
* there is no such share key, this will be null.
*/
private final String shareKey;
/**
* Creates a new SharedAuthenticatedUser which copies the details of the
* given AuthenticatedUser, including that user's identifier (username).
* The new SharedAuthenticatedUser will not have any associated share key.
*
* @param authenticatedUser
* The AuthenticatedUser to copy.
*/
public SharedAuthenticatedUser(AuthenticatedUser authenticatedUser) {
super(authenticatedUser.getAuthenticationProvider(), authenticatedUser.getCredentials());
this.shareKey = null;
this.identifier = authenticatedUser.getIdentifier();
}
/**
* Creates a new SharedAuthenticatedUser associating the given user with
* their corresponding credentials and share key. The identifier (username)
* of the user will be randomly generated.
*
* @param authenticationProvider
* The AuthenticationProvider that has authenticated the given user.
*
* @param credentials
* The credentials given by the user when they authenticated.
*
* @param shareKey
* The share key which was provided by this user when they
* authenticated, or null if no share key was provided.
*/
public SharedAuthenticatedUser(AuthenticationProvider authenticationProvider,
Credentials credentials, String shareKey) {
super(authenticationProvider, credentials);
this.shareKey = shareKey;
this.identifier = UUID.randomUUID().toString();
}
/**
* Returns the share key which was provided by this user when they
* authenticated. If there is no such share key, null is returned.
*
* @return
* The share key which was provided by this user when they
* authenticated, or null if no share key was provided.
*/
public String getShareKey() {
return shareKey;
}
@Override
public String getIdentifier() {
return identifier;
}
@Override
public void setIdentifier(String identifier) {
throw new UnsupportedOperationException("Users authenticated via share keys are immutable.");
}
}

View File

@@ -0,0 +1,136 @@
/*
* 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.auth.jdbc.sharing.user;
import java.util.Collections;
import java.util.Map;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.sharing.permission.SharedObjectPermissionSet;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet;
import org.apache.guacamole.net.auth.simple.SimpleSystemPermissionSet;
/**
* An immutable implementation of User which defines READ permission for each of
* the objects accessible through the various directories of a given
* SharedUserContext.
*
* @author Michael Jumper
*/
public class SharedUser implements User {
/**
* The AuthenticatedUser that this SharedUser represents.
*/
private final AuthenticatedUser user;
/**
* The SharedUserContext which should be used to define which objects this
* SharedUser has READ permission for.
*/
private final SharedUserContext userContext;
/**
* Creates a new SharedUser whose identity is defined by the given
* AuthenticatedUser, and who has strictly READ access to all objects
* accessible via the various directories of the given SharedUserContext.
*
* @param user
* The AuthenticatedUser that the SharedUser should represent.
*
* @param userContext
* The SharedUserContext which should be used to define which objects
* the SharedUser has READ permission for.
*/
public SharedUser(AuthenticatedUser user, SharedUserContext userContext) {
this.user = user;
this.userContext = userContext;
}
@Override
public String getIdentifier() {
return user.getIdentifier();
}
@Override
public void setIdentifier(String identifier) {
throw new UnsupportedOperationException("Users authenticated via share keys are immutable.");
}
@Override
public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Do nothing - no attributes supported
}
@Override
public String getPassword() {
return null;
}
@Override
public void setPassword(String password) {
throw new UnsupportedOperationException("Users authenticated via share keys are immutable.");
}
@Override
public SystemPermissionSet getSystemPermissions() throws GuacamoleException {
return new SimpleSystemPermissionSet();
}
@Override
public ObjectPermissionSet getConnectionPermissions() throws GuacamoleException {
Directory<Connection> connectionDirectory = userContext.getConnectionDirectory();
return new SharedObjectPermissionSet(connectionDirectory.getIdentifiers());
}
@Override
public ObjectPermissionSet getConnectionGroupPermissions() throws GuacamoleException {
Directory<ConnectionGroup> connectionGroupDirectory = userContext.getConnectionGroupDirectory();
return new SharedObjectPermissionSet(connectionGroupDirectory.getIdentifiers());
}
@Override
public ObjectPermissionSet getUserPermissions() throws GuacamoleException {
Directory<User> userDirectory = userContext.getUserDirectory();
return new SharedObjectPermissionSet(userDirectory.getIdentifiers());
}
@Override
public ObjectPermissionSet getSharingProfilePermissions() throws GuacamoleException {
return new SimpleObjectPermissionSet();
}
@Override
public ObjectPermissionSet getActiveConnectionPermissions() throws GuacamoleException {
return new SimpleObjectPermissionSet();
}
}

View File

@@ -17,13 +17,15 @@
* under the License. * under the License.
*/ */
package org.apache.guacamole.auth.jdbc.sharing; package org.apache.guacamole.auth.jdbc.sharing.user;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDirectory;
import org.apache.guacamole.auth.jdbc.sharing.connectiongroup.SharedRootConnectionGroup;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.form.Form; import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.auth.ActiveConnection; import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
@@ -34,32 +36,23 @@ import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.SharingProfile; import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.auth.simple.SimpleConnectionDirectory;
import org.apache.guacamole.net.auth.simple.SimpleConnectionGroup;
import org.apache.guacamole.net.auth.simple.SimpleConnectionGroupDirectory; import org.apache.guacamole.net.auth.simple.SimpleConnectionGroupDirectory;
import org.apache.guacamole.net.auth.simple.SimpleConnectionRecordSet; import org.apache.guacamole.net.auth.simple.SimpleConnectionRecordSet;
import org.apache.guacamole.net.auth.simple.SimpleDirectory; import org.apache.guacamole.net.auth.simple.SimpleDirectory;
import org.apache.guacamole.net.auth.simple.SimpleUser;
import org.apache.guacamole.net.auth.simple.SimpleUserDirectory; import org.apache.guacamole.net.auth.simple.SimpleUserDirectory;
/** /**
* The user context of a SharedConnectionUser, providing access ONLY to the * The user context of a SharedUser, providing access ONLY to the user
* user themselves, the single SharedConnection associated with that user, and * themselves, the any SharedConnections associated with that user via share
* an internal root connection group containing only that single * keys, and an internal root connection group containing only those
* SharedConnection. * connections.
* *
* @author Michael Jumper * @author Michael Jumper
*/ */
public class SharedConnectionUserContext implements UserContext { public class SharedUserContext implements UserContext {
/** /**
* Provider for retrieving SharedConnection instances. * The AuthenticationProvider that created this SharedUserContext.
*/
@Inject
private Provider<SharedConnection> connectionProvider;
/**
* The AuthenticationProvider that created this SharedConnectionUserContext.
*/ */
private AuthenticationProvider authProvider; private AuthenticationProvider authProvider;
@@ -72,7 +65,8 @@ public class SharedConnectionUserContext implements UserContext {
* A directory of all connections visible to the user for whom this user * A directory of all connections visible to the user for whom this user
* context was created. * context was created.
*/ */
private Directory<Connection> connectionDirectory; @Inject
private SharedConnectionDirectory connectionDirectory;
/** /**
* A directory of all connection groups visible to the user for whom this * A directory of all connection groups visible to the user for whom this
@@ -94,56 +88,50 @@ public class SharedConnectionUserContext implements UserContext {
private ConnectionGroup rootGroup; private ConnectionGroup rootGroup;
/** /**
* Creates a new SharedConnectionUserContext which provides access ONLY to * Creates a new SharedUserContext which provides access ONLY to the given
* the given user, the single SharedConnection associated with that user, * user, the SharedConnections associated with the share keys used by that
* and an internal root connection group containing only that single * user, and an internal root connection group containing only those
* SharedConnection. * SharedConnections.
*
* @param authProvider
* The AuthenticationProvider that created this
* SharedUserContext;
* *
* @param user * @param user
* The SharedConnectionUser for whom this SharedConnectionUserContext * The RemoteAuthenticatedUser for whom this SharedUserContext is being
* is being created. * created.
*/ */
public void init(SharedConnectionUser user) { public void init(AuthenticationProvider authProvider, RemoteAuthenticatedUser user) {
// Get the definition of the shared connection
SharedConnectionDefinition definition =
user.getSharedConnectionDefinition();
// Create a single shared connection accessible by the user
SharedConnection connection = connectionProvider.get();
connection.init(user, definition);
// Build list of all accessible connection identifiers
Collection<String> connectionIdentifiers =
Collections.singletonList(connection.getIdentifier());
// Associate the originating authentication provider // Associate the originating authentication provider
this.authProvider = user.getAuthenticationProvider(); this.authProvider = authProvider;
// The connection directory should contain only the shared connection // Provide access to all connections shared with the given user
this.connectionDirectory = new SimpleConnectionDirectory( this.connectionDirectory.init(user);
Collections.<Connection>singletonList(connection));
// The user should have access only to the shared connection and himself
this.self = new SimpleUser(user.getIdentifier(),
Collections.<String>singletonList(user.getIdentifier()),
connectionIdentifiers,
Collections.<String>emptyList());
// The root group contains only the shared connection
String rootIdentifier = connection.getParentIdentifier();
this.rootGroup = new SimpleConnectionGroup(rootIdentifier, rootIdentifier,
connectionIdentifiers, Collections.<String>emptyList());
// The connection group directory contains only the root group // The connection group directory contains only the root group
this.rootGroup = new SharedRootConnectionGroup(this);
this.connectionGroupDirectory = new SimpleConnectionGroupDirectory( this.connectionGroupDirectory = new SimpleConnectionGroupDirectory(
Collections.singletonList(this.rootGroup)); Collections.singletonList(this.rootGroup));
// The user directory contains only this user // The user directory contains only this user
this.self = new SharedUser(user, this);
this.userDirectory = new SimpleUserDirectory(this.self); this.userDirectory = new SimpleUserDirectory(this.self);
} }
/**
* Registers a new share key with this SharedUserContext such that the user
* will have access to the connection associated with that share key. The
* share key will be automatically de-registered when it is no longer valid.
*
* @param shareKey
* The share key to register.
*/
public void registerShareKey(String shareKey) {
connectionDirectory.registerShareKey(shareKey);
}
@Override @Override
public User self() { public User self() {
return self; return self;

View File

@@ -55,8 +55,7 @@ import org.apache.guacamole.token.StandardTokens;
import org.apache.guacamole.token.TokenFilter; import org.apache.guacamole.token.TokenFilter;
import org.mybatis.guice.transactional.Transactional; import org.mybatis.guice.transactional.Transactional;
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper; import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionDefinition; import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper;
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel;
@@ -695,7 +694,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
@Override @Override
@Transactional @Transactional
public GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user, public GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user,
SharedConnectionDefinition definition, SharedConnectionDefinition definition,
GuacamoleClientInformation info) GuacamoleClientInformation info)
throws GuacamoleException { throws GuacamoleException {

View File

@@ -24,8 +24,8 @@ import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionDefinition; import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser; import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.ConnectionGroup;
@@ -173,7 +173,7 @@ public interface GuacamoleTunnelService {
* If the connection cannot be established due to concurrent usage * If the connection cannot be established due to concurrent usage
* rules. * rules.
*/ */
GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user, GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user,
SharedConnectionDefinition definition, SharedConnectionDefinition definition,
GuacamoleClientInformation info) GuacamoleClientInformation info)
throws GuacamoleException; throws GuacamoleException;

View File

@@ -0,0 +1,6 @@
<meta name="after" content=".caption .activeUserCount">
<!-- The user sharing this connection (if any) -->
<span class="jdbc-share-tag" ng-show="item.wrappedItem.attributes['jdbc-shared-by']"
translate="HOME.INFO_SHARED_BY"
translate-values="{USERNAME: item.wrappedItem.attributes['jdbc-shared-by']}"></span>

View File

@@ -0,0 +1,35 @@
/*
* 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.
*/
/* Label/tag denoting the user that shared a connection */
.jdbc-share-tag {
background: #0095ff;
padding: 0.25em;
-moz-border-radius: 0.25em;
-webkit-border-radius: 0.25em;
-khtml-border-radius: 0.25em;
border-radius: 0.25em;
color: white;
font-size: 0.75em;
font-weight: bold;
}

View File

@@ -42,6 +42,10 @@
"NAME" : "PostgreSQL" "NAME" : "PostgreSQL"
}, },
"HOME" : {
"INFO_SHARED_BY" : "Shared by {USERNAME}"
},
"USER_ATTRIBUTES" : { "USER_ATTRIBUTES" : {
"FIELD_HEADER_DISABLED" : "Login disabled:", "FIELD_HEADER_DISABLED" : "Login disabled:",

View File

@@ -10,6 +10,14 @@
"org.apache.guacamole.auth.mysql.MySQLSharedAuthenticationProvider" "org.apache.guacamole.auth.mysql.MySQLSharedAuthenticationProvider"
], ],
"css" : [
"styles/jdbc.css"
],
"html" : [
"html/shared-connection.html"
],
"translations" : [ "translations" : [
"translations/en.json", "translations/en.json",
"translations/fr.json", "translations/fr.json",

View File

@@ -10,6 +10,14 @@
"org.apache.guacamole.auth.postgresql.PostgreSQLSharedAuthenticationProvider" "org.apache.guacamole.auth.postgresql.PostgreSQLSharedAuthenticationProvider"
], ],
"css" : [
"styles/jdbc.css"
],
"html" : [
"html/shared-connection.html"
],
"translations" : [ "translations" : [
"translations/en.json", "translations/en.json",
"translations/fr.json", "translations/fr.json",