mais-access 1.1.2 → 2.1.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/lib/mais/access/controller.rb +27 -0
- data/lib/mais/access/dispatcher.rb +73 -0
- data/lib/{mais-access → mais/access}/user.rb +17 -16
- data/lib/mais/access.rb +33 -0
- metadata +86 -30
- data/.gitattributes +0 -2
- data/.gitignore +0 -2
- data/Gemfile +0 -4
- data/README.md +0 -11
- data/lib/mais-access/dispatcher.rb +0 -61
- data/lib/mais-access.rb +0 -20
- data/mais-access.gemspec +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a805653b216cf10cc22f5022741c3e1983f281283a079dc3b44288d74826a40
|
4
|
+
data.tar.gz: 69c9cbfb19ce32de6b7a7ba5479454e04c7cc4600b00eb80f84d7c5c89c89575
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d661f99ca020d193ff8b55f9ac94b8fc8ed783390812bf9a83a4b2d4042d2361ebeae802ffdc328f6661d2ca9feae36eb47382a86bddcf0b0ae062b5c133cc0b
|
7
|
+
data.tar.gz: 4965b71be71484ae798897f07b84bef6f16d33e8db7e40e97637708b7af2956dbfc9ab32f626100c7ab8b529ce9f5b6ff23b43369389f77d633878e5747ea299
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MaisAccess
|
4
|
+
module Controller
|
5
|
+
require "mais/access/dispatcher"
|
6
|
+
require "mais/access/user"
|
7
|
+
|
8
|
+
include MaisAccess::Dispatcher
|
9
|
+
|
10
|
+
MAIS_CLIENT = (Rails.application.credentials[:client] || "unregistered").freeze
|
11
|
+
PROMPT = "access - MAIS - #{MAIS_CLIENT}"
|
12
|
+
public_constant :MAIS_CLIENT
|
13
|
+
private_constant :PROMPT
|
14
|
+
|
15
|
+
def mais_user
|
16
|
+
@mais_user ||= MaisAccess::User.new(session[:mais_user])
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def authenticate_mais_user!
|
22
|
+
valid_jwt? || authenticate_or_request_with_http_basic(PROMPT) do |l, p|
|
23
|
+
user?(l, p)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MaisAccess
|
4
|
+
module Dispatcher
|
5
|
+
AUTH_ENDPOINT = "#{ENV.fetch("MAIS_ACCOUNTS_HOSTNAME")}/api/authenticate"
|
6
|
+
JWT_ENDPOINT = "#{ENV.fetch("MAIS_ACCOUNTS_HOSTNAME")}/api/verify"
|
7
|
+
private_constant :AUTH_ENDPOINT
|
8
|
+
private_constant :JWT_ENDPOINT
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def set_session(user = nil, jwt = nil)
|
13
|
+
reset_session
|
14
|
+
session[:mais_user] = user
|
15
|
+
session[:jwt] = jwt
|
16
|
+
end
|
17
|
+
|
18
|
+
def make_secure_http(endpoint)
|
19
|
+
uri = URI(endpoint)
|
20
|
+
|
21
|
+
begin
|
22
|
+
# Setup and yield an HTTPS connection to the specified endpoint
|
23
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
24
|
+
yield(uri.path, http)
|
25
|
+
end
|
26
|
+
rescue StandardError => e
|
27
|
+
Rails.logger.error(e)
|
28
|
+
# Something went wrong, so save our butts and don't them in
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid_jwt?
|
34
|
+
return false if session[:mais_user].blank? || session[:jwt].blank?
|
35
|
+
|
36
|
+
make_secure_http(JWT_ENDPOINT) do |path, http|
|
37
|
+
# Try to access the verification endpoint with the stored JWT
|
38
|
+
request = Net::HTTP::Get.new(path)
|
39
|
+
request["Authorization"] = session[:jwt]
|
40
|
+
response = http.request(request)
|
41
|
+
|
42
|
+
# If the server returns HTTP 204 No Content, the token is valid.
|
43
|
+
# `next` is required instead of `return` because `return` exits
|
44
|
+
# without finishing the block execution (cleaning up)
|
45
|
+
next true if response.code == "204"
|
46
|
+
|
47
|
+
# invalid token, force a session reset
|
48
|
+
set_session
|
49
|
+
false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def user?(login, password)
|
54
|
+
make_secure_http(AUTH_ENDPOINT) do |path, http|
|
55
|
+
# Post the credentials to the authentication endpoint
|
56
|
+
request = Net::HTTP::Post.new(path, { "Content-Type" => "application/json" })
|
57
|
+
request.body = JSON.generate({ user: { username: login, password: password } })
|
58
|
+
response = http.request(request)
|
59
|
+
|
60
|
+
# Parse the response body as JSON
|
61
|
+
body = JSON.parse(response.body)
|
62
|
+
|
63
|
+
# If the user is valid, reset the session and set the current mais user
|
64
|
+
if response.code == "201" && body["user"]
|
65
|
+
set_session(body["user"], response["Authorization"])
|
66
|
+
next true
|
67
|
+
end
|
68
|
+
|
69
|
+
false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -1,16 +1,17 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MaisAccess
|
4
|
-
# An abstract class used to store the currently authenticated MAIS user. An instance
|
5
|
-
# this class is initialized every time `authenticate_mais_user!` completes
|
6
|
-
# The current MAIS user can be accessed anytime via the `mais_user`
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MaisAccess
|
4
|
+
# An abstract class used to store the currently authenticated MAIS user. An instance
|
5
|
+
# of this class is initialized every time `authenticate_mais_user!` completes
|
6
|
+
# successfully. The current MAIS user can be accessed anytime via the `mais_user`
|
7
|
+
# method.
|
8
|
+
class User
|
9
|
+
attr_reader :username, :full_name
|
10
|
+
|
11
|
+
def initialize(*params)
|
12
|
+
params = params[0]
|
13
|
+
@username = params["username"]
|
14
|
+
@full_name = params["full_name"]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/mais/access.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MaisAccess
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
initializer("mais.middleware") do |_app|
|
6
|
+
# Hook into ActionController's loading process
|
7
|
+
ActiveSupport.on_load(:action_controller) do
|
8
|
+
require "mais/access/controller"
|
9
|
+
|
10
|
+
include MaisAccess::Controller
|
11
|
+
|
12
|
+
# Mark the `mais_user` reader method as a helper so that it can be accessed
|
13
|
+
# from a view
|
14
|
+
helper_method :mais_user
|
15
|
+
|
16
|
+
# Force a MAIS user authentication check on every request
|
17
|
+
before_action :authenticate_mais_user!
|
18
|
+
|
19
|
+
# "Cross-Site Request Forgery (CSRF) is an attack that allows a malicious user
|
20
|
+
# to spoof legitimate requests to your server, masquerading as an authenticated
|
21
|
+
# user. Rails protects against this kind of attack by generating unique tokens
|
22
|
+
# and validating their authenticity with each submission."
|
23
|
+
#
|
24
|
+
# https://link.medium.com/q1U60lAYm7
|
25
|
+
#
|
26
|
+
# If Rails detects an invalid authentication token, reset the current session.
|
27
|
+
# Prepend forgery protection to the beginning of the request action chain to
|
28
|
+
# ensure that it is the first thing to run.
|
29
|
+
protect_from_forgery with: :reset_session, prepend: true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
CHANGED
@@ -1,79 +1,135 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mais-access
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elias Gabriel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - ">="
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
33
|
+
version: 12.3.3
|
20
34
|
type: :runtime
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
40
|
+
version: 12.3.3
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
42
|
+
name: practical-pig
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
47
|
+
version: '1.0'
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
54
|
+
version: '1.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: rubocop
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
|
-
- - "
|
59
|
+
- - ">="
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '0'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- - "
|
66
|
+
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
55
|
-
|
56
|
-
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
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: rubocop-performance
|
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: rubocop-rails
|
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
|
+
description: mais-access provides a simple yet secure HTTP(S) authentication barrier
|
112
|
+
for applications developed within the MAIS system. After initial connection, sessions
|
113
|
+
for authenticated clients are validated by JSON Web Tokens for reduced overhead
|
114
|
+
and improved security.
|
57
115
|
email: me@eliasfgabriel.com
|
58
116
|
executables: []
|
59
117
|
extensions: []
|
60
|
-
extra_rdoc_files:
|
61
|
-
- README.md
|
118
|
+
extra_rdoc_files: []
|
62
119
|
files:
|
63
|
-
-
|
64
|
-
-
|
65
|
-
-
|
66
|
-
-
|
67
|
-
|
68
|
-
- lib/mais-access/dispatcher.rb
|
69
|
-
- lib/mais-access/user.rb
|
70
|
-
- mais-access.gemspec
|
71
|
-
homepage: https://github.com/sdbase/mais-access
|
120
|
+
- lib/mais/access.rb
|
121
|
+
- lib/mais/access/controller.rb
|
122
|
+
- lib/mais/access/dispatcher.rb
|
123
|
+
- lib/mais/access/user.rb
|
124
|
+
homepage: https://github.com/metabronx/mais-access
|
72
125
|
licenses:
|
73
126
|
- CC-BY-NC-SA-4.0
|
74
127
|
metadata:
|
75
|
-
|
76
|
-
|
128
|
+
homepage_uri: https://www.metabronx.com
|
129
|
+
source_code_uri: https://github.com/metabronx/mais-access
|
130
|
+
bug_tracker_uri: https://github.com/metabronx/mais-access/issues
|
131
|
+
rubygems_mfa_required: 'true'
|
132
|
+
funding_uri: https://www.metabronx.com/invest
|
77
133
|
post_install_message:
|
78
134
|
rdoc_options: []
|
79
135
|
require_paths:
|
@@ -82,15 +138,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
138
|
requirements:
|
83
139
|
- - ">="
|
84
140
|
- !ruby/object:Gem::Version
|
85
|
-
version:
|
141
|
+
version: 2.4.0
|
86
142
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
143
|
requirements:
|
88
144
|
- - ">="
|
89
145
|
- !ruby/object:Gem::Version
|
90
146
|
version: '0'
|
91
147
|
requirements: []
|
92
|
-
rubygems_version: 3.
|
148
|
+
rubygems_version: 3.4.6
|
93
149
|
signing_key:
|
94
150
|
specification_version: 4
|
95
|
-
summary:
|
151
|
+
summary: Provides an HTTP/JWT authentication middleware.
|
96
152
|
test_files: []
|
data/.gitattributes
DELETED
data/.gitignore
DELETED
data/Gemfile
DELETED
data/README.md
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
# MAIS Access
|
2
|
-
|
3
|
-
A simple gem that enforces HTTP Basic Authentication for account holders in the MAIS business management and information system.
|
4
|
-
|
5
|
-
## License
|
6
|
-
|
7
|
-
Copyright � 2019 [Elias Gabriel](https://eliasfgabriel.com/), [sdbase](http://sdbase.com/)
|
8
|
-
|
9
|
-
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
10
|
-
|
11
|
-
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MaisAccess
|
4
|
-
module Dispatcher
|
5
|
-
require 'net/https'
|
6
|
-
require 'uri'
|
7
|
-
require 'json'
|
8
|
-
require 'mais-access/user'
|
9
|
-
|
10
|
-
attr_reader :mais_user
|
11
|
-
|
12
|
-
MAIS_CLIENT = ENV['MAIS_CLIENT']
|
13
|
-
APP_TITLE = "access - MAIS - #{MAIS_CLIENT}"
|
14
|
-
|
15
|
-
def authenticate_mais_user!
|
16
|
-
# Prompt the user for HTTP Basic credentials or authenticate if they are
|
17
|
-
# cached in the browser session
|
18
|
-
authenticate_or_request_with_http_basic(APP_TITLE) { |l, p| user?(l, p) }
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def user?(login, password)
|
24
|
-
begin
|
25
|
-
url = URI("#{ENV['MAIS_ACCOUNTS_HOSTNAME']}/authenticate")
|
26
|
-
|
27
|
-
# Setup https connection and specify certificate bundle if enabled
|
28
|
-
if (ENV.fetch("USE_HTTPS") { false })
|
29
|
-
http = Net::HTTP.new(url.host, 443)
|
30
|
-
http.use_ssl = true
|
31
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
32
|
-
http.cert_store = OpenSSL::X509::Store.new
|
33
|
-
http.cert_store.set_default_paths
|
34
|
-
http.cert_store.add_file("/etc/pki/tls/certs/server.crt")
|
35
|
-
else
|
36
|
-
http = Net::HTTP.new(url.host, url.port)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Get the credentials and POST them to `accounts.scenycwork.net/authenticate`
|
40
|
-
request = Net::HTTP::Post.new(url.path, {'Content-Type' => 'application/json'})
|
41
|
-
request.set_form_data({ "username" => login, "password" => password })
|
42
|
-
response = http.request(request)
|
43
|
-
|
44
|
-
# Parse the response body as JSON
|
45
|
-
body = JSON.parse(response.body)
|
46
|
-
|
47
|
-
# If the user is valid, set the current mais user
|
48
|
-
if response.code == '200' && body["authenticated"]
|
49
|
-
@mais_user = MaisAccess::User.new(body["user"])
|
50
|
-
# let them in
|
51
|
-
return true
|
52
|
-
end
|
53
|
-
rescue => e
|
54
|
-
Rails.logger.error(e)
|
55
|
-
end
|
56
|
-
|
57
|
-
# Something went wrong, so save our butts and don't them in.
|
58
|
-
return false
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
data/lib/mais-access.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MaisAccess
|
4
|
-
class Railtie < ::Rails::Railtie
|
5
|
-
initializer('mais.middleware') do |app|
|
6
|
-
# Hook into ActionController's loading process
|
7
|
-
ActiveSupport.on_load(:action_controller) do
|
8
|
-
require 'mais-access/dispatcher'
|
9
|
-
include MaisAccess::Dispatcher
|
10
|
-
|
11
|
-
# Mark the `mais_user` reader method (defined in Mais::Dispatcher) as a
|
12
|
-
# helper so that it can be accessed from a view
|
13
|
-
helper_method :mais_user
|
14
|
-
|
15
|
-
# Force a MAIS user authentication check on every request
|
16
|
-
before_action :authenticate_mais_user!
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/mais-access.gemspec
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
lib = File.expand_path("lib", __dir__)
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "mais-access"
|
8
|
-
spec.version = "1.1.2"
|
9
|
-
spec.author = "Elias Gabriel"
|
10
|
-
spec.email = "me@eliasfgabriel.com"
|
11
|
-
spec.summary = "A MAIS(tm) authentication middleware."
|
12
|
-
spec.description = "A simple gem that provides HTTP Basic Authentication for users registered with the MAIS(tm) accounts application."
|
13
|
-
spec.homepage = "https://github.com/sdbase/mais-access"
|
14
|
-
spec.license = "CC-BY-NC-SA-4.0"
|
15
|
-
|
16
|
-
spec.metadata["homepage_uri"] = spec.metadata["source_code_uri"] = spec.homepage
|
17
|
-
spec.extra_rdoc_files = ["README.md"]
|
18
|
-
|
19
|
-
spec.require_paths = ["lib"]
|
20
|
-
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
21
|
-
`git ls-files -z`.split("\x0")
|
22
|
-
end
|
23
|
-
|
24
|
-
spec.add_dependency "rails", '>= 4.0.2'
|
25
|
-
|
26
|
-
spec.add_development_dependency "bundler", '~> 2.0'
|
27
|
-
spec.add_development_dependency "rake", '~> 10.0'
|
28
|
-
end
|