Mostrando entradas con la etiqueta JAVA. Mostrar todas las entradas
Mostrando entradas con la etiqueta JAVA. Mostrar todas las entradas

lunes, 23 de febrero de 2015

WSO2 MB: Enviando y recibiendo mensajes con JMS.

image

En esta entrada queremos mostrarles como usar el WSO2 Message Broker para enviar y recibir mensajes usando las colas JMS desde Java.

Para los códigos de enviar un mensaje a una cola en el WSO2 MB y para leer de la cola, hacemos uso de los ejemplos entregados por WSO2 en la documentación de la herramienta.

En el MB tenemos una cola creada como se muestra a continuación:

image

El nombre de la cola es QueueMB1, por lo que debe ser revisado en el código, y el puerto que se está usando para conectarse a la consola web del WSO2 MB es el 9447 lo que nos indica que el Offset es de 4, por lo que también debe ser ajustado en el código de las clases que envían y reciben los mensajes.

El proyecto con las clases usadas lo pueden obtener de este enlace.

Resultado de la ejecución de la clase QueueSender en el MB para 100 mensajes:

image
image

Resultado de la ejecución de la clase QueueSender en el IDE:

image


Resultado de la ejecución de la clase QueueReceiver en el MB:

image


Resultado en el IDE:

image

De esta manera estamos seguros que el WSO2 MB 2.2.0 funciona para el envío y recepción de mensajes desde JAVA y seguramente desde otros sistemas, como puede ser el WSO2 ESB.

viernes, 7 de marzo de 2014

Como ya habíamos visto en la entrada anterior, el WSO2 Identity Server tiene entre sus múltiples funcionalidades relacionadas con la seguridad la de actuar como un Identity Provider o IDP.
Se había dejado como pendiente la captura de los atributos contenidos en el almacén de usuarios que estuviera usando el  WSO2 Identity Server y eso será lo que haremos en esta entrada.

El escenario:
  • Digamos que tenemos inicialmente el WSO2 configurado para que use su BD interna de los usuarios, pero queremos que también use un LDAP como almacén de usuarios, pueden usar este enlace para configurar su LDAP y de esta manera se podrán autenticar en las aplicaciones usando los usuarios del LDAP.
  • El Identity server tiene un conjunto de usuarios, los cuales tienen atributos definidos a través de la funcionalidad “My Profile” y el LDAP contiene información de los usuarios la cual depende de la implementación de cada organización y la información que se desee guardar en el LDAP.
  • Se desea que las aplicaciones que participen en el escenario de Single Sign On usando el WSO2 Identity Server puedan obtener los atributos requeridos del usuario autenticado en ellas, bien sea desde el almacén por defecto de la herramienta o bien desde el LDAP.

Veamos cómo se hace:

Lo primero es ir al WSO2 Identity Server y editar la configuración del SSO para la aplicación que requiere dichos atributos. Noten el valor del “Consumer Index” pues luego hará falta.


Cuando entramos en  su configuración debemos marcar la opción que dice “Enable Attribute Profile”:






Y en la parte de Claim seleccionamos aquellas que se correspondan con los atributos que sabemos están en el LDAP y le damos al botón “Add Claim” y así veremos cómo se van agregando los atributos.

NOTA: El WSO2 Identity Server también tiene la posibilidad de gestionar estos atributos, lo cual es muy útil para saber que Claims se corresponden con qué atributos en el LDAP o para agregar nuevas en caso de ser requerido. En el caso del rol y de la URL tuve que realizar ajustes pues no se correspondían con el esquema de atributos soportados por el LDAP.

Cuando nos volvamos a autenticar en la aplicación que usamos de ejemplo veremos entonces los valores de  los atributos.

Mi primera prueba fue con un usuario interno, en este caso el admin de la herramienta y tuve que llenar en “My Profile” los datos que se querían mostrar.



Luego al volver a autenticarme con las credenciales del LDAP se cargaron mis atributos sin problema desde el LDAP. Aquí aconsejo revisar el LDAP de cada cual y ver los atributos que tienen sus usuarios, luego revisar los claims que tengan y ver si tienen para obtener esos atributos. En caso de que no los tengan bien pueden modificar los existentes o agregar nuevos.

Para capturar estos datos se usó la siguiente página JSP:


En el caso de la aplicación PHP el escenario es bastante similar. Hay que editar la configuración del SSO para dicha aplicación y seleccionar los atributos que queremos obtener.

Al revisar el código de la aplicación demo vemos que también tiene implementada la captura de los atributos, como se puede ver en la siguiente imagen:




Pero al probarla no se mostraba nada. Así que volví a repetir el mismo procedimiento de la entrada anterior y comparar los mensajes SAML intercambiados, y resulta que mientras la aplicación JAVA está enviando un atributo AttributeConsumingServiceIndex con el identificador que se muestra en su configuración de SSO en el WSO2 Identity Server, la aplicación web en PHP no lo hacía, por lo que tuve que modificar el código.

En el fichero Settings.php agregué un nuevo atributo AttributeConsumingServiceIndex a la clase OneLogin_Saml_Settings tal y como se muestra en esta imagen:




Luego  en el fichero settings.php le seteo el valor correspondiente:


Y por último en el fichero AuthRequest.php modifico la estructura del mensaje SAML a enviar para que incluya este atributo:



        $request = <<<AUTHNREQUEST
<samlp:AuthnRequest
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
    ID="$id"
    Version="2.0"
    IssueInstant="$issueInstant"
 Destination="{$this->_settings->idpSingleSignOnUrl}"
    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
    AssertionConsumerServiceURL="{$this->_settings->spReturnUrl}"
 AttributeConsumingServiceIndex="{$this->_settings->AttributeConsumingServiceIndex}">
    <saml:Issuer>{$this->_settings->spIssuer}</saml:Issuer>
    <samlp:NameIDPolicy
        Format="{$this->_settings->requestedNameIdFormat}"
        AllowCreate="true"></samlp:NameIDPolicy>
    <samlp:RequestedAuthnContext Comparison="exact">
        <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
    </samlp:RequestedAuthnContext>
</samlp:AuthnRequest>
AUTHNREQUEST;


De esta manera al tratar de acceder a la aplicación php vemos como el mensaje llega al WSO2 Identity Server con el atributo requerido y también podemos ver como el mensaje SAML de respuesta contiene los valores de los atributos solicitados, pero llegado este punto tampoco se muestran en la página web. De nuevo a revisar código PHP  :-(


Aquí les dejo un fragmento del mensaje obtenido desde el WSO2 Identity Server, es solo un fragmento pues el mensaje es bastante grande:
<saml2:AttributeStatement>
<saml2:Attribute Name="http://wso2.org/claims/country"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Cuba</saml2:AttributeValue></saml2:Attribute>
<saml2:Attribute Name="http://wso2.org/claims/mobile"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">+5353722213</saml2:AttributeValue></saml2:Attribute>
<saml2:Attribute Name="http://wso2.org/claims/role"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">admin,Internal/everyone</saml2:AttributeValue></saml2:Attribute>
<saml2:Attribute Name="http://wso2.org/claims/url"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">http://desarrollosoa.blogspot.com/</saml2:AttributeValue></saml2:Attribute>
</saml2:AttributeStatement>



Luego de revisar el código php noté que había un problema con el fichero Response.php que contiene la clase OneLogin_Saml_Response y es que esta clase no capturaba los datos de los mensajes SAML2, solo los SAML, así que la modifiqué de esta manera.

<?php

/**
 * Parse the SAML response and maintain the XML for it.
 */
class OneLogin_Saml_Response
{
    /**
     * @var OneLogin_Saml_Settings
     */
    protected $_settings;

    /**
     * The decoded, unprocessed XML assertion provided to the constructor.
     * @var string
     */
    public $assertion;

    /**
     * A DOMDocument class loaded from the $assertion.
     * @var DomDocument
     */
    public $document;

    /**
     * Construct the response object.
     *
     * @param OneLogin_Saml_Settings $settings Settings containing the necessary X.509 certificate to decode the XML.
     * @param string $assertion A UUEncoded SAML assertion from the IdP.
     */
    public function __construct(OneLogin_Saml_Settings $settings, $assertion)
    {
        $this->_settings = $settings;
        $this->assertion = base64_decode($assertion);
        $this->document = new DOMDocument();
        $this->document->loadXML($this->assertion);
    }

    /**
     * Determine if the SAML Response is valid using the certificate.
     *
     * @throws Exception
     * @return bool Validate the document
     */
    public function isValid()
    {
        $xmlSec = new OneLogin_Saml_XmlSec($this->_settings, $this);
        return $xmlSec->isValid();
    }

    /**
     * Get the NameID provided by the SAML response from the IdP.
     */
    public function getNameId()
    {
        $entries = $this->_queryAssertion('/saml2:Subject/saml2:NameID');
        return $entries->item(0)->nodeValue;
    }

    /**
     * Get the SessionNotOnOrAfter attribute, as Unix Epoc, from the
     * AuthnStatement element.
     * Using this attribute, the IdP suggests the local session expiration
     * time.
     * 
     * @return The SessionNotOnOrAfter as unix epoc or NULL if not present
     */
    public function getSessionNotOnOrAfter()
    {
        $entries = $this->_queryAssertion('/saml2:AuthnStatement[@SessionNotOnOrAfter]');
        if ($entries->length == 0) {
            return NULL;
        }
        $notOnOrAfter = $entries->item(0)->getAttribute('SessionNotOnOrAfter');
        return strtotime($notOnOrAfter);
    }

    public function getAttributes()
    {
        $entries = $this->_queryAssertion('/saml2:AttributeStatement/saml2:Attribute');

        $attributes = array();
        /** @var $entry DOMNode */
        foreach ($entries as $entry) {
            $attributeName = $entry->attributes->getNamedItem('Name')->nodeValue;

            $attributeValues = array();
            foreach ($entry->childNodes as $childNode) {
                if ($childNode->nodeType == XML_ELEMENT_NODE && $childNode->tagName === 'saml2:AttributeValue'){
                    $attributeValues[] = $childNode->nodeValue;
                }
            }

            $attributes[$attributeName] = $attributeValues;
        }
        return $attributes;
    }

    /**
     * @param string $assertionXpath
     * @return DOMNodeList
     */
    protected function _queryAssertion($assertionXpath)
    {
        $xpath = new DOMXPath($this->document);
        $xpath->registerNamespace('samlp'   , 'urn:oasis:names:tc:SAML:2.0:protocol');
        $xpath->registerNamespace('saml'    , 'urn:oasis:names:tc:SAML:2.0:assertion');
        $xpath->registerNamespace('ds'      , 'http://www.w3.org/2000/09/xmldsig#');
  $xpath->registerNamespace('saml2'   , 'urn:oasis:names:tc:SAML:2.0:assertion');

        $signatureQuery = '/samlp:Response/saml:Assertion/ds:Signature/ds:SignedInfo/ds:Reference';
        $assertionReferenceNode = $xpath->query($signatureQuery)->item(0);
        if (!$assertionReferenceNode) {
            throw new Exception('Unable to query assertion, no Signature Reference found?');
        }
        $id = substr($assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue, 1);

        $nameQuery = "/samlp:Response/saml:Assertion[@ID='$id']" . $assertionXpath;
        return $xpath->query($nameQuery);
    }
}


Así fue entonces como desde la aplicación web en PHP pude mostrar los valores de los atributos.



Es válido aclarar que este problema no es generado por el WSO2 Identity Server, sino por el código PHP empleado en este ejemplo.

Espero les sea de utilidad.

miércoles, 5 de marzo de 2014

WSO2 como proveedor de SSO en la práctica usando el Identity Server


Todos aquellos que han podido experimentar con la herramienta WSO2 Identity Server saben que esta puede actuar como un servidor de Single Sign On bien usando OpenID o SAML.

Para resumir, el SSO nos permite acceder usando una sola cuenta a diferentes sistemas autenticándonos solo en uno de ellos, el resto se comunica con un servidor de SSO que les provee las credenciales que hayamos introducido anteriormente o les transfiere un token mediante el cual es confirma que nos hemos autenticado previamente.

De esta forma se evita  que los usuarios tengan una cuenta por sistema y que se tengan que autenticar en cada sistema al que deseen entrar.

Claro que previamente todos estos sistemas deben haberse configurado para lograr este escenario y es lo que veremos en esta entrada.

En lo personal había probado el Identity Server de WSO2 para establecer SSO entre los mismos servidores de la suite y en una PoC para aplicaciones web en JAVA, pero no había probado hasta el momento con aplicaciones web en PHP. Hasta hace poco usaba el CAS para montar escenarios de SSO.

El escenario es el siguiente:
  • Se tiene una aplicación web en JAVA desplegada en un servidor tomcat y se tiene una aplicación web en PHP desplegada en un servidor Apache.
  • Se desea proporcionar un ambiente de SSO para que los usuarios una vez autenticados en cualquiera de las 2 aplicaciones tengan acceso a la otra sin tener que volverse a autenticar.

Para aplicaciones web en JAVA solo basta con seguir los pasos de este ejemplo
Lo principal a tener en cuenta es:

Tener en el pom de nuestra webapp la siguiente dependencia:

    <dependencies>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.identity.sso.agent</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>

Y en el web.xml el siguiente filtro:
    <filter>
        <filter-name>SSOFilter</filter-name>
        <filter-class>org.wso2.carbon.identity.sso.agent.SSOAgentFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>SSOFilter</filter-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>/samlsso</url-pattern>
        <url-pattern>/openid</url-pattern>
        <url-pattern>/logout</url-pattern>
    </filter-mapping>

Por último deben configurar bien el fichero de propiedades para que la aplicación pueda redireccionar hacia donde tengan ubicado el WSO2 IS y se pueda establecer la configuración.

Realmente los pasos son muy sencillos y no dan motivo de pérdida.

Les dejo una imagen de la configuración hecha en el WSO2 IS para esta aplicación:


Luego de tener la webapp en JAVA funcionando me decidí a implementar lo mismo para PHP.

Estuve revisando algunas implementaciones para SAML y finalmente me opté por  ONELOGIN http://support.onelogin.com/entries/268420-saml-toolkit-for-php
Debo reconocer que de PHP no veía nada desde hace ya 9 años y que la elección fue sin muchos criterios a tener en cuenta, pero el uso de la aplicación demo fue bastante sencillo.

Descargue de github la herramienta https://codeload.github.com/onelogin/php-saml/zip/master que dentro tiene un demo.

Instalé WAMP, para así tener mi servidor APACHE con PHP y copié para la carpeta www la aplicación de ONELOGIN luego de descompactarlo.

Los cambios que hice fueron inicialmente en el fichero settings.php que queda como sigue para mi escenario:
<?php
/**
 * SAMPLE Code to demonstrate how provide SAML settings.
 *
 * The settings are contained within a OneLogin_Saml_Settings object. You need to
 * provide, at a minimum, the following things:
 *
 *  - idpSingleSignOnUrl
 *    This is the URL to forward to for auth requests.
 *    It will be provided by your IdP.
 *
 *  - idpPublicCertificate
 *    This is a certificate required to authenticate your request.
 *    This certificate should be provided by your IdP.
 * 
 *  - spReturnUrl
 *    The URL that the IdP should redirect to once the authorization is complete.
 *    You must provide this, and it should point to the consume.php script or its equivalent.
 */

define('XMLSECLIBS_DIR', './../ext/xmlseclibs/');
require_once XMLSECLIBS_DIR . 'xmlseclibs.php';

define('ONELOGIN_SAML_DIR', './../src/OneLogin/Saml/');
require_once ONELOGIN_SAML_DIR . 'AuthRequest.php';
require_once ONELOGIN_SAML_DIR . 'Response.php';
require_once ONELOGIN_SAML_DIR . 'Settings.php';
require_once ONELOGIN_SAML_DIR . 'XmlSec.php';

$settings = new OneLogin_Saml_Settings();

// When using Service Provider Initiated SSO (starting at index.php), this URL asks the IdP to authenticate the user.
//$settings->idpSingleSignOnUrl = 'https://app.onelogin.com/saml/signon/6171';
$settings->idpSingleSignOnUrl = 'https://localhost:9443/samlsso';

// The certificate for the users account in the IdP
$settings->idpPublicCertificate = <<<CERTIFICATE
-----BEGIN CERTIFICATE-----
MIICNTCCAZ6gAwIBAgIES343gjANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJVUzELMAkGA1UE
CAwCQ0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxDTALBgNVBAoMBFdTTzIxEjAQBgNVBAMMCWxv
Y2FsaG9zdDAeFw0xMDAyMTkwNzAyMjZaFw0zNTAyMTMwNzAyMjZaMFUxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzENMAsGA1UECgwEV1NPMjESMBAGA1UE
AwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCUp/oV1vWc8/TkQSiAvTou
sMzOM4asB2iltr2QKozni5aVFu818MpOLZIr8LMnTzWllJvvaA5RAAdpbECb+48FjbBe0hseUdN5
HpwvnH/DW8ZccGvk53I6Orq7hLCv1ZHtuOCokghz/ATrhyPq+QktMfXnRS4HrKGJTzxaCcU7OQID
AQABoxIwEDAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEAW5wPR7cr1LAdq+IrR44i
QlRG5ITCZXY9hI0PygLP2rHANh+PYfTmxbuOnykNGyhM6FjFLbW2uZHQTY1jMrPprjOrmyK5sjJR
O4d1DeGHT/YnIjs9JogRKv4XHECwLtIVdAbIdWHEtVZJyMSktcyysFcvuhPQK8Qc/E/Wq8uHSCo=
-----END CERTIFICATE-----
CERTIFICATE;

// The URL where to the SAML Response/SAML Assertion will be posted
$settings->spReturnUrl = 'http://localhost/php-saml/demo/consume.php';

// Name of this application
$settings->spIssuer = 'php-saml';

// Mio
$settings->destination = 'https://localhost:9443/samlsso';

// Tells the IdP to return the email address of the current user
$settings->requestedNameIdFormat = OneLogin_Saml_Settings::NAMEID_EMAIL_ADDRESS;

return $settings;

Lo siguiente que hice fue ir al WSO2 IS y configurar esta aplicación web de la misma forma en que ya había configurado la aplicación para JAVA, cambiando claro los nombres de algunos campos.

Al intentar autenticarme en la aplicación PHP la redirección funcionó sin problemas, el usuario se autenticó en el IS pero me dio un error en la misma página de autenticación del WSO2 IS.
El error era que me faltaba un atributo en el SAML2 request que estaba enviando la aplicación web al WSO2 IS. En StackOverflow pueden ver mi pregunta y los avances que hice hasta llegar a la solución http://stackoverflow.com/questions/22182354/sso-for-php-webapp-with-wso2-identity-server-authentication-request-failed/

Finalmente cuando pude pasar este error me dio otro relacionado con la validación de la firma y que pueden ver al final del enlace anterior. Para evitarlo tuve que desmarcar la opción de validación y por eso mi configuración para la webapp en PHP dentro del WSO2 IS queda de la siguiente manera:


Para finalizar probé autenticarme en una aplicación y luego acceder a la otra, pude comprobar que no me pedía autenticación. Que era lo que estaba buscando.

Como elementos pendientes quedan:
  • Obtener atributos de los usuarios desde el IS para propagarlos hacia las aplicaciones web.
  • Configurar el single sing out en la aplicación web en PHP y probar este mecanismo. La idea es que si me des autentico de una aplicación lo mismo debe pasar en el resto.
En otras entradas estaremos viendo ambos elementos.
Espero les sea de utilidad.