sábado, 2 de septiembre de 2017

Configurar Android Devices para desarrollo en Linux (Debian 8)

Es muy comun encontrarse este error despues de ejecutar:

adb devices

#Error al mostrar que no hay permisos:
List of devices attached
M4 SS1070       no permissions (verify udev rules); see [http://developer.android.com/tools/device.html]

Aquí el problema es que los usb son mas restrictivos en linux.
Pero no te alarmes. Es sencillo de resolver.

Lo que voy a mostrar en una recopilación de la mayoría de vendors para que no sufras mas por esto. Eventualmente si después de seguir los pasos abajo mostrados aun te sigue saliendo.
Tendrás que buscar el vendo-Id correcto para tu device.

#Edita/Agregar el archivo de udev rules
sudo nano /etc/udev/rules.d/51-android.rules

#Agregar la lista de abajo al archivo.#51-android.rules: 
#Lista con los vendors mas comunes
SUBSYSTEM=="usb", ATTR{idVendor}=="0502", MODE="0666", GROUP="plugdev" #Acer
SUBSYSTEM=="usb", ATTR{idVendor}=="0b05", MODE="0666", GROUP="plugdev" #ASUS
SUBSYSTEM=="usb", ATTR{idVendor}=="413c", MODE="0666", GROUP="plugdev" #Dell
SUBSYSTEM=="usb", ATTR{idVendor}=="0489", MODE="0666", GROUP="plugdev" #Foxconn
SUBSYSTEM=="usb", ATTR{idVendor}=="04c5", MODE="0666", GROUP="plugdev" #Fujitsu
SUBSYSTEM=="usb", ATTR{idVendor}=="04c5", MODE="0666", GROUP="plugdev" #Fujitsu Toshiba
SUBSYSTEM=="usb", ATTR{idVendor}=="091e", MODE="0666", GROUP="plugdev" #Garmin-Asus
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", MODE="0666", GROUP="plugdev" #Google
SUBSYSTEM=="usb", ATTR{idVendor}=="201E", MODE="0666", GROUP="plugdev" #Haier
SUBSYSTEM=="usb", ATTR{idVendor}=="109b", MODE="0666", GROUP="plugdev" #Hisense
SUBSYSTEM=="usb", ATTR{idVendor}=="0bb4", MODE="0666", GROUP="plugdev" #HTC
SUBSYSTEM=="usb", ATTR{idVendor}=="12d1", MODE="0666", GROUP="plugdev" #Huawei
SUBSYSTEM=="usb", ATTR{idVendor}=="24e3", MODE="0666", GROUP="plugdev" #K-Touch
SUBSYSTEM=="usb", ATTR{idVendor}=="2116", MODE="0666", GROUP="plugdev" #KT Tech
SUBSYSTEM=="usb", ATTR{idVendor}=="0482", MODE="0666", GROUP="plugdev" #Kyocera
SUBSYSTEM=="usb", ATTR{idVendor}=="17ef", MODE="0666", GROUP="plugdev" #Lenovo
SUBSYSTEM=="usb", ATTR{idVendor}=="1004", MODE="0666", GROUP="plugdev" #LG
SUBSYSTEM=="usb", ATTR{idVendor}=="22b8", MODE="0666", GROUP="plugdev" #Motorola
SUBSYSTEM=="usb", ATTR{idVendor}=="0e8d", MODE="0666", GROUP="plugdev" #MTK
SUBSYSTEM=="usb", ATTR{idVendor}=="0409", MODE="0666", GROUP="plugdev" #NEC
SUBSYSTEM=="usb", ATTR{idVendor}=="2080", MODE="0666", GROUP="plugdev" #Nook
SUBSYSTEM=="usb", ATTR{idVendor}=="0955", MODE="0666", GROUP="plugdev" #Nvidia
SUBSYSTEM=="usb", ATTR{idVendor}=="2257", MODE="0666", GROUP="plugdev" #OTGV
SUBSYSTEM=="usb", ATTR{idVendor}=="10a9", MODE="0666", GROUP="plugdev" #Pantech
SUBSYSTEM=="usb", ATTR{idVendor}=="1d4d", MODE="0666", GROUP="plugdev" #Pegatron
SUBSYSTEM=="usb", ATTR{idVendor}=="0471", MODE="0666", GROUP="plugdev" #Philips
SUBSYSTEM=="usb", ATTR{idVendor}=="04da", MODE="0666", GROUP="plugdev" #PMC-Sierra
SUBSYSTEM=="usb", ATTR{idVendor}=="05c6", MODE="0666", GROUP="plugdev" #Qualcomm
SUBSYSTEM=="usb", ATTR{idVendor}=="1f53", MODE="0666", GROUP="plugdev" #SK Telesys
SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", MODE="0666", GROUP="plugdev" #Samsung
SUBSYSTEM=="usb", ATTR{idVendor}=="04dd", MODE="0666", GROUP="plugdev" #Sharp
SUBSYSTEM=="usb", ATTR{idVendor}=="054c", MODE="0666", GROUP="plugdev" #Sony
SUBSYSTEM=="usb", ATTR{idVendor}=="0fce", MODE="0666", GROUP="plugdev" #Sony Ericsson
SUBSYSTEM=="usb", ATTR{idVendor}=="2340", MODE="0666", GROUP="plugdev" #Teleepoch
SUBSYSTEM=="usb", ATTR{idVendor}=="0930", MODE="0666", GROUP="plugdev" #Toshiba
SUBSYSTEM=="usb", ATTR{idVendor}=="19d2", MODE="0666", GROUP="plugdev" #ZTE
SUBSYSTEM=="usb", ATTR{idVendor}=="0414", MODE="0666", GROUP="plugdev" #Gigabyte

#Ejecutar el comando:
sudo udevadm control --reload-rules
#Esto con el objetivo de no reiniciar tu PC
#Y refrescar los Udev en "caliente"


#Por ultimo simplemente volver a conectar nuestro device a la computadora y ejecutar
adb devices

List of devices attached
M4 SS1070       device



martes, 18 de julio de 2017

miércoles, 1 de marzo de 2017

Seguridad con SpringBoot y CSRF

La seguridad con con Springboot es realmente sencilla.
Para activar la Basic Html, simplemente creamos un conjunto de clases de esta forma:

@EnableWebSecurity
@Configuration
@EnableSpringHttpSession
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${com.pmj.dyo.configuration.security.user}")
    private String user;

    @Value("${com.pmj.dyo.configuration.security.password}")
    private String password;
    
    @Value("${com.pmj.dyo.configuration.csrf.login}")
    private String loginPage;
    
    @Value("${com.pmj.dyo.configuration.csrf.allow}")
    private String[] patterns;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers(patterns).permitAll().and().csrf().disable().httpBasic().and()
                .addFilterAfter(csrfFilter(patterns), FilterSecurityInterceptor.class)
                .addFilterAfter(new CsrfGrantingFilter(loginPage), CsrfFilter.class);
    }

    private Filter csrfFilter(String[] patterns) {
        CsrfFilter csrfFilter = new CsrfFilter(csrfTokenRepository());
        csrfFilter.setRequireCsrfProtectionMatcher(csrfProtectionMatcher(patterns));
        return csrfFilter;
    }

    private NoAntPathRequestMatcher csrfProtectionMatcher(String[] patterns) {
        return new NoAntPathRequestMatcher(patterns);
    }

    private CsrfTokenRepository csrfTokenRepository() {
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName(X_XSRF_TOKEN);
        return repository;
    }

    /**
     * bCryptPasswordEncoder
     * 
     * @return
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * configureGlobal
     * 
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser(user).password(password).roles("USER");
    }

    /**
     * sessionRepository
     * 
     * @return
     */
    @Bean
    @SuppressWarnings("rawtypes")
    public SessionRepository sessionRepository() {
        return new MapSessionRepository();
    }

    /**
     * sessionStrategy
     * 
     * @return
     */
    @Bean
    public HeaderHttpSessionStrategy sessionStrategy() {
        return new HeaderHttpSessionStrategy();
    }

    /**
     * @return the user
     */
    public String getUser() {
        return user;
    }

    /**
     * @param user
     *            the user to set
     */
    public void setUser(String user) {
        this.user = user;
    }

    /**
     * @return the password
     */
    public String getPassword() {
        return password;
    }

    /**
     * @param password
     *            the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////


public class CsrfGrantingFilter implements Filter {

    public static final String X_XSRF_TOKEN = "X-XSRF-TOKEN";

    private String loginPage;

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * Login Page injection
     * @param loginPage
     */
    public CsrfGrantingFilter(String loginPage) {
        this.loginPage = loginPage;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        CsrfToken csrf = (CsrfToken) servletRequest.getAttribute(CsrfToken.class.getName());
        String token = csrf.getToken();
        if (token != null && isAuthenticating(servletRequest)) {
            servletRequest.setAttribute(X_XSRF_TOKEN, true);
        } else {
            servletRequest.setAttribute(X_XSRF_TOKEN, false);
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    private boolean isAuthenticating(ServletRequest servletRequest) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        return request.getRequestURI().equals(loginPage);
    }

    @Override
    public void destroy() {
        LogUtil.info(log, this.toString());
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        LogUtil.info(log, filterConfig.toString());
    }
}

///////////////////////////////////////////////////////////////////////////////

@RequestMapping(method = POST, path = "", produces = APPLICATION_JSON_VALUE)
    public Map login(@RequestBody UserEntry userEntry, ServletRequest servletRequest,
            ServletResponse servletResponse) {
        boolean xsrfAvailable = (boolean) servletRequest.getAttribute(X_XSRF_TOKEN);
        Authorization aut = client.login(userEntry);
        Map resolve = new HashMap(); 
        if (aut != null && aut.isAccepted() && xsrfAvailable) {
            CsrfToken csrf = (CsrfToken) servletRequest.getAttribute(CsrfToken.class.getName());
            String token = csrf.getToken();
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            Cookie cookie = new Cookie(X_XSRF_TOKEN, token);
            cookie.setPath("/");
            cookie.setSecure(true);
            response.addCookie(cookie);
            resolve.put(STATUS, AUTHORIZED);
            resolve.put(USERNAME, username);
            resolve.put(PASSWORD, password);
            resolve.put(ROLE, aut.getRole());
            resolve.put(TOKEN, token);
        } else {
            resolve.put(STATUS, UNAUTHORIZED);
        }
        return resolve;
    }


Básicamente lo que sucede en este ejemplo es lo siguiente:
  1.  El punto de control que genera el Cross Site Request Forgery es /login
  2. Si el login no es efectivo el CSRF no aparece como set en el cookie
  3. Ahora bien el toque esta en que el body también presenta el basic autentification y el CSRF. De esta forma es mas sencillo que el Javascript almacene los datos.
  4. Cada nuevo request debe enviar los Basic Login y el CSRF.
Dentro de la aplicacion pasa esto:
  1.  La aplicacion fija el toking en el login. 
  2. Si cualquier otro request que no este en la lista de permitidos es invocada. Revisa el Basic Aut y el CSRF toking. La clase que genera los toking es CsrfGrantingFilter.

martes, 21 de febrero de 2017

Java Poi con Maven y Springboot Restful (Usando imagenes)

Este articulo no es un "tutorial". Es un conjunto de notas de mi experiencia.
Si vas a adentrarte en este articulo. Debes conocer como usar POI. Maven y Springboot Restfull.

 Lo primero que debemos hacer es configurar el POM del maven para usar  todas las características de excel. Claramente algunas no son necesarias. Pero es mejor tenerlas para usar TODO lo que Excel ofrece:


      dom4j
      dom4j
      1.6.1
  
  
   org.apache.poi
   poi
   3.9
  
  
   org.apache.poi
   poi-ooxml
   3.9
  
  
      org.apache.commons
      commons-collections4
      4.1
  
  
      org.apache.xmlbeans
      xmlbeans
      2.3.0
  

Ahora bien para generar reportes POI ofrece varias características principalmente varias vistas de distintos tipos. Para no complicarse vamos a usar lo que SpringBoot ofrece.


No es recomendable... generar los reportes como stream dentro de los HttpServletResponse . Porque se puede cortar el request y el reporte quedar corrupto. Usar siempre las vistas.

//Lo primero debemos escojer un view que se ajuste a las necesidades. Al final hablare de los distintos tipos
public class ExcelBuilder extends AbstractXssfStreamingView {

    @Override
    public void buildExcelDocument(Map model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception {
        //Aqui ya vienen inicializado el workbook.
        //La unica funcion es implementar. Seria; Crear el sheet, crear filas, celdas, poner estilos, etc
        //No debemos preocuparnos por escribir el workbook en el response. Solo dejemos que el metodo termine.
        //Y Spring sabe que debe tomar el workbook y generar un archivo.
    }

}

//Debemos configurar que la vista sea usada para los endpoint
//Sencillamente podemos usar esto:
@Configuration
public class ExcelBuilderConfig {
    
    @Bean
    public ExcelBuilder ExcelBuilder() {
        return new ExcelBuilder();
    }

}

//Ahora bien solo resta usarlo en algun endpoint
@Api(value = "contest", description = "the contest API")
public interface ContestApi {

    /**
     * contestApplicantExport
     *
     * @param contest
     * @param brandid
     * @return
     */
    @ApiOperation(value = "", notes = "Returns a excel file report according to the information provided (A5:ContestApplicantExport)")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "gallery response"),
            @ApiResponse(code = 200, message = "unexpected error")})
    @RequestMapping(value = "/contest/{contest}/{brandid}", produces = {
            "application/ms-excel"}, method = RequestMethod.GET)
    @org.springframework.web.bind.annotation.GetMapping("/dyo-config")
    ModelAndView contestApplicantExport(
            @ApiParam(value = "Filter based on a specific contest", required = true) @PathVariable("contest") String contest,
            @ApiParam(value = "Filter based on a specific brand", required = true) @PathVariable("brandid") String brandid,
            @ApiParam(value = "HttpServletResponse to setup the excel ouput", required = true) HttpServletResponse response);
}



@Configuration
@RestController
public class ContestApiController implements ContestApi {


    @Override
    public ModelAndView contestApplicantExport(
            @ApiParam(value = "Filter based on a specific contest", required = true) @PathVariable("contest") String contest,
            @ApiParam(value = "Filter based on a specific brand", required = true) @PathVariable("brandid") String brandid,
            @ApiParam(value = "HttpServletResponse to setup the excel ouput", required = true) HttpServletResponse response) {

        Book book = /*Cualquier Objecto*/
        return new ModelAndView("ExcelBuilder", "book", book); //Note que "book" es el dato que se puede obtener de la vista
    }
}

Los diferentes tipos de vistas presentes generan diferentes tipos de workbook.
Cada una con características diferentes:
Podemos mencionar XSSFWorkbook and SXSSFWorkbook.

La clase AbstractXssfStreamingViewes funcional para archivos muy grandes. Sin embargo tiene la tendencia a no funcionar de buena manera con las imágenes. Puede que te tengas que enfrentar a Streaching o a resize ridículos. Después de muchos intentos y de canas verdes. Descubrí que si uno quiere insertar imágenes. La mejor forma es usando XSSFWorkbook.

Para eso escribí mi propia versión de un AbstractXlsxView.

//Simplemente hacemos esto:
public abstract class AbstractXssfStreamingView extends AbstractXlsxView {

    @Override
    protected XSSFWorkbook createWorkbook(Map model, HttpServletRequest request) {
        return new XSSFWorkbook();
    }

}

//Y lo utilizamos:
public class ExcelBuilder extends AbstractXssfStreamingView {

    @Override
    public void buildExcelDocument(Map model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Book book = (Book) model.get("book"); //Usar de referencia al ejemplo de arriba para obtener los datmos del model
        //Tu codigo aqui
    }

}

//En el codigo de ejemplo para agregar imagenes creo un pequeno factory.
//La implementacion es por un conjunto de intefacez de esta manera:

@FunctionalInterface
public interface CellProduceInterface {

    /**
     * update
     * 
     * @param excelCell
     * @param data
     * @param workbook
     * @param sheet
     * @param point
     */
    public void update(org.apache.poi.ss.usermodel.Cell excelCell, Cell data, Workbook workbook, Sheet sheet, Point point);

}

//Y su implementacion
//Notas:
//El excelCell es el cell de POI
//El data es el modelo de la informacion de la celda
//Woorkbook asociado con la vista 
//Sheet actual en que que excelCell esta presente
public class ImageCellProduce implements CellProduceInterface {

    private static final Logger log = LoggerFactory.getLogger(ImageCellProduce.class);

    @Override
    public void update(org.apache.poi.ss.usermodel.Cell excelCell, Cell data, Workbook workbook, Sheet sheet,
            Point point) {
        String path = (String) data.getValue();
        if (path != null) {
            byte[] bytes = ExcelUtil.convertImagePathToBytes(path);
            if (bytes.length > 0) {
                int pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
                CreationHelper helper = workbook.getCreationHelper();
                Drawing drawing = sheet.createDrawingPatriarch();
                ClientAnchor anchor = helper.createClientAnchor();
                //Col and Cell pos
                anchor.setCol1(point.y);
                anchor.setRow1(point.x);
                //Height is not possible without stretched the image
                Picture pict = drawing.createPicture(anchor, pictureIdx);
                if (data.getWidth() != -1) {
                    sheet.setColumnWidth(point.y, ExcelUtil.calculatePoiWidthSize(data.getWidth()));
                    data.setWidth(-1);//Asi no se vuelve a aplicar el estilo mas adelante
                }
                if (data.getHeight() != -1) {
                    excelCell.getRow().setHeight((short) ExcelUtil.calculatePoiWidthSize(data.getHeight()));
                    data.setHeight(-1); //Asi no se vuelve a aplicar el estilo mas adelante
                }
                data.setAutoSize(false);
                pict.resize(); //Como es XSSFWorkbook no deberia presentar problemas con el size y mantiene el original
            }
        } else {
            log.info("ImageCellProduce: Ignoring image on " + point);
        }
    }

}

AEM hablemos del arquetipo 11

Cuando creamos un proyecto con AEM. Siempre es importante saber que arquetipo estamos usando. Pues esto me determinara que source, herramien...