Initial commit

This commit is contained in:
Basti 2012-02-27 16:32:12 +01:00
commit 0d15277178
12 changed files with 361 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
config.yml

9
Gemfile Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,5 @@
require 'rake/testtask'
Rake::TestTask.new do |t|
t.pattern = 'spec/**/*_spec.rb'
end

7
config.ru Normal file
View File

@ -0,0 +1,7 @@
require 'rubygems'
require 'bundler'
Bundler.require
require './liquor_cabinet'
run LiquorCabinet

11
config.yml.example Normal file
View File

@ -0,0 +1,11 @@
development:
riak:
host: localhost
http_port: 8098
test:
riak:
host: localhost
http_port: 8098
production:

View 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
View 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
View 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
View 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
View 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