content: /*
   * Copyright (C) 2013 Glyptodon LLC
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  
  package net.sourceforge.guacamole.net.auth.mysql;
  
  
  import com.google.inject.Inject;
  import java.util.Set;
  import org.glyptodon.guacamole.GuacamoleClientException;
  import org.glyptodon.guacamole.GuacamoleException;
  import org.glyptodon.guacamole.net.auth.Connection;
  import org.glyptodon.guacamole.net.auth.Directory;
  import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionParameterMapper;
  import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionPermissionMapper;
  import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionParameter;
  import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionParameterExample;
  import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionPermissionKey;
  import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionGroupService;
  import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionService;
  import net.sourceforge.guacamole.net.auth.mysql.service.PermissionCheckService;
  import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
  import org.glyptodon.guacamole.GuacamoleUnsupportedException;
  import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
  import org.mybatis.guice.transactional.Transactional;
  
  /**
   * A MySQL-based implementation of the connection directory.
   *
   * @author James Muehlner
   */
  public class ConnectionDirectory implements Directory<String, Connection>{
  
      /**
       * The ID of the user who this connection directory belongs to.
       * Access is based on his/her permission settings.
       */
      private int user_id;
  
      /**
       * The ID of the parent connection group.
       */
      private Integer parentID;
  
      /**
       * Service for checking permissions.
       */
      @Inject
      private PermissionCheckService permissionCheckService;
  
      /**
       * Service managing connections.
       */
      @Inject
      private ConnectionService connectionService;
  
      /**
       * Service managing connection groups.
       */
      @Inject
      private ConnectionGroupService connectionGroupService;
  
      /**
       * Service for manipulating connection permissions in the database.
       */
      @Inject
      private ConnectionPermissionMapper connectionPermissionDAO;
  
      /**
       * Service for manipulating connection parameters in the database.
       */
      @Inject
      private ConnectionParameterMapper connectionParameterDAO;
  
      /**
       * Set the user and parentID for this directory.
       *
       * @param user_id The ID of the user owning this connection directory.
       * @param parentID The ID of the parent connection group.
       */
      public void init(int user_id, Integer parentID) {
          this.parentID = parentID;
          this.user_id = user_id;
      }
  
      @Transactional
      @Override
      public Connection get(String identifier) throws GuacamoleException {
  
          // Get connection
          MySQLConnection connection =
                  connectionService.retrieveConnection(identifier, user_id);
          
          if(connection == null)
              return null;
          
          // Verify permission to use the parent connection group for organizational purposes
          permissionCheckService.verifyConnectionGroupUsageAccess
                  (connection.getParentID(), user_id, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL);
  
          // Verify access is granted
          permissionCheckService.verifyConnectionAccess(
                  this.user_id,
                  connection.getConnectionID(),
                  MySQLConstants.CONNECTION_READ);
  
          // Return connection
          return connection;
  
      }
  
      @Transactional
      @Override
      public Set<String> getIdentifiers() throws GuacamoleException {
          
          // Verify permission to use the connection group for organizational purposes
          permissionCheckService.verifyConnectionGroupUsageAccess
                  (parentID, user_id, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL);
          
          return permissionCheckService.retrieveConnectionIdentifiers(user_id, 
                  parentID, MySQLConstants.CONNECTION_READ);
      }
  
      @Transactional
      @Override
      public void add(Connection object) throws GuacamoleException {
  
          String name = object.getName().trim();
          if(name.isEmpty())
              throw new GuacamoleClientException("The connection name cannot be blank.");
          
          // Verify permission to create
          permissionCheckService.verifySystemAccess(this.user_id,
                  MySQLConstants.SYSTEM_CONNECTION_CREATE);
          
          // Verify permission to edit the connection group
          permissionCheckService.verifyConnectionGroupAccess(this.user_id, 
                  this.parentID, MySQLConstants.CONNECTION_GROUP_UPDATE);
          
          // Verify permission to use the connection group for organizational purposes
          permissionCheckService.verifyConnectionGroupUsageAccess
                  (parentID, user_id, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL);
  
          // Verify that no connection already exists with this name.
          MySQLConnection previousConnection =
                  connectionService.retrieveConnection(name, parentID, user_id);
          if(previousConnection != null)
              throw new GuacamoleClientException("That connection name is already in use.");
  
          // Create connection
          MySQLConnection connection = connectionService.createConnection(
                  name, object.getConfiguration().getProtocol(), user_id, parentID);
          
          // Set the connection ID
          object.setIdentifier(connection.getIdentifier());
  
          // Add connection parameters
          createConfigurationValues(connection.getConnectionID(),
                  object.getConfiguration());
  
          // Finally, give the current user full access to the newly created
          // connection.
          ConnectionPermissionKey newConnectionPermission = new ConnectionPermissionKey();
          newConnectionPermission.setUser_id(this.user_id);
          newConnectionPermission.setConnection_id(connection.getConnectionID());
  
          // Read permission
          newConnectionPermission.setPermission(MySQLConstants.CONNECTION_READ);
          connectionPermissionDAO.insert(newConnectionPermission);
  
          // Update permission
          newConnectionPermission.setPermission(MySQLConstants.CONNECTION_UPDATE);
          connectionPermissionDAO.insert(newConnectionPermission);
  
          // Delete permission
          newConnectionPermission.setPermission(MySQLConstants.CONNECTION_DELETE);
          connectionPermissionDAO.insert(newConnectionPermission);
  
          // Administer permission
          newConnectionPermission.setPermission(MySQLConstants.CONNECTION_ADMINISTER);
          connectionPermissionDAO.insert(newConnectionPermission);
  
      }
  
      /**
       * Inserts all parameter values from the given configuration into the
       * database, associating them with the connection having the givenID.
       *
       * @param connection_id The ID of the connection to associate all
       *                      parameters with.
       * @param config The GuacamoleConfiguration to read parameters from.
       */
      private void createConfigurationValues(int connection_id,
              GuacamoleConfiguration config) {
  
          // Insert new parameters for each parameter in the config
          for (String name : config.getParameterNames()) {
  
              // Create a ConnectionParameter based on the current parameter
              ConnectionParameter parameter = new ConnectionParameter();
              parameter.setConnection_id(connection_id);
              parameter.setParameter_name(name);
              parameter.setParameter_value(config.getParameter(name));
  
              // Insert connection parameter
              connectionParameterDAO.insert(parameter);
          }
  
      }
  
      @Transactional
      @Override
      public void update(Connection object) throws GuacamoleException {
  
          // If connection not actually from this auth provider, we can't handle
          // the update
          if (!(object instanceof MySQLConnection))
              throw new GuacamoleUnsupportedException("Connection not from database.");
  
          MySQLConnection mySQLConnection = (MySQLConnection) object;
  
          // Verify permission to update
          permissionCheckService.verifyConnectionAccess(this.user_id,
                  mySQLConnection.getConnectionID(),
                  MySQLConstants.CONNECTION_UPDATE);
  
          // Perform update
          connectionService.updateConnection(mySQLConnection);
  
          // Delete old connection parameters
          ConnectionParameterExample parameterExample = new ConnectionParameterExample();
          parameterExample.createCriteria().andConnection_idEqualTo(mySQLConnection.getConnectionID());
          connectionParameterDAO.deleteByExample(parameterExample);
  
          // Add connection parameters
          createConfigurationValues(mySQLConnection.getConnectionID(),
                  object.getConfiguration());
  
      }
  
      @Transactional
      @Override
      public void remove(String identifier) throws GuacamoleException {
  
          // Get connection
          MySQLConnection mySQLConnection =
                  connectionService.retrieveConnection(identifier, user_id);
          
          if(mySQLConnection == null)
              throw new GuacamoleResourceNotFoundException("Connection not found.");
          
          // Verify permission to use the parent connection group for organizational purposes
          permissionCheckService.verifyConnectionGroupUsageAccess
                  (mySQLConnection.getParentID(), user_id, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL);
  
          // Verify permission to delete
          permissionCheckService.verifyConnectionAccess(this.user_id,
                  mySQLConnection.getConnectionID(),
                  MySQLConstants.CONNECTION_DELETE);
  
          // Delete the connection itself
          connectionService.deleteConnection(mySQLConnection.getConnectionID());
  
      }
  
      @Override
      public void move(String identifier, Directory<String, Connection> directory) 
              throws GuacamoleException {
          
          if(!(directory instanceof ConnectionDirectory))
              throw new GuacamoleUnsupportedException("Directory not from database");
          
          Integer toConnectionGroupID = ((ConnectionDirectory)directory).parentID;
          
          // Get connection
          MySQLConnection mySQLConnection =
                  connectionService.retrieveConnection(identifier, user_id);
          
          if(mySQLConnection == null)
              throw new GuacamoleResourceNotFoundException("Connection not found.");
  
          // Verify permission to update the connection
          permissionCheckService.verifyConnectionAccess(this.user_id,
                  mySQLConnection.getConnectionID(),
                  MySQLConstants.CONNECTION_UPDATE);
          
          // Verify permission to use the from connection group for organizational purposes
          permissionCheckService.verifyConnectionGroupUsageAccess
                  (mySQLConnection.getParentID(), user_id, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL);
  
          // Verify permission to update the from connection group
          permissionCheckService.verifyConnectionGroupAccess(this.user_id,
                  mySQLConnection.getParentID(), MySQLConstants.CONNECTION_GROUP_UPDATE);
          
          // Verify permission to use the to connection group for organizational purposes
          permissionCheckService.verifyConnectionGroupUsageAccess
                  (toConnectionGroupID, user_id, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL);
  
          // Verify permission to update the to connection group
          permissionCheckService.verifyConnectionGroupAccess(this.user_id,
                  toConnectionGroupID, MySQLConstants.CONNECTION_GROUP_UPDATE);
  
          // Verify that no connection already exists with this name.
          MySQLConnection previousConnection =
                  connectionService.retrieveConnection(mySQLConnection.getName(), 
                  toConnectionGroupID, user_id);
          if(previousConnection != null)
              throw new GuacamoleClientException("That connection name is already in use.");
          
          // Update the connection
          mySQLConnection.setParentID(toConnectionGroupID);
          connectionService.updateConnection(mySQLConnection);
      }
  
  }
  