30 de dezembro de 2011

Preparando a execução de um programa em desktop remoto

A adoção maciça da internet e os benefícios associados a ela têm levado as empresas a optar cada vez com mais frequência por programas que possam ser executados nessa infraestrutura. O principal benefício aqui está relacionado aos custos já que os desktops não precisam ser tão potentes - basta que tenham acesso à internet para executarem esses programas remotos - e a manutenção dos equipamentos passa ser centralizada.

Por outro lado, nem todos os programas em uso pelas empresas operam nativamente na internet. Seja por que são programas legados ou por que o fabricamente ainda não os preparou para o ambiente web ou simplesmente porque há muitos programas e não houve tempo hábil para uma conversão em massa. Por isso, programas como o RDS (Remote Desktop Services) da Microsoft e as soluções da Citrix para virtualização de desktops têm sido bastante adotados.

Essas soluções permitem que um usuário execute remotamente os programas que necessita, através de um desktop virtual acessível via rede local ou internet. Com isso, o hardware mais robusto fica centralizado num servidor e pode ser compartilhado por diversos usuários simultâneos.

Nesse ambiente, diversos usuários podem estar executando sua aplicação no mesmo computador remoto, compartilhando recursos como a pasta de trabalho, memória mapeada, disco, etc. Desse modo, as diferentes execuções simultâneas do programa podem conflitar entre si. Decisões baseadas no nome do computador, seu endereço IP ou outra informação de hardware também podem causar problemas já que todos os usuários remotos lerão os mesmos valores. A ABC71, por exemplo, usa o nome do computador onde nosso ERP está em execução como parte do controle de licenças.

Em Windows, normalmente as soluções de desktop virtual se baseiam nos serviços de Remote Desktop (antes chamado de Terminal Services), razão pela qual podemos usar a Remote Desktop Services API para preparar nossas aplicações para que rodem nesse ambiente. Essa API permite resgatar e modificar informações sobre um usuário específico, além de executar tarefas relativas à administração do serviço.

Para começar, temos que determinar se o programa está executando num desktop convencional ou num virtual. Isso pode ser conseguido com uma chamada à função GetSystemMetrics. Saber que estou numa sessão remota me permite, por exemplo, ajustar uma pasta de trabalho coerente para cada execução distinta do meu programa.
function TForm1.IsRemoteSession : boolean;
var res : integer;
begin
res := GetSystemMetrics (SM_REMOTESESSION);
Result := (res <> 0);
end;
A função GetSystemMetrics é um curinga da API do Windows; com ela é possível recuperar uma porção de parâmetros do ambiente operacional. Chamada com SM_REMOTESESSION, ela retorna um valor diferente de zero se o programa estiver num desktop virtual.

Muitas informações valiosas sobre a sessão remota em andamento podem ser obtidas através da função WTSQuerySessionInformation. No exemplo abaixo, eu a utilizo para recuperar o nome do computador que está acessando o desktop remoto:
procedure TForm1.GetSessionInfo;
var lSessionId: DWORD;
lBuffer : PChar;
lBytesReturned : DWORD;
lStationName : String;
begin
lSessionId := 0;

{ Descobre a identificação da sessão do usuário com base na identificação do programa no Windows }
if not ProcessIdToSessionId (GetCurrentProcessId (), DWORD(@lSessionId)) then
raise Exception.Create ('Não foi possível obter Remote SessoinId');

lBuffer := Nil;
lBytesReturned := 0;
lStationName := '';

{ Obtem nome da máquina Client }
if (WTSQuerySessionInformation (WTS_CURRENT_SERVER_HANDLE,
lSessionId,
WTSClientName,
lBuffer,
lBytesReturned))
then
lStationName := String(lBuffer)
else
raise Exception.Create ('Não foi possível obter o nome da estação');

{ Libera a memória alocada automaticamente }
WTSFreeMemory (lBuffer);

{ ... }
end;
Vamos por partes. Para recuperar as informações de uma sessão remota, primeiro temos que identificar essa sessão. A função ProcessIdToSessionId da API do Windows nos fornece isso, mapeando o Process ID de nosso programa para a correspondente identificação da sessão remota onde ele está executando.

O passo seguinte é chamar a função WTSQuerySessionInformation para levantar as informações desejadas. Essa função recebe 5 parâmetros. O primeiro é um handle para o servidor da sessão remota. No exemplo, passei a constante WTS_CURRENT_SERVER_HANDLE para indicar que quero informações sobre o servidor atual. Poderia ter usado WTSOpenServer para abrir outro servidor remoto.

O segundo parâmetro é a identificação da sessão que obtivemos no primeiro passo. O parâmetro seguinte é o que determina qual informação será recuperada. Os valores permitidos são os listados no enumerado WTS_INFO_CLASS. No exemplo, usei WTSClientName para obter o nome do computador do usuário que diparou o acesso remoto; a lista de informações recuperáveis inclui o endereço IP desse mesmo computador, a pasta de trabalho remota (no servidor), o nome do usuário e informações sobre o uso da sessão e sobre a Client, dentre outras.

Os dois últimos parâmetros são, respectivamente, um ponteiro para a área que receberá a informação solicitada e a quantidade de bytes que essa informação está ocupando. Como ambos são calculados pela função WTSQuerySessionInformation, não é preciso alocar previamente a memória para eles. No entanto, é nossa responsabilidade liberar a memória alocada usando a função WTSFreeMemory, como mostra o passo final do código do exemplo.

Se tivesse usado GetComputerName, eu obteria o nome do computador remoto, isto é, o servidor onde o programa está efetivamente sendo executado. A questão é que o mesmo nome seria retornado para qualquer usuário que acesse um desktop remoto nesse servidor ...

A lista de funções da Remote Desktop Services API inclui ainda formas de administrar o RDS tais como iniciar e encerrar sessões, inventariar as sessões ativas, modificar configurações de uma sessão, etc.

21 de dezembro de 2011

Design Patterns com Delphi: Template - Parte II

No meu último post, introduzi o conceito do Design Pattern comportamental Template. Em resumo, esse padrão estabelece uma sequência de passos para a execução de uma determinada tarefa, permitindo que sejam criadas heranças que implementem suas próprias versões de um ou mais dos passos, de acordo com a necessidade.

Neste post, eu sugiro uma implementação em Delphi do exemplo envolvendo criação de infraestruturas que usei para apresentar o Template no outro post. Para facilitar o entendimento, reproduzo novamente abaixo o diagrama UML que modela a infraestrutura da transações de negócio usada como exemplo:
Diagrama UML para o padrão Template
Como dá para intuir a partir da simplicidade do diagrama, a codificação também não é complexa. A primeira providência é criar a classe que define o formato geral do algorítimo, os passos que o comporão, em qual ordem esses passos devem ser chamados para completar a tarefa e quais deles poderão ser particularizados em heranças do algorítimo. Esta classe - chamada de Abstract Class - servirá, portanto, de base para as demais implementações do mesmo algorítimo. No nosso exemplo, é a classe TWTransacao que tem essa responsabilidade:
TWTransacao=class
protected
_Erros: TWErros; { Lista de erros da transação }

procedure BeginTrans;
procedure CommitTrans;
procedure RollbackTrans;

function ConsisteDados : boolean;virtual;
function GravaDados : integer;virtual;

public
function ExecutarTransacao : integer;
end;

{ ... }

function TWTransacao.ConsisteDados : boolean;
begin
Result := true;
end;

function TWTransacao.GravaDados : integer;
begin
Result := 0;
end;

function TWTransacao.ExecutarTransacao : integer;
begin
Result := -99;

Try
_Erros.Clear;

{ Inicia uma transação com o banco de dados }
BeginTrans;

{ Se estiver tudo OK com os dados, gravá-los no banco }
if (ConsisteDados) then
Result := GravaDados;

{ Efetiva a transação }
CommitTrans;
Except
{ Se houve algum erro, aborta a transação com o banco de dados}
RollbackTrans;
Result := -100;
end;
end;

Observe que há apenas uma função pública : ExecutarTransacao. As partes do programa que desejarem executar uma transação de negócio precisam ter acesso apenas a ela, não importando quais ou quantos são os passos necessários para executá-la.

Em contraste, tanto os passos do algorítimo que poderão ser estendidos pelas heranças quanto os métodos internos auxiliares foram colocados na área protegida, escondendo os detalhes para códigos externos. Aqui, as funções ConsisteDados e GravaDados poderiam ter sido declaradas como abstratas, o que forçaria as heranças a fornecer-lhes uma implementação. No entanto, não é mandatório que elas tenham essa característica, de modo que o exemplo ilustra como a classe base do Template pode providenciar uma versão padrão dos passos do algorítimo, mesmo que sejam versões bem simples. Elas poderiam, por exemplo, registrar um log da execução.

As funções relativas a transações com o banco de dados (BeginTrans, CommitTrans e RollbackTrans) apenas repassam suas respectivas chamadas diretamente ao banco; por isso, suas implementações foram omitidas no quadro acima.

Já temos a classe base, podemos então codificar as heranças previstas para transações de produtos, pedidos de venda e notas fiscais:
TWTrnProduto = class(TWTransacao)
private
_prod : TWProduto;
protected
function ConsisteDados : boolean;override;
function GravaDados : integer;override;
{ ... }
end;

TWTrnPedidoVenda = class(TWTransacao)
private
_Pedido : TWPedidoVenda;
protected
function ConsisteDados : boolean;override;
function GravaDados : integer;override;
{ ... }
end;

TWTrnNotaFiscal = class(TWTransacao)
private
_Nota : TWNotaFiscal;
protected
function ConsisteDados : boolean;override;
function GravaDados : integer;override;
{ ... }
end;

{ ... }

function TWTrnProduto.ConsisteDados : boolean;
begin
Result := inherited ConsisteDados;

if (TWProduto.ExisteProd (_Prod) ) then
begin
Result := false;
_Erros.Add ('Produto já existe');
end;
end;

function TWTrnProduto.GravaDados : integer;
begin
inherited GravaDados;

_Produto.Insert();
end;

function TWTrnPedidoVenda.ConsisteDados : boolean;
begin
Result := inherited ConsisteDados;

if (TWPedidoVenda.ExistePedido (_Pedido) ) then
begin
Result := false;
_Erros.Add ('Já existe pedido com esse número.');
end;

if (not _Pedido.VerifSaldoProdutos) then
Result := false;

if (not _Pedido.VerifDadosComerciais) then
Result := false;

{ Se detectou algum erro, reporta para a transação }
_Erros.Add(_Pedido.Erros);
end;

function TWTrnPedidoVenda.GravaDados : integer;
begin
inherited GravaDados;

{ Insere o pedido, atualiza saldo dos produtos e faz outros ajustes }
_Pedido.Insert ();
end;
Vemos no quadro anterior que as transações reais são heranças da classe de transação básica e que elas fornecem suas próprias versões de passos do algorítimo base. Com isso, elas garantem que suas próprias regras de negócio sejam respeitadas, enquanto são mantidas a ordem de chamada dos passos e outras regras estipuladas pela classe TWTransacao. A transação de nota fiscal foi omitida no quadro mas a ideia dela é bastante similar à de pedidos de venda.

Para completar o exemplo, falta a classe Client, isto é, aquela parte do código que utilizará o nosso template.
TWTela = class
protected
_Trn : TWTransacao;

public
{ ... }
procedure ExecutarTransacao(); end;

{ ... }

procedure TWTela.ExecutarTransacao();
begin
_Trn := ObtemTransacao ();
_Trn.ExecutarTransacao;
end;
Como mostra o quadro, o código da classe TWTela acaba ficando extremamente simples. Aqui, o maior problema talvez seja decidir o melhor meio de obter uma instância da transação correta. Dependendo das circunstâncias, podemos optar pelo método Factory ou organizar as telas usando o padrão Mediator - o que permitiria cada tela instanciar diretamente a transação adequada.

13 de dezembro de 2011

Design Patterns com Delphi: Template - Parte I

Criar uma infraestrutura de software é padronizar a forma como as coisas acontececerão dentro de um programa, estabelecendo regras e ditando as mensagens que cada parte do código está preparada para receber. Por exemplo, uma infraestrutura para realizar transações de negócio dentro do sistema pode ser descrita simplificadamente em poucos passos : abertura de transação com o banco de dados; consistência das informações contidas no(s) objeto(s) de negócio envolvido(s); persistência das informações nas respectivas tabelas; encerramento da transação. Todo objeto com capacidade de realizar uma transação terá necessariamente que implementar esses passos.

Se cada Caso de Uso implementar uma versão independente dos passos, mudanças nessa sequência teriam que ser replicadas em todas as implementações. O alvo do Design Pattern comportamental Template é justamente situações como esta, onde um algorítimo é definido de modo centralizado, moldando a forma de trabalho a ser seguida por todas as classes de objeto que desejarem implementá-lo. Um ou mais passos podem ser sobrepostos pelas heranças, permitindo que elas implementem comportamentos diferenciados quando for necessário.

Essa idéia é similar à do Pattern Strategy. A diferença mais importante entre ambos é que o Template permite que heranças alterem partes de um algorítimo enquanto no Strategy temos que optar por uma das versões de um algorítimo bem conhecido e executar todo o trabalho com esse algorítimo.

O quadro a seguir traz um diagrama UML mostrando as relações esperadas entre as classes participantes da solução para o Template. O cenário abordado é o da transação de negócio citado no primeiro parágrafo deste post:
Diagrama UML para o padrão Template
Neste exemplo, nem todos os métodos são virtuais. São declarados como virtuais apenas aqueles que poderão (ou deverão) ter uma implementação distinta nas classes herdadas. Como podemos ver, o algorítimo é decomposto em métodos mais granulares que podem ser sobrepostos quando ncessários. Neste contexto, tais métodos são chamados de operações primitivas.

No exemplo, o método ExecutarTransacao é responsável por chamar cada operação primitiva no tempo correto, moldando efetivamente o algorítimo que deve ser executado por cada classe da hierarquia. Este método é chamado de Template (modelo).

Formalmente, as classes participantes de uma solução para o pattern Template recebem os seguintes nomes:
É denominada Abstract Class a classe base que define as operações primitivas que estarão disponíveis para o algorítimo. Ela também define o esqueleto do algorítimo ao implementar o método Template, chamando cada operação primitiva na ordem correta. No diagrama acima, este é o papel da classe TWTransacao. É importante ressaltar que, apesar do nome, essa classe não precisa ser obrigatoriamente abstrata; se for adequado, a Abstract Class pode prover versões básicas de todas as operações primitivas.

As classes que provêm versões particulares de uma ou mais das operações primitivas são chamadas Concrete Class. Isso permite-lhes realizar ações específicas dentro dos passos básicos que formam o algorítimo determinado pela classe base. No exemplo, as classes TWTrnProduto, TWTrnNotaFiscal e TWTrnPedidoVenda são desse tipo. Elas fornecem suas próprias versões dos métodos que consistem e gravam os dados relativos, respectivamente, às transações para inserir um novo produto, criar uma nota fiscal e criar um pedido de venda.

O Client é qualquer parte do sistema que esteja apta a disparar o algorítimo definido na classe base. A classe TWTela do diagrama de exemplo detem esse papel.
No próximo post, eu apresento uma sugestão de como implementar o exemplo proposto usando Delphi.

Mais Informações
Posts sobre Design Patterns

8 de dezembro de 2011

HTML5 criará novos desafios para os profissionais de Segurança em TI

Agora parece que a Adobe se rendeu em definitivo ao avanço do HTML5, que nasceu com a missão de flexibilizar o desenvolvimento para aplicações Web, eliminando a necessidade de instalação de plugins de terceiros - como o próprio Flash da Adobe. Em post recente no blog do Flex, a empresa recomenda que se opte pelo HTML5 pois essa tecnologia prevalecerá no longo prazo.

Eles também anunciaram que não desenvolverão o plugin do Flash para versões futuras dos navegadores de dispositivos móveis. É claro que não pretendem deixar de criar ferramentas e estão usando o know-how que têm e a base fiel de clientes conquistados para se manterem relevantes. Ao que parece, a ideia é criar novas ferramentas mantendo a essência da filosofia de desenvolvimento das ferramentas atuais - só que produzindo HTML5 e Javascript ao invés do código próprietário do Flahs. Veja aqui o comunicado publicado no site da empresa, revelando os planos para o futuro do Flash.

Com isso, a euforia com o HTML5 já começa a assentar, dando lugar ao pragmatismo. Os especialistas começam a fazer avaliações mais realistas sobre a nova tecnologia e levantam questões até aqui deixadas em segundo plano. A expectativa de adoção dela vem suscitando, por exemplo, questões relativas à segurança, conforme mostra o artigo abaixo, cujo original (em inglês) foi publicado pela InfoWorld:

A adoção do HTML5 abrirá espaço para a criação de uma vasta gama de novas aplicações Web, mas poderá também introduzir novos desafios para os profissionais de segurança nas empresas, de acordo com a firma de segurança Sophos.

Em suas previsões sobre segurança para 2012, a Sophos identifica como principais riscos as novas tecnologias para Web e redes - incluindo HTML5. Enquanto essas tecnologias introduzem novos recursos impressionantes para desenvolvimento de aplicações Web ricas, elas também introduzem novas formas de ataque, explicou a companhia.

O HTML4 supriu conteúdo na Web por muitos anos, mas é uma linguagem de programação muito básica, de modo que os desenvolvedores tiveram que complementá-lo com add-ons como o JavaScript, Flash e Google Gears. Estes add-ons têm frequentemente muitas vulnerabilidades, tornando o sistema inseguro como um todo.

HTML5 remove a necessidade da maioria dos add-ons, porque é uma linguagem mais sofisticada e vem com um banco de dados completo, permitindo que os usuários armazenem gigas de informação. Então, por exemplo, você pode criar uma animação completa, realidade virtual em 3D ou guardar aplicações completas dentro do navegador.

De acordo com James Lyne, tecnólogo sênior na Sophos, isso chega muito perto da visão in-client originalmente associada à computação na nuvem. No entanto, armazenar dados dentro do navegador torna o navegador em si um alvo para os cyber criminosos.

"Tradicionalmente, o navegador é um meio para que cyber criminosos obtenham acesso ao seu PC. Agora, eles tentarão atacar o próprio navegador para roubar seus dados", diz Lyne.

A execução em sandbox do HTML5 também aumenta os riscos de "clickjacking" (induzir um usuário Web a revelar informações confidentiais ou tomar o controle do computador desse usuário quando ele clica num link aparentemente inócuo) já que as páginas Web não são mais capazes de identificar de onde os comandos estão vindo.

"Todo aquele código escrito pelos desenvolvedores para prevenir que aplicações Web sejam capturadas por alguém não autorizado ou sofram clickjacking agora não funcionará", diz Lyne. "Implementaram um recurso para aumentar a segurança mas inadivertidamente desativaram um outro mais importante."

Além disso, HTML levanta novas questões sobre cookies, que poderão tornar redundantes as recomendações para remoção deles após um determinado período.

"O HTML5 poderia até ter novos super-cookies," diz Lyne. "Se um site não for codificado de forma apropriada, os caras maus poderão compilar uma enorme base de dados com as URLs por onde você navegou e rastrear as informações que você eventualmente tenha fornecido nelas. Eles poderão capturar uma grande quantidade de dados."

Apesar desses problemas pontenciais, Lyne diz que adotar o HTML5 trará muitos benefícios para a segurança. Haverá redução nos riscos relacionados aos add-ons, bem como mais segurançã ao facilitar validação de dados no lado client das aplicações. Também haverão bibliotecas capazes de lidar com questões como SQL injection.

"Ao longo do tempo, o HTML5 vai resolver muitos dos problemas que temos, mas, como qualquer nova tencologia, há uma tendência a uma regressão num primeiro momemto," diz ele. "De maneira geral, deveríamos acelerar na direção do HTML5 porque Flash tem sido traumático e as novas aplicações Web estão muito legais - só temos que nos certificar de não estarmos adotando um pesadelo."