Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

João Batista Neto

Conexão SSH com PHP usando State design pattern

Recommended Posts

Um dos padrões de projeto mais legais é o State, com ele, um objeto pode mudar seu comportamento em tempo de execução dependendo do estado corrente e cada implementação de estado preocupa-se apenas com o que deve fazer, deixando assim um código limpo e totalmente reutilizável.

Imagem Postada


Cada ConcreteState implementa um comportamento específico permitindo, dessa forma, que novos comportamentos sejam anexados.

No caso de uma conexão SSH:

Imagem Postada

Nosso contexto inicia-se como SSHStateClosed, assim que executamos o método open() nosso estado passa a ser SSHStateEstabilished e podemos verificar a assinatura do host e autenticar; Assim que autenticado o estado passa à SSHStateListen para que possamos enviar comandos ao servidor remoto.

Como uma autenticação pode ser de várias formas, deixamos a implementação separada em um objeto específico, dessa forma, podemos autenticar com usuário e senha ou, se for o caso, utilizando certificados digitais (entre outras).

Um comando deveria ser implementado da mesma forma, porém, para simplificar as coisas, vamos trabalhar com strings mesmo:

com/ssh/auth/SSHAuthenticatePassword.php

<?php
/**
* @author João Batista Neto
* @package ssh
* @category ssh, state
*/
namespace comsshauth;

/**
* @see ISSHAuthentication
*/
use comsshinterfacesISSHAuthentication;

/**
* Implementa uma autenticação utilizando nome de usuário e senha
* @author João Batista Neto
* @final
* @package ssh
* @category ssh, state, authentication
*/
final class SSHAuthenticatePassword implements ISSHAuthentication {
/**
* @var string
*/
private $user;

/**
* @var string
*/
private $pswd;

/**
* Constroi o objeto de autenticação
* @param string $user
* @param string $pswd
*/
public function __construct( $user , $pswd ){
$this->user =& $user;
$this->pswd =& $pswd;
}

/**
* Efetua a autenticação
* @param resource $resource
* @throws InvalidArgumentException se o recurso não for válido
*/
public function authenticate( &$resource ){
if ( is_resource( $resource ) ){
return ssh2_auth_password( $resource , $this->user , $this->pswd );
} else {
throw new InvalidArgumentException( 'Recurso inválido.' );
}

return false;
}

/**
* Recupera o nome do usuário
* @return string
*/
public function getUser(){
return $this->user;
}
}



com/ssh/interfaces/AbstractSSHState.php

<?php
/**
* @author João Batista Neto
* @package ssh
* @subpackage interfaces
* @category ssh, state
*/
namespace comsshinterfaces;

/**
* Interface para um estado de conexão
* @abstract
* @author João Batista Neto
* @package ssh
* @subpackage interfaces
* @category ssh, state
*/
abstract class AbstractSSHState implements ISSHState {
/**
* @var resource
*/
protected $resource;

/**
* Autentica o usuário
* @param ISSHAuthentication $auth
* @param ISSHConnection $context
* @return boolean
* @throws BadMethodCallException se o estado não implementar o método authenticate
*/
public function authenticate( ISSHAuthentication $auth , ISSHConnection $context ){
throw new BadMethodCallException( sprintf( '%s não implementa o método %s' , get_class( $this ) , __METHOD__ ) );
}

/**
* Executa um comando no servidor remoto
* @param string $command
* @param ISSHConnection $context
* @return boolean
* @throws BadMethodCallException se o estado não implementar o método execute
*/
public function execute( $command , ISSHConnection $context ){
throw new BadMethodCallException( sprintf( '%s não implementa o método %s' , get_class( $this ) , __METHOD__ ) );
}

/**
* Recupera a fingerprint do servidor
* @param ISSHConnection $context
* @return string
* @throws BadMethodCallException se o estado não implementar o método execute
*/
public function getFingerprint( ISSHConnection $context ){
throw new BadMethodCallException( sprintf( '%s não implementa o método %s' , get_class( $this ) , __METHOD__ ) );
}

/**
* Abre uma nova conexão
* @param string $host
* @param integer $port
* @param ISSHConnection $context
* @return boolean
* @throws BadMethodCallException se o estado não implementar o método open
*/
public function open( $host , $port = 22 , ISSHConnection $context ){
throw new BadMethodCallException( sprintf( '%s não implementa o método %s' , get_class( $this ) , __METHOD__ ) );
}

/**
* Define o recurso da conexão
* @param resource $resource
* @throws InvalidArgumentException se o recurso não for válido
*/
public function setResource( &$resource ){
if ( !is_resource( $resource ) ){
throw new InvalidArgumentException( 'Recurso inválido.' );
} else {
$this->resource =& $resource;
}
}
}



com/ssh/interfaces/ISSHAuthentication.php

<?php
/**
* @author João Batista Neto
* @package ssh
* @subpackage interfaces
* @category ssh, state
*/
namespace comsshinterfaces;

/**
* Interface para autenticação SSH
* @author João Batista Neto
* @package ssh
* @subpackage interfaces
* @category ssh, state
*/
interface ISSHAuthentication {
/**
* Efetua a autenticação do usuário
* @param resource $resource
*/
public function authenticate( &$resource );

/**
* Recupera o nome do usuário
* @return string
*/
public function getUser();
}



com/ssh/interfaces/ISSHConnection.php

<?php
/**
* @author João Batista Neto
* @package ssh
* @subpackage interfaces
* @category ssh, state
*/
namespace comsshinterfaces;

/**
* Interface para uma conexão SSH
* @author João Batista Neto
* @package ssh
* @subpackage interfaces
* @category ssh, state
*/
interface ISSHConnection {
/**
* Autentica um usuário
* @param ISSHAuthentication $auth
* @return boolean
*/
public function authenticate( ISSHAuthentication $auth );

/**
* Executa um comando no servidor remoto
* @param string $command
* @return boolean
*/
public function execute( $command );

/**
* Recupera a fingerprint do servidor
* @return string
*/
public function getFingerprint();

/**
* Abre uma nova conexão
* @param stirng $host
* @param integer $port
* @return boolean
*/
public function open( $host , $port );

/**
* Modifica o estado da conexão
* @param ISSHState $state
*/
public function changeState( ISSHState $state );
}



com/ssh/interfaces/ISSHState.php

<?php
/**
* @author João Batista Neto
* @package ssh
* @subpackage interfaces
* @category ssh, state
*/
namespace comsshinterfaces;

/**
* Interface para um estado de conexão
* @author João Batista Neto
* @package ssh
* @subpackage interfaces
* @category ssh, state
*/
interface ISSHState {
/**
* Autentica o usuário
* @param ISSHAuthentication $auth
* @param ISSHConnection $context
*/
public function authenticate( ISSHAuthentication $auth , ISSHConnection $context );

/**
* Executa um comando no servidor remoto
* @param string $command
* @param ISSHConnection $context
*/
public function execute( $command , ISSHConnection $context );

/**
* Recupera a fingerprint do servidor
* @param ISSHConnection $context
* @return string
*/
public function getFingerprint( ISSHConnection $context );

/**
* Abre uma nova conexão
* @param string $host
* @param integer $port
* @param ISSHConnection $context
*/
public function open( $host , $port = 22 , ISSHConnection $context );

/**
* Define o recurso da conexão
* @param resource $resource
*/
public function setResource( &$resource );
}



com/ssh/state/SSHStateClosed.php

<?php
/**
* @author João Batista Neto
* @package ssh
* @category ssh, state
*/
namespace comsshstate;

/**
* @see AbstractSSHState
*/
use comsshinterfacesAbstractSSHState;

/**
* @see ISSHConnection
*/
use comsshinterfacesISSHConnection;

/**
* Implementa o estado para uma conexão fechada
* @author João Batista Neto
* @final
* @package ssh
* @category ssh, state
*/
final class SSHStateClosed extends AbstractSSHState {
/**
* @staticvar
* @var ISSHConnection
*/
static private $context;

/**
* Abre uma nova conexão
* @param string $host
* @param integer $port
* @param ISSHConnection $context
* @return boolean
* @throws RuntimeException se não for possível estabelecer a conexão
* @uses SSHStateEstabilished
*/
public function open( $host , $port = 22 , ISSHConnection $context ){
$resource = ssh2_connect( $host , $port , array(
'disconnect' => array( 'SSHStateClosed' , 'disconnect' )
) );

if ( !is_resource( $resource ) ){
throw new RuntimeException( 'Não foi possível estabelecer a conexão.' );
} else {
$estabilished = new SSHStateEstabilished();
$estabilished->setResource( $resource );

self::$context =& $context;
self::$context->changeState( $estabilished );
}

return true;
}

/**
* Muda o estado da conexão para SSHStateClosed caso o servidor envie um disconnect
* @static
*/
static public function disconnect(){
self::$context->changeState( new SSHStateClosed() );
}
}



com/ssh/state/SSHStateEstabilished.php

<?php
/**
* @author João Batista Neto
* @package ssh
* @category ssh, state
*/
namespace comsshstate;

/**
* @see AbstractSSHState
*/
use comsshinterfacesAbstractSSHState;

/**
* @see ISSHConnection
*/
use comsshinterfacesISSHConnection;

/**
* @see ISSHAuthentication
*/
use comsshinterfacesISSHAuthentication;

/**
* Implementa o estado para uma conexão estabelecida
* @author João Batista Neto
* @final
* @package ssh
* @category ssh, state
*/
final class SSHStateEstabilished extends AbstractSSHState {
/**
* Efetua a autenticação do usuário
* @param ISSHAuthentication $auth
* @param ISSHConnection $context
* @return boolean
* @throws RuntimeException se não for possível autenticar o usuário
* @uses SSHStateAuthenticated
*/
public function authenticate( ISSHAuthentication $auth , ISSHConnection $context ){
if ( !$auth->authenticate( $this->resource ) ){
throw new RuntimeException( sprintf( 'Não foi possível autenticar o usuário %s.' , $auth->getUser() ) );
} else {
$authenticated = new SSHStateListen();
$authenticated->setResource( $this->resource );
$context->changeState( $authenticated );
}

return true;
}

/**
* Recupera o hash da chave do servidor da conexão ativa
* @return string
* @throws LogicException se o estado não implementar o método execute
*/
public function getFingerprint( ISSHConnection $context ){
return ssh2_fingerprint( $this->resource , SSH2_FINGERPRINT_MD5 | SSH2_FINGERPRINT_HEX );
}
}



com/ssh/state/SSHStateListen.php

<?php
/**
* @author João Batista Neto
* @package ssh
* @category ssh, state
*/
namespace comsshstate;

/**
* @see AbstractSSHState
*/
use comsshinterfacesAbstractSSHState;

/**
* @see ISSHConnection
*/
use comsshinterfacesISSHConnection;

/**
* Implementa o estado para uma conexão autenticada
* @author João Batista Neto
* @final
* @package ssh
* @category ssh, state
*/
final class SSHStateListen extends AbstractSSHState {
/**
* @var resource
*/
private $shell;

/**
* Executa um comando no servidor remoto
* @param string $command
* @param ISSHConnection $context
* @return string A saída do servidor remoto
* @throws RuntimeException se não for possível executar o comando
*/
public function execute( $command , ISSHConnection $context ){
$ret = null;

$stream = ssh2_exec( $this->resource , $command );

if ( is_resource( $stream ) ){
stream_set_blocking( $stream , true );

while ( ( $line = fgets( $stream , 4096 ) ) !== false ){
$ret .= $line;
}
} else {
throw new RuntimeException( 'Não foi possível executar o comando.' );
}

return $ret;
}
}



com/ssh/SSHConnection.php

<?php
/**
* @author João Batista Neto
* @package ssh
* @category ssh, state
*/
namespace comssh;

/**
* @see ISSHConnection
*/
use comsshinterfacesISSHConnection;

/**
* @see ISSHAuthentication
*/
use comsshinterfacesISSHAuthentication;

/**
* @see ISSHState
*/
use comsshinterfacesISSHState;

/**
* @see SSHStateClosed
*/
use comsshstateSSHStateClosed;

/**
* Implementa uma conexão com um servidor remoto via SSH
* @author João Batista Neto
* @final
* @package ssh
* @category ssh, state
*/
class SSHConnection implements ISSHConnection {
/**
* @var ISSHState
*/
private $state;

/**
* Cria o objeto de conexão
*/
public function __construct(){
$this->state = new SSHStateClosed();
}

/**
* Faz a autenticação no servidor
* @param ISSHAuthentication $auth
* @return boolean
*/
public function authenticate( ISSHAuthentication $auth ){
return $this->state->authenticate( $auth , $this );
}

/**
* Executa um comando no servidor
* @param string $command
* @return boolean
*/
public function execute( $command ){
return $this->state->execute( $command , $this );
}

/**
* Recupera a fingerprint do servidor
* @return string
*/
public function getFingerprint(){
return $this->state->getFingerprint( $this );
}

/**
* Abre uma conexão com um servidor remoto
* @param string $host
* @param integer $port
* @return boolean
*/
public function open( $host , $port = 22 ){
return $this->state->open( $host , $port , $this );
}

/**
* Modifica o estado da conexão
* @param ISSHState $state
*/
public function changeState( ISSHState $state ){
$this->state =& $state;
}
}



Agora, para conectar em um servidor remoto via PHP é simples:

<?php
use comsshSSHConnection;
use comsshauthSSHAuthenticatePassword;

$teste = new SSHConnection();
$teste->open( '127.0.0.1' );
$teste->authenticate( new SSHAuthenticatePassword( 'usuario' , 'senha' ) );
echo $teste->execute( 'ls -l /var/www/html/xxx/com/ssh' );



No meu caso, a saída foi:

total 16drwxr-xr-x. 3 neto apache 4096 Mar 11 18:18 authdrwxr-xr-x. 3 neto apache 4096 Mar 11 18:18 interfaces-rw-r--r--. 1 neto apache 1681 Mar 11 18:17 SSHConnection.phpdrwxr-xr-x. 3 neto apache 4096 Mar 12 06:35 state


Imagem Postada

 

 

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

to com um problema, copiei todos arquivos mas to tendo um erro!

o arquivo que roda o comando ssh é o test.php

todos arquivos e pasta estao com chmod 777 e o php é 5.3.2

Fatal error: Class 'com\ssh\SSHConnection' not found in /var/www/lighttpd/ssh/test.php on line 5

4 ./php.php

4 ./com/ssh/auth/SSHAuthenticatePassword.php

8 ./com/ssh/auth

4 ./com/ssh/interfaces/ISSHAuthentication.php

4 ./com/ssh/interfaces/AbstractSSHState.php

4 ./com/ssh/interfaces/ISSHState.php

16 ./com/ssh/interfaces

4 ./com/ssh/SSHConnection.php

4 ./com/ssh/state/SSHStateClosed.php

4 ./com/ssh/state/SSHStateListen.php

4 ./com/ssh/state/SSHStateEstabilished.php

16 ./com/ssh/state

48 ./com/ssh

52 ./com

4 ./test.php

64 .

Compartilhar este post


Link para o post
Compartilhar em outros sites

todos arquivos e pasta estao com chmod 777 e o php é 5.3.2

 

Jamais de 777 em produção, é um erro grave de segurança.

 

Fatal error: Class 'com\ssh\SSHConnection' not found in /var/www/lighttpd/ssh/test.php on line 5

 

Veja esse link http://forum.imasters.com.br/public/style_emoticons/default/seta.gif http://br2.php.net/manual/pt_BR/function.spl-autoload-register.php

 

Se estiver em ambiente Windows, bastará o uso da função sem argumentos, desde que você tenha as pastas no seu include_path

 

No Linux, você deverá converter os separadores do namespace para separadores de diretório.

 

Eu costumo colocar esses tipos de diretórios dentro de um outro chamado application pois, dessa forma, fica mais fácil normalizar:

 

application/config.php

<?php
/**
* Configurações do ambiente
* @package		config
* @category	configurations
*/
namespace config;

/**
* Caminho do diretório application
* @var string
*/
$applicationPath = realpath( 'application' );

/**
* Configurações das mensagens de erro em desenvolvimento e produção
*/
if ( defined( 'production' ) ){
/**
 * Configurações de exibição de erro para produção
 */
error_reporting( E_ALL & ~E_DEPRECATED );
ini_set( 'display_errors' , 'Off' );
} else {
/**
 * Configurações de exibição de erro para desenvolvimento
 */
error_reporting( E_ALL | E_STRICT );
ini_set( 'display_errors' , 'On' );
}

/**
* Definição do include_path, diretório application assume prioridade em relação
* aos originais.
*/
set_include_path( sprintf( '%s%s%s' , $applicationPath , PATH_SEPARATOR , get_include_path() ) );

/**
* Registrando a função __autoload para carregar automaticamente nossas classes.
* Para normalizar, substituimos a \ usada na hierarquia dos NAMESPACES pela constante DIRECTORY_SEPARATOR,
* isso funcionará tanto em servidores Windows quanto Linux, já que a constante DIRECTORY_SEPARATOR conterá
* o valor adequado para o ambiente.
*/
spl_autoload_register( function( $class ){
require sprintf( '%s.php' , str_replace( '\\' , DIRECTORY_SEPARATOR , $class ) );
} );

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pessoal, infelizmente não consegui rodar a solução =(

 

Algum de vocês conhece esse erro? Estou tentando rodar em um servidor Linux

 

[root@dyn529872 teste_ssh]# php index.php
PHP Parse error:  syntax error, unexpected T_STRING, expecting T_CONSTANT_ENCAPSED_STRING or '(' in /var/www/html/teste_ssh/index.php on line 3
You have new mail in /var/spool/mail/root
[root@dyn529872 teste_ssh]#

Poderiam me dar uma ajuda?

Abraço

 

Srs,

Estou tentando instalar essa biblioteca já faz algum tempo. Já tentei no Cent OS, no REDHAT e no Debian porem não obtive sucesso em nenhum dos casos, alguem teria um tutorial de como instalar?

Obrigado

Compartilhar este post


Link para o post
Compartilhar em outros sites

Senhores,

Finalmente consegui instalar a biblioteca sem que ela apresente erro. Estou utilizando RH 5.5 só que estou recebendo uma msg de erro quando abro a Pagina pelo apache:

 

[Thu Nov 04 14:46:59 2010] [error] [client 9.18.218.31] PHP Warning:  ssh2_connect() [<a href='function.ssh2-connect'>function.ssh2-connect</a>]: Unable to connect to 9.18.218.20 on port 22 in /var/www/html/teste_ssh01.php on line 11
[Thu Nov 04 14:46:59 2010] [error] [client 9.18.218.31] PHP Warning:  ssh2_connect() [<a href='function.ssh2-connect'>function.ssh2-connect</a>]: Unable to connect to 9.18.218.20 in /var/www/html/teste_ssh01.php on line 11

Se rodo ele na linha de comando ele roda de boa:

 

 

[root@xxxx html]# php teste_ssh01.php
okay: logged in...
Sist. Arq.            Tam   Usad Disp  Uso% Montado em
/dev/hda1             1,9G  293M  1,6G  16% /
/dev/hda9             1,9G   35M  1,8G   2% /tmp
/dev/hda7             4,8G  599M  3,9G  14% /opt
/dev/hda6             4,8G  1,8G  2,8G  40% /home
/dev/hda5             9,5G  8,1G  909M  91% /var
/dev/hda3             9,5G  1,3G  7,8G  14% /usr
/dev/hda2              19G   19G     0 100% /img
tmpfs                 993M     0  993M   0% /dev/shm
/dev/hda10             74G  7,6G   63G  11% /backup
<br>
[root@xxxx html]#

Se puderem me ajudar eu agradeço.

Abraço

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.