obscured-doorman 0.4.0
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 +7 -0
- data/.codeclimate.yml +28 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/publish.yml +44 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +14 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.simplecov +6 -0
- data/.travis.yml +17 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +144 -0
- data/README.md +115 -0
- data/lib/obscured-doorman.rb +69 -0
- data/lib/obscured-doorman/base.rb +203 -0
- data/lib/obscured-doorman/configuration.rb +123 -0
- data/lib/obscured-doorman/errors.rb +44 -0
- data/lib/obscured-doorman/helpers.rb +66 -0
- data/lib/obscured-doorman/loggable.rb +51 -0
- data/lib/obscured-doorman/mailer.rb +46 -0
- data/lib/obscured-doorman/messages.rb +30 -0
- data/lib/obscured-doorman/models/token.rb +57 -0
- data/lib/obscured-doorman/models/user.rb +160 -0
- data/lib/obscured-doorman/providers/base/configuration.rb +69 -0
- data/lib/obscured-doorman/providers/bitbucket.rb +79 -0
- data/lib/obscured-doorman/providers/bitbucket/access_token.rb +27 -0
- data/lib/obscured-doorman/providers/bitbucket/configuration.rb +38 -0
- data/lib/obscured-doorman/providers/bitbucket/messages.rb +13 -0
- data/lib/obscured-doorman/providers/bitbucket/strategy.rb +53 -0
- data/lib/obscured-doorman/providers/github.rb +78 -0
- data/lib/obscured-doorman/providers/github/access_token.rb +23 -0
- data/lib/obscured-doorman/providers/github/configuration.rb +38 -0
- data/lib/obscured-doorman/providers/github/messages.rb +13 -0
- data/lib/obscured-doorman/providers/github/strategy.rb +53 -0
- data/lib/obscured-doorman/strategies/forgot_password.rb +157 -0
- data/lib/obscured-doorman/strategies/password.rb +38 -0
- data/lib/obscured-doorman/strategies/remember_me.rb +54 -0
- data/lib/obscured-doorman/utilities/roles.rb +11 -0
- data/lib/obscured-doorman/utilities/types.rb +14 -0
- data/lib/obscured-doorman/version.rb +7 -0
- data/obscured-doorman.gemspec +42 -0
- data/spec/config/mongoid.yml +11 -0
- data/spec/doorman_spec.rb +203 -0
- data/spec/errors_spec.rb +11 -0
- data/spec/factories/token_factory.rb +8 -0
- data/spec/factories/user_factory.rb +12 -0
- data/spec/helpers/application_helper.rb +52 -0
- data/spec/helpers/request_helper.rb +53 -0
- data/spec/loggable_spec.rb +27 -0
- data/spec/mailer_spec.rb +26 -0
- data/spec/matchers/time.rb +7 -0
- data/spec/setup.rb +58 -0
- data/spec/token_spec.rb +62 -0
- data/spec/user_spec.rb +151 -0
- metadata +361 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'haml'
|
4
|
+
|
5
|
+
module Obscured
|
6
|
+
module Doorman
|
7
|
+
module Strategies
|
8
|
+
class Password < Warden::Strategies::Base
|
9
|
+
include Haml::Helpers
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
params['user'] &&
|
13
|
+
params['user']['username'] &&
|
14
|
+
params['user']['password']
|
15
|
+
end
|
16
|
+
|
17
|
+
def authenticate!
|
18
|
+
fail!(Doorman::MESSAGES[:login_bad_credentials]) unless valid?
|
19
|
+
|
20
|
+
user = User.authenticate(
|
21
|
+
params['user']['username'],
|
22
|
+
params['user']['password']
|
23
|
+
)
|
24
|
+
|
25
|
+
if user.nil?
|
26
|
+
fail!(Doorman::MESSAGES[:login_bad_credentials])
|
27
|
+
elsif Doorman.configuration[:confirmation] && !user.confirmed
|
28
|
+
user.confirm
|
29
|
+
|
30
|
+
fail!(Doorman::MESSAGES[:login_not_confirmed])
|
31
|
+
else
|
32
|
+
success!(user)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Obscured
|
4
|
+
module Doorman
|
5
|
+
module Strategies
|
6
|
+
class RememberMeStrategy < Warden::Strategies::Base
|
7
|
+
def valid?
|
8
|
+
!env['rack.cookies'][Doorman.configuration.remember_cookie].nil?
|
9
|
+
end
|
10
|
+
|
11
|
+
def authenticate!
|
12
|
+
token = env['rack.cookies'][Doorman.configuration.remember_cookie]
|
13
|
+
return unless token
|
14
|
+
|
15
|
+
token = Token.where(token: token).first
|
16
|
+
user = token&.user
|
17
|
+
env['rack.cookies'].delete(Doorman.configuration.remember_cookie) && return if user.nil?
|
18
|
+
success!(user)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module RememberMe
|
23
|
+
def self.registered(app)
|
24
|
+
app.use Rack::Cookies
|
25
|
+
|
26
|
+
Warden::Strategies.add(:remember_me, Strategies::RememberMeStrategy)
|
27
|
+
|
28
|
+
app.before do
|
29
|
+
warden.authenticate(:remember_me)
|
30
|
+
end
|
31
|
+
|
32
|
+
Warden::Manager.after_authentication do |user, auth, _opts|
|
33
|
+
if auth.winning_strategy.is_a?(Strategies::RememberMeStrategy) ||
|
34
|
+
(auth.winning_strategy.is_a?(Strategies::Password) && auth.params['user']['remember_me'])
|
35
|
+
|
36
|
+
token = user.tokens.where(type: :remember).first
|
37
|
+
user.remember_me! # new token
|
38
|
+
auth.env['rack.cookies'][Doorman.configuration.remember_cookie] = {
|
39
|
+
value: token.token,
|
40
|
+
expires: (Time.now + Doorman.configuration.remember_for.days.seconds),
|
41
|
+
path: '/'
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Warden::Manager.before_logout do |user, auth, _opts|
|
47
|
+
user&.forget_me! if user
|
48
|
+
auth.env['rack.cookies'].delete(Doorman.configuration.remember_cookie)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'obscured-doorman/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |gem|
|
8
|
+
gem.name = 'obscured-doorman'
|
9
|
+
gem.version = Obscured::Doorman::VERSION
|
10
|
+
gem.authors = ['Erik Hennerfors']
|
11
|
+
gem.email = ['erik.hennerfors@obscured.se']
|
12
|
+
gem.description = "Doorman is a front of Warden used by Obscured and it's applications"
|
13
|
+
gem.summary = "Doorman is a front of Warden used by Obscured and it's applications"
|
14
|
+
gem.homepage = 'https://github.com/gonace/Obscured.Doorman.git'
|
15
|
+
|
16
|
+
gem.required_ruby_version = '>= 2'
|
17
|
+
|
18
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
19
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
20
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
21
|
+
gem.require_paths = ['lib']
|
22
|
+
|
23
|
+
gem.add_dependency 'bcrypt'
|
24
|
+
gem.add_dependency 'geocoder'
|
25
|
+
gem.add_dependency 'haml'
|
26
|
+
gem.add_dependency 'mail'
|
27
|
+
gem.add_dependency 'mongoid'
|
28
|
+
gem.add_dependency 'rack'
|
29
|
+
gem.add_dependency 'rack-contrib'
|
30
|
+
gem.add_dependency 'rest-client'
|
31
|
+
gem.add_dependency 'sinatra'
|
32
|
+
gem.add_dependency 'sinatra-contrib'
|
33
|
+
gem.add_dependency 'sinatra-flash'
|
34
|
+
gem.add_dependency 'sinatra-partial'
|
35
|
+
gem.add_dependency 'warden'
|
36
|
+
|
37
|
+
gem.add_development_dependency 'dotenv'
|
38
|
+
gem.add_development_dependency 'factory_bot'
|
39
|
+
gem.add_development_dependency 'rack-test'
|
40
|
+
gem.add_development_dependency 'rspec'
|
41
|
+
gem.add_development_dependency 'simplecov'
|
42
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'setup'
|
4
|
+
|
5
|
+
|
6
|
+
describe Obscured::Doorman::Base do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
def app
|
10
|
+
Obscured::Doorman::Spec::App
|
11
|
+
end
|
12
|
+
|
13
|
+
let!(:email) { 'homer.simpson@obscured.se' }
|
14
|
+
let!(:password) { 'Password123' }
|
15
|
+
let!(:user) { FactoryBot.create(:user, username: email) }
|
16
|
+
|
17
|
+
describe 'login' do
|
18
|
+
def do_login(overrides = {})
|
19
|
+
cmd = {}
|
20
|
+
cmd[:user] = {}
|
21
|
+
cmd[:user][:username] = overrides[:username] if overrides[:username]
|
22
|
+
cmd[:user][:password] = overrides[:password] if overrides[:password]
|
23
|
+
post '/doorman/login', cmd
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'successful' do
|
27
|
+
before(:each) do
|
28
|
+
do_login(username: email, password: password)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'redirects user to front page (/home)' do
|
32
|
+
expect(last_response.status).to eq(302)
|
33
|
+
expect(last_response).to be_redirect
|
34
|
+
expect(last_response.location).to eq('http://example.org/home')
|
35
|
+
follow_redirect!
|
36
|
+
expect(last_response.body).to eq('Hello World!')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns session info' do
|
40
|
+
get '/session'
|
41
|
+
|
42
|
+
expect(last_response.status).to eq(200)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns user info' do
|
46
|
+
get '/user'
|
47
|
+
|
48
|
+
expect(last_response.status).to eq(200)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'unsuccessful' do
|
53
|
+
context 'wrong password' do
|
54
|
+
before(:each) do
|
55
|
+
do_login(username: email, password: 'foo/bar')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'redirects back to login if authentication failed' do
|
59
|
+
expect(last_response.status).to eq(302)
|
60
|
+
follow_redirect!
|
61
|
+
expect(last_response.location).to eq('http://example.org/doorman/login')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'does not return user info' do
|
65
|
+
get '/user'
|
66
|
+
|
67
|
+
expect(last_response.status).to eq(403)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'does not return user info' do
|
71
|
+
get '/user/xml'
|
72
|
+
|
73
|
+
expect(last_response.status).to eq(403)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'user not confirmed' do
|
78
|
+
before(:each) do
|
79
|
+
Obscured::Doorman.configuration[:confirmation] = true
|
80
|
+
do_login(username: email, password: password)
|
81
|
+
end
|
82
|
+
|
83
|
+
after(:each) do
|
84
|
+
Obscured::Doorman.configuration[:confirmation] = false
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'redirects back to login when authentication failed due to confirmation not completed' do
|
88
|
+
expect(last_response.status).to eq(302)
|
89
|
+
follow_redirect!
|
90
|
+
expect(last_response.location).to eq('http://example.org/doorman/login')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'logout' do
|
97
|
+
def do_login(overrides = {})
|
98
|
+
cmd = {}
|
99
|
+
cmd[:user] = {}
|
100
|
+
cmd[:user][:username] = overrides[:username] if overrides[:username]
|
101
|
+
cmd[:user][:password] = overrides[:password] if overrides[:password]
|
102
|
+
post '/doorman/login', cmd
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'successful' do
|
106
|
+
before(:each) do
|
107
|
+
do_login(username: email, password: password)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'logs out the user' do
|
111
|
+
get '/logout'
|
112
|
+
|
113
|
+
expect(last_response.status).to eq(200)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'register' do
|
119
|
+
def do_register(overrides = {})
|
120
|
+
cmd = {}
|
121
|
+
cmd[:user] = {}
|
122
|
+
cmd[:user][:username] = overrides[:username] if overrides[:username]
|
123
|
+
cmd[:user][:password] = overrides[:password] if overrides[:password]
|
124
|
+
post '/doorman/register', cmd
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'successful' do
|
128
|
+
before(:each) do
|
129
|
+
do_register(username: 'lisa.simpson@obscured.se', password: "#{password}#!")
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'redirects user to front page (/home)' do
|
133
|
+
expect(last_response.status).to eq(302)
|
134
|
+
expect(last_response).to be_redirect
|
135
|
+
expect(last_response.location).to eq('http://example.org/home')
|
136
|
+
|
137
|
+
follow_redirect!
|
138
|
+
|
139
|
+
expect(last_response.body).to eq('Hello World!')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe 'confirm' do
|
145
|
+
def do_confirm(token)
|
146
|
+
get "/doorman/confirm/#{token}"
|
147
|
+
end
|
148
|
+
|
149
|
+
let!(:token) { FactoryBot.create(:token, type: :confirm, user: user) }
|
150
|
+
|
151
|
+
context 'successful' do
|
152
|
+
before(:each) do
|
153
|
+
do_confirm(token.token)
|
154
|
+
|
155
|
+
user.reload
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'confirms user and removes token' do
|
159
|
+
expect(last_response.status).to eq(302)
|
160
|
+
expect(user.tokens.where(type: :confirm).count).to eq(0)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'forget' do
|
166
|
+
def do_forgot(overrides = {})
|
167
|
+
cmd = {}
|
168
|
+
cmd[:user] = {}
|
169
|
+
cmd[:user][:username] = overrides[:username] if overrides[:username]
|
170
|
+
post '/doorman/forgot', cmd
|
171
|
+
end
|
172
|
+
|
173
|
+
let(:token) { user.tokens.where(type: :password).first }
|
174
|
+
|
175
|
+
context 'successful' do
|
176
|
+
before(:each) do
|
177
|
+
do_forgot(username: user.username)
|
178
|
+
user.reload
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'confirms user and removes token' do
|
182
|
+
expect(last_response.status).to eq(302)
|
183
|
+
expect(user.tokens.where(type: :password).count).to eq(1)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe 'reset' do
|
189
|
+
def do_reset(overrides = {})
|
190
|
+
cmd = {}
|
191
|
+
cmd[:user] = {}
|
192
|
+
cmd[:user][:username] = overrides[:username] if overrides[:username]
|
193
|
+
cmd[:user][:token] = overrides[:token] if overrides[:token]
|
194
|
+
post '/doorman/reset', cmd
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe 'configuration' do
|
199
|
+
it 'should return default configuration' do
|
200
|
+
expect(Obscured::Doorman.default_configuration).to_not be(nil)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
data/spec/errors_spec.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
FactoryBot.define do
|
4
|
+
factory :user, class: Obscured::Doorman::User do
|
5
|
+
username { 'homer.simpson@obscured.se' }
|
6
|
+
password { BCrypt::Password.create('Password123') }
|
7
|
+
first_name { 'Homer' }
|
8
|
+
last_name { 'Simpson' }
|
9
|
+
mobile { '+467855568' }
|
10
|
+
role { :admin }
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Obscured
|
4
|
+
module Doorman
|
5
|
+
module Spec
|
6
|
+
class App < Sinatra::Base
|
7
|
+
helpers Obscured::Doorman::Helpers
|
8
|
+
register Sinatra::Flash
|
9
|
+
|
10
|
+
use Rack::Session::Cookie,
|
11
|
+
key: 'rack.session',
|
12
|
+
path: '/',
|
13
|
+
secret: 'rspec_secret'
|
14
|
+
use Obscured::Doorman::Middleware
|
15
|
+
|
16
|
+
Obscured::Doorman::Middleware.set :views, "#{File.dirname(__FILE__)}/../views/doorman"
|
17
|
+
|
18
|
+
get '/home' do
|
19
|
+
authorize!
|
20
|
+
|
21
|
+
'Hello World!'
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/session' do
|
25
|
+
authenticate
|
26
|
+
|
27
|
+
{ session: session_info, user: user }.to_json
|
28
|
+
end
|
29
|
+
|
30
|
+
get '/user' do
|
31
|
+
authorized?
|
32
|
+
content_type :json
|
33
|
+
|
34
|
+
{ user: user }.to_json
|
35
|
+
end
|
36
|
+
|
37
|
+
get '/user/xml' do
|
38
|
+
authorized?(:xml)
|
39
|
+
content_type :xml
|
40
|
+
|
41
|
+
{ user: user }.to_xml
|
42
|
+
end
|
43
|
+
|
44
|
+
get '/logout' do
|
45
|
+
authorized?
|
46
|
+
|
47
|
+
logout
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|