segunda-feira, 9 de maio de 2016

Revoluções silenciosas...

Certas funcionalidades do Genexus mudaram silenciosamente na Ev3, nenhum alarde foi feito, talvez devido a operação lava jato, no Brasil, estejamos nos acostumando com noticias explosivas e ruidosas, e quando a coisa é mais discreta não damos a devida atenção. Desta vez acredito que Genexus nos entregou um modelo um pouco mais flexível, portanto, melhor.

Estou falando sobre a determinação da tabela base e estendida, tema fundamental para quem desenvolve sistemas no Genexus, e que já tivemos a oportunidade de apresentar aqui no Genexando em dois artigos.


O fato é que Genexus define a tabela resultante (tabela base) que será utilizada para recuperar registros quando programamos um for each, e também as tabelas que irão complementar a informação que desejamos, definindo um conjunto chamado tabela estendida, que é a soma da tabela base e das demais complementares.


O que devemos fazer em um comando for each é dizer quais atributos desejamos, a ferramenta faz o resto.
 
Tomemos como exemplo o modelo apresentado acima. Se programarmos um for each, como o apresentado a seguir, com os atributos ClienteNome, CidadeNome e PaisNome, teremos como tabela base a Cliente e a estendida Cliente-Cidade-Pais.




Não será necessário fazer nada, para que tenhamos uma pesquisa completa na tabela Cliente com o nome da cidade e país incluída na apresentação, este modelo é natural no Genexus, e muita gente utiliza isto sem perceber o recurso.


A revolução ocorreu no seguinte sentido, uma nova cláusula no for each foi entregue, a base transaction, ou seja, podemos informar a transação que originará a tabela base do modelo. Desta forma poderíamos programar de forma explicita a transação Cliente no for each. Para transações com dois níveis teríamos que colocar o nome da transação e o nome do segundo nível, por exemplo, NotaFiscal.Produto. Algo como:



O fato é que podemos alterar a base transaction do for each para apontar para outra tabela qualquer, e aqui apresentamos o efeito da revolução. Se por exemplo, dissermos que a base transaction do exemplo seja a Cidade, nos Genexus anteriores teríamos um erro no for each, pois o Genexus não alcançaria o ClienteNome, e como resultado, um ruidoso erro indicando um não relacionamento entre os atributos.

Na versão EV3 do Genexus, o erro de não relacionamento não ocorrerá e em seu lugar teremos um aviso que o ClienteNome não poderá ser alcançado. Observe que a tabela base do exemplo passa a ser Cidade, como determinamos no for each Cidade, e Cliente passa a não fazer parte da solução.


Podemos levar este cenário ao extremo, ao apontarmos como tabela base uma que não poderá ser resolvida por meio do conceito da tabela estendida, façamos uma pequena modificação no modelo, acrescentando um conceito de Empresa que possui Clientes.



Se pegarmos o mesmo for each anterior e apontarmos para Empresa, nenhum atributo poderá ser alcançado pelo conceito da tabela estendida, e o Genexus será compreensivo e não apresentará nenhuma mensagem de erro, apenas avisos.



Portanto, muita atenção na leitura do Navigation View para não deixar as mensagens de warning passarem desapercebidas. Acredito que este novo modelo ficou melhor, porém exigirá mais atenção do desenvolvedor.

Apoiemos as revoluções silenciosas, e também as ruidosas, desde que sejam importantes.


domingo, 17 de abril de 2016

OWASP em Brasilia!


A nova liderança do capítulo da OWASP em Brasília convida para o primeiro evento de 2016 GRATUITO, de interesse de desenvolvedores, profissionais de segurança da informação, estudantes das áreas de computação e Tecnologia da Informação e demais interessados no assunto.

O 1° Encontro OWASP 2016 será um evento de Segurança da Informação, mais especificamente Segurança em Aplicações, que acontecerá no dia 04 de Junho de 2016
 em Brasília, DF. O evento contará com profissionais da área de Segurança da Informação de diversos Estados, como também diversos membros da OWASP. 

sexta-feira, 15 de abril de 2016

Login

Pergunta recorrente pelos desenvolvedores iniciantes, como desenvolver um login?
Esta é o primeiro muro que um desenvolvedor sofre ao se deparar com o Genexus, e muitas vezes a dificuldade não está na compreensão do processo em si, mas na escolha dos elementos necessários para realizar a operação.

Me desculpem os veteranos, mas no intuito de contribuir com os recém-nascidos, vamos responder isso de uma vez por todas, rs.

Use o GAM

A melhor opção que você poderá desenvolver é utilizando o GAM, pois é um recurso que já está disponível, foi desenvolvido mediante o protocolo OAUTH e protegerá todos os seus objetos expostos incluindo webservices.

Simples de utilizar, bastando nas Preferences ligar o Enable Integrated Security=Yes, em seguida trabalhar na configuração dos perfis (Roles) e usuários (Users).

O GAM poderá se comunicar com sistemas de login existentes por meio dos métodos de autenticação externa, que utilizará um webservice para conectar ao seu sistema remoto.

Maiores informações

Porque precisamos de Login?

Para os teimosos que pretendem escrever seus próprios mecanismos, vamos a uma estrutura básica.

Fazer login significa fornecer informações suficientes para que o script em execução no servidor possa identificar a pessoa que está utilizando o sistema. Desta forma será necessário ter algumas informações para que o controle possa ser efetuado. Previamente será necessário cadastrar as pessoas que serão identificadas, sendo que normalmente necessitamos de duas informações para proceder a identificação: usuário ou email e a senha. Uma transação serve a esta finalidade.
 

Algumas chaves possíveis para representar usuários:
  • Email: alguns sistemas utilizam email para identificar uma pessoa, o que não deixa de ser uma boa estratégia porém, muitas pessoas preferem ficar trocando de caixa de email de tempos em tempos, temos também pessoas que utilizam email de outra pessoa, como por exemplo, a namorada apaixonada que utiliza o email do namorado (acuado), claro que com primeiras intenções.
  • CPF: para os brasileiros temos o numero do CPF que também pode ser uma boa opção de chave, porém temos também algumas dificuldades, principalmente para pessoas que utilizam o CPF de terceiros (o filho que utiliza o CPF do pai ou do avô), e estrangeiros também não o possuem.
  • Palavras simples: apesar das críticas ainda acho que uma palavra definida pela própria pessoa seja a melhor estratégia para usuários. Colocando a palavra como chave primária teremos a garantia de que não será repetida, portanto, temos uma boa e livre opção.]
Teremos também a necessidade de identificar objetos (programas), perfis e associar tudo isso ao usuário.

 
 Veja que a complexidade vai aumentando, por isso a recomendação pelo GAM.

Processo de Login

Quanto uma pessoa utiliza um navegador de internet para se comunicar com um servidor Web se estabelece um registro da conexão que é a denominada chave de sessão. Trata-se de um valor randômico que inclui letras e números, único e que vai permitir entregar a comunicação de forma adequada para o requisitante.


A chave de sessão é criada  com ou sem operações de login, ou seja, é uma característica da Web, bastando chamar uma página ou script, que a mesma será criada. Localmente se criará um cookie e no servidor uma websession. Na imagem acima a chave é apresentada na variável Valor.

O servidor Web armazena esta chave em uma porção de memória chamada de websession, juntamente com outras informações que permitem estabelecer o contato com o cliente remoto. O endereço IP, e principalmente a data e hora da última comunicação com o servidor, de forma que depois de um certo tempo limite de inatividade (Time Session) o servidor pode destruir as sessões que expiraram (normalmente a sessão dura 20 minutos).

O processo de login normalmente utiliza o conceito do websession para armazenar alguma informação que determine que o cliente remoto passou pelo processo de identificação, para fins de controle de autorização para acesso aos objetos, normalmente o Perfil de acesso. A este perfil associa-se os objetos que possui privilégio de acesso, e por ai vai.

Operação de Login


A operação de login é simples, bastando um webpanel com duas variáveis e um botão.


E ao pressionamento do Confirm, a avaliação para saber se a pessoa informou corretamente os dois valores.

Event Enter
    &erro = ''
    for each
        where UsuarioId = &UsuarioId
        if UsuarioSenha <> &UsuarioSenha
            &erro = 'Senha incorreta'
        endif
    when none
        &erro = 'Usuário não encontrado'       
    endfor

    if &erro.IsEmpty()
        &websession.set('Usuario', &UsuarioId)
        Inicial()

    else
        msg(&erro)
    endif
Endevent


Observe que se o usuário e a senha estiverem corretos a página de ingresso no sistema Inicial(), será chamada, caso contrário a mensagem indicativa será exibida.

Na página Inicial() e em todos os objetos será necessário programar uma avaliação para se saber se existe alguma coisa registrada na variável Usuario na websession.

Event Start
    if &websession.get('Usuario').IsEmpty()
        Login()
    endif

Endevent






Esta é a estrutura mais básica que podemos construir. Falta ainda identificar o perfil, mas creio que daqui pra frente fica simples.

Considerações

Apesar da simplicidade temos muitas implicações relacionadas neste processo, recomendo inclusive que você estude um pouco a respeito da OWASP TOP 10, que trata dos problemas de vulnerabilidade que nossas aplicações poderão enfrentar.

  1. Recomenda-se que dados sensíveis como a própria senha do usuário seja gravada no banco em forma encriptada, praticamente dados pessoais devem sempre ser protegidos. Top A6: Exposição de dados sensíveis.
  2. A comunicação entre o navegador de internet e o servidor tem que ocorrer de forma encriptada por meio de um certificado SSL, e com protocolo HTTPS. Se não for desse jeito, qualquer pessoa poderá interceptar a comunicação e ler o usuário e senha transmitidas. Top A5 – Configuração Incorreta de Segurança, A2 – Quebra de autenticação e Gerenciamento de Sessão.
  3. Se não for avaliado o acesso em cada programa, e muitas vezes a verificação também tem que envolver os dados apresentados, estaremos quebrando mais um protocolo de segurança. Top A7 – Falta de Função para Controle do Nível de Acesso
  4. Normalmente nossas interfaces de login são submetidas a um mecanismo de invasão por força bruta onde um script busca criar as senhas para tentar ingressar. Deve haver um mecanismo de proteção para se evitar que sejam realizadas chamadas indiscriminadas em uma única chave de sessão. Uma estratégia é incluir um controle do tipo Captcha, para identificar um usuário humano. http://marketplace.genexus.com/product.aspx?captcha,en
  5. Os navegadores de internet tem a péssima funcionalidade de lembrar os valores de login. Se o procedimento não for corretamente implementado, poderá haver a possibilidade de um usuário ingressar no login de outro.
  6. Controles de segurança implementados em Javascript poderão ser facilmente quebrados, bastando o usuário desligar o privilégio de execução no seu navegador. Toda operação de segurança deverá ocorrer no servidor e não no cliente.

Poderíamos ficar discorrendo sobre esse assunto por várias páginas, mas acho que já deu pra dar uma noção. Eu particularmente acho que o GAM é a melhor solução, pois não precisaremos programar nada, e teremos mais tempo para desenvolver outras questões importantes relacionadas com segurança, que podem muitas vezes inviabilizar o sistema na web.

Este tema é profundo e requer não apenas atenção para se criar uma bela interface para realizar o login.

Bom trabalho!

sexta-feira, 8 de abril de 2016

Sobre Blob e Images

Carregar imagens diretamente no banco de dados é uma operação bastante interessante e prática utilizando os tipos Image e Blob no Genexus. Porém, quais as diferenças e até onde chegamos com esses dois recursos.

Parece até aquela briguinha manjada dos super heróis, aquela coisa meio armada das lutas de vale tudo dos anos 80. Falo isso porque os dois recursos são ótimos e não competem entre si, podem inclusive encontrar várias áreas em que se conectam, e cada um vai auxiliá-lo a desenvolver excelentes recursos.

Porém, antes de avançarmos, vamos revisar os artigos já apresentados sobre esses dois assuntos, aqui do Genexando.

Manipulando Imagens

  • http://www.genexando.com/2009/12/tratando-imagens.html
    Neste artigo chegamos a conclusão que o tipo Image nos ofereciam recursos que permitiam a carga da imagem, e principalmente sua posterior recuperação por meio do image.Link(), desta forma podemos ligar a imagem a um controle, ou programar o acesso por meio de <img src='...' >
  • http://www.genexando.com/2015_10_01_archive.html
    Neste artigo mais recente, falamos a respeito de resoluções, dimensões de imagens e também passamos rapidamente sobre o armazenamento com Blob e Image.

Manipulando Blob


O que devo escolher: Blob ou Image?

Apesar de diversos artigos ainda não tivemos um que respondesse definitivamente a respeito deste assunto. Ambos os tipos permitem armazenar dados binários no banco de dados. Então porque precisamos de dois tipos distintos? Qual é mais indicado em cada situação?

Image 

É destinado a armazernar e recuperar imagens, aceitando as extensões:  jpe, jpg, jpeg, gif, png, bmp, ico, svg, tif, tiff, ai, drw, pct, psp, xcf, psd, raw. Desta forma arquivos do tipo pdf, doc, xls, não serão aceitos.

É disponibilizado uma série de propriedades que facilitam a operação da imagem.


Image.FromURL()
Permite carregar uma imagem a partir de uma URL, bastando informar o local onde a mesma se encontra publicada, como por exemplo o logotipo do Genexando, que pode ser carregado em uma variável &image.

Event 'fromurl'
    &image.FromURL('http://1.bp.blogspot.com/-m1NiBb4VqsA/UXvcMgc9YpI/AAAAAAAAAsc/kUip66gYUGk/s1600/genexandologo.png')
Endevent


Image.FromImage()
Carrega uma imagem a partir de outra que já se encontra carregada na Kb, na área Customization - Images, como por exemplo a ActionExport, que representa um XLS.

Event 'fromimage'
    &image.FromImage(ActionExport)
Endevent


*** Inverti a ordem e coloquei uma URL em um FromImage e funcionou também, portanto, tanto FromImage como FromUrl aceitam imagens de URLs externas.


Image.ImageURI, Image.ImageName, Image.ImageType
Para quem precisa recuperar a imagem a partir de uma URL ou fisicamente no disco, é necessário compreender como a imagem carregada no BD se transforma novamente em um arquivo no servidor. Em nossa Kb temos uma pasta chamada PUBLICTEMPSTORAGE\MULTIMEDIA que é utilizada para construir os objetos temporários, como as imagens, por exemplo.

Image.ImageURI: devolve o caminho via servidor web até a imagem.
http://localhost/nomekb/PublicTempStorage/multimedia/nomekb_229a89cef51d48579586cdcf30d6ad85.png

Image.ImageName: devolve o nome da imagem, sem a extensão.
nomekb_229a89cef51d48579586cdcf30d6ad85

Image.ImageType: devolve o tipo ou extensão da imagem
png 
   
*** Se for necessário obter o caminho físico até a imagem, poderemos utilizar o caminho conhecido até a pasta PublicTempStorage\multimedia e acrescentar o ImageName e ImageType para completar.

Z:\Models\TESTE\EV3\Extensao\CSharpModel\web\PublicTempStorage\multimedia

*** Os valores ImageName, ImageType e ImageURI somente quando referenciarmos um atributo armazenado em uma tabela, isto devido a necessidade de se reconstruir a imagem em formato de arquivo físico novamente para ser devolvida para o navegador. Se a operação envolver apenas a carga de uma imagem em uma variável, a mesma ficará no somente no navegador, desta forma não teremos as informações até que ocorra a carga da imagem a partir do servidor, ou seja, a mesma for armazenada e posteriormente recuperada.

Blob

Blob é mais flexível em termos de carga pois aceitam qualquer tipo de arquivo, desde que se estabeleça um atributo para armazenar a extensão na propriedade File Type Attribute. Isto quer dizer se definirmos esta propriedade para um atributo do tipo Character(4), teremos a possibilidade de armazenar qualquer coisa, uma vez que para reconstruir o objeto a extensão será variável e recuperada deste atributo.

Assim como ocorre com as imagens os atributos do tipo Blob precisam se transformar novamente em arquivos físicos, para que possam ser enviados aos navegadores, e o mesmo é construido na pasta PUBLICTEMPSTORAGE. Com atributos do tipo Blob as informações não são tão explicitas quanto as definidas no tipo Image.

Blob.ToString() ou link(Blob)
A operação retorna o caminho físico até o arquivo reconstruído para ser devolvido ao navegador. Neste caso o nome do arquivo original não será mais considerado, sendo gerado novos arquivos aleatórios, abaixo um exemplo.

Z:\Models\TESTE\EV3\Extensao\CSharpModel\web\PublicTempStorage\usercontrol5863015.pdf

&URL = pathtourl(Blob)
Esta operação é meio obscura na documentação, mas muito importante pois devolve o link utilizado pelo navegador de internet para enviar o arquivo gerado pelo Genexus. Está documentada aqui http://wiki.genexus.com/commwiki/servlet/wiki?9563,PathToURL+function,. O resultado devolvido é algo semelhante ao apresentado a seguir.

http://localhost/nomekb/PublicTempStorage/usercontrol5863015.pdf



Tamanho do Blob: &File.GetLength()
Outra operação interessante que pode realizada no Blob e igualmente obscura na documentação é a obtenção da dimensão do Blob por meio de um tipo File, a operação GetLength() devolve a dimensão em bytes do arquivo. A documentação encontra-se aqui http://wiki.genexus.com/commwiki/servlet/wiki?6915,File+data+type,

&File.Source  = Blob.ToString()
&size = &File.GetLength()


Resultado:
12311

Tamanho do Blob: &File.GetName()
Utilizando o File também será possível obter o nome do arquivo gerado pelo Genexus. Com este nome também será possível chegar a URL da imagem.

&File.Source  = Blob.ToString()
&filename = &File.GetName()


Resultado:

usercontrol1740234.pdf

Base64 String

Os navegadores de internet conseguem entender um formato chamado Base64 String que representa uma imagem, porém codificada num chamado encode. Neste formato a imagem é gerada como uma string, no seguinte formato, apresentado de forma reduzida.

data:image/png;base64,iVBORw0KAACrQAAAmICA...WgA5ErkJggg==

O modelo foi implementado como uma maneira de reduzir a comunicação entre o servidor e o cliente, e não nos cabe aqui discutir o que é melhor ou pior, porque a imagem pode ser alcançada pela sua URI ou por uma string codificada no formato Base64.

A seguir alguma documentação sobre este tema:

Blob.FromBase64String()
Blob.ToBase64String()
Os Blobs incluem métodos que transformam uma string em Base64 em um Blob (Blob.FromBase64String()) ou um Blob em Base64 (Blob.ToBase64String()). Isso é interessante porque Image não possui este recurso, somente Blob.

Se for necessário uma Image neste formato é possível converter em Blob e utilizar o recurso da conversão Base64String().  Para isso será necessário que a Image e Blob sejam do mesmo tipo, ou seja, compartilhem do mesmo MIME type.

&Blob  = &Image
&string = &Blob.ToBase64String()

Ver documentação em http://wiki.genexus.com/commwiki/servlet/wiki?15204,Image+data+type

Redimensionamento?

Esta é uma boa pergunta, pois não temos como redimensionar a imagem quando realizamos o upload, e este é um fator muito importante visto que os aparelhos atuais geram imagens quase sempre desnecessariamente muito grandes. Imagens grandes muitas vezes não vão atender nossa necessidade na grande maioria dos casos, normalmente o que ser faz é reduzir a imagem a uma dimensão predeterminada.

Para smartdevices temos uma propriedade para escalar a imagem, Scale Type Property


Voltemos outra hora para tratarmos especificamente deste assunto.