sábado, 27 de abril de 2013

Grids Elásticos

O mundo é caótico! Seu funcionamento é definido por uma série de eventos que se interligam gerando ações e reações, na grande maioria das vezes de forma totalmente imprevisível, ou seja, apesar da previsibilidade existente na ordem natural do Universo, o que manda mesmo é a imprevisibilidade. Quem disse isso? o filósofo Douglas Oliveira, rs...

Brincadeira a parte, acredito mesmo nisso, e de certa forma temos a prova quando programamos,  pois em muitas ocasiões lidamos com situações de total imprevisibilidade. Por exemplo, como saber de antemão quantos produtos certo cliente comprará em uma loja virtual, quantos alunos teremos matriculados em uma disciplina, ou quantas provas certo professor aplicará em sua turma, entre outras maluquices, pode ser um desafio enorme. Acredito que você também tem muitos exemplos de situações imprevisíveis.

O conteúdo da nossa caixa de ferramentas é que determinará se teremos sucesso ou não na solução dessas situações. Já dominamos algumas ferramentas essenciais como as tabelas, coleções de dados, variáveis do tipo vetor, matriz, que dão conta na grande maioria das situações, ou seja, existem elementos que combinados permitem que consigamos sair de certos enroscos ditos ‘imprevistos’. 

Então vou tratar de uma nova estratégia que é a construção de Grids Elásticos, ou seja, certo grid que tenha a capacidade de se ajustar conforme a necessidade de armazenamento.  Já falei um pouco sobre esse assunto por aqui e na ocasião recorremos aos controles gxui para resolvermos a situação, (Grids Dinamicos),  o caso agora é um pouquinho diferente.  Buscamos criar um grid que possa ser reconstruído (em runtime) mediante a determinação do número de colunas e linhas, e claro seu conteúdo.

FreeStyleGrid

Naturalmente o FreeStyleGrid já é elástico, pois temos a possibilidade de determinar seu número de colunas, pelas  sua propriedade Columns, que, interessante, pode ser modificadas em runtime.

Para montar o Grid que utilizamos como exemplo você deve primeiro criar algo como:



E nos eventos programar

Event Refresh
 Grid1.Columns = &col
EndEvent


Event Load
 &t = &row * &col
 for &i = 1 to &t
  &conteudo = &i.tostring()
  load
 endfor
EndEvent


O resultado é a apresentação de um Grid nas dimensões determinadas, como os tipox 2x4 e 3x3, apresentados a seguir, e que se ajustam conforme se varia os valores dos campos &row e &row, e em seguida o pressionamento de Confirm.




Títulos

Digamos que minha parte terminou, agora começa a sua, que é determinar o conteúdo do grid. Quanto ao quesito titulo ainda vou dar um pequeno empurrão.



Para produzir a linha de títulos apresentada na imagem anterior, precisaríamos de uma simples inclusão de código, como o apresentado a seguir.

Event Load
 &t = &row * &col
 for &i = 1 to &t

  if &i <= &row
   &conteudo = 'Titulo' + &i.tostring()
   &conteudo.Enabled = 0
  else
   &conteudo = &i.tostring()
   &conteudo.Enabled = 1
  endif


  load
 endfor
EndEvent


Titulo em Colunas


Na primeira linha do Grid foi fácil, o problema é como colocar títulos em colunas.  Quem é da época das matrizes lineares conseguiria sair dessa sem muitas dificuldades, não que eu seja dessa época, apenas estudei o assunto.  Como a informação era representada de forma contínua na memória (e ainda é), as posições de certa matriz eram relativas, ou seja, calculadas em função do número de colunas e linhas, para isso havia uma fórmula para determinar a primeira posição na próxima linha, bastante simples, cuja forma é apresentado a seguir.

&col1 = &r * &col + 1

Onde, &r representava a linha corrente, e &col o número de colunas, então com esse conceito, programamos um modelo mais complexo, mas que produz uma bela coluna de titulos.

Event Load
 &i    = 1
 &col1 = &r * &col + 1

 for &r = 1 to &row
  for &c = 1 to &col

   &flag=0
   &conteudo = ''
   &conteudo.Enabled = 1

   // titulo primeira linha
   if &r = 1
    &conteudo = 'Titulo' + &i.tostring()
    &conteudo.Enabled = 0
    &flag=1
   endif

   // titulo coluna
   if &i = &col1
    &conteudo = 'Col' + &i.tostring()
    &conteudo.Enabled = 0
    msg('aqui')
    &flag=1
   endif

   // primeira posicao no grid
   if &r = 1 and &c = 1
    &conteudo = ''
    &conteudo.Enabled = 0
    &flag=1
   endif

   // conteudo
   if &flag=0
    &conteudo = &i.tostring()
   endif

   &i += 1
   load
  endfor
  &col1 = &r * &col + 1
 endfor
EndEvent


Com esse pequeno algoritmozinho, podemos gerar enfim nossa Grid Elástica que pode ser expansível conforme sua necessidade e com todas as características de um grid tradicional, incluindo titulos, conteúdos, e por ai vai.

 
Enfim, fica aqui a estrutura do Grid, quanto a parte de conteúdos reais vou deixar para você pensar na estratégia de ligar com tabelas, Data Providers, e outras fontes.
 

Conclusões

Acredito que esse post será muito útil para todos, assim como foi pra mim também, mas observe que utilizamos uma variável do tipo Character para determinar o conteúdo, isso porque esta permite que tenhamos não apenas textos mas também numerous, representados como strings.  Mas se você tiver que apresentar imagens, campos binarios, a coisa complica bem.

E para finalizar, gostaria de me desculpar com nossas amigas do Genexando, pelo exemplo, digamos, 'de menino', acho que ficaria meio esquisito se eu usasse a figura de uma caixinha de maquiagem, ou mesmo do conteúdo de uma bolsa feminina, para exemplificar essa situação.  Mesmo que essas duas situações nos pareçam mais caóticas que a nossa amada e inseparável caixa de ferramentas (me desculpe novamente).

quarta-feira, 24 de abril de 2013

Ataque Chinalenigena!

Uma notícia que trouxe grande apreensão recentemente, foi a declaração aberta referente ao cyberataque realizado pela China às empresas e ao próprio governo americano, e a acusação foi clara: o ataque partiu do próprio governo chinês!! Muitos analistas entendem que o que está ocorrendo é uma declaração aberta de guerra, claro que fora dos padrões tradicionais, pois nesse modelo não existem armas, mortes de inocentes, mas sim informações, computadores, sistemas, que são roubadas.(Spy Chief Calls Cyberattacks Top Threat to the U.S.)

Minha apreensão não é devido ao prejuízo que os Estados Unidos ou China, possam vir a ter diante dessa 'guerra', isso ai é briga de cachorros grandes, mas sim pela vulnerabilidade que os nossos próprios sistemas se encontram, e pior, diariamente precisam percorrer esse mesmo campo minado. Pois há de se lembrar que o mercado de desenvolvimento de software cresce de forma significativa na adoção de soluções web-based ao invés de computer based (windows), e a grande maioria sonha em migrar seus sistemas.

Assim como aquele que coloca uma grade na janela visando aumentar a barreira ao acesso da invasão em sua casa, precisamos criar soluções que contemplem o máximo de segurança possível, conhecer as nossas deficiências e gerar os controles necessários para buscar minimizar os impactos sobre os sistemas e negócios.

Top 10 2013-appsec-risks.png

O que devemos considerar é que com o advento da web e dos banco de dados, toda informação crucial da empresa deixou de ser armazenada de forma espalhada por diversos computadores, ou arquivos, mas foram todos concentrados em um único local, e pior com acesso livre, portanto, a bandeira a ser conquistada.  E aqui vale tudo, programa espião instalado nas máquinas, scanners, sniffers, malwares, virus, e outras mais indiretas como SQL Injection, Javascript Injection, ou seja, estamos fritos.

GeneXus

A Artech recentemente desenvolveu o GAM, Genexus Access Manager, para o Ev2, que auxilia em alguns pontos como o controle sobre a autenticação (quem pode ingressar) e a autorização (quem pode executar) e que permite de forma mais automática gerenciar a segurança dos objetos da aplicação.  Creio que o ponto negativo desse recurso é sua indisponibilidade para versões anteriores (EV1, por exemplo), pois seria indispensável em qualquer sistema Web.  Mas, será que pelo menos esses dois elementos são efetivos em nossos sistemas?

A questão fundamental é outra: Como e quais recursos podemos usar na aplicação e a quais as melhores práticas e estratégias, pois a ferramenta permite que você mesmo crie sua politica de segurança, independentemente do GAM.  Ouvi recentemente uma pergunta, que me deixou de cabelos em pé, que exemplifica bem essa questão: Qual o melhor local para guardar a senha da pessoa no Cookie ou na WebSession? O que você responderia?

Hot Spots!

O problema não é o GeneXus em si, pois a ferramenta vem buscando se adequar às novas formas de ataque e adotando as politicas discutidas na OWASP, mas sim todo ambiente no qual o sistema será implantado, a infra-estrutura de segurança, o controle sobre possíveis tentativas de invasões, pessoas qualificadas em segurança que vigiem os ataques, ou seja, se defender nessa guerra custa caro e principalmente, requer muita atenção.


A OWASP apresenta um excelente recurso para entendermos melhor essa situação, em uma série de vídeos, que tratam desse assunto de forma interessante e, apesar do inglês, simples. Curioso? clique aqui!

Conclusões


Gosto de finalizar com conclusões otimistas, um resumo das soluções, mas aqui não tenho muito a dizer a não ser: que tal nossa comunidade de amigos começar a tratar essa questão mais seriamente, fica aqui o estimulo, o convite e o canal para discutirmos.


Mais informações:
(imagens: http://owasp.org)


terça-feira, 23 de abril de 2013

Testando e Homologando

Um dos problemas que enfrentamos na fase de homologação e testes de sistemas é a sua execução reproduzindo as mesmas condições que os usuários enfrentam no uso diário, pois na grande maioria das vezes os dados utilizados pelos desenvolvedores é de certa maneira 'viciado' e quase nunca mostra de fato o que está ocorrendo na produção.

Na lista de situações a serem resolvidas temos o próprio login na conta do usuário que já é um desafio, mesmo porque, não é uma boa prática guardar a senha desprotegida no banco de dados, isso para que você entre 'como ele' no sistema. Procure utilizar um algoritmo de encriptação, e claro, com isso, não saberíamos seus dados para acessar o sistema.

De certa forma o sistema tem que reproduzir uma situação de segurança que permita que após o ingresso de um desenvolvedor ou testador do sistema, a opção de troca de usuário permita que o mesmo reproduza a situação do usuário que encontrou algum problema no sistema, isso de forma que o pessoal possa entender a causa.  Algo como:



Essa situação é tão perigosa, que não vou te dar nenhuma solução prática, melhor você pensar em uma que atenda sua necessidade, mas não fique nervosinho, vou te passar alguns elementos que possam te ajudar nessa construção.

Claro que você não pode nem cogitar de instalar algo assim no sistema em Produção, melhor se tiver uma instalação própria para homologação e que essa seja 'preparada' para eliminar informações importantes como nomes de usuários, documentos importantes, emails, e outras coisas que possam ser utilizadas de forma indevida, toda precaução na Web é pouco.

Agora que você está bem avisado da situação, vamos às dicas.

Qual o sistema atual?

Então, a primeira coisa é detectar qual o sistema que está em execução, se é o de homologação ou produção.  Para isso temos a ajuda de &httprequest que nos informa a URL que foi solicitada. Algo assim nos daria a certeza que apenas o sistema de testes permitiria essa liberação.


Event Start
   if &httprequest.BaseUrl.IndexOf('homologacao') > 0
     // libera porque esta no sistema de homologacao
   endif
Endevent


Esse pequeno trecho de código, permite identificar na própria URL qual o sistema em execução no servidor, que pode ser uma aplicação, pasta virtual, ou mesmo um domínio.  Se detectado que se trata do sistema de testes, libera a troca.

WebSession ou Cookie

Outro recurso indispensável na web é o uso de sessões web no próprio Server, as chamadas WebSessions. Muito simples

   &websession.Set("USUARIO", &usuarioId)

Recomendação Importante! Nunca grave senhas na Sessão Web ou Cookie.

Cookies gravam informações na própria máquina do usuário, se estiverem encriptadas ótimo, caso contrário não utilize, pois é possível ler os dados desses arquivos, pois são textos.

Encriptação

Fácil de usar em GeneXus, com as funções:


  • encrypt64
  • decrypt64

E que precisam apenas de uma &chave, mas aqui tem um segredinho, essa variável deve ser declarada como Character(32), para não ter nenhum enrosco, e seu conteúdo pode ser obtido com getsitekey(), dai basta encriptar para guardar e decriptar ao recuperar.

Caso deseje outro modelo de encriptação pode importar para o projeto com os External Objects.

Quem me chama?

Algumas instalações podem utilizar o conceito de liberar se alguém autorizado chamou, então é necessário saber qual a URL de onde partiu a chamada, além da própria chamada.

Event Start
   &deonde = &httprequest.Referrer
   if &deonde.IndexOf('paginaautorizada')      // libera porque veio do lugar correto
   endif
Endevent



Ambientes separados de Homologação e Produção

Nem preciso dizer da importância disso, só não se esqueça de utilizar banco de dados diferentes.



Creio que com esses elementos você conseguirá produzir algo bem interessante em seu sistema, de forma controlada e segura.

(fonte da imagem: http://abhaychnadra.blogspot.com.br/)

segunda-feira, 22 de abril de 2013

Business Component com Transação de Dois Níveis

Acho que vou insistir mais um pouco nesse tema, me desculpem os já convencidos, mas creio que Business Component é bom demais para não ser utilizado, e infelizmente o que tenho percebido é que ainda tem muita gente que insiste em utilizar as procedures para operar as tabelas geradas pelo Gx, quer por falta de conhecimento ou mesmo receio ante a tanta mudança conceitual, então, para tentar convencê-lo, nada melhor que ampliar um pouco o conceito dos posts anteriores, com algo, diria, mais substancial.

O tema fundamental aqui é o seguinte: E se eu tiver uma transação de dois ou mais níveis, como encaixo o Business Component?

O GeneXus tem evoluído também na questão que se refere (parece a Dilma!, rs...)  à sua linguagem de programação, e em alguns pontos está claramente mais 'orientado à objetos', pois na sintaxe dos BC´s existem propriedades e métodos que claramente remetem à esse novo modelo.  O lado negativo é que esse modelo, implica em maior dificuldade para aqueles que não estão acostumados com esse estilo. Então, acredito que a melhor maneira de explicar é simplesmente esquecendo-se de toda teoria envolvida e partindo para um lado mais prático da coisa.

Portanto, o que buscaremos é montar um modelo que possa ser reproduzido nas diversas situações, e sem muita teoria, e as transações escolhidas são os já tão explorados NotaFiscal, Cliente e Produto, mais especificamente a NotaFiscal que possui dois níveis.



Essa transação, como você já sabe, gera duas tabelas distintas, conectadas porque no segundo nível da transação temos o atributo NotaFiscalId, que fica meio 'oculto' na estrutura.


Portanto qualquer operação de inserção ou atualização de registros se deve levar em conta que temos duas tabelas, e era isso que tínhamos que programar no modelo, diria, 'mais tradicional' do Genexus.  Sendo ainda que primeiro tinhamos que criar o registro em NotaFiscal para em seguida operar a tabela NotaFiscalProduto. Então, se fossemos inserir um registro nas duas tabelas teriamos que programar algo como:

 new
    NotaFiscalData  = &Today
    ClienteId  = &ClienteId
 endnewZ

 for each // (autonumber)
    &NotaFiscalId   = NotaFiscalId
 endfor

 new
    NotaFiscalId         = &NotaFiscalId
    ProdutoId            = &ProdutoId
    NotaFiscalProdutoQtd = &NotaFiscalProdutoQtd
 endnew


Essa foi apenas uma forma simplificada de tratar a situação, cuidado não é segura, pois o for each incluído para pegar o último registro inserido, devido à caracteristica do autonumber no atributo NotaFiscalId, não é uma boa estratégia, pois teríamos problema se tivessemos um caso de concorrência de inclusão simultânea na base.  Mas por outro lado o exemplo explicita uma situação que vivemos no GeneXus tradicional que é a necessidade de incluir primeiro o registro em NotaFiscal para em seguida incluir alguma coisa em NotaFiscalProduto.

Business Component de Dois Níveis

No modelo aplicado aos Business Components, as duas tabelas são vistas como apenas uma, pois não é necessário separar as operações, da mesma forma que funciona no formulário da transação. O trecho de código abaixo realiza a mesma operação do modelo anterior, sem o problema da concorrência que citamos anteriormente.

&notafiscal.Load(&notafiscalId)
&notafiscal.NotaFiscalData = &Today
&notafiscal.ClienteId      = &ClienteId
&notafiscal.Produto.Item(1).ProdutoId = &ProdutoQtd
&notafiscal.Produto.Item(1).NotaFiscalProdutoQtd = &NotaFiscalProdutoQtd
&notafiscal.Save()
commit

 

Ou seja, só pela razão da inserção ocorrer apenas em uma única operação, já é melhor que o modelo anterior. Porém o modelo tem algumas coisas estranhas a primeira vista, como por exemplo, as duas linhas abaixo.

&notafiscal.Produto.Item(1).ProdutoId
&notafiscal.Produto.Item(1).NotaFiscalProdutoQtd


Referindo-se à estrutura da Transação temos a equivalência dos elementos da seguinte forma, Produto é o subnível da transação e NotaFiscalProdutoQtd e ProdutoId são atributos do segundo nível.


E o número entre parenteses Item(1), este equivale ao item a ser inserido na nota, 1, 2, 3, ....

'update' de um item


Este trecho de código pode ser utilizado para realizer o update de um item da nota fiscal.

&notafiscal.Load(&notafiscalId)
&notafiscal.Produto.Item(&item).ProdutoId = &NovoProdutoQtd
&notafiscal.Produto.Item(&item).NotaFiscalProdutoQtd = &NovoNotaFiscalProdutoQtd
&notafiscal.Save()
commit


'delete' um item


Para remover um item da nota fiscal poderíamos programar:

&notafiscal.Load(&notafiscalId)
&notafiscal.Produto.Remove(&itemaremover)

&notafiscal.Save()
commit


Percorrendo os itens

O exemplo a seguir é útil para percorrer a lista para realizar uma operação qualquer, como a  atualização dos dados. No caso, altera-se a quantidade de certo produto previamente inserido na nota fiscal. Para isso é necessário criar uma variável &item do tipo NotaFiscal.Produto. e com esta realizar uma operação for...in


&notafiscal.Load(&notafiscalId)
for &item in &notafiscal.Produto
   if &item.ProdutoId = &ProdutoId
      &item.NotaFiscalProdutoQtd = &NotaFiscalProdutoQtd
   endif
endfor
&notafiscal.Save()
commit


Observe que a atualização ocorre na variável &item, e ao realizar o save()commit, os dados são gravados na nota.



Chega por hoje,


Business component é um recurso indispensável, você não acha? Eu sim, e vou além, sem ele estamos perdendo tempo.  Mesmo que de vez em quando temos certos enroscos pra resolver, algumas mensagens estranhas, creio que deixar a transação cuidando da inserção, atualização e eliminação dos registros nas tabelas é um bom negócio.  Claro, que essa é apenas minha humilde opinião.

Tem mais exemplos em:

http://wiki.gxtechnical.com/commwiki/servlet/hwiki?Business+Component+Samples,

quarta-feira, 3 de abril de 2013

Domínios Enumerados 'Desbloqueados'

Domínio enumerado é um recurso muito interessante, creio que você já o esta utilizando, pois podemos criar listas com valores constantes e pré-definidos, e com isso eliminar definitivamente as constantes da programação, tornando o código final mais legível, mais atraente, até parece que fica mais inteligente. Mas por outro lado, ao decidir utilizar esse recurso, todas as operações sobre o atributo ou variável do tipo domínio ficam reféns das definições iniciais.

Se por exemplo definirmos um domínio enumerado do tipo Status, com os valores Aberto e Fechado, como apresentado abaixo, somente poderemos manipular valores, para os atributos e variáveis definidas nesse tipo, de acordo com esse conjunto.


Isso significa o seguinte, se a variável &status abaixo, definida do tipo Status, somente aceita atribuições do tipo:

&status = Status.Aberto

Que, claro, é exatamente o objetivo desse recurso, melhorar a sintaxe e definir conteúdos limitados, e previamente definidos.

Ok, mas o que acontece quando precisamos designar para &status valores a partir de outros tipos, numeric, character, ...

&status = 1

Isso não será possível (pelo menos da forma habitual).

'Desbloqueando' Domínios Enumerados


O termo 'desbloqueando' é meio forte para definir esse assunto, mas achei que se adequava muito bem a esse contexto.  A situação que me ocorreu foi a necessidade de guardar o valor desta variável &status na sessão, e claro, nesse local somente temos a possibilidade de armazenar textos.

&websession.set("STATUS", &Status.ToString())

Portanto, para se obter o valor convertido para texto, nada mais simples que um ToString().

A recuperação do valor foi o desafio aqui, pois como faríamos,  pois a variável não aceitaria valores numéricos convertidos como na expressão abaixo.

&status = &websession.Get("STATUS").toNumeric()

A saída é uma operação, meio escondida na documentação chamada Convert, que pode ser realizada da seguinte forma:


&status = Status.Convert(&websession.Get("STATUS").ToNumeric())

Meio estranho, mas funciona, o resultado será a conversão do valor numérico pelo valor respectivo no conjunto.