linked_rails-auth 0.0.1

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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +28 -0
  4. data/Rakefile +34 -0
  5. data/app/actions/linked_rails/auth/access_token_action_list.rb +16 -0
  6. data/app/actions/linked_rails/auth/confirmation_action_list.rb +17 -0
  7. data/app/actions/linked_rails/auth/otp_attempt_action_list.rb +13 -0
  8. data/app/actions/linked_rails/auth/otp_secret_action_list.rb +31 -0
  9. data/app/actions/linked_rails/auth/password_action_list.rb +25 -0
  10. data/app/actions/linked_rails/auth/registration_action_list.rb +15 -0
  11. data/app/actions/linked_rails/auth/session_action_list.rb +22 -0
  12. data/app/actions/linked_rails/auth/unlock_action_list.rb +17 -0
  13. data/app/controllers/linked_rails/auth/access_tokens_controller.rb +131 -0
  14. data/app/controllers/linked_rails/auth/confirmations_controller.rb +87 -0
  15. data/app/controllers/linked_rails/auth/otp_attempts_controller.rb +21 -0
  16. data/app/controllers/linked_rails/auth/otp_secrets_controller.rb +40 -0
  17. data/app/controllers/linked_rails/auth/passwords_controller.rb +63 -0
  18. data/app/controllers/linked_rails/auth/registrations_controller.rb +33 -0
  19. data/app/controllers/linked_rails/auth/sessions_controller.rb +55 -0
  20. data/app/controllers/linked_rails/auth/unlocks_controller.rb +44 -0
  21. data/app/forms/linked_rails/auth/access_token_form.rb +20 -0
  22. data/app/forms/linked_rails/auth/confirmation_form.rb +9 -0
  23. data/app/forms/linked_rails/auth/otp_attempt_form.rb +9 -0
  24. data/app/forms/linked_rails/auth/otp_secret_form.rb +12 -0
  25. data/app/forms/linked_rails/auth/password_form.rb +21 -0
  26. data/app/forms/linked_rails/auth/registration_form.rb +21 -0
  27. data/app/forms/linked_rails/auth/session_form.rb +13 -0
  28. data/app/forms/linked_rails/auth/unlock_form.rb +9 -0
  29. data/app/helpers/linked_rails/auth/otp_helper.rb +30 -0
  30. data/app/models/linked_rails/auth/access_token.rb +40 -0
  31. data/app/models/linked_rails/auth/confirmation.rb +70 -0
  32. data/app/models/linked_rails/auth/guest_user.rb +29 -0
  33. data/app/models/linked_rails/auth/otp_attempt.rb +41 -0
  34. data/app/models/linked_rails/auth/otp_base.rb +57 -0
  35. data/app/models/linked_rails/auth/otp_secret.rb +91 -0
  36. data/app/models/linked_rails/auth/password.rb +47 -0
  37. data/app/models/linked_rails/auth/registration.rb +39 -0
  38. data/app/models/linked_rails/auth/session.rb +46 -0
  39. data/app/models/linked_rails/auth/unlock.rb +59 -0
  40. data/app/policies/linked_rails/auth/access_token_policy.rb +17 -0
  41. data/app/policies/linked_rails/auth/confirmation_policy.rb +17 -0
  42. data/app/policies/linked_rails/auth/otp_attempt_policy.rb +17 -0
  43. data/app/policies/linked_rails/auth/otp_secret_policy.rb +37 -0
  44. data/app/policies/linked_rails/auth/password_policy.rb +23 -0
  45. data/app/policies/linked_rails/auth/registration_policy.rb +13 -0
  46. data/app/policies/linked_rails/auth/session_policy.rb +17 -0
  47. data/app/policies/linked_rails/auth/unlock_policy.rb +21 -0
  48. data/app/serializers/linked_rails/auth/access_token_serializer.rb +11 -0
  49. data/app/serializers/linked_rails/auth/confirmation_serializer.rb +10 -0
  50. data/app/serializers/linked_rails/auth/otp_attempt_serializer.rb +12 -0
  51. data/app/serializers/linked_rails/auth/otp_secret_serializer.rb +14 -0
  52. data/app/serializers/linked_rails/auth/password_serializer.rb +18 -0
  53. data/app/serializers/linked_rails/auth/registration_serializer.rb +12 -0
  54. data/app/serializers/linked_rails/auth/session_serializer.rb +10 -0
  55. data/app/serializers/linked_rails/auth/unlock_serializer.rb +9 -0
  56. data/config/routes.rb +4 -0
  57. data/lib/generators/linked_rails/auth/install_generator.rb +155 -0
  58. data/lib/generators/linked_rails/auth/templates/README +2 -0
  59. data/lib/generators/linked_rails/auth/templates/doorkeeper_jwt_initializer.rb +52 -0
  60. data/lib/generators/linked_rails/auth/templates/locales.yml +39 -0
  61. data/lib/generators/linked_rails/auth/templates/migration.rb.erb +20 -0
  62. data/lib/linked_rails/auth/auth_helper.rb +101 -0
  63. data/lib/linked_rails/auth/controller/error_handling.rb +17 -0
  64. data/lib/linked_rails/auth/controller.rb +16 -0
  65. data/lib/linked_rails/auth/engine.rb +8 -0
  66. data/lib/linked_rails/auth/errors/account_locked.rb +10 -0
  67. data/lib/linked_rails/auth/errors/expired.rb +10 -0
  68. data/lib/linked_rails/auth/errors/unauthorized.rb +10 -0
  69. data/lib/linked_rails/auth/errors/unknown_email.rb +14 -0
  70. data/lib/linked_rails/auth/errors/wrong_password.rb +14 -0
  71. data/lib/linked_rails/auth/errors.rb +7 -0
  72. data/lib/linked_rails/auth/routes.rb +86 -0
  73. data/lib/linked_rails/auth/version.rb +7 -0
  74. data/lib/linked_rails/auth.rb +46 -0
  75. data/lib/tasks/linked_rails/auth_tasks.rake +6 -0
  76. metadata +257 -0
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class AccessTokenPolicy < LinkedRails.policy_parent_class
6
+ permit_attributes %i[email password redirect_url]
7
+
8
+ def create?
9
+ true
10
+ end
11
+
12
+ def show?
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class ConfirmationPolicy < LinkedRails.policy_parent_class
6
+ permit_attributes %i[email]
7
+
8
+ def create?
9
+ true
10
+ end
11
+
12
+ def show?
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class OtpAttemptPolicy < LinkedRails.policy_parent_class
6
+ permit_attributes %i[otp_attempt]
7
+
8
+ def show?
9
+ user_context.guest?
10
+ end
11
+
12
+ def create?
13
+ user_context.guest? && record.active?
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class OtpSecretPolicy < LinkedRails.policy_parent_class
6
+ permit_attributes %i[otp_attempt]
7
+
8
+ def show?
9
+ user_context.guest? || current_user? || administrate_otp?
10
+ end
11
+
12
+ def create?
13
+ return forbid_with_message(I18n.t('messages.otp_secrets.already_exists')) if user_context.otp_active?
14
+
15
+ user_context.guest? || current_user?
16
+ end
17
+
18
+ def destroy?
19
+ raise(ActiveRecord::RecordNotFound) unless administrate_otp? || current_user?
20
+
21
+ return forbid_with_message(I18n.t('messages.otp_secrets.not_activated')) unless record.active?
22
+
23
+ current_user? || administrate_otp?
24
+ end
25
+
26
+ private
27
+
28
+ def current_user?
29
+ record.owner_id == user_context.id
30
+ end
31
+
32
+ def administrate_otp?
33
+ false
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class PasswordPolicy < LinkedRails.policy_parent_class
6
+ permit_attributes %i[password password_confirmation reset_password_token],
7
+ has_properties: {reset_password_token: true}
8
+ permit_attributes %i[email], has_properties: {reset_password_token: false}
9
+
10
+ def create?
11
+ true
12
+ end
13
+
14
+ def update?
15
+ true
16
+ end
17
+
18
+ def show?
19
+ true
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class RegistrationPolicy < Pundit::PolicyFinder.new(LinkedRails.user_class).policy || LinkedRails.policy_parent_class
6
+ permit_attributes %i[email password password_confirmation redirect_url]
7
+
8
+ def create?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class SessionPolicy < LinkedRails.policy_parent_class
6
+ permit_attributes %i[email redirect_url]
7
+
8
+ def create?
9
+ true
10
+ end
11
+
12
+ def show?
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class UnlockPolicy < LinkedRails.policy_parent_class
6
+ permit_attributes %i[email]
7
+
8
+ def create?
9
+ true
10
+ end
11
+
12
+ def update?
13
+ true
14
+ end
15
+
16
+ def show?
17
+ true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class AccessTokenSerializer < LinkedRails.serializer_parent_class
6
+ attribute :email, predicate: Vocab.schema.email, datatype: RDF::XSD[:string]
7
+ attribute :password, predicate: Vocab.ontola[:password], datatype: RDF::XSD[:string]
8
+ attribute :redirect_url, predicate: Vocab.ontola[:redirectUrl], datatype: RDF::XSD[:string]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class ConfirmationSerializer < LinkedRails.serializer_parent_class
6
+ attribute :email, predicate: Vocab.schema.email, datatype: RDF::XSD[:string]
7
+ attribute :redirect_url, predicate: Vocab.ontola[:redirectUrl]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class OtpAttemptSerializer < LinkedRails.serializer_parent_class
6
+ attribute :otp_attempt,
7
+ predicate: LinkedRails.app_ns[:otp],
8
+ datatype: RDF::XSD[:integer],
9
+ if: method(:never)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class OtpSecretSerializer < LinkedRails.serializer_parent_class
6
+ attribute :otp_attempt,
7
+ predicate: LinkedRails.app_ns[:otp],
8
+ datatype: RDF::XSD[:integer],
9
+ if: method(:never)
10
+ attribute :active, predicate: LinkedRails.app_ns[:otpActive]
11
+ has_one :image, predicate: Vocab.schema.image
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class PasswordSerializer < LinkedRails.serializer_parent_class
6
+ attribute :email, predicate: Vocab.schema.email, datatype: RDF::XSD[:string]
7
+ attribute :password,
8
+ predicate: Vocab.ontola[:password],
9
+ datatype: Vocab.ontola['datatype/password'],
10
+ if: method(:never)
11
+ attribute :password_confirmation,
12
+ predicate: Vocab.ontola[:passwordConfirmation],
13
+ datatype: Vocab.ontola['datatype/password'],
14
+ if: method(:never)
15
+ attribute :reset_password_token, predicate: Vocab.ontola[:resetPasswordToken], datatype: RDF::XSD[:string]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class RegistrationSerializer < RDF::Serializers.serializer_for(LinkedRails.user_class) || LinkedRails.serializer_parent_class
6
+ attribute :email, predicate: Vocab.schema.email, datatype: RDF::XSD[:string]
7
+ attribute :password, predicate: Vocab.ontola[:password], datatype: RDF::XSD[:string]
8
+ attribute :password_confirmation, predicate: Vocab.ontola[:passwordConfirmation], datatype: RDF::XSD[:string]
9
+ attribute :redirect_url, predicate: Vocab.ontola[:redirectUrl], datatype: RDF::XSD[:string]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class SessionSerializer < LinkedRails.serializer_parent_class
6
+ attribute :email, predicate: Vocab.schema.email, datatype: RDF::XSD[:string]
7
+ attribute :redirect_url, predicate: Vocab.ontola[:redirectUrl], datatype: RDF::XSD[:string]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Auth
5
+ class UnlockSerializer < LinkedRails.serializer_parent_class
6
+ attribute :email, predicate: Vocab.schema.email, datatype: RDF::XSD[:string]
7
+ end
8
+ end
9
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.routes.draw do
4
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+
6
+ module LinkedRails
7
+ module Auth
8
+ class InstallGenerator < ::Rails::Generators::Base
9
+ include ::Rails::Generators::Migration
10
+ source_root File.expand_path("templates", __dir__)
11
+ desc "Installs LinkedRails Auth."
12
+
13
+ def install
14
+ template "doorkeeper_jwt_initializer.rb", "config/initializers/doorkeeper_jwt.rb"
15
+ template "locales.yml", "config/locales/linked_rails_auth.en.yml"
16
+ route "use_linked_rails_auth"
17
+
18
+ migration_template(
19
+ "migration.rb.erb",
20
+ "db/migrate/install_linked_rails_auth.rb",
21
+ migration_version: migration_version
22
+ )
23
+ update_user_model
24
+ insert_doorkeeper
25
+ create_doorkeeper_app
26
+
27
+ readme "README"
28
+ end
29
+
30
+ private
31
+
32
+ def create_doorkeeper_app
33
+ if Doorkeeper::Application.any?
34
+ Rails.logger.info('Skipping Doorkeeper app creation, already exists')
35
+
36
+ return
37
+ end
38
+ client_id = ask('Enter the client_id of your doorkeeper app', default: 'client_id')
39
+ client_secret = ask('Enter the client_secret of your doorkeeper app', default: 'client_secret')
40
+ client_token = ask('Enter the client_token of your doorkeeper app. Leave empty to create new.')
41
+
42
+ libro_app = Doorkeeper::Application.find_or_initialize_by(uid: client_id) do |app|
43
+ app.name = 'Libro'
44
+ app.redirect_uri = 'http://example.com/'
45
+ app.scopes = 'guest user'
46
+ app.secret = client_secret
47
+ end
48
+ libro_app.save!(validate: false)
49
+ # rubocop:disable Rails/SkipsModelValidations
50
+ libro_app.update_columns(uid: client_id, secret: client_secret)
51
+ # rubocop:enable Rails/SkipsModelValidations
52
+
53
+ ActiveRecord::Base.connection.try(:reset_pk_sequence!, Doorkeeper::Application.table_name)
54
+
55
+ token = Doorkeeper::AccessToken.find_or_create_for(
56
+ application: libro_app,
57
+ scopes: 'service',
58
+ expires_in: 10.years.to_i,
59
+ resource_owner: nil,
60
+ use_refresh_token: true
61
+ )
62
+
63
+ if client_token.present?
64
+ token.update(token: client_token)
65
+ else
66
+ Rails.logger.info("Generated client token: #{token.token}")
67
+ end
68
+ end
69
+
70
+ def insert_doorkeeper
71
+ file = 'config/initializers/doorkeeper.rb'
72
+ data = "api_only\n"\
73
+ "base_controller 'ApplicationController'\n"\
74
+ "base_metal_controller 'ApplicationController'\n"
75
+ inject_into_file file, optimize_indentation(data, 2), after: "orm :active_record\n", verbose: false
76
+ uncomment_lines file, "access_token_generator '::Doorkeeper::JWT'"
77
+ uncomment_lines file, 'use_refresh_token'
78
+
79
+ replace_doorkeeper_line(
80
+ '# default_scopes :public',
81
+ 'default_scopes :guest'
82
+ )
83
+ replace_doorkeeper_line(
84
+ '# optional_scopes :write, :update',
85
+ 'optional_scopes :user'
86
+ )
87
+ replace_doorkeeper_line(
88
+ '# grant_flows %w[authorization_code client_credentials]',
89
+ 'grant_flows %w[client_credentials authorization_code password]'
90
+ )
91
+ replace_doorkeeper_line("resource_owner_authenticator do\n(.*?)end\n", authentication, true)
92
+ end
93
+
94
+ def authentication
95
+ <<-FOO
96
+ resource_owner_authenticator do
97
+ if doorkeeper_token&.acceptable?('user')
98
+ User.find_by(id: doorkeeper_token.resource_owner_id)
99
+ elsif doorkeeper_token&.acceptable?('guest') && doorkeeper_token_payload['user']
100
+ LinkedRails.guest_user_class.new(id: doorkeeper_token.resource_owner_id)
101
+ end
102
+ end
103
+
104
+ resource_owner_from_credentials do
105
+ request.params[:user] = request.params[:access_token] || {}
106
+ request.params[:user][:email] ||= (request.params[:username] || request.params[:email])&.downcase
107
+ request.params[:user][:password] ||= request.params[:token] || request.params[:password]
108
+ request.env['devise.allow_params_authentication'] = true
109
+ user =
110
+ if request.params[:scope] == 'guest'
111
+ LinkedRails.guest_user_class.new
112
+ else
113
+ request.env['warden'].authenticate(scope: :user, store: false)
114
+ end
115
+ raise_login_error(request) if user.blank?
116
+ request.env['warden'].logout
117
+ user
118
+ end
119
+ FOO
120
+ end
121
+
122
+ def inject_controller_include
123
+ sentinel = /LinkedRails::Controller\n/m
124
+ in_root do
125
+ inject_into_file(
126
+ "app/controllers/application_controller.rb",
127
+ optimize_indentation('include LinkedRails::Auth::AuthHelper', 2),
128
+ after: sentinel
129
+ )
130
+ end
131
+ end
132
+
133
+ def migration_version
134
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
135
+ end
136
+
137
+ def replace_doorkeeper_line(old, new, multi_line_sentinel = false)
138
+ sentinel = multi_line_sentinel ? /^(\s*)#{old}/m : /^(\s*)#[[:blank:]]*(.*#{old})/
139
+ gsub_file('config/initializers/doorkeeper.rb', sentinel, new)
140
+ end
141
+
142
+ def update_user_model
143
+ file = 'app/models/user.rb'
144
+ no_guest = "\ndef guest?\n false\nend"
145
+ inject_into_file file, optimize_indentation(no_guest, 2), after: ":recoverable, :rememberable, :validatable\n", verbose: false
146
+ end
147
+
148
+ class << self
149
+ def next_migration_number(dirname)
150
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end