Configurar autentificación con certificado x509 de la FNMT o Dni-e

379 views
Skip to first unread message

apicito

unread,
Oct 20, 2015, 7:24:07 AM10/20/15
to symfony-es
Estoy intentando controlar el acceso a una parte de una aplicación Symfony2.7 mediante un certificado digital de FNMT. Consigo que el navegador me pida que seleccione uno pero no se como  tengo que manejar los datos del mismo para comprobar si es un usuario válido en la tabla de usuarios.
Siguiendo la informacion que he encontrado configuro el security.yml asi:
    providers:
        x509_provider
:
            entity
:
               
class: AppBundle:Cfg\Usuario
                property
: dni
    firewalls
:
        main
:
            anonymous
: ~

        xes
:
            pattern
: ^/xes
            x509
: true
            provider
: x509_provider

Pero no se como hacer la clase provider para gestionar el certificado. Es decir:
  • Comprobar que se ha seleccionado un certificado
  • Comprobar que certificado no está caducado
  • Extraer del certificado el CIF y con él buscar en la tabla de usuarios si existe.

No acabo de entender como gestiona Symfony el certificado y donde puedo introducir yo la lógica que necesito.

Se agradece cualquier comentario.

Gracias


apicito

unread,
Oct 21, 2015, 1:39:43 PM10/21/15
to symfony-es
He seguido otros manuales:
Using pre Authenticated Security Firewalls
How to Create a custom User Provider
y he creado las clases:
src/Acme/AppBundle/Security/User/WebserviceUser.php
y
src/Acme/AppBundle/Security/User/WebserviceUserProvider.php
esta última la he modificado para leer los datos del certificado con la función getDatosCertificado(), asi:
<?php

namespace AppBundle\Security\User;

//use AppBundle\Security\User\WebserviceUser;

use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;

class WebserviceUserProvider implements UserProviderInterface
{
   
public function loadUserByUsername($username)
   
{

echo
'estou no provider';

        $userData
= $this->getDatosCertificado();

       
if ($userData) {
            $username
= $userData["DNI"];
            $password
= '';
            $salt
= '';
            $roles
= array('ROLE_USER');

           
return new WebserviceUser($username, $password, $salt, $roles);
       
}

       
throw new UsernameNotFoundException(
            sprintf
('Username "%s" does not exist.', $username)
       
);
   
}

   
private function getDatosCertificado()
   
{
       
if ( isset( $_SERVER['SSL_CLIENT_S_DN'] ) ) {
            $datosCertificado
= array();
            $datos
= explode(',' , $_SERVER['SSL_CLIENT_S_DN']);
            $gn
= explode( '=' , $datos[1] );
            $datosCertificado
["APELIDOS"] = $gn[1];
            $sn
= explode( '=' , $datos[2] );
            $datosCertificado
["NOME"] = $sn[1];
            $dni
= explode( '=' , $datos[3] );
            $datosCertificado
["DNI"] = $dni[1];
           
return $datosCertificado;        
       
} else {
           
return null;
       
}

   
}

   
public function refreshUser(UserInterface $user)
   
{
       
if (!$user instanceof WebserviceUser) {
           
throw new UnsupportedUserException(
                sprintf
('Instances of "%s" are not supported.', get_class($user))
           
);
       
}

       
return $this->loadUserByUsername($user->getUsername());
   
}

   
public function supportsClass($class)
   
{
       
return $class === 'Ltx\AppBundle\Security\User\WebserviceUser';
   
}
}

el security.yml:
security:

    providers
:
        webservice
:
            id
: webservice_user_provider

    firewalls
:

        dev
:
            pattern
: ^/(_(profiler|wdt)|css|images|js)/
            security
: false

        xes
:
            pattern
: ^/xes
            x509:
                provider: webservice
                user: "SSL_CLIENT_S_DN"

    access_control:
        - { path: ^/
xes, roles: ROLE_ADMIN, requires_channel: https }
y el services:
services:
    webservice_user_provider
:
       
class: AppBundle\Security\User\WebserviceUserProvider

Y al acceder a la parte protegida por https obtengo el mensaje de uqe no se ha creado el token.
A Token was not found in the TokenStorage.

Alguna sugerencia de lo que estoy haciendo mal?
Gracias.

Carlos Cordova

unread,
Mar 24, 2016, 7:57:08 AM3/24/16
to symfony-es
Estimado, estoy viendo lo mismo y aun no lo resuelvo el problema...lo pudiste resolver??

Jose R. Prieto

unread,
Mar 24, 2016, 8:10:42 AM3/24/16
to symfo...@googlegroups.com

Es interesante lo que estás haciendo...
Mucho...

Qué opinas de liberarlo?

--
--
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Google Groups.
Para publicar en este grupo, envía un email a symfo...@googlegroups.com
Para darte de baja, envía un email a symfony-es+...@googlegroups.com
El resto de opciones puedes encontrarlas en http://groups.google.com/group/symfony-es?hl=es

---
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a symfony-es+...@googlegroups.com.
Para acceder a más opciones, visita https://groups.google.com/d/optout.

apicito

unread,
Mar 24, 2016, 9:08:51 AM3/24/16
to symfony-es
No creo que mi código sirve para utilizar como modelo, acepto todas las sugerencias que puedan mejorarlo, pero funciona en lo básico.
Primero, aclarar lo siguiente:
Consideraciones:
  • el nombre del usuario es su Documento de identidad, que obtengo del certificado a través de  $_SERVER['SSL_CLIENT_S_DN']
  •  Uso PDO para el acceso a Base de Datos
  • Almaceno en el token de session los permisos de acceso de los usuarios
 
 Me falta:
  • Capturar el código de la entidad desde la url
  • En el primer request comprobar la validez del certificado contra la autoridad correspondiente. Yo uso @firma y tengo que hacer una llamada a un webservice de esta plataforma.

Espero que a alguien le pueda servir de ayuda.

Si encontrais alguna mejora, por favor publicadla en este hilo.

Saludos.


security.yml:

security:

    providers
:
        x509Provider_Provider
:
            id
: x509Provider_Service

    firewalls
:


        dev
:
            pattern
: ^/(_(profiler|wdt)|css|images|js)/
            security
: false

        xes
:
            pattern
: ^
/xes
            x509:
                provider: x509Provider_Provider

                user: "SSL_CLIENT_S_DN"

    access_control:
        - { path: ^/
xes, roles: ROLE_ADMIN, requires_channel: https }

services.yml:

services:
    x509Provider_Service
:
       
class:         AppBundle\Security\User\x509Provider
        arguments
:     ["@db0"]

x509Provider.php:

<?php

namespace AppBundle\Security\User;

use Symfony\Component\HttpFoundation\RequestStack;


use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;

class x509Provider implements UserProviderInterface
{
   
protected $pdo;

   
public function __construct(\PDO $pdo)
   
{
        $this
->pdo = $pdo;
   
}

   
public function loadUserByUsername($username)
   
{
       
// $entId debe recuperarse do request
       
// $entId = '32085';
       
//-------------------------------------
        $entId
= '32085';

        $userData
= $this->getDatosCertificado();
        $username
= $userData["DNI"];
        $roles
= array();
        $nome
= $userData["NOME"]." ".$userData["APELIDOS"];
        $email
= "";
        $entidade
= array();
        $permisos
= array();        

        $ent
= $this->getEntidade($entId);        

       
if ($ent) {

            $entidade
= $ent;

            $usuariodb
= $this->getUsuarioDb($userData["DNI"], $entidade);

           
if ($usuariodb) {
                $username
= $userData["DNI"];
                $roles
= explode( ',' , $usuariodb[0]['USU_ROLES'] );
                $nome
= $usuariodb[0]['USU_NOME'];
                $email
= $usuariodb[0]['USU_EMAIL'];

                $i
= 0;
               
foreach ( $usuariodb as $permiso) {
                    $permisos
[$i]['PRG'] = $permiso['CLASE'];
                    $permisos
[$i]['PART'] = $permiso['PART'];
                    $permisos
[$i]['NOME'] = $permiso['NOME'];

                    $p1
= explode( ',' , $permiso['USU_PERMISOS'] );
                   
foreach ($p1 as $p2) {
                        $p3
= explode( '=' , $p2 );
                        $permisos
[$i]["PERMISO"] = str_split ( $p3[1] , $split_length = 1 );
                   
}

                    $i
++;
               
}
           
}  
       
}

       
return new x509User($username, $roles, $nome, $email, $entidade, $permisos);
   
}

   
private function getEntidade($entId) {
        $sql
= "select * from ENT where ENT_CODIGO = :ENT_CODIGO";
        $q
= $this->pdo->prepare($sql);
        $q
->bindParam(':ENT_CODIGO', $entId, \PDO::PARAM_INT);
        $q
->execute();  
       
return $q->fetch();
   
}

   
private function getUsuarioDb($dni, $entidade) {
        $sql  
= "select * from ".$entidade['CODIGO']."_USU";
        $sql
.= " inner join ".$entidade['CODIGO']."_PER on PER_USUARIO = USU_CODIGO";
        $sql
.= " inner join ".$entidade['CODIGO']."_APL on CODIGO = PER_APL";
        $sql
.= " where USU_DNI=:dni";
        $sql
.= " order by NOME";
        $q
= $this->pdo->prepare($sql);
        $q
->bindParam(':dni', $dni, \PDO::PARAM_STR);
        $q
->execute();            
       
return $q->fetchAll();
   
}

   
private function getDatosCertificado()
   
{
       
if ( isset( $_SERVER['SSL_CLIS_DN'] ) ) {
            $datosCertificado
= array();
            $datos
= explode( ',' , $_SERVER['SSL_CLIS_DN'] );

            $gn
= explode( '=' , $datos[1] );
            $datosCertificado
["APELIDOS"] = $gn[1];
            $sn
= explode( '=' , $datos[2] );
            $datosCertificado
["NOME"] = $sn[1];
            $dni
= explode( '=' , $datos[3] );
            $datosCertificado
["DNI"] = $dni[1];
           
return $datosCertificado;        
       
} else {
           
return null;
       
}
   
}

   
public function refreshUser(UserInterface $user)
   
{

       
return $this->loadUserByUsername($user->getUsername());
   
}

   
public function supportsClass($class)
   
{

       
return $class === 'AppBundle\Security\User\x509User';
   
}
}


x509User.php

<?php

namespace AppBundle\Security\User;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;

class x509User implements UserInterface, EquatableInterface
{
   
private $username;
   
private $roles;
   
private $nome;
   
private $email;
   
private $entidade;
   
private $permisos;

   
public function __construct($username, array $roles, $nome, $email, array $entidade, array $permisos)
   
{
        $this
->username = $username;
        $this
->roles    = $roles;
        $this
->nome     = $nome;
        $this
->email    = $email;
        $this
->entidade = $entidade;
        $this
->permisos = $permisos;

   
}

   
public function getUsername()
   
{
       
return $this->username;
   
}

   
public function getRoles()
   
{
       
return $this->roles;
   
}

   
public function getNome()
   
{
       
return $this->nome;
   
}

   
public function getEmail()
   
{
       
return $this->email;
   
}

   
public function getEntidade()
   
{
       
return $this->entidade;
   
}

   
public function getEntidadeCodigo()
   
{
       
return $this->entidade['ENT_CODIGO'];
   
}

   
public function getPermisos()
   
{
       
return $this->permisos;
   
}

   
// UserInterface
   
public function getPassword(){return $this->nome;}
   
public function getSalt(){return $this->nome;}
   
public function eraseCredentials(){ }
   
public function isEqualTo(UserInterface $user)
   
{
       
if (!$user instanceof WebserviceUser) {
           
return false;
       
}

       
if ($this->username !== $user->getUsername()) {
           
return false;
       
}

       
return true;
   
}
}


Reply all
Reply to author
Forward
0 new messages