desviar 0.0.15 → 0.0.16

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.
data/README.md CHANGED
@@ -2,20 +2,31 @@
2
2
 
3
3
  This is a Ruby-based app server built on Sinatra to create
4
4
  preauthorized time-limited, random URIs used in devops deployment
5
- scripts or in web applications such as confirmation emails.
5
+ scripts or in web applications such as confirmation emails. Your
6
+ scenario is that you have a database, repository or webserver
7
+ (possibly behind a firewall) that needs to stay both hidden and
8
+ secure, but you need to provide a means for a script to invoke an API
9
+ call or for a remote user to click a direct link to fetch a specific
10
+ item from its hidden source without presenting credentials.
6
11
 
7
12
  It operates similarly to TinyURL or the Amazon S3 temporary-URI
8
- feature: provide the tool with the URI to an existing secure resource,
9
- specify a number of seconds you want to authorize references to it,
10
- and you'll get back a temporary URI good for that amount of time.
11
-
12
- You can set it up on a DMZ network or in the cloud behind an
13
+ feature: provide the tool with the URI and credentials to an existing
14
+ secure resource, specify a number of seconds you want to authorize
15
+ references to it, and you'll get back a temporary URI good for that
16
+ amount of time. An analogy is the inexpensive key-card issued by a
17
+ hotel's desk clerk: to access secure content in the room, you first
18
+ need to present your credit-card credentials; your room key is all you
19
+ need thereafter, at least until the key expires. The hotel remains
20
+ secure even if you keep the key after checkout.
21
+
22
+ You can set up desviar on a DMZ network or in the cloud behind an
13
23
  iptables/nginx configuration to provide whatever ACL restrictions you
14
24
  want, and you can reference any source URI (not just those stored on
15
- S3).
25
+ S3 or an equivalent service).
16
26
 
17
- Secure content is cached in memory (sqlite3) by default; for
18
- troubleshooting, you can store content in a file.
27
+ Secure content is encrypted and cached in memory (sqlite3) by default;
28
+ for troubleshooting, you can store content in a file and/or turn off
29
+ encryption.
19
30
 
20
31
  #### Installation ####
21
32
 
@@ -27,7 +38,7 @@ Clone this repo and perform the following:
27
38
  git clone https://github.com/instantlinux/desviar.git
28
39
  cd desviar
29
40
  cp config/config.rb.example config/config.rb
30
- sudo apt-get install -y make libsqlite3-dev ruby-dev
41
+ sudo apt-get install -y make g++ libsqlite3-dev ruby-dev
31
42
  # package names above may differ if not using Ubuntu
32
43
  sudo gem install bundler
33
44
  sudo bundle install
@@ -35,9 +46,9 @@ Clone this repo and perform the following:
35
46
  rackup -p 4567
36
47
 
37
48
  ##### From rubygems.org #####
38
- Invoke the following:
49
+ [![Gem Version](https://badge.fury.io/rb/desviar.png)](http://badge.fury.io/rb/desviar) Invoke the following:
39
50
 
40
- sudo apt-get install -y make libsqlite3-dev ruby-dev
51
+ sudo apt-get install -y make g++ libsqlite3-dev ruby-dev
41
52
  sudo gem install desviar
42
53
  wget https://raw.github.com/instantlinux/desviar/master/config.ru
43
54
  wget https://raw.github.com/instantlinux/desviar/master/config/config.rb.example
@@ -47,7 +58,7 @@ Invoke the following:
47
58
 
48
59
  #### Usage ####
49
60
 
50
- Default credential of [app](http://localhost:4567) is user _desviar_, pw _password_.
61
+ In your browser, the [app](http://localhost:4567)'s default credential upon installation is user _desviar_, pw _password_.
51
62
 
52
63
  Commands:
53
64
  * /create - generate a new pre-authenticated URI
@@ -56,19 +67,14 @@ Commands:
56
67
  * /link/nnn - retrieve details
57
68
  * /config - set runtime configuration
58
69
 
59
- For scripting, the list, link and config commands can be modified with a _/json_ suffix (e.g. _/config/json_) to generate json instead of html output.
60
-
61
- Here's an example of creating a new link via _curl_:
62
-
63
- curl --digest --user desviar:password http://localhost:4567/create \
64
- --data "redir_uri=http://localhost/test&expiration=1800&captcha=1&notes=testing"
70
+ For scripting, the list, link and config commands can be modified with a _/json_ suffix (e.g. _/config/json_) to generate json instead of html output. Script examples for Ruby and bash are provided in the [examples directory](https://github.com/instantlinux/desviar/tree/master/examples).
65
71
 
66
72
  Security notes:
67
73
  Consider moving the default database location from /dev/shm/desviar, and set its permissions to 0600. You can modify config.ru to direct log output to a different file.
68
74
 
69
75
  #### Features implemented ####
70
76
 
71
- - [x] HTTP digest authentication for user interface
77
+ - [x] HTTP digest authentication for client API and user interface
72
78
  - [ ] Parse htpasswd files to support multiple credentials
73
79
  - [x] Bypass authentication for generated URIs
74
80
  - [x] Basic HTTP authentication for remote URIs
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "dm-timestamps", ">= 1.2"
39
39
  spec.add_dependency "dm-validations", ">= 1.2"
40
40
  spec.add_dependency "multi_json", ">= 1.7"
41
+ spec.add_dependency "net-http-digest_auth", ">= 1.4"
41
42
  spec.add_dependency "rack-recaptcha", ">= 0.6"
42
43
  spec.add_dependency "rack-test", ">= 0.6"
43
44
  spec.add_dependency "sinatra", ">= 1.4"
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+ # API examples - Desviar::Client
3
+ #
4
+ # Created 1 Aug 2013
5
+ #
6
+ # Copyright 2013 Richard Braun
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+
14
+ # Usage:
15
+ # Start the server first (rackup -p 4567)
16
+ # Adjust credentials below if you've modified the server
17
+ # Invoke this program ./bash-client.sh
18
+
19
+ echo "========= Desviar::Client config =========="
20
+ curl --digest --user desviar:password -q http://localhost:4567/config/json
21
+ echo
22
+ echo "========= Desviar::Client create =========="
23
+ # In this example, create an item valid for 5 seconds and another valid for
24
+ # 10 minutes to demonstrate cleanup after 6 seconds. The notes field is
25
+ # an arbitrary payload which can be used for whatever tracking purposes
26
+ # your application requires.
27
+ id=`curl -q --digest --user desviar:password http://localhost:4567/create \
28
+ --data "redir_uri=https://rubygems.org/gems/desviar&expiration=5&captcha=1&notes=tracking-item" \
29
+ --location |grep ID:|egrep -o [0-9]+`
30
+ curl --digest --user desviar:password -q http://localhost:4567/link/json/$id
31
+ echo
32
+ id=`curl -q --digest --user desviar:password http://localhost:4567/create \
33
+ --data "redir_uri=https://rubygems.org/gems/desviar&expiration=600&notes=item%202" \
34
+ --location |grep ID:|egrep -o [0-9]+`
35
+ curl --digest --user desviar:password -q http://localhost:4567/link/json/$id
36
+ echo
37
+ echo "========= Desviar::Client list ============"
38
+ curl --digest --user desviar:password -q http://localhost:4567/list/json
39
+ echo
40
+ echo "========= Desviar::Client clean ============"
41
+ sleep 6
42
+ curl --digest --user desviar:password -q http://localhost:4567/clean
43
+ curl --digest --user desviar:password -q http://localhost:4567/list/json
44
+ echo
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+ # API examples - Desviar::Client
3
+ #
4
+ # Created 1 Aug 2013
5
+ #
6
+ # Copyright 2013 Richard Braun
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+
14
+ # Usage:
15
+ # Start the server first (rackup -p 4567)
16
+ # Adjust credentials below if you've modified the server
17
+ # Invoke this program ./ruby-client.rb
18
+
19
+ require 'desviar/client'
20
+ require 'pp'
21
+
22
+ obj = Desviar::Client.new 'desviar', 'password'
23
+ puts "Starting Desviar::Client for server #{obj.server_uri} as user #{obj.user_name}"
24
+
25
+ puts "========= Desviar::Client config =========="
26
+ puts "dbencrypt is #{obj.config_item 'dbencrypt'}"
27
+ puts "debug is #{obj.config_item :debug}"
28
+ puts "Full config hash is:"
29
+ pp obj.config
30
+
31
+ puts "========= Desviar::Client create =========="
32
+ # Can specify option names using symbols/strings; values must be strings
33
+
34
+ # The notes field is an arbitrary payload which can be used for
35
+ # whatever tracking purposes your application requires.
36
+
37
+ # Example 1: create an item valid for 5 seconds to demonstrate
38
+ # cleanup after 6 seconds. Fetch will invoke captcha (get credentials
39
+ # from Google for a "real" test)
40
+ link = obj.create "https://rubygems.org/gems/desviar", 5, {
41
+ :captcha => 1.to_s,
42
+ "notes" => "tracking-item" }
43
+ puts link['temp_uri'].inspect
44
+ puts obj.fetch link['temp_uri']
45
+
46
+ # Example 2: same as #1 except without captcha
47
+ link = obj.create "https://rubygems.org/gems/desviar", 5, {
48
+ "notes" => "tracking example 2" }
49
+ puts link['temp_uri'].inspect
50
+ puts obj.fetch link['temp_uri']
51
+
52
+ # Example 3: valid for 10 minutes
53
+ pp obj.create "https://rubygems.org/gems/desviar", 600, {
54
+ "notes" => "example 3" }
55
+
56
+ puts "========= Desviar::Client list ============"
57
+ pp obj.list
58
+ begin
59
+ pp obj.list_item 999
60
+ rescue ArgumentError
61
+ puts "Handling expected error for item 999"
62
+ end
63
+
64
+ puts "========= Desviar::Client clean ============"
65
+ sleep 6
66
+ obj.clean
67
+ pp obj.list
@@ -3,7 +3,8 @@ require 'webrick/httpauth/htpasswd'
3
3
  module Desviar::Auth
4
4
 
5
5
  def self.htpasswd
6
- @htpasswd ||= Htpasswd.new(git.path_to("htpasswd"))
6
+ # @htpasswd ||= Htpasswd.new(git.path_to("htpasswd"))
7
+ @htpasswd ||= Htpasswd.new('.htpasswd')
7
8
  end
8
9
 
9
10
  def self.authentication
@@ -26,7 +27,8 @@ module Desviar::Auth
26
27
  end
27
28
  end
28
29
 
29
- def self.unauthorized!(realm = Desviar::info)
30
+ # def self.unauthorized!(realm = Desviar::info)
31
+ def self.unauthorized!(realm = 'desviar-realm')
30
32
  headers "WWW-Authenticate" => %(Basic realm="#{realm}")
31
33
  throw :halt, [ 401, "Authorization Required" ]
32
34
  end
@@ -75,8 +75,17 @@ module Desviar
75
75
  http.use_ssl = params[:redir_uri].index('https') == 0
76
76
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
77
77
  req = Net::HTTP::Get.new(object.request_uri)
78
- req.basic_auth params[:remoteuser], params[:remotepw] if params[:remoteuser] != ''
79
- response = http.request(req)
78
+ if params[:remoteuser] != ''
79
+ req.basic_auth params[:remoteuser], params[:remotepw]
80
+ end
81
+ begin
82
+ response = http.request(req)
83
+ rescue Errno::ECONNREFUSED
84
+ error 401
85
+ end
86
+ if response.code.to_i != 200
87
+ error response.code.to_i
88
+ end
80
89
  if !$config[:dbencrypt]
81
90
  @desviar[:content] = response.body[0, $config[:contentmax]]
82
91
  else
@@ -204,6 +213,7 @@ module Desviar
204
213
  def self.new(*)
205
214
  # TODO: htpasswd parsing
206
215
  # Desviar::Auth::authenticate!
216
+ # @auth.call()
207
217
  app = Rack::Auth::Digest::MD5.new(super) do |username|
208
218
  {$config[:adminuser] => $config[:adminpw]}[username]
209
219
  end
@@ -212,6 +222,22 @@ module Desviar
212
222
  app
213
223
  end
214
224
 
225
+ =begin
226
+ class Mytest
227
+ def initialize(app)
228
+ # Desviar::Public::log "Initializing auth from .htpasswd"
229
+ @obj = Rack::Auth::Basic.new(app) do |username, password|
230
+ unless File.exist?('../config/.htpasswd')
231
+ raise PasswordFileNotFound.new("#{file} is not found. Please create it with htpasswd")
232
+ end
233
+ htpasswd = WEBrick::HTTPAuth::Htpasswd.new(file)
234
+ crypted = htpasswd.get_passwd(nil, user, false)
235
+ crypted == pass.crypt(crypted) if crypted
236
+ end
237
+ end
238
+ end
239
+ =end
240
+
215
241
  end
216
242
 
217
243
  #############################################
@@ -0,0 +1,134 @@
1
+ # Client functions for Desviar main object
2
+ #
3
+ # Copyright 2013 Richard Braun
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ require 'json'
12
+ require 'uri'
13
+ require 'net/http'
14
+ require 'net/http/digest_auth'
15
+
16
+ module Desviar
17
+ class Client
18
+ ##
19
+ # Client functions for Desviar main object
20
+
21
+ # Create new client
22
+ def initialize(user_name, private_key, server_uri = 'http://localhost:4567')
23
+ @server_uri = server_uri
24
+ @user_name = user_name
25
+ @private_key = private_key
26
+ end
27
+
28
+ # Return server's URI
29
+ def server_uri()
30
+ @server_uri
31
+ end
32
+
33
+ # Return username
34
+ def user_name()
35
+ @user_name
36
+ end
37
+
38
+ # Fetch configuration (as a hash)
39
+ def config
40
+ JSON.parse get("config/json")
41
+ end
42
+
43
+ # Fetch a single configuration item
44
+ def config_item(item)
45
+ cfg = JSON.parse get("config/json")
46
+ raise ArgumentError, "Invalid item #{item}" if !cfg.has_key?(item.to_s)
47
+ cfg[item.to_s]
48
+ end
49
+
50
+ # Fetch list of stored redirects
51
+ def list
52
+ JSON.parse get("list/json")
53
+ end
54
+
55
+ # Fetch meta information about a particular redirect
56
+ def list_item(id)
57
+ JSON.parse get("link/json/#{id}")
58
+ end
59
+
60
+ # Clean out expired entries
61
+ def clean
62
+ get("clean")
63
+ end
64
+
65
+ # Create a new redirect
66
+ def create(uri, expiration = 900, opts = {})
67
+ opts[:captcha_button] = "Proceed" if !opts.has_key?(:captcha_button) && !opts.has_key?("captcha_button")
68
+ newlink = post("create", { :redir_uri => uri, :expiration => expiration.to_s }.merge(opts))
69
+ JSON.parse get("link/json/#{newlink.split('/').last}")
70
+ end
71
+
72
+ # Fetch content
73
+ def fetch(temp_uri)
74
+ get("desviar/#{temp_uri}", false)
75
+ end
76
+
77
+ private
78
+
79
+ # Internal function: raw, digest-authenticated HTTP get from server
80
+ def get(path, need_auth = true)
81
+ uri = URI.parse "#{@server_uri}/#{path}"
82
+ uri.user = @user_name
83
+ uri.password = @private_key
84
+
85
+ http = Net::HTTP.new uri.host, uri.port
86
+ http.use_ssl = @server_uri.index('https') == 0
87
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
88
+ req = Net::HTTP::Get.new uri.request_uri
89
+ response = http.request req
90
+ if need_auth
91
+ auth = Net::HTTP::DigestAuth.new.auth_header(
92
+ uri, response['www-authenticate'], 'GET')
93
+ req = Net::HTTP::Get.new uri.request_uri
94
+ req.add_field 'Authorization', auth
95
+ response = http.request req
96
+ end
97
+ case response.code.to_i
98
+ when 200
99
+ response.body
100
+ when 301..302
101
+ response.response['Location']
102
+ else
103
+ raise ArgumentError, "#{path} return status #{response.code}"
104
+ end
105
+ end
106
+
107
+ # Internal function: HTTP post
108
+ def post(path, params)
109
+ uri = URI.parse "#{@server_uri}/#{path}"
110
+ uri.user = @user_name
111
+ uri.password = @private_key
112
+
113
+ http = Net::HTTP.new uri.host, uri.port
114
+ http.use_ssl = @server_uri.index('https') == 0
115
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
116
+ req = Net::HTTP::Get.new uri.request_uri
117
+ response = http.request req
118
+ auth = Net::HTTP::DigestAuth.new.auth_header(
119
+ uri, response['www-authenticate'], 'POST')
120
+ req = Net::HTTP::Post.new uri.request_uri
121
+ req.add_field 'Authorization', auth
122
+ req.set_form_data params
123
+ response = http.request req
124
+ case response.code.to_i
125
+ when 200
126
+ response.body
127
+ when 303
128
+ response.response['Location']
129
+ else
130
+ raise ArgumentError, "#{path} return status #{response.code}"
131
+ end
132
+ end
133
+ end
134
+ end
@@ -10,6 +10,9 @@
10
10
 
11
11
  module Desviar
12
12
  module Model
13
+ ##
14
+ # Data model for Desviar main object
15
+
13
16
  class Main
14
17
  include DataMapper::Resource
15
18
 
Binary file
@@ -1,7 +1,7 @@
1
1
  module Desviar
2
- VERSION = "0.0.15"
3
- RELEASE = "2013-07-30"
4
- TIMESTAMP = "2013-07-29 08:27:33 -07:00"
2
+ VERSION = "0.0.16"
3
+ RELEASE = "2013-08-03"
4
+ TIMESTAMP = "2013-08-03 09:12:27 -07:00"
5
5
 
6
6
  def self.info
7
7
  "#{name} v#{VERSION} (#{RELEASE})"
@@ -1,13 +1,14 @@
1
1
  <div class="desviar">
2
2
  <a href="/desviar/<%= @desviar.temp_uri[0, @desviar.temp_uri.length - $config[:urisuffix].length] %>">
3
- https:<%= settings.bind %>:<%= settings.port %>/desviar/<%= @desviar.temp_uri[0, @desviar.temp_uri.length - $config[:urisuffix].length] %></a><br><hr>
3
+ http://<%= settings.bind %>:<%= settings.port %>/desviar/<%= @desviar.temp_uri[0, @desviar.temp_uri.length - $config[:urisuffix].length] %></a><br><hr>
4
4
  <h2><a href="<%= @desviar.redir_uri %>"><%= @desviar.redir_uri %></a></h2>
5
5
  <div class="sbody" id="box">
6
6
  <%= @desviar.formatted_notes %>
7
7
  </div>
8
8
  <div class="sdate">
9
- Created <%= @desviar.created_at.strftime("%D at %T %Z") %> <%= ENV['REMOTE_USER'] %><br>
10
- Expires <%= @desviar.expires_at.strftime("%D at %T %Z") %>
9
+ Created <%= @desviar.created_at.strftime("%D at %T %Z") %><br>
10
+ Expires <%= @desviar.expires_at.strftime("%D at %T %Z") %><br>
11
+ ID: <%= @desviar.id %>
11
12
  <%= @desviar.captcha ? "<br>CAPTCHA is required" : "" %>
12
13
  </div>
13
14
  <hr>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: desviar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.15
4
+ version: 0.0.16
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-30 00:00:00.000000000 Z
12
+ date: 2013-08-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dm-core
@@ -107,6 +107,22 @@ dependencies:
107
107
  - - ! '>='
108
108
  - !ruby/object:Gem::Version
109
109
  version: '1.7'
110
+ - !ruby/object:Gem::Dependency
111
+ name: net-http-digest_auth
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '1.4'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '1.4'
110
126
  - !ruby/object:Gem::Dependency
111
127
  name: rack-recaptcha
112
128
  requirement: !ruby/object:Gem::Requirement
@@ -222,9 +238,12 @@ files:
222
238
  - config.ru
223
239
  - config/config.rb.example
224
240
  - desviar.gemspec
241
+ - examples/bash-client.sh
242
+ - examples/ruby-client.rb
225
243
  - lib/auth.rb
226
244
  - lib/authorization.rb
227
245
  - lib/desviar.rb
246
+ - lib/desviar/client.rb
228
247
  - lib/encrypt.rb
229
248
  - lib/model.rb
230
249
  - lib/public/favicon.ico
@@ -239,7 +258,7 @@ files:
239
258
  homepage: http://github.com/instantlinux/desviar
240
259
  licenses: []
241
260
  post_install_message: ! "------------------------------------------------------------------------------\nDesviar
242
- v0.0.15\n\nTo configure, download from:\n https://raw.github.com/instantlinux/desviar/master/config/config.rb.example\ninto
261
+ v0.0.16\n\nTo configure, download from:\n https://raw.github.com/instantlinux/desviar/master/config/config.rb.example\ninto
243
262
  a new file config.rb and export DESVIAR_CONFIG=<path>/config.rb.\n\nThanks for using
244
263
  Desviar.\n------------------------------------------------------------------------------\n"
245
264
  rdoc_options: []