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,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Obscured
|
4
|
+
module Doorman
|
5
|
+
# Contains logging behavior.
|
6
|
+
module Loggable
|
7
|
+
# Get the logger.
|
8
|
+
#
|
9
|
+
# @note Will try to grab Rails' logger first before creating a new logger
|
10
|
+
# with stdout.
|
11
|
+
#
|
12
|
+
# @example Get the logger.
|
13
|
+
# Loggable.logger
|
14
|
+
#
|
15
|
+
# @return [ Logger ] The logger.
|
16
|
+
def logger
|
17
|
+
return @logger if defined?(@logger)
|
18
|
+
|
19
|
+
@logger = default_logger
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set the logger.
|
23
|
+
#
|
24
|
+
# @example Set the logger.
|
25
|
+
# Loggable.logger = Logger.new($stdout)
|
26
|
+
#
|
27
|
+
# @param [ Logger ] logger The logger to set.
|
28
|
+
#
|
29
|
+
# @return [ Logger ] The new logger.
|
30
|
+
def logger=(logger)
|
31
|
+
@logger = logger
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Gets the default Mongoid logger - stdout.
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
#
|
40
|
+
# @example Get the default logger.
|
41
|
+
# Loggable.default_logger
|
42
|
+
#
|
43
|
+
# @return [ Logger ] The default logger.
|
44
|
+
def default_logger
|
45
|
+
logger = Logger.new($stdout)
|
46
|
+
logger.level = Doorman.configuration.log_level
|
47
|
+
logger
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Obscured
|
4
|
+
module Doorman
|
5
|
+
class Mailer
|
6
|
+
def initialize(opts = {})
|
7
|
+
@to = opts[:to]
|
8
|
+
@from = "doorman@#{Doorman.configuration.smtp_domain}"
|
9
|
+
@subject = opts[:subject]
|
10
|
+
|
11
|
+
@text = opts[:text]
|
12
|
+
@html = opts[:html]
|
13
|
+
end
|
14
|
+
|
15
|
+
def deliver!
|
16
|
+
Doorman.logger.debug "Sending mail to #{@to}, from: #{@from}, with subject: #{@subject} and text #{@text}"
|
17
|
+
mail = Mail.new(to: @to, from: @from, subject: @subject) do
|
18
|
+
delivery_method :smtp,
|
19
|
+
address: Doorman.configuration.smtp_server,
|
20
|
+
port: Doorman.configuration.smtp_port,
|
21
|
+
domain: Doorman.configuration.smtp_domain,
|
22
|
+
enable_starttls_auto: true,
|
23
|
+
authentication: :plain,
|
24
|
+
user_name: Doorman.configuration.smtp_username,
|
25
|
+
password: Doorman.configuration.smtp_password
|
26
|
+
end
|
27
|
+
|
28
|
+
unless @text.blank?
|
29
|
+
text_part = Mail::Part.new(body: @text)
|
30
|
+
mail.text_part = text_part
|
31
|
+
end
|
32
|
+
|
33
|
+
unless @html.blank?
|
34
|
+
html_part = Mail::Part.new(body: @html) do
|
35
|
+
content_type 'text/html; charset=utf-8'
|
36
|
+
end
|
37
|
+
mail.html_part = html_part
|
38
|
+
end
|
39
|
+
|
40
|
+
mail.deliver
|
41
|
+
rescue => e
|
42
|
+
Doorman.logger.error e
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Obscured
|
4
|
+
module Doorman
|
5
|
+
MESSAGES = {
|
6
|
+
auth_required: 'You must be logged in to view this page.',
|
7
|
+
signup_disabled: 'Registration is disabled, contact team member for creation of account!',
|
8
|
+
signup_success: 'You have signed up successfully. A confirmation email has been sent to you.',
|
9
|
+
confirm_no_user: 'Invalid confirmation URL. Please make sure you have the correct link from the email, and are not already confirmed.',
|
10
|
+
confirm_success: 'You have successfully confirmed your account. Please log in.',
|
11
|
+
# Auto login upon confirmation?
|
12
|
+
login_bad_credentials: 'Invalid Login and Password. Please try again.',
|
13
|
+
login_not_confirmed: 'You must confirm your account before you can log in. Please click the confirmation link sent to you.',
|
14
|
+
# Note: resend confirmation link?
|
15
|
+
logout_success: 'You have been logged out.',
|
16
|
+
forgot_no_user: 'There is no user with that Username or Email. Please try again.',
|
17
|
+
forgot_success: 'An email with instructions to reset your password has been sent to you.',
|
18
|
+
reset_no_user: 'Invalid reset URL. Please make sure you have the correct link from the email, and have already reset the password.',
|
19
|
+
reset_system_user: 'Your trying to reset the password of a system user, unfortunate for you, this action is not allowed',
|
20
|
+
reset_unmatched_passwords: 'Password and confirmation do not match. Please try again.',
|
21
|
+
reset_success: 'Your password has been reset.',
|
22
|
+
# Registration
|
23
|
+
register_account_exists: 'Account already registered.',
|
24
|
+
# Token
|
25
|
+
token_used: 'The token has already been used, request a new token and try again.',
|
26
|
+
token_expired: 'The token has expired, request a new token and try again.',
|
27
|
+
token_not_found: 'The token was not found, request a new token and try again.'
|
28
|
+
}.freeze
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Obscured
|
4
|
+
module Doorman
|
5
|
+
class Token
|
6
|
+
include Mongoid::Document
|
7
|
+
include Mongoid::Timestamps
|
8
|
+
|
9
|
+
store_in database: Doorman.configuration.db_name,
|
10
|
+
client: Doorman.configuration.db_client,
|
11
|
+
collection: 'tokens'
|
12
|
+
|
13
|
+
field :type, type: Symbol
|
14
|
+
field :token, type: String
|
15
|
+
field :expires_at, type: DateTime, default: -> { DateTime.now + 2.hours }
|
16
|
+
field :used_at, type: DateTime
|
17
|
+
field :user_id, type: BSON::ObjectId
|
18
|
+
|
19
|
+
belongs_to :user, autosave: true, class_name: 'Obscured::Doorman::User', inverse_of: 'tokens'
|
20
|
+
|
21
|
+
index({ expires_at: 1 }, background: true, expire_after_seconds: 172_800)
|
22
|
+
index({ used_at: 1 }, background: true, expire_after_seconds: 345_600)
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def make(opts)
|
26
|
+
raise Doorman::Error.new(:already_exists, what: 'Token does already exists!') if Token.where(user: opts[:user], type: opts[:type]).exists?
|
27
|
+
|
28
|
+
token = new
|
29
|
+
token.user = opts[:user]
|
30
|
+
token.type = opts[:type]
|
31
|
+
token.token = opts[:token]
|
32
|
+
token.expires_at = opts[:expires] if opts[:expires]
|
33
|
+
token
|
34
|
+
end
|
35
|
+
|
36
|
+
def make!(opts)
|
37
|
+
token = make(opts)
|
38
|
+
token.save
|
39
|
+
token
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def use!
|
44
|
+
self.used_at = DateTime.now
|
45
|
+
save
|
46
|
+
end
|
47
|
+
|
48
|
+
def usable?
|
49
|
+
used_at.nil? && expires_at > DateTime.now
|
50
|
+
end
|
51
|
+
|
52
|
+
def used?
|
53
|
+
!used_at.nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'obscured-timeline'
|
4
|
+
|
5
|
+
module Obscured
|
6
|
+
module Doorman
|
7
|
+
class User
|
8
|
+
include Mongoid::Document
|
9
|
+
include Mongoid::Timestamps
|
10
|
+
include Mongoid::Timeline::Tracker
|
11
|
+
|
12
|
+
store_in database: Doorman.configuration.db_name,
|
13
|
+
client: Doorman.configuration.db_client,
|
14
|
+
collection: 'users'
|
15
|
+
|
16
|
+
field :username, type: String
|
17
|
+
field :password, type: String
|
18
|
+
field :salt, type: String
|
19
|
+
field :first_name, type: String
|
20
|
+
field :last_name, type: String
|
21
|
+
field :mobile, type: String
|
22
|
+
field :role, type: Symbol, default: Doorman::Roles::ADMIN
|
23
|
+
field :confirmed, type: Boolean, default: false
|
24
|
+
|
25
|
+
has_many :tokens, autosave: true, class_name: 'Obscured::Doorman::Token', foreign_key: 'user_id'
|
26
|
+
|
27
|
+
index({ username: 1 }, background: true)
|
28
|
+
|
29
|
+
after_initialize :set_salt
|
30
|
+
|
31
|
+
alias email username
|
32
|
+
|
33
|
+
attr_accessor :confirmed
|
34
|
+
|
35
|
+
class << self
|
36
|
+
def make(opts)
|
37
|
+
raise Doorman::Error.new(:already_exists, what: 'User does already exists!') if User.where(username: opts[:username]).exists?
|
38
|
+
|
39
|
+
user = new
|
40
|
+
user.username = opts[:username]
|
41
|
+
user.set_password(opts[:password])
|
42
|
+
user.first_name = opts[:first_name] unless opts[:first_name].nil?
|
43
|
+
user.last_name = opts[:last_name] unless opts[:last_name].nil?
|
44
|
+
user.mobile = opts[:mobile] unless opts[:mobile].nil?
|
45
|
+
user.role = opts[:role] unless opts[:role].nil?
|
46
|
+
user.confirmed = opts[:confirmed] unless opts[:confirmed].nil?
|
47
|
+
user.add_event(type: :account, message: 'Account created', producer: opts[:producer].nil? ? user.username : opts[:producer])
|
48
|
+
user
|
49
|
+
end
|
50
|
+
|
51
|
+
def make!(opts)
|
52
|
+
user = make(opts)
|
53
|
+
user.save
|
54
|
+
user
|
55
|
+
end
|
56
|
+
|
57
|
+
def authenticate(username, password)
|
58
|
+
user = where(username: username).first
|
59
|
+
return user if user&.authenticated?(password)
|
60
|
+
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def registered?(username)
|
65
|
+
where(username: username).exists?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def name
|
70
|
+
"#{first_name} #{last_name}"
|
71
|
+
end
|
72
|
+
|
73
|
+
def name=(arguments)
|
74
|
+
self.first_name = arguments[:first_name]
|
75
|
+
self.last_name = arguments[:last_name]
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_password(password)
|
79
|
+
self.password = BCrypt::Password.create(password)
|
80
|
+
end
|
81
|
+
|
82
|
+
def authenticated?(password)
|
83
|
+
(BCrypt::Password.new(self.password) == password)
|
84
|
+
end
|
85
|
+
alias password? authenticated?
|
86
|
+
|
87
|
+
def remember_me!
|
88
|
+
add_event(type: :remember, message: 'Account set to be remembered upon login', producer: username)
|
89
|
+
token = tokens.build(
|
90
|
+
type: :remember,
|
91
|
+
token: SecureRandom.uuid,
|
92
|
+
expires_at: (DateTime.now + Doorman.configuration.remember_for.days)
|
93
|
+
)
|
94
|
+
save
|
95
|
+
token
|
96
|
+
end
|
97
|
+
|
98
|
+
def forget_me!
|
99
|
+
add_event(type: :remember, message: 'Account set not to be remembered upon login', producer: username)
|
100
|
+
tokens.where(type: :remember).destroy
|
101
|
+
end
|
102
|
+
|
103
|
+
def confirm
|
104
|
+
add_event(type: :confirm, message: 'Confirmation token created', producer: username)
|
105
|
+
tokens.where(type: :confirm).destroy
|
106
|
+
token = tokens.build(
|
107
|
+
type: :confirm,
|
108
|
+
token: SecureRandom.uuid,
|
109
|
+
expires_at: (DateTime.now + 14.days)
|
110
|
+
)
|
111
|
+
save
|
112
|
+
token
|
113
|
+
end
|
114
|
+
|
115
|
+
def confirm!
|
116
|
+
add_event(type: :confirmation, message: 'Account was successfully confirmed', producer: username)
|
117
|
+
self.confirmed = true
|
118
|
+
tokens.where(type: :confirm).destroy
|
119
|
+
save
|
120
|
+
end
|
121
|
+
|
122
|
+
def forgot_password!
|
123
|
+
add_event(type: :password, message: 'Reset password procedure has been started', producer: username)
|
124
|
+
tokens.where(type: :password).destroy
|
125
|
+
token = tokens.build(
|
126
|
+
user: self,
|
127
|
+
type: :password,
|
128
|
+
token: SecureRandom.uuid,
|
129
|
+
expires_at: (DateTime.now + 2.hours)
|
130
|
+
)
|
131
|
+
save
|
132
|
+
token
|
133
|
+
end
|
134
|
+
|
135
|
+
def remembered_password!
|
136
|
+
add_event(type: :password, message: 'Reset password procedure has been cancelled since successful login was achieved', producer: username)
|
137
|
+
tokens.where(type: :password).destroy
|
138
|
+
end
|
139
|
+
|
140
|
+
def reset_password!(password, token)
|
141
|
+
token = tokens.find_by(token: token)
|
142
|
+
if token && token.type.eql?(:password)
|
143
|
+
set_password(password)
|
144
|
+
add_event(type: :password, message: 'Password was successfully reset', producer: username)
|
145
|
+
return save
|
146
|
+
end
|
147
|
+
false
|
148
|
+
end
|
149
|
+
|
150
|
+
protected
|
151
|
+
|
152
|
+
def set_salt
|
153
|
+
return unless salt.nil? || salt.empty?
|
154
|
+
|
155
|
+
secret = Digest::SHA1.hexdigest("--#{username}--")
|
156
|
+
self.salt = Digest::SHA1.hexdigest("--#{Time.now.utc}--#{secret}--")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Obscured
|
4
|
+
module Doorman
|
5
|
+
module Providers
|
6
|
+
class BaseConfiguration
|
7
|
+
def self.config_option(name)
|
8
|
+
define_method(name) do
|
9
|
+
read_value(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
define_method("#{name}=") do |value|
|
13
|
+
set_value(name, value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Name of the authentication provider
|
18
|
+
config_option :provider
|
19
|
+
# Enables/disables the provider
|
20
|
+
config_option :enabled
|
21
|
+
|
22
|
+
# Provider client id
|
23
|
+
config_option :client_id
|
24
|
+
# Provider client secret
|
25
|
+
config_option :client_secret
|
26
|
+
# Provider scopes
|
27
|
+
config_option :scopes
|
28
|
+
|
29
|
+
# Provider authentication endpoint
|
30
|
+
config_option :authorize_url
|
31
|
+
# Provider token endpoint
|
32
|
+
config_option :token_url
|
33
|
+
# Provider login endpoint
|
34
|
+
config_option :login_url
|
35
|
+
# Provider redirect endpoint
|
36
|
+
config_option :redirect_url
|
37
|
+
|
38
|
+
# Authentication domains to login
|
39
|
+
config_option :domains
|
40
|
+
# Authentication token
|
41
|
+
config_option :token
|
42
|
+
|
43
|
+
attr_reader :defaults
|
44
|
+
|
45
|
+
def [](key)
|
46
|
+
read_value(key)
|
47
|
+
end
|
48
|
+
|
49
|
+
def []=(key, value)
|
50
|
+
set_value(key, value)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def read_value(name)
|
56
|
+
if @config_values.key?(name)
|
57
|
+
@config_values[name]
|
58
|
+
else
|
59
|
+
@defaults.send(name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_value(name, value)
|
64
|
+
@config_values[name] = value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('bitbucket/configuration', __dir__)
|
4
|
+
require File.expand_path('bitbucket/messages', __dir__)
|
5
|
+
require File.expand_path('bitbucket/access_token', __dir__)
|
6
|
+
require File.expand_path('bitbucket/strategy', __dir__)
|
7
|
+
|
8
|
+
module Obscured
|
9
|
+
module Doorman
|
10
|
+
module Providers
|
11
|
+
module Bitbucket
|
12
|
+
class << self
|
13
|
+
# Configuration Object (instance of Obscured::Doorman::Providers::Bitbucket::Configuration)
|
14
|
+
attr_writer :configuration
|
15
|
+
|
16
|
+
def setup
|
17
|
+
yield(configuration)
|
18
|
+
end
|
19
|
+
|
20
|
+
def configuration
|
21
|
+
@configuration ||= Bitbucket::Configuration.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_configuration
|
25
|
+
configuration.defaults
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.registered(app)
|
30
|
+
app.helpers Doorman::Base::Helpers
|
31
|
+
app.helpers Doorman::Helpers
|
32
|
+
|
33
|
+
Warden::Strategies.add(:bitbucket, Bitbucket::Strategy)
|
34
|
+
|
35
|
+
app.get '/doorman/oauth2/bitbucket' do
|
36
|
+
redirect("#{Bitbucket.configuration[:authorize_url]}?client_id=#{Bitbucket.configuration[:client_id]}&response_type=code&scopes=#{Bitbucket.configuration[:scopes]}")
|
37
|
+
end
|
38
|
+
|
39
|
+
app.get '/doorman/oauth2/bitbucket/callback/?' do
|
40
|
+
response = RestClient::Request.new(
|
41
|
+
method: :post,
|
42
|
+
url: Bitbucket.configuration[:token_url],
|
43
|
+
user: Bitbucket.configuration[:client_id],
|
44
|
+
password: Bitbucket.configuration[:client_secret],
|
45
|
+
payload: "code=#{params[:code]}&grant_type=authorization_code&scope=#{Bitbucket.configuration[:scopes]}",
|
46
|
+
headers: { Accept: 'application/json' }
|
47
|
+
).execute
|
48
|
+
|
49
|
+
json = JSON.parse(response.body)
|
50
|
+
token = Bitbucket::AccessToken.new(
|
51
|
+
access_token: json['access_token'],
|
52
|
+
refresh_token: json['refresh_token'],
|
53
|
+
scopes: json['scopes'],
|
54
|
+
expires_in: json['expires_in']
|
55
|
+
)
|
56
|
+
|
57
|
+
emails = RestClient.get 'https://api.bitbucket.org/2.0/user/emails', Authorization: "Bearer #{token.access_token}"
|
58
|
+
emails = JSON.parse(emails.body)
|
59
|
+
token.emails = emails.values[1].map { |e| e['email'] }
|
60
|
+
Bitbucket.configuration[:token] = token
|
61
|
+
|
62
|
+
# Authenticate with :bitbucket strategy
|
63
|
+
warden.authenticate!(:bitbucket)
|
64
|
+
rescue RestClient::ExceptionWithResponse => e
|
65
|
+
message = JSON.parse(e.response)
|
66
|
+
Doorman.logger.error e
|
67
|
+
notify :error, "#{message['error_description']} (#{message['error']})"
|
68
|
+
redirect(Doorman.configuration.paths[:login])
|
69
|
+
ensure
|
70
|
+
# Notify if there are any messages from Warden.
|
71
|
+
notify :error, warden.message unless warden.message.blank?
|
72
|
+
|
73
|
+
redirect(Doorman.configuration.use_referrer && session[:return_to] ? session.delete(:return_to) : Doorman.configuration.paths[:success])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|