twproxy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Screencast](http://i.imgur.com/NmGf4iE.gif)
|
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: []
|