onetime_token 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e9d29bc8839bac2d7e3494efd6492c780df1520b
4
+ data.tar.gz: 274b4b0e0a9d8e44efa133df885c2d43f9ac7fd8
5
+ SHA512:
6
+ metadata.gz: 9dafb630c6df34a2207fff970f4258ad6d0cc45022a0637c5d924b4b2d500d41c36af4172062c7765508537673d5ff470005ae022be1b38851cc60f2529b9230
7
+ data.tar.gz: 92e164b77708c33f0aaf92dcd6497586cc1a55d8788b1fdf9f7280c0cc3c2ac06d9a01005d52ac7a3fdc1697299003db47a0cff59aeef8ddcec76f50a9fde5a5
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in onetime_token.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 f-kubotar
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # OnetimeToken
2
+
3
+ Generate a temporary token of secret associated with ActiveRecord. it is stored in redis.
4
+ You will be able to verify whether the token is correct.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'onetime_token'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install onetime_token
19
+
20
+ ## Usage
21
+
22
+ ```ruby
23
+ OnetimeToken.configure do |config|
24
+ config.redis = {
25
+ url: 'redis://localhost:6379'
26
+ deiver: :redis # e.g :hireds
27
+ pool: {
28
+ size: 1,
29
+ timeout: 1,
30
+ }
31
+ }
32
+ end
33
+ ```
34
+
35
+ ```ruby
36
+ class User < ActiveRecord::Base
37
+ extend OnetimeToken
38
+
39
+ has_onetime_token :email_confirmation, expires_in: 1.hours
40
+ end
41
+ ```
42
+
43
+ ```ruby
44
+ user = User.find(1)
45
+ token = user.generate_email_confirmation_token
46
+ token.secret #=> 9eyZsVbrr4jLiVcERI7V6gmo
47
+
48
+ User.find_by_email_confirmation_token('9eyZsVbrr4jLiVcERI7V6gmo')
49
+ #=> #<User id: 1>
50
+
51
+ user = User.find(1)
52
+ user.verify_email_confirmation_token('9eyZsVbrr4jLiVcERI7V6gmo')
53
+ #=> true
54
+
55
+ User.find_by_email_confirmation_token('9eyZsVbrr4jLiVcERI7V6gmo')
56
+ #=> nil
57
+ ```
58
+
59
+ ## Contributing
60
+
61
+ 1. Fork it ( https://github.com/f-kubotar/onetime_token/fork )
62
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
63
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
64
+ 4. Push to the branch (`git push origin my-new-feature`)
65
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,31 @@
1
+ module OnetimeToken
2
+ module HasToken
3
+ def has_onetime_token(name, options={})
4
+ define_singleton_method :"find_by_#{name}_token" do |secret|
5
+ token = Token.new(self, name, secret)
6
+ if stored_model_id = token.model_id
7
+ find_by(id: stored_model_id)
8
+ end
9
+ end
10
+
11
+ define_method :"generate_#{name}_token" do |_options={}|
12
+ Token.generate_for self, name, _options
13
+ end
14
+
15
+ define_method :"#{name}_token_properties" do |secret|
16
+ token = Token.new(self.class, name, secret)
17
+ token.properties
18
+ end
19
+
20
+ define_method(:"verify_#{name}_token") do |secret|
21
+ token = Token.new(self.class, name, secret)
22
+ if id && id == token.model_id
23
+ token.expire
24
+ true
25
+ else
26
+ false
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,55 @@
1
+ module OnetimeToken
2
+ class Token
3
+ attr_reader :name, :secret
4
+
5
+ class << self
6
+ def generate_for(model, name, options={})
7
+ redis = OnetimeToken.redis_pool
8
+
9
+ token = nil
10
+ while token.nil? || redis.exists(token.key)
11
+ secret = SecureRandom.urlsafe_base64(15)
12
+ token = new(model.class, name, secret)
13
+ end
14
+
15
+ properties = (options[:properties] || {})
16
+ properties[:id] = model.id
17
+
18
+ redis.pipelined do
19
+ redis.set token.key, JSON.dump(properties)
20
+ expires_in = (options[:expires_in] || 3600 * 24 * 60)
21
+ redis.expire token.key, expires_in
22
+ end
23
+
24
+ token
25
+ end
26
+ end
27
+
28
+ def initialize(model_class, name, secret)
29
+ @model_class = model_class
30
+ @name = name
31
+ @secret = secret
32
+ end
33
+
34
+ def expire
35
+ OnetimeToken.redis_pool.del key
36
+ end
37
+
38
+ def properties
39
+ @properties ||=
40
+ if serialized_value = OnetimeToken.redis_pool.get(key)
41
+ JSON.parse(serialized_value, symbolize_names: true)
42
+ else
43
+ {}
44
+ end
45
+ end
46
+
47
+ def model_id
48
+ properties[:id]
49
+ end
50
+
51
+ def key
52
+ @key ||= "#{@model_class.name.downcase}_#{@name}/#{@secret}"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module OnetimeToken
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,38 @@
1
+ require 'json'
2
+
3
+ require 'connection_pool'
4
+ require 'redis/namespace'
5
+
6
+ require 'onetime_token/token'
7
+ require 'onetime_token/has_token'
8
+
9
+ module OnetimeToken
10
+ class << self
11
+ def extended(base)
12
+ base.extend HasToken
13
+ end
14
+
15
+ def configure
16
+ yield self
17
+ end
18
+
19
+ def redis_pool
20
+ @redis
21
+ end
22
+
23
+ def redis=(connection_options)
24
+ @redis =
25
+ if connection_options.is_a?(ConnectionPool)
26
+ connection_options
27
+ else
28
+ pool_options = {timeout: 1, size: 1}.
29
+ merge(connection_options.delete(:pool) || {})
30
+ ConnectionPool::Wrapper.new(pool_options) do
31
+ namespace = connection_options.delete(:namespace) || 'onetime_token'
32
+ client = Redis.new(connection_options)
33
+ Redis::Namespace.new(namespace, redis: client)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'onetime_token/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "onetime_token"
8
+ spec.version = OnetimeToken::VERSION
9
+ spec.authors = ["f-kubotar"]
10
+ spec.email = ["dev@hadashikick.jp"]
11
+ spec.summary = %q{Generate a temporary token of secret associated with ActiveRecord. it is stored in redis.}
12
+ spec.description = %q{Generate a temporary token of secret associated with ActiveRecord. it is stored in redis.
13
+ You will be able to verify whether the token is correct.}
14
+ spec.homepage = "https://github.com/f-kubotar/onetime_token"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'redis'
23
+ spec.add_dependency 'redis-namespace'
24
+ spec.add_dependency 'connection_pool'
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.6"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "rspec"
29
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ class User
4
+ extend OnetimeToken
5
+
6
+ attr_reader :id
7
+
8
+ has_onetime_token :email_confirmation
9
+ has_onetime_token :alter_email, expires_in: 60
10
+
11
+ def self.find_by(attributes)
12
+ new attributes
13
+ end
14
+
15
+ def initialize(attributes={})
16
+ @id = attributes[:id]
17
+ end
18
+ end
19
+
20
+ describe OnetimeToken::HasToken do
21
+ after do
22
+ OnetimeToken.redis_pool.keys.each{|key| OnetimeToken.redis_pool.del key }
23
+ end
24
+
25
+ let(:user) do
26
+ User.new(id: 101)
27
+ end
28
+
29
+ describe '.has_onetime_token' do
30
+ it 'specified expiration date' do
31
+ token = user.generate_alter_email_token(properties: {email: 'hoge@example.com'})
32
+ expect(token.properties[:email]).to eq('hoge@example.com')
33
+ end
34
+ end
35
+
36
+ describe 'Model.find_by_:name_token' do
37
+ let(:token) do
38
+ user.generate_email_confirmation_token
39
+ end
40
+
41
+ it "should find by sotred model id" do
42
+ expect(User).to receive(:find_by).with(id: user.id)
43
+ User.find_by_email_confirmation_token(token.secret)
44
+ end
45
+ end
46
+
47
+ describe 'Model#verify_:name_token' do
48
+ let(:token) do
49
+ user.generate_email_confirmation_token
50
+ end
51
+
52
+ context 'valid token secret' do
53
+ let(:secret) do
54
+ token.secret
55
+ end
56
+
57
+ it "should be true" do
58
+ expect(user.verify_email_confirmation_token secret).to be_truthy
59
+ end
60
+
61
+ it "should expire stored data" do
62
+ expect {
63
+ user.verify_email_confirmation_token secret
64
+ }.to change{ OnetimeToken.redis_pool.get token.key }.to(nil)
65
+ end
66
+ end
67
+
68
+ context 'invalid token secret' do
69
+ let(:secret) do
70
+ 'soaieowao'
71
+ end
72
+
73
+ it "should be false" do
74
+ expect(user.verify_email_confirmation_token secret).to be_falsey
75
+ end
76
+
77
+ it "should expire stored data" do
78
+ expect {
79
+ user.verify_email_confirmation_token secret
80
+ }.to_not change{ OnetimeToken.redis_pool.get token.key }
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path('../../lib/onetime_token', __FILE__)
2
+
3
+ OnetimeToken.configure do |config|
4
+ config.redis = {
5
+ url: 'redis://localhost:6379'
6
+ }
7
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe OnetimeToken::Token do
4
+ after do
5
+ OnetimeToken.redis_pool.keys.each{|key| OnetimeToken.redis_pool.del(key) }
6
+ end
7
+
8
+ let(:user) do
9
+ double :user, id: 101
10
+ end
11
+
12
+ describe '.generate_for' do
13
+ let(:token) do
14
+ OnetimeToken::Token.generate_for(user, :email_confirmation)
15
+ end
16
+
17
+ it "should return secret string" do
18
+ expect(token.secret.length).to be <= 20
19
+ end
20
+
21
+ it "should save to redis a secret string" do
22
+ expect(OnetimeToken.redis_pool.get token.key).to_not be_nil
23
+ end
24
+
25
+ it "should expires in 2 months after" do
26
+ expect(OnetimeToken.redis_pool.ttl token.key).to eq(3600 * 24 * 60)
27
+ end
28
+
29
+ context 'specified expiration date' do
30
+ let(:token) do
31
+ OnetimeToken::Token.generate_for(user, :email_confirmation, expires_in: 60)
32
+ end
33
+
34
+ it "expires in specify date after" do
35
+ expect(OnetimeToken.redis_pool.ttl token.key).to eq(60)
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#key' do
41
+ let(:token) do
42
+ OnetimeToken::Token.generate_for(user, :email_confirmation)
43
+ end
44
+
45
+ it "should containg token name and secret" do
46
+ expect(token.key).to be_include(token.secret)
47
+ expect(token.key).to be_include('email_confirmation')
48
+ end
49
+ end
50
+
51
+ describe '#expire' do
52
+ let(:token) do
53
+ OnetimeToken::Token.generate_for(user, :email_confirmation)
54
+ end
55
+
56
+ it "should remove stored data" do
57
+ expect {
58
+ token.expire
59
+ }.to change{ OnetimeToken.redis_pool.get(token.key) }.to(nil)
60
+ end
61
+ end
62
+
63
+ describe '#model_id' do
64
+ let(:token) do
65
+ OnetimeToken::Token.generate_for(user, :email_confirmation)
66
+ end
67
+
68
+ it "should fetch model the specified id" do
69
+ expect(token.model_id).to eq(user.id)
70
+ end
71
+ end
72
+
73
+ describe '#properties' do
74
+ let(:token) do
75
+ OnetimeToken::Token.generate_for(user, :email_confirmation)
76
+ end
77
+
78
+ it "should fetch model the specified id" do
79
+ expect(token.properties).to eq(id: user.id)
80
+ end
81
+
82
+ context 'sotre properties' do
83
+ let(:token) do
84
+ OnetimeToken::Token.generate_for(user, :alter_email,
85
+ properties: { alter_email: 'hoge@example.com' })
86
+ end
87
+
88
+ it "should fetch properteis" do
89
+ expect(token.properties).to eq(id: user.id, alter_email: 'hoge@example.com')
90
+ end
91
+ end
92
+ end
93
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: onetime_token
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - f-kubotar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis-namespace
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: connection_pool
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: |-
98
+ Generate a temporary token of secret associated with ActiveRecord. it is stored in redis.
99
+ You will be able to verify whether the token is correct.
100
+ email:
101
+ - dev@hadashikick.jp
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - lib/onetime_token.rb
112
+ - lib/onetime_token/has_token.rb
113
+ - lib/onetime_token/token.rb
114
+ - lib/onetime_token/version.rb
115
+ - onetime_token.gemspec
116
+ - spec/has_token_spec.rb
117
+ - spec/spec_helper.rb
118
+ - spec/token_spec.rb
119
+ homepage: https://github.com/f-kubotar/onetime_token
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.2.2
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Generate a temporary token of secret associated with ActiveRecord. it is
143
+ stored in redis.
144
+ test_files:
145
+ - spec/has_token_spec.rb
146
+ - spec/spec_helper.rb
147
+ - spec/token_spec.rb