authtown 0.1.0 → 0.3.0

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: 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