Thư viện tri thức trực tuyến
Kho tài liệu với 50,000+ tài liệu học thuật
© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

Rails for Java Developers phần 10 pdf
Nội dung xem thử
Mô tả chi tiết
AUTHORIZATION WITH THE AUTHORIZATION PLUGIN 289
With the Authorization plugin, role permissions are assigned in the controller itself. Instead of using pure Ruby code, the permit code parses a
mini-language that aspires to read like a human language. For example, the following lines in the controller will specify that only administrators can edit Quips, and mere mortals can only view them:
Download code/rails_xt/app/controllers/quips_controller.rb
READ_ACTIONS = %w(index list show)
permit 'admin or mortal', :only=>READ_ACTIONS
permit 'admin', :except=>READ_ACTIONS
As with the Acegi pattern language, we find the Authorization plugin’s
mini-language to be self-explanatory.
You can test that the authorization protections work by loading the test
fixture data into the development database. Rails has a built-in task
specifically for this purpose. From the rails_xt directory, rake db:fixtures:
load will blow away the development database and replace its contents
with the test fixtures. After loading the fixtures, you can run script/server
and navigate to /quips. If you are Quentin, you will have read/write
access, but as Aaron you will have read access only.
Both Acegi and the Authorization plugin are much more powerful than
we have shown here. Both provide the ability to associate roles with
particular objects. Acegi also has one incredible feature that we have
not seen anywhere else. Because it integrates with the web tier, with
simple method interception, and with AspectJ’s pointcuts, Acegi can
secure just about anything. Better yet, you can use the same configuration and roles from end-to-end in your application. You can use the
same roles to secure web endpoints, methods, objects, and anything
you can capture in an AspectJ pointcut. For the biggest, hairiest problems out there, we would not use anything else.
The acts_as_authenticated/Authorization plugin tandem also has its
area of excellence: the tiny amount of configuration and code involved.
The amount of configuration required is an order of magnitude less
than Acegi, and it is not spread across multiple files and languages.
This parsimony extends to the implementation as well. The entire runtime footprint of both plugins together is less than 1,000 lines of Ruby
code. Security-related code is costly to develop and maintain, so getting
a lot done in a little code is a big advantage.
TESTING AUTHENTICATION AND AUTHORIZATION 290
10.3 Testing Authentication and Authorization
When you add authn and authz support to a Rails application, you
will typically break any functional tests that are already in place. This
is because functional tests exercise all controller code, including the
filters that are used to implement security.
For example, when we added authn and authz to People and Quips in
the previous two sections, we broke every test that invoked a secure
action, for a total of fifteen broken tests.
We have two problems here. First, we would like to be able to test the
logic of the controllers separately from the security constraints. So, we
would like a set of functional tests that do not include any security
filters. Second, we would like to be able to test the security constraints
themselves. Moreover, both of these sets of tests must be easy to write.
Otherwise, busy developers won’t write them. It would be a shame to
have an application where everything was testable except security.
The acts_as_authenticated plugin includes an AuthenticatedTestHelper
module to simplify security testing. You can make AuthenticatedTestHelper available to all your tests by mixing the module into TestCase
in test/test_helper.rb:
Download code/rails_xt/test/test_helper.rb
class Test::Unit::TestCase
include AuthenticatedTestHelper
AuthenticatedTestHelper adds several new test methods. One of the most
helpful is login_as. To get our tests to pass again, we can simply login_as
some account that has every necessary role. A test case’s setup method
is a perfect place to do this, since it runs before every test:
Download code/rails_xt/test/functional/people_controller_test.rb
def setup
@controller = PeopleController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
login_as(:quentin)
end
Since our authn and authz approach stores users and roles in the
database, we also need to add the security-related tables to the test
fixture.
TESTING AUTHENTICATION AND AUTHORIZATION 291
For example, we have used role-based security for the QuipsController, so
the functional test will need to have access to users, roles, and roles_users:
Download code/rails_xt/test/functional/quips_controller_test.rb
class QuipsControllerTest < Test::Unit::TestCase
fixtures :quips, :users, :roles, :roles_users
We used the previous approach to fix the fifteen broken functional test
for QuipsController and PeopleController. The fix required five total lines of
changed code:
• Including AuthenticatedTestHelper (one line)
• Adding login_as(:quentin) to two test classes (two lines)
• Editing the fixture line for the same two test classes (two lines)
Now the functional tests are working again, so we can turn our attention to testing the security constraints themselves. The AuthenticatedTestHelper includes an assert_requires_login method that checks that a particular controller invocation gets redirected to login:
Download code/rails_xt/test/functional/quips_security_test.rb
assert_requires_login do |c|
c.post :create, :quip => {}
end
Notice that this code lives in a different test class, QuipsSecurityTest instead of QuipsControllerTest. We are using a separate test class because
the QuipsControllerTest always logs in as Quentin, and now we are testing
what happens when there is no login. You can also use assert_requires_
login to test that Aaron (a mortal) lacks a role that would be allowed to
create a quip:
Download code/rails_xt/test/functional/quips_security_test.rb
assert_requires_login(:aaron) do |c|
c.post :create, :quip => {}
end
The syntax is a bit twisted here, in that assert_requires_login(:aaron) actually means “Assert that logging in as Aaron isn’t enough and that you
get redirected back to login.”
Rather than testing the redirect, you might want to test that failed
logins do not change the database. AuthenticatedTestHelper provides a
nifty assert_difference method for this kind of test. assert_difference takes
three arguments: an object, a method name, and a difference (which
defaults to +1). It also expects a block of code. assert_difference calls the