none
Wcf Impersonate através de Domínio, Usuário e Senha! RRS feed

  • Pergunta

  • Boa Tarde pessoal,

    gostaria de realizar um Impersonate no meu serviço pois a conexão de meu banco de dados é integrated, só que, gostaria de passar os dados para que isso seja realizado, é possível?

    Exemplo: Uma máquina fora do dominio, passe o dominio, usuario e senha, e eu consiga realizar o impersonate para aquele usuário para aquele serviço.

    Abs
    Marcel Piva
    terça-feira, 29 de setembro de 2009 20:30

Respostas

  • Boas Marcel,

    Quando você habilita o recurso de impersonation do WCF (http://www.israelaece.com/post/WCF-Seguranca.aspx), obrigatoriamente as credenciais do usuário cliente devem ser passadas para o serviço, que por sua vez, executará a operação em cima das credenciais do respectivo cliente. Mesmo que você opte por ter um maior controle do impersonation (de forma imperativa), ainda sim o usuário tem que pertencer ao seu domínio/servidor.

    Agora, se você quiser validar um usuário que esteja além da sua rede, fora do domínio, então talvez poderia recorrer a API do sistema operacional, onde via PInvoke, você autentica o usuário, e se as credenciais forem válidas, faz o impersonation de forma manual, através do WindowsIdentity, assim como eu mostro aqui: http://www.israelaece.com/post/ExecutionContext.aspx. Atente-se apenas porque aquele código tem um bug, ou seja, o método Impersonate retorna a instância de uma classe chamada WindowsImpersonationContext, que representa o usuário anterior ao processo de impersonation. O ideal é que você invoque o método Undo ou envolve-lo em um bloco using, para garantir que o impersonation seja desfeito quando o processo acabar ou se alguma exceção for disparada. O exemplo fica:

    IntPtr token;
    if (LogonUser("Teste", "aqui é o dominio", "123456", 2, 0, out token))
    {
        try
        {
            using (WindowsIdentity.Impersonate(token))
            {
                //tarefas
            }
        }
        finally
        {
            token = IntPtr.Zero;
        }
    }

    Como você pode notar, isso não é nativo ao WCF. Repare que eu peguei uma parte de um artigo distinto ao WCF, mas que talvez funcione como quer. É importante que você teste.
    http://www.israelaece.com
    • Marcado como Resposta Piva, Marcel terça-feira, 29 de setembro de 2009 23:18
    terça-feira, 29 de setembro de 2009 22:01
    Moderador
  • Boas Marcel,

    Como a autenticação acontece antes, quando chega no momento de executar a operação você já não deverá ter mais o contexto.

    Acredito que o impersonation deve acontecer dentro da operação. Você tem a propriedade PrimaryIdentity (OperationContext.Current.ServiceSecurityContext.PrimaryIdentity) para ter acesso as credenciais do usuário atual. Você pode validar o usuário como está fazendo, ou seja, utilizando UserNamePasswordValidator através do LogonUser.

    Uma vez que a autenticação retorna como verdadeira, você armazena em algum lugar o token. Com isso, dentro da operação, onde precisar da impersonation, você faz:

    using(WindowsIdentity.Impersonate(AlgumObjeto.TokenDoUsuarioAutenticado))
    {
        //tarefas
    }
    http://www.israelaece.com
    • Marcado como Resposta Piva, Marcel quarta-feira, 30 de setembro de 2009 14:03
    quarta-feira, 30 de setembro de 2009 02:56
    Moderador

Todas as Respostas

  • Boas Marcel,

    Quando você habilita o recurso de impersonation do WCF (http://www.israelaece.com/post/WCF-Seguranca.aspx), obrigatoriamente as credenciais do usuário cliente devem ser passadas para o serviço, que por sua vez, executará a operação em cima das credenciais do respectivo cliente. Mesmo que você opte por ter um maior controle do impersonation (de forma imperativa), ainda sim o usuário tem que pertencer ao seu domínio/servidor.

    Agora, se você quiser validar um usuário que esteja além da sua rede, fora do domínio, então talvez poderia recorrer a API do sistema operacional, onde via PInvoke, você autentica o usuário, e se as credenciais forem válidas, faz o impersonation de forma manual, através do WindowsIdentity, assim como eu mostro aqui: http://www.israelaece.com/post/ExecutionContext.aspx. Atente-se apenas porque aquele código tem um bug, ou seja, o método Impersonate retorna a instância de uma classe chamada WindowsImpersonationContext, que representa o usuário anterior ao processo de impersonation. O ideal é que você invoque o método Undo ou envolve-lo em um bloco using, para garantir que o impersonation seja desfeito quando o processo acabar ou se alguma exceção for disparada. O exemplo fica:

    IntPtr token;
    if (LogonUser("Teste", "aqui é o dominio", "123456", 2, 0, out token))
    {
        try
        {
            using (WindowsIdentity.Impersonate(token))
            {
                //tarefas
            }
        }
        finally
        {
            token = IntPtr.Zero;
        }
    }

    Como você pode notar, isso não é nativo ao WCF. Repare que eu peguei uma parte de um artigo distinto ao WCF, mas que talvez funcione como quer. É importante que você teste.
    http://www.israelaece.com
    • Marcado como Resposta Piva, Marcel terça-feira, 29 de setembro de 2009 23:18
    terça-feira, 29 de setembro de 2009 22:01
    Moderador
  • Legal Israel, eu já havia testado essa implementação e realmente funciona, porém, imaginei que pudesse resolver isto sem a necessidade do uso da API do Windows.

    Vou seguir adiante com essa implementação.

    Abraços


    Marcel Piva
    terça-feira, 29 de setembro de 2009 23:20
  • Poxa Israel, novos problemas!!!

    Seguinte....

    Implementei a autenticação em minha classe MyAuthentication : UserNamePasswordValidator que está em no assembly Framework.Security chamando o LogonUser através de outra classe reservada em NativeMethods.

    Quando aplico o Impersonate, tudo funciona como disse anteriormente, porém, aquele lance de realizar as tarefas e depois aplicar o Dispose não vai funcionar pra mim, pois essa classe MyAuthentication é utilizada pelo CustomCertificateValidator do WCF, pois então, após o Impersonate o meu aplicativo continua com seu fluxo até chegar a camada de acesso a dados, e então vezes ele aplica o Impersonate, vezes não aplica.

    Aindei lendo a respeito e me parece que o Impersonate é realizado por Thread, e talvez por isto deva estar se perdendo, então tentei o ImpersonateLoggedOnUser também nativo e funcionou um pouqinho melhor, mas ainda está maluco, ora funciona, ora não funciona.

    Tem idéia do que pode estar acontecendo ???

    Abs.,


    Marcel Piva
    quarta-feira, 30 de setembro de 2009 01:56
  • Boas Marcel,

    Qual o código que está utilizando? Dependendo, talvez algum código é executado em uma thread diferente, e o contexto de segurança não é propagado.

    Não entendi quando você fala que o Dispose não vai funcionar para você.
    http://www.israelaece.com
    quarta-feira, 30 de setembro de 2009 02:07
    Moderador
  • Humm.. veja só Israel... Vou tentar expor o fluxo da chamada a uma tabela.


    Existe uma classe chamada ServiceHelper<T> a qual crio um proxy do meu serviço utilizando ChannelFactory, através dele, passo o Usuario e a Senha pelo factory.Credentials.UserName.UserName = "xpto" e factory.Credentials.UserName.Password = "xpto", logo a seguir é chamado o método Validate da classe MyAuthentication : UserNamePasswordValidator, para este método foi implementado o seguinte:

    Separei os métodos nativos em uma classe chamada NativeMethods por conta do CodeAnalisys.

    IntPtr token;
    
    try
    {
        if (NativeMethods.LogonUser(userName, domain, password, 0, 2, out token)
        {
            WindowsIdentity.Impersonate(token);
    
            // Tentei também com o ImpersonateLoggedOnUser(token)
            // como citei no post anterior.
        }
    }
    finally
    {
        token = IntPtr.Zero;
    }


    Após isto, volta para o fluxo do WCF chamando a camada de negócio que está separada em alguns projetos.
    Business.Facade <> Business.Components <> [DataContracts] <> DataAccess

    Então em meu DataAccess ora o WindowsIdentity.GetCurrent() está com Impersonate, ora não está...

    Estou utilizando o DataContext para ter acesso aos dados.

    using (DataContext context = new DataContext("connectionstring...")
    {
        DbConnection connection = context.Connection;
    
        // Aqui é onde tenho problemas.
        // Ora funciona, ora não funciona a impersonificação.
        connection.Open();
    }

    PS: Talvez tenha erros nos códigos acima, fiz todo aqui no post.

    Abraços.

    Marcel Piva
    quarta-feira, 30 de setembro de 2009 02:30
  • Boas Marcel,

    Como a autenticação acontece antes, quando chega no momento de executar a operação você já não deverá ter mais o contexto.

    Acredito que o impersonation deve acontecer dentro da operação. Você tem a propriedade PrimaryIdentity (OperationContext.Current.ServiceSecurityContext.PrimaryIdentity) para ter acesso as credenciais do usuário atual. Você pode validar o usuário como está fazendo, ou seja, utilizando UserNamePasswordValidator através do LogonUser.

    Uma vez que a autenticação retorna como verdadeira, você armazena em algum lugar o token. Com isso, dentro da operação, onde precisar da impersonation, você faz:

    using(WindowsIdentity.Impersonate(AlgumObjeto.TokenDoUsuarioAutenticado))
    {
        //tarefas
    }
    http://www.israelaece.com
    • Marcado como Resposta Piva, Marcel quarta-feira, 30 de setembro de 2009 14:03
    quarta-feira, 30 de setembro de 2009 02:56
    Moderador
  • Muito bom, vou tentar isto amanhã.
    E darei um feedback!
    Obrigado por enquanto!
    Abs. e boa noite!

    Marcel Piva
    quarta-feira, 30 de setembro de 2009 03:00
  • Bom Dia Israel.

    Com meus testes aqui, armazenei uma variável estática em meu assembly Framework.Core chamada EnviromentSecurity.Token e a recupero na camada de acesso a dados.

    Funcionou sem problemas...

    Porém, terei diversos usuários acessando o serviço, isso será um problema não?? Pois o impersonate é no servidor e a variavel será estática e compartilhada com os usuários.

    Abs


    Marcel Piva
    quarta-feira, 30 de setembro de 2009 14:03
  • Bom Dia Israel.

    Com meus testes aqui, armazenei uma variável estática em meu assembly Framework.Core chamada EnviromentSecurity.Token e a recupero na camada de acesso a dados.

    Funcionou sem problemas...

    Porém, terei diversos usuários acessando o serviço, isso será um problema não?? Pois o impersonate é no servidor e a variavel será estática e compartilhada com os usuários.

    Abs


    Marcel Piva

    Pensei em criar uma lista e armazenar o Token e o usuário e buscar o usuário nessa lista para recuperar o token. O que acha?
    Marcel Piva
    quarta-feira, 30 de setembro de 2009 14:08
  • Boas Marcel,

    Acho que o mais ideal seria você criar um "principal/identity" próprio. Fica mais coerente.

    Repare neste artigo: http://www.israelaece.com/post/WCF-Seguranca-Autenticacao-e-Autorizacao-Customizadas.aspx

    Note que eu criei um XmlIdentity/XmlPrincipal e depois você "pluga" através do IAuthorizationPolicy. Você pode tentar aplicar as mesmas técnicas para "plugar" o que precisa.


    http://www.israelaece.com
    quarta-feira, 30 de setembro de 2009 14:25
    Moderador
  • Israel,

    eu tenho meu Identity e Principal implementados, porém, acho que terei problemas em disponibilizar a aplicação via ClickOnce para X tipos de sistemas operacionais por conta da utilização da API do Windows.

    O Identity e Principal estão implementados em Framework.Security. (Client/Server)
    E o restante referente ao WCF está em Framework.WCF.Security (Server)


    Marcel Piva
    quarta-feira, 30 de setembro de 2009 15:07
  • Boas Marcel,

    Não entendi. :)
    http://www.israelaece.com
    quarta-feira, 30 de setembro de 2009 15:31
    Moderador
  • Utilizo o IIdetntiy/IPrincipal em ambos lados Client/Server, não devo deixar o consumo da API (LogonUser) do lado do cliente.

    Então, botei isso na geladeira por enquanto, voltarei a ver isso depois, preciso dar andamento aqui.

    Darei mais detalhes depois.

    Obrigado pela força por enquanto.

    []s


    Marcel Piva
    quarta-feira, 30 de setembro de 2009 16:59