Thinking on hiring me?

Please read

Fernando Guillén

a Freelance Web Developer

cabecera decorativa

software development as an artistic expression

Archive for the ‘nota mental’ Category

Jueves, Febrero 5th, 2009

Ruby on Rails, Agujero de seguridad en mi API xml

En uno de los pet-projects en los que ando he intentado construir todo un poco académicamente rollo RESTfull (o casi full :) ) y tal.

Pero lo que nos acomete ahora es que he intentado soportar en todas las acciones de controlador en formato .xml.. ¿para qué?.. pues no sé.. pero ahí que lo he dejado.. sin testear ni nada, por si acaso.

Resulta que el otro día me levanté inquieto por la mañana con el pensamiento de que las si se hacía una petición a una acción con el formato .xml la respuesta que le iba a llegar al cliente no me iba a gustar nada.. pero luego se me pasaba y me olvidaba.

Bueno pues la acabo de hacer, acabo de hacer la petición con el formato .xml y, confirmando mis miedos, la respuesta no me ha gustado nada.

http://127.0.0.1:3000/people/103-fernando-guillen.xml (no clckes.. no te va a funcionar :))

<user>
<activated-at type="datetime">2009-02-02T01:01:22+01:00</activated-at>
<activation-code nil="true"></activation-code>
<company-name>Mi company</company-name>
<company-url>http://micompany.com</company-url>
<created-at type="datetime">2009-02-02T01:01:10+01:00</created-at>
<crypted-password>8e2434d3e574c95f4cfd0f457b20c8959ecbf04b</crypted-password>
<iail>xxxx@xxx.com</iail>
<id type="integer">103</id>
<login>fguillen</login>
<name>Fernando Guillen</name>
<password-reset-code>fcc786cd2137679f1055976989f5469607c9d663</password-reset-code>
<permalink>fernando-guillen</permalink>
<personal-web-name>mi web</personal-web-name>
<personal-web-url>http://google.es</personal-web-url>
<public-profile type="boolean">true</public-profile>
<remember-token nil="true">
<remember-token-expires-at type="datetime" nil="true">
<role>User</role>
<salt>5c26f52452d00dbd997f8c6998f5c3de1e4f53f3</salt>
−
<text>
Vaya texto guapo que pongo aquí:
 
Con varias líneas
 
y alert("hola");
</text>
<updated-at type="datetime">2009-02-04T18:52:21+01:00</updated-at>
</remember-token-expires-at>
</remember-token></user>

En la respuesta están todos los datos del usuario 103.. ¡todos! hasta la password, aunque está cifrada.. y ¡¡el email!!.. y si hacemos:

http://127.0.0.1:3000/people.xml

Entonces es cuando casi me desmayo..

Alguien podría descargarse toda la base de datos de mi aplicación a base de peticiones .xml o usando ActiveResource.. ¿qué hago ahora?…

  • …¿Eliminar por completo el soporte .xml?
  • …¿Crear vistas .xml para sólo devolver los atributos públicos?
  • …¿Alguna manera de decirle al modelo o al controlador qué atributos quieres que aparezcan en el formato .xml?

Antes de liarme a postear de manera alarmista en éste mi blog, sin solución alguna que ofrecer, comenté estas inquietudes en la sagrada lista ror-es y aparecieron el señor Blat y Pablo Formoso al rescate con un buen par de links y sugerencias:

Estos dos links te enseñan a pasarle parámetros a las invocaciones del método .to_xml de tus modelos de tal modo que puedes poner en tus controladores cosas como esta:

format.xml  { render :xml => user.to_xml( :except => [:email, :crypted_password] ) }

Y así empecé pero no me acabó de convencer pues tengo miedo de olvidarme algún controlador y decido pasar la seguridad al modelo y sobrescribir el método .to_xml de la clase User. Pero los métodos para hacer esto que se sugieren por ahí son un poco tediosas con llamadas a Builder::XmlMarkup y vamos.. un rollo. Lo suyo sería trasladar la facilidad del :except y el :only a la sobrescritura del .to_xml y en los comentarios de este post tenemos una solución bien ingeniosa:

  alias_method :ar_to_xml, :to_xml
  def to_xml(options = {})
    default_except = [
      :activated_at,
      :activation_code,
      :email,
      :login,
      :password_reset_code,
      :permalink,
      :public_profile,
      :role,
      :crypted_password,
      :salt,
      :remember_token,
      :remember_token_expires_at,
      :created_at,
      :updated_at
    ]
    options[:except] = (options[:except] ? options[:except] + default_except : default_except)
    self.ar_to_xml( options )
  end

Sobre todo fíjate en el truqui del alias_method para seguir permitiendo llamadas al comportamiento original del to_xml.

Si sigues los comentarios del hilo que te he indicado verás que las propuestas continúan hasta formar un solución polivalente que bien se puede convertir en un buen parche para Rails.

Jueves, Enero 29th, 2009

Ruby: El operador ‘~>’ y el Gem::Requirement

En un hilo de ror-es con título: ‘Problema con carga de rubygem en App rails al hacer rake db:create && rake db:migrate‘, Andrés Gutierrez se debatía con las versiones de las gemas solicitadas por un proyecto Rails que esta intentando arrancar.

La configuración del proyecto le requería tener instalada una gema y para indicarle la versión se utilizaba el operador ‘~>‘.. no el ‘==’, el ‘>’, el ‘>=’.. no, tenía el simbolito raro ese de la ñ.

En concreto la especificación de la gema requerida es tal que así:

config.gem 'mislav-will_paginate', :version => '~> 2.2.3', :lib => 'will_paginate', :source => 'http://gems.github.com'

Su duda venía de que él tenía instalada la versión 2.3.6 de la gema en concreto y no entendía porque no cumplía los requisitos.

El caso es que me piqué y al más puro ‘estilo Daniel R. Troitiño‘ ahí que fui a las tripas del código a ver que demonios hacía el operador ése..

Si miramos el fichero:

 $ mate rubygems/requirement.rb

Tenemos la lista de operadores y lo que se hace con cada uno:

 OPS = {
   "="  =>  lambda { |v, r| v == r },
   "!=" =>  lambda { |v, r| v != r },
   ">"  =>  lambda { |v, r| v > r },
   "<"  =>  lambda { |v, r| v < r },
   ">=" =>  lambda { |v, r| v >= r },
   "<=" =>  lambda { |v, r| v <= r },
   "~>" =>  lambda { |v, r| v >= r && v < r.bump }
 }

Donde ‘v‘ parece ser la versión disponible y ‘r‘ la requerida
Lo raro del operador ~> es que a la versión requerida le hace un bump
y el código de esto está en otro fichero:

 $ mate rubygems/version.rb

Aquí lo pego:

 # Return a new version object where the next to the last revision
 # number is one greater. (e.g.  5.3.1 => 5.4)
 def bump
   ints = build_array_from_version_string
   ints.pop if ints.size > 1
   ints[-1] += 1
   self.class.new(ints.join("."))
 end

Al parecer a la versión requerida en la aplicación, en este caso ‘2.2.6‘, le hace un bump y la deja en ‘2.3‘ y por lo tanto la
condición:

 '2.3.6' < '2.3'

No se cumple .. como podemos comprobar aquí:

irb>
>> req = Gem::Requirement.create("~> 2.2.3")
>> req.satisfied_by?( Gem::Version.new('2.3.6') )
=> false
>> req.satisfied_by?( Gem::Version.new('2.2.6') )
=> true
>> req.satisfied_by?( Gem::Version.new('2.1.6') )
=> false

Lo que parece que quiere el operador ~> es que la gema disponible esté
en la misma familia X.X que la gema requerida.

Hasta aquí he llegado. Si por favor conoces la documentación oficial donde se explica pásame el link para darme bien de tollejas.

Jueves, Enero 22nd, 2009

Como linkar a la última versión de un fichero en GitHub

Sino conoces github no sigas leyendo.. y échale un vistazo, sino es el caso igual te interese este truquito que he descubierto trasteando.

Resulta que si quieres compartir un link de un fichero de un proyecto en github puedes navegar por los ficheros llegar al que quieres compartir y copiar el link que muestra la página, algo como esto:

http://github.com/fguillen/euruko_app/blob/b94f2458b54fdea82e970810de5e9bbe9f63a385/doc/TODO.md

Y bien, funciona, o eso parece.. pero no, porque entonces estarás compartiendo el estado congelado del fichero en un commit determinado osea que cuando tus amigos sigan el link dentro de unos diás, después de unos commits, seguirán viendo la misma versión ya anticuada.

El truco para compartir un link que siempre tenga la última versión disponible es sustituir el identificador del commit por el alias de ‘último commit‘ que es ‘HEAD‘, tal que así:

http://github.com/fguillen/euruko_app/blob/HEAD/doc/TODO.md

Ahí queda :)

Sábado, Diciembre 27th, 2008

Shell: script para borrar todos los delete pendientes con GIT

Tengo la mala costumbre de borrar los ficheros desde mi editor de texto así que cuando vuelvo a consola y hago un ‘git status‘ me salen un montón de ficheros borrados y no encuentro el comando de ‘git‘ para decirle: cárgatelos todos en el próximo commit.

Así que me he hecho este script para decirle exactamente eso:

$ git rm `git status | grep "deleted" | cut -f 2 -d ":" | cut -d " " -f 5`

Cuidadín con él eh :).. a ver si no va a estar bien..

Actualización: como dice Juanma en los comentarios podemos hacerlo mucho más elegantemente con:

git add -u
Jueves, Septiembre 4th, 2008

Ruby, sustituyendo matches de una regex en un String con matches de la misma regex

Madre mía que título bueno me ha quedado :)

Esto es una nota mental y puede que si no sabes de que hablo no te interese y si sabes de que hablo ya lo sepas.

Intento sustituir una parte de un String por otra parte que se encuentra en el mismo String.

Es decir, tengo esto “me gusta el heavy y no me gusta el country” y quiero obtener esto otro “me gusta el country y no me gusta el heavy”.

Se puede hacer así:

>> "me gusta el heavy y no me gusta el country".gsub( /me gusta (.*) y no me gusta el (.*)/, 'me gusta \2 y no me gusta el \1' )
=> "me gusta country y no me gusta el el heavy"

Donde \1 y \2 son las ocurrencias de los (.*).

El ejemplo puede parecer un poco tonto, pero esta utilidad me ha venido muy bien para quedarme con el contenido de una etiqueta html:

>> "<body>contenido</body>".gsub( /.*<body[^>]*>(.*)<\/body>.*/mix, '\1' ).strip
=> "contenido"

Que se puede solucionar de muchas otras maneras pero esta me ha parecido la más sencilla.

Escribo esta nota mental por lo poco intuitivo que es el uso de ‘\1′ como cadena sustitutiva pues se supone que las cadenas entre comillas simples no se interpretan…

Otra cosa es que google siempre me llevaba a soluciones como esta:

>> "me gusta el heavy y no me gusta el country".gsub( /me gusta (.*) y no me gusta el (.*)/, "me gusta #{$2} y no me gusta el #{$1}" )
=> "me gusta country y no me gusta el el heavy"

Y aunque parece que funciona no es así porque los contenidos de $1 y $2 los ha cogido del gsub anterior y no de éste:

>> "me gusta el musical y no me gusta el flamenco".gsub( /me gusta (.*) y no me gusta el (.*)/, "me gusta #{$2} y no me gusta el #{$1}" )
=> "me gusta country y no me gusta el el heavy"
>> "me gusta el musical y no me gusta el flamenco".gsub( /me gusta (.*) y no me gusta el (.*)/, "me gusta #{$2} y no me gusta el #{$1}" )
=> "me gusta flamenco y no me gusta el el musical"
Miércoles, Agosto 27th, 2008

Ruby, uso de caracteres no ASCII en la consola IRB del Mac OS X

Mis versiones:

  • Mac OS X 10.5.4
  • Ruby 1.8.6 patchlevel 114
  • Rails 2.1.0

Tengo problemas para introducir caracteres no ASCII ( caracteres especiales, acentos y ñs ) en la consola IRB de Ruby así como también en la consola script/console de Rails.

Gracias a las lista ror-es lo he podido solucionar.

Resumo aquí a mi manera el post con la solución:

Instalamos la versión universal de la librería readline mediante MacPorts:

$ sudo port install readline +universal

Si te dá error de:

-bash: port: command not found

Asegúrate que tienes instalados los MacPorts y que tienes esto en tu .bash_profile:

export PATH=$PATH:/opt/local/bin
export MANPATH=$MANPATH:/opt/local/share/man
export INFOPATH=$INFOPATH:/opt/local/share/info

Instalamos la extesión Ruby para readline. Vigila la versión que te bajas, debe coincidir con tu versión de Ruby exactamente:

$ ruby --version
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]

v1_8_6_114

$ cd /tmp
$ svn co http://svn.ruby-lang.org/repos/ruby/tags/v1_8_6_114/ext/readline/ readline

Aplicamos un parchecito:

$ curl http://pastie.textmate.org/pastes/168767/download | patch readline/extconf.rb

Y compilamos, necesitarás tener instaladas las OS X developer tools:

$ cd readline
$ ruby extconf.rb
$ make
$ sudo make install

Si el script/console te sigue dando problemas asegúrate de tener bien la $KCODE:

$ script/console
Loading development environment (Rails 2.1.0)
>> puts $KCODE
UTF8
=> nil

Gracias de nuevo a Daniel Rodriguez Troitiño por la solución.

Jueves, Julio 31st, 2008

Ruby: cliente Pop SSL ó como leer tu cuenta Gmail desde Ruby.

Cómo ya dicen en muchas partes, pero me repito aquí para parecer muy listo y por marcarlo como nota mental, para acceder a tu cuenta Pop en Gmail desde Ruby hay que hacer una ñapa porque la versión actual de Ruby (1.8.6) no admite conexiones Pop bajo SSL.

Si si.. la 1.9 ya tiene el soporte, y dicen por ahí que descargándote el pop.rb de esta versión desde el repositorio y haciéndole un require ya funciona.. pero no, por lo menos a mí no porque me salta alguna dependencia con OpenSSl:

NoMethodError: undefined method `set_params' for #<openssl::ssl::sslcontext:0x210f4d0>;
</openssl::ssl::sslcontext:0x210f4d0>

Y no quería seguir tirando de el hilo.

El workarround que más me ha gustado ha sido el de montar un tunel ssh y hacer la petición desde el Net::POP3 al túnel y que el túnel se la haga llegar al pop de Gmail bajo SSL.

No voy a entrar en detalle porque ya lo hacen en muchos sitios.

Actualizado: acabo de ver un sitio dónde explican como usar la implementación pop.rb de la 1.9 sin el problema que me da a mí.

Los pasos resumidos que este post explica son:

1) Descargarte una implementación de la librería POP3: la versión 13778.

2) Ponerla en tu carpeta lib con este nombre por ejemplo: pop_ssl.rb con esto sobreescribiremos la implementación de esta librería que trae ruby por defecto con una versión mayor.

3) Descargarte este fichero y ponerlo también en lib: pop_gmail_client.rb

class PopGmailClient
  def self.read_account( account_user, account_pass )
    mails = []
    Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
    Net::POP3.start( 'pop.gmail.com', 995, account_user, account_pass ) do |pop|
      pop.each_mail do |m|
        mails << m.pop
      end
    end
    mails
  end
end

4) Requerirlos desde el environment.rb al final del todo con:

require 'pop_ssl'
require 'pop_gmail_client'

Y ya podemos usarlo como queramos:

$ script/console
Loading development environment (Rails 2.0.2)
>> PopGmailClient.read_account( "fguillen.testing@gmail.com", "xxx" )

O desde ruby directamente:

$ irb
>> require 'lib/pop_ssl.rb'
=> true
>> require 'lib/pop_client_gmail.rb'
=> true
>> PopGmailClient.read_account( "fguillen.testing@gmail.com", "xxx" )
Miércoles, Julio 23rd, 2008

Texto vertical con javascript

Ni CSS3 ni SVG ni leches.

<script>document.write( "el texto".replace( /(.)/g, "$1<br />" ) ); </script>

Ya sé que no queda vertical y que es un seudo horizo-vertical.. pero por lo menos funciona. ;)

Sábado, Julio 19th, 2008

2 horas programando para 4 líneas

Es lo que tiene ruby.

Llevo un par de horas intentando sacar un Float en formato ‘d.ddd.ddd,dd’. Hay muchas cosas en internet para conseguirlo, pero no todas funcionaban bien, y otras funcionaban demasiado bien, con un montón de opciones.

El caso es que hay un helper del ActionView que tiene la función number_to_currency pero es un cabroncete de helper y no podía acceder desde el modelo. También teníamos la gema Currency pero era un pedazo monstruo para la tontada que yo quería.

Al final la gema Scruffy me ha dado la pista y esto es lo que tengo:

class Float
  def en_euros
    parts = sprintf("%01.#{2}f", self).split('.')
    parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1.") + "," + parts[1].to_s
  end
end

Es un parchecito del Float para poder hacer esto:

>> 1234.566.en_euros
=> "1.234,57"
Viernes, Junio 27th, 2008

Ruby on Rails, el plugin cache-test y la activación de la caché en los tests

Este es mi primer apunte seudo-técnico sobre RoR. No es muy profundo, en realidad lo dejo caer aquí como nota mental.

Resulta que cuando instalas el interesantísimo plugin para testear cachés y sweepers: cache-test, éste tiene en su configuración la activación de las cachés en modo test:

ActionController::Base.perform_caching = true

Tanto en el fragment_cache_test.rb como en el page_cache_test.rb.

Hasta ahora esto no me había causado ningún conflicto pues sólo hacía una llamada a una página get/post cacheada en cada test y con la misma llamada comprobaba todo.

Pero con Shoulda se hacen varias llamadas a la misma página en cada llamada se comprueba una cosa y si la página está cacheada hay varias cosas que pueden fallar como éstas:

context "on GET to :show" do
  setup do
    get( :show, :id => '1' )
  end
  should_assign_to :variable1
  should_assign_to :variable2
  should_assign_to :variable3
  should_render_template :show
end

Aquí se hace una llamada get para cada should_ , el primero funcionará pero los posteriores al estar activada la caché fallarán porque la variable o la vista buscada tendrá valor ‘nil‘.

Este no es un problema de Shoulda, simplemente no me había aparecido hasta ahora. Con los tests normales surgirá igual si tienes la caché activada y haces 2 llamadas a la misma página cacheada y esperas encontrar una variable asignada, la segunda en ejecutarse fallará.

Workarround

Lo único que he encontrado por ahora es poner esto en el setup de los tests:

ActionController::Base.fragment_cache_store.reset

Para mí me funciona, para mis tests y para la configuración de mis cachés, puede que a ti no te funcione.

Y lo que si puede ser es que tengas una solución mejor, plis coméntala.

a Freelance Web Developer is proudly powered by WordPress
Entries (RSS) and Comments (RSS).

Creative Commons License
Fernando Guillen's blog by Fernando Guillen is licensed under a Creative Commons Attribution-NoDerivs 3.0 Unported License.