twproxy 0.0.1
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 +7 -0
- data/Gemfile +6 -0
- data/LICENSE +20 -0
- data/README.md +117 -0
- data/bin/twproxy +54 -0
- data/lib/twproxy/proxy.rb +115 -0
- data/lib/twproxy/views/login.haml +56 -0
- data/lib/twproxy.rb +1 -0
- data/twproxy.gemspec +18 -0
- metadata +94 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d76c0ff8f096bc96a9a2bb037879a31fd08f958f
|
4
|
+
data.tar.gz: e56c25cac077859244f4e062af7edcc59df27b4c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fe6b63a884f567df715720db6b1ee556b91ae947e0e22def49b3911c70a2dee0851054a73478e8392a45fc3785ac8e5db466968101bb09afb3f8c48c7276c08e
|
7
|
+
data.tar.gz: ba4b836a56c8316719a53a456e6bf59c3f468e681337e7289e4aae42fec1254a950def4d3d1b7dbb65ccacf10950ecc35b50778e59336be47ab37607a9f38a4e
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
Copyright (c) 2016 Steve Gattuso
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
this software and associated documentation files (the "Software"), to deal in
|
6
|
+
the Software without restriction, including without limitation the rights to
|
7
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
8
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
9
|
+
so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
12
|
+
copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# TiddlyWiki Proxy
|
2
|
+
*An authenticated proxy for protecting your wiki.*
|
3
|
+
|
4
|
+

|
5
|
+
|
6
|
+
This gem provides an easy way to secure a TiddlyWiki installation so you can
|
7
|
+
expose it to the internet without having to worry about others gaining access.
|
8
|
+
The proxy provides you with a username, password and optionally a 2-factor auth
|
9
|
+
code (based on TOTP, which is compatible with [Google Authenticator](https://support.google.com/accounts/answer/1066447?hl=en)).
|
10
|
+
|
11
|
+
Although this proxy can be used as a server in itself, it's highly recommended
|
12
|
+
that you run it behind a proper webserver (such as nginx's reverse proxy) that
|
13
|
+
is secured with SSL.
|
14
|
+
|
15
|
+
## Usage reference
|
16
|
+
Installation:
|
17
|
+
```
|
18
|
+
$ gem install twproxy
|
19
|
+
```
|
20
|
+
|
21
|
+
Argument reference:
|
22
|
+
```
|
23
|
+
$ twproxy --help
|
24
|
+
Usage: twproxy [options]
|
25
|
+
-p, --port PORT Specify port to run the proxy on. Defaults to 8888.
|
26
|
+
-b, --bind HOSTNAME Specify hostname to bind to. Defaults to 127.0.0.1.
|
27
|
+
-s, --enable-ssl Ensures cookies are marked as secure.
|
28
|
+
-d, --destination URL Specify the url of the TiddlyWiki server. Defaults to http://localhost:8080.
|
29
|
+
-g CLEARTEXT, Generates a SHA1 hashed password
|
30
|
+
--generate-password
|
31
|
+
-u, --username USER Sets the username. Defaults to user.
|
32
|
+
-P, --password HASHED Sets the user's password. Use a SHA1 hash or -g. Defaults to test.
|
33
|
+
-a, --auth KEY Sets a TOTP key for use with Google Authenticator.
|
34
|
+
-h, --help Displays this help
|
35
|
+
```
|
36
|
+
|
37
|
+
## Basic Usage
|
38
|
+
Let's say we have a
|
39
|
+
[tiddlywiki server](http://tiddlywiki.com/static/Using%2520TiddlyWiki%2520on%2520Node.js.html)
|
40
|
+
running on `http://localhost:8080` that we'd like to protect with twproxy.
|
41
|
+
Fistly we'll want to generate a hashed version of the password we'd like to use:
|
42
|
+
|
43
|
+
```
|
44
|
+
$ twproxy -g helloWorld
|
45
|
+
5395ebfd174b0a5617e6f409dfbb3e064e3fdf0a
|
46
|
+
```
|
47
|
+
|
48
|
+
Great! Now let's spin up twproxy so we can start working on our wiki,
|
49
|
+
protecting it with a username of `stevenleeg` and a password of `helloWorld`:
|
50
|
+
|
51
|
+
```
|
52
|
+
$ twproxy -u stevenleeg -P 5395ebfd174b0a5617e6f409dfbb3e064e3fdf0a -d http://localhost:8080/
|
53
|
+
```
|
54
|
+
|
55
|
+
If all goes well you can now navigate to `http://localhost:8888` and see a
|
56
|
+
prompt for your username and password.
|
57
|
+
|
58
|
+
That's all there is to it! Twproxy is set up and ready to go for basic usage,
|
59
|
+
however if you're paranoid I'd recommend further securing your wiki with SSL
|
60
|
+
(using an nginx reverse proxy) and 2FA.
|
61
|
+
|
62
|
+
## Enabling 2FA
|
63
|
+
Sometimes a username and password isn't enough for paranoid minds. Not to
|
64
|
+
worry! Twproxy lets you add another layer of security onto your wiki by
|
65
|
+
supporting time-based one-time password authentication. This allows you to also
|
66
|
+
require a unique one-time code from Google Authenticator (or any other TOTP app)
|
67
|
+
in order to sign in.
|
68
|
+
|
69
|
+
Setting up 2FA requires acquiring a secret key that is used to generate one-time
|
70
|
+
passwords. [This online generator](http://www.xanxys.net/totp/) will do the job,
|
71
|
+
but if you're serious about security you should know never to trust an online
|
72
|
+
generator of secret keys.
|
73
|
+
|
74
|
+
For this example use case I used the online generator to get a key (note, I
|
75
|
+
copied the base32 version of the secret key):
|
76
|
+
`o76swdmjl74izl7bihmziod333cwnje3`.
|
77
|
+
|
78
|
+
I can then add 2FA to our previous example by adding the `-a` flag:
|
79
|
+
```
|
80
|
+
$ twproxy -u stevenleeg -P 5395ebfd174b0a5617e6f409dfbb3e064e3fdf0a -d http://localhost:8080/ -a o76swdmjl74izl7bihmziod333cwnje3
|
81
|
+
```
|
82
|
+
|
83
|
+
To test it, scan the QR code on the secret key generator's site into your
|
84
|
+
favorite TOTP app and try logging in (the authentication screen will now show a
|
85
|
+
third box for an auth code).
|
86
|
+
|
87
|
+
If all goes well you should only be able to sign in after entering the correct
|
88
|
+
auth code generated on your phone.
|
89
|
+
|
90
|
+
## Enabling SSL
|
91
|
+
If you plan on exposing your wiki to the internet, it's a smart idea to protect
|
92
|
+
it by running it over an encrypted HTTPS connection. The easiest way to do this
|
93
|
+
is by using an [nginx reverse proxy](https://www.nginx.com/resources/admin-guide/reverse-proxy/)
|
94
|
+
paired with an SSL certificate from [Let's Encrypt](https://letsencrypt.org/).
|
95
|
+
|
96
|
+
Getting those two set up is out of scope of this readme, however it may be
|
97
|
+
helpful to check out DigitalOcean's [SSL installation guide](https://www.digitalocean.com/community/tutorials/how-to-create-an-ssl-certificate-on-nginx-for-ubuntu-14-04) if you're lost.
|
98
|
+
|
99
|
+
An important note for those attempting to run twproxy over an SSL connection:
|
100
|
+
you must add the `--enable-ssl` flag when starting the proxy! Without this flag twproxy
|
101
|
+
won't mark cookies as secure, leaving you confused each time you log in
|
102
|
+
correctly only to see yet another authentication prompt.
|
103
|
+
|
104
|
+
## Using with Docker
|
105
|
+
**Note:** More documentation on this to come. This is still a WIP.
|
106
|
+
|
107
|
+
## Issues
|
108
|
+
If you're having trouble with twproxy please don't hesitate to open an issue to
|
109
|
+
let me know. I'm actively maintaining this project for my own use, so it's in
|
110
|
+
my best interest to make sure it is secure and functional.
|
111
|
+
|
112
|
+
If you are filing a bug report please make sure you describe all steps
|
113
|
+
necessary to reproduce the issue. This will make my job much easier and make
|
114
|
+
sure your bug gets fixed faster.
|
115
|
+
|
116
|
+
Happy wiki-ing!
|
117
|
+
|
data/bin/twproxy
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "twproxy"
|
3
|
+
require "sinatra"
|
4
|
+
require "optparse"
|
5
|
+
require "digest/sha1"
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: twproxy [options]"
|
10
|
+
|
11
|
+
opts.on("-p", "--port PORT", "Specify port to run the proxy on. Defaults to 8888.") do |port|
|
12
|
+
options[:port] = port
|
13
|
+
end
|
14
|
+
opts.on("-b", "--bind HOSTNAME", "Specify hostname to bind to. Defaults to 127.0.0.1.") do |hostname|
|
15
|
+
options[:bind] = hostname
|
16
|
+
end
|
17
|
+
opts.on("-s", "--enable-ssl", "Ensures cookies are marked as secure.") do |ssl|
|
18
|
+
options[:enable_ssl] = ssl
|
19
|
+
end
|
20
|
+
opts.on("-d", "--destination URL", "Specify the url of the TiddlyWiki server. Defaults to http://localhost:8080.") do |url|
|
21
|
+
options[:url] = url
|
22
|
+
end
|
23
|
+
opts.on("-g", "--generate-password CLEARTEXT", "Generates a SHA1 hashed password") do |pass|
|
24
|
+
puts Digest::SHA1.hexdigest(pass)
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
opts.on("-u", "--username USER", "Sets the username. Defaults to user.") do |user|
|
28
|
+
options[:username] = user
|
29
|
+
end
|
30
|
+
opts.on("-P", "--password HASHED", "Sets the user's password. Use a SHA1 hash or -g. Defaults to test.") do |hash|
|
31
|
+
options[:password] = hash
|
32
|
+
end
|
33
|
+
opts.on("-a", "--auth KEY", "Sets a TOTP key for use with Google Authenticator.") do |key|
|
34
|
+
options[:auth] = key
|
35
|
+
end
|
36
|
+
opts.on("-h", "--help", "Displays this help") do
|
37
|
+
puts opts
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
end.parse!
|
41
|
+
|
42
|
+
TWProxy.set :bind, options[:bind] || "127.0.0.1"
|
43
|
+
|
44
|
+
TWProxy.set :port, options[:port] || 8888
|
45
|
+
TWProxy.set :enable_ssl, options[:enable_ssl] || false
|
46
|
+
TWProxy.set :url, options[:url] || "http://localhost:8080" || ENV['WIKI_URL']
|
47
|
+
TWProxy.set :username, options[:username] || ENV['WIKI_USER'] || "user"
|
48
|
+
# Default password is test
|
49
|
+
TWProxy.set :password,
|
50
|
+
options[:password] || ENV['WIKI_PASS'] || "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
|
51
|
+
TWProxy.set :auth, options[:auth] || ENV['WIKI_AUTH']
|
52
|
+
|
53
|
+
TWProxy.run!
|
54
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "sinatra"
|
2
|
+
require "net/http"
|
3
|
+
require "uri"
|
4
|
+
require "digest/sha1"
|
5
|
+
require "rotp"
|
6
|
+
|
7
|
+
class TWProxy < Sinatra::Base
|
8
|
+
##
|
9
|
+
# Authenticate each request before permitting it to access the wiki
|
10
|
+
#
|
11
|
+
before do
|
12
|
+
@accepted_token = Digest::SHA1.hexdigest(
|
13
|
+
"#{settings.username}#{settings.password}#{settings.auth}"
|
14
|
+
)
|
15
|
+
|
16
|
+
token = request.cookies["auth"]
|
17
|
+
|
18
|
+
if token == @accepted_token
|
19
|
+
@authenticated = true
|
20
|
+
elsif request.path != "/login"
|
21
|
+
redirect "/login"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Authentication handlers
|
27
|
+
#
|
28
|
+
get "/login" do
|
29
|
+
redirect "/" if @authenticated
|
30
|
+
haml :login
|
31
|
+
end
|
32
|
+
|
33
|
+
post "/login" do
|
34
|
+
hashed_pass = Digest::SHA1.hexdigest(params["pass"])
|
35
|
+
user = params["user"] == settings.username
|
36
|
+
pass = hashed_pass == settings.password
|
37
|
+
|
38
|
+
if settings.auth
|
39
|
+
totp = ROTP::TOTP.new(settings.auth)
|
40
|
+
auth = totp.verify(params["auth"])
|
41
|
+
else
|
42
|
+
auth = true
|
43
|
+
end
|
44
|
+
|
45
|
+
if !auth
|
46
|
+
@error = "Auth code is incorrect"
|
47
|
+
haml :login
|
48
|
+
elsif user && pass && auth
|
49
|
+
token = Digest::SHA1.hexdigest(
|
50
|
+
"#{params["user"]}#{hashed_pass}#{settings.auth}"
|
51
|
+
)
|
52
|
+
response.set_cookie("auth", value: token,
|
53
|
+
secure: settings.enable_ssl,
|
54
|
+
expires: (Date.today >> 1).to_time,
|
55
|
+
httponly: true)
|
56
|
+
|
57
|
+
redirect "/"
|
58
|
+
else
|
59
|
+
@error = "Invalid username/password"
|
60
|
+
haml :login
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
get "/logout" do
|
65
|
+
response.set_cookie("auth", nil)
|
66
|
+
redirect "/login"
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Wiki proxy
|
71
|
+
#
|
72
|
+
def get_uri
|
73
|
+
capture = URI::decode(params[:captures][0])
|
74
|
+
base = settings.url
|
75
|
+
if settings.url[-1] != '/'
|
76
|
+
base += '/'
|
77
|
+
end
|
78
|
+
|
79
|
+
uri = URI.parse("#{base}#{URI::encode(capture)}")
|
80
|
+
end
|
81
|
+
|
82
|
+
put /\/(.*)/ do
|
83
|
+
uri = get_uri
|
84
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
85
|
+
|
86
|
+
req = Net::HTTP::Put.new(uri.request_uri, initheader = { "Content-Type" => "application/json"})
|
87
|
+
request.body.rewind
|
88
|
+
req.body = request.body.read
|
89
|
+
|
90
|
+
resp = http.request(req)
|
91
|
+
|
92
|
+
status resp.code
|
93
|
+
headers({"Etag" => resp["Etag"]})
|
94
|
+
resp.body
|
95
|
+
end
|
96
|
+
|
97
|
+
delete /\/(.*)/ do
|
98
|
+
uri = get_uri
|
99
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
100
|
+
|
101
|
+
req = Net::HTTP::Delete.new(uri.request_uri)
|
102
|
+
resp = http.request(req)
|
103
|
+
|
104
|
+
status resp.code
|
105
|
+
resp.body
|
106
|
+
end
|
107
|
+
|
108
|
+
get /\/(.*)/ do
|
109
|
+
uri = get_uri
|
110
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
111
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
112
|
+
|
113
|
+
http.request(req).body
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title wiki - login
|
5
|
+
:css
|
6
|
+
body {
|
7
|
+
font-family: 'Helvetica';
|
8
|
+
color: #111;
|
9
|
+
font-weight: 300;
|
10
|
+
}
|
11
|
+
.box {
|
12
|
+
width: 400px;
|
13
|
+
position: fixed;
|
14
|
+
top: 50%;
|
15
|
+
left: 50%;
|
16
|
+
transform: translate(-50%, -80%);
|
17
|
+
background: #EEE;
|
18
|
+
padding: 15px;
|
19
|
+
box-sizing: border-box;
|
20
|
+
}
|
21
|
+
h1 {
|
22
|
+
margin: 0px 0px 10px 0px;
|
23
|
+
font-weight: 300;
|
24
|
+
font-size: 24px;
|
25
|
+
}
|
26
|
+
input {
|
27
|
+
display: block;
|
28
|
+
width: 100%;
|
29
|
+
margin-bottom: 5px;
|
30
|
+
font-family: 'Helvetica';
|
31
|
+
font-weight: 300;
|
32
|
+
font-size: 18px;
|
33
|
+
padding: 5px;
|
34
|
+
box-sizing: border-box;
|
35
|
+
outline: none;
|
36
|
+
}
|
37
|
+
|
38
|
+
input[type=submit] {
|
39
|
+
background: #00B4FF;
|
40
|
+
border: 0px;
|
41
|
+
color: #FFF;
|
42
|
+
margin-bottom: 0px;
|
43
|
+
}
|
44
|
+
|
45
|
+
%body
|
46
|
+
.box
|
47
|
+
%h1 Login
|
48
|
+
- if @error
|
49
|
+
%p= @error
|
50
|
+
|
51
|
+
%form{action:'/login', method: 'POST'}
|
52
|
+
%input{type: 'text', placeholder: 'Username', name: 'user'}
|
53
|
+
%input{type: 'password', placeholder: 'Password', name: 'pass'}
|
54
|
+
- if settings.auth
|
55
|
+
%input{type: 'number', placeholder: 'Auth code', name: 'auth'}
|
56
|
+
%input{type: 'submit', value: 'Login'}
|
data/lib/twproxy.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'twproxy/proxy'
|
data/twproxy.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "twproxy"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.date = "2016-04-09"
|
5
|
+
s.summary = "TiddlyWiki Proxy"
|
6
|
+
s.description = "An authenticated proxy for TiddlyWiki"
|
7
|
+
s.authors = ["Steve Gattuso"]
|
8
|
+
s.email = "steve@stevegattuso.me"
|
9
|
+
s.files = ["lib/twproxy.rb"]
|
10
|
+
s.files = %w(Gemfile LICENSE README.md twproxy.gemspec) + Dir['lib/**/*', 'bin/*']
|
11
|
+
s.license = "MIT"
|
12
|
+
s.executables << 'twproxy'
|
13
|
+
|
14
|
+
s.add_dependency 'sinatra', '~> 1.4'
|
15
|
+
s.add_dependency 'haml', '~> 4.0'
|
16
|
+
s.add_dependency 'rotp', '~> 2.1'
|
17
|
+
end
|
18
|
+
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: twproxy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steve Gattuso
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sinatra
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: haml
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rotp
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.1'
|
55
|
+
description: An authenticated proxy for TiddlyWiki
|
56
|
+
email: steve@stevegattuso.me
|
57
|
+
executables:
|
58
|
+
- twproxy
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- Gemfile
|
63
|
+
- LICENSE
|
64
|
+
- README.md
|
65
|
+
- bin/twproxy
|
66
|
+
- lib/twproxy.rb
|
67
|
+
- lib/twproxy/proxy.rb
|
68
|
+
- lib/twproxy/views/login.haml
|
69
|
+
- twproxy.gemspec
|
70
|
+
homepage:
|
71
|
+
licenses:
|
72
|
+
- MIT
|
73
|
+
metadata: {}
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 2.6.3
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: TiddlyWiki Proxy
|
94
|
+
test_files: []
|