visor-auth 0.0.1

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.
@@ -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: