Continuando con la prueba de conexión entre el mundo de Microsoft Access y WordPress, llegamos al artículo número 7 en donde vamos ahora a sofisticar un poco más nuestra solución para agregar seguridad por usuario en las publicaciones. Recomiendo que lean los 6 artículos previos sobre XMLRPC:
- Parte 1: Publicar un post
- Parte 2: Publicar un archivo
- Parte 3: Usar una imagen destacada
- Parte 4: Publicar un tipo de post personalizado
- Parte 5: Publicar campos personalizados
- Parte 6: Ejemplo completo
Detalle del requerimiento
- Se desea asignar seguridad por usuario a cada post y su archivo adjunto
- Un usuario no puede ver lo de otro
- Un administrador puede ver todo
Enfoque de la solución
Trabajaremos con el campo autor de cada post / imagen.
- Al momento de crear un post, le asignamos un autor
- Agregaremos código en WordPress para controlar si el usuario conectado coincide con el autor
Modificaciones en WordPress - Parte 1 - Plugin de seguridad
Instalaremos un plugin de seguridad que directamente impida el acceso al sitio, si el visitante no está conectado. Usamos "Private Only"
Modificaciones en WordPress - Parte 2 - Query
Vamos a interceptar el query de los posts y las imágenes para evitar que me deje ver archivos / posts que no son de mi autoría a través de un plugin construido por nosotros. Código:
# Muestra los posts sólo si es el autor o es administrador
function seguridad_posts_media($query) {
if( !current_user_can( 'manage_options' ) ) {
global $user_ID;
$query->set('author', $user_ID );
}
return $query;
}
add_filter('pre_get_posts', 'seguridad_posts_media');
Modificaciones en WordPress - Parte 3 - Descarga de imágenes
Si bien el paso dos, controlar el acceso, nos falta un detalle. El usuario puede acceder directamente al archivo y descargarlo si conoce un URL como esta:
Para resolver esto tenemos que hacer algo más sofisticado. Modificaremos nuestro .htaccess para que redireccione a una página nuestra los accesos a ese tipo de URLs. Código:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^wp-content/uploads/(.*)$ descargar.php?file=$1 [QSA,L]
# BEGIN WordPress
# END WordPress
Y creamos nuestra página descargar.php para que realice los controles y efectúe la descarga del archivo. Código:
<?php
require_once('wp-load.php');
# Obtener path interno del archivo
list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL);
$file = rtrim($basedir,'/').'/'.str_replace('..', '', isset($_GET[ 'file' ])?$_GET[ 'file' ]:'');
# Obtener id del archivo
$enlace = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$id_archivo = obtener_id_archivo_desde_url ($enlace);
# Obtener autor del archivo
$post = get_post($id_archivo);
$autor = $post->post_author;
# Descargar archivo
if ( get_current_user_id() == $autor ) {
header('Content-Type: application/pdf');
readfile ($file);
}
else {
echo "Acceso denegado...";
}
# Función para obtener el ID de un archivo a través de su URL
function obtener_id_archivo_desde_url ( $url ) {
$parsed_url = explode( parse_url( WP_CONTENT_URL, PHP_URL_PATH ), $url );
$this_host = str_ireplace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) );
$file_host = str_ireplace( 'www.', '', parse_url( $url, PHP_URL_HOST ) );
if ( ! isset( $parsed_url[1] ) || empty( $parsed_url[1] ) || ( $this_host != $file_host ) ) {
return;
}
global $wpdb;
$attachment = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->prefix}posts WHERE guid RLIKE %s;", $parsed_url[1] ) );
return $attachment[0];
}
?>
Modificaciones en Access - Parte 1 - Especificar el autor al publicar un post
Es un dato más, veremos que no cambia mucho respecto a lo que veníamos haciendo, pero debemos tener en cuenta que nuestras credenciales deben ser de administrador para poder especificar el autor. Los cambios están resaltados.Código:
Function PublicarEnWordPress(CodigoArchivo As String, NumeroDocumento As String, CodigoCliente As String) As String
' Datos de Autenticación
CargarConstantes
txtURL = SITIO
txtUserName = USUARIO
txtPassword = PASSWORD
' Datos del Post
txtTitulo = CodigoCliente & " - " & NumeroDocumento
txtContenido = "Este es un custom post de ejemplo publicado desde Microsoft Access"
Dim txtCategorias(1) As String
txtCategorias(1) = "Uncategorized"
'txtImagen = "64"
txtTipoContenido = "proceso"
txtCliente = CodigoCliente
txtNumeroCertificado = NumeroDocumento
txtDocumento = CodigoArchivo
txtAutor = "3"
' ServerXMLHTTP
Dim objSvrHTTP As ServerXMLHTTP
Dim strT As String
Set objSvrHTTP = New ServerXMLHTTP
' Autenticación
objSvrHTTP.Open "POST", txtURL, False, CStr(txtUsuario), CStr(txtPassword)
objSvrHTTP.setRequestHeader "Accept", "application/xml"
objSvrHTTP.setRequestHeader "Content-Type", "application/xml"
strT = strT & "<methodCall>"
' Acción
strT = strT & "<methodName>wp.newPost</methodName>"
' General
strT = strT & "<params>"
strT = strT & "<param><value><string>0</string></value></param>"
strT = strT & "<param><value>" & txtUserName & "</value></param>"
strT = strT & "<param><value><string>" & txtPassword & "</string></value></param>"
strT = strT & "<param>"
strT = strT & "<struct>"
' Categorías
strT = strT & "<member><name>categories</name><value><array>"
strT = strT & "<data>"
For i = 1 To UBound(txtCategorias)
strT = strT & "<value>" & txtCategorias(i) & "</value>"
Next i
strT = strT & "</data>"
strT = strT & "</array></value></member>"
' Título, contenido y fecha
strT = strT & "<member><name>post_content</name><value><![CDATA[" & txtContenido & "]]></value></member>"
strT = strT & "<member><name>post_title</name><value>" & txtTitulo & "</value></member>"
strT = strT & "<member><name>dateCreated</name><value><dateTime.iso8601>" & Format(Now(), "yyyyMMdd") & "T" & Format(Now(), "hh:mm:ss") & "</dateTime.iso8601></value></member>"
' Imagen destacada
' strT = strT & "<member><name>post_thumbnail</name><value><![CDATA[" & txtImagen & "]]></value></member>"
' Tipo de contenido
strT = strT & "<member><name>post_type</name><value>" & txtTipoContenido & "</value></member>"
' Autor
strT = strT & "<member><name>post_author</name><value>" & txtAutor & "</value></member>"
' Campos personalizados
strT = strT & "<member><name>custom_fields</name><value><array><data><value>"
strT = strT & "<struct>"
strT = strT & "<member><name>key</name><value>cliente</value></member>"
strT = strT & "<member><name>value</name><value>" & txtCliente & "</value></member>"
strT = strT & "</struct>"
strT = strT & "<struct>"
strT = strT & "<member><name>key</name><value>numero_de_certificado</value></member>"
strT = strT & "<member><name>value</name><value>" & txtNumeroCertificado & "</value></member>"
strT = strT & "</struct>"
strT = strT & "<struct>"
strT = strT & "<member><name>key</name><value>documento</value></member>"
strT = strT & "<member><name>value</name><value>" & txtDocumento & "</value></member>"
strT = strT & "</struct>"
strT = strT & "</value></data></array></value></member>"
' Tipo de publicación
strT = strT & "<member><name>post_status</name><value>publish</value></member>"
strT = strT & "</struct>"
strT = strT & "</param>"
strT = strT & "</params>"
strT = strT & "</methodCall>"
' Publicación
objSvrHTTP.send strT
' Debug
Debug.Print objSvrHTTP.responseText
'MsgBox objSvrHTTP.responseText
PublicarEnWordPress = resultado(objSvrHTTP.responseText, "POST")
End Function
Modificaciones en Access - Parte 2 - Especificar el autor al publicar una imagen
El método para subir imágenes no nos deja especificar el autor. Hay un plugin que encontrarán en el directorio que extiende el método, pero preferí no utilizarlo. La opción es modificar el autor de la imagen luego de subirla, utilizando un método que hasta ahora no hemos utilizado, el método de editar posts. Código:
Function CambiarAutorEnWordPress(PostId As String, NuevoAutor As String) As String
' Datos de Autenticación
CargarConstantes
txtURL = SITIO
txtUserName = USUARIO
txtPassword = PASSWORD
' Datos del Post
txtId = PostId
txtAutor = NuevoAutor
' ServerXMLHTTP
Dim objSvrHTTP As ServerXMLHTTP
Dim strT As String
Set objSvrHTTP = New ServerXMLHTTP
' Autenticación
objSvrHTTP.Open "POST", txtURL, False, CStr(txtUsuario), CStr(txtPassword)
objSvrHTTP.setRequestHeader "Accept", "application/xml"
objSvrHTTP.setRequestHeader "Content-Type", "application/xml"
strT = strT & "<methodCall>"
' Acción
strT = strT & "<methodName>wp.editPost</methodName>"
' General
strT = strT & "<params>"
strT = strT & "<param><value><string>0</string></value></param>"
strT = strT & "<param><value>" & txtUserName & "</value></param>"
strT = strT & "<param><value><string>" & txtPassword & "</string></value></param>"
strT = strT & "<param><value><int>" & txtId & "</int></value></param>"
strT = strT & "<param>"
strT = strT & "<struct>"
' Autor
strT = strT & "<member><name>post_author</name><value>" & txtAutor & "</value></member>"
strT = strT & "</struct>"
strT = strT & "</param>"
strT = strT & "</params>"
strT = strT & "</methodCall>"
' Publicación
objSvrHTTP.send strT
' Debug
Debug.Print objSvrHTTP.responseText
'MsgBox objSvrHTTP.responseText
CambiarAutorEnWordPress = resultado(objSvrHTTP.responseText, "EDIT")
End Function
Modificaciones en Access - Parte 3 - Cambio en el análisis del resultado
Un detalle, la edición del post, nos devuelve un valor bool, en función de si pudo o no modificar el post, con lo cual hacemos un pequeño cambio a nuestra función de análisis de resultados:
Código:
Function resultado(strXML, tipo As String) As String
Dim error As String
Dim hayError As Boolean
error = ""
hayError = False
Set xmlDoc = CreateObject("MSXML2.DOMDocument")
xmlDoc.loadXML strXML
' Busco errores
Set elements = xmlDoc.getElementsByTagName("fault")
For Each el In elements
error = error & el.Text
hayError = True
Next
' Si no hay errores
If Not hayError Then
' POST o MEDIA?
Dim tipoXML As String
If tipo = "POST" Then
tipoXML = "params"
ElseIf tipo = "EDIT" Then
tipoXML = "boolean"
Else
tipoXML = "string"
End If
Set elements = xmlDoc.getElementsByTagName(tipoXML)
' Obtengo el código generado
resultado = elements(0).Text
' Si hay errores
Else
MsgBox error
resultado = "-1"
End If
'MsgBox resultado
End Function
Resultados
Ahora bien, cómo se verá la solución? Si tenemos permisos, nada cambia, si no tenemos permisos, pueden suceder tres cosas:
Caso 1: no estoy conectado
Caso 2: intento acceder a un post sobre el que no tengo permiso
Caso 3: intento acceder directamente a un archivo al que no tengo permiso (esté o no conectado)
Robusto, no? Aquí terminamos por ahora, luego iremos ajustando.
Cualquier duda me consultan. Hasta la próxima!
Bibliografía
Estos son todos los enlaces que me hicieron encontrar la luz en este oscuro camino. Agradezco a los autores y les recomiendo que los visiten.
- http://codex.wordpress.org/XML-RPC_WordPress_API
- http://codex.wordpress.org/XML-RPC_WordPress_API/Posts
- http://jeffreycarandang.com/tutorials/hide-wordpress-posts-media-uploaded-users/
- http://0to5.com/protecting-wordpress-media-uploads-unless-user-is-logged-in/
- http://wordpress.stackexchange.com/questions/37144/how-to-protect-uploads-if-user-is-not-logged-in
- https://gist.github.com/hakre/1552239
- http://php.net/manual/es/function.readfile.php
- http://frankiejarrett.com/get-an-attachment-id-by-url-in-wordpress/
No hay comentarios:
Publicar un comentario