forget-passwords 0.2.12 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -3
- data/lib/forget-passwords/template.rb +64 -13
- data/lib/forget-passwords/version.rb +1 -1
- data/lib/forget-passwords.rb +21 -14
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dacb56a002005cc14bb46809690604cccd905eee5c5fc8c5efb78557629611e6
|
4
|
+
data.tar.gz: 704b521acb64f33a934c8768e8bc1496a86ae55d27a0598f4172a7ddc6c8d0e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e79d40cbf1151f1a6fb056e17e90f022f65cf575e9eac4a2d1931b4c9c32723924a7e27f83ec893acbafa46710c6da68de3e2791005066ba74eea2a880f0f39
|
7
|
+
data.tar.gz: c306b865ed182f590338d26a7a41a165c38626519dbfc627b77fac4f8a271f7a9d83274a10ec8ae7629a9992407fe92524a4b2a9eb5e4c74ebf1b3b5907e67de
|
data/README.md
CHANGED
@@ -524,6 +524,15 @@ email:
|
|
524
524
|
# additional SMTP configuration would go here, if applicable.
|
525
525
|
```
|
526
526
|
|
527
|
+
## Alternate Authentication Methods
|
528
|
+
|
529
|
+
It is possible to take the token in the cookie and feed it in as
|
530
|
+
either a `Basic` authentication password or `Bearer` token. In the
|
531
|
+
case of `Basic`, the username is ignored. This enables `curl` or API
|
532
|
+
access, or other automated things like feed readers. There is
|
533
|
+
currently no UI for this, but an "app password" management screen is
|
534
|
+
potentially on the horizon.
|
535
|
+
|
527
536
|
## Future Directions
|
528
537
|
|
529
538
|
This project began on something of a lark, with the intent to make a
|
@@ -569,9 +578,11 @@ favourite flavour of templating engine.
|
|
569
578
|
|
570
579
|
### Reconcile with OAuth
|
571
580
|
|
572
|
-
|
573
|
-
|
574
|
-
|
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
|
+
|
575
586
|
Indeed, bearer tokens would make for an _excellent_ cleavage plane for
|
576
587
|
_segmented_ authentication: Method X to bearer token, then bearer
|
577
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
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
69
|
-
|
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
|
-
|
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
|
140
|
-
@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
|
data/lib/forget-passwords.rb
CHANGED
@@ -11,6 +11,7 @@ require 'rack'
|
|
11
11
|
require 'rack/request'
|
12
12
|
require 'rack/response'
|
13
13
|
|
14
|
+
require 'base64'
|
14
15
|
require 'mail'
|
15
16
|
|
16
17
|
module ForgetPasswords
|
@@ -310,7 +311,7 @@ module ForgetPasswords
|
|
310
311
|
}
|
311
312
|
|
312
313
|
# grab the template since we'll use it
|
313
|
-
template = @templates[:email]
|
314
|
+
template = @templates[:email, req]
|
314
315
|
|
315
316
|
# process the templates
|
316
317
|
doc = template.process vars: vars
|
@@ -335,7 +336,7 @@ module ForgetPasswords
|
|
335
336
|
uri = req_uri req
|
336
337
|
resp = Rack::Response.new
|
337
338
|
resp.status = status
|
338
|
-
@templates[key].populate resp, req, vars, base: uri
|
339
|
+
@templates[key, req].populate resp, req, vars, base: uri
|
339
340
|
resp.set_header "Variable-#{@vars[:type]}", resp.content_type
|
340
341
|
raise ForgetPasswords::ErrorResponse, resp
|
341
342
|
end
|
@@ -346,7 +347,7 @@ module ForgetPasswords
|
|
346
347
|
uri = req_uri req
|
347
348
|
resp = Rack::Response.new
|
348
349
|
resp.status = 401
|
349
|
-
@templates[:default_401].populate resp, req, {
|
350
|
+
@templates[:default_401, req].populate resp, req, {
|
350
351
|
FORWARD: req_uri(req).to_s, LOGIN: @targets[:login] }, base: uri
|
351
352
|
resp.set_header "Variable-#{@vars[:type]}", resp.content_type
|
352
353
|
resp
|
@@ -385,7 +386,7 @@ module ForgetPasswords
|
|
385
386
|
target != uri # (note this should always be true)
|
386
387
|
resp.set_cookie @keys[:cookie], {
|
387
388
|
value: token, secure: req.ssl?, httponly: true,
|
388
|
-
domain: uri.host, path: ?/, same_site: :strict
|
389
|
+
domain: uri.host, path: ?/, same_site: :lax, # strict is too strict
|
389
390
|
expires: time_delta(@state.expiry[:cookie]),
|
390
391
|
}
|
391
392
|
|
@@ -443,7 +444,7 @@ module ForgetPasswords
|
|
443
444
|
# update the cookie expiration
|
444
445
|
resp.set_cookie @keys[:cookie], {
|
445
446
|
value: token, secure: req.ssl?, httponly: true,
|
446
|
-
domain: uri.host, path: ?/, same_site: :strict
|
447
|
+
domain: uri.host, path: ?/, same_site: :lax, # strict is too strict
|
447
448
|
expires: time_delta(@state.expiry[:cookie], now),
|
448
449
|
}
|
449
450
|
|
@@ -494,7 +495,7 @@ module ForgetPasswords
|
|
494
495
|
|
495
496
|
# return 200 now because this is now a content handler
|
496
497
|
resp.status = 200
|
497
|
-
@templates[:email_sent].populate resp, req, {
|
498
|
+
@templates[:email_sent, req].populate resp, req, {
|
498
499
|
FORWARD: forward.to_s, FROM: @email[:from].to_s, EMAIL: address.to_s },
|
499
500
|
base: uri
|
500
501
|
end
|
@@ -531,27 +532,33 @@ module ForgetPasswords
|
|
531
532
|
end
|
532
533
|
|
533
534
|
def handle_auth req
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
535
|
+
auth = req.get_header('Authorization') || req.env['HTTP_AUTHORIZATION']
|
536
|
+
if auth and !auth.strip.empty?
|
537
|
+
# warn "has authorization header #{auth}"
|
538
|
+
mech, *auth = auth.strip.split
|
539
|
+
token = case mech.downcase
|
540
|
+
when 'basic'
|
541
|
+
# can't trust/use rack here
|
542
|
+
Base64.decode64(auth.first || '').split(?:, 2).last
|
543
|
+
when 'bearer'
|
544
|
+
auth.first
|
540
545
|
end
|
546
|
+
|
541
547
|
if token
|
542
548
|
handle_token req, token
|
543
549
|
else
|
544
|
-
# XXX one day maybe this can be more descriptive??
|
545
550
|
default_401 req
|
546
551
|
end
|
547
552
|
elsif knock = req.GET[@keys[:query]]
|
548
|
-
# check for a knock
|
553
|
+
# check for a knock; this overrides an existing cookie
|
554
|
+
# warn "has knock token #{knock}"
|
549
555
|
handle_knock req, knock
|
550
556
|
# elsif req.post?
|
551
557
|
# # next check for a login/logout attempt
|
552
558
|
# handle_post req
|
553
559
|
elsif token = req.cookies[@keys[:cookie]]
|
554
560
|
# next check for a cookie
|
561
|
+
# warn "has cookie #{token}"
|
555
562
|
handle_cookie req, token
|
556
563
|
else
|
557
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.
|
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:
|
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.
|
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: []
|