mini_auth_rb 0.3.0 → 0.3.2
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 +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +230 -0
- data/Rakefile +12 -0
- data/lib/mini_auth/api_guard.rb +65 -0
- data/lib/mini_auth/auth_error.rb +12 -0
- data/lib/mini_auth/auth_manager.rb +66 -0
- data/lib/mini_auth/guard.rb +36 -0
- data/lib/mini_auth/guarded.rb +47 -0
- data/lib/mini_auth/guest_error.rb +6 -0
- data/lib/mini_auth/reset_guard_middleware.rb +15 -0
- data/lib/mini_auth/session_guard.rb +102 -0
- data/lib/mini_auth/test_guard.rb +23 -0
- data/lib/mini_auth/tests_auth.rb +31 -0
- data/lib/mini_auth/version.rb +5 -0
- data/lib/mini_auth.rb +21 -0
- data/mini_auth.gemspec +38 -0
- metadata +25 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b90d8b77cce44d67049557a5aa2fecaff4fb13de9008c27b0fd0fb5ace999e4
|
4
|
+
data.tar.gz: 35bc5f618a549a68848091bc4ed02a731d198bbe6e05c8b2fa004483e6da3486
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5c4d1be7976a86a5ee77e1b25de8639c13e8644a8cf2135829500e2fa4770451ee1404ac6850d9467ae31d979dea69e8d51971b04c165de28fda578737290b7
|
7
|
+
data.tar.gz: e6a7f512d6efa74baed2518da4b6c046d4c1fa7e1f959ce0ecd3d88824d3cf500c50e02af88620b97fbb628362d0c9bf749aede87f1be869a8777fa06c66004f
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Ali Alhoshaiyan
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all 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,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
# MiniAuth
|
2
|
+
|
3
|
+
A small authenticated gem inspired by Laravel guards pattern.
|
4
|
+
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Install the gem and add to the application's Gemfile by executing:
|
9
|
+
|
10
|
+
$ bundle add mini_auth_rb
|
11
|
+
|
12
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
13
|
+
|
14
|
+
$ gem install mini_auth_rb
|
15
|
+
|
16
|
+
|
17
|
+
## Guard Pattern
|
18
|
+
|
19
|
+
Authentication should be easy to use, implement and simple enough to be secure. Often unnecessary complexity can
|
20
|
+
lead to undesired security problems, and unfortunately a lot of Rails authentication libraries are built upon a lot
|
21
|
+
of layers and try to support different frameworks alongside Rails thus making them more complex and weird to deal with.
|
22
|
+
|
23
|
+
Here with `mini_auth_rb`, you will get the most simple infrastructure you need to deal with, i.e. authentication guards
|
24
|
+
and one helper middleware. You need to bring your own controller, views, password resets, etc.
|
25
|
+
|
26
|
+
Without further ado, let us understand what is a guard?
|
27
|
+
|
28
|
+
Imagine your self going to a party, and you are there at the location but outside. You notice that there is two doors,
|
29
|
+
one for party guests and another one for staff who are running things.
|
30
|
+
|
31
|
+
As a party guest, you will be asked to identify your self by the receptionist (our guard in this case) by showing your
|
32
|
+
invitation. Without a proper and valid invitation you will be simply denied access period.
|
33
|
+
|
34
|
+
As for staff, they have their own entrance, a backdoor. If they try to enter the building from the front door they will
|
35
|
+
be stopped and should go instead through the appropriate door.
|
36
|
+
|
37
|
+
Same goes for guests, they are not allowed through the back door.
|
38
|
+
|
39
|
+
I know this is confusing, by hold on a bit and everything should be clear.
|
40
|
+
|
41
|
+
|
42
|
+
## Our Sample Application
|
43
|
+
|
44
|
+
```text
|
45
|
+
If you are looking for setup instructions and already understand how the library work, you can skip to the next section.
|
46
|
+
```
|
47
|
+
|
48
|
+
We have a small web store, and it is divided into two sections (or routes), the store itself (guest area),
|
49
|
+
and a back-office where staff can supervise and manage the store.
|
50
|
+
|
51
|
+
Our `routes.rb` looks like this:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
Rails.application.routes.draw do
|
55
|
+
# Guest Area
|
56
|
+
resources :items
|
57
|
+
resource :account
|
58
|
+
post :logout, to: 'logout#perform'
|
59
|
+
|
60
|
+
# Registration Help
|
61
|
+
resource :login, only: [:new, :create]
|
62
|
+
resource :registration, only: [:new, :create]
|
63
|
+
|
64
|
+
# Staff Area (back-office)
|
65
|
+
namespace :staff do
|
66
|
+
resources :items
|
67
|
+
resources :orders
|
68
|
+
resources :users
|
69
|
+
resource :login, only: [:new, :create]
|
70
|
+
resource :registration, only: [:new, :create]
|
71
|
+
end
|
72
|
+
|
73
|
+
root to: 'home#index'
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
Stop here for a moment and think how are you going to solve the issue of authenticating different kind of users (or entities).
|
78
|
+
|
79
|
+
Some will suggest that there should be a `User` model with some kind of column to identify the type of role, either
|
80
|
+
a user of staff.
|
81
|
+
|
82
|
+
Hmm, but how are we going to prevent users from entering back office. Easy! you say, use a before action that does a check.
|
83
|
+
|
84
|
+
Well buddy, let us stop here. We need a clear line between user and staff here, I can't just keep asking people at the
|
85
|
+
party are you a guest or a staff? I need a solid way to identify between them and know where every one should go.
|
86
|
+
|
87
|
+
Here is an idea, let's make all staff dress in a custom that makes it easy to tell them apart from guest and makes it
|
88
|
+
easy for our guards to stop anyone not authorized from entering the back-office and any other restricted areas.
|
89
|
+
|
90
|
+
We can introduce two models, a `Staff` and a `User` model. Our code should know exactly what type they are dealing with
|
91
|
+
and simple static analysis should let us know if something is wrong.
|
92
|
+
|
93
|
+
|
94
|
+
## Auth Configuration
|
95
|
+
|
96
|
+
To use MiniAuth in your project, you will need to create a new initializer file at `config/initializers/mini_auth.rb`,
|
97
|
+
and add the following content:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
# frozen_string_literal: true
|
101
|
+
|
102
|
+
# This middleware is used to remove any initialized guard related to the current request
|
103
|
+
# after it has been processed and response is generated.
|
104
|
+
Rails.application.config.middleware.use(MiniAuth::ResetGuardMiddleware)
|
105
|
+
|
106
|
+
ActiveSupport.on_load :after_initialize do
|
107
|
+
routes = Rails.application.routes.url_helpers
|
108
|
+
|
109
|
+
guards = [
|
110
|
+
{
|
111
|
+
name: :web,
|
112
|
+
builder: -> (manager, name, request) {
|
113
|
+
MiniAuth::SessionGuard.new(name, User, request, routes.login_path, 86400)
|
114
|
+
}
|
115
|
+
},
|
116
|
+
{
|
117
|
+
name: :staff,
|
118
|
+
builder: -> (manager, name, request) {
|
119
|
+
MiniAuth::SessionGuard.new(name, Staff, request, routes.staff_login_path, 3600)
|
120
|
+
}
|
121
|
+
}
|
122
|
+
]
|
123
|
+
|
124
|
+
Rails.application.config.mini_auth = {
|
125
|
+
default: :web,
|
126
|
+
guards: guards.freeze
|
127
|
+
}
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
|
132
|
+
## Guarding Controllers
|
133
|
+
|
134
|
+
Guards work at the controller level by leveraging the `before_action` feature in Rails.
|
135
|
+
|
136
|
+
To use guard throughout your application, add the following concern to your `ApplicationController` class:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
class ApplicationController < ActionController::Base
|
140
|
+
include MiniAuth::Guarded
|
141
|
+
|
142
|
+
# ... other stuff here
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
Now we can simply protect or guard a controller or specific action as follows:
|
147
|
+
|
148
|
+
Here is an example for users:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
class AccountController < ApplicationController
|
152
|
+
auth_requests! :web
|
153
|
+
|
154
|
+
def index
|
155
|
+
# current_user.is_a?(User) #=> true
|
156
|
+
end
|
157
|
+
|
158
|
+
def update
|
159
|
+
# This is just an example
|
160
|
+
current_user.update!(user_update_params)
|
161
|
+
end
|
162
|
+
|
163
|
+
def deactivate
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
Here is an example for staff:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
class Staff::UsersController < ApplicationController
|
173
|
+
auth_requests! :staff, only: :index
|
174
|
+
|
175
|
+
def index
|
176
|
+
# current_user.is_a?(Staff) #=> true
|
177
|
+
end
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
We can also prevent authenticated users from entering a specific controller:
|
182
|
+
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
class LoginController < ApplicationController
|
186
|
+
guest_only! :web
|
187
|
+
|
188
|
+
def new
|
189
|
+
# current_user.nil? #=> true
|
190
|
+
end
|
191
|
+
|
192
|
+
def create
|
193
|
+
# ...
|
194
|
+
end
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
198
|
+
## The Guard Class
|
199
|
+
|
200
|
+
Have a look at [guard.rb](lib/mini_auth/guard.rb), it defines an interface that dictates how the process
|
201
|
+
of authentication works.
|
202
|
+
|
203
|
+
Whenever a request is made to a guarded controller, the `user` method will be called and it should return an instance
|
204
|
+
of the current entity or `nil` if the request is not authenticated.
|
205
|
+
|
206
|
+
For example, in a typical web application, the `SessionGuard` will look for a specific key within the current Rails session,
|
207
|
+
and try to match it against a record in the database.
|
208
|
+
|
209
|
+
Another example is the `ApiGuard`, it will read a `Bearer` token from the current request and find the user related to it.
|
210
|
+
|
211
|
+
The `SessionGuard` can be considered a stateful guard as it depends on the current state of the session and provides
|
212
|
+
the `attempt` and `login` methods to set the current user of the session.
|
213
|
+
|
214
|
+
|
215
|
+
## SessionGuard
|
216
|
+
|
217
|
+
|
218
|
+
|
219
|
+
## ApiGuard
|
220
|
+
|
221
|
+
|
222
|
+
## Your Own Guard
|
223
|
+
|
224
|
+
|
225
|
+
### How it Works?
|
226
|
+
|
227
|
+
|
228
|
+
## License
|
229
|
+
|
230
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
class MiniAuth::ApiGuard < MiniAuth::Guard
|
6
|
+
attr_reader :name, :model, :request
|
7
|
+
|
8
|
+
# @param [Symbol] name
|
9
|
+
# @param [Class] model
|
10
|
+
# @param [ActionDispatch::Request] request
|
11
|
+
def initialize(name, model, request)
|
12
|
+
@name = name
|
13
|
+
@model = model
|
14
|
+
@request = request
|
15
|
+
end
|
16
|
+
|
17
|
+
def attempt!(credentials, remember = nil)
|
18
|
+
raise NotImplementedError, 'ApiGuard does not support auth attempt.'
|
19
|
+
end
|
20
|
+
|
21
|
+
def login!(user, remember = nil)
|
22
|
+
@user = user
|
23
|
+
end
|
24
|
+
|
25
|
+
def logout!
|
26
|
+
@user = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def user
|
30
|
+
@user ||= infer_current_user
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def infer_current_user
|
36
|
+
return nil if @infer_tried
|
37
|
+
|
38
|
+
@infer_tried ||= true
|
39
|
+
key = key_from_bearer || key_from_basic
|
40
|
+
return nil if key.nil?
|
41
|
+
|
42
|
+
@model.where(api_key: key).first
|
43
|
+
end
|
44
|
+
|
45
|
+
def key_from_basic
|
46
|
+
header = auth_header
|
47
|
+
return nil if header.empty?
|
48
|
+
return nil unless header.start_with?('Bearer ')
|
49
|
+
|
50
|
+
header.delete_prefix('Bearer ')
|
51
|
+
end
|
52
|
+
|
53
|
+
def key_from_bearer
|
54
|
+
header = auth_header
|
55
|
+
return nil if header.empty?
|
56
|
+
return nil unless header.start_with?('Basic ')
|
57
|
+
|
58
|
+
header = header.delete_prefix('Basic ')
|
59
|
+
Base64.decode64(header).split(':')[0]
|
60
|
+
end
|
61
|
+
|
62
|
+
def auth_header
|
63
|
+
request.headers['Authorization']
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniAuth
|
4
|
+
Semaphore = Mutex.new
|
5
|
+
|
6
|
+
class AuthManager
|
7
|
+
attr_reader :default_guard, :definitions
|
8
|
+
|
9
|
+
def initialize(default_guard, definitions)
|
10
|
+
@default_guard = default_guard
|
11
|
+
@definitions = definitions.clone(freeze: false)
|
12
|
+
@guards = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Hash] config
|
16
|
+
def self.make(config)
|
17
|
+
new(config[:default], config[:guards])
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [MiniAuth::AuthManager]
|
21
|
+
def self.instance
|
22
|
+
Semaphore.synchronize do
|
23
|
+
@@instances ||= {}
|
24
|
+
@@instances[Thread.current.__id__] ||= make(Rails.application.config.mini_auth)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.reset!
|
29
|
+
Semaphore.synchronize do
|
30
|
+
@@instances ||= {}
|
31
|
+
@@instances.delete(Thread.current.__id__)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset_guards!
|
36
|
+
@guards = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param [ActionDispatch::Request] request
|
40
|
+
# @param [MiniAuth::Guard|NilClass] guard
|
41
|
+
# @return [MiniAuth::Guard]
|
42
|
+
def fetch_guard(request, guard = nil)
|
43
|
+
guard = default_guard if guard.nil?
|
44
|
+
@guards[guard] ||= init_guard(request, guard)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def supports_guard?(guard)
|
50
|
+
@definitions.any? { |gd| gd[:name] == guard }
|
51
|
+
end
|
52
|
+
|
53
|
+
def guard_definition(guard)
|
54
|
+
@definitions.find { |gd| gd[:name] == guard }
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param [ActionDispatch::Request] request
|
58
|
+
# @param [MiniAuth::Guard|NilClass] guard
|
59
|
+
def init_guard(request, guard)
|
60
|
+
raise ArgumentError, "Undefined guard #{guard}" unless supports_guard?(guard)
|
61
|
+
|
62
|
+
guard_builder = guard_definition(guard)[:builder]
|
63
|
+
guard_builder.call(self, guard, request)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniAuth::Guard
|
4
|
+
# @param [Hash] credentials
|
5
|
+
# @param [Boolean] remember
|
6
|
+
def attempt!(credentials, remember = false)
|
7
|
+
raise NotImplementedError, 'Use implementation instead.'
|
8
|
+
end
|
9
|
+
|
10
|
+
# @param [Object] user
|
11
|
+
# @param [Boolean] remember
|
12
|
+
def login!(user, remember = false)
|
13
|
+
raise NotImplementedError, 'Use implementation instead.'
|
14
|
+
end
|
15
|
+
|
16
|
+
def logout!
|
17
|
+
raise NotImplementedError, 'Use implementation instead.'
|
18
|
+
end
|
19
|
+
|
20
|
+
def user
|
21
|
+
raise NotImplementedError, 'Use implementation instead.'
|
22
|
+
end
|
23
|
+
|
24
|
+
def logged_in?
|
25
|
+
user.present?
|
26
|
+
end
|
27
|
+
|
28
|
+
def guest?
|
29
|
+
!logged_in?
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [String] A URL to authentication path
|
33
|
+
def auth_url
|
34
|
+
raise NotImplementedError, 'Use implementation instead.'
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniAuth
|
4
|
+
module Guarded
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# @return [MiniAuth::AuthManager]
|
8
|
+
def auth_manager
|
9
|
+
@auth_manager ||= MiniAuth::AuthManager.instance
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [MiniAuth::Guard]
|
13
|
+
def auth_guard(name = nil)
|
14
|
+
auth_manager.fetch_guard(request, name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_user
|
18
|
+
auth_guard.user
|
19
|
+
end
|
20
|
+
|
21
|
+
class_methods do
|
22
|
+
def auth_requests!(guard = nil, **options)
|
23
|
+
raise ArgumentError, 'Guard must be a Symbol' unless guard.nil? || guard.is_a?(Symbol)
|
24
|
+
|
25
|
+
before_action(options.slice(:only, :except)) do |c|
|
26
|
+
guard_instance = c.auth_guard(guard)
|
27
|
+
|
28
|
+
unless guard_instance.logged_in?
|
29
|
+
raise AuthError.new('User must be logged in!', guard_instance.auth_url)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def guest_only!(guard = nil, **options)
|
35
|
+
raise ArgumentError, 'Guard must be a Symbol' unless guard.nil? || guard.is_a?(Symbol)
|
36
|
+
|
37
|
+
before_action(options.slice(:only, :except)) do |c|
|
38
|
+
guard_instance = c.auth_guard(guard)
|
39
|
+
|
40
|
+
unless guard_instance.guest?
|
41
|
+
raise GuestError, 'This resource is only available for unauthenticated requests'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bcrypt'
|
4
|
+
|
5
|
+
class MiniAuth::SessionGuard < MiniAuth::Guard
|
6
|
+
REMEMBER_DURATION = 86400 * 30
|
7
|
+
|
8
|
+
attr_reader :name, :model, :request, :session, :login_url
|
9
|
+
|
10
|
+
def self.hash_password(plaintext)
|
11
|
+
BCrypt::Password.create(plaintext, cost: 12)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [String] name
|
15
|
+
# @param [Class] model
|
16
|
+
# @param [ActionDispatch::Request] request
|
17
|
+
# @param [String] login_url
|
18
|
+
# @param [Integer] remember_time
|
19
|
+
def initialize(name, model, request, login_url, remember_time = REMEMBER_DURATION)
|
20
|
+
@name = name
|
21
|
+
@model = model
|
22
|
+
@request = request
|
23
|
+
@login_url = login_url
|
24
|
+
@remember_time = remember_time
|
25
|
+
end
|
26
|
+
|
27
|
+
def attempt!(credentials, remember = false)
|
28
|
+
user = @model.where(credentials.except(:password)).first
|
29
|
+
return false if user.blank?
|
30
|
+
|
31
|
+
unless BCrypt::Password.new(user.password).is_password?(credentials[:password])
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
|
35
|
+
login!(user, remember)
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
def login!(user, remember = false)
|
40
|
+
@user = user
|
41
|
+
@request.reset_session
|
42
|
+
@request.session[session_storage_key] = user.id
|
43
|
+
|
44
|
+
if remember
|
45
|
+
@user.remember_token = SecureRandom.base58(48)
|
46
|
+
@user.save!
|
47
|
+
|
48
|
+
@request.cookie_jar.encrypted.signed[:remember_me] = {
|
49
|
+
value: @user.remember_token,
|
50
|
+
expires: (Time.now + @remember_time).utc,
|
51
|
+
httponly: true
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
@user
|
56
|
+
end
|
57
|
+
|
58
|
+
def logout!
|
59
|
+
@user = nil
|
60
|
+
|
61
|
+
@request.session.delete(session_storage_key)
|
62
|
+
@request.cookie_jar.delete(:remember_me)
|
63
|
+
@request.reset_session
|
64
|
+
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def user
|
69
|
+
@user ||= fetch_session_user
|
70
|
+
end
|
71
|
+
|
72
|
+
def auth_url
|
73
|
+
login_url
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def session_storage_key
|
79
|
+
"_mini_auth_session_guard_#{@name.downcase}_#{@model.name.downcase}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def fetch_session_user
|
83
|
+
if @request.session[session_storage_key].present?
|
84
|
+
return @model.where(id: @request.session[session_storage_key]).first
|
85
|
+
end
|
86
|
+
|
87
|
+
token = @request.cookie_jar.encrypted.signed[:remember_me]
|
88
|
+
unless token.blank?
|
89
|
+
user = @model.where(remember_token: token).first
|
90
|
+
|
91
|
+
if user
|
92
|
+
login!(user, false)
|
93
|
+
else
|
94
|
+
@request.cookie_jar.delete(:remember_me)
|
95
|
+
end
|
96
|
+
|
97
|
+
return user
|
98
|
+
end
|
99
|
+
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MiniAuth::TestGuard < MiniAuth::Guard
|
4
|
+
def attempt!(credentials, remember = nil)
|
5
|
+
raise NotImplementedError, 'Testing guard does not support login attempts.'
|
6
|
+
end
|
7
|
+
|
8
|
+
def login!(user, remember = nil)
|
9
|
+
@user = user
|
10
|
+
end
|
11
|
+
|
12
|
+
def logout!
|
13
|
+
@user = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def user
|
17
|
+
@user ||= nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def auth_url
|
21
|
+
'/login'
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniAuth
|
4
|
+
module TestsAuth
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def after_teardown
|
8
|
+
MiniAuth::AuthManager.reset!
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def act_as(user, guard = nil)
|
13
|
+
manager = auth_manager
|
14
|
+
manager.instance_eval do |s|
|
15
|
+
@definitions.unshift({ name: guard || @default_guard, builder: -> (_, _, _) { MiniAuth::TestGuard.new } })
|
16
|
+
end
|
17
|
+
|
18
|
+
manager.fetch_guard(ActionDispatch::Request.new({}), guard).login!(user)
|
19
|
+
end
|
20
|
+
|
21
|
+
def actor(guard = nil)
|
22
|
+
auth_manager.fetch_guard(nil, guard).user
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def auth_manager
|
28
|
+
MiniAuth::AuthManager.instance
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/mini_auth.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_model'
|
5
|
+
require 'action_pack'
|
6
|
+
require 'bcrypt'
|
7
|
+
|
8
|
+
require_relative 'mini_auth/version'
|
9
|
+
require_relative 'mini_auth/guard'
|
10
|
+
require_relative 'mini_auth/auth_error'
|
11
|
+
require_relative 'mini_auth/guest_error'
|
12
|
+
require_relative 'mini_auth/auth_manager'
|
13
|
+
require_relative 'mini_auth/guarded'
|
14
|
+
require_relative 'mini_auth/session_guard'
|
15
|
+
require_relative 'mini_auth/api_guard'
|
16
|
+
require_relative 'mini_auth/reset_guard_middleware'
|
17
|
+
require_relative 'mini_auth/test_guard'
|
18
|
+
require_relative 'mini_auth/tests_auth'
|
19
|
+
|
20
|
+
module MiniAuth
|
21
|
+
end
|
data/mini_auth.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/mini_auth/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'mini_auth_rb'
|
7
|
+
spec.version = MiniAuth::VERSION
|
8
|
+
spec.authors = ['Ali Alhoshaiyan']
|
9
|
+
spec.email = ['ahoshaiyan@fastmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'Pragmatic authentication for Ruby on Rails.'
|
12
|
+
spec.description = 'MiniAuth is a pragmatic authentication gem for Rails that is inspired by Laravel\'s guards pattern that provides highly customizable and simple authentication for your web applications and APIs.'
|
13
|
+
spec.homepage = 'https://github.com/ahoshaiyan/mini_auth'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = '>= 2.6.6'
|
16
|
+
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/ahoshaiyan/mini_auth'
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/ahoshaiyan/mini_auth'
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(__dir__) do
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
spec.bindir = 'exe'
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ['lib']
|
32
|
+
|
33
|
+
# Uncomment to register a new dependency of your gem
|
34
|
+
spec.add_runtime_dependency 'activesupport', '>= 6.0'
|
35
|
+
spec.add_runtime_dependency 'activemodel', '>= 6.0'
|
36
|
+
spec.add_runtime_dependency 'actionpack', '>= 6.0'
|
37
|
+
spec.add_runtime_dependency 'bcrypt', '>= 1.0.0'
|
38
|
+
end
|
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_auth_rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ali Alhoshaiyan
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
11
|
date: 2022-11-27 00:00:00.000000000 Z
|
@@ -74,7 +74,25 @@ email:
|
|
74
74
|
executables: []
|
75
75
|
extensions: []
|
76
76
|
extra_rdoc_files: []
|
77
|
-
files:
|
77
|
+
files:
|
78
|
+
- CHANGELOG.md
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- lib/mini_auth.rb
|
84
|
+
- lib/mini_auth/api_guard.rb
|
85
|
+
- lib/mini_auth/auth_error.rb
|
86
|
+
- lib/mini_auth/auth_manager.rb
|
87
|
+
- lib/mini_auth/guard.rb
|
88
|
+
- lib/mini_auth/guarded.rb
|
89
|
+
- lib/mini_auth/guest_error.rb
|
90
|
+
- lib/mini_auth/reset_guard_middleware.rb
|
91
|
+
- lib/mini_auth/session_guard.rb
|
92
|
+
- lib/mini_auth/test_guard.rb
|
93
|
+
- lib/mini_auth/tests_auth.rb
|
94
|
+
- lib/mini_auth/version.rb
|
95
|
+
- mini_auth.gemspec
|
78
96
|
homepage: https://github.com/ahoshaiyan/mini_auth
|
79
97
|
licenses:
|
80
98
|
- MIT
|
@@ -82,7 +100,7 @@ metadata:
|
|
82
100
|
homepage_uri: https://github.com/ahoshaiyan/mini_auth
|
83
101
|
source_code_uri: https://github.com/ahoshaiyan/mini_auth
|
84
102
|
changelog_uri: https://github.com/ahoshaiyan/mini_auth
|
85
|
-
post_install_message:
|
103
|
+
post_install_message:
|
86
104
|
rdoc_options: []
|
87
105
|
require_paths:
|
88
106
|
- lib
|
@@ -90,15 +108,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
90
108
|
requirements:
|
91
109
|
- - ">="
|
92
110
|
- !ruby/object:Gem::Version
|
93
|
-
version: 2.6.
|
111
|
+
version: 2.6.6
|
94
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
113
|
requirements:
|
96
114
|
- - ">="
|
97
115
|
- !ruby/object:Gem::Version
|
98
116
|
version: '0'
|
99
117
|
requirements: []
|
100
|
-
rubygems_version: 3.
|
101
|
-
signing_key:
|
118
|
+
rubygems_version: 3.1.6
|
119
|
+
signing_key:
|
102
120
|
specification_version: 4
|
103
121
|
summary: Pragmatic authentication for Ruby on Rails.
|
104
122
|
test_files: []
|