authtown 0.1.0 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b75bcb24f21f87172f654d1d987c6fba611ffdd03b6a4bc65dc5e8aeed65324c
4
- data.tar.gz: 62111ad44b4391e6a2300e38ede4e55638c4239a3d1f71fd74ce5a22613c9a53
3
+ metadata.gz: 1d2619b2daf3aa55e9406234f5ebffcc6120db6b88c11b86f220349e3b1f55fc
4
+ data.tar.gz: 27d16fd12a96f4e4cda4d0c642b780768df4380132c1b93ad1c2de354ff5e58b
5
5
  SHA512:
6
- metadata.gz: f9bd62d8b2e309d9adf91d8751d5d7a8843f7306220052790f98baf97b1c929140fb3f4661ed724df83417cf196a7f4590803f354f7524903e2213de7c59bbdf
7
- data.tar.gz: d34c203ead7a8ae36a0e064cc2c4764d186705d95addc83b1c7d9c3a0ed2896e16e162a2eb80e2784a9ea9a93ece6da075b6466e21266a95fc1e4fb2abf9ea89
6
+ metadata.gz: 0afe6909ccf879aba4e28484f3f83b035613e80e43940e1a4aba3c0b3dd67f9aae7cc3f55122ce4276eaf21426829e9c515b57b6293a431beb8acd1ab416641d
7
+ data.tar.gz: 37e8e4f7b0c438050eee6321cea152c0f776667262dee23859698c902184b6d8f69fdfd628d2e28e410e1aa40df050bd3ed43e1d0f03dba82fd5e108c3a250b8
data/.rubocop.yml CHANGED
@@ -21,3 +21,6 @@ AllCops:
21
21
  - script/**/*
22
22
  - test/fixtures/**/*
23
23
  - vendor/**/*
24
+
25
+ Metrics/ParameterLists:
26
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -7,7 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- - ...
10
+ ## [0.3.0] - 2024-04-07
11
+
12
+ - Refactor how Bridgetown's view layer is integrated with Rodauth, improve performance
13
+
14
+ ## [0.2.0] - 2024-04-03
15
+
16
+ - Integration with new bridgetown_sequel gem tested
11
17
 
12
18
  ## [0.1.0] - YYYY-MM-DD
13
19
 
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/BlockLength
4
+ # @param config [Bridgetown::Configuration::ConfigurationDSL]
5
+ # @param rodauth_config [Proc]
6
+ # @param account_landing_page [String] relative URL to redirect to upon login / create account
7
+ # @param user_class_resolver [Proc] return the class of the user model
8
+ # @param user_name_field [String] save a name when creating a new user, or set to nil to skip
9
+ Bridgetown.initializer :authtown do |
10
+ config,
11
+ rodauth_config: nil,
12
+ account_landing_page: "/account/profile",
13
+ user_class_resolver: -> { User },
14
+ user_name_field: :first_name
15
+ |
16
+
17
+ config.authtown ||= {}
18
+ config.authtown.user_class_resolver ||= user_class_resolver
19
+
20
+ config.only :server do
21
+ require "authtown/routes/rodauth"
22
+
23
+ # @param app [Class<Roda>]
24
+ config.roda do |app|
25
+ app.prepend Authtown::ViewMixin
26
+
27
+ secret = ENV.fetch("RODA_SECRET_KEY")
28
+ app.plugin(:sessions, secret:)
29
+
30
+ app.plugin :rodauth, render: false do
31
+ enable :login, :logout, :create_account, :remember, :reset_password, :internal_request
32
+ hmac_secret secret
33
+
34
+ base_url config.url
35
+
36
+ prefix "/auth"
37
+
38
+ login_redirect account_landing_page
39
+ create_account_redirect account_landing_page
40
+ logout_redirect "/"
41
+
42
+ set_deadline_values? true # for remember, etc.
43
+ # TODO: why isn't this working? might be a schema issue with old AR:
44
+ remember_deadline_interval days: 30
45
+ extend_remember_deadline? true
46
+
47
+ login_label "Email Address"
48
+ login_button "Sign In"
49
+
50
+ accounts_table :users
51
+ account_password_hash_column :password_hash
52
+
53
+ require_login_confirmation? false
54
+ require_password_confirmation? false
55
+
56
+ # Require passwords to have at least 8 characters
57
+ password_minimum_length 8
58
+
59
+ # Don't allow passwords to be too long, to prevent long password DoS attacks
60
+ password_maximum_length 64
61
+
62
+ reset_password_email_sent_redirect "/account/reset-link-sent"
63
+ reset_password_autologin? true
64
+ reset_password_redirect account_landing_page
65
+
66
+ before_create_account do
67
+ # Ensure timestamps get saved
68
+ account[:created_at] = account[:updated_at] = Time.now
69
+ next unless user_name_field
70
+
71
+ # Save name details
72
+ account[user_name_field] = param(user_name_field) if param(user_name_field)
73
+ end
74
+
75
+ after_login do
76
+ remember_login
77
+ end
78
+
79
+ instance_exec(&rodauth_config) if rodauth_config
80
+ end
81
+ end
82
+ end
83
+
84
+ # Register your builder:
85
+ config.builder Authtown::Builder
86
+ end
87
+ # rubocop:enable Metrics/BlockLength
@@ -9,7 +9,12 @@ module Authtown
9
9
  rodauth.load_memory
10
10
 
11
11
  init_current_user
12
- Lifeform::Form.rodauth = rodauth
12
+
13
+ # @example hook usage:
14
+ # hook :authtown, :initialized do |rodauth|
15
+ # Lifeform::Form.rodauth = rodauth
16
+ # end
17
+ Bridgetown::Hooks.trigger(:authtown, :initialized, rodauth)
13
18
 
14
19
  r.on "auth" do
15
20
  r.rodauth
@@ -19,8 +24,8 @@ module Authtown
19
24
  def init_current_user
20
25
  Authtown::Current.user =
21
26
  if rodauth.logged_in?
22
- account_id = rodauth.account_from_session[:id]
23
- User.find(account_id)
27
+ # load existing account hash into Model:
28
+ bridgetown_site.config.authtown.user_class_resolver.().(rodauth.account_from_session)
24
29
  end
25
30
  end
26
31
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Authtown
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -3,44 +3,64 @@
3
3
  module Authtown::ViewMixin
4
4
  def locals = @_route_locals
5
5
 
6
- # TODO: this is super hacky
6
+ def current_user = Authtown::Current.user
7
+
8
+ ### Compatibility layer to translate Rodauth view calls to Bridgetown Routing
9
+
10
+ # Return a single hash combining the template and opts arguments.
11
+ def parse_template_opts(template, opts)
12
+ opts = opts.to_h
13
+ if template.is_a?(Hash)
14
+ opts.merge!(template)
15
+ else
16
+ opts[:template] = template
17
+ opts
18
+ end
19
+ end
20
+
21
+ def find_template(opts) = opts
22
+
23
+ def template_path(opts) = opts[:template]
24
+
25
+ def login_failed_reset_password_request_form
26
+ # part of reset_password internals…no-op since we're rendering our own forms
27
+ end
28
+
7
29
  def view(*args, view_class: Bridgetown::ERBView, **kwargs) # rubocop:disable Metrics
8
30
  kwargs = args.first if args.first.is_a?(Hash)
9
31
 
10
- # If we're farming out to another view, let's go!
11
- unless kwargs.empty?
12
- response["X-Bridgetown-SSR"] = "1"
32
+ return super if kwargs.empty? # regular Bridgetown behavior
13
33
 
14
- # UGH, hate special casing this
15
- if kwargs.dig(:locals, :rodauth)&.prefix
16
- kwargs[:template] =
17
- "#{kwargs.dig(:locals, :rodauth).prefix.delete_prefix("/")}/#{kwargs[:template]}"
18
- end
34
+ unless kwargs.dig(:locals, :rodauth)
35
+ raise "The `view' method with keyword arguments should only be used by Rodauth internally. " \
36
+ "It is not supported by Bridgetown's view layer."
37
+ end
19
38
 
20
- Bridgetown::Routes::Manifest.generate_manifest(bridgetown_site).each do |route|
21
- file, localized_file_slugs = route
39
+ if kwargs.dig(:locals, :rodauth)&.prefix
40
+ kwargs[:template] =
41
+ "#{kwargs.dig(:locals, :rodauth).prefix.delete_prefix("/")}/#{kwargs[:template]}"
42
+ end
22
43
 
23
- file_slug = localized_file_slugs.first
44
+ # TODO: this should really be some sort of exposed method from the routes plugin
45
+ response["X-Bridgetown-SSR"] = "1"
24
46
 
25
- next unless file_slug == kwargs[:template]
47
+ Bridgetown::Routes::Manifest.generate_manifest(bridgetown_site).each do |route|
48
+ file, localized_file_slugs = route
26
49
 
27
- Bridgetown::Routes::CodeBlocks.eval_route_file file, file_slug, self
28
- route_block = Bridgetown::Routes::CodeBlocks.route_block(file_slug)
29
- response.instance_variable_set(
30
- :@_route_file_code, route_block.instance_variable_get(:@_route_file_code)
31
- ) # could be nil
32
- @_route_locals = kwargs[:locals]
33
- return instance_exec(request, &route_block)
34
- end
50
+ file_slug = localized_file_slugs.first
35
51
 
36
- Bridgetown.logger.warn("Template not found: #{kwargs[:template]}")
37
- return nil # couldn't find template, 404
52
+ next unless file_slug == kwargs[:template]
53
+
54
+ Bridgetown::Routes::CodeBlocks.eval_route_file file, file_slug, self
55
+ route_block = Bridgetown::Routes::CodeBlocks.route_block(file_slug)
56
+ response.instance_variable_set(
57
+ :@_route_file_code, route_block.instance_variable_get(:@_route_file_code)
58
+ ) # could be nil
59
+ @_route_locals = kwargs[:locals]
60
+ return instance_exec(request, &route_block)
38
61
  end
39
62
 
40
- response._fake_resource_view(
41
- view_class:, roda_app: self, bridgetown_site:
42
- )
63
+ Bridgetown.logger.warn("Rodauth template not found: #{kwargs[:template]}")
64
+ nil # couldn't find template, 404
43
65
  end
44
-
45
- def current_user = Authtown::Current.user
46
66
  end
data/lib/authtown.rb CHANGED
@@ -5,106 +5,50 @@ require "mail"
5
5
  require "authtown/builder"
6
6
  require "authtown/view_mixin"
7
7
 
8
- # REQUIRES:
9
- # init :"bridgetown-activerecord", sequel_support: :postgres
10
-
11
- # ActiveRecord schema:
8
+ # rubocop:disable Layout/LineLength
9
+ ### Simple migration strategy:
12
10
  #
13
- # class CreateUsers < ActiveRecord::Migration[7.0]
14
- # def change
15
- # create_table :users do |t|
16
- # t.string :email, null: false, index: { unique: true }
17
- # t.string :first_name
18
- # t.string :password_hash, null: false
19
- # t.timestamps
11
+ # Sequel.migration do
12
+ # change do
13
+ # extension :date_arithmetic
14
+
15
+ # create_table(:users) do
16
+ # primary_key :id, type: :Bignum
17
+ # citext :email, null: false
18
+ # constraint :valid_email, email: /^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
19
+ # String :first_name
20
+ # String :password_hash, null: false
21
+ # index :email, unique: true
20
22
  # end
21
- # end
22
- # end
23
- #
24
- # class CreateAccountRememberKeys < ActiveRecord::Migration[7.0]
25
- # def change
26
- # create_table :account_remember_keys do |t|
27
- # t.foreign_key :users, column: :id
28
- # t.string :key, null: false
29
- # t.datetime :deadline, null: false
23
+
24
+ # # Used by the remember me feature
25
+ # create_table(:account_remember_keys) do
26
+ # foreign_key :id, :users, primary_key: true, type: :Bignum
27
+ # String :key, null: false
28
+ # DateTime :deadline, { null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: 30) }
29
+ # end
30
+
31
+ # create_table(:account_password_reset_keys) do
32
+ # foreign_key :id, :users, primary_key: true, type: :Bignum
33
+ # String :key, null: false
34
+ # DateTime :deadline, { null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: 1) }
35
+ # DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
30
36
  # end
31
37
  # end
32
38
  # end
39
+ # rubocop:enable Layout/LineLength
33
40
 
34
- class Authtown::Current < ActiveSupport::CurrentAttributes
35
- # @!parse def self.user = User.new
36
- attribute :user
37
- end
38
-
39
- # rubocop:disable Metrics/BlockLength
40
- # @param config [Bridgetown::Configuration::ConfigurationDSL]
41
- # @param rodauth_config [Proc]
42
- Bridgetown.initializer :authtown do |
43
- config,
44
- rodauth_config: nil,
45
- account_landing_page: "/account/profile"
46
- |
47
-
48
- config.only :server do
49
- require "authtown/routes/rodauth"
50
-
51
- # @param app [Class<Roda>]
52
- config.roda do |app|
53
- app.prepend Authtown::ViewMixin
54
-
55
- secret = ENV.fetch("RODA_SECRET_KEY")
56
- app.plugin(:sessions, secret:)
57
-
58
- app.plugin :rodauth do
59
- enable :login, :logout, :create_account, :remember, :reset_password
60
- hmac_secret secret
61
-
62
- prefix "/auth"
41
+ Thread.attr_accessor :authtown_state
42
+ class Authtown::Current
43
+ class << self
44
+ def thread_state = Thread.current.authtown_state ||= {}
63
45
 
64
- login_redirect account_landing_page
65
- create_account_redirect account_landing_page
66
- logout_redirect "/"
67
-
68
- set_deadline_values? true # for remember, etc.
69
- remember_deadline_interval days: 30 # TODO: why isn't this working?!
70
- extend_remember_deadline? true
71
-
72
- login_label "Email Address"
73
- login_button "Sign In"
74
-
75
- accounts_table :users
76
- account_password_hash_column :password_hash
77
-
78
- require_login_confirmation? false
79
- require_password_confirmation? false
80
-
81
- # Require passwords to have at least 8 characters
82
- password_minimum_length 8
83
-
84
- # Don't allow passwords to be too long, to prevent long password DoS attacks
85
- password_maximum_length 64
86
-
87
- reset_password_email_sent_redirect "/account/reset-link-sent"
88
- reset_password_autologin? true
89
- reset_password_redirect account_landing_page
90
-
91
- before_create_account do
92
- # Make sure timestamps get saved
93
- account[:created_at] = account[:updated_at] = Time.now
94
-
95
- account[:first_name] = param(:first_name)
96
- end
97
-
98
- after_login do
99
- remember_login
100
- end
101
-
102
- instance_exec(&rodauth_config) if rodauth_config
103
- end
46
+ def user=(new_user)
47
+ thread_state[:user] = new_user
104
48
  end
105
- end
106
49
 
107
- # Register your builder:
108
- config.builder Authtown::Builder
50
+ def user = thread_state[:user]
51
+ end
109
52
  end
110
- # rubocop:enable Metrics/BlockLength
53
+
54
+ require "authtown/initializer"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authtown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bridgetown Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-27 00:00:00.000000000 Z
11
+ date: 2024-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bridgetown
@@ -132,6 +132,7 @@ files:
132
132
  - bridgetown.automation.rb
133
133
  - lib/authtown.rb
134
134
  - lib/authtown/builder.rb
135
+ - lib/authtown/initializer.rb
135
136
  - lib/authtown/routes/rodauth.rb
136
137
  - lib/authtown/version.rb
137
138
  - lib/authtown/view_mixin.rb
@@ -154,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
154
155
  - !ruby/object:Gem::Version
155
156
  version: '0'
156
157
  requirements: []
157
- rubygems_version: 3.3.3
158
+ rubygems_version: 3.5.3
158
159
  signing_key:
159
160
  specification_version: 4
160
161
  summary: Rodauth integration for Bridgetown