Archivos de Categoría: Coldfusion

Ajax en Coldfusion

Al igual que ya hice un manual básico de como centralizo la seguridad de las llamadas Ajax en Codeigniter, ahora me toca hacerlo para Coldfusion. Lo principal es que todas las llamadas sean a componentes y que estos a su vez se extiendan del Ajax.cfc que dejo a continuación:

component hint="componente securiza las llamadas de Ajax y carga los parametros que recibe la página"{

    variables.fields = {};

    package void function init(){
        headers = getHttpRequestData().headers;

        if(cgi.content_type eq 'application/json;charset=utf-8'){
            variables.fields = deserializeJSON(ToString(getHTTPRequestData().content));
        }else if(structKeyExists(headers, "X-Requested-With") and headers["X-Requested-With"] eq "XMLHttpRequest"){
            variables.fields = form;
        }else{
            cfheader(
                statuscode = 401,
                statustext = "Invalid Request"
            );
            abort;
        }

        if(not (structKeyExists(headers, "X-CSRF-TOKEN") and CSRFverifyToken(headers["X-CSRF-TOKEN"])) ){
            cfheader(
                statuscode = 401,
                statustext = "Invalid CSRF token"
            );
            abort;
        }
    }
}

Este lo que hace es que solo permitirá llamadas que estén mandando un JSON como contenido o que tengan una cabecera de petición por Ajax (X-Requested-With: XMLHttpRequest
). Tambien se encarga de comprobar que en la cabecera de las peticiones viene el parámetro X-CSRF-TOKEN que nos permitirá validar el token de la petición para evitar ataques CSRF. Por ultimo cargará todos los parámetros en una variable fields.

Un ejemplo de componente que se encargue del login por ejemplo sería así:

component extends="cfc.Ajax"{     

    remote any function login ( ) returnFormat="JSON" {
        this.init();

        if(not structKeyExists(variables.fields, 'email') or not isValid('email', variables.fields.email)) {
            cfheader(
                statuscode = 403,
                statustext = "Invalid Email"
            );                   
            return {};     
        }
        
        if(not structKeyExists(variables.fields, 'password') or len(variables.fields.password) eq 0) {
            cfheader(
                statuscode = 403,
                statustext = "Invalid Password"
            );            
            return {};
        }

        userObj = new cfc.User();
        result = userObj.login(email=variables.fields.email, password=variables.fields.password);        
        
        if(result.status neq 200){
            cfheader(
                statuscode = result.status,
                statustext = "Invalid Email or Password"
            );       
            return {};
        }else{             
 
            return result;
        }
    } 

}

Y para integrarlo con el html/javascript, lo primero sería meter en la cabecera de las páginas el token:

<meta name="csrf-token" content="<cfoutput>#CSRFGenerateToken()#</cfoutput>">

Y hacer que todas las peticiones de Ajax que hagamos con jQuery, le añada el token:

$.ajaxSetup({
	beforeSend: function (xhr){
		xhr.setRequestHeader('X-CSRF-TOKEN',document.querySelector("meta[name='csrf-token']").content);        
	}
});

O si usamos Axios en Vue:

window.axios.defaults.headers.common = {
	'X-Requested-With': 'XMLHttpRequest',
	'X-CSRF-TOKEN' : document.querySelector("meta[name='csrf-token']").content
};

Llamadas de ajax en ColdFusion y jQuery

Es un ejemplo muy facilito pero me ha servido sobre todo para crear componentes en cfscript que su día cuando empecé no se podía usar.
Lo primero sería tener un formulario, en este caso uno para logarse en la web.

<table>
    <tr>
        <td>Email</td>
        <td><input name="email" type="text"></td>
    </tr>
    <tr>
        <td>Contraseña</td>
        <td><input name="password" type="input"></td>
    </tr>
    <tr>
        <td colspan="2"><button id="login">Logarse</button></td>
    </tr>
</table>

Y un javascript que controle el envío del formulario:

$( document ).ready(function() {               
    $('#login').click( function(e) {
    $('#resultLogin').text('');
    $.post(
        'ajax/user.cfc', // ruta del componente
        {
            method: 'login', // método al que llamaremos
            email: $('input[name="email"]').val(),
            password: $('input[name="password"]').val()
        }
        , "json")
        .done( function(result) {                           
            // es este caso siempre entrará por aquí cuando sea un acceso valido                            
            $('#resultLogin').text(JSON.stringify(result, undefined, 2));
        })
        .fail( function(xhr, status, error) {
            // si falla alguna de las validaciones o el usuario no es válido se generará un error
            $('#resultLogin').text(error);
        });
    });
});     

Por último el componente, que tendría en este caso todos los métodos que pudieran ser llamados por Ajax sobre el usuario:

component { 
    remote any function login ( required string email, required string password ) returnFormat="JSON" {
        // validación del email
        if(!isValid('email', email)) {
            cfheader(
                statuscode = 403,
                statustext = "Invalid Email"
            );       
            return {};     
        }
        // validación de la contraseña
        if(len(password) eq 0) {
            cfheader(
                statuscode = 403,
                statustext = "Invalid Password"
            );            
            return {};
        }

        userObj = createObject('component', 'cfc.User');
        result = userObj.login(email=email, password=password);
        // el componente devuelve una estructura con status 200 si es un usuario válido
        if(result.status neq 200){
            cfheader(
                statuscode = result.status,
                statustext = "Invalid Email or Password"
            );       
            return {};
        }else{
            structDelete(result, 'status');
            return result;
        }
    }  

    // el resto de metodos...

}

Web en multidioma en coldfusion

Nuevo proyecto y toca volver al ColdFusion y SQL Server así que las siguientes entradas seguramente tratarán de estos temas ya que los tenía un pelín oxidados.

Lo primero que me ha tocado hacer es montar el sistema multidioma de del sitio, y gustándome como me gusta Laravel, he decidido basarme también en ficheros JSON que iran en cada carpeta para personalizar dependiendo de la sección, y unos en el raíz que servirán como traducciones generales.

La estructura esta subida en mi github y básicamente lo que he hecho es crear un componente helpers.cfc que se carga en el application para que sea accesible desde toda la web. En este componente la idea es meter todas las funciones generales que pueda necesitar.

Así desde cualquier punto de la web puedo hacer un cfoutput #application.helpers.translate(‘TEXTO A TRADUCIR’, ‘IDIOMA A MOSTRAR’)#. La función está hecha para que en caso de no pasarle el idioma coja el del navegador por defecto