viernes, 14 de junio de 2013

Restringir usuarios mostrados en el widget "user suggestion" del manejador de contenido Nuxeo

Una manera de restringir los usuarios desplegados en el widget user suggestion de nuxeo, podría ser la siguiente:

1-Sobreescribir el widget single_user_suggestion_widget_template.xhtml

-En modo edicion del widget, crear otra variable llamada userGroup:
   
El valor de esta variable se la pasaremos mediante custom properties del widget en nuxeo studio:
-Localizar el rich:suggestionBox component y realizar los siguientes cambios:
suggestionAction="#{myUserSuggestionActions.getSuggestions}"
Agregar otro f:param

Dentro de
tambien agregar el f:param

Dentro de
 
agregar:

2-Sobre-escribir el método de busqueda localizado en el seam component UserSuggestionActions. Cree otra clase y nombre mi componente así: myUserSuggestionActions

-Agregar estas variables a la clase:
	@RequestParameter
	protected String userSuggestionGroup;

	protected String cachedUserSuggestionGroup;

	public static final String WILDCARD_ALL_USER_GROUP = "*.*";
-Sobreescribir el método getUserSuggestions
	public List getUserSuggestions(Object input)
			throws ClientException {
		try {
			String searchPattern = (String) input;
			List users = new ArrayList();
			if(searchPattern.trim().equals(WILDCARD_ALL_USER_GROUP)){
				return getListUsersByWildCard();
			}
			users = userManager.searchUsers(searchPattern);
			if (userSuggestionGroup != null && !userSuggestionGroup.equals("")) {
				List usersByRol = new ArrayList();
				DataModel dataModel = null;
				for (DocumentModel doc : users) {
					dataModel = doc.getDataModel(userConfig.schemaName);
					NuxeoPrincipal principal = userManager
							.getPrincipal((String) dataModel
									.getValue(userConfig.nameKey));
					if (principal != null
							&& principal.isMemberOf(userSuggestionGroup)) {
						usersByRol.add(doc);
					}
				}
				return usersByRol;
			} else {
				return users;
			}
		} catch (SizeLimitExceededException e) {
			addSearchOverflowMessage();
			return Collections.emptyList();
		} catch (Exception e) {
			throw new ClientException("error searching for principals", e);
		}
	}
-Agregar los otros métodos locales
	private List getListUsersByWildCard() throws ClientException{
		List users = new ArrayList();
		if (userSuggestionGroup != null && !userSuggestionGroup.equals("")) {
			List listUsernames = userManager.getUsersInGroup(userSuggestionGroup);			
			if(listUsernames!= null && listUsernames.size() >= 0){
				for (String id : listUsernames) {
					users.add(userManager.getUserModel(id));
				}
			}					
		}
		return users;		
	}
Si se utiliza un wildcard en conjunto con el nuevo parámetro del widget userGroup, entonces el widget mostrara todos los usuarios que pertenezcan al grupo pasado como parámetro. El wilcard utilizado es *.*

jueves, 28 de febrero de 2013

Restful spring roo - Spring Security - Cliente Jersey -

En esta oportunidad voy a explicar cómo crear servicios restful en una aplicación generada mediante spring roo y desarrollaré una aplicación cliente jersey que lea la data expuesta en los servicios restful, no solamente leeré la data sino que demostraré como podemos editar un atributo y enviarlo de nuevo mediante un servicio rest con método PUT para su actualización en la base de datos. También explicaré como configurar spring security de tal manera que para las urls que expongan los servicios rest queden aseguradas pero no redirijan a la página de login, dejando de esta manera la página de login para los otros módulos que estén asegurados. Voy a omitir los pasos de configuración de la base de datos y de ingeniería reversa de mis tablas. Solamente trabajaremos con una tabla llamada ejemplo la cual creó una entidad Ejemplo.java.

A continuación los pasos a seguir y cumplir con los requerimientos expuestos:

1. Ejecutamos el comando json add:
 json add --class ~.model.Ejemplo
Este comando introducirá la anotación @RooJson en el tipo de destino especificado:

@RooJson
public class Ejemplo {

}

Y creara el siguiente archivo .aj donde están declarados todos los métodos que convertirán la entidad a json y viceversa:
package org.springroo.model;

import flexjson.JSONDeserializer;
import flexjson.JSONSerializer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springroo.model.Ejemplo;


privileged aspect Ejemplo_Roo_Json { 

public String Ejemplo.toJson() {

return new JSONSerializer().exclude("*.class").serialize(this);

}

public static Ejemplo Ejemplo.fromJsonToEjemplo(String json) {

return new JSONDeserializer<Ejemplo>().use(null, Ejemplo.class).deserialize(json);

}

public static String Ejemplo.toJsonArray(Collection<Ejemplo> collection) {

return new JSONSerializer().exclude("*.class").serialize(collection);

}

public static Collection<Ejemplo> Ejemplo.fromJsonArrayToEjemploes(String json) {

return new JSONDeserializer<List<Ejemplo>>().use(null, ArrayList.class).use("values", Ejemplo.class).deserialize(json);

}

}


2. Crear un controllador donde se expondrán los servicios rest mediante el siguiente comando:
controller class --class ~.web.EjemploController --preferredMapping /restAPI/ejemplo


Esto creara la siguiente clase java:

package org.springroo.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springroo.model.Ejemplo;

@RequestMapping("/restAPI/ejemplo")
@Controller
public class EjemploController {

}


3. Ejecutamos el comando web mvc json add:
web mvc json add --jsonObject ~.model.Ejemplo --class ~.web.EjemploController

Este comando introduce la anotación @RooWebJson en el tipo de destino especificado.

package org.springroo.web;

import org.springframework.roo.addon.web.mvc.controller.json.RooWebJson;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springroo.model.Ejemplo;


@RequestMapping("/restAPI/ejemplo")
@Controller
@RooWebJson(jsonObject = Ejemplo.class)
public class EjemploController {

}

Creando este archivo .aj con los métodos crud restful:

// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO.
// You may push code into the target .java compilation unit if you wish to edit any member(s).

package org.springroo.web;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springroo.model.Ejemplo;
import org.springroo.services.EjemploService;
import org.springroo.web.EjemploController;

privileged aspect EjemploController_Roo_Controller_Json {
    
    @Autowired
    EjemploService EjemploController.EjemploService;
    
    @RequestMapping(value = "/{id}", headers = "Accept=application/json")
    @ResponseBody
    public ResponseEntity EjemploController.showJson(@PathVariable("id") Integer id) {
        Ejemplo Ejemplo = EjemploService.findEjemplo(id);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json; charset=utf-8");
        if (Ejemplo == null) {
            return new ResponseEntity(headers, HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity(Ejemplo.toJson(), headers, HttpStatus.OK);
    }
    
    @RequestMapping(headers = "Accept=application/json")
    @ResponseBody
    public ResponseEntity EjemploController.listJson() {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json; charset=utf-8");
        List result = EjemploService.findAllEjemploes();
        return new ResponseEntity(Ejemplo.toJsonArray(result), headers, HttpStatus.OK);
    }
    
    @RequestMapping(method = RequestMethod.POST, headers = "Accept=application/json")
    public ResponseEntity EjemploController.createFromJson(@RequestBody String json) {
        Ejemplo Ejemplo = Ejemplo.fromJsonToEjemplo(json);
        EjemploService.saveEjemplo(Ejemplo);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        return new ResponseEntity(headers, HttpStatus.CREATED);
    }
    
    @RequestMapping(value = "/jsonArray", method = RequestMethod.POST, headers = "Accept=application/json")
    public ResponseEntity EjemploController.createFromJsonArray(@RequestBody String json) {
        for (Ejemplo Ejemplo: Ejemplo.fromJsonArrayToEjemploes(json)) {
            EjemploService.saveEjemplo(Ejemplo);
        }
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        return new ResponseEntity(headers, HttpStatus.CREATED);
    }
    
    @RequestMapping(method = RequestMethod.PUT, headers = "Accept=application/json")
    public ResponseEntity EjemploController.updateFromJson(@RequestBody String json) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        Ejemplo Ejemplo = Ejemplo.fromJsonToEjemplo(json);
        if (EjemploService.updateEjemplo(Ejemplo) == null) {
            return new ResponseEntity(headers, HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity(headers, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/jsonArray", method = RequestMethod.PUT, headers = "Accept=application/json")
    public ResponseEntity EjemploController.updateFromJsonArray(@RequestBody String json) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        for (Ejemplo Ejemplo: Ejemplo.fromJsonArrayToEjemploes(json)) {
            if (EjemploService.updateEjemplo(Ejemplo) == null) {
                return new ResponseEntity(headers, HttpStatus.NOT_FOUND);
            }
        }
        return new ResponseEntity(headers, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE, headers = "Accept=application/json")
    public ResponseEntity EjemploController.deleteFromJson(@PathVariable("id") Integer id) {
        Ejemplo Ejemplo = EjemploService.findEjemplo(id);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        if (Ejemplo == null) {
            return new ResponseEntity(headers, HttpStatus.NOT_FOUND);
        }
        EjemploService.deleteEjemplo(Ejemplo);
        return new ResponseEntity(headers, HttpStatus.OK);
    }
    
}


4. Asegurando nuestra aplicación mediante el siguiente comando:
spring security setup

Este comando creara el siguiente archivo de configuración: applicationContext-security.xml

Configuración que asegura nuestras urls de servicios rest pero sin redirigir a nuestro modulo de login:
<!-- HTTP security configurations -->
<http pattern="/restAPI/**" create-session="stateless" authentication-manager-ref="authenticationManager">
<intercept-url pattern="/restAPI/**" access="ROLE_ADMIN" />
<http-basic />
</http>

Y dejamos la configuración por defecto que nos genero spring roo:

<http auto-config="true" use-expressions="true" create-session="always">
<form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t" />
<logout logout-url="/resources/j_spring_security_logout" />
<!-- Configure these elements to secure URIs in your application -->
<intercept-url pattern="/choices/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/member/**" access="isAuthenticated()" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/**" access="permitAll" />
</http>


5. Probamos nuestras configuraciones en el navegador y la salida de nuestros servicios rest:

Intentando acceder a un modulo seguro nos muestra el login form:




Intentado acceder a una url segura de nuestros servicios rest:





Consultando el registro con id = 1 de nuestra tabla ejemplo veamos la salida en nuestro navegador:
url: http://localhost:8080/appXXXXXXX/restAPI/ejemplo/1 muestra la siguiente salida:

{"id":1,"idejemplo":1,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":"WILLIAMS","text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":"","text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":"","text48":"","text49":null,"text5":"","text50":"","text52":"","text6":"","text7":"","text8":"","text9":""}

Consultando todos los registros:
url: http://localhost:8080/appNegocioIribarren/restAPI/ejemplo muestra la siguiente salida:

[{"id":2,"idejemplo":2,"t1":null,"t11":null,"t12":null,"t13":null,"t14":null,"t15":null,"t16":null,"t17":null,"t18":null,"t2":null,"t3":null,"t4":null,"t5":null,"t6":null,"t7":null,"t8":null,"text1":null,"text10":null,"text11":null,"text12":null,"text13":null,"text14":null,"text15":null,"text16":null,"text17":null,"text18":null,"text19":null,"text20":null,"text21":null,"text22":null,"text23":null,"text24":null,"text25":null,"text26":null,"text27":null,"text28":null,"text29":null,"text3":null,"text30":null,"text31":null,"text32":null,"text33":null,"text34":null,"text35":null,"text36":null,"text39":null,"text4":null,"text40":null,"text41":null,"text42":null,"text43":null,"text44":null,"text45":null,"text46":null,"text47":null,"text48":null,"text49":null,"text5":null,"text50":null,"text52":null,"text6":null,"text7":null,"text8":null,"text9":null},{"id":3,"idejemplo":3,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":"","text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":null,"text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":null,"text48":"","text49":null,"text5":"","text50":"","text52":null,"text6":"","text7":"","text8":"","text9":""},{"id":1,"idejemplo":1,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":"WILLIAMS","text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":"","text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":"","text48":"","text49":null,"text5":"","text50":"","text52":"","text6":"","text7":"","text8":"","text9":""}]

6.Agreamos la siguiente dependencia en nuestro archivo pom.xml la necesitaremos para comenzar a desarrollar nuestra aplicación cliente:

<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.8</version>
</dependency>


7. Creando nuestra aplicación cliente mediante la api de jersey client, la explicaré mediante el mismo código:

package org.springroo.test;

import java.io.IOException;

import javax.naming.AuthenticationException;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.star.lang.IllegalArgumentException;


public class ClientJersey {

 public static void main(String[] args) throws IOException,
   AuthenticationException {
  Client client = Client.create();
  try {
   String auth = new sun.misc.BASE64Encoder().encode("admin:admin"
     .getBytes());
   WebResource webResource = client
     .resource("http://192.168.1.103:8080/appXXXXXX/restAPI/ejemplo");
   ClientResponse response = webResource
     .header("Authorization", "Basic " + auth)
     .type("application/json").accept("application/json")
     .get(ClientResponse.class);

   int statusCode = response.getStatus();
   if (statusCode == 401) {
    throw new AuthenticationException(
      "Invalid Username or Password");
   }
   if (statusCode == 403) {
    throw new AuthenticationException("Usuario no autorizado");
   }
//Obtenemos el arreglo de objetos Ejemplo y lo recorremos para obtener el objeto de id = 1
   JSONArray ja = new JSONArray(response.getEntity(String.class));
   JSONObject joForUpdate = null;
   for (int i = 0; i < ja.length(); i++) {
    if (ja.getJSONObject(i).getInt("id") == 1) {
     joForUpdate = ja.getJSONObject(i);
     break;
    }
   }
   if (joForUpdate == null)
    throw new IllegalArgumentException("json object null");

   System.out.println(joForUpdate.get("id"));
   joForUpdate.put("text1", "RIVAS");

   // Luego de obtener el objeto de id = 1 realizamos la actualizacion de su atributo text1 
   response = webResource.header("Authorization", "Basic " + auth)
     .type("application/json").accept("application/json")
     .put(ClientResponse.class, joForUpdate.toString());

   System.out.println("Estatus: " + response.getStatus());

  } catch (JSONException e) {
   e.printStackTrace();
  } catch (IllegalArgumentException e) {
   e.printStackTrace();
  } finally {
   client.destroy();
  }

 }

}

8. Verificación de si el atributo text1 del registro con id = 1 cambio:
url: http://localhost:8080/appXXXXXX/restAPI/ejemplo

[{"id":2,"idejemplo":2,"t1":null,"t11":null,"t12":null,"t13":null,"t14":null,"t15":null,"t16":null,"t17":null,"t18":null,"t2":null,"t3":null,"t4":null,"t5":null,"t6":null,"t7":null,"t8":null,"text1":null,"text10":null,"text11":null,"text12":null,"text13":null,"text14":null,"text15":null,"text16":null,"text17":null,"text18":null,"text19":null,"text20":null,"text21":null,"text22":null,"text23":null,"text24":null,"text25":null,"text26":null,"text27":null,"text28":null,"text29":null,"text3":null,"text30":null,"text31":null,"text32":null,"text33":null,"text34":null,"text35":null,"text36":null,"text39":null,"text4":null,"text40":null,"text41":null,"text42":null,"text43":null,"text44":null,"text45":null,"text46":null,"text47":null,"text48":null,"text49":null,"text5":null,"text50":null,"text52":null,"text6":null,"text7":null,"text8":null,"text9":null},{"id":3,"idejemplo":3,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":null,"text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":null,"text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":null,"text48":"","text49":null,"text5":"","text50":"","text52":null,"text6":"","text7":"","text8":"","text9":""},{"id":1,"idejemplo":1,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":"RIVAS","text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":"","text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":"","text48":"","text49":null,"text5":"","text50":"","text52":"","text6":"","text7":"","text8":"","text9":""}]

Con esto hemos finalizado este corto pero muy educativo tutorial.



martes, 26 de febrero de 2013

Enlazando álbums en la nueva versión de picassaweb

Desde la migración de picassaweb a la nueva versión, desapareció la opción de Enlazar este álbum. Pues bien, si por casualidad tienes un código viejo de cuando compartiste un álbum como diapositiva, solo tienes que copiar ese código y reemplazar el id del usuario y el id del álbum que quieras compartir en la variable host del componente object.

Código viejo:

<object data="http://picasaweb.google.com/s/c/bin/slideshow.swf" height="400" type="application/x-shockwave-flash" width="550">
<param name="movie" value="http://picasaweb.google.com/s/c/bin/slideshow.swf" />
<param name="flashvars" value="host=picasaweb.google.com&amp;hl=es&amp;feat=flashalbum&amp;RGB=0x000000&amp;feed=http%3A%2F%2Fpicasaweb.google.com%2Fdata%2Ffeed%2Fapi%2Fuser%2F107611341400605027389%2Falbumid%2F5567340469573655985%3Falt%3Drss%26kind%3Dphoto%26hl%3Des" />
<param name="pluginspage" value="http://www.macromedia.com/go/getflashplayer" />
</object>

El id del usuario google esta marcado en azul y el id del álbum esta marcado en rojo.

En la nueva versión web de picassa solo permite compartir el álbum como dirección url:
https://plus.google.com/photos/113536766050611718301/albums/5627336090337889201?authkey=CPb2iJiw5MWqbg

Solo deben remplazar cada id en su respectivo lugar y listo.