Mensagens One-Way não são assíncronas

by Israel Aece 31. July 2009 08:47

Uma das possibilidades que temos ao construir um serviço WCF, é a capacidade de enviar mensagens conhecidas como one-way. Quando você tem operações que não retornam valores e, principalmente, o cliente não está interessado no sucesso ou falha da mesma, podemos recorrer à esta técnica. Ela permite que o proxy envie a mensagem ao seu destino, sem esperar pela sua execução. Há um bloqueio mínimo do lado do cliente, que é apenas necessário para que o proxy mande a mensagem e o serviço a enfileire. A partir daí, o cliente está livre para dar continuidade na execução do programa.

Dependendo do problema que ocorra, o retorno pode ser imediato, como acontece quando o respectivo endpoint não é encontrado, e dificilmente você notará esse bloqueio. Já em outros casos, quando a quantidade de informação enviada ao serviço é muito grande, isso também pode demorar a retornar, o que pode gerar uma má experiência (bloqueio) ao usuário. Outro problema que é muito comum, é quando você define o modelo de sincronização como Single e utiliza um binding que dá suporte à sessões, obrigando ao serviço (dispatcher) assegurar a entrega das mensagens de forma ordenada, impossibilitando que outra mensagem seja lida até que o processamento da mensagem atual seja completada.

Uma forma de contornar esse tipo de problema é trabalhar com mensagens assíncronas, como é mostrado aqui. Isso evitará a necessidade do cliente ficar aguardando até que a mensagem one-way seja efetivamente entregue ao serviço, mas como um ponto negativo, que é a saturação do ThreadPool, dependendo do volume de requisições que são realizadas. Outra alternativa é definir o gerenciamento de instância do serviço como PerCall e com modo de sincronização Multiple, onde cada requisição será atendida por uma instância diferente e podendo mais que uma thread enviar as mensagens.

Tags: ,

WCF

A sua sessão expirou

by Israel Aece 28. July 2009 08:18

Um amigo me pediu uma ajuda para escrever a seguinte mensagem na página de Login de uma aplicação ASP.NET: "A sua sessão expirou. Efetue o login novamente.". Como ele está utilizando FormsAuthentication, pensei em analisar a existência da QueryString "ReturnUrl", que geralmente é adicionada como parte da URL, quando você tenta acessar algum recurso protegido e que você não esteja devidamente autenticado.

O problema desta técnica é que as vezes o usuário tem o endereço (nos "Favoritos", por exemplo) desta página restrita, e suponhamos que ele abra o navegador e tente acessá-la diretamente. O FormsAuthentication fará a parte dele, ou seja, redirecionará o usuário para a página de Login com a QueryString "ReturnUrl" contendo a página que ele tentou acessar. Se estiver analisando essa QueryString, a mensagem de "sessão expirada" será exibida, mas não faz sentido neste caso, já que é o primeiro acesso deste usuário.

Para tentar contornar esse problema, a solução foi a criação de um módulo, vinculando ao evento BeginRequest. Esse módulo será responsável por analisar a validade do cookie de autenticação, e se estiver expirado, redirecionará o usuário para a página de Login. Esse mesmo processo acaba sendo feito pelo próprio FormsAuthenticationModule, mas com este novo módulo, a ideia é nos antecipar ao FormsAuthentication e adicionar um novo item na coleção de QueryStrings, que especifica que a autenticação expirou. Esse valor será analisado pela página de Login, que baseando-se nele irá ou não exibir a mensagem em questão. Abaixo temos o módulo na íntegra:

using System;
using System.Web;
using System.Web.Security;

public class AuthenticationExpiredModule : IHttpModule
{
    private HttpApplication _app;

    public void Init(HttpApplication context)
    {
        this._app = context;
        this._app.BeginRequest += new EventHandler(this._app_BeginRequest);
    }

    private void _app_BeginRequest(object sender, EventArgs e)
    {
        if (this._app.Request.Path != FormsAuthentication.LoginUrl)
        {
            HttpCookie authCookie = 
                this._app.Request.Cookies[FormsAuthentication.FormsCookieName];

            if (authCookie != null)
            {
                FormsAuthenticationTicket ticket =
                    FormsAuthentication.Decrypt(authCookie.Value);

                if (ticket.Expired)
                {
                    this._app.CompleteRequest();
                    FormsAuthentication.RedirectToLoginPage("Authentication=Expired");
                }
            }
        }
    }

    public void Dispose() { }
}

Depois de criado, precisamos registrá-lo no arquivo de configuração (Web.config) para que ele comece a fazer parte da execução:

<httpModules>
    <add
        name="AuthenticationExpiredModule"
        type="AuthenticationExpiredModule"/>
</httpModules>

E, finalmente, já na página de Login, verificando a existência da QueryString:

this.AuthenticationExpiredMessage.Visible = Request.QueryString["Authentication"] == "Expired";

Tags: ,

ASP.NET | Security

Utilizando o EF em Ambiente Parcialmente Confiável

by Israel Aece 16. July 2009 08:51

Estava utilizando a template de projeto Web Site para a criação de um site com uma vida curta, e que se destina a realizar uma tarefa temporária. Como ele deve acessar uma fonte de dados SQL Server para manipular alguns dados, optei por utilizar o Entity Framework como forma de acesso, ao invés do LINQ To SQL ou até mesmo do ADO.NET.

Em pouco tempo essa aplicação ficou pronta e tudo funcionava tranquilamente na máquina de desenvolvimento, até que decidi - erroneamente - configurar o Web.Config localmente. Entre essas configurações, uma delas foi ajustar o nível de segurança que a aplicação deverá rodar, que – teoricamente – não preciso nada mais do que o nível "Medium". Sendo assim, o meu arquivo Web.Config passou a ficar com a seguinte entrada:

<trust level="Medium" />

Ao tentar recompilar a aplicação no Visual Studio .NET, me deparo com o seguinte erro listado na IDE:

Type ‘System.Data.Entity.EntityDesignerBuildProvider’ cannot be instantiated under a partially trusted security policy (AllowPartiallyTrustedCallersAttribute is not present on the target assembly).

Ao abrir o arquivo Web.Config da aplicação, você notará que no elemento connectionStrings possuirá as referências para os arquivos que possuem as descrições das entidades e mapeamentos (CSDL, MSL e SSDL), acrescentado o prefixo "res://", que indica que eles serão adicionados ao assembly como Resource.

Além disso, você verá que existe um builder provider do tipo EntityDesignerBuildProvider, vinculado à aplicação. Essa classe é responsável por extrair as informações dos arquivos mencionados acima, e modificar os assemblies que estão sendo gerados, embutindo-as como Resources. Esse processo não pode ser executado em ambiente parcialmente confiável, já que a permissão necessária não será concedida à aplicação. Veja que a mensagem de erro informa que o Assembly onde está declarado este builder provider, não pode ser chamado por aplicações parcialmente confiáveis, pois a ausência do atributo AllowPartiallyTrustedCallersAttribute evita isso.

A transformação do arquivo EDMX (CSDL, MSL e SSDL) ocorre somente na primeira vez que se compila a aplicação (%windir%\Microsoft.NET\Framework\Versao\Temporary ASP.NET Files), e se nesse momento estiver com ela configurada como parcialmente confiável, assim como eu fiz acima, você irá obter o erro em questão. Se você optar por pré-compilar a aplicação em ambiente “FullTrust”, e fazer o deployment em ambiente parcialmente confiável, você não terá este problema. Isso é perfeitamente possível, já que as configurações de uma aplicação (Web.Config) não são compiladas.

Uma outra alternativa é utilizar o conceito de sandboxing, onde você isola o EDMX em uma Class Library, e faz referencia para ela no projeto Web. Como o arquivo EDMX estará embutido na DLL gerada, você não precisa mais das referências aos arquivos CSDL, MSL e SSDL na aplicação Web, e muito menos do build provider EntityDesignerBuildProvider no Web.Config. Neste caso, o ponto negativo é ter que gerenciar dois projetos e Assemblies.

Ainda há uma última alternativa neste caso, onde você extrai os arquivos CSDL, MSL e SSDL a partir do EDMX, e modifica a connectionString para apontar fisicamente para estes arquivos, que devem estar na mesma aplicação (talvez no diretório bin). Particularmente prefiro a opção da geração do Assembly a parte, que facilita a reutilização por várias aplicações e não corremos o risco de alguém, acidentalmente, excluir estes arquivos que são necessários para o Entity Framework trabalhar.

Tags: ,

ASP.NET | Data | Security

Security-Transparent Code

by Israel Aece 14. July 2009 19:06

Uma das grandes inovações que o .NET trouxe é a capacidade de conceder ou negar acesso à uma determinada aplicação (Assembly), independentemente do usuário que está acessando-a, sendo ou não um “Administrador” da máquina onde a aplicação está sendo executada. Esse novo conceito, presente desde a primeira versão do .NET Framework, é chamada de Code Access Security – CAS.

Esse recurso é extremamente rico em detalhes, onde podemos customizar quais direitos a aplicação terá baseando-se em condições que são avaliadas durante a execução da mesma.  Para compor o CAS, temos vários objetos, tais como: Permissions, Permissions Sets, Security Levels, Membership Conditions, Demands, etc. Ao mesmo tempo que isso é extremamente poderoso, a sua configuração torna-se complexa demais, principalmente quando você não tem um conhecimento intermediário sobre o seu funcionamento.

Um dos principais cenários onde precisamos fazer o uso destas funcionalidades, é quando estamos criando libraries (DLLs) para serem consumidas em ambientes parcialmente confiáveis, ou seja, você cria uma DLL para encapsular o acesso ao banco de dados, sistema de arquivos, etc., e quer consumí-la em uma aplicação que não está sendo executada em “FullTrust” (o que muitas vezes acontece). Nesses casos, se gasta mais tempo testando e configurando as políticas com seus respectivos níveis de segurança, do que na tarefa que a DLL irá executar.

A partir do .NET Framework 2.0, a Microsoft trouxe um recurso chamado de Security-Transparent Code. Essa funcionalidade facilita a escrita e configuração de DLLs que serão consumidas por aplicações que rodam em ambientes parcialmente confiáveis. A ideia desta técnica é você refatorar o código (seja em assemblies, classes ou métodos) em “código transparente” e “código crítico”, com o propósito de separar o código que roda como parte da aplicação do código que roda como parte da infraestrutura, criando uma espécie de barreira entre essas duas seções.

O “código transparente” não fará nada (do ponto de vista da segurança) além do que lhe foi concedido, ou seja, não acessará nenhum tipo de código que exija a elevação dos privilégios atuais. Ao precisar executar uma tarefa que exige um nível de segurança mais elevado, entre em cena o “código crítico”, que rodará em “FullTrust”. Neste caso, o “código transparente” irá invocar algum membro do “código crítico”, que possuirá todos os privilégios necessários, independentemente de quais o cliente possua, evitando elevar os privilégios do “código transparente”.

Os códigos marcados como “transparente” terão algumas restrições, tais como: elevar os privilégios (Asserts) que lhe foram concedidos; efetuar Link Demands (tudo será transformado em “full demand”) e qualquer código unsafe que ele eventualmente execute em “código transparente”, também efetuará uma “full demand”.

Observação: A diferença entre Link Demand e Demand (ou Full Demand) é que a primeira ocorre somente em tempo de compilação (JIT) e apenas irá verificar se o chamador imediato tem a permissão requerida. Já a segunda opção, Demand, executará a stack walk, ou seja, irá percorrer todos os elementos da stack, verificando se todos eles possuem a permissão solicitada.

Depois desta reestruturação (física ou virtual), a CLR irá assegurar (em tempo de execução) que o “código transparente” não poderá efetuar qualquer elevação de privilégios, garantindo que tudo o que o ele tentar executar, exigirá que toda a call stack tenha a respectiva permissão. E, para customizar a comportamento desta funcionalidade, a Microsoft disponibiliza alguns atributos:

  • SecurityTransparentAttribute: Somente permitido em nível de Assembly, diz ao runtime que ele será “transparente”, ou seja, não poderá executar qualquer ação que exija a elevação de privilégios. É importante dizer que “código transparente” não pode invocar “código crítico”.
  • SecurityCriticalAttribute: Quando utilizado em nível de Assembly, indica que ele pode ter “código crítico”. Isso vai depender da opção que você define ao utilizar o enumerador SecurityCriticalScope. A opção Everything indica que todo o código será considerado “código crítico”, enquanto a opção Explicit, determina que somente o membro onde o atributo está sendo aplicado será considerado como “código crítico”.
  • SecurityTreatAsSafeAttribute: Indica que o membro onde este atributo está sendo aplicado, pode ser acessado por outros membros que estão marcados com os atributos SecurityTransparentAttribute ou AllowPartiallyTrustedCallersAttribute. Não há nenhuma validação que evite a compilação quando você aplica este atributo em métodos públicos, e se você fizer isso, estes membros podem ser acessados através do “código transparente” tendo ter possíveis vulnerabilidades.

No exemplo abaixo, estamos definindo o atributo SecurityCriticalAttribute em nível de Assembly, dizendo que ele poderá conter “código crítico”. Com isso, somos obrigados a determinar qual método será “crítico”, utilizando o atributo SecurityCriticalAttribute ou o SecurityTreatAsSafeAttribute. Neste caso, optamos pela segunda opção, pois desejarmos que o método ReadFile possa ser invocado a partir de membro internos bem como externos, e com a configuração abaixo, será considerado “transparente”, e sem este atributo não seria possível.

[assembly: SecurityCritical]

static void Main(string[] args)
{
    Console.WriteLine(ReadFile());
}

[SecurityCritical]
[SecurityTreatAsSafeAttribute] 
private static string ReadFile(string filename)
{
    new FileIOPermission(FileIOPermissionAccess.Read, filename).Assert();
    //Ler o arquivo
}

Tags: ,

Security

Plataforma de Identidade

by Israel Aece 13. July 2009 17:10

A Microsoft anunciou hoje os nomes oficiais do projeto "Geneva":

Geneva Server -> Volta a ser Active Directory Federation Services - ADFS
Windows Cardspace Geneva -> Volta a ser Windows Cardspace
Geneva Framework -> Windows Identity Foundation - WIF

Já há a lista aqui de artigos que estou preparando para introduzir, desenvolver e fazer uso destas tecnologias em aplicações .NET.

Tags:

CSD | WIF

IIS, WCF e Partial Trust

by Israel Aece 9. July 2009 11:39

Há algum tempo eu falei sobre a possibilidade de invocar serviços WCF em aplicações parcialmente confiáveis. Mas ainda há um segundo cenário, que é a exposição de serviços através de aplicações ASP.NET Web Site/WCF Service. Para criar um serviço WCF, você não precisa necessariamente criar um tipo de projeto exclusivo como o WCF Service. Um projeto do tipo ASP.NET Web Site pode, tranquilamente, servir como host de um serviço WCF, e para isso, basta adicionar um arquivo *.svc e configurá-lo corretamente no Web.config.

Independentemente de qual das alternativas utilize, você poderá se deparar com uma restrição de segurança, e dependendo das funcionalidades (mais precisamente do binding e seus elementos) que utiliza, o serviço não rodará. Isso muitas vezes acontece quando você faz o deployment para um servidor, em que a configuração padrão do .NET Framework foi alterada (visando uma maior segurança). Uma das regras mais importantes que se deve ter ao configurar um servidor Web (IIS), é não permitir que as aplicações que rodem ali executem em "Full Trust". Para isso, se altera o arquivo Web.config (que está em nível de servidor), definindo o atributo level do elemento trust para "Medium" ou qualquer nível abaixo disso.

Neste ambiente, você poderá utilizar os bindings BasicHttpBinding, WSHttpBinding ou o WebHttpBinding, desde que eles estejam com a segurança desabilitada ou com a proteção em nível de transporte. O binding WSDualHttpBinding também não pode ser utilizado neste cenário, já que algumas tarefas que ele desempenha exige um nível de segurança mais elevado. Finalmente, para tentar resolver este problema, podemos fazer uso das técnicas mostradas pelo Juval Lowy neste artigo, disponibilizando alguns helpers para facilitar a criação de hosts em ambientes parcialmente confiáveis.

Tags: , ,

Security | WCF

Serviços Declarativos

by Israel Aece 9. July 2009 11:07

Uma nova funcionalidade que estará presente no WCF 4.0, é a capacidade de criar serviços de forma declarativa. Quando trabalhamos com WCF, tudo o que devemos fazer é criar uma Interface que representará o contrato, implementamos ela em uma classe que representará o serviço e, finalmente, criamos o código necessário para servir como hosting para o serviço.

A criação do hosting conseguimos fazer totalmente de forma declarativa, enquanto as outras duas, somente podemos utilizar o modelo imperativo (via C# ou VB.NET) para criá-los. A proposta da Microsoft é permitir que a criação de um serviço WCF seja feita totalmente (definição e implementação) de forma declarativa, ou seja, utilizando o XAML. Ainda há o que chamamos de "projeção de contratos", que permitirá separar a definição da representação (mensagens). Com isso, vamos ter apenas uma única definição, mas podendo ser representada de diferentes formas (SOAP, REST, etc.). Para mais detalhes sobre serviços declarativos, você pode consultar a documentação oficial desta funcionalidade.

Tags:

WCF

Utilizando Generics em Serviços

by Israel Aece 9. July 2009 09:31

Uma das grandes desilusões ao utilizar o WCF, é não conseguir utilizar tipos de dados genéricos nos serviços. Generics são tipos de dados exclusivos do .NET Framework, e utilizá-los viola os princípios da orientação a serviços, já que podem existir linguagens que não suportam essa funcionalidade. É importante dizer que isso não é uma limitação do WCF, mas sim do WSDL, que é o documento utilizado para expor os metadados para os consumidores.

O Visual Studio .NET/compilador não vão proibí-lo de utilizá-los nos contratos. O problema será na exposição das informações (via WSDL) para o cliente e, consequentemente, o proxy gerado possuirá tipos "estranhos". Por exemplo, se uma operação do contrato retornar ou receber um tipo genérico como Teste<int>, ao gerar o proxy, ele irá criar um tipo (classe) chamado TesteOfInt, substituindo os parâmetros genéricos do mesmo, pelo tipo especificado na operação. Isso pode piorar um pouco se o parâmetro genérico informado for uma classe, como por exemplo Teste<OutraClasse>, resultando no seguinte tipo do lado do cliente: TesteOfOutraClasseKdjwuy4p, ou seja, acrescentando uma informação randômica para evitar eventuais conflitos com outros tipos no mesmo contrato.

Felizmente você pode sobrescrever esse comportamento, definindo a propriedade Name da atributo DataContractAttribute. Nela você pode especificar o formato do nome que deseja expor no WSDL. Se notarmos no primeiro exemplo abaixo, o parâmetro {0} será substituído pelo tipo genérico especificado. Caso a classe Teste suportasse mais do que um parâmetro genérico, então você pode também especificá-los para compor o nome, assim como mostrado no segundo exemplo.

[DataContract(Name = "TesteCom{0}")]
public class Teste<T> { }

[DataContract(Name = "TesteComTipo{0}EComChave{1}")]
public class Teste<T, D> { }

Um outro ponto importante é com relação as coleções. Elas também são características da plataforma, e não devem ser expostas além do serviço. Ao utilizar coleções que implementam direta ou indiretamente as Interfaces IEnumerable<T>, ICollection<T> ou IList<T>, o WCF automaticamente irá convertê-las em um array do tipo especificado no parâmetro genérico (<T>), facilitando a interoperabilidade com outras plataformas.

Ainda falando em coleções, ao criar uma coleção customizada, você poderá fazer uso do atributo CollectionDataContractAttribute, permitindo customizar o nome do tipo da coleção (através da propriedade Name) a ser colocado no WSDL e, consequentemente, criado no cliente (trabalhando da mesma forma que a propriedade Name do atributo DataContractAttribute). Ao utilizar este atributo, ainda temos a garantia de que o WCF verificará a existência do método Add durante a carga do serviço, e não existindo, uma exceção do tipo InvalidDataContractException será lançada. Este método se faz necessário para remontar a coleção durante a deserialização da mensagem.

Como dito acima, as coleções são características da plataforma, e não devem propagar além dos limites do serviço. Mas em algumas situações, isso pode ser interessante. Quando você está compartilhando tipos entre o cliente e o serviço (exige .NET/WCF presente dos dois lados), você pode optar por criar o proxy respeitando e preservando essas coleções. Para que isso seja possível, basta você utilizar a opções /reference/collectionType do utilitário svcutil.exe. A primeira opção determina o Assembly (reference) onde está a coleção, enquanto a segunda opção determina qual é a coleção. É importante dizer que este recurso também está disponível quando a criação do proxy é feita pela IDE do Visual Studio .NET.

Tags: , ,

WCF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host