mais-access 1.1.2 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
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