mais-access 1.1.1 → 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: f88002e1195c7f1187c27fa7a244619fa3101a2ab91a650260dfcd9acb9d9912
4
- data.tar.gz: fdc4e863615ef1ff267d989a0bdd2696ffa2bdbd876695cf4a15b484c4949aaf
3
+ metadata.gz: 7a805653b216cf10cc22f5022741c3e1983f281283a079dc3b44288d74826a40
4
+ data.tar.gz: 69c9cbfb19ce32de6b7a7ba5479454e04c7cc4600b00eb80f84d7c5c89c89575
5
5
  SHA512:
6
- metadata.gz: 59f48ae598a636cfcaa9b82acb728e70977d7dbd384cf85404d3d11179db9ec46b79ce0c479790e4c05e1e13dd12f98f1d3bcd54a4bfa04f53cd662528d69dd9
7
- data.tar.gz: e350a1b236ecefb20a7184d301c94c3be458c5ec4a8e1b81c986a17ef59257467c4fe89e324f1de48a5a699318073a0981800f4f8e6d33c90a1e9a58eb1df53c
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
@@ -0,0 +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
+ # 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.1
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-02-18 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,16 +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
- rubyforge_project:
93
- rubygems_version: 2.7.6.2
148
+ rubygems_version: 3.4.6
94
149
  signing_key:
95
150
  specification_version: 4
96
- summary: A MAIS(tm) authentication middleware.
151
+ summary: Provides an HTTP/JWT authentication middleware.
97
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,44 +0,0 @@
1
- module MaisAccess
2
- module Dispatcher
3
- require 'net/https'
4
- require 'uri'
5
- require 'json'
6
- require 'mais-access/user'
7
-
8
- attr_reader :mais_user
9
-
10
- MAIS_CLIENT = ENV['MAIS_CLIENT']
11
-
12
- def authenticate_mais_user!
13
- # Prompt the user for HTTP Basic credentials, or authenticate if they are cached in the session
14
- authenticate_or_request_with_http_basic("access - MAIS - #{MAIS_CLIENT}") do |login, password|
15
- begin
16
- # Setup https connection and specify certificate bundle
17
- url = URI("#{ENV['MAIS_ACCOUNTS_HOSTNAME']}/authenticate")
18
- http = Net::HTTP.new(url.host, 443)
19
- http.use_ssl = true
20
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
21
- http.cert_store = OpenSSL::X509::Store.new
22
- http.cert_store.set_default_paths
23
- http.cert_store.add_file("/etc/pki/tls/certs/server.crt")
24
-
25
- # Get the credentials and POST them to `accounts.scenycwork.net/authenticate`
26
- request = Net::HTTP::Post.new(url.path, {'Content-Type' => 'application/json'})
27
- request.set_form_data({ "username" => login, "password" => password })
28
- response = http.request(request)
29
-
30
- # Parse the response body as JSON
31
- body = JSON.parse(response.body)
32
-
33
- # If the user is valid, set the current mais user and passes the filter action
34
- if response.code == '200' && body["authenticated"]
35
- @mais_user = MaisAccess::User.new(body["user"])
36
- return true
37
- end
38
- rescue => e
39
- # Something went wrong, so save our butts and don't them in.
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,16 +0,0 @@
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 everytime `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
data/lib/mais-access.rb DELETED
@@ -1,18 +0,0 @@
1
- module MaisAccess
2
- class Railtie < ::Rails::Railtie
3
- initializer('mais.middleware') do |app|
4
- # Hook into ActionController's loading process
5
- ActiveSupport.on_load(:action_controller) do
6
- require 'mais-access/dispatcher'
7
- include MaisAccess::Dispatcher
8
-
9
- # Mark the `mais_user` reader method (defined in Mais::Dispatcher) as a
10
- # helper so that it can be accessed from a view
11
- helper_method :mais_user
12
-
13
- # Force a MAIS user authentication check on every request
14
- before_action :authenticate_mais_user!
15
- end
16
- end
17
- end
18
- end
data/mais-access.gemspec DELETED
@@ -1,27 +0,0 @@
1
- lib = File.expand_path("lib", __dir__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
-
4
- Gem::Specification.new do |spec|
5
- spec.name = "mais-access"
6
- spec.version = "1.1.1"
7
- spec.platform = Gem::Platform::RUBY
8
- spec.author = "Elias Gabriel"
9
- spec.email = "me@eliasfgabriel.com"
10
- spec.summary = "A MAIS(tm) authentication middleware."
11
- spec.description = "A simple gem that provides HTTP Basic Authentication for users registered with the MAIS(tm) accounts application."
12
- spec.homepage = "https://github.com/sdbase/mais-access"
13
- spec.license = "CC-BY-NC-SA-4.0"
14
-
15
- spec.metadata["homepage_uri"] = spec.metadata["source_code_uri"] = spec.homepage
16
- spec.extra_rdoc_files = ["README.md"]
17
-
18
- spec.require_paths = ["lib"]
19
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
- `git ls-files -z`.split("\x0")
21
- end
22
-
23
- spec.add_dependency "rails", '>= 4.0.2'
24
-
25
- spec.add_development_dependency "bundler", '~> 2.0'
26
- spec.add_development_dependency "rake", '~> 10.0'
27
- end