forget-passwords 0.2.13 → 0.3.1

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: 4db3ce83ab06fe2e7ba9fbf453370f1182e8fc6e7f888e1edd2851e1eb4dfe1a
4
- data.tar.gz: '049d12c72b928352235e422c2c737a7c7827d5284a68054ea09f50c7d3a962d0'
3
+ metadata.gz: 356eaeb9312780934bfae2ec2563654d692923be2633da67962cb8e4def0eb7e
4
+ data.tar.gz: 94cdf2ecac3f9509159e648bcf672c9f6bda8b70adbeba01120ce4ba96585384
5
5
  SHA512:
6
- metadata.gz: 2d3bc2353a0b41009cb7b537a67bc58210670095949afbe21ada7221912c3d6a91c580240d56803b159a32a73c3d4e1c756f7b34c288879be61f842740c6bf20
7
- data.tar.gz: 3c7771eb7709deeeca3c7617be519168d6387c619b5e0de0a61243905bb8a4f5a9a2667ab6a63ada192c962a426301e2d83400a5370dc212aaa7e2ee1f69eefb
6
+ metadata.gz: 567efe5a44252f6aa39206ed58325f6f74f9acad040af449383317f7bc80c2c7ecd0680a19e8231a6d1710dccba22521c651495ef992b04a3329f25527738f74
7
+ data.tar.gz: e20c7477ae6b0bf4c9fb54bbae5b285dc962c27a07e5815a1e1c1da5b37064f9d2a11dfe8fbab5413a127c550e40277a7010f56c5e9642e54b63f267adb8e100
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
@@ -56,7 +56,7 @@ DESC
56
56
  spec.add_runtime_dependency 'fcgi', '>= 0.9.2.1'
57
57
  spec.add_runtime_dependency 'iso8601', '>= 0.12'
58
58
  spec.add_runtime_dependency 'mail', '>= 2.7.1'
59
- spec.add_runtime_dependency 'rack', '>= 2.0'
59
+ spec.add_runtime_dependency 'rack', '~> 2'
60
60
  spec.add_runtime_dependency 'sequel', '>= 5.20'
61
61
  spec.add_runtime_dependency 'uuidtools', '>= 2.1'
62
62
 
@@ -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
@@ -101,7 +101,7 @@ module Dry::Types::Builder
101
101
  reqd = keys.select(&:required?)
102
102
 
103
103
  if reqd.empty?
104
- # there aren't any requireed keys, but we'll set the empty hash
104
+ # there aren't any required keys, but we'll set the empty hash
105
105
  # as a default if there exist optional keys, otherwise any
106
106
  # default will interfere with input from upstream.
107
107
  return default({}.freeze) unless keys.empty?
@@ -1,3 +1,3 @@
1
1
  module ForgetPasswords
2
- VERSION = '0.2.13'
2
+ VERSION = '0.3.1'
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.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Taylor
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-08 00:00:00.000000000 Z
11
+ date: 2023-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -154,16 +154,16 @@ dependencies:
154
154
  name: rack
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
- - - ">="
157
+ - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: '2.0'
159
+ version: '2'
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
- - - ">="
164
+ - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: '2.0'
166
+ version: '2'
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: sequel
169
169
  requirement: !ruby/object:Gem::Requirement
@@ -305,7 +305,7 @@ 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
308
+ rubygems_version: 3.3.15
309
309
  signing_key:
310
310
  specification_version: 4
311
311
  summary: Web authentication module for the extremely lazy