pillowfort 0.1.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: dc163e24085705f50d12a3a32b6e117d5d9bbad4
4
+ data.tar.gz: 854b47f3081729b9d72d0fda086d370b561083e5
5
+ SHA512:
6
+ metadata.gz: ee784476931c9b559307ef274d36da12a320dd103e84a3b2d5c86528afc6abe524f819fd4c4ca24fb782c460518deb96dff9e11077e0a8dea9d275f6b1e91c2b
7
+ data.tar.gz: 8b72dd59a2514733e3277967690e2f0c929428786cd8519b93a160bcfca8e6bcd5c0c559fa5d9aad46a9501795413eff1d875db288cd0ff3e0f25aca128c5ab2
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Tim Lowrimore
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Pillowfort'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+ load 'rails/tasks/statistics.rake'
20
+
21
+
22
+
23
+ Bundler::GemHelper.install_tasks
24
+
@@ -0,0 +1,46 @@
1
+ require 'pillowfort/model_context'
2
+
3
+ module Pillowfort
4
+ module Concerns::ControllerAuthentication
5
+ extend ActiveSupport::Concern
6
+ include ActionController::HttpAuthentication::Basic::ControllerMethods
7
+
8
+ included do
9
+ before_filter :authenticate_from_account_token!
10
+ end
11
+
12
+ private
13
+
14
+ def authenticate_from_account_token!
15
+ context = Pillowfort::ModelContext
16
+ resource_class = context.model_class
17
+
18
+ ensure_resource_reader(context)
19
+
20
+ authenticate_or_request_with_http_basic do |email, token|
21
+ resource_class.authenticate_securely(email, token) do |resource|
22
+ @authentication_resource = resource
23
+ end
24
+ end
25
+
26
+ allow_client_to_handle_unauthorized_status
27
+ end
28
+
29
+ def ensure_resource_reader(context)
30
+ reader_name = context.resource_reader_name
31
+ return if respond_to? reader_name
32
+
33
+ self.class.send :define_method, reader_name do
34
+ @authentication_resource
35
+ end
36
+ end
37
+
38
+ # This is necessary, as it allows Cordova to properly delegate 401 response
39
+ # handling to our application. If we keep this header, Cordova will defer
40
+ # handling to iOS, and we'll never see the 401 status in the app... it'll just
41
+ # do nothing.
42
+ def allow_client_to_handle_unauthorized_status
43
+ headers.delete('WWW-Authenticate')
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,111 @@
1
+ require 'bcrypt'
2
+ require 'pillowfort/model_context'
3
+
4
+ module Pillowfort
5
+ module Concerns::ModelAuthentication
6
+ extend ActiveSupport::Concern
7
+ include BCrypt
8
+
9
+ MIN_PASSWORD_LENGTH = 8
10
+
11
+ included do
12
+ Pillowfort::ModelContext.model_class = self
13
+
14
+ # Provided by Rails
15
+ has_secure_password
16
+
17
+ validates :email, presence: true, uniqueness: true
18
+ validates :password, length: { minimum: MIN_PASSWORD_LENGTH }
19
+
20
+ before_save :ensure_auth_token
21
+
22
+ def ensure_auth_token
23
+ reset_auth_token if auth_token.blank?
24
+ end
25
+
26
+ def reset_auth_token
27
+ self.auth_token = generate_auth_token
28
+ self.auth_token_expires_at = 1.day.from_now
29
+ end
30
+
31
+ def reset_auth_token!
32
+ reset_auth_token
33
+ save validate: false
34
+ end
35
+
36
+ def token_expired?
37
+ auth_token_expires_at <= Time.now
38
+ end
39
+
40
+ private
41
+
42
+ def touch_token_expiry!
43
+ update_column :auth_token_expires_at, 1.day.from_now
44
+ end
45
+
46
+ def generate_auth_token
47
+ resource_class = self.class
48
+ loop do
49
+ token = resource_class.friendly_token
50
+ break token unless resource_class.where(auth_token: token).first
51
+ end
52
+ end
53
+ end
54
+
55
+ module ClassMethods
56
+ def authenticate_securely(email, token)
57
+ return false if email.blank? || token.blank?
58
+
59
+ transaction do
60
+ resource = find_by_email(email)
61
+
62
+ if resource
63
+
64
+ # if the resource token is expired, reset it and
65
+ # return false, triggering a 401
66
+ if resource.token_expired?
67
+ resource.reset_auth_token!
68
+ return false
69
+ else
70
+ if secure_compare(resource.auth_token, token)
71
+
72
+ # If the resource successfully authenticates within the alotted window
73
+ # of time, we'll extend the window.
74
+ resource.send :touch_token_expiry!
75
+ yield resource
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def find_and_authenticate(email, password)
83
+ resource = find_by_email(email)
84
+
85
+ if resource && resource.authenticate(password)
86
+ resource.tap do |u|
87
+ u.reset_auth_token!
88
+ end
89
+ else
90
+ return false
91
+ end
92
+ end
93
+
94
+ # constant-time comparison algorithm to prevent timing attacks. Lifted
95
+ # from Devise.
96
+ def secure_compare(a, b)
97
+ return false if a.blank? || b.blank? || a.bytesize != b.bytesize
98
+ l = a.unpack "C#{a.bytesize}"
99
+
100
+ res = 0
101
+ b.each_byte { |byte| res |= byte ^ l.shift }
102
+ res == 0
103
+ end
104
+
105
+ # Generates a value for our auth token. Lifted from Devise.
106
+ def friendly_token
107
+ SecureRandom.base64(32).tr('+/=lIO0', 'pqrsxyz')
108
+ end
109
+ end
110
+ end
111
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
data/lib/pillowfort.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "pillowfort/engine"
2
+
3
+ module Pillowfort
4
+ end
@@ -0,0 +1,7 @@
1
+ module Pillowfort
2
+ class Engine < ::Rails::Engine
3
+ config.generators do |g|
4
+ g.test_framework :rspec
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ require "pillowfort/pillow_fight"
2
+
3
+ module Pillowfort::ModelContext
4
+ mattr_accessor :model_class
5
+
6
+ def self.resource_reader_name
7
+ begin
8
+ "current_#{model_class.name.underscore}"
9
+ rescue NoMethodError => nme
10
+ Pillowfort::PillowFight.error_message <<-EOF
11
+ It seems no `model_class` can be found. The likely culprit is:
12
+
13
+ 1.) You forgot to include Pillowfort::Concerns::ModelAuthentication into
14
+ the model of your choosing.
15
+ 2.) You forgot to set `config.eager_load` to `true`, in your environment
16
+ config (e.g. development.rb)
17
+
18
+ If neither of the aforementioned options are the issue, you've likely
19
+ found a bug. Please report it at:
20
+
21
+ https://github.com/coroutine/pillowfort/issues
22
+
23
+ Cheers!
24
+ Coroutine
25
+
26
+ EOF
27
+
28
+ # rethrow
29
+ raise nme
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ module Pillowfort::PillowFight
2
+ def self.error_message(msg)
3
+ puts "\e[31m"
4
+ puts '*'*80
5
+ puts "#{' '*34}Pillow Fight!"
6
+ puts '*'*80
7
+ puts msg
8
+ puts '*'*80
9
+ puts "\e[0m"
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Pillowfort
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :pillowfort do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pillowfort
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Tim Lowrimore
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bcrypt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.7
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.1.7
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
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: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: factory_girl_rails
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
+ description: Opinionated, session-less API authentication
84
+ email:
85
+ - tlowrimore@coroutine.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - MIT-LICENSE
91
+ - Rakefile
92
+ - app/controllers/pillowfort/concerns/controller_authentication.rb
93
+ - app/models/pillowfort/concerns/model_authentication.rb
94
+ - config/routes.rb
95
+ - lib/pillowfort.rb
96
+ - lib/pillowfort/engine.rb
97
+ - lib/pillowfort/model_context.rb
98
+ - lib/pillowfort/pillow_fight.rb
99
+ - lib/pillowfort/version.rb
100
+ - lib/tasks/pillowfort_tasks.rake
101
+ homepage: https://github.com/coroutine/pillowfort
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.4.5
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Opinionated, session-less API authentication
125
+ test_files: []