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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc9907ad2cf934f7a61fc086c1b92a75239a76d3f31dd460c27610a33507721f
4
- data.tar.gz: 1ec26dfefef621b95ba1234e9f925905bd4231b358372e143e01720bc8215d46
3
+ metadata.gz: 7a805653b216cf10cc22f5022741c3e1983f281283a079dc3b44288d74826a40
4
+ data.tar.gz: 69c9cbfb19ce32de6b7a7ba5479454e04c7cc4600b00eb80f84d7c5c89c89575
5
5
  SHA512:
6
- metadata.gz: ccead31f66c557e86aa9e4d718159e1928d8f8785fb2221d44e9997c010a840fa418b3cac28a3f00915660eae173a284500a328c404028cbafe81e10b773aa45
7
- data.tar.gz: 398360da3a38d578c41d0441d36f6f71ea0e6a0ca59b323d4bd97415d66fd17a5594b0683b36c5bf3cfbfb39b73f593d0ed6aedc90c3420c635cffeede4a6298
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 of
5
- # this class is initialized every time `authenticate_mais_user!` completes successfully.
6
- # The current MAIS user can be accessed anytime via the `mais_user` method.
7
- class User
8
- attr_reader :username, :full_name
9
-
10
- def initialize(*params)
11
- params = params[0]
12
- @username = params["username"]
13
- @full_name = params["full_name"]
14
- end
15
- end
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
@@ -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: 1.1.2
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: 2020-05-31 00:00:00.000000000 Z
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: 4.0.2
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: 4.0.2
40
+ version: 12.3.3
27
41
  - !ruby/object:Gem::Dependency
28
- name: bundler
42
+ name: practical-pig
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '2.0'
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: '2.0'
54
+ version: '1.0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: rake
56
+ name: rubocop
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - "~>"
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: '10.0'
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: '10.0'
55
- description: A simple gem that provides HTTP Basic Authentication for users registered
56
- with the MAIS(tm) accounts application.
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
- - ".gitattributes"
64
- - ".gitignore"
65
- - Gemfile
66
- - README.md
67
- - lib/mais-access.rb
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
- source_code_uri: https://github.com/sdbase/mais-access
76
- homepage_uri: https://github.com/sdbase/mais-access
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: '0'
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.1.3
148
+ rubygems_version: 3.4.6
93
149
  signing_key:
94
150
  specification_version: 4
95
- summary: A MAIS(tm) authentication middleware.
151
+ summary: Provides an HTTP/JWT authentication middleware.
96
152
  test_files: []
data/.gitattributes DELETED
@@ -1,2 +0,0 @@
1
- # Auto detect text files and perform LF normalization
2
- * text=auto
data/.gitignore DELETED
@@ -1,2 +0,0 @@
1
- /node_modules
2
- *.gem
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in parceler.gemspec
4
- gemspec
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