Changeset 8891
- Timestamp:
- 02/18/08 00:21:18 (2 years ago)
- Files:
-
- trunk/activeresource/lib/active_resource/base.rb (modified) (4 diffs)
- trunk/activeresource/lib/active_resource/connection.rb (modified) (4 diffs)
- trunk/activeresource/test/authorization_test.rb (modified) (1 diff)
- trunk/activeresource/test/base_test.rb (modified) (4 diffs)
- trunk/activeresource/test/base/custom_methods_test.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activeresource/lib/active_resource/base.rb
r8827 r8891 86 86 # 87 87 # Many REST APIs will require authentication, usually in the form of basic 88 # HTTP authentication. Authentication can be specified by putting the credentials 89 # in the +site+ variable of the Active Resource class you need to authenticate. 90 # 91 # class Person < ActiveResource::Base 92 # self.site = "http://ryan:password@api.people.com:3000/" 93 # end 94 # 88 # HTTP authentication. Authentication can be specified by: 89 # * putting the credentials in the URL for the +site+ variable. 90 # 91 # class Person < ActiveResource::Base 92 # self.site = "http://ryan:password@api.people.com:3000/" 93 # end 94 # 95 # * defining +user+ and/or +password+ variables 96 # 97 # class Person < ActiveResource::Base 98 # self.site = "http://api.people.com:3000/" 99 # self.user = "ryan" 100 # self.password = "password" 101 # end 102 # 95 103 # For obvious security reasons, it is probably best if such services are available 96 104 # over HTTPS. 97 105 # 106 # Note: Some values cannot be provided in the URL passed to site. e.g. email addresses 107 # as usernames. In those situations you should use the seperate user and password option. 98 108 # == Errors & Validation 99 109 # … … 165 175 # ActiveResource's mapping to work. 166 176 def site 177 # Not using superclass_delegating_reader because don't want subclasses to modify superclass instance 178 # 179 # With superclass_delegating_reader 180 # 181 # Parent.site = 'http://anonymous@test.com' 182 # Subclass.site # => 'http://anonymous@test.com' 183 # Subclass.site.user = 'david' 184 # Parent.site # => 'http://david@test.com' 185 # 186 # Without superclass_delegating_reader (expected behaviour) 187 # 188 # Parent.site = 'http://anonymous@test.com' 189 # Subclass.site # => 'http://anonymous@test.com' 190 # Subclass.site.user = 'david' # => TypeError: can't modify frozen object 191 # 167 192 if defined?(@site) 168 193 @site … … 176 201 def site=(site) 177 202 @connection = nil 178 @site = site.nil? ? nil : create_site_uri_from(site) 203 if site.nil? 204 @site = nil 205 else 206 @site = create_site_uri_from(site) 207 @user = @site.user if @site.user 208 @password = @site.password if @site.password 209 end 210 end 211 212 # Gets the user for REST HTTP authentication 213 def user 214 # Not using superclass_delegating_reader. See +site+ for explanation 215 if defined?(@user) 216 @user 217 elsif superclass != Object && superclass.user 218 superclass.user.dup.freeze 219 end 220 end 221 222 # Sets the user for REST HTTP authentication 223 def user=(user) 224 @connection = nil 225 @user = user 226 end 227 228 # Gets the password for REST HTTP authentication 229 def password 230 # Not using superclass_delegating_reader. See +site+ for explanation 231 if defined?(@password) 232 @password 233 elsif superclass != Object && superclass.password 234 superclass.password.dup.freeze 235 end 236 end 237 238 # Sets the password for REST HTTP authentication 239 def password=(password) 240 @connection = nil 241 @password = password 179 242 end 180 243 … … 207 270 if defined?(@connection) || superclass == Object 208 271 @connection = Connection.new(site, format) if refresh || @connection.nil? 272 @connection.user = user if user 273 @connection.password = password if password 209 274 @connection 210 275 else trunk/activeresource/lib/active_resource/connection.rb
r8827 r8891 56 56 # services. 57 57 class Connection 58 attr_reader :site 58 attr_reader :site, :user, :password 59 59 attr_accessor :format 60 60 … … 69 69 def initialize(site, format = ActiveResource::Formats[:xml]) 70 70 raise ArgumentError, 'Missing site URI' unless site 71 @user = @password = nil 71 72 self.site = site 72 73 self.format = format … … 76 77 def site=(site) 77 78 @site = site.is_a?(URI) ? site : URI.parse(site) 79 @user = @site.user if @site.user 80 @password = @site.password if @site.password 81 end 82 83 # Set user for remote service. 84 def user=(user) 85 @user = user 86 end 87 88 # Set password for remote service. 89 def password=(password) 90 @password = password 78 91 end 79 92 … … 167 180 end 168 181 169 # Sets authorization header ; authentication information is pulled from credentials provided with site URI.182 # Sets authorization header 170 183 def authorization_header 171 (@ site.user || @site.password ? { 'Authorization' => 'Basic ' + ["#{@site.user}:#{ @site.password}"].pack('m').delete("\r\n") } : {})184 (@user || @password ? { 'Authorization' => 'Basic ' + ["#{@user}:#{ @password}"].pack('m').delete("\r\n") } : {}) 172 185 end 173 186 trunk/activeresource/test/authorization_test.rb
r8566 r8891 46 46 end 47 47 48 def test_authorization_header_explicitly_setting_username_and_password 49 @authenticated_conn = ActiveResource::Connection.new("http://@localhost") 50 @authenticated_conn.user = 'david' 51 @authenticated_conn.password = 'test123' 52 authorization_header = @authenticated_conn.send!(:authorization_header) 53 assert_equal @authorization_request_header['Authorization'], authorization_header['Authorization'] 54 authorization = authorization_header["Authorization"].to_s.split 55 56 assert_equal "Basic", authorization[0] 57 assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1] 58 end 59 60 def test_authorization_header_explicitly_setting_username_but_no_password 61 @conn = ActiveResource::Connection.new("http://@localhost") 62 @conn.user = "david" 63 authorization_header = @conn.send!(:authorization_header) 64 authorization = authorization_header["Authorization"].to_s.split 65 66 assert_equal "Basic", authorization[0] 67 assert_equal ["david"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1] 68 end 69 70 def test_authorization_header_explicitly_setting_password_but_no_username 71 @conn = ActiveResource::Connection.new("http://@localhost") 72 @conn.password = "test123" 73 authorization_header = @conn.send!(:authorization_header) 74 authorization = authorization_header["Authorization"].to_s.split 75 76 assert_equal "Basic", authorization[0] 77 assert_equal ["", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1] 78 end 79 48 80 def test_get 49 81 david = @authenticated_conn.get("/people/2.xml") trunk/activeresource/test/base_test.rb
r8827 r8891 43 43 mock.head "/people/2/addresses/1.xml", {}, nil, 404 44 44 end 45 46 Person.user = nil 47 Person.password = nil 45 48 end 46 49 … … 69 72 end 70 73 74 def test_should_accept_setting_user 75 Forum.user = 'david' 76 assert_equal('david', Forum.user) 77 assert_equal('david', Forum.connection.user) 78 end 79 80 def test_should_accept_setting_password 81 Forum.password = 'test123' 82 assert_equal('test123', Forum.password) 83 assert_equal('test123', Forum.connection.password) 84 end 85 86 def test_user_variable_can_be_reset 87 actor = Class.new(ActiveResource::Base) 88 actor.site = 'http://cinema' 89 assert_nil actor.user 90 actor.user = 'username' 91 actor.user = nil 92 assert_nil actor.user 93 assert_nil actor.connection.user 94 end 95 96 def test_password_variable_can_be_reset 97 actor = Class.new(ActiveResource::Base) 98 actor.site = 'http://cinema' 99 assert_nil actor.password 100 actor.password = 'username' 101 actor.password = nil 102 assert_nil actor.password 103 assert_nil actor.connection.password 104 end 105 71 106 def test_site_reader_uses_superclass_site_until_written 72 107 # Superclass is Object so returns nil. … … 104 139 105 140 fruit.site = 'http://market' 106 assert_equal fruit.site, apple.site, 'subclass did not adopt changes toparent class'141 assert_equal fruit.site, apple.site, 'subclass did not adopt changes from parent class' 107 142 108 143 fruit.site = 'http://supermarket' 109 assert_equal fruit.site, apple.site, 'subclass did not adopt changes to parent class' 110 end 111 144 assert_equal fruit.site, apple.site, 'subclass did not adopt changes from parent class' 145 end 146 147 def test_user_reader_uses_superclass_user_until_written 148 # Superclass is Object so returns nil. 149 assert_nil ActiveResource::Base.user 150 assert_nil Class.new(ActiveResource::Base).user 151 Person.user = 'anonymous' 152 153 # Subclass uses superclass user. 154 actor = Class.new(Person) 155 assert_equal Person.user, actor.user 156 157 # Subclass returns frozen superclass copy. 158 assert !Person.user.frozen? 159 assert actor.user.frozen? 160 161 # Changing subclass user doesn't change superclass user. 162 actor.user = 'david' 163 assert_not_equal Person.user, actor.user 164 165 # Changing superclass user doesn't overwrite subclass user. 166 Person.user = 'john' 167 assert_not_equal Person.user, actor.user 168 169 # Changing superclass user after subclassing changes subclass user. 170 jester = Class.new(actor) 171 actor.user = 'john.doe' 172 assert_equal actor.user, jester.user 173 174 # Subclasses are always equal to superclass user when not overridden 175 fruit = Class.new(ActiveResource::Base) 176 apple = Class.new(fruit) 177 178 fruit.user = 'manager' 179 assert_equal fruit.user, apple.user, 'subclass did not adopt changes from parent class' 180 181 fruit.user = 'client' 182 assert_equal fruit.user, apple.user, 'subclass did not adopt changes from parent class' 183 end 184 185 def test_password_reader_uses_superclass_password_until_written 186 # Superclass is Object so returns nil. 187 assert_nil ActiveResource::Base.password 188 assert_nil Class.new(ActiveResource::Base).password 189 Person.password = 'my-password' 190 191 # Subclass uses superclass password. 192 actor = Class.new(Person) 193 assert_equal Person.password, actor.password 194 195 # Subclass returns frozen superclass copy. 196 assert !Person.password.frozen? 197 assert actor.password.frozen? 198 199 # Changing subclass password doesn't change superclass password. 200 actor.password = 'secret' 201 assert_not_equal Person.password, actor.password 202 203 # Changing superclass password doesn't overwrite subclass password. 204 Person.password = 'super-secret' 205 assert_not_equal Person.password, actor.password 206 207 # Changing superclass password after subclassing changes subclass password. 208 jester = Class.new(actor) 209 actor.password = 'even-more-secret' 210 assert_equal actor.password, jester.password 211 212 # Subclasses are always equal to superclass password when not overridden 213 fruit = Class.new(ActiveResource::Base) 214 apple = Class.new(fruit) 215 216 fruit.password = 'mega-secret' 217 assert_equal fruit.password, apple.password, 'subclass did not adopt changes from parent class' 218 219 fruit.password = 'ok-password' 220 assert_equal fruit.password, apple.password, 'subclass did not adopt changes from parent class' 221 end 222 112 223 def test_updating_baseclass_site_object_wipes_descendent_cached_connection_objects 113 224 # Subclasses are always equal to superclass site when not overridden … … 117 228 fruit.site = 'http://market' 118 229 assert_equal fruit.connection.site, apple.connection.site 230 first_connection = apple.connection.object_id 119 231 120 232 fruit.site = 'http://supermarket' 121 assert_equal fruit.connection.site, apple.connection.site 233 assert_equal fruit.connection.site, apple.connection.site 234 second_connection = apple.connection.object_id 235 assert_not_equal(first_connection, second_connection, 'Connection should be re-created') 236 end 237 238 def test_updating_baseclass_user_wipes_descendent_cached_connection_objects 239 # Subclasses are always equal to superclass user when not overridden 240 fruit = Class.new(ActiveResource::Base) 241 apple = Class.new(fruit) 242 fruit.site = 'http://market' 243 244 fruit.user = 'david' 245 assert_equal fruit.connection.user, apple.connection.user 246 first_connection = apple.connection.object_id 247 248 fruit.user = 'john' 249 assert_equal fruit.connection.user, apple.connection.user 250 second_connection = apple.connection.object_id 251 assert_not_equal(first_connection, second_connection, 'Connection should be re-created') 252 end 253 254 def test_updating_baseclass_password_wipes_descendent_cached_connection_objects 255 # Subclasses are always equal to superclass password when not overridden 256 fruit = Class.new(ActiveResource::Base) 257 apple = Class.new(fruit) 258 fruit.site = 'http://market' 259 260 fruit.password = 'secret' 261 assert_equal fruit.connection.password, apple.connection.password 262 first_connection = apple.connection.object_id 263 264 fruit.password = 'supersecret' 265 assert_equal fruit.connection.password, apple.connection.password 266 second_connection = apple.connection.object_id 267 assert_not_equal(first_connection, second_connection, 'Connection should be re-created') 122 268 end 123 269 trunk/activeresource/test/base/custom_methods_test.rb
r8566 r8891 33 33 mock.post "/people/1/addresses/new/link.xml", {}, { :street => '12345 Street' }.to_xml(:root => 'address'), 201, 'Location' => '/people/1/addresses/2.xml' 34 34 end 35 36 Person.user = nil 37 Person.password = nil 35 38 end 36 39