authifer 0.0.1rc1

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: 8d5fca606d9cc2e2a117e9f453aa91bc2dccca57
4
+ data.tar.gz: e98a59759f13e73a33bc75cb7b56a15811a06c69
5
+ SHA512:
6
+ metadata.gz: b11cd52286bbb36c085e9ee5ef3651839f3487e593f64152ec9ad88f080810096642ff518861aaa1e742609e9cae11e89a314a582ab89c924551773b2bc93a50
7
+ data.tar.gz: 3719253f7317fd67c26d084f91146ede71f7a57ac748442434dae7922c2fabf839ffee4933a7e0455464fc04044c10372fe2eebed0bde31bee976dddfde7014e
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Zee Spencer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # Authifer
2
+
3
+ `Trust me Authifer, I'm not half as think as you drunk I am!`
4
+
5
+ One obstacles to chiseling away a monolithic application architecture is
6
+ managing user authentication on several smaller apps.
7
+
8
+ Authifer abstracts authentication into it's own application so you can focus
9
+ your attention on adding useful features for your customers instead of managing
10
+ user identities.
11
+
12
+ ## Usage
13
+
14
+ Gimme a second. I'll figure it out.
15
+
16
+ ## Contributing
17
+
18
+ We love pull requests and issues!
19
+
20
+ ### For enhancements
21
+
22
+ Create an issue or pull request with:
23
+
24
+ 1. A good name
25
+ 2. Tests where applicable
26
+ 3.
27
+
28
+ ### For bug fixes
29
+
30
+ Create an issue with:
31
+
32
+ 1. Reproduction steps
33
+ 2. Stack trace if applicable
34
+ 3. Proposed fix if applicable
data/lib/authifer.rb ADDED
@@ -0,0 +1,21 @@
1
+ require_relative 'authifer/schema'
2
+
3
+ module Authifer
4
+ def self.connect_to_database(database_url)
5
+ require_relative 'authifer/app'
6
+ Authifer::App.set :database, database_url
7
+ require_relative 'authifer/user'
8
+ end
9
+
10
+ def self.views_path=views_path
11
+ @views_path = views_path
12
+ end
13
+
14
+ def self.views_path
15
+ @views_path ||= File.join(base_path, 'views')
16
+ end
17
+
18
+ def self.base_path
19
+ @base_path ||= File.join(File.dirname(File.expand_path(__FILE__)), 'authifer')
20
+ end
21
+ end
@@ -0,0 +1,85 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/activerecord'
3
+ require 'songkick/oauth2/provider'
4
+ require_relative 'authentication_helper'
5
+ require_relative 'renderer'
6
+ require_relative 'oauth_helper'
7
+ require_relative 'data_helper'
8
+ require_relative 'paths'
9
+
10
+ module Authifer
11
+ class App < Sinatra::Base
12
+ register Sinatra::ActiveRecordExtension
13
+
14
+ set :views, Authifer.views_path
15
+ enable :sessions
16
+ extend Authifer::Paths
17
+
18
+ helpers do
19
+ include Authifer::AuthenticationHelper
20
+ include Authifer::OAuthHelper
21
+ include Authifer::DataHelper
22
+ include Authifer::Paths
23
+
24
+
25
+ def display
26
+ @renderer ||= Renderer.new(self)
27
+ end
28
+
29
+ def redirect_url
30
+ @redirect_url ||= params[:redirect_url] || "/"
31
+ end
32
+ end
33
+
34
+ post users_path do
35
+ user = create_user(params[:user])
36
+ if user.persisted?
37
+ login(user)
38
+ redirect redirect_url
39
+ else
40
+ display.register(user)
41
+ end
42
+ end
43
+
44
+ get new_user_path do
45
+ display.register(build_user)
46
+ end
47
+
48
+ post sessions_path do
49
+ user = authenticate_user(params[:user])
50
+ if user.errors.empty?
51
+ login(user)
52
+ redirect redirect_url
53
+ else
54
+ display.login(user)
55
+ end
56
+ end
57
+
58
+ get new_session_path do
59
+ display.login(build_user)
60
+ end
61
+
62
+ [{ method: :get, path: delete_session_path},
63
+ { method: :delete, path: sessions_path }].each do |route|
64
+ __send__(route[:method], route[:path]) do
65
+ logout
66
+ display.login(build_user)
67
+ end
68
+ end
69
+
70
+ before "/oauth/authorize" do
71
+ ensure_logged_in!
72
+ end
73
+
74
+ [:get, :post].each do |method|
75
+ __send__(method, '/oauth/token') { handle_oauth }
76
+ __send__(method, '/oauth/authorize') { handle_oauth }
77
+ end
78
+
79
+ post '/oauth/allow' do
80
+ @auth = Songkick::OAuth2::Provider::Authorization.new(current_user, params)
81
+ @auth.grant_access!
82
+ redirect @auth.redirect_uri, @auth.response_status
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,38 @@
1
+ module Authifer
2
+ module AuthenticationHelper
3
+ def ensure_logged_in!
4
+ unless logged_in?
5
+ @redirect_url = request.fullpath
6
+ halt display.login(build_user)
7
+ end
8
+ end
9
+
10
+ def current_user
11
+ @current_user ||= logged_in? ? find_user(id: session[:user_id]) : build_user
12
+ end
13
+
14
+ def login(user)
15
+ session[:user_id] = user.id
16
+ end
17
+
18
+ def logged_in?
19
+ !session[:user_id].nil?
20
+ end
21
+
22
+ def logout
23
+ session[:user_id] = nil
24
+ end
25
+
26
+ def authenticate_user(user_attributes)
27
+ user = find_user(email: user_attributes[:email])
28
+
29
+ user = build_user if !user
30
+
31
+ if user.password != user_attributes[:password] || user.password.nil?
32
+ user.errors.add(:credentials, "are invalid. We don't have any users with that email/password combination")
33
+ end
34
+
35
+ user
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,15 @@
1
+ module Authifer
2
+ module DataHelper
3
+ def find_user(attributes)
4
+ User.find_by(attributes)
5
+ end
6
+
7
+ def create_user(user_attributes)
8
+ User.create(user_attributes)
9
+ end
10
+
11
+ def build_user(attributes={})
12
+ User.new(attributes)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ module Authifer
2
+ module OAuthHelper
3
+ def resource_owner
4
+ @resource_owner ||= current_user.persisted? ? current_user : :implicit
5
+ end
6
+
7
+ def oauth2
8
+ @oauth2 ||= Songkick::OAuth2::Provider.parse(resource_owner, env)
9
+ end
10
+
11
+ def handle_oauth
12
+ if oauth2.redirect?
13
+ redirect oauth2.redirect_uri, oauth2.response_status
14
+ end
15
+
16
+ headers oauth2.response_headers
17
+ status oauth2.response_status
18
+
19
+ if body = oauth2.response_body
20
+ body
21
+ elsif oauth2.valid?
22
+ erb :authorize, locals: { authorization_request: oauth2 }
23
+ else
24
+ raise "Error Will Robinson"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ module Authifer
2
+ module Paths
3
+ def users_path
4
+ "/users"
5
+ end
6
+
7
+ def new_user_path
8
+ "#{users_path}/new"
9
+ end
10
+
11
+ def sessions_path
12
+ "/sessions"
13
+ end
14
+
15
+ def new_session_path
16
+ "#{sessions_path}/new"
17
+ end
18
+
19
+ def delete_session_path
20
+ "#{sessions_path}/delete"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ module Authifer
2
+ class Renderer
3
+ attr_reader :app
4
+
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def login(user)
10
+ if app.logged_in?
11
+ app.redirect app.redirect_url
12
+ else
13
+ app.erb :new_session, locals: { user: user }
14
+ end
15
+ end
16
+
17
+ def register(user)
18
+ if app.logged_in?
19
+ app.redirect app.redirect_url
20
+ else
21
+ app.erb :new_user, locals: { user: user }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ module Authifer
2
+ module Schema
3
+ def self.migrate
4
+ require 'songkick/oauth2/provider'
5
+ Songkick::OAuth2::Model::Schema.migrate
6
+ ActiveRecord::Migrator.up(File.join(Authifer.base_path,'schema'))
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :email, :password_digest
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ namespace :authifer do
2
+ namespace :db do
3
+ desc "Migrate up the database"
4
+ task :migrate do
5
+ require_relative 'schema'
6
+ Authifer::Schema.migrate
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ require 'protected_attributes'
2
+ require 'bcrypt'
3
+
4
+ module Authifer
5
+ class User < ActiveRecord::Base
6
+ include Songkick::OAuth2::Model::ResourceOwner
7
+ include Songkick::OAuth2::Model::ClientOwner
8
+
9
+ validates :password, confirmation: true
10
+ validate :password_is_set
11
+ validates :email, uniqueness: true
12
+
13
+ def password=password
14
+ @password = BCrypt::Password.create(password)
15
+ self.password_digest = @password
16
+ end
17
+
18
+ def password
19
+ @password ||= BCrypt::Password.new(password_digest) if password_digest
20
+ end
21
+
22
+ def password_is_set
23
+ if !password || password == ""
24
+ errors.add(:password, "can't be blank")
25
+ end
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,5 @@
1
+ <% if model.errors.has_key? field %>
2
+ <% model.errors.messages[field].each do |m|%>
3
+ <p class="error"><%= field.to_s.capitalize %> <%= m %></p>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1 @@
1
+ <input type="hidden" name="redirect_url" value="<%=redirect_url%>" />
@@ -0,0 +1,9 @@
1
+ <h2>You're about to grant access to <%= authorization_request.client.name %></h2>
2
+ <form method="POST" action="/oauth/allow">
3
+ <% authorization_request.params.each do |key, value| %>
4
+ <input type="hidden" name="<%= key %>" value="<%= value %>">
5
+ <% end %>
6
+ <button>It's cool. I dig it</button>
7
+ </form>
8
+ <a href="http://calmingmanatee.com/">No thanks. I'd rather see a picture of a
9
+ manatee</a>
@@ -0,0 +1,5 @@
1
+ <!DOCTYPE html>
2
+ <body>
3
+ <%= yield %>
4
+ </body>
5
+ </html>
@@ -0,0 +1,13 @@
1
+ <div id="login_form">
2
+ <form method="POST" action="<%= sessions_path %>">
3
+ <%= erb :_error, locals: { model: user, field: :credentials } %>
4
+ <label>Email: <input type="email" name="user[email]" value="<%=user.email%>"></label>
5
+ <%= erb :_error, locals: { model: user, field: :email } %>
6
+
7
+ <label>Password: <input type="password" name="user[password]"></label>
8
+ <%= erb :_error, locals: { model: user, field: :password } %>
9
+
10
+ <%= erb :_redirect_url_fields %>
11
+ <button>Log in</button>
12
+ </form>
13
+ </div>
@@ -0,0 +1,15 @@
1
+ <div id="registration_form">
2
+ <form method="POST" action="<%= users_path %>">
3
+ <label>Email: <input type="email" name="user[email]" value="<%=user.email%>"></label>
4
+ <%= erb :_error, locals: { model: user, field: :email } %>
5
+
6
+ <label>Password: <input type="password" name="user[password]"></label>
7
+ <%= erb :_error, locals: { model: user, field: :password } %>
8
+
9
+ <label>Confirm Password: <input type="password" name="user[password_confirmation]"></label>
10
+ <%= erb :_error, locals: { model: user, field: :password_confirmation } %>
11
+
12
+ <%= erb :_redirect_url_fields %>
13
+ <button>Register</button>
14
+ </form>
15
+ </div>
metadata ADDED
@@ -0,0 +1,230 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: authifer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1rc1
5
+ platform: ruby
6
+ authors:
7
+ - Zee Spencer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sinatra-activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: songkick-oauth2-provider
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.10'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bcrypt-ruby
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
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: protected_attributes
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
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: rake
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
+ - !ruby/object:Gem::Dependency
98
+ name: oauth2
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: capybara
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: minitest
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: minitest-test
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: dotenv
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: pg
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ description: Run your own Authentication provider!
182
+ email: zee@zeespencer.com
183
+ executables: []
184
+ extensions: []
185
+ extra_rdoc_files: []
186
+ files:
187
+ - lib/authifer/views/_error.erb
188
+ - lib/authifer/views/_redirect_url_fields.erb
189
+ - lib/authifer/views/authorize.erb
190
+ - lib/authifer/views/layout.erb
191
+ - lib/authifer/views/new_session.erb
192
+ - lib/authifer/views/new_user.erb
193
+ - lib/authifer/app.rb
194
+ - lib/authifer/authentication_helper.rb
195
+ - lib/authifer/data_helper.rb
196
+ - lib/authifer/oauth_helper.rb
197
+ - lib/authifer/paths.rb
198
+ - lib/authifer/renderer.rb
199
+ - lib/authifer/schema/20131029055507_create_users.rb
200
+ - lib/authifer/schema.rb
201
+ - lib/authifer/tasks.rb
202
+ - lib/authifer/user.rb
203
+ - lib/authifer.rb
204
+ - README.md
205
+ - LICENSE
206
+ homepage: https://github.com/makeheadspace/authifer
207
+ licenses:
208
+ - MIT
209
+ metadata: {}
210
+ post_install_message:
211
+ rdoc_options: []
212
+ require_paths:
213
+ - lib
214
+ required_ruby_version: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - '>='
217
+ - !ruby/object:Gem::Version
218
+ version: '0'
219
+ required_rubygems_version: !ruby/object:Gem::Requirement
220
+ requirements:
221
+ - - '>'
222
+ - !ruby/object:Gem::Version
223
+ version: 1.3.1
224
+ requirements: []
225
+ rubyforge_project:
226
+ rubygems_version: 2.0.3
227
+ signing_key:
228
+ specification_version: 4
229
+ summary: Run your own Authentication provider!
230
+ test_files: []