tokens 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +9 -0
- data/Gemfile.lock +102 -0
- data/README.rdoc +7 -39
- data/Rakefile +3 -3
- data/lib/tokens.rb +6 -139
- data/lib/tokens/active_record.rb +135 -0
- data/lib/tokens/generator.rb +12 -0
- data/lib/tokens/railtie.rb +11 -0
- data/lib/tokens/token.rb +1 -0
- data/lib/tokens/version.rb +6 -8
- data/spec/spec_helper.rb +8 -6
- data/spec/support/config/boot.rb +14 -0
- data/spec/support/config/database.yml +3 -0
- data/spec/support/log/test.log +9813 -0
- data/spec/support/models.rb +7 -0
- data/spec/tokens_spec.rb +69 -71
- data/templates/tokens.rb +22 -0
- data/tokens.gemspec +61 -0
- metadata +15 -2
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
abstract (1.0.0)
|
5
|
+
actionmailer (3.0.0)
|
6
|
+
actionpack (= 3.0.0)
|
7
|
+
mail (~> 2.2.5)
|
8
|
+
actionpack (3.0.0)
|
9
|
+
activemodel (= 3.0.0)
|
10
|
+
activesupport (= 3.0.0)
|
11
|
+
builder (~> 2.1.2)
|
12
|
+
erubis (~> 2.6.6)
|
13
|
+
i18n (~> 0.4.1)
|
14
|
+
rack (~> 1.2.1)
|
15
|
+
rack-mount (~> 0.6.12)
|
16
|
+
rack-test (~> 0.5.4)
|
17
|
+
tzinfo (~> 0.3.23)
|
18
|
+
activemodel (3.0.0)
|
19
|
+
activesupport (= 3.0.0)
|
20
|
+
builder (~> 2.1.2)
|
21
|
+
i18n (~> 0.4.1)
|
22
|
+
activerecord (3.0.0)
|
23
|
+
activemodel (= 3.0.0)
|
24
|
+
activesupport (= 3.0.0)
|
25
|
+
arel (~> 1.0.0)
|
26
|
+
tzinfo (~> 0.3.23)
|
27
|
+
activeresource (3.0.0)
|
28
|
+
activemodel (= 3.0.0)
|
29
|
+
activesupport (= 3.0.0)
|
30
|
+
activesupport (3.0.0)
|
31
|
+
archive-tar-minitar (0.5.2)
|
32
|
+
arel (1.0.1)
|
33
|
+
activesupport (~> 3.0.0)
|
34
|
+
builder (2.1.2)
|
35
|
+
columnize (0.3.1)
|
36
|
+
diff-lcs (1.1.2)
|
37
|
+
erubis (2.6.6)
|
38
|
+
abstract (>= 1.0.0)
|
39
|
+
i18n (0.4.1)
|
40
|
+
linecache19 (0.5.11)
|
41
|
+
ruby_core_source (>= 0.1.4)
|
42
|
+
mail (2.2.6.1)
|
43
|
+
activesupport (>= 2.3.6)
|
44
|
+
mime-types
|
45
|
+
treetop (>= 1.4.5)
|
46
|
+
mime-types (1.16)
|
47
|
+
polyglot (0.3.1)
|
48
|
+
rack (1.2.1)
|
49
|
+
rack-mount (0.6.13)
|
50
|
+
rack (>= 1.0.0)
|
51
|
+
rack-test (0.5.6)
|
52
|
+
rack (>= 1.0)
|
53
|
+
rails (3.0.0)
|
54
|
+
actionmailer (= 3.0.0)
|
55
|
+
actionpack (= 3.0.0)
|
56
|
+
activerecord (= 3.0.0)
|
57
|
+
activeresource (= 3.0.0)
|
58
|
+
activesupport (= 3.0.0)
|
59
|
+
bundler (~> 1.0.0)
|
60
|
+
railties (= 3.0.0)
|
61
|
+
railties (3.0.0)
|
62
|
+
actionpack (= 3.0.0)
|
63
|
+
activesupport (= 3.0.0)
|
64
|
+
rake (>= 0.8.4)
|
65
|
+
thor (~> 0.14.0)
|
66
|
+
rake (0.8.7)
|
67
|
+
rspec (2.0.0.rc)
|
68
|
+
rspec-core (= 2.0.0.rc)
|
69
|
+
rspec-expectations (= 2.0.0.rc)
|
70
|
+
rspec-mocks (= 2.0.0.rc)
|
71
|
+
rspec-core (2.0.0.rc)
|
72
|
+
rspec-expectations (2.0.0.rc)
|
73
|
+
diff-lcs (>= 1.1.2)
|
74
|
+
rspec-mocks (2.0.0.rc)
|
75
|
+
rspec-core (= 2.0.0.rc)
|
76
|
+
rspec-expectations (= 2.0.0.rc)
|
77
|
+
rspec-rails (2.0.0.rc)
|
78
|
+
rspec (= 2.0.0.rc)
|
79
|
+
ruby-debug-base19 (0.11.24)
|
80
|
+
columnize (>= 0.3.1)
|
81
|
+
linecache19 (>= 0.5.11)
|
82
|
+
ruby_core_source (>= 0.1.4)
|
83
|
+
ruby-debug19 (0.11.6)
|
84
|
+
columnize (>= 0.3.1)
|
85
|
+
linecache19 (>= 0.5.11)
|
86
|
+
ruby-debug-base19 (>= 0.11.19)
|
87
|
+
ruby_core_source (0.1.4)
|
88
|
+
archive-tar-minitar (>= 0.5.2)
|
89
|
+
sqlite3-ruby (1.3.1)
|
90
|
+
thor (0.14.3)
|
91
|
+
treetop (1.4.8)
|
92
|
+
polyglot (>= 0.3.1)
|
93
|
+
tzinfo (0.3.23)
|
94
|
+
|
95
|
+
PLATFORMS
|
96
|
+
ruby
|
97
|
+
|
98
|
+
DEPENDENCIES
|
99
|
+
rails (= 3.0.0)
|
100
|
+
rspec-rails (= 2.0.0.rc)
|
101
|
+
ruby-debug19
|
102
|
+
sqlite3-ruby
|
data/README.rdoc
CHANGED
@@ -4,52 +4,24 @@
|
|
4
4
|
|
5
5
|
=== Installation
|
6
6
|
|
7
|
-
You can use I18n-JS as plugin and gem. Choose what's best for you!
|
8
|
-
|
9
|
-
script/plugin install git://github.com/fnando/tokens.git
|
10
|
-
|
11
|
-
or
|
12
|
-
|
13
7
|
gem install tokens
|
14
8
|
|
15
9
|
=== Setting up
|
16
10
|
|
17
|
-
|
18
|
-
|
19
|
-
class CreateTokens < ActiveRecord::Migration
|
20
|
-
def self.up
|
21
|
-
create_table :tokens do |t|
|
22
|
-
t.integer :tokenizable_id, :null => false
|
23
|
-
t.string :tokenizable_type, :name, :null => false
|
24
|
-
t.string :token, :limit => 40, :null => false
|
25
|
-
t.text :data, :null => true
|
26
|
-
t.datetime :expires_at, :null => true
|
27
|
-
t.datetime :created_at
|
28
|
-
end
|
29
|
-
|
30
|
-
add_index :tokens, :tokenizable_type
|
31
|
-
add_index :tokens, :tokenizable_id
|
32
|
-
add_index :tokens, :token
|
33
|
-
add_index :tokens, :expires_at
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.down
|
37
|
-
drop_table :tokens
|
38
|
-
end
|
39
|
-
end
|
11
|
+
Add Tokens to your Gemfile and run <tt>rails generate tokens:install</tt>.
|
12
|
+
This will create a new migration file. Execute it by running <tt>rake db:migrate</tt>.
|
40
13
|
|
41
|
-
|
42
|
-
<tt>tokens</tt> to your model and be happy!
|
14
|
+
Finally, add the macro <tt>tokenizable</tt> to your model and be happy!
|
43
15
|
|
44
16
|
class User < ActiveRecord::Base
|
45
17
|
tokenizable
|
46
18
|
end
|
47
19
|
|
48
|
-
# create a new
|
20
|
+
# create a new user; remember that the object need to be saved before creating
|
49
21
|
# the token because it depends on the id
|
50
22
|
user = User.create(:login => "fnando")
|
51
23
|
|
52
|
-
#
|
24
|
+
# create token that never expires
|
53
25
|
user.add_token(:activate)
|
54
26
|
|
55
27
|
# uses custom expires_at
|
@@ -61,9 +33,8 @@ Run migrations with <tt>rake db:migrate</tt>. Add the method call
|
|
61
33
|
# uses custom size (up to 32)
|
62
34
|
user.add_token(:activate, :size => 20)
|
63
35
|
|
64
|
-
# create token with arbitrary data
|
65
|
-
data
|
66
|
-
user.add_token(:activate, :data => data.to_json)
|
36
|
+
# create token with arbitrary data.
|
37
|
+
user.add_token(:activate, :data => {:action => 'do something'})
|
67
38
|
|
68
39
|
# find token by name
|
69
40
|
user.find_token_by_name(:reset_account)
|
@@ -86,9 +57,6 @@ Run migrations with <tt>rake db:migrate</tt>. Add the method call
|
|
86
57
|
# remove a token by its name
|
87
58
|
user.remove_token(:activate)
|
88
59
|
|
89
|
-
# find user by token
|
90
|
-
User.find_by_token(:activate, 'ea2f14aeac40')
|
91
|
-
|
92
60
|
# find user by valid token (same name, same hash, not expired)
|
93
61
|
User.find_by_valid_token(:activate, 'ea2f14aeac40')
|
94
62
|
|
data/Rakefile
CHANGED
@@ -9,9 +9,9 @@ begin
|
|
9
9
|
gem.email = "fnando.vieira@gmail.com"
|
10
10
|
gem.homepage = "http://github.com/fnando/tokens"
|
11
11
|
gem.authors = ["Nando Vieira"]
|
12
|
-
gem.version =
|
12
|
+
gem.version = Tokens::Version::STRING
|
13
13
|
gem.summary = "Generate named tokens on your ActiveRecord models."
|
14
|
-
gem.files = FileList["README.rdoc
|
14
|
+
gem.files = FileList["{Rakefile,README.rdoc,Gemfile,Gemfile.lock,tokens.gemspec}", "{lib,spec,templates}/**/*"]
|
15
15
|
end
|
16
16
|
|
17
17
|
Jeweler::GemcutterTasks.new
|
@@ -20,5 +20,5 @@ rescue LoadError => e
|
|
20
20
|
end
|
21
21
|
|
22
22
|
RSpec::Core::RakeTask.new do |t|
|
23
|
-
t.ruby_opts = %[ -
|
23
|
+
t.ruby_opts = %[ -Ilib -Ispec ]
|
24
24
|
end
|
data/lib/tokens.rb
CHANGED
@@ -1,140 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require "tokens/version"
|
1
|
+
require "rails/railtie"
|
2
|
+
require "active_record"
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
module ClassMethods
|
10
|
-
# Set up model for using tokens.
|
11
|
-
#
|
12
|
-
# class User < ActiveRecord::Base
|
13
|
-
# tokenizable
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
def tokenizable
|
17
|
-
has_many :tokens, :as => :tokenizable, :dependent => :destroy
|
18
|
-
include InstanceMethods
|
19
|
-
end
|
20
|
-
|
21
|
-
alias_method :has_tokens, :tokenizable
|
22
|
-
|
23
|
-
# Generate token with specified length.
|
24
|
-
#
|
25
|
-
# User.generate_token(10)
|
26
|
-
#
|
27
|
-
def generate_token(size)
|
28
|
-
validity = Proc.new {|token| Token.where(:token => token).first.nil?}
|
29
|
-
|
30
|
-
begin
|
31
|
-
seed = "--#{rand}--#{Time.now}--#{rand}--"
|
32
|
-
token = Digest::SHA1.hexdigest(seed)[0, size]
|
33
|
-
end while validity[token] == false
|
34
|
-
|
35
|
-
token
|
36
|
-
end
|
37
|
-
|
38
|
-
# Find a token
|
39
|
-
#
|
40
|
-
# User.find_token(:activation, "abcdefg")
|
41
|
-
# User.find_token(:name => activation, :token => "abcdefg")
|
42
|
-
# User.find_token(:name => activation, :token => "abcdefg", :tokenizable_id => 1)
|
43
|
-
#
|
44
|
-
def find_token(*args)
|
45
|
-
if args.first.kind_of?(Hash)
|
46
|
-
options = args.first
|
47
|
-
else
|
48
|
-
options = {
|
49
|
-
:name => args.first,
|
50
|
-
:token => args.last
|
51
|
-
}
|
52
|
-
end
|
53
|
-
|
54
|
-
options.merge!(:name => options[:name].to_s, :tokenizable_type => self.name)
|
55
|
-
Token.where(options).includes(:tokenizable).first
|
56
|
-
end
|
57
|
-
|
58
|
-
# Find object by token.
|
59
|
-
#
|
60
|
-
# User.find_by_token(:activation, "abcdefg")
|
61
|
-
#
|
62
|
-
def find_by_token(name, hash)
|
63
|
-
token = find_token(:name => name.to_s, :token => hash)
|
64
|
-
return nil unless token
|
65
|
-
token.tokenizable
|
66
|
-
end
|
67
|
-
|
68
|
-
# Find object by valid token (same name, same hash, not expired).
|
69
|
-
#
|
70
|
-
# User.find_by_valid_token(:activation, "abcdefg")
|
71
|
-
#
|
72
|
-
def find_by_valid_token(name, hash)
|
73
|
-
token = find_token(:name => name.to_s, :token => hash)
|
74
|
-
return nil if !token || token.expired?
|
75
|
-
token.tokenizable
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
module InstanceMethods
|
80
|
-
# Verify if given token is valid.
|
81
|
-
#
|
82
|
-
# @user.valid_token?(:active, "abcdefg")
|
83
|
-
#
|
84
|
-
def valid_token?(name, hash)
|
85
|
-
self.tokens.where(:name => name.to_s, :token => hash.to_s).first != nil
|
86
|
-
end
|
87
|
-
|
88
|
-
# Find a token.
|
89
|
-
#
|
90
|
-
# @user.find_token(:activation, "abcdefg")
|
91
|
-
#
|
92
|
-
def find_token(name, token)
|
93
|
-
self.class.find_token(
|
94
|
-
:tokenizable_id => self.id,
|
95
|
-
:name => name.to_s, :token => token
|
96
|
-
)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Find token by its name.
|
100
|
-
def find_token_by_name(name)
|
101
|
-
self.tokens.find_by_name(name.to_s)
|
102
|
-
end
|
103
|
-
|
104
|
-
# Remove token.
|
105
|
-
#
|
106
|
-
# @user.remove_token(:activate)
|
107
|
-
#
|
108
|
-
def remove_token(name)
|
109
|
-
return if new_record?
|
110
|
-
token = find_token_by_name(name)
|
111
|
-
token && token.destroy
|
112
|
-
end
|
113
|
-
|
114
|
-
# Add a new token.
|
115
|
-
#
|
116
|
-
# @user.add_token(:api_key, :expires_at => nil)
|
117
|
-
# @user.add_token(:api_key, :size => 20)
|
118
|
-
# @user.add_token(:api_key, :data => data.to_yaml)
|
119
|
-
#
|
120
|
-
def add_token(name, options={})
|
121
|
-
options.reverse_merge!({
|
122
|
-
:expires_at => 2.days.from_now,
|
123
|
-
:size => 12,
|
124
|
-
:data => nil
|
125
|
-
})
|
126
|
-
|
127
|
-
remove_token(name)
|
128
|
-
attrs = {
|
129
|
-
:name => name.to_s,
|
130
|
-
:token => self.class.generate_token(options[:size]),
|
131
|
-
:expires_at => options[:expires_at],
|
132
|
-
:data => options[:data]
|
133
|
-
}
|
134
|
-
|
135
|
-
self.tokens.create!(attrs)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
ActiveRecord::Base.send :include, Tokens
|
4
|
+
require "tokens/active_record"
|
5
|
+
require "tokens/token"
|
6
|
+
require "tokens/version"
|
7
|
+
require "tokens/railtie"
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Tokens
|
2
|
+
module ActiveRecord
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval { extend ClassMethods }
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Set up model for using tokens.
|
9
|
+
#
|
10
|
+
# class User < ActiveRecord::Base
|
11
|
+
# tokenizable
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
def tokenizable
|
15
|
+
has_many :tokens, :as => :tokenizable, :dependent => :destroy
|
16
|
+
include InstanceMethods
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generate token with specified length.
|
20
|
+
#
|
21
|
+
# User.generate_token(10)
|
22
|
+
#
|
23
|
+
def generate_token(size)
|
24
|
+
validity = Proc.new {|token| Token.where(:token => token).first.nil?}
|
25
|
+
|
26
|
+
begin
|
27
|
+
seed = "--#{rand}--#{Time.now}--#{rand}--"
|
28
|
+
token = Digest::SHA1.hexdigest(seed)[0, size]
|
29
|
+
end while validity[token] == false
|
30
|
+
|
31
|
+
token
|
32
|
+
end
|
33
|
+
|
34
|
+
# Find a token
|
35
|
+
#
|
36
|
+
# User.find_token(:activation, "abcdefg")
|
37
|
+
# User.find_token(:name => activation, :token => "abcdefg")
|
38
|
+
# User.find_token(:name => activation, :token => "abcdefg", :tokenizable_id => 1)
|
39
|
+
#
|
40
|
+
def find_token(*args)
|
41
|
+
if args.first.kind_of?(Hash)
|
42
|
+
options = args.first
|
43
|
+
else
|
44
|
+
options = {
|
45
|
+
:name => args.first,
|
46
|
+
:token => args.last
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
options.merge!(:name => options[:name].to_s, :tokenizable_type => self.name)
|
51
|
+
Token.where(options).includes(:tokenizable).first
|
52
|
+
end
|
53
|
+
|
54
|
+
# Find object by token.
|
55
|
+
#
|
56
|
+
# User.find_by_token(:activation, "abcdefg")
|
57
|
+
#
|
58
|
+
def find_by_token(name, hash)
|
59
|
+
token = find_token(:name => name.to_s, :token => hash)
|
60
|
+
return nil unless token
|
61
|
+
token.tokenizable
|
62
|
+
end
|
63
|
+
|
64
|
+
# Find object by valid token (same name, same hash, not expired).
|
65
|
+
#
|
66
|
+
# User.find_by_valid_token(:activation, "abcdefg")
|
67
|
+
#
|
68
|
+
def find_by_valid_token(name, hash)
|
69
|
+
token = find_token(:name => name.to_s, :token => hash)
|
70
|
+
return nil if !token || token.expired?
|
71
|
+
token.tokenizable
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module InstanceMethods
|
76
|
+
# Verify if given token is valid.
|
77
|
+
#
|
78
|
+
# @user.valid_token?(:active, "abcdefg")
|
79
|
+
#
|
80
|
+
def valid_token?(name, hash)
|
81
|
+
self.tokens.where(:name => name.to_s, :token => hash.to_s).first != nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# Find a token.
|
85
|
+
#
|
86
|
+
# @user.find_token(:activation, "abcdefg")
|
87
|
+
#
|
88
|
+
def find_token(name, token)
|
89
|
+
self.class.find_token(
|
90
|
+
:tokenizable_id => self.id,
|
91
|
+
:name => name.to_s, :token => token
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Find token by its name.
|
96
|
+
def find_token_by_name(name)
|
97
|
+
self.tokens.find_by_name(name.to_s)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Remove token.
|
101
|
+
#
|
102
|
+
# @user.remove_token(:activate)
|
103
|
+
#
|
104
|
+
def remove_token(name)
|
105
|
+
return if new_record?
|
106
|
+
token = find_token_by_name(name)
|
107
|
+
token && token.destroy
|
108
|
+
end
|
109
|
+
|
110
|
+
# Add a new token.
|
111
|
+
#
|
112
|
+
# @user.add_token(:api_key, :expires_at => nil)
|
113
|
+
# @user.add_token(:api_key, :size => 20)
|
114
|
+
# @user.add_token(:api_key, :data => data.to_yaml)
|
115
|
+
#
|
116
|
+
def add_token(name, options={})
|
117
|
+
options.reverse_merge!({
|
118
|
+
:expires_at => 2.days.from_now,
|
119
|
+
:size => 12,
|
120
|
+
:data => nil
|
121
|
+
})
|
122
|
+
|
123
|
+
remove_token(name)
|
124
|
+
attrs = {
|
125
|
+
:name => name.to_s,
|
126
|
+
:token => self.class.generate_token(options[:size]),
|
127
|
+
:expires_at => options[:expires_at],
|
128
|
+
:data => options[:data]
|
129
|
+
}
|
130
|
+
|
131
|
+
self.tokens.create!(attrs)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|