prx_auth-rails 0.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +6 -0
  3. data/Guardfile +8 -0
  4. data/README.md +44 -2
  5. data/Rakefile +17 -0
  6. data/app/controllers/prx_auth/rails/sessions_controller.rb +108 -0
  7. data/app/views/prx_auth/rails/sessions/auth_error.html.erb +15 -0
  8. data/app/views/prx_auth/rails/sessions/show.html.erb +38 -0
  9. data/config/routes.rb +7 -0
  10. data/lib/prx_auth/rails.rb +10 -2
  11. data/lib/prx_auth/rails/configuration.rb +28 -0
  12. data/lib/prx_auth/rails/engine.rb +5 -0
  13. data/lib/prx_auth/rails/ext/controller.rb +32 -1
  14. data/lib/prx_auth/rails/railtie.rb +2 -2
  15. data/lib/prx_auth/rails/token.rb +35 -0
  16. data/lib/prx_auth/rails/version.rb +1 -1
  17. data/prx_auth-rails.gemspec +13 -3
  18. data/test/dummy/Rakefile +6 -0
  19. data/test/dummy/app/assets/config/manifest.js +2 -0
  20. data/test/dummy/app/assets/images/.keep +0 -0
  21. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  22. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  23. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  24. data/test/dummy/app/controllers/application_controller.rb +8 -0
  25. data/test/dummy/app/controllers/concerns/.keep +0 -0
  26. data/test/dummy/app/helpers/application_helper.rb +2 -0
  27. data/test/dummy/app/javascript/packs/application.js +15 -0
  28. data/test/dummy/app/jobs/application_job.rb +7 -0
  29. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  30. data/test/dummy/app/models/application_record.rb +3 -0
  31. data/test/dummy/app/models/concerns/.keep +0 -0
  32. data/test/dummy/app/views/layouts/application.html.erb +15 -0
  33. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  34. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  35. data/test/dummy/bin/rails +5 -0
  36. data/test/dummy/bin/rake +5 -0
  37. data/test/dummy/bin/setup +33 -0
  38. data/test/dummy/bin/spring +10 -0
  39. data/test/dummy/config.ru +6 -0
  40. data/test/dummy/config/application.rb +22 -0
  41. data/test/dummy/config/boot.rb +5 -0
  42. data/test/dummy/config/cable.yml +10 -0
  43. data/test/dummy/config/database.yml +25 -0
  44. data/test/dummy/config/environment.rb +5 -0
  45. data/test/dummy/config/environments/development.rb +76 -0
  46. data/test/dummy/config/environments/production.rb +120 -0
  47. data/test/dummy/config/environments/test.rb +60 -0
  48. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  49. data/test/dummy/config/initializers/assets.rb +12 -0
  50. data/test/dummy/config/initializers/backtrace_silencers.rb +8 -0
  51. data/test/dummy/config/initializers/content_security_policy.rb +28 -0
  52. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  53. data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  54. data/test/dummy/config/initializers/inflections.rb +16 -0
  55. data/test/dummy/config/initializers/mime_types.rb +4 -0
  56. data/test/dummy/config/initializers/permissions_policy.rb +11 -0
  57. data/test/dummy/config/initializers/prx_auth.rb +8 -0
  58. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  59. data/test/dummy/config/locales/en.yml +33 -0
  60. data/test/dummy/config/puma.rb +43 -0
  61. data/test/dummy/config/routes.rb +3 -0
  62. data/test/dummy/config/spring.rb +6 -0
  63. data/test/dummy/config/storage.yml +34 -0
  64. data/test/dummy/lib/assets/.keep +0 -0
  65. data/test/dummy/log/.keep +0 -0
  66. data/test/dummy/public/404.html +67 -0
  67. data/test/dummy/public/422.html +67 -0
  68. data/test/dummy/public/500.html +66 -0
  69. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  70. data/test/dummy/public/apple-touch-icon.png +0 -0
  71. data/test/dummy/public/favicon.ico +0 -0
  72. data/test/dummy/storage/.keep +0 -0
  73. data/test/log/development.log +0 -0
  74. data/test/prx_auth/rails/configuration_test.rb +36 -0
  75. data/test/prx_auth/rails/sessions_controller_test.rb +94 -0
  76. data/test/prx_auth/rails/token_test.rb +45 -0
  77. data/test/test_helper.rb +35 -0
  78. metadata +243 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ac6efcb3433ddd7f49eaa06285689d79320b8318
4
- data.tar.gz: 44c91e2e17b65173218c068abeb642e972123cbd
2
+ SHA256:
3
+ metadata.gz: 3405e4e72d2c39585111bef4fca339a73ce11ec86a2c288f5c82d8934c0620fe
4
+ data.tar.gz: 3450063f4c4fdae2464f755222fafee08c627eda3aa8fee1d267db1474db58de
5
5
  SHA512:
6
- metadata.gz: 542bb85398b96f70a29e1db48f1a7a14b9b1a5a8136dc2284ffcccff2bf10c55fbddef9c8141b4b89ea00a2877b9c30a96e210ba4ac044677fe228ed6bde3873
7
- data.tar.gz: f611e92844200d7b7a23f483054722c474f15e4a8081854678617b1939187f1fcabce14b4c5b4442649141a64b8a3a8537d6bc802f623d6380013ec806415fba
6
+ metadata.gz: edf33f5cf4bc105818bb069554506f0b2d9ef5b8bb18cd1602b2ceb6384991804b5b3268c6bd5c519c08e8503df51c132a9bec045f13995c1d55088194fff489
7
+ data.tar.gz: 4aa62706ee892c2335756e2c3ae61171b77acc69bfe0ec06433449726f8e884cee4af2031e55e418c6780e9aedc8bfb1c78359c072c089389422a2d4aa15c95b
data/.gitignore CHANGED
@@ -14,4 +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
22
+ .ruby-version
23
+ .DS_Store
@@ -0,0 +1,8 @@
1
+ guard :minitest, all_after_pass: true do
2
+ watch(%r{^test/(.*)\/?test_(.*)\.rb})
3
+ watch(%r{^lib/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
4
+ watch(%r{^lib/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
5
+ watch(%r{^lib/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
6
+ watch(%r{^test/.+_test\.rb})
7
+ watch(%r{^test/test_helper\.rb}) { 'test' }
8
+ end
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,7 +15,48 @@ And then execute:
14
15
 
15
16
  ## Usage
16
17
 
17
- That should be it, I think.
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:
21
+
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.
26
+
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.
32
+
33
+ ### Configuration
34
+
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.
41
+
42
+ In your rails app, add a file to config/initializers called
43
+ `prx_auth.rb`:
44
+
45
+ ```ruby
46
+ PrxAuth::Rails.configure do |config|
47
+
48
+ # enables automatic installation of token parser middleware
49
+ config.install_middleware = false # default: true
50
+
51
+ # automatically adds namespace to all scoped queries, e.g. .authorized?(:foo) will be treated
52
+ # as .authorized?(:my_great_ns, :foo). Has no impact on unscoped queries.
53
+ config.namespace = :my_great_ns # default: derived from Rails::Application name.
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>'
58
+ end
59
+ ```
18
60
 
19
61
  ## Contributing
20
62
 
data/Rakefile CHANGED
@@ -1 +1,18 @@
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
+
1
8
  require "bundler/gem_tasks"
9
+ require 'rake'
10
+ require "rake/testtask"
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = false
16
+ end
17
+
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,10 +1,18 @@
1
1
  require "prx_auth/rails/version"
2
+ require "prx_auth/rails/configuration"
2
3
  require "prx_auth/rails/railtie" if defined?(Rails)
4
+ require "prx_auth/rails/engine" if defined?(Rails)
5
+
3
6
  module PrxAuth
4
7
  module Rails
5
8
  class << self
6
- attr_accessor :middleware
9
+ attr_accessor :configuration
10
+
11
+ def configure
12
+ yield configuration
13
+ end
7
14
  end
8
- self.middleware = true
15
+
16
+ self.configuration = Configuration.new
9
17
  end
10
18
  end
@@ -0,0 +1,28 @@
1
+ class PrxAuth::Rails::Configuration
2
+ attr_accessor :install_middleware,
3
+ :namespace,
4
+ :prx_client_id,
5
+ :id_host
6
+
7
+
8
+ def initialize
9
+ @install_middleware = true
10
+ if defined?(::Rails)
11
+ klass = ::Rails.application.class
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
19
+ else
20
+ klass.name
21
+ end
22
+
23
+ @namespace = klass_name.underscore.intern
24
+ @prx_client_id = nil
25
+ @id_host = nil
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ module PrxAuth
2
+ module Rails
3
+ class Engine < ::Rails::Engine; end
4
+ end
5
+ end
@@ -1,13 +1,44 @@
1
+ require 'prx_auth/rails/token'
2
+
1
3
  module PrxAuth
2
4
  module Rails
3
5
  module Controller
4
6
  def prx_auth_token
5
- request.env['prx.auth']
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'])
6
11
  end
7
12
 
8
13
  def prx_authenticated?
9
14
  !!prx_auth_token
10
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
36
+ if !defined? @_prx_auth_token
37
+ @_prx_auth_token = request.env['prx.auth'] && PrxAuth::Rails::Token.new(request.env['prx.auth'])
38
+ else
39
+ @_prx_auth_token
40
+ end
41
+ end
11
42
  end
12
43
  end
13
44
  end
@@ -9,8 +9,8 @@ module PrxAuth::Rails
9
9
  end
10
10
 
11
11
  initializer 'prx_auth.insert_middleware' do |app|
12
- if PrxAuth::Rails.middleware
13
- app.config.middleware.insert ActionDispatch::ParamsParser, Rack::PrxAuth
12
+ if PrxAuth::Rails.configuration.install_middleware
13
+ app.config.middleware.insert_after Rack::Head, Rack::PrxAuth
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,35 @@
1
+ require 'rack/prx_auth'
2
+
3
+ class PrxAuth::Rails::Token
4
+ def initialize(token_data)
5
+ @token_data = token_data
6
+ @namespace = PrxAuth::Rails.configuration.namespace
7
+ end
8
+
9
+ def authorized?(resource, namespace=nil, scope=nil)
10
+ namespace, scope = @namespace, namespace if scope.nil? && !namespace.nil?
11
+ @token_data.authorized?(resource, namespace, scope)
12
+ end
13
+
14
+ def globally_authorized?(namespace, scope=nil)
15
+ namespace, scope = @namespace, namespace if scope.nil?
16
+ @token_data.globally_authorized?(namespace, scope)
17
+ end
18
+
19
+ def resources(namespace=nil, scope=nil)
20
+ namespace, scope = @namespace, namespace if scope.nil? && !namespace.nil?
21
+ @token_data.resources(namespace, scope)
22
+ end
23
+
24
+ def scopes
25
+ @token_data.scopes
26
+ end
27
+
28
+ def user_id
29
+ @token_data.user_id
30
+ 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 = "0.1.0"
3
+ VERSION = "1.3.0"
4
4
  end
5
5
  end