Tech Tidbits - Ruby, Ruby On Rails, Merb, .Net, Javascript, jQuery, Ajax, CSS...and other random bits and pieces.

Saturday, July 26, 2008

authentication token

I had a need to both validate the source of data sent to a web service and to only accept the data if it was sent within a limited time period. One thing I didn't need, (but I imagine would be necessary in most cases) was to only allow the request once, in which case you would probably want to store the token in a database and flag it once it had been "redeemed."

To create the token, first concatenate current time in epoch seconds (or whatever time format you like, but stick with UTC since the end user may be any where in the world), username, and a password, then m5d and base64 that concatenated string and prepend it with the same timestamp, separated by a colon (':').

#!/usr/bin/ruby
require 'digest/md5'
require 'base64'

# Username/password
UID = 'marypoppins'
PWD = "supercalifragilisticexpialidocious"

# create token using utc secconds, uid, and pwd
def create_token(t=nil)
timestamp = t || Time.now.utc.to_i.to_s
plaintext = timestamp + UID + PWD
md5 = Digest::MD5.digest(plaintext)
return timestamp + ':' + Base64.encode64(md5).strip
end

# validate token
def validate_token(token)
token_timestamp = token.split(':')[0]
token2 = create_token(token_timestamp)
return token == token2 ? true : false
end

# validate timestamp and token
def validate_token_and_timestamp(token,num_secs=60)
token_timestamp = token.split(':')[0]
return nil if Time.now.to_i - token_timestamp.to_i > num_secs
token2 = create_token(token_timestamp)
return token == token2 ? true : false
end

The most basic example: create a token and validate it by creating a new token using the timestamp in the original token with the known user id and password. If the two tokens match, the original token is valid (which it will be in this contrived example):

# create a token and validate
if (validate_token(create_token))
puts "Valid token"
else
puts "Invalid token"
end

To check if the token was passed within a certain time period, just compare the timestamp on the original token with current time.

# create a token and validate timestamp and token
if (validate_token_and_timestamp(create_token,10))
puts "Valid token"
else
puts "Invalid token"
end


For demonstration only, use sleep to timeout a token:

# create a token, but let token timeout before trying to validate
timeout_token = create_token
sleep 15
if (validate_token_and_timestamp(timeout_token,10))
puts "Valid token"
else
puts "Invalid token"
end

Monday, July 21, 2008

Self Referential Associations with has_many :through


db/migrate/001_create_teletubbies.rb

class CreateTeletubbies < ActiveRecord::Migration
def self.up
create_table :teletubbies do |t|
t.string :name
t.timestamps
end
Teletubby.create(:name => 'Tinky Winky')
Teletubby.create(:name => 'Dipsy')
Teletubby.create(:name => 'Laa Laa')
Teletubby.create(:name => 'Po')
end

def self.down
drop_table :teletubbies
end
end

db/migrate/002_create_friendships.rb

class CreateFriendships < ActiveRecord::Migration
def self.up
create_table :friendships do |t|
t.integer :teletubby_id
t.integer :friend_id
t.timestamps
end
# Tinky Winky loves all the other Teletubbies very much
Friendship.create(:teletubby_id => 1, :friend_id => 2)
Friendship.create(:teletubby_id => 1, :friend_id => 3)
Friendship.create(:teletubby_id => 1, :friend_id => 4)
# Dipsy loves all the other Teletubbies very much
Friendship.create(:teletubby_id => 2, :friend_id => 1)
Friendship.create(:teletubby_id => 2, :friend_id => 3)
Friendship.create(:teletubby_id => 2, :friend_id => 4)
# Laa Laa loves Po very much
Friendship.create(:teletubby_id => 3, :friend_id => 4)
# Po loves Laa Laa very much
Friendship.create(:teletubby_id => 4, :friend_id => 3)
# Big Hug
end

def self.down
drop_table :friendships
end
end

app/models/teletubby.rb

class Teletubby < ActiveRecord::Base
# a teletubby 'has_many' friendships (teletubbies love each other very much)
has_many :friendships

# a teletubby 'has_many' friends ':through' friendships relationship
has_many :friends, :through => :friendships
end

app/models/friendship.rb

class Friendship < ActiveRecord::Base
# ActiveRecord already knows what a teletubby is (reflection)
belongs_to :teletubby

# No actual friendship model, so add :class_name and :foreign_key
belongs_to :friend, :class_name => "Teletubby", :foreign_key => "friend_id"
end


The database should look a little something like this:

mysql> select * from teletubbies;
+----+-------------+---------------------+---------------------+
| id | name | created_at | updated_at |
+----+-------------+---------------------+---------------------+
| 1 | Tinky Winky | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 2 | Dipsy | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 3 | Laa Laa | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 4 | Po | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
+----+-------------+---------------------+---------------------+
4 rows in set (0.00 sec)

mysql> select * from friendships;
+----+--------------+-----------+---------------------+---------------------+
| id | teletubby_id | friend_id | created_at | updated_at |
+----+--------------+-----------+---------------------+---------------------+
| 1 | 1 | 2 | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 2 | 1 | 3 | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 3 | 1 | 4 | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 4 | 2 | 1 | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 5 | 2 | 3 | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 6 | 2 | 4 | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 7 | 3 | 4 | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
| 8 | 4 | 3 | 2008-07-21 15:41:42 | 2008-07-21 15:41:42 |
+----+--------------+-----------+---------------------+---------------------+
8 rows in set (0.00 sec)


Use script/console to check the relationships:

dsparling-imbps-computer:~/Sites/teletubbies dsparlingimbp$ script/console
Loading development environment (Rails 2.0.2)
>> t1 = Teletubby.find(1)
=> #<Teletubby id: 1, name: "Tinky Winky", created_at: "2008-07-21 15:41:42", updated_at: "2008-07-21 15:41:42">
>> t1.friends.each { |f| puts "#{t1.name} loves #{f.name} very much." }; false
Tinky Winky loves Dipsy very much.
Tinky Winky loves Laa Laa very much.
Tinky Winky loves Po very much.
=> false
>> t2 = Teletubby.find(2)
=> #<Teletubby id: 2, name: "Dipsy", created_at: "2008-07-21 15:41:42", updated_at: "2008-07-21 15:41:42">
>> t2.friends.each { |f| puts "#{t2.name} loves #{f.name} very much." }; false
Dipsy loves Tinky Winky very much.
Dipsy loves Laa Laa very much.
Dipsy loves Po very much.
=> false
>> t3 = Teletubby.find(3)
=> #<Teletubby id: 3, name: "Laa Laa", created_at: "2008-07-21 15:41:42", updated_at: "2008-07-21 15:41:42">
>> t3.friends.each { |f| puts "#{t3.name} loves #{f.name} very much." }; false
Laa Laa loves Po very much.
=> false
>> t4 = Teletubby.find(4)
=> #<Teletubby id: 4, name: "Po", created_at: "2008-07-21 15:41:42", updated_at: "2008-07-21 15:41:42">
>> t4.friends.each { |f| puts "#{t4.name} loves #{f.name} very much." }; false
Po loves Laa Laa very much.
=> false

Saturday, July 12, 2008

Screencast Addiction

Ruby/RoR
http://www.railsenvy.com/speaking
http://railscasts.com
http://www.rubyplus.org
http://www.buildingwebapps.com/podcasts
http://peepcode.com
http://www.pragprog.com/screencasts
http://envycasts.com

Rails Conf(s)
http://www.railsenvy.com/tags/conferences
http://www.railsenvy.com/2008/6/2/Railsconf-videos
http://blip.tv/search?q=rails+conf&x=0&y=0
http://www.confreaks.com
http://mwrc2008.confreaks.com/
The RubyFringe Videos

OSCON
http://www.railsenvy.com/2008/7/29/oscon-videos
http://blip.tv/search?q=oscon&x=0&y=0

Other
ThoughtWorks IT Matters and ThoughtWorks Talks
http://www.justin.tv/signals37/archive
http://developer.yahoo.com/yui/theater/
http://www.theflexshow.com/blog/
Gooogle search railsconf
Google search oscon
InfoQ Presentations

 

ruby-mp3info

I have an old SanDisk mp3 player that I use to listen to music and podcasts. Though it's a rather small card (512M), it's still annoying to have to scroll through my single playlist to get to a specific file.

The SanDisk sorts by the mp3 info title tag, so I wrote a small script that I run on my podcast files before I load them on my player so that they will show up first in the playlist by prepending '01_' to the mp3 title tag (and using the mp3 filename if the title is nil).

First, install the ruby-mp3info gem if you don't have it:

gem install ruby-mp3info

edit_mp3info_title.rb

require 'rubygems'
require "mp3info"

dir = Dir.open('/Users/dsparlingimbp/PODCAST/mp3')
puts "mp3 directory: #{dir.path}"

dir.each do |file|
next unless /\.mp3$/ =~ file
puts "mp3: #{file}"

mp3file = "#{dir.path}/#{file}"

Mp3Info.open(mp3file) do |mp3|
puts "original title: #{mp3.tag.title}"
title = '01_' + (mp3.tag.title || file)
mp3.tag.title = title
puts "new title: #{mp3.tag.title}"
end

end

Friday, July 11, 2008

Podcast Addiction

It's bad enough that I subscribe to more RSS feeds than I can possibly read in a day, but I've also become addicted to podcasts...many are tech podcasts, but I listen to a wide variety...

From my current iTunes podcasts directory:

Andy Jordan's Tech Diary
Audible Ajax
boagworld
The Bugle
The Christian Science Monitor - News
Digital Media Insider
The Flex Show (podcast and screencast)
FOO Casts
The French Pod Class
Hanselminutes
IT Conversations
L33t Tech News
Learning Rails (now a screencast)
.NET Rocks!
php_abstract
php|architecht
podcasts - Channel 9
Pragmatic Programmers
Radio Allegro
Rails Envy Podcast
Railscasts (screencast)
Rubyology
Ruby on Rails Podcast
StackOverflow
TalkCrunch
The Thirsty Developer
This American Life
Wall Street Journal Tech News Briefings
Wall Street Journal on Small Business
Wall Street Journal's Your Money Matters
The Washington Institute Podcast
The Web 2.0 Show
Web Design TV
WebDevRadio.com

A few others:
French Vibes

And a couple of "top tech podcasts" links:
The top 40 tech podcasts - .net magazine
Top 10 Best Technology Podcasts

Thursday, July 10, 2008

Ignore exisiting directory in subverion Rails repo

I needed to ignore the log directory of an existing Rails app I had checked out:

svn up
svn delete log
svn commit -m 'removed log dir'
mkdir log
svn add --non-recursive log
svn propset svn:ignore "*" log/
svn commit -m "Add log dir, ignore contents"

With a little help from:
http://rails.wincent.com/wiki/Checking_a_new_Rails_application_into_an_existing_Subversion_repository

About Me

My photo
Developer (Ruby on Rails, iOS), musician/composer, Buddhist, HSP, Vegan, Aspie.

Labels