Initial commit
This commit is contained in:
commit
0d15277178
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
config.yml
|
9
Gemfile
Normal file
9
Gemfile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
source "http://rubygems.org"
|
||||||
|
|
||||||
|
gem "sinatra"
|
||||||
|
gem "sinatra-contrib"
|
||||||
|
gem "riak-client"
|
||||||
|
|
||||||
|
group :test do
|
||||||
|
gem 'purdytest', :require => false
|
||||||
|
end
|
43
Gemfile.lock
Normal file
43
Gemfile.lock
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
GEM
|
||||||
|
remote: http://rubygems.org/
|
||||||
|
specs:
|
||||||
|
backports (2.3.0)
|
||||||
|
beefcake (0.3.7)
|
||||||
|
builder (3.0.0)
|
||||||
|
eventmachine (0.12.10)
|
||||||
|
i18n (0.6.0)
|
||||||
|
minitest (2.10.0)
|
||||||
|
multi_json (1.0.4)
|
||||||
|
purdytest (1.0.0)
|
||||||
|
minitest (~> 2.2)
|
||||||
|
rack (1.3.5)
|
||||||
|
rack-protection (1.1.4)
|
||||||
|
rack
|
||||||
|
rack-test (0.6.1)
|
||||||
|
rack (>= 1.0)
|
||||||
|
riak-client (1.0.0)
|
||||||
|
beefcake (~> 0.3.7)
|
||||||
|
builder (>= 2.1.2)
|
||||||
|
i18n (>= 0.4.0)
|
||||||
|
multi_json (~> 1.0.0)
|
||||||
|
sinatra (1.3.1)
|
||||||
|
rack (>= 1.3.4, ~> 1.3)
|
||||||
|
rack-protection (>= 1.1.2, ~> 1.1)
|
||||||
|
tilt (>= 1.3.3, ~> 1.3)
|
||||||
|
sinatra-contrib (1.3.1)
|
||||||
|
backports (>= 2.0)
|
||||||
|
eventmachine
|
||||||
|
rack-protection
|
||||||
|
rack-test
|
||||||
|
sinatra (~> 1.3.0)
|
||||||
|
tilt (~> 1.3)
|
||||||
|
tilt (1.3.3)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
purdytest
|
||||||
|
riak-client
|
||||||
|
sinatra
|
||||||
|
sinatra-contrib
|
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Liquor Cabinet
|
||||||
|
|
||||||
|
Liquor Cabinet is where Frank stores all his stuff. It's a
|
||||||
|
remoteStorage-compatible storage provider API, based on Sinatra and currently
|
||||||
|
using Riak as backend. You can use it on its own, or e.g. mount it from a Rails
|
||||||
|
application.
|
||||||
|
|
||||||
|
It's merely implementing the storage API, not including the Webfinger and Oauth
|
||||||
|
parts of remoteStorage. You have to set the authorization keys/values in the
|
||||||
|
database yourself.
|
||||||
|
|
||||||
|
If you have any questions about this thing, drop by #unhosted on Freenode, and
|
||||||
|
we'll happily answer them.
|
5
Rakefile
Normal file
5
Rakefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
require 'rake/testtask'
|
||||||
|
|
||||||
|
Rake::TestTask.new do |t|
|
||||||
|
t.pattern = 'spec/**/*_spec.rb'
|
||||||
|
end
|
7
config.ru
Normal file
7
config.ru
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
require 'rubygems'
|
||||||
|
require 'bundler'
|
||||||
|
|
||||||
|
Bundler.require
|
||||||
|
|
||||||
|
require './liquor_cabinet'
|
||||||
|
run LiquorCabinet
|
11
config.yml.example
Normal file
11
config.yml.example
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
development:
|
||||||
|
riak:
|
||||||
|
host: localhost
|
||||||
|
http_port: 8098
|
||||||
|
|
||||||
|
test:
|
||||||
|
riak:
|
||||||
|
host: localhost
|
||||||
|
http_port: 8098
|
||||||
|
|
||||||
|
production:
|
43
lib/remote_storage/riak.rb
Normal file
43
lib/remote_storage/riak.rb
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
require "riak"
|
||||||
|
|
||||||
|
module RemoteStorage
|
||||||
|
module Riak
|
||||||
|
|
||||||
|
def authorize_request(user, category, token)
|
||||||
|
return true if category == "public" && env["REQUEST_METHOD"] == "GET"
|
||||||
|
|
||||||
|
client = ::Riak::Client.new(settings.riak_config)
|
||||||
|
categories = client.bucket("authorizations").get("#{user}:#{token}").data
|
||||||
|
|
||||||
|
halt 403 unless categories.include?(category)
|
||||||
|
rescue ::Riak::HTTPFailedRequest
|
||||||
|
halt 403
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_data(user, category, key)
|
||||||
|
client = ::Riak::Client.new(settings.riak_config)
|
||||||
|
client.bucket("user_data").get("#{user}:#{category}:#{key}").data
|
||||||
|
rescue ::Riak::HTTPFailedRequest
|
||||||
|
halt 404
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_data(user, category, key, data)
|
||||||
|
client = ::Riak::Client.new(settings.riak_config)
|
||||||
|
object = client.bucket("user_data").new("#{user}:#{category}:#{key}")
|
||||||
|
object.content_type = "text/plain"
|
||||||
|
object.data = data
|
||||||
|
object.store
|
||||||
|
rescue ::Riak::HTTPFailedRequest
|
||||||
|
halt 422
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_data(user, category, key)
|
||||||
|
client = ::Riak::Client.new(settings.riak_config)
|
||||||
|
riak_response = client.bucket("user_data").delete("#{user}:#{category}:#{key}")
|
||||||
|
halt riak_response[:code]
|
||||||
|
rescue ::Riak::HTTPFailedRequest
|
||||||
|
halt 404
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
26
liquor-cabinet.gemspec
Normal file
26
liquor-cabinet.gemspec
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
lib = File.expand_path('../lib/', __FILE__)
|
||||||
|
$:.unshift lib unless $:.include?(lib)
|
||||||
|
|
||||||
|
require 'bundler/version'
|
||||||
|
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "liquor-cabinet"
|
||||||
|
s.version = "0.0.1"
|
||||||
|
s.platform = Gem::Platform::RUBY
|
||||||
|
s.authors = ["Sebastian Kippe"]
|
||||||
|
s.email = ["sebastian@5apps.com"]
|
||||||
|
s.homepage = ""
|
||||||
|
s.summary = ""
|
||||||
|
s.description = ""
|
||||||
|
|
||||||
|
s.required_rubygems_version = ">= 1.3.6"
|
||||||
|
|
||||||
|
s.add_dependency('sinatra')
|
||||||
|
s.add_dependency('sinatra-contrib')
|
||||||
|
s.add_dependency('riak-client')
|
||||||
|
|
||||||
|
s.files = Dir.glob("{bin,lib}/**/*") + Dir['*.rb']
|
||||||
|
# s.executables = ['config.ru']
|
||||||
|
s.require_paths << '.'
|
||||||
|
end
|
51
liquor-cabinet.rb
Normal file
51
liquor-cabinet.rb
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
$LOAD_PATH << File.join(File.expand_path(File.dirname(__FILE__)), 'lib')
|
||||||
|
|
||||||
|
require "json"
|
||||||
|
require "sinatra/base"
|
||||||
|
require "sinatra/reloader"
|
||||||
|
require "remote_storage/riak"
|
||||||
|
|
||||||
|
class LiquorCabinet < Sinatra::Base
|
||||||
|
|
||||||
|
include RemoteStorage::Riak
|
||||||
|
|
||||||
|
configure :development do
|
||||||
|
register Sinatra::Reloader
|
||||||
|
enable :logging
|
||||||
|
end
|
||||||
|
|
||||||
|
configure :development, :test, :production do
|
||||||
|
config = File.read(File.expand_path('config.yml', File.dirname(__FILE__)))
|
||||||
|
riak_config = YAML.load(config)[ENV['RACK_ENV']]['riak'].symbolize_keys
|
||||||
|
set :riak_config, riak_config
|
||||||
|
end
|
||||||
|
|
||||||
|
before "/:user/:category/:key" do
|
||||||
|
@user, @category, @key = params[:user], params[:category], params[:key]
|
||||||
|
token = env["HTTP_AUTHORIZATION"] ? env["HTTP_AUTHORIZATION"].split(" ")[1] : ""
|
||||||
|
|
||||||
|
authorize_request(@user, @category, token)
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/ohai" do
|
||||||
|
"Ohai."
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/headers" do
|
||||||
|
env["HTTP_AUTHORIZATION"]
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/:user/:category/:key" do
|
||||||
|
get_data(@user, @category, @key)
|
||||||
|
end
|
||||||
|
|
||||||
|
put "/:user/:category/:key" do
|
||||||
|
data = request.body.read
|
||||||
|
put_data(@user, @category, @key, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
delete "/:user/:category/:key" do
|
||||||
|
delete_data(@user, @category, @key)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
137
spec/app_spec.rb
Normal file
137
spec/app_spec.rb
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
ENV["RACK_ENV"] = "test"
|
||||||
|
require_relative "spec_helper"
|
||||||
|
|
||||||
|
describe "App" do
|
||||||
|
include Rack::Test::Methods
|
||||||
|
include RemoteStorage::Riak
|
||||||
|
|
||||||
|
def app
|
||||||
|
LiquorCabinet
|
||||||
|
end
|
||||||
|
|
||||||
|
def storage_client
|
||||||
|
::Riak::Client.new(settings.riak_config)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should say hello" do
|
||||||
|
get "/ohai"
|
||||||
|
assert last_response.ok?
|
||||||
|
last_response.body.must_include "Ohai."
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return 404 on non-existing routes" do
|
||||||
|
get "/myunclesam"
|
||||||
|
last_response.status.must_equal 404
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET public data" do
|
||||||
|
before do
|
||||||
|
object = storage_client.bucket("user_data").new("jimmy:public:foo")
|
||||||
|
object.content_type = "text/plain"
|
||||||
|
object.data = "some text data"
|
||||||
|
object.store
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
storage_client.bucket("user_data").delete("jimmy:public:foo")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the value on all get requests" do
|
||||||
|
get "/jimmy/public/foo"
|
||||||
|
|
||||||
|
last_response.status.must_equal 200
|
||||||
|
last_response.body.must_equal "some text data"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "private data" do
|
||||||
|
before do
|
||||||
|
object = storage_client.bucket("user_data").new("jimmy:documents:foo")
|
||||||
|
object.content_type = "text/plain"
|
||||||
|
object.data = "some private text data"
|
||||||
|
object.store
|
||||||
|
|
||||||
|
auth = storage_client.bucket("authorizations").new("jimmy:123")
|
||||||
|
auth.data = ["documents", "public"]
|
||||||
|
auth.store
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
storage_client.bucket("user_data").delete("jimmy:documents:foo")
|
||||||
|
storage_client.bucket("authorizations").delete("jimmy:123")
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET" do
|
||||||
|
it "returns the value" do
|
||||||
|
header "Authorization", "Bearer 123"
|
||||||
|
get "/jimmy/documents/foo"
|
||||||
|
|
||||||
|
last_response.status.must_equal 200
|
||||||
|
last_response.body.must_equal "some private text data"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET nonexisting key" do
|
||||||
|
it "returns a 404" do
|
||||||
|
header "Authorization", "Bearer 123"
|
||||||
|
get "/jimmy/documents/somestupidkey"
|
||||||
|
|
||||||
|
last_response.status.must_equal 404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PUT" do
|
||||||
|
it "saves the value" do
|
||||||
|
header "Authorization", "Bearer 123"
|
||||||
|
put "/jimmy/documents/bar", "another text"
|
||||||
|
|
||||||
|
last_response.status.must_equal 200
|
||||||
|
storage_client.bucket("user_data").get("jimmy:documents:bar").data.must_equal "another text"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE" do
|
||||||
|
it "removes the key" do
|
||||||
|
header "Authorization", "Bearer 123"
|
||||||
|
delete "/jimmy/documents/foo"
|
||||||
|
|
||||||
|
last_response.status.must_equal 204
|
||||||
|
lambda {storage_client.bucket("user_data").get("jimmy:documents:foo")}.must_raise Riak::HTTPFailedRequest
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "unauthorized access" do
|
||||||
|
before do
|
||||||
|
auth = storage_client.bucket("authorizations").new("jimmy:123")
|
||||||
|
auth.data = ["documents", "public"]
|
||||||
|
auth.store
|
||||||
|
|
||||||
|
header "Authorization", "Bearer 321"
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET" do
|
||||||
|
it "returns a 403" do
|
||||||
|
get "/jimmy/documents/foo"
|
||||||
|
|
||||||
|
last_response.status.must_equal 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PUT" do
|
||||||
|
it "returns a 403" do
|
||||||
|
put "/jimmy/documents/foo", "some text"
|
||||||
|
|
||||||
|
last_response.status.must_equal 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE" do
|
||||||
|
it "returns a 403" do
|
||||||
|
delete "/jimmy/documents/foo"
|
||||||
|
|
||||||
|
last_response.status.must_equal 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
spec/spec_helper.rb
Normal file
15
spec/spec_helper.rb
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
require 'rubygems'
|
||||||
|
require 'bundler'
|
||||||
|
Bundler.require
|
||||||
|
|
||||||
|
require_relative '../liquor_cabinet'
|
||||||
|
require 'minitest/autorun'
|
||||||
|
require 'rack/test'
|
||||||
|
require 'purdytest'
|
||||||
|
require 'riak'
|
||||||
|
|
||||||
|
set :environment, :test
|
||||||
|
|
||||||
|
config = File.read(File.expand_path('../config.yml', File.dirname(__FILE__)))
|
||||||
|
riak_config = YAML.load(config)[ENV['RACK_ENV']]['riak'].symbolize_keys
|
||||||
|
set :riak_config, riak_config
|
Loading…
x
Reference in New Issue
Block a user