forget-passwords 0.2.13 → 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: 4db3ce83ab06fe2e7ba9fbf453370f1182e8fc6e7f888e1edd2851e1eb4dfe1a
4
- data.tar.gz: '049d12c72b928352235e422c2c737a7c7827d5284a68054ea09f50c7d3a962d0'
3
+ metadata.gz: dacb56a002005cc14bb46809690604cccd905eee5c5fc8c5efb78557629611e6
4
+ data.tar.gz: 704b521acb64f33a934c8768e8bc1496a86ae55d27a0598f4172a7ddc6c8d0e0
5
5
  SHA512:
6
- metadata.gz: 2d3bc2353a0b41009cb7b537a67bc58210670095949afbe21ada7221912c3d6a91c580240d56803b159a32a73c3d4e1c756f7b34c288879be61f842740c6bf20
7
- data.tar.gz: 3c7771eb7709deeeca3c7617be519168d6387c619b5e0de0a61243905bb8a4f5a9a2667ab6a63ada192c962a426301e2d83400a5370dc212aaa7e2ee1f69eefb
6
+ metadata.gz: 1e79d40cbf1151f1a6fb056e17e90f022f65cf575e9eac4a2d1931b4c9c32723924a7e27f83ec893acbafa46710c6da68de3e2791005066ba74eea2a880f0f39
7
+ data.tar.gz: c306b865ed182f590338d26a7a41a165c38626519dbfc627b77fac4f8a271f7a9d83274a10ec8ae7629a9992407fe92524a4b2a9eb5e4c74ebf1b3b5907e67de
data/README.md CHANGED
@@ -578,9 +578,11 @@ favourite flavour of templating engine.
578
578
 
579
579
  ### Reconcile with OAuth
580
580
 
581
- The authentication cookie used by Forget Passwords bears a striking
582
- resemblance to an [OAuth](https://oauth.net/) bearer token, such that
583
- could actually _be_ (at least a proxy for) an OAuth bearer token.
581
+ Let's face it: this thing is 98% of what [OAuth](https://oauth.net/)
582
+ does: it trades one token for another over a more-or-less secure side
583
+ channel. It could be made a heck of a lot simpler by just…wrapping
584
+ OAuth.
585
+
584
586
  Indeed, bearer tokens would make for an _excellent_ cleavage plane for
585
587
  _segmented_ authentication: Method X to bearer token, then bearer
586
588
  token to `REMOTE_USER`. This means we could have multiple
@@ -4,6 +4,7 @@ require 'xml-mixup'
4
4
  require 'http-negotiate'
5
5
  require 'forget-passwords/types'
6
6
  require 'uri'
7
+ require 'time'
7
8
 
8
9
  module ForgetPasswords
9
10
 
@@ -18,6 +19,8 @@ module ForgetPasswords
18
19
  DEFAULT_PATH = (
19
20
  Pathname(__FILE__).parent + '../../content').expand_path.freeze
20
21
 
22
+ # this is a default document root subpath
23
+ DEFAULT_DOCROOT = '.forgetpw'.freeze
21
24
 
22
25
  # Normalize the input to symbol `:like_this`.
23
26
  #
@@ -57,21 +60,67 @@ module ForgetPasswords
57
60
  @path = Pathname(path).expand_path
58
61
  @base = base
59
62
  @transform = transform
60
- @mapping = mapping.map do |k, v|
61
- name = normalize k
62
- template = v.is_a?(ForgetPasswords::Template) ? v :
63
- ForgetPasswords::Template.new(self, k, @path + v)
64
- [name, template]
65
- end.to_h
63
+ @mapping = mapping
64
+ @templates = {
65
+ @path => mapping.map do |k, v|
66
+ name = normalize k
67
+ template = v.is_a?(ForgetPasswords::Template) ? v :
68
+ ForgetPasswords::Template.new(self, k, @path + v)
69
+ [name, template]
70
+ end.to_h
71
+ }
66
72
  end
67
73
 
68
- def [] key
69
- @mapping[normalize key]
74
+ # Fetch the appropriate template, optionally relative to a given root.
75
+ #
76
+ # @param key [Symbol, #to_sym] the template key.
77
+ # @param root [nil, String] an optional document root.
78
+ #
79
+ # @return [nil, ForgetPasswords::Template] a template
80
+ #
81
+ def [] key, root = nil
82
+ key = normalize key
83
+ # bail early if we don't know the key
84
+ return unless @mapping[key]
85
+
86
+ # obtain optional root
87
+ root = if root
88
+ r = root.respond_to?(:env) ? root.env['DOCUMENT_ROOT'] : root
89
+ r = (Pathname(r) + DEFAULT_DOCROOT).expand_path
90
+ r.readable? ? r : nil
91
+ end
92
+
93
+ if root
94
+ # get the full file path
95
+ fp = root + @mapping[key]
96
+ if fp.readable?
97
+ mt = fp.mtime
98
+ rootmap = @templates[root] ||= {}
99
+ template = rootmap[key]
100
+
101
+ # congratulations, you found it
102
+ return template if template and mt <= template.modified
103
+
104
+ # XXX this could explode obvs
105
+ begin
106
+ template = ForgetPasswords::Template.new(self, key, fp, mt)
107
+ rootmap[key] = template
108
+ return template
109
+ rescue
110
+ # XXX duhh what do we do here
111
+ nil
112
+ end
113
+ end
114
+ end
115
+
116
+ # otherwise just return the default
117
+ @templates[@path][key]
70
118
  end
71
119
 
72
120
  def []= key, path
73
121
  name = normalize key
74
- @mapping[name] = path.is_a?(ForgetPasswords::Template) ? path :
122
+ # XXX do something less dumb here
123
+ @templates[@path][name] = path.is_a?(ForgetPasswords::Template) ? path :
75
124
  ForgetPasswords::Template.new(self, key, @path + path)
76
125
  end
77
126
 
@@ -132,12 +181,13 @@ module ForgetPasswords
132
181
 
133
182
  public
134
183
 
135
- attr_reader :name, :doc, :mapper
184
+ attr_reader :name, :doc, :mapper, :modified
136
185
 
137
- def initialize mapper, name, content
186
+ def initialize mapper, name, content, modified = Time.now
138
187
  # boring members
139
- @mapper = mapper
140
- @name = name
188
+ @mapper = mapper
189
+ @name = name
190
+ @modified = modified
141
191
 
142
192
  # resolve content
143
193
  @doc = case content
@@ -253,6 +303,7 @@ module ForgetPasswords
253
303
  # @return [Rack::Response] the response object, updated in place
254
304
  #
255
305
  def populate resp, headers = {}, vars = {}, base: nil
306
+
256
307
  if (body, type = serialize(
257
308
  process(vars: vars, base: base), headers, full: true))
258
309
  #resp.length = body.bytesize # not sure if necessary
@@ -1,3 +1,3 @@
1
1
  module ForgetPasswords
2
- VERSION = '0.2.13'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -311,7 +311,7 @@ module ForgetPasswords
311
311
  }
312
312
 
313
313
  # grab the template since we'll use it
314
- template = @templates[:email]
314
+ template = @templates[:email, req]
315
315
 
316
316
  # process the templates
317
317
  doc = template.process vars: vars
@@ -336,7 +336,7 @@ module ForgetPasswords
336
336
  uri = req_uri req
337
337
  resp = Rack::Response.new
338
338
  resp.status = status
339
- @templates[key].populate resp, req, vars, base: uri
339
+ @templates[key, req].populate resp, req, vars, base: uri
340
340
  resp.set_header "Variable-#{@vars[:type]}", resp.content_type
341
341
  raise ForgetPasswords::ErrorResponse, resp
342
342
  end
@@ -347,7 +347,7 @@ module ForgetPasswords
347
347
  uri = req_uri req
348
348
  resp = Rack::Response.new
349
349
  resp.status = 401
350
- @templates[:default_401].populate resp, req, {
350
+ @templates[:default_401, req].populate resp, req, {
351
351
  FORWARD: req_uri(req).to_s, LOGIN: @targets[:login] }, base: uri
352
352
  resp.set_header "Variable-#{@vars[:type]}", resp.content_type
353
353
  resp
@@ -386,7 +386,7 @@ module ForgetPasswords
386
386
  target != uri # (note this should always be true)
387
387
  resp.set_cookie @keys[:cookie], {
388
388
  value: token, secure: req.ssl?, httponly: true,
389
- domain: uri.host, path: ?/, same_site: :strict,
389
+ domain: uri.host, path: ?/, same_site: :lax, # strict is too strict
390
390
  expires: time_delta(@state.expiry[:cookie]),
391
391
  }
392
392
 
@@ -444,7 +444,7 @@ module ForgetPasswords
444
444
  # update the cookie expiration
445
445
  resp.set_cookie @keys[:cookie], {
446
446
  value: token, secure: req.ssl?, httponly: true,
447
- domain: uri.host, path: ?/, same_site: :strict,
447
+ domain: uri.host, path: ?/, same_site: :lax, # strict is too strict
448
448
  expires: time_delta(@state.expiry[:cookie], now),
449
449
  }
450
450
 
@@ -495,7 +495,7 @@ module ForgetPasswords
495
495
 
496
496
  # return 200 now because this is now a content handler
497
497
  resp.status = 200
498
- @templates[:email_sent].populate resp, req, {
498
+ @templates[:email_sent, req].populate resp, req, {
499
499
  FORWARD: forward.to_s, FROM: @email[:from].to_s, EMAIL: address.to_s },
500
500
  base: uri
501
501
  end
@@ -534,6 +534,7 @@ module ForgetPasswords
534
534
  def handle_auth req
535
535
  auth = req.get_header('Authorization') || req.env['HTTP_AUTHORIZATION']
536
536
  if auth and !auth.strip.empty?
537
+ # warn "has authorization header #{auth}"
537
538
  mech, *auth = auth.strip.split
538
539
  token = case mech.downcase
539
540
  when 'basic'
@@ -549,13 +550,15 @@ module ForgetPasswords
549
550
  default_401 req
550
551
  end
551
552
  elsif knock = req.GET[@keys[:query]]
552
- # check for a knock first; this overrides everything
553
+ # check for a knock; this overrides an existing cookie
554
+ # warn "has knock token #{knock}"
553
555
  handle_knock req, knock
554
556
  # elsif req.post?
555
557
  # # next check for a login/logout attempt
556
558
  # handle_post req
557
559
  elsif token = req.cookies[@keys[:cookie]]
558
560
  # next check for a cookie
561
+ # warn "has cookie #{token}"
559
562
  handle_cookie req, token
560
563
  else
561
564
  default_401 req
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forget-passwords
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.13
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Taylor
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-08 00:00:00.000000000 Z
11
+ date: 2023-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -290,7 +290,7 @@ homepage: https://github.com/doriantaylor/rb-forget-passwords
290
290
  licenses:
291
291
  - Apache-2.0
292
292
  metadata: {}
293
- post_install_message:
293
+ post_install_message:
294
294
  rdoc_options: []
295
295
  require_paths:
296
296
  - lib
@@ -305,8 +305,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
305
305
  - !ruby/object:Gem::Version
306
306
  version: '0'
307
307
  requirements: []
308
- rubygems_version: 3.3.11
309
- signing_key:
308
+ rubygems_version: 3.1.2
309
+ signing_key:
310
310
  specification_version: 4
311
311
  summary: Web authentication module for the extremely lazy
312
312
  test_files: []