forget-passwords 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
data/behaviour.org ADDED
@@ -0,0 +1,112 @@
1
+ #+STARTUP: showall hidestars
2
+ * behaviours
3
+ ** forgetpw authenticator
4
+ - [ ] if the request has a cookie with the specified key (eg ~forgetpw~)
5
+ - [ ] if it matches a valid (ie matching, non-expired) entry in the database
6
+ - [ ] set REMOTE_USER to whatever the user is
7
+ - [ ] email address ?
8
+ - [ ] otherwise return 403
9
+ - [ ] (give the user an opportunity to reenter)
10
+ - [ ] if the request-uri has the specified query parameter (eg ~knock~)
11
+ - [ ] if it matches an active nonce
12
+ - [ ] set-cookie to a new nonce
13
+ - [ ] redirect to itself
14
+ - [ ] otherwise return 403
15
+ - [ ] return 401 for all residual requests
16
+ - [ ] 401 page should say something like "this site is restricted to
17
+ people with certain email addresses; please enter your email
18
+ address"
19
+ - [ ] provide form input
20
+ - [ ] if the entry is found in the database
21
+ - [ ] go to confirmation page
22
+ - [ ] confirmation page is same url as whatever url was POSTed
23
+ - [ ] confirmation page says something like "check your
24
+ $EMAIL; you can close this window."
25
+ - [ ] otherwise return 403 (different 403 page)
26
+ - [ ] don't give anything away
27
+ - [ ] "your email address $EMAIL was not found on the access
28
+ list. if you think this is an error, contact $WHOEVER"
29
+ *** confirmation process (that returns confirmation page)
30
+ - [ ] internally pipes out to whatever sends the email
31
+ - [ ] gonna need to know:
32
+ - [ ] the email address
33
+ - [ ] the domain
34
+ - [ ] the template
35
+ - [ ] gonna need to have some braking/throttling mechanism
36
+ ** state mechanism
37
+ - [ ] store all the state information beyond the most basic
38
+ configuration that you would pass in on the command line
39
+ *** user
40
+ - [ ] email should be the unique thing
41
+ - [ ] principal may be different but otherwise is same as email
42
+ - [ ] principal should also be unique
43
+ - [ ]
44
+ *** token
45
+ - [ ] map the tokens to the user
46
+ *** usage
47
+ - [ ] log the ip of which token gets used by whom and when
48
+ *** acl
49
+ - [ ] associate ~Host:~ header domain name with user
50
+ - [ ] via email address or domain
51
+ - [ ]
52
+ ** cli
53
+ - [ ] runs the authenticator
54
+ - [ ] wraps the email drop thing whatever
55
+ - [ ] marshals operations on the state database
56
+ * components
57
+ ** template processor
58
+ - [ ] ideally this should be pluggable
59
+ - [ ] defaults packaged with and sourced from forgetpw package
60
+ - [ ] defaults can be overridden in command-line config
61
+ - [ ] command to disgorge copies of defaults into a directory
62
+ - (so they can be edited)
63
+ *** (x?)html
64
+ - [ ] variables are processing instructions
65
+ - <?var EMAIL?>
66
+ *** markdown?
67
+ - XXX how do we express variable substitutions?
68
+ ** email preparer
69
+ - make (x?)html and parallel text version
70
+ - wrap in mime whatever
71
+ - embed any images (godddddd)
72
+ - where do we put images?
73
+ * templates
74
+ - [X] obtaining a magic link
75
+ - [X] put in your email (basic 401)
76
+ - [X] email wasn't on the list (403?)
77
+ - [X] problem sending email (500)
78
+ - [X] email sent, you can close this window and follow the link in
79
+ the email
80
+ - [X] bad token, you're gonna have to generate a new one (409)
81
+ - do we care about announcing the distinction between a bad token
82
+ in the query vs in the cookie?
83
+ - [ ] token doesn't match a user (403)
84
+ - likewise, do we care about the distinction between a malformed
85
+ token and a well-formed token that isn't in the database?
86
+ - what about if the token /did/ match a user?
87
+ - anyway you're gonna have to generate a new one so give the form
88
+ - [X] token expired/invalidated (401)
89
+ - you'll have to generate a new one
90
+ - [-] you have been logged out
91
+ - [X] your other sessions remain open
92
+ - [ ] all your other sessions are logged out as well
93
+ - log back in?
94
+ - [-] the email itself
95
+ - [X] html version
96
+ - [ ] text version
97
+ * issues
98
+ - rack seems to have a problem transmitting error body content, i
99
+ hope to god it isn't some kind of i/o voodoo
100
+ - okay it has to do with ~Rack::Handler::FastCGI~ running
101
+ ~out.flush~ when vanilla fcgi can just...not do that
102
+ - *HOWEVER* it is not unreasonable for rack to do this so the bad
103
+ actor here is ~mod_authnz_fcgi~
104
+ - https://bz.apache.org/bugzilla/show_bug.cgi?id=65984
105
+ - ~mod_authnz_fcgi~ kinda sucks but it's *almost* good
106
+ - i mean it's just a barely-fitting solution
107
+ - also fastcgi is out of style with the kids
108
+ - so is apache for that matter
109
+ - also the error handler seems to overwrite ~Content-Type~ with
110
+ whatever is in the standard error page
111
+ - so that'll have to get overwritten in config
112
+ -
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "forget-passwords"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>Protected Area</title>
5
+ </head>
6
+ <body>
7
+ <h1>Protected Area</h1>
8
+ <p>This website is only available to certain people. If you're one of them, your e-mail address&#x2014;most likely the one associated with your workplace&#x2014;will be on the list. Enter it here and we'll send you a special link that will grant you access.</p>
9
+ <form method="post" action="$LOGIN" accept-charset="utf-8">
10
+ <input type="hidden" name="forward" value="$FORWARD"/>
11
+ <input type="email" name="email" placeholder="e.g. you@yourcompany.biz"/>
12
+ <input type="submit" value="Send Link"/>
13
+ </form>
14
+ </body>
15
+ </html>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>Not Found</title>
5
+ </head>
6
+ <body>
7
+ <h1>Not Found</h1>
8
+ <p>There is no known resource at this address. <a href="javascript:history.go(-1)">Go back?</a></p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>Something is Scrambled Under the Hood</title>
5
+ </head>
6
+ <body>
7
+ <h1>Something is Scrambled Under the Hood</h1>
8
+ <p>There's a problem in the plumbing responsible for keeping you logged in, and we're not sure what to make of it from here. The most expedient thing to do is send you a new link to refresh your session.</p>
9
+ <form method="post" action="" accept-charset="utf-8">
10
+ <input type="email" name="email" placeholder="e.g. you@yourcompany.biz"/>
11
+ <input type="submit" value="Send Link"/>
12
+ </form>
13
+ </body>
14
+ </html>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>Something Broke in the Process</title>
5
+ </head>
6
+ <body>
7
+ <h1>Something Broke in the Process</h1>
8
+ <p>If you see this message, it's because some piece of the system necessary to serve you this website has failed. It probably just needs to be kicked in the pants, but somebody is going to have to do that manually. Please contact the person who runs this site and let them know.</p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>Your Session Has Run Out</title>
5
+ </head>
6
+ <body>
7
+ <h1>Your Session Has Run Out</h1>
8
+ <p>The token associated with the link that logged you in has either expired or otherwise been invalidated (e.g. by logging out). To log back in, we can e-mail you another link.</p>
9
+ <form method="post" action="$LOGIN" accept-charset="utf-8">
10
+ <input type="hidden" name="forward" value="$FORWARD"/>
11
+ <input type="email" name="email" placeholder="e.g. you@yourcompany.biz"/>
12
+ <input type="submit" value="Send Link"/>
13
+ </form>
14
+ </body>
15
+ </html>
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>We Had A Problem With Your E-Mail Address</title>
5
+ </head>
6
+ <body>
7
+ <h1>We Had A Problem With Your E-Mail Address</h1>
8
+ <p>We weren't able to use <strong><?var $EMAIL?></strong> as an e-mail address. Please try another.</p>
9
+ <form method="post" action="$LOGIN" accept-charset="utf-8">
10
+ <input type="hidden" name="forward" value="$FORWARD"/>
11
+ <input type="email" name="email" placeholder="e.g. you@yourcompany.biz"/>
12
+ <input type="submit" value="Send Link"/>
13
+ </form>
14
+ </body>
15
+ </html>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>Check Your E-Mail</title>
5
+ </head>
6
+ <body>
7
+ <h1>Check Your E-Mail</h1>
8
+ <p>We (<code><?var $FROM?></code>) just sent you (<code><?var $EMAIL?></code>) a link that will log you in and send you back to <code><?var FORWARD?></code>. If you don't see it in your inbox shortly, check the usual places (spam/junk mail).</p>
9
+ <p>There is nothing left to do on this page, so close it at your leisure.</p>
10
+ </body>
11
+ </html>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>Your Link to Access <?var $DOMAIN?></title>
5
+ </head>
6
+ <body>
7
+ <p><a href="$KNOCK_URL">Proceed to <?var $PRETTY_URL?></a></p>
8
+ <p>You can delete this e-mail once you've used the link, because clicking it will cause it to expire. If you need to log in again, or log in from another device, <a href="$URL">visit the site</a> on that device to generate a new e-mail with a new link&#x2014;and of course, follow the new link from whichever device you intend to access the site with.</p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>You Are Logged Out</title>
5
+ </head>
6
+ <body>
7
+ <h1>You Are Logged Out</h1>
8
+ <p>You are logged out on this browser.</p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>You Are Logged Out</title>
5
+ </head>
6
+ <body>
7
+ <h1>You Are Logged Out</h1>
8
+ <p>You are logged out on this browser.</p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>The Link Has Expired</title>
5
+ </head>
6
+ <body>
7
+ <h1>The Link Has Expired</h1>
8
+ <p>The link you followed to complete the sign-in process has expired. We'll have to send you another one.</p>
9
+ <form method="post" action="$LOGIN" accept-charset="utf-8">
10
+ <input type="hidden" name="forward" value="$FORWARD"/>
11
+ <input type="email" name="email" placeholder="e.g. you@yourcompany.biz"/>
12
+ <input type="submit" value="Send Link"/>
13
+ </form>
14
+ </body>
15
+ </html>
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>We Don't Have This E-Mail Address (Yet)</title>
5
+ </head>
6
+ <body>
7
+ <h1>We Don't Have This E-Mail Address (Yet)</h1>
8
+ <p>The e-mail address you entered doesn't appear to be on the list. Feel free to try another one, or contact the person who shared this site with you to make sure we have your preferred address for accessing this website.</p>
9
+ <form method="post" action="$LOGIN" accept-charset="utf-8">
10
+ <input type="hidden" name="forward" value="$FORWARD"/>
11
+ <input type="email" name="email" placeholder="e.g. other-you@different.place"/>
12
+ <input type="submit" value="Send Link"/>
13
+ </form>
14
+ </body>
15
+ </html>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>POST Only</title>
5
+ </head>
6
+ <body>
7
+ <h1><code>POST</code> Only</h1>
8
+ <p>This resource only responds to <code>POST</code> requests.</p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>We Had a Problem With the Forwarding Address</title>
5
+ </head>
6
+ <body>
7
+ <h1>We Had a Problem With the Forwarding Address</h1>
8
+ <p>A valid Web address to forward to should have been sent along in the request that got you here. <a href="javascript:history.go(-1)">Go back</a> and try again.</p>
9
+ </body>
10
+ </html>
data/etc/text-only.xsl ADDED
@@ -0,0 +1,105 @@
1
+ <?xml version="1.0"?>
2
+ <xsl:stylesheet version="1.0"
3
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4
+ xmlns:html="http://www.w3.org/1999/xhtml"
5
+ xmlns:x="urn:x-dummy"
6
+ exclude-result-prefixes="html x">
7
+
8
+ <xsl:output method="text" media-type="text/plain" omit-xml-declaration="yes"/>
9
+
10
+ <xsl:strip-space elements="*"/>
11
+
12
+ <xsl:template name="repeat">
13
+ <xsl:param name="string" select="''"/>
14
+ <xsl:param name="n" select="1"/>
15
+ <xsl:value-of select="$string"/>
16
+ <xsl:if test="$n &gt; 1">
17
+ <xsl:call-template name="repeat">
18
+ <xsl:with-param name="string" select="$string"/>
19
+ <xsl:with-param name="n" select="$n - 1"/>
20
+ </xsl:call-template>
21
+ </xsl:if>
22
+ </xsl:template>
23
+
24
+ <xsl:template match="/">
25
+ <xsl:apply-templates select="/html/body|/html:html/html:body"/>
26
+ </xsl:template>
27
+
28
+ <xsl:template match="body|html:body">
29
+ <xsl:apply-templates select="*"/>
30
+ </xsl:template>
31
+
32
+ <!-- link text <https://...> -->
33
+ <xsl:template match="a[@href]|html:a[@href]">
34
+ <xsl:apply-templates/>
35
+ <xsl:text disable-output-escaping="yes"> &lt;</xsl:text>
36
+ <xsl:value-of select="normalize-space(@href)"/>
37
+ <xsl:text disable-output-escaping="yes">&gt; </xsl:text>
38
+ </xsl:template>
39
+
40
+ <x:headings>
41
+ <x:h1>#</x:h1>
42
+ <x:h2>=</x:h2>
43
+ <x:h3>~</x:h3>
44
+ <x:h4>-</x:h4>
45
+ <x:h5>-</x:h5>
46
+ <x:h6>-</x:h6>
47
+ </x:headings>
48
+
49
+ <xsl:variable name="headings" select="document('')/xsl:stylesheet/x:headings/*"/>
50
+
51
+ <xsl:template match="html:p[normalize-space(.) != '']">
52
+ <xsl:apply-templates/>
53
+ <xsl:text>
54
+
55
+ </xsl:text>
56
+ </xsl:template>
57
+
58
+ <xsl:template match="html:h1|html:h2|html:h3|html:h4|html:h5|html:h6">
59
+ <xsl:variable name="text">
60
+ <xsl:apply-templates/>
61
+ </xsl:variable>
62
+ <xsl:value-of select="$text" disable-output-escaping="yes"/>
63
+ <xsl:if test="string-length(normalize-space($text))">
64
+ <xsl:text>
65
+ </xsl:text>
66
+ <xsl:variable name="ln" select="local-name()"/>
67
+
68
+ <xsl:call-template name="repeat">
69
+ <xsl:with-param name="string"
70
+ select="normalize-space($headings[local-name(.) = $ln])"/>
71
+ <xsl:with-param name="n" select="string-length($text)"/>
72
+ </xsl:call-template>
73
+ <xsl:text>
74
+
75
+ </xsl:text>
76
+ </xsl:if>
77
+ </xsl:template>
78
+
79
+ <!-- stuff that we never want to render in text -->
80
+ <xsl:template match="html:img|html:object|html:iframe|html:form"/>
81
+
82
+ <xsl:variable name="vws"
83
+ select="'&#x09;&#x0a;&#x0d;&#x85;&#xa0;&#x2028;&#x2029;'"/>
84
+
85
+ <xsl:template match="text()">
86
+ <xsl:variable name="_" select="translate(., $vws, ' ')"/>
87
+ <xsl:variable name="starts-with-ws" select="starts-with($_, ' ')"/>
88
+ <xsl:variable name="ends-with-ws"
89
+ select="substring($_, string-length($_)) = ' '"/>
90
+
91
+ <xsl:if test="$starts-with-ws">
92
+ <xsl:text> </xsl:text>
93
+ </xsl:if>
94
+ <xsl:value-of select="normalize-space($_)" disable-output-escaping="yes"/>
95
+ <xsl:if test="$ends-with-ws">
96
+ <xsl:text> </xsl:text>
97
+ </xsl:if>
98
+
99
+ </xsl:template>
100
+
101
+ <xsl:template match="*">
102
+ <xsl:apply-templates select="text()|*"/>
103
+ </xsl:template>
104
+
105
+ </xsl:stylesheet>
data/exe/forgetpw ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: enh-ruby -*-
3
+
4
+ # code is here
5
+ require 'forget-passwords/cli'
6
+
7
+ ForgetPasswords::CLI.run
@@ -0,0 +1,67 @@
1
+ # -*- mode: enh-ruby -*-
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'forget-passwords/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "forget-passwords"
8
+ spec.version = ForgetPasswords::VERSION
9
+ spec.authors = ["Dorian Taylor"]
10
+ spec.email = ["code@doriantaylor.com"]
11
+ spec.license = 'Apache-2.0'
12
+ spec.homepage = 'https://github.com/doriantaylor/rb-forget-passwords'
13
+ spec.summary = 'Web authentication module for the extremely lazy'
14
+ spec.description = <<-DESC
15
+ This little module (and attendant command line tool and rackup app)
16
+ exists for the purpose of providing rudimentary access control to a
17
+ website when the prospective users are both small in number, and very
18
+ busy. It circumvents schmucking around provisioning passwords by
19
+ generating a link which you can pass to each of your users through
20
+ some other mechanism, that when visited logs them in and keeps them
21
+ logged in as long as you want. This is basically the equivalent of
22
+ having a "forgot password" link without anybody having to click on
23
+ "forgot password", and is perfectly adequate security in certain
24
+ contexts, namely the ones the author of this gem is interested in.
25
+ DESC
26
+
27
+ # switch based on whether we're a git or hg repository
28
+ wd = File.dirname lib
29
+ spec.files = if File.exists?("#{wd}/.git")
30
+ `git ls-files -z`
31
+ elsif File.exists?("#{wd}/.hg")
32
+ `hg files -0`
33
+ else
34
+ raise "Can't find a git or hg repository!"
35
+ end.split("\x0").reject do |f|
36
+ f.match(%r{^(test|spec|features)/})
37
+ end
38
+
39
+ spec.bindir = "exe"
40
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
41
+ spec.require_paths = ["lib"]
42
+
43
+ # ruby
44
+ spec.required_ruby_version = '>= 2'
45
+
46
+ # dev/test dependencies
47
+ spec.add_development_dependency 'bundler', '>= 2.1'
48
+ spec.add_development_dependency 'rake', '>= 13.0'
49
+ spec.add_development_dependency 'rspec', '>= 3.9'
50
+
51
+ # stuff we use
52
+ spec.add_runtime_dependency 'commander', '>= 4.4'
53
+ spec.add_runtime_dependency 'deep_merge', '>= 1.2'
54
+ spec.add_runtime_dependency 'dry-schema', '>= 1.9.1'
55
+ spec.add_runtime_dependency 'dry-types', '>= 1.5.1'
56
+ spec.add_runtime_dependency 'fcgi', '>= 0.9.2.1'
57
+ spec.add_runtime_dependency 'iso8601', '>= 0.12'
58
+ spec.add_runtime_dependency 'mail', '>= 2.7.1'
59
+ spec.add_runtime_dependency 'rack', '>= 2.0'
60
+ spec.add_runtime_dependency 'sequel', '>= 5.20'
61
+ spec.add_runtime_dependency 'uuidtools', '>= 2.1'
62
+
63
+ # stuff i wrote
64
+ spec.add_runtime_dependency 'http-negotiate', '>= 0.1.3'
65
+ spec.add_runtime_dependency 'uuid-ncname', '>= 0.2'
66
+ spec.add_runtime_dependency 'xml-mixup', '>= 0.1.13'
67
+ end