mini_auth_rb 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|