prx_auth-rails 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/README.md +25 -6
  4. data/Rakefile +12 -4
  5. data/app/controllers/prx_auth/rails/sessions_controller.rb +108 -0
  6. data/app/views/prx_auth/rails/sessions/auth_error.html.erb +15 -0
  7. data/app/views/prx_auth/rails/sessions/show.html.erb +38 -0
  8. data/config/routes.rb +7 -0
  9. data/lib/prx_auth/rails.rb +1 -0
  10. data/lib/prx_auth/rails/configuration.rb +15 -4
  11. data/lib/prx_auth/rails/engine.rb +5 -0
  12. data/lib/prx_auth/rails/ext/controller.rb +29 -4
  13. data/lib/prx_auth/rails/token.rb +5 -1
  14. data/lib/prx_auth/rails/version.rb +1 -1
  15. data/prx_auth-rails.gemspec +3 -1
  16. data/test/dummy/Rakefile +6 -0
  17. data/test/dummy/app/assets/config/manifest.js +2 -0
  18. data/test/dummy/app/assets/images/.keep +0 -0
  19. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  20. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  21. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  22. data/test/dummy/app/controllers/application_controller.rb +8 -0
  23. data/test/dummy/app/controllers/concerns/.keep +0 -0
  24. data/test/dummy/app/helpers/application_helper.rb +2 -0
  25. data/test/dummy/app/javascript/packs/application.js +15 -0
  26. data/test/dummy/app/jobs/application_job.rb +7 -0
  27. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  28. data/test/dummy/app/models/application_record.rb +3 -0
  29. data/test/dummy/app/models/concerns/.keep +0 -0
  30. data/test/dummy/app/views/layouts/application.html.erb +15 -0
  31. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  32. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  33. data/test/dummy/bin/rails +5 -0
  34. data/test/dummy/bin/rake +5 -0
  35. data/test/dummy/bin/setup +33 -0
  36. data/test/dummy/bin/spring +10 -0
  37. data/test/dummy/config.ru +6 -0
  38. data/test/dummy/config/application.rb +22 -0
  39. data/test/dummy/config/boot.rb +5 -0
  40. data/test/dummy/config/cable.yml +10 -0
  41. data/test/dummy/config/database.yml +25 -0
  42. data/test/dummy/config/environment.rb +5 -0
  43. data/test/dummy/config/environments/development.rb +76 -0
  44. data/test/dummy/config/environments/production.rb +120 -0
  45. data/test/dummy/config/environments/test.rb +60 -0
  46. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  47. data/test/dummy/config/initializers/assets.rb +12 -0
  48. data/test/dummy/config/initializers/backtrace_silencers.rb +8 -0
  49. data/test/dummy/config/initializers/content_security_policy.rb +28 -0
  50. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  51. data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  52. data/test/dummy/config/initializers/inflections.rb +16 -0
  53. data/test/dummy/config/initializers/mime_types.rb +4 -0
  54. data/test/dummy/config/initializers/permissions_policy.rb +11 -0
  55. data/test/dummy/config/initializers/prx_auth.rb +8 -0
  56. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/test/dummy/config/locales/en.yml +33 -0
  58. data/test/dummy/config/puma.rb +43 -0
  59. data/test/dummy/config/routes.rb +3 -0
  60. data/test/dummy/config/spring.rb +6 -0
  61. data/test/dummy/config/storage.yml +34 -0
  62. data/test/dummy/lib/assets/.keep +0 -0
  63. data/test/dummy/log/.keep +0 -0
  64. data/test/dummy/public/404.html +67 -0
  65. data/test/dummy/public/422.html +67 -0
  66. data/test/dummy/public/500.html +66 -0
  67. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  68. data/test/dummy/public/apple-touch-icon.png +0 -0
  69. data/test/dummy/public/favicon.ico +0 -0
  70. data/test/dummy/storage/.keep +0 -0
  71. data/test/prx_auth/rails/configuration_test.rb +18 -12
  72. data/test/prx_auth/rails/sessions_controller_test.rb +94 -0
  73. data/test/prx_auth/rails/token_test.rb +1 -1
  74. data/test/test_helper.rb +20 -9
  75. metadata +153 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5204c5e69c74ec1fa4d6acf36b64c62700ba72d5308a020deee0526ff4dd5499
4
- data.tar.gz: c5bbf5402868c36ba3e2c011eb4eb906d895e3b5237975a329099047f39a8cb3
3
+ metadata.gz: 3405e4e72d2c39585111bef4fca339a73ce11ec86a2c288f5c82d8934c0620fe
4
+ data.tar.gz: 3450063f4c4fdae2464f755222fafee08c627eda3aa8fee1d267db1474db58de
5
5
  SHA512:
6
- metadata.gz: 0d3c3f2ba128a55921138c56e1c052ef6dfd030f720886daa5b77d831545ae9c093ac6c19cbc9887c41b17003142fe2c6eedbf18f6a102ce5e08ec7179556b49
7
- data.tar.gz: cedc895cbe9b69bd7f87c365c7a0bf606236291e7572a7da5395c110612adf1e4164e40ed02e1f00517ee89da3e7c26563329b82bdb7026db4b563c113758fcf
6
+ metadata.gz: edf33f5cf4bc105818bb069554506f0b2d9ef5b8bb18cd1602b2ceb6384991804b5b3268c6bd5c519c08e8503df51c132a9bec045f13995c1d55088194fff489
7
+ data.tar.gz: 4aa62706ee892c2335756e2c3ae61171b77acc69bfe0ec06433449726f8e884cee4af2031e55e418c6780e9aedc8bfb1c78359c072c089389422a2d4aa15c95b
data/.gitignore CHANGED
@@ -14,6 +14,10 @@ rdoc
14
14
  spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
+ test/dummy/db/
18
+ test/dummy/log/development.log
19
+ test/dummy/log/test.log
20
+ test/log/test.log
17
21
  tmp
18
22
  .ruby-version
19
23
  .DS_Store
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # PrxAuth::Rails
2
2
 
3
- Rails integration for next generation PRX Authorization system.
3
+ Rails integration for next generation PRX Authorization system. This
4
+ provides common OpenId authorization patterns used in PRX apps.
4
5
 
5
6
  ## Installation
6
7
 
@@ -14,17 +15,32 @@ And then execute:
14
15
 
15
16
  ## Usage
16
17
 
17
- Installing the gem in a Rails project will automatically add the appropriate Rack middleware to your Rails application and add two methods to your controllers. These methods are:
18
+ Installing the gem in a Rails project will automatically add the
19
+ appropriate Rack middleware to your Rails application and add two
20
+ methods to your controllers. These methods are:
18
21
 
19
- * `prx_auth_token`: returns a token (similar to PrxAuth::Token) which automatically namespaces queries. The main methods you will be interested in are `authorized?`, `globally_authorized?` and `resources`. More information can be found in PrxAuth.
22
+ * `prx_auth_token`: returns a token (similar to PrxAuth::Token) which
23
+ automatically namespaces queries. The main methods you will be
24
+ interested in are `authorized?`, `globally_authorized?` and `resources`.
25
+ More information can be found in PrxAuth.
20
26
 
21
- * `prx_authenticated?`: returns whether or not this request includes a valid PrxAuth token.
27
+ * `prx_authenticated?`: returns whether or not this request includes a
28
+ valid PrxAuth token.
29
+
30
+ This will let set up the Rails app to be ready for HTTP requests
31
+ associated with an OpenId access token.
22
32
 
23
33
  ### Configuration
24
34
 
25
- Generally, configuration is not required and the gem aims for great defaults, but you can override some settings if you need to change the default behavior.
35
+ Generally, configuration is not required and the gem aims for great
36
+ defaults, but you can override some settings if you need to change the
37
+ default behavior.
38
+
39
+ If you're using the Rails server-side session flow, you must supply the
40
+ client_id via configuration.
26
41
 
27
- In your rails app, add a file to config/initializers called `prx_auth.rb`:
42
+ In your rails app, add a file to config/initializers called
43
+ `prx_auth.rb`:
28
44
 
29
45
  ```ruby
30
46
  PrxAuth::Rails.configure do |config|
@@ -36,6 +52,9 @@ PrxAuth::Rails.configure do |config|
36
52
  # as .authorized?(:my_great_ns, :foo). Has no impact on unscoped queries.
37
53
  config.namespace = :my_great_ns # default: derived from Rails::Application name.
38
54
  # e.g. class Feeder < Rails::Application => :feeder
55
+
56
+ # Set up the PRX OpenID client_id if using the backend rails sessions flow.
57
+ config.client_id = '<some client id>'
39
58
  end
40
59
  ```
41
60
 
data/Rakefile CHANGED
@@ -1,10 +1,18 @@
1
- require 'bundler/gem_tasks'
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
2
9
  require 'rake'
3
- require 'rake/testtask'
10
+ require "rake/testtask"
4
11
 
5
- Rake::TestTask.new do |t|
12
+ Rake::TestTask.new(:test) do |t|
6
13
  t.libs << 'test'
7
- t.pattern = 'test/**/*test.rb'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = false
8
16
  end
9
17
 
10
18
  task default: :test
@@ -0,0 +1,108 @@
1
+ require 'open-uri'
2
+
3
+ module PrxAuth::Rails
4
+ class SessionsController < ApplicationController
5
+ include PrxAuth::Rails::Engine.routes.url_helpers
6
+
7
+ skip_before_action :authenticate!
8
+
9
+ before_action :set_nonce!, only: :show
10
+
11
+ ID_NONCE_SESSION_KEY = 'id_prx_openid_nonce'.freeze
12
+
13
+ def new
14
+ set_nonce! unless fetch_nonce.present?
15
+
16
+ config = PrxAuth::Rails.configuration
17
+
18
+ id_auth_params = {
19
+ client_id: config.prx_client_id,
20
+ nonce: fetch_nonce,
21
+ response_type: 'id_token token',
22
+ scope: 'openid apps',
23
+ prompt: 'necessary'
24
+ }
25
+
26
+ redirect_to '//' + config.id_host + '/authorize?' + id_auth_params.to_query
27
+ end
28
+
29
+ def show
30
+ end
31
+
32
+ def auth_error
33
+ @auth_error_message = params.require(:error)
34
+ end
35
+
36
+ def create
37
+ jwt_id_claims = id_claims
38
+ jwt_access_claims = access_claims
39
+
40
+ jwt_access_claims['id_token'] = jwt_id_claims.as_json
41
+
42
+ result_path = if valid_nonce?(jwt_id_claims['nonce']) &&
43
+ users_match?(jwt_id_claims, jwt_access_claims)
44
+ sign_in_user(jwt_access_claims)
45
+ after_sign_in_path_for(current_user)
46
+ else
47
+ auth_error_sessions_path(error: 'verification_failed')
48
+ end
49
+ reset_nonce!
50
+
51
+ redirect_to result_path
52
+ end
53
+
54
+ private
55
+
56
+ def after_sign_in_path_for(_)
57
+ return super if defined?(super)
58
+
59
+ "/"
60
+ end
61
+
62
+ def id_claims
63
+ id_token = params.require('id_token')
64
+ validate_token(id_token)
65
+ end
66
+
67
+ def access_claims
68
+ access_token = params.require('access_token')
69
+ validate_token(access_token)
70
+ end
71
+
72
+ def reset_nonce!
73
+ session[ID_NONCE_SESSION_KEY] = nil
74
+ end
75
+
76
+ def set_nonce!
77
+ n = session[ID_NONCE_SESSION_KEY]
78
+ return n if n.present?
79
+
80
+ session[ID_NONCE_SESSION_KEY] = SecureRandom.hex
81
+ end
82
+
83
+ def fetch_nonce
84
+ session[ID_NONCE_SESSION_KEY]
85
+ end
86
+
87
+ def valid_nonce?(nonce)
88
+ return false if fetch_nonce.nil?
89
+
90
+ fetch_nonce == nonce
91
+ end
92
+
93
+ def users_match?(claims1, claims2)
94
+ return false if claims1['sub'].nil? || claims2['sub'].nil?
95
+
96
+ claims1['sub'] == claims2['sub']
97
+ end
98
+
99
+ def validate_token(token)
100
+ id_host = PrxAuth::Rails.configuration.id_host
101
+ prx_auth_cert = Rack::PrxAuth::Certificate.new("https://#{id_host}/api/v1/certs")
102
+ auth_validator = Rack::PrxAuth::AuthValidator.new(token, prx_auth_cert, id_host)
103
+ auth_validator.
104
+ claims.
105
+ with_indifferent_access
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,15 @@
1
+ <div class='main'>
2
+ <section>
3
+ <h3>Not authorized for this application.</h3>
4
+
5
+ <p>Message was: <pre><%= @auth_error_message %></pre>
6
+ <% if @auth_error_message == 'invalid_scope' %>
7
+ Did you add a row in the account_applications table on id.prx?
8
+ <% end %>
9
+ </p>
10
+
11
+ <p>
12
+ <a href="<%= new_sessions_path %>">Try logging in again</a>
13
+ </p>
14
+ </section>
15
+ </div>
@@ -0,0 +1,38 @@
1
+ <div style="display:none;">
2
+ <%= form_for(:sessions, :url => PrxAuth::Rails::Engine.routes.url_helpers.sessions_path) do |f| %>
3
+ <%= hidden_field_tag :access_token, '', id: 'access-token-field' %>
4
+ <%= hidden_field_tag :id_token, '', id: 'id-token-field' %>
5
+ <%= f.submit id: 'sessions-form-submit' %>
6
+ <% end %>
7
+ </div>
8
+
9
+ <script type='application/javascript'>
10
+
11
+ function parseURLFragment() {
12
+ let hashParams = {};
13
+ let e,
14
+ a = /\+/g, // Regex for replacing addition symbol with a space
15
+ r = /([^&;=]+)=?([^&;]*)/g,
16
+ d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
17
+ q = window.location.hash.substring(1);
18
+
19
+ while (e = r.exec(q))
20
+ hashParams[d(e[1])] = d(e[2]);
21
+
22
+ return hashParams;
23
+ }
24
+
25
+ window.addEventListener("load", () => {
26
+ var idToken = document.querySelector("#id-token-field");
27
+ var accessToken = document.querySelector("#access-token-field");
28
+ var submit = document.querySelector("input#sessions-form-submit[type=submit]");
29
+
30
+ var hashParams = parseURLFragment();
31
+
32
+ accessToken.value = hashParams['access_token'];
33
+ idToken.value = hashParams['id_token'];
34
+
35
+ submit.click();
36
+ });
37
+
38
+ </script>
@@ -0,0 +1,7 @@
1
+ PrxAuth::Rails::Engine.routes.draw do
2
+ scope module: 'prx_auth/rails' do
3
+ resource 'sessions', except: :index, :defaults => { :format => 'html' } do
4
+ get 'auth_error', to: 'sessions#auth_error'
5
+ end
6
+ end
7
+ end
@@ -1,6 +1,7 @@
1
1
  require "prx_auth/rails/version"
2
2
  require "prx_auth/rails/configuration"
3
3
  require "prx_auth/rails/railtie" if defined?(Rails)
4
+ require "prx_auth/rails/engine" if defined?(Rails)
4
5
 
5
6
  module PrxAuth
6
7
  module Rails
@@ -1,17 +1,28 @@
1
1
  class PrxAuth::Rails::Configuration
2
- attr_accessor :install_middleware, :namespace
2
+ attr_accessor :install_middleware,
3
+ :namespace,
4
+ :prx_client_id,
5
+ :id_host
6
+
3
7
 
4
8
  def initialize
5
9
  @install_middleware = true
6
10
  if defined?(::Rails)
7
11
  klass = ::Rails.application.class
8
- klass_name = if klass.parent_name.present?
9
- klass.parent_name
12
+ parent_name = if ::Rails::VERSION::MAJOR >= 6
13
+ klass.module_parent_name
14
+ else
15
+ klass.parent_name
16
+ end
17
+ klass_name = if parent_name.present?
18
+ parent_name
10
19
  else
11
20
  klass.name
12
21
  end
13
22
 
14
23
  @namespace = klass_name.underscore.intern
24
+ @prx_client_id = nil
25
+ @id_host = nil
15
26
  end
16
27
  end
17
- end
28
+ end
@@ -0,0 +1,5 @@
1
+ module PrxAuth
2
+ module Rails
3
+ class Engine < ::Rails::Engine; end
4
+ end
5
+ end
@@ -4,16 +4,41 @@ module PrxAuth
4
4
  module Rails
5
5
  module Controller
6
6
  def prx_auth_token
7
+ rack_auth_token = env_prx_auth_token
8
+ return rack_auth_token if rack_auth_token.present?
9
+
10
+ session['prx.auth'] && Rack::PrxAuth::TokenData.new(session['prx.auth'])
11
+ end
12
+
13
+ def prx_authenticated?
14
+ !!prx_auth_token
15
+ end
16
+
17
+ def authenticate!
18
+ return true if current_user.present?
19
+
20
+ redirect_to PrxAuth::Rails::Engine.routes.url_helpers.new_sessions_path
21
+ end
22
+
23
+ def current_user
24
+ return if prx_auth_token.nil?
25
+
26
+ PrxAuth::Rails::Token.new(prx_auth_token)
27
+ end
28
+
29
+ def sign_in_user(token)
30
+ session['prx.auth'] = token
31
+ end
32
+
33
+ private
34
+
35
+ def env_prx_auth_token
7
36
  if !defined? @_prx_auth_token
8
37
  @_prx_auth_token = request.env['prx.auth'] && PrxAuth::Rails::Token.new(request.env['prx.auth'])
9
38
  else
10
39
  @_prx_auth_token
11
40
  end
12
41
  end
13
-
14
- def prx_authenticated?
15
- !!prx_auth_token
16
- end
17
42
  end
18
43
  end
19
44
  end
@@ -28,4 +28,8 @@ class PrxAuth::Rails::Token
28
28
  def user_id
29
29
  @token_data.user_id
30
30
  end
31
- end
31
+
32
+ def authorized_account_ids(scope)
33
+ @token_data.authorized_account_ids(scope)
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  module PrxAuth
2
2
  module Rails
3
- VERSION = "1.2.0"
3
+ VERSION = "1.3.0"
4
4
  end
5
5
  end
@@ -29,7 +29,9 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'coveralls', '~> 0'
30
30
  spec.add_development_dependency 'guard'
31
31
  spec.add_development_dependency 'guard-minitest'
32
- spec.add_development_dependency 'rails'
32
+ spec.add_development_dependency "rails", "~> 6.1.0"
33
+ spec.add_development_dependency 'pry'
34
+ spec.add_development_dependency 'sqlite3'
33
35
 
34
36
 
35
37
 
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative "config/application"
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,2 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../stylesheets .css
File without changes
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Channel < ActionCable::Channel::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Connection < ActionCable::Connection::Base
3
+ end
4
+ end