visor-auth 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,215 @@
1
+ require 'sinatra/base'
2
+ require 'json'
3
+ require 'uri'
4
+
5
+ module Visor
6
+ module Auth
7
+
8
+ # The VISoR Auth Server class. This class supports all users and groups management
9
+ # operations through the REST API implemented along the following routes.
10
+ #
11
+ class Server < Sinatra::Base
12
+ include Visor::Common::Exception
13
+ include Visor::Common::Config
14
+
15
+ # Configuration
16
+ #
17
+ configure do
18
+ backend_map = {'mongodb' => Visor::Auth::Backends::MongoDB,
19
+ 'mysql' => Visor::Auth::Backends::MySQL}
20
+
21
+ #TODO: catch error when Config does not found a key (like :visor_auth) in config file:
22
+ # /Users/joaodrp/workspace/visor/common/lib/common/config.rb:65:in `load_config': undefined method `merge' for nil:NilClass (NoMethodError)
23
+ conf = Visor::Common::Config.load_config(:visor_auth)
24
+ log = Visor::Common::Config.build_logger(:visor_auth)
25
+
26
+ DB = backend_map[conf[:backend].split(':').first].connect uri: conf[:backend]
27
+
28
+ disable :show_exceptions, :logging #, :protection
29
+ use Rack::CommonLogger, log
30
+ end
31
+
32
+ #configure :development do
33
+ #require 'sinatra/reloader'
34
+ #register Sinatra::Reloader
35
+ #end
36
+
37
+ # Helpers
38
+ #
39
+ helpers do
40
+ def json_error(code, message)
41
+ error code, {code: code, message: message}.to_json
42
+ end
43
+ end
44
+
45
+ # Filters
46
+ #
47
+ before do
48
+ @parse_opts = {symbolize_names: true}
49
+ content_type :json
50
+ end
51
+
52
+ # Routes
53
+ #
54
+
55
+ # @method get_users
56
+ # @overload get '/users'
57
+ #
58
+ # Get information about all registered users.
59
+ #
60
+ # { "users": [{
61
+ # "_id":<_id>,
62
+ # "access_key":<access_key>,
63
+ # "secret_key":<secret_key>,
64
+ # "email":<email>,
65
+ # "created_at":<creation timestamp>,
66
+ # "updated_at":<update timestamp>,
67
+ # }, ...]}
68
+ #
69
+ # The following options can be passed as query parameters.
70
+ #
71
+ # @param [String] access_key The user access_key.
72
+ # @param [String] email The user email address.
73
+ # @param [Date] created_at The image creation timestamp.
74
+ # @param [Date] updated_at The image update timestamp.
75
+ #
76
+ # @return [JSON] The registered users information.
77
+ #
78
+ # @raise [HTTP Error 404] If there is no registered users.
79
+ #
80
+ get '/users' do
81
+ begin
82
+ users = DB.get_users(params)
83
+ {users: users}.to_json
84
+ rescue NotFound => e
85
+ json_error 404, e.message
86
+ end
87
+ end
88
+
89
+ # @method get_user
90
+ # @overload get '/users/:access_key'
91
+ #
92
+ # Get information about a specific user.
93
+ #
94
+ # {"image": {
95
+ # "_id":<_id>,
96
+ # "access_key":<access_key>,
97
+ # "secret_key":<secret_key>,
98
+ # "email":<email>,
99
+ # "created_at":<creation timestamp>,
100
+ # "updated_at":<update timestamp>
101
+ # }}
102
+ #
103
+ # @param [String] access_key The wanted user access_key.
104
+ #
105
+ # @return [JSON] The user detailed information.
106
+ #
107
+ # @raise [HTTP Error 404] If user not found.
108
+ #
109
+ get '/users/:access_key' do |access_key|
110
+ begin
111
+ user = DB.get_user(access_key)
112
+ {user: user}.to_json
113
+ rescue NotFound => e
114
+ json_error 404, e.message
115
+ end
116
+ end
117
+
118
+ # @method post
119
+ # @overload post '/users'
120
+ #
121
+ # Registers a new user and returns its data.
122
+ #
123
+ # @param [JSON] http-body The user information.
124
+ #
125
+ # @return [JSON] The already created user detailed information.
126
+ #
127
+ # @raise [HTTP Error 400] User information validation errors.
128
+ # @raise [HTTP Error 404] User not found after registered.
129
+ # @raise [HTTP Error 409] access_key was already taken.
130
+ #
131
+ post '/users' do
132
+ begin
133
+ info = JSON.parse(request.body.read, @parse_opts)
134
+ user = DB.post_user(info[:user])
135
+ {user: user}.to_json
136
+ rescue NotFound => e
137
+ json_error 404, e.message
138
+ rescue ArgumentError => e
139
+ json_error 400, e.message
140
+ rescue ConflictError => e
141
+ json_error 409, e.message
142
+ end
143
+ end
144
+
145
+ # @method put
146
+ # @overload put '/users/:access_key'
147
+ #
148
+ # Update an existing user information and return it.
149
+ #
150
+ # @param [String] access_key The wanted user access_key.
151
+ # @param [JSON] http-body The user information.
152
+ #
153
+ # @return [JSON] The already updated user detailed information.
154
+ #
155
+ # @raise [HTTP Error 400] User information validation errors.
156
+ # @raise [HTTP Error 404] User not found.
157
+ #
158
+ put '/users/:access_key' do |access_key|
159
+ begin
160
+ info = JSON.parse(request.body.read, @parse_opts)
161
+ user = DB.put_user(access_key, info[:user])
162
+ {user: user}.to_json
163
+ rescue NotFound => e
164
+ json_error 404, e.message
165
+ rescue ArgumentError => e
166
+ json_error 400, e.message
167
+ end
168
+ end
169
+
170
+ # @method delete
171
+ # @overload delete '/users/:access_key'
172
+ #
173
+ # Delete an user and returns its information.
174
+ #
175
+ # @param [String] access_key The wanted user access_key.
176
+ #
177
+ # @return [JSON] The already deleted user detailed information.
178
+ #
179
+ # @raise [HTTP Error 404] User not found.
180
+ #
181
+ delete '/users/:access_key' do |access_key|
182
+ begin
183
+ user = DB.delete_user(access_key)
184
+ {user: user}.to_json
185
+ rescue NotFound => e
186
+ json_error 404, e.message
187
+ end
188
+ end
189
+
190
+ # misc handlers: error, not_found, etc.
191
+ get "*" do
192
+ json_error 404, 'Invalid operation or path.'
193
+ end
194
+
195
+ put "*" do
196
+ json_error 404, 'Invalid operation or path.'
197
+ end
198
+
199
+ post "*" do
200
+ json_error 404, 'Invalid operation or path.'
201
+ end
202
+
203
+ delete "*" do
204
+ json_error 404, 'Invalid operation or path.'
205
+ end
206
+
207
+ error do
208
+ json_error 500, env['sinatra.error'].message
209
+ end
210
+
211
+ end
212
+ end
213
+ end
214
+
215
+
@@ -0,0 +1,5 @@
1
+ module Visor
2
+ module Auth
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
data/lib/visor-auth.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'visor-common'
2
+
3
+ $:.unshift File.expand_path('../../lib', __FILE__)
4
+ require 'auth/version'
5
+ require 'auth/backends/base'
6
+ require 'auth/backends/mongo_db'
7
+ require 'auth/backends/mysql_db'
8
+ require 'auth/server'
9
+ require 'auth/client'
10
+ require 'auth/cli'
11
+
12
+
@@ -0,0 +1,189 @@
1
+ require "spec_helper"
2
+ require "securerandom"
3
+
4
+ include Visor::Auth::Backends
5
+
6
+ describe Visor::Auth::Server do
7
+ let(:parse_opts) { {symbolize_names: true} }
8
+
9
+ let(:valid_post) { {user: {access_key: 'foo', email: 'foo@bar.com'}} }
10
+ let(:invalid_post) { {user: {access_key: 'foo'}} }
11
+
12
+ let(:valid_update) { {user: {email: 'foobar@bar.com'}} }
13
+ let(:invalid_update) { {user: {secret_key: 'this is not valid'}} }
14
+
15
+ inserted = []
16
+
17
+ def users_from(last_response)
18
+ response = JSON.parse(last_response.body, parse_opts)
19
+ response[:user] || response[:users]
20
+ end
21
+
22
+ def message_from(last_response)
23
+ JSON.parse(last_response.body, parse_opts)[:message]
24
+ end
25
+
26
+ def delete_all
27
+ get '/users'
28
+ users = users_from(last_response)
29
+ users.each { |user| delete "/users/#{user[:access_key]}" }
30
+ end
31
+
32
+ def generate_user
33
+ {user: {access_key: SecureRandom.hex(10), email: 'foo@bar.com'}}
34
+ end
35
+
36
+ before(:each) do
37
+ post '/users', generate_user.to_json
38
+ @valid_access_key = users_from(last_response)[:access_key]
39
+ inserted << @valid_access_key
40
+ end
41
+
42
+ after(:all) do
43
+ inserted.each { |access_key| delete "/users/#{access_key}" }
44
+ end
45
+
46
+ describe "GET on /users" do
47
+ before(:each) do
48
+ get '/users'
49
+ last_response.should be_ok
50
+ end
51
+
52
+ it "should return an array" do
53
+ get '/users'
54
+ users = users_from(last_response)
55
+ users.should be_a Array
56
+ end
57
+
58
+ it "should filter results" do
59
+ get '/users'
60
+ users = users_from(last_response)
61
+ get "/users?email=#{users.sample[:email]}"
62
+ users = users_from(last_response)
63
+ users.each { |u| u[:email].should == users.sample[:email] }
64
+ end
65
+
66
+ it "should raise an 404 error if no users were found" do
67
+ #delete_all
68
+ #get '/images'
69
+ #last_response.status.should == 404
70
+ #message_from(last_response).should match /no image found/
71
+ end
72
+ end
73
+
74
+ describe "GET on /users/:access_key" do
75
+ before(:each) do
76
+ get "/users/#{@valid_access_key}"
77
+ last_response.should be_ok
78
+ end
79
+
80
+ it "should return a hash with the user data" do
81
+ user = users_from(last_response)
82
+ user.should be_a Hash
83
+ user[:email].should be_a String
84
+ end
85
+
86
+ it "should raise a 404 error if user not found" do
87
+ get "/users/fake_id"
88
+ last_response.status.should == 404
89
+ message_from(last_response).should match /No user found/
90
+ end
91
+ end
92
+
93
+ describe "POST on /users" do
94
+ it "should create a new user and return its data" do
95
+ post '/users', generate_user.to_json
96
+ last_response.should be_ok
97
+ user = users_from(last_response)
98
+ user[:access_key].should be_a String
99
+ inserted << user[:access_key]
100
+ end
101
+
102
+ it "should raise a 409 error if access_key was already taken" do
103
+ exists = { user: { access_key: @valid_access_key, email:'foo@bar.com' }}
104
+ post '/users', exists.to_json
105
+ last_response.status.should == 409
106
+ end
107
+
108
+ it "should raise a 400 error if data validation fails" do
109
+ post '/users', invalid_post.to_json
110
+ last_response.status.should == 400
111
+ message_from(last_response).should match /email/
112
+ end
113
+
114
+ it "should raise a 404 error if referenced an invalid kernel/ramdisk image" do
115
+ #post '/images', valid_post.merge(kernel: "fake_id").to_json
116
+ #message_from(last_response).should match /no image found/
117
+ end
118
+ end
119
+
120
+ describe "PUT on /users/:access_key" do
121
+ it "should update an existing user data and return it" do
122
+ put "/users/#{@valid_access_key}", valid_update.to_json
123
+ last_response.should be_ok
124
+ user = users_from(last_response)
125
+ user[:email].should == valid_update[:user][:email]
126
+ end
127
+
128
+ it "should raise a 409 error if access_key was already taken" do
129
+ exists = { user: { access_key: @valid_access_key, email:'foo@bar.com' }}
130
+ post '/users', exists.to_json
131
+ last_response.status.should == 409
132
+ end
133
+
134
+ it "should raise a 400 error if user data validation fails" do
135
+ put "/users/#{@valid_access_key}", invalid_update.to_json
136
+ last_response.status.should == 400
137
+ end
138
+
139
+ it "should raise a 404 error if referenced an invalid kernel/ramdisk image" do
140
+ #put "/images/#{@valid_access_key}", valid_update.merge(kernel: "fake_id").to_json
141
+ #message_from(last_response).should match /No image found/
142
+ end
143
+ end
144
+
145
+ describe "DELETE on /users/:access_key" do
146
+ it "should delete an user data" do
147
+ delete "/users/#{@valid_access_key}"
148
+ last_response.should be_ok
149
+
150
+ user = users_from(last_response)
151
+ user.should be_a Hash
152
+ user[:access_key].should == @valid_access_key
153
+
154
+ get "/users/#{@valid_access_key}"
155
+ last_response.status.should == 404
156
+ end
157
+
158
+ it "should raise a 404 error if user not found" do
159
+ delete "/users/fake_id"
160
+ last_response.status.should == 404
161
+ message_from(last_response).should match /No user found/
162
+ end
163
+ end
164
+
165
+ describe "Operation on a not implemented path" do
166
+ after(:each) do
167
+ last_response.status.should == 404
168
+ message_from(last_response).should match /Invalid operation or path/
169
+ end
170
+
171
+ it "should raise a 404 error for a GET request" do
172
+ get "/fake"
173
+ end
174
+
175
+ it "should raise a 404 error for a POST request" do
176
+ post "/fake"
177
+ end
178
+
179
+ it "should raise a 404 error for a PUT request" do
180
+ put "/fake"
181
+ end
182
+
183
+ it "should raise a 404 error for a POST request" do
184
+ delete "/fake"
185
+ end
186
+ end
187
+
188
+ end
189
+
metadata ADDED
@@ -0,0 +1,203 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: visor-auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - João Pereira
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack-test
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: yard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: mongo
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: bson_ext
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: mysql2
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: sinatra
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
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: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: thin
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ description: The VISOR Auth System, responsible for maintaining VISOR users accounts.
143
+ email: joaodrp@gmail.com
144
+ executables:
145
+ - visor-auth
146
+ - visor-admin
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - lib/auth/backends/base.rb
151
+ - lib/auth/backends/mongo_db.rb
152
+ - lib/auth/backends/mysql_db.rb
153
+ - lib/auth/cli.rb
154
+ - lib/auth/client.rb
155
+ - lib/auth/server.rb
156
+ - lib/auth/version.rb
157
+ - lib/visor-auth.rb
158
+ - spec/lib/server_spec.rb
159
+ - bin/visor-auth
160
+ - bin/visor-admin
161
+ homepage: http://cvisor.org
162
+ licenses: []
163
+ post_install_message: ! '
164
+
165
+
166
+ ****************************** VISOR ******************************
167
+
168
+
169
+ visor-auth was successfully installed!
170
+
171
+
172
+ Generate the VISOR configuration file for this machine (if not already done) by
173
+ running the ''visor-config'' command.
174
+
175
+
176
+ *******************************************************************
177
+
178
+
179
+ '
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ none: false
185
+ requirements:
186
+ - - ! '>='
187
+ - !ruby/object:Gem::Version
188
+ version: 1.9.2
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ none: false
191
+ requirements:
192
+ - - ! '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ requirements: []
196
+ rubyforge_project:
197
+ rubygems_version: 1.8.24
198
+ signing_key:
199
+ specification_version: 3
200
+ summary: ! 'VISOR: Virtual Images Management Service for Cloud Infrastructures'
201
+ test_files:
202
+ - spec/lib/server_spec.rb
203
+ has_rdoc: