├── .gitignore
├── Gemfile
├── MIT-LICENSE
├── README.rdoc
├── Rakefile
├── VERSION
├── aegis.gemspec
├── lib
├── aegis.rb
└── aegis
│ ├── action.rb
│ ├── active_record_ext.rb
│ ├── compiler.rb
│ ├── controller.rb
│ ├── errors.rb
│ ├── has_role.rb
│ ├── loader.rb
│ ├── parser.rb
│ ├── permissions.rb
│ ├── resource.rb
│ ├── role.rb
│ ├── sieve.rb
│ ├── spec.rb
│ ├── spec
│ └── matchers.rb
│ └── util.rb
└── spec
├── aegis
├── controller_spec.rb
├── has_role_spec.rb
├── loader_spec.rb
├── permissions_spec.rb
├── sieve_spec.rb
└── spec
│ └── matchers_spec.rb
├── app_root
├── app
│ ├── controllers
│ │ ├── application_controller.rb
│ │ └── reviews_controller.rb
│ └── models
│ │ ├── permissions.rb
│ │ ├── property.rb
│ │ ├── review.rb
│ │ └── user.rb
├── config
│ ├── boot.rb
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── in_memory.rb
│ │ ├── mysql.rb
│ │ ├── postgresql.rb
│ │ ├── sqlite.rb
│ │ └── sqlite3.rb
│ └── routes.rb
├── db
│ └── migrate
│ │ ├── 001_create_users.rb
│ │ ├── 002_create_properties.rb
│ │ └── 003_create_reviews.rb
├── lib
│ └── console_with_fixtures.rb
├── log
│ └── .gitignore
└── script
│ └── console
├── controllers
└── reviews_controller_spec.rb
├── rcov.opts
├── spec.opts
├── spec_helper.rb
└── support
└── spec_candy.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | doc
2 | pkg
3 | *.gem
4 | .idea
5 | Gemfile.lock
6 |
7 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'http://rubygems.org'
2 |
3 | gem 'rails', '=2.3.8'
4 | gem "aegis", :path => '.'
5 | gem 'rspec', '=1.3.1'
6 | gem 'rspec-rails', '=1.3.3'
7 | gem 'ruby-debug'
8 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 Henning Koch
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.rdoc:
--------------------------------------------------------------------------------
1 | = Aegis - A complete authorization solution for Rails
2 |
3 | Aegis is an authorization solution for Ruby on Rails that supports roles and a RESTish, resource-style declaration of
4 | permission rules. Getting started with Aegis is easy and requires very little integration. As your authorization
5 | requirements become more complex, Aegis will grow with you.
6 |
7 |
8 | == End of life notice!
9 |
10 | The authors of this gem have moved on to create {Consul}[https://github.com/makandra/consul], our next-gen
11 | authorization solution. While Aegis remains a stable solution for Rails 2, this gem is not being developed further.
12 |
13 | If you are looking for Rails 3+ support you might want to browse through {forks of Aegis}[https://github.com/bitcababy/aegis/network].
14 |
15 | If you are interested in taking over future maintenance of Aegis, write to henning.koch@makandra.de regarding transfer
16 | of gem ownership. Please attach links to work you did on Aegis in a fork, so we can know you are serious about this.
17 |
18 |
19 | == Getting started
20 |
21 | All your permissions live in a single class Permissions.
22 | Permissions are described using resources, similiar to your routes.
23 | Your permission resources can match those in your routes, but don't have to.
24 |
25 | Access to resources or individual actions can be granted or denied to specific roles.
26 |
27 | class Permissions < Aegis::Permissions
28 |
29 | role :user
30 | role :admin
31 |
32 | resources :projects do
33 | allow :everyone
34 | end
35 |
36 | resources :users do
37 | allow :admin
38 | end
39 |
40 | end
41 |
42 | To give your user model a role, it needs to have an attribute +role_name+. The has_role macro wires everything together:
43 |
44 | class User < ActiveRecord::Base
45 | has_role
46 | end
47 |
48 | You can now check if a user has permission to access a given action in your controllers and views:
49 |
50 | <% if current_user.may_update_project? @project %>
51 | <%= link_to 'Edit', edit_project_path(@project) %>
52 | <% end %>
53 |
54 | You can protect all actions in a controller through an Aegis resource with a single line:
55 |
56 | class ProjectsController < ApplicationController
57 | permissions :projects
58 | end
59 |
60 |
61 | == Further reading
62 |
63 | You are now familiar with the basic use case. Aegis can do a *lot* more than that.
64 | There is an awesome {documentation wiki}[http://wiki.github.com/makandra/aegis/] with detailed information on many basic and advanced topics, including:
65 |
66 | * {Defining roles and basic permissions}[http://wiki.github.com/makandra/aegis/defining-roles-and-basic-permissions]
67 | * {Checking permissions}[http://wiki.github.com/makandra/aegis/checking-permissions]
68 | * {Giving your user model a role}[http://wiki.github.com/makandra/aegis/giving-your-user-model-a-role]
69 | * {Defining permissions with resources}[http://wiki.github.com/makandra/aegis/defining-permissions-with-resources]
70 | * {Controller integration}[http://wiki.github.com/makandra/aegis/controller-integration]
71 | * {Giving default access to superusers}[http://wiki.github.com/makandra/aegis/giving-default-access-to-superusers]
72 | * {Distinguishing between reading and writing actions}[http://wiki.github.com/makandra/aegis/distinguishing-between-reading-and-writing-actions]
73 | * {Aliasing actions}[http://wiki.github.com/makandra/aegis/aliasing-actions]
74 | * {Checking permissions when no user is signed in}[http://wiki.github.com/makandra/aegis/checking-permissions-when-no-user-is-signed-in]
75 | * {Handling denied permissions in your controllers}[http://wiki.github.com/makandra/aegis/handling-denied-permissions-in-your-controllers]
76 | * {Changing behavior when a permission is undefined}[http://wiki.github.com/makandra/aegis/changing-behavior-when-a-permission-is-undefined]
77 | * {Multiple roles per user}[http://wiki.github.com/makandra/aegis/multiple-roles-per-user]
78 | * {Testing permissions}[http://wiki.github.com/makandra/aegis/testing-permissions]
79 | * {Upgrading to Aegis 2}[http://wiki.github.com/makandra/aegis/upgrading-to-aegis-2]
80 |
81 |
82 | == Installation
83 |
84 | Aegis is a gem, which you can install with
85 | sudo gem install aegis
86 |
87 | In Rails 2, add the following to your environment.rb:
88 | config.gem 'aegis'
89 |
90 | In Rails 3, add the following to your Gemfile:
91 | gem 'aegis'
92 |
93 |
94 | == Compatibility
95 |
96 | Aegis was tested in Rails 2 only. If you are looking for Rails 3+ support you might want to browse through {forks of Aegis}[https://github.com/bitcababy/aegis/network].
97 |
98 |
99 | == Credits
100 |
101 | Henning Koch, Tobias Kraze
102 |
103 | {makandra.com}[http://makandra.com/]
104 |
105 | {gem-session.com}[http://gem-session.com/]
106 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake'
2 | require 'rake/rdoctask'
3 | require 'spec/rake/spectask'
4 |
5 | desc 'Default: Run Aegis specs'
6 | task :default => :spec
7 |
8 | desc "Run Aegis specs"
9 | Spec::Rake::SpecTask.new() do |t|
10 | t.spec_opts = ['--options', "\"spec/spec.opts\""]
11 | t.spec_files = FileList['spec/**/*_spec.rb']
12 | end
13 |
14 | desc 'Generate documentation for the Aegis gem'
15 | Rake::RDocTask.new(:rdoc) do |rdoc|
16 | rdoc.rdoc_dir = 'rdoc'
17 | rdoc.title = 'Aegis'
18 | rdoc.options << '--line-numbers' << '--inline-source'
19 | rdoc.rdoc_files.include('README')
20 | rdoc.rdoc_files.include('lib/**/*.rb')
21 | end
22 |
23 | begin
24 | require 'jeweler'
25 | Jeweler::Tasks.new do |gemspec|
26 | gemspec.name = "aegis"
27 | gemspec.summary = "Complete authorization solution for Rails"
28 | gemspec.email = "henning.koch@makandra.de"
29 | gemspec.homepage = "http://github.com/makandra/aegis"
30 | gemspec.description = "Aegis is an authorization solution for Ruby on Rails that supports roles and a RESTish, resource-style declaration of permission rules."
31 | gemspec.authors = ["Henning Koch", "Tobias Kraze"]
32 | gemspec.post_install_message = "Upgrade notice:\nIf you are using Aegis' automatic controller integration, include Aegis::Controller in your ApplicationController\nAlso see http://wiki.github.com/makandra/aegis/controller-integration\n"
33 | end
34 | rescue LoadError
35 | puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
36 | end
37 |
38 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 2.5.3
2 |
--------------------------------------------------------------------------------
/aegis.gemspec:
--------------------------------------------------------------------------------
1 | # Generated by jeweler
2 | # DO NOT EDIT THIS FILE DIRECTLY
3 | # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4 | # -*- encoding: utf-8 -*-
5 |
6 | Gem::Specification.new do |s|
7 | s.name = %q{aegis}
8 | s.version = "2.5.3"
9 |
10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11 | s.authors = ["Henning Koch", "Tobias Kraze"]
12 | s.date = %q{2010-11-10}
13 | s.description = %q{Aegis is an authorization solution for Ruby on Rails that supports roles and a RESTish, resource-style declaration of permission rules.}
14 | s.email = %q{henning.koch@makandra.de}
15 | s.extra_rdoc_files = [
16 | "README.rdoc"
17 | ]
18 | s.files = [
19 | ".gitignore",
20 | "Gemfile",
21 | "MIT-LICENSE",
22 | "README.rdoc",
23 | "Rakefile",
24 | "VERSION",
25 | "aegis.gemspec",
26 | "lib/aegis.rb",
27 | "lib/aegis/action.rb",
28 | "lib/aegis/active_record_ext.rb",
29 | "lib/aegis/compiler.rb",
30 | "lib/aegis/controller.rb",
31 | "lib/aegis/errors.rb",
32 | "lib/aegis/has_role.rb",
33 | "lib/aegis/loader.rb",
34 | "lib/aegis/parser.rb",
35 | "lib/aegis/permissions.rb",
36 | "lib/aegis/resource.rb",
37 | "lib/aegis/role.rb",
38 | "lib/aegis/sieve.rb",
39 | "lib/aegis/spec.rb",
40 | "lib/aegis/spec/matchers.rb",
41 | "lib/aegis/util.rb",
42 | "spec/aegis/controller_spec.rb",
43 | "spec/aegis/has_role_spec.rb",
44 | "spec/aegis/loader_spec.rb",
45 | "spec/aegis/permissions_spec.rb",
46 | "spec/aegis/sieve_spec.rb",
47 | "spec/aegis/spec/matchers_spec.rb",
48 | "spec/app_root/app/controllers/application_controller.rb",
49 | "spec/app_root/app/controllers/reviews_controller.rb",
50 | "spec/app_root/app/models/permissions.rb",
51 | "spec/app_root/app/models/property.rb",
52 | "spec/app_root/app/models/review.rb",
53 | "spec/app_root/app/models/user.rb",
54 | "spec/app_root/config/boot.rb",
55 | "spec/app_root/config/database.yml",
56 | "spec/app_root/config/environment.rb",
57 | "spec/app_root/config/environments/in_memory.rb",
58 | "spec/app_root/config/environments/mysql.rb",
59 | "spec/app_root/config/environments/postgresql.rb",
60 | "spec/app_root/config/environments/sqlite.rb",
61 | "spec/app_root/config/environments/sqlite3.rb",
62 | "spec/app_root/config/routes.rb",
63 | "spec/app_root/db/migrate/001_create_users.rb",
64 | "spec/app_root/db/migrate/002_create_properties.rb",
65 | "spec/app_root/db/migrate/003_create_reviews.rb",
66 | "spec/app_root/lib/console_with_fixtures.rb",
67 | "spec/app_root/log/.gitignore",
68 | "spec/app_root/script/console",
69 | "spec/controllers/reviews_controller_spec.rb",
70 | "spec/rcov.opts",
71 | "spec/spec.opts",
72 | "spec/spec_helper.rb",
73 | "spec/support/spec_candy.rb"
74 | ]
75 | s.homepage = %q{http://github.com/makandra/aegis}
76 | s.post_install_message = %q{Upgrade notice:
77 | If you are using Aegis' automatic controller integration, include Aegis::Controller in your ApplicationController
78 | Also see http://wiki.github.com/makandra/aegis/controller-integration
79 | }
80 | s.rdoc_options = ["--charset=UTF-8"]
81 | s.require_paths = ["lib"]
82 | s.rubygems_version = %q{1.3.7}
83 | s.summary = %q{Complete authorization solution for Rails}
84 | s.test_files = [
85 | "spec/app_root/app/controllers/application_controller.rb",
86 | "spec/app_root/app/controllers/reviews_controller.rb",
87 | "spec/app_root/app/models/permissions.rb",
88 | "spec/app_root/app/models/property.rb",
89 | "spec/app_root/app/models/review.rb",
90 | "spec/app_root/app/models/user.rb",
91 | "spec/app_root/config/boot.rb",
92 | "spec/app_root/config/environment.rb",
93 | "spec/app_root/config/environments/in_memory.rb",
94 | "spec/app_root/config/environments/mysql.rb",
95 | "spec/app_root/config/environments/postgresql.rb",
96 | "spec/app_root/config/environments/sqlite.rb",
97 | "spec/app_root/config/environments/sqlite3.rb",
98 | "spec/app_root/config/routes.rb",
99 | "spec/app_root/db/migrate/001_create_users.rb",
100 | "spec/app_root/db/migrate/002_create_properties.rb",
101 | "spec/app_root/db/migrate/003_create_reviews.rb",
102 | "spec/app_root/lib/console_with_fixtures.rb",
103 | "spec/controllers/reviews_controller_spec.rb",
104 | "spec/spec_helper.rb",
105 | "spec/aegis/has_role_spec.rb",
106 | "spec/aegis/loader_spec.rb",
107 | "spec/aegis/permissions_spec.rb",
108 | "spec/aegis/sieve_spec.rb",
109 | "spec/aegis/spec/matchers_spec.rb",
110 | "spec/aegis/controller_spec.rb",
111 | "spec/support/spec_candy.rb"
112 | ]
113 |
114 | if s.respond_to? :specification_version then
115 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
116 | s.specification_version = 3
117 |
118 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
119 | else
120 | end
121 | else
122 | end
123 | end
124 |
125 |
--------------------------------------------------------------------------------
/lib/aegis.rb:
--------------------------------------------------------------------------------
1 | require 'aegis/loader'
2 | Aegis::Loader.load_paths
3 |
--------------------------------------------------------------------------------
/lib/aegis/action.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | class Action
3 |
4 | attr_reader :name, :takes_object, :takes_parent_object, :writing, :sieves, :pluralize_resource
5 |
6 | def initialize(name, options)
7 | @name = name.to_s
8 | @sieves = []
9 | update(options, true)
10 | end
11 |
12 | def update(options, use_defaults = false)
13 | update_attribute(options, :takes_object, use_defaults, true)
14 | update_attribute(options, :takes_parent_object, use_defaults, false)
15 | update_attribute(options, :writing, use_defaults, true)
16 | update_attribute(options, :pluralize_resource, use_defaults, false)
17 | end
18 |
19 | def update_attribute(options, key, use_defaults, default)
20 | value = options[key]
21 | value = default if value.nil? && use_defaults
22 | instance_variable_set("@#{key}", value) unless value.nil?
23 | end
24 |
25 | def may?(user, *args)
26 | context = extract_context(user, args)
27 | user.roles.any? do |role|
28 | may_as_role?(role, context, *args)
29 | end
30 | end
31 |
32 | def may!(user, *args)
33 | may?(user, *args) or raise Aegis::AccessDenied, "Access denied: #{args.inspect}"
34 | end
35 |
36 | def self.index(options = {})
37 | new('index', options.reverse_merge(:takes_object => false, :pluralize_resource => true, :writing => false))
38 | end
39 |
40 | def self.show(options = {})
41 | new('show', options.reverse_merge(:takes_object => true, :writing => false))
42 | end
43 |
44 | def self.update(options = {})
45 | new('update', options.reverse_merge(:takes_object => true, :writing => true))
46 | end
47 |
48 | def self.create(options = {})
49 | new('create', options.reverse_merge(:takes_object => false, :writing => true))
50 | end
51 |
52 | def self.destroy(options = {})
53 | new('destroy', options.reverse_merge(:takes_object => true, :writing => true))
54 | end
55 |
56 | def self.undefined
57 | new(nil, :takes_object => false, :writing => true)
58 | end
59 |
60 | def self.allow_to_all
61 | action = undefined
62 | action.sieves << Aegis::Sieve.allow_to_all
63 | action
64 | end
65 |
66 | def self.deny_to_all
67 | action = undefined
68 | action.sieves << Aegis::Sieve.deny_to_all
69 | action
70 | end
71 |
72 | def abstract?
73 | name.blank?
74 | end
75 |
76 | def inspect
77 | "Action(#{{ :name => name, :takes_object => takes_object, :takes_parent_object => takes_parent_object, :sieves => sieves }.inspect})"
78 | end
79 |
80 | private
81 |
82 | def may_as_role?(role, context, *args)
83 | context.role = role
84 | may = role.may_by_default?
85 | for sieve in sieves
86 | opinion = sieve.may?(context, *args)
87 | may = opinion unless opinion.nil?
88 | end
89 | may
90 | end
91 |
92 | # not *args so we can change the array reference
93 | def extract_context(user, args)
94 | context = {}
95 | context[:user] = user
96 | if takes_parent_object
97 | context[:parent_object] = args.shift or raise ArgumentError, "No parent object given"
98 | end
99 | if takes_object
100 | context[:object] = args.shift or raise ArgumentError, "No object given"
101 | end
102 | OpenStruct.new(context)
103 | end
104 |
105 | end
106 | end
107 |
--------------------------------------------------------------------------------
/lib/aegis/active_record_ext.rb:
--------------------------------------------------------------------------------
1 | ActiveRecord::Base.extend(Aegis::HasRole)
2 |
--------------------------------------------------------------------------------
/lib/aegis/compiler.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | class Compiler
3 |
4 | ATOM_GROUPS = {
5 | :namespace => :structure,
6 | :resource => :structure,
7 | :resources => :structure,
8 | :action => :structure,
9 | :allow => :sieve,
10 | :deny => :sieve,
11 | :reading => :sieve,
12 | :writing => :sieve
13 | }
14 |
15 | def initialize(resource)
16 | @resource = resource
17 | end
18 |
19 | def compile(atoms)
20 | for atom in atoms
21 | case atom_group(atom)
22 | when :structure
23 | compile_structure(atom)
24 | when :sieve
25 | compile_sieve(atom)
26 | else
27 | unexpected_atom_type!(atom)
28 | end
29 | end
30 | end
31 |
32 | def self.compile(resource, atoms)
33 | new(resource).compile(atoms)
34 | end
35 |
36 | private
37 |
38 | def compile_structure(atom)
39 | case atom[:type]
40 | when :action
41 | compile_action(atom)
42 | when :namespace
43 | compile_namespace(atom)
44 | when :resource
45 | compile_child_resource(atom, :singleton)
46 | when :resources
47 | compile_child_resource(atom, :collection)
48 | else
49 | unexpected_atom_type!(atom)
50 | end
51 | end
52 |
53 | def compile_namespace(atom)
54 | atom[:options].merge!(:only => [])
55 | compile_child_resource(atom, :singleton)
56 | end
57 |
58 | def compile_action(atom)
59 | action = @resource.create_or_update_action(
60 | atom[:name],
61 | create_action_options(atom[:options]),
62 | update_action_options(atom[:options])
63 | )
64 | for sieve_atom in atom[:children]
65 | compile_sieve(sieve_atom, [action])
66 | end
67 | end
68 |
69 | def compile_sieve(atom, affected_actions = @resource.actions)
70 | case atom[:type]
71 | when :allow
72 | for action in affected_actions
73 | action.sieves << Aegis::Sieve.new(atom[:role_name], true, atom[:block])
74 | end
75 | when :deny
76 | for action in affected_actions
77 | action.sieves << Aegis::Sieve.new(atom[:role_name], false, atom[:block])
78 | end
79 | when :reading
80 | for child in atom[:children]
81 | compile_sieve(child, @resource.reading_actions)
82 | end
83 | when :writing
84 | for child in atom[:children]
85 | compile_sieve(child, @resource.writing_actions)
86 | end
87 | else
88 | unexpected_atom_type!(atom)
89 | end
90 | end
91 |
92 | def compile_child_resource(atom, type)
93 | child = Aegis::Resource.new(@resource, atom[:name], type, atom[:options])
94 | @resource.children << child
95 | Aegis::Compiler.compile(child, atom[:children])
96 | end
97 |
98 | def create_action_options(options)
99 | { :takes_object => @resource.new_action_takes_object?(options),
100 | :takes_parent_object => @resource.new_action_takes_parent_object?(options)
101 | }.merge(update_action_options(options))
102 | end
103 |
104 | def update_action_options(options)
105 | { :writing => options[:writing],
106 | :pluralize_resource => options[:collection] }
107 | end
108 |
109 | def atom_group(atom)
110 | ATOM_GROUPS[atom[:type]]
111 | end
112 |
113 | def unexpected_atom_type!(atom)
114 | raise Aegis::InvalidSyntax, "Unexpected atom type: #{atom[:type]}"
115 | end
116 |
117 | end
118 | end
119 |
--------------------------------------------------------------------------------
/lib/aegis/controller.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | module Controller
3 |
4 | def self.included(base)
5 | base.send :include, InstanceMethods
6 | base.send :extend, ClassMethods
7 | end
8 |
9 | module ClassMethods
10 |
11 | private
12 |
13 | def require_permissions(options = {})
14 | before_filter :unchecked_permissions, options
15 | end
16 |
17 | def skip_permissions(options = {})
18 | skip_before_filter :unchecked_permissions, options
19 | end
20 |
21 | def permissions(resource, options = {})
22 |
23 | filter_options = options.slice(:except, :only)
24 |
25 | skip_before_filter :unchecked_permissions, filter_options
26 |
27 | # Store arguments for testing
28 | @aegis_permissions_resource = resource
29 | @aegis_permissions_options = options
30 |
31 | before_filter :check_permissions, filter_options
32 |
33 | instance_eval do
34 |
35 | private
36 |
37 | actions_map = (options[:map] || {}).stringify_keys
38 | object_method = options[:object] || :object
39 | parent_object_method = options[:parent_object] || :parent_object
40 | user_method = options[:user] || :current_user
41 | permissions = lambda { Aegis::Permissions.app_permissions(options[:permissions]) }
42 |
43 | define_method :check_permissions do
44 | action = permissions.call.guess_action(
45 | resource,
46 | action_name.to_s,
47 | actions_map
48 | )
49 | args = []
50 | args << permissions.call.send(:handle_missing_user, send(user_method))
51 | args << send(parent_object_method) if action.takes_parent_object
52 | args << send(object_method) if action.takes_object
53 | action.may!(*args)
54 | end
55 |
56 | end
57 |
58 | end
59 |
60 | end
61 |
62 | module InstanceMethods
63 |
64 | private
65 |
66 | def unchecked_permissions
67 | raise Aegis::UncheckedPermissions, "This controller does not check permissions"
68 | end
69 |
70 | end
71 |
72 | end
73 | end
74 |
75 |
--------------------------------------------------------------------------------
/lib/aegis/errors.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 |
3 | class AccessDenied < StandardError
4 | end
5 |
6 | class UncheckedPermissions < StandardError
7 | end
8 |
9 | class InvalidSyntax < StandardError
10 | end
11 |
12 | class MissingUser < StandardError
13 | end
14 |
15 | class MissingAction < StandardError
16 | end
17 |
18 | end
19 |
--------------------------------------------------------------------------------
/lib/aegis/has_role.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | module HasRole
3 |
4 | def has_role(options = {})
5 |
6 | permissions = lambda { Aegis::Permissions.app_permissions(options[:permissions]) }
7 |
8 | may_pattern = /^may_(.+?)([\!\?])$/
9 |
10 | send :define_method, :role_names do
11 | (role_name || '').split(/\s*,\s*/)
12 | end
13 |
14 | send :define_method, :role_names= do |role_names|
15 | self.role_name = role_names.reject(&:blank?).join(',')
16 | end
17 |
18 | send :define_method, :role do
19 | roles.first
20 | end
21 |
22 | send :define_method, :roles do
23 | role_names.collect do |role_name|
24 | permissions.call.find_role_by_name(role_name)
25 | end.compact
26 | end
27 |
28 | send :define_method, :has_role? do |role_name|
29 | role_names.include?(role_name.to_s)
30 | end
31 |
32 | Aegis::Util.define_class_method(self, :validates_role) do |*validate_options|
33 | validate_options = validate_options[0] || {}
34 |
35 | send :define_method, :validate_role do
36 | unless role_names.size > 0 && role_names.size == roles.size
37 | message = validate_options[:message] || I18n.translate('activerecord.errors.messages.inclusion')
38 | errors.add :role_name, message
39 | end
40 | end
41 |
42 | validate :validate_role
43 | end
44 |
45 | if options[:default]
46 |
47 | unless method_defined?(:after_initialize)
48 | send :define_method, :after_initialize do
49 | end
50 | end
51 |
52 | send :define_method, :set_default_role_name do
53 | if new_record? && role_name.blank?
54 | self.role_name = options[:default]
55 | end
56 | end
57 |
58 | after_initialize :set_default_role_name
59 |
60 | end
61 |
62 | unless method_defined?(:method_missing_with_aegis_permissions)
63 |
64 | # Delegate may_...? and may_...! methods to the permissions class.
65 | send :define_method, :method_missing_with_aegis_permissions do |symb, *args|
66 | method_name = symb.to_s
67 | if method_name =~ may_pattern
68 | action_path, severity = $1, $2
69 | Aegis::Util.define_class_method(self, method_name) do |*method_args|
70 | permissions.call.send("may#{severity}", self, action_path, *method_args)
71 | end
72 | send(method_name, *args)
73 | else
74 | method_missing_without_aegis_permissions(symb, *args)
75 | end
76 | end
77 |
78 | alias_method_chain :method_missing, :aegis_permissions
79 |
80 | send :define_method, :respond_to_with_aegis_permissions? do |symb, *args|
81 | include_private = args.first.nil? ? false : args.first
82 | respond_to_without_aegis_permissions?(symb, include_private) || (symb.to_s =~ may_pattern)
83 | end
84 |
85 | alias_method_chain :respond_to?, :aegis_permissions
86 |
87 | end
88 | end
89 |
90 | end
91 | end
92 |
--------------------------------------------------------------------------------
/lib/aegis/loader.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | class Loader
3 | class << self
4 |
5 | def paths
6 | [ 'ostruct',
7 | 'aegis/util',
8 | 'aegis/errors',
9 | 'aegis/action',
10 | 'aegis/compiler',
11 | 'aegis/has_role',
12 | 'aegis/parser',
13 | 'aegis/permissions',
14 | 'aegis/resource',
15 | 'aegis/role',
16 | 'aegis/sieve',
17 | 'aegis/controller',
18 | 'aegis/active_record_ext' ]
19 | end
20 |
21 | def load_paths
22 | for path in paths
23 | require path
24 | end
25 | @loaded = true
26 | end
27 |
28 | def loaded?
29 | @loaded
30 | end
31 |
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/lib/aegis/parser.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | class Parser
3 |
4 | attr_reader :atoms
5 |
6 | def self.parse(&block)
7 | Aegis::Parser.new.parse(&block)
8 | end
9 |
10 | def initialize
11 | @atoms = []
12 | end
13 |
14 | def parse(&block)
15 | instance_eval(&block) if block
16 | atoms
17 | end
18 |
19 | def action(*args, &block)
20 | if block && block.arity > 0
21 | # useful warning for people upgrading from Aegis 2
22 | raise Aegis::InvalidSyntax, "Action blocks do not take block arguments in Aegis 2. allow/deny blocks do."
23 | end
24 | split_definitions(*args) do |name, options|
25 | @atoms.push({
26 | :type => :action,
27 | :name => name.to_s,
28 | :options => options,
29 | :children => Aegis::Parser.parse(&block)
30 | })
31 | end
32 | end
33 |
34 | def namespace(*args, &block)
35 | split_definitions(*args) do |name, options|
36 | @atoms.push({
37 | :type => :namespace,
38 | :name => name.to_s,
39 | :options => options,
40 | :children => Aegis::Parser.parse(&block)
41 | })
42 | end
43 | end
44 |
45 | def resource(*args, &block)
46 | split_definitions(*args) do |name, options|
47 | @atoms.push({
48 | :type => :resource,
49 | :name => name.to_s,
50 | :options => options,
51 | :children => Aegis::Parser.parse(&block)
52 | })
53 | end
54 | end
55 |
56 | def resources(*args, &block)
57 | split_definitions(*args) do |name, options|
58 | @atoms.push({
59 | :type => :resources,
60 | :name => name.to_s,
61 | :options => options,
62 | :children => Aegis::Parser.parse(&block)
63 | })
64 | end
65 | end
66 |
67 | def allow(*args, &block)
68 | split_definitions(*args) do |role_name, options|
69 | @atoms.push({
70 | :type => :allow,
71 | :role_name => role_name.to_s,
72 | :block => block
73 | })
74 | end
75 | end
76 |
77 | def deny(*args, &block)
78 | split_definitions(*args) do |role_name, options|
79 | @atoms.push({
80 | :type => :deny,
81 | :role_name => role_name.to_s,
82 | :block => block
83 | })
84 | end
85 | end
86 |
87 | def reading(&block)
88 | block or raise Aegis::InvalidSyntax, "missing block"
89 | @atoms.push({
90 | :type => :reading,
91 | :children => Aegis::Parser.parse(&block)
92 | })
93 | end
94 |
95 | def writing(&block)
96 | block or raise Aegis::InvalidSyntax, "missing block"
97 | @atoms.push({
98 | :type => :writing,
99 | :children => Aegis::Parser.parse(&block)
100 | })
101 | end
102 |
103 | private
104 |
105 | def split_definitions(*args, &definition)
106 | options = args.extract_options!
107 | args = [nil] if args.empty?
108 | for name in args
109 | definition.call(name, options)
110 | end
111 | end
112 |
113 | end
114 | end
115 |
--------------------------------------------------------------------------------
/lib/aegis/permissions.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | class Permissions
3 | class << self
4 |
5 | MISSING_ACTION_STRATEGIES = [
6 | :allow, :deny, :default_permission, :error
7 | ]
8 |
9 | def missing_action_means(strategy)
10 | prepare
11 | MISSING_ACTION_STRATEGIES.include?(strategy) or raise Aegis::InvalidSyntax, "missing_action_means must be one of #{MISSING_ACTION_STRATEGIES.inspect}"
12 | @missing_action_strategy = strategy
13 | end
14 |
15 | def missing_user_means(strategy_symbol = nil, &strategy_block)
16 | prepare
17 | @missing_user_strategy = strategy_symbol || strategy_block
18 | end
19 |
20 | def alias_action(aliases)
21 | prepare
22 | aliases.each do |key, value|
23 | @action_aliases[key.to_s] = value.to_s
24 | end
25 | end
26 |
27 | def permission(*args)
28 | raise Aegis::InvalidSyntax, "The Aegis API has changed. See http://wiki.github.com/makandra/aegis/upgrading-to-aegis-2 for migration instructions."
29 | end
30 |
31 | def action(*args, &block)
32 | prepare
33 | @parser.action(*args, &block)
34 | end
35 |
36 | def resource(*args, &block)
37 | prepare
38 | @parser.resource(*args, &block)
39 | end
40 |
41 | def namespace(*args, &block)
42 | prepare
43 | @parser.namespace(*args, &block)
44 | end
45 |
46 | def resources(*args, &block)
47 | prepare
48 | @parser.resources(*args, &block)
49 | end
50 |
51 | def may?(user, path, *args)
52 | query_action(:may?, user, path, *args)
53 | end
54 |
55 | def may!(user, path, *args)
56 | query_action(:may!, user, path, *args)
57 | end
58 |
59 | def role(role_name, options = {})
60 | role_name = role_name.to_s
61 | role_name != 'everyone' or raise Aegis::InvalidSyntax, "Cannot define a role named: #{role_name}"
62 | @roles_by_name ||= {}
63 | @roles_by_name[role_name] = Aegis::Role.new(role_name, options)
64 | end
65 |
66 | def roles
67 | @roles_by_name.values.sort
68 | end
69 |
70 | def find_role_by_name(name)
71 | @roles_by_name[name.to_s]
72 | end
73 |
74 | def guess_action(resource_name, action_name, map = {})
75 | compile
76 | action = nil
77 | action_name = action_name.to_s
78 | possible_paths = guess_action_paths(resource_name, action_name, map)
79 | possible_paths.detect do |path|
80 | action = find_action_by_path(path, false)
81 | end
82 | handle_missing_action(action, possible_paths.first)
83 | end
84 |
85 | def find_action_by_path(path, handle_missing = true)
86 | compile
87 | action = @actions_by_path[path.to_s]
88 | action = handle_missing_action(action, path) if handle_missing
89 | action
90 | end
91 |
92 | def app_permissions(option)
93 | if option.is_a?(Class)
94 | option
95 | else
96 | (option || '::Permissions').constantize
97 | end
98 | end
99 |
100 | def inspect
101 | compile
102 | "Permissions(#{@root_resource.inspect})"
103 | end
104 |
105 | private
106 |
107 | def query_action(verb, user, path, *args)
108 | prepare
109 | user = handle_missing_user(user)
110 | action = find_action_by_path(path)
111 | action.send(verb, user, *args)
112 | end
113 |
114 | def handle_missing_user(possibly_missing_user)
115 | possibly_missing_user ||= case @missing_user_strategy
116 | when :error then raise Aegis::MissingUser, "Cannot check permission without a user"
117 | when Proc then @missing_user_strategy.call
118 | end
119 | end
120 |
121 | def handle_missing_action(possibly_missing_action, path)
122 | possibly_missing_action ||= case @missing_action_strategy
123 | when :default_permission then Aegis::Action.undefined
124 | when :allow then Aegis::Action.allow_to_all
125 | when :deny then Aegis::Action.deny_to_all
126 | when :error then raise Aegis::MissingAction, "Missing action: #{path}"
127 | end
128 | end
129 |
130 | def guess_action_paths(resource_name, action_name, map)
131 | if mapped = map[action_name]
132 | [ mapped.to_s ]
133 | else
134 | [ "#{action_name}_#{resource_name.to_s.singularize}",
135 | "#{action_name}_#{resource_name.to_s.pluralize}",
136 | resource_name ]
137 | end
138 | end
139 |
140 | def prepare
141 | unless @parser
142 | @parser = Aegis::Parser.new
143 | @missing_user_strategy = :error
144 | @missing_action_strategy = :default_permission
145 | @action_aliases = {
146 | 'new' => 'create',
147 | 'edit' => 'update'
148 | }
149 | end
150 | end
151 |
152 | def compile
153 | unless @root_resource
154 | prepare
155 | @root_resource = Aegis::Resource.new(nil, nil, :root, {})
156 | Aegis::Compiler.compile(@root_resource, @parser.atoms)
157 | index_actions
158 | end
159 | end
160 |
161 | def index_actions
162 | @actions_by_path = @root_resource.index_actions_by_path(@action_aliases)
163 | end
164 |
165 | end
166 | end
167 | end
168 |
--------------------------------------------------------------------------------
/lib/aegis/resource.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | class Resource
3 |
4 | attr_reader :parent, :children, :name, :type, :never_takes_object, :actions
5 |
6 | def initialize(parent, name, type, options)
7 | @parent = parent
8 | @children = []
9 | @name = name
10 | @type = type
11 | @actions = initial_actions(options)
12 | # @never_takes_object = options[:object] == false
13 | end
14 |
15 | def inspect
16 | "Resource(#{{:name => name || 'root', :actions => actions, :children => children, :parent => parent}.inspect})"
17 | end
18 |
19 | def find_action_by_name(name)
20 | name = name.to_s
21 | @actions.detect { |action| action.name == name }
22 | end
23 |
24 | def create_or_update_action(name, create_options, update_options)
25 | action = nil
26 | if action = find_action_by_name(name)
27 | action.update(update_options)
28 | else
29 | action = Action.new(name, create_options)
30 | @actions << action
31 | end
32 | action
33 | end
34 |
35 | def root?
36 | type == :root
37 | end
38 |
39 | def singleton?
40 | type == :singleton
41 | end
42 |
43 | def collection?
44 | type == :collection
45 | end
46 |
47 | def reading_actions
48 | actions.reject(&:writing)
49 | end
50 |
51 | def writing_actions
52 | actions.select(&:writing)
53 | end
54 |
55 | def action_paths(action, aliases)
56 | if root?
57 | action_names_with_aliases(action.name, aliases)
58 | else
59 | action_names_with_aliases(action.name, aliases).collect do |action_name|
60 | build_path(
61 | action_name,
62 | parent && parent.path(false),
63 | action.pluralize_resource ? name.pluralize : name.singularize
64 | )
65 | end
66 | end
67 | end
68 |
69 | def action_names_with_aliases(native_name, aliases)
70 | names = [native_name]
71 | aliases.each do |key, value|
72 | names << key if value == native_name
73 | end
74 | names
75 | end
76 |
77 | def index_actions_by_path(aliases)
78 | index = {}
79 | actions.each do |action|
80 | action_paths(action, aliases).each do |path|
81 | index[path] = action
82 | end
83 | end
84 | children.each do |child|
85 | index.merge! child.index_actions_by_path(aliases)
86 | end
87 | index
88 | end
89 |
90 | def new_action_takes_object?(action_options = {})
91 | collection? && action_options[:collection] != true # && !never_takes_object
92 | end
93 |
94 | def new_action_takes_parent_object?(action_options = {})
95 | parent && parent.collection? # && !parent.never_takes_object
96 | end
97 |
98 | protected
99 |
100 | def path(pluralize = true)
101 | parent_path = parent && parent.path(false)
102 | pluralized_name = name ? (pluralize ? name.pluralize : name.singularize) : nil
103 | build_path(parent_path, pluralized_name)
104 | end
105 |
106 | private
107 |
108 | def build_path(*args)
109 | args.select(&:present?).join("_")
110 | end
111 |
112 | def filter_actions(actions, options)
113 | if options[:only]
114 | actions = actions.select(&action_name_filter(options[:only]))
115 | elsif options[:except]
116 | actions = actions.reject(&action_name_filter(options[:except]))
117 | end
118 | actions
119 | end
120 |
121 | def action_name_filter(whitelist)
122 | whitelist = whitelist.collect(&:to_s)
123 | lambda { |action| whitelist.include?(action.name) }
124 | end
125 |
126 | def initial_actions(options)
127 | send("initial_actions_for_#{type}", options)
128 | end
129 |
130 | def initial_actions_for_collection(options = {})
131 | filter_actions([
132 | Aegis::Action.index(initial_action_options),
133 | Aegis::Action.show(initial_action_options),
134 | Aegis::Action.update(initial_action_options),
135 | Aegis::Action.create(initial_action_options),
136 | Aegis::Action.destroy(initial_action_options),
137 | ], options)
138 | end
139 |
140 | def initial_actions_for_singleton(options = {})
141 | filter_actions([
142 | Aegis::Action.show(initial_action_options(:takes_object => false)),
143 | Aegis::Action.update(initial_action_options(:takes_object => false)),
144 | Aegis::Action.create(initial_action_options(:takes_object => false)),
145 | Aegis::Action.destroy(initial_action_options(:takes_object => false))
146 | ], options)
147 | end
148 |
149 | def initial_action_options(options = {})
150 | { :takes_parent_object => new_action_takes_parent_object? }.merge(options)
151 | end
152 |
153 | def initial_actions_for_root(options = {})
154 | []
155 | end
156 |
157 | end
158 | end
159 |
--------------------------------------------------------------------------------
/lib/aegis/role.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | class Role
3 |
4 | attr_reader :name, :default_permission
5 |
6 | def initialize(name, options)
7 | @name = name
8 | @default_permission = options[:default_permission] == :allow ? :allow : :deny
9 | freeze
10 | end
11 |
12 | def may_by_default?
13 | @default_permission == :allow
14 | end
15 |
16 | def <=>(other)
17 | name <=> other.name
18 | end
19 |
20 | def to_s
21 | name.to_s.humanize
22 | end
23 |
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/aegis/sieve.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | class Sieve
3 |
4 | def initialize(role_name, effect, block = nil)
5 | role_name = 'everyone' if role_name.blank?
6 | @role_name = role_name.to_s
7 | @effect = effect
8 | @block = block
9 | end
10 |
11 | def may?(context, *args)
12 | matches_role = @role_name == 'everyone' || @role_name == context.role.name
13 | if matches_role
14 | if @block
15 | block_result = context.instance_exec(*args, &@block)
16 | block_result ? @effect : !@effect
17 | else
18 | @effect
19 | end
20 | else
21 | nil
22 | end
23 | end
24 |
25 | def inspect
26 | "Sieve(#{{:role_name => @role_name, :effect => @effect ? :allow : :deny, :block => @block.present?}.inspect})"
27 | end
28 |
29 | def self.allow_to_all
30 | new('everyone', true)
31 | end
32 |
33 | def self.deny_to_all
34 | new('everyone', false)
35 | end
36 |
37 | end
38 |
39 | end
40 |
--------------------------------------------------------------------------------
/lib/aegis/spec.rb:
--------------------------------------------------------------------------------
1 | # Support aegis/spec for old code.
2 | # Maybe remove this some day.
3 | require 'aegis/spec/matchers'
4 |
5 |
--------------------------------------------------------------------------------
/lib/aegis/spec/matchers.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | module Spec
3 | module Matchers
4 |
5 | class CheckPermissions
6 |
7 | def initialize(expected_resource, expected_options = {})
8 | @expected_resource = expected_resource
9 | @expected_options = expected_options
10 | end
11 |
12 | def matches?(controller)
13 | @controller_class = controller.class
14 | @actual_resource = @controller_class.instance_variable_get('@aegis_permissions_resource')
15 | @actual_options = @controller_class.instance_variable_get('@aegis_permissions_options')
16 | @actual_resource == @expected_resource && @actual_options == @expected_options
17 | end
18 |
19 | def failure_message
20 | if @actual_resource != @expected_resource
21 | "expected #{@controller_class} to check permissions against resource #{@expected_resource.inspect}, but it checked against #{@actual_resource.inspect}"
22 | else
23 | "expected #{@controller_class} to check permissions with options #{@expected_options.inspect}, but options were #{@actual_options.inspect}"
24 | end
25 | end
26 |
27 | def negative_failure_message
28 | if @actual_resource == @expected_resource
29 | "expected #{@controller_class} to not check permissions against resource #{@expected_resource.inspect}"
30 | else
31 | "expected #{@controller_class} to not check permissions with options #{@expected_options.inspect}"
32 | end
33 | end
34 |
35 | def description
36 | description = "check permissions against resource #{@expected_resource.inspect}"
37 | description << " with options #{@expected_options.inspect}" if @expected_options.any?
38 | description
39 | end
40 |
41 | end
42 |
43 | def check_permissions(*args)
44 | CheckPermissions.new(*args)
45 | end
46 |
47 | class BeAllowedTo
48 |
49 | def initialize(expected_action, *expected_args)
50 | @expected_action = expected_action
51 | @expected_args = expected_args
52 | end
53 |
54 | def matches?(user)
55 | @actual_user = user
56 | @actual_user.send("may_#{@expected_action}?", *@expected_args)
57 | end
58 |
59 | def description
60 | "be allowed to #{action_as_prose}"
61 | end
62 |
63 | def failure_message
64 | "expected #{@actual_user.inspect} to be allowed to #{action_as_prose}"
65 | end
66 |
67 | def negative_failure_message
68 | "expected #{@actual_user.inspect} to be denied to #{action_as_prose}"
69 | end
70 |
71 | private
72 |
73 | def action_as_prose
74 | @expected_action.to_s + (@expected_args.present? ? " given #{@expected_args.inspect}" : "")
75 | end
76 |
77 | end
78 |
79 | def be_allowed_to(*args)
80 | BeAllowedTo.new(*args)
81 | end
82 |
83 | end
84 | end
85 | end
86 |
87 | ActiveSupport::TestCase.send :include, Aegis::Spec::Matchers
88 |
89 |
--------------------------------------------------------------------------------
/lib/aegis/util.rb:
--------------------------------------------------------------------------------
1 | module Aegis
2 | class Util
3 | class << self
4 |
5 | def define_class_method(object, method, &body)
6 | prototype = object.respond_to?(:singleton_class) ? object.singleton_class : object.metaclass
7 | prototype.send(:define_method, method, &body)
8 | end
9 |
10 | end
11 | end
12 | end
--------------------------------------------------------------------------------
/spec/aegis/controller_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Aegis::Controller do
4 |
5 | before(:each) do
6 |
7 | permissions_class = @permissions_class = Class.new(Aegis::Permissions) do
8 | role :user
9 | resources :posts do
10 | reading do
11 | allow :user
12 | end
13 | end
14 | end
15 |
16 | @user_class = Class.new(ActiveRecord::Base) do
17 | set_table_name 'users'
18 | has_role :permissions => permissions_class
19 | end
20 |
21 | user = @user = @user_class.new(:role_name => 'user')
22 |
23 | @controller_class = Class.new(ActionController::Base) do
24 | include Aegis::Controller
25 | define_method :current_user do
26 | user
27 | end
28 | end
29 |
30 | end
31 |
32 | describe 'require_permissions' do
33 |
34 | it "should set a before_filter :unchecked_permissions" do
35 | @controller_class.should_receive(:before_filter).with(:unchecked_permissions, :only => :show)
36 | @controller_class.class_eval do
37 | require_permissions :only => :show
38 | end
39 | end
40 |
41 | end
42 |
43 | describe 'skip_permissions' do
44 |
45 | it "should skip a before_filter :unchecked_permissions" do
46 | @controller_class.should_receive(:skip_before_filter).with(:unchecked_permissions, :only => :show)
47 | @controller_class.class_eval do
48 | skip_permissions :only => :show
49 | end
50 | end
51 |
52 | end
53 |
54 | describe 'unchecked_permissions' do
55 |
56 | it "should raise Aegis::UncheckedPermissions" do
57 | controller = @controller_class.new
58 | expect { controller.send(:unchecked_permissions) }.to raise_error(Aegis::UncheckedPermissions)
59 | end
60 |
61 | end
62 |
63 | describe 'permissions' do
64 |
65 | it "should fetch the context through #object, #parent_object and #current_user by default" do
66 | permissions_class = @permissions_class
67 | @controller_class.class_eval do
68 | permissions :posts, :permissions => permissions_class
69 | end
70 | controller = @controller_class.new
71 | permissions_class.stub(:guess_action => stub('action', :takes_object => true, :takes_parent_object => true).as_null_object)
72 | controller.should_receive(:object).and_return('the object')
73 | controller.should_receive(:parent_object).and_return('the parent object')
74 | controller.should_receive(:current_user).and_return('the user')
75 | controller.send(:check_permissions)
76 | end
77 |
78 | it "should allow custom readers for object, parent object and the current user" do
79 | permissions_class = @permissions_class
80 | @controller_class.class_eval do
81 | permissions :posts, :permissions => permissions_class, :user => :my_user, :object => :my_object, :parent_object => :my_parent
82 | end
83 | controller = @controller_class.new
84 | permissions_class.stub(:guess_action => stub('action', :takes_object => true, :takes_parent_object => true).as_null_object)
85 | controller.should_receive(:my_object).and_return('the object')
86 | controller.should_receive(:my_parent).and_return('the parent object')
87 | controller.should_receive(:my_user).and_return('the user')
88 | controller.send(:check_permissions)
89 | end
90 |
91 | it 'should install a before_filter that checks permissions' do
92 | @controller_class.should_receive(:before_filter).with(:check_permissions, :only => [:update])
93 | permissions_class = @permissions_class
94 | @controller_class.class_eval do
95 | permissions :posts, :permissions => permissions_class, :only => [:update]
96 | end
97 | end
98 |
99 | end
100 |
101 | describe 'check_permissions' do
102 |
103 | before(:each) do
104 | permissions_class = @permissions_class
105 | map = @map = { 'controller_action' => 'permission_path' }
106 | @controller_class.class_eval do
107 | permissions :posts, :permissions => permissions_class, :map => map
108 | end
109 | @controller = @controller_class.new
110 | @controller.stub(:object => "object", :parent_object => "parent object")
111 | end
112 |
113 | it "should guess the aegis-action using the current resource, action name and actions-map" do
114 | @controller.stub(:action_name => 'update')
115 | action = stub("action").as_null_object
116 | @permissions_class.should_receive(:guess_action).with(:posts, 'update', @map).and_return(action)
117 | @controller.send(:check_permissions)
118 | end
119 |
120 | it "should raise an error if permission is denied" do
121 | @controller.stub(:action_name => 'update')
122 | lambda { @controller.send(:check_permissions) }.should raise_error(Aegis::AccessDenied)
123 | end
124 |
125 | it "should pass silently if permission is granted" do
126 | @controller.stub(:action_name => 'show')
127 | lambda { @controller.send(:check_permissions) }.should_not raise_error
128 | end
129 |
130 | end
131 |
132 | end
133 |
--------------------------------------------------------------------------------
/spec/aegis/has_role_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Aegis::HasRole do
4 |
5 | before(:each) do
6 |
7 | @permissions_class = permissions_class = Class.new(Aegis::Permissions) do
8 | role :user
9 | role :moderator
10 | role :admin
11 | end
12 |
13 | @user_class = Class.new(ActiveRecord::Base) do
14 | set_table_name 'users'
15 | has_role :permissions => permissions_class
16 | end
17 |
18 | end
19 |
20 | describe 'has_role' do
21 |
22 | it "should define accessors for the role association" do
23 | user = @user_class.new
24 | user.should respond_to(:role)
25 | user.should respond_to(:roles)
26 | user.should respond_to(:has_role?)
27 | end
28 |
29 | it "should allow a default for new records" do
30 | permissions_class = @permissions_class
31 | @user_class.class_eval { has_role :permissions => permissions_class, :default => "admin" }
32 | user = @user_class.new
33 | user.role_name.should == 'admin'
34 | end
35 |
36 | end
37 |
38 | describe 'role' do
39 |
40 | it "should return the first role" do
41 | user = @user_class.new
42 | user.should_receive(:roles).and_return(['first role', 'second role'])
43 | user.role.should == 'first role'
44 | end
45 |
46 | it "should be nil if no roles are associated" do
47 | user = @user_class.new
48 | user.should_receive(:roles).and_return([])
49 | user.role.should be_nil
50 | end
51 |
52 | end
53 |
54 | describe 'roles' do
55 |
56 | it "should return the corresponding role for each role name" do
57 | user = @user_class.new
58 | user.should_receive(:role_names).and_return(['admin', 'user'])
59 | user.roles.collect(&:name).should == ['admin', 'user']
60 | end
61 |
62 | it "should ignore unknown role names that doesn't match a known role" do
63 | user = @user_class.new
64 | user.should_receive(:role_names).and_return(['unknown role', 'user'])
65 | user.roles.collect(&:name).should == ['user']
66 | end
67 |
68 | end
69 |
70 | describe 'role_names' do
71 |
72 | it "should be empty if the role name is blank" do
73 | user = @user_class.new(:role_name => '')
74 | user.role_names.should be_empty
75 | end
76 |
77 | it "should be empty if the role_name is nil" do
78 | user = @user_class.new(:role_name => nil)
79 | user.role_names.should be_empty
80 | end
81 |
82 | it "should deserialize a single role name into an array with a single element" do
83 | user = @user_class.new(:role_name => 'admin')
84 | user.role_names.should == ['admin']
85 | end
86 |
87 | it "should deserialize multiple, comma-separated role names into an array" do
88 | user = @user_class.new(:role_name => 'admin,user')
89 | user.role_names.should == ['admin', 'user']
90 | end
91 |
92 | it "should ignore whitespace around the comma-separator" do
93 | user = @user_class.new(:role_name => 'admin , user')
94 | user.role_names.should == ['admin', 'user']
95 | end
96 |
97 | end
98 |
99 | describe 'role_names=' do
100 |
101 | it "should serialize the given array into a comma-separated string and store it into #role_name" do
102 | user = @user_class.new
103 | user.should_receive(:role_name=).with("first,second")
104 | user.role_names = ['first', 'second']
105 | end
106 |
107 | it "should ignore blank role names" do
108 | user = @user_class.new
109 | user.should_receive(:role_name=).with("first,second")
110 | user.role_names = ['', nil, 'first', '', nil, 'second', '', nil]
111 | end
112 |
113 | end
114 |
115 | describe 'has_role?' do
116 |
117 | it "should return true if the user has the given role name" do
118 | user = @user_class.new(:role_name => 'admin, user')
119 | user.should have_role('admin')
120 | user.should have_role('user')
121 | end
122 |
123 | it "should return false if the user does not have the given role name" do
124 | user = @user_class.new(:role_name => 'admin, user')
125 | user.should_not have_role('moderator')
126 | end
127 |
128 | it "should allow to query the role name as a symbol instead of a string" do
129 | user = @user_class.new(:role_name => 'admin, user')
130 | user.should have_role(:admin)
131 | end
132 |
133 | end
134 |
135 | describe 'method_missing' do
136 |
137 | it "should delegate may...? messages to the permissions class" do
138 | user = @user_class.new
139 | @permissions_class.should_receive(:may?).with(user, 'do_action', 'argument')
140 | user.may_do_action?('argument')
141 | end
142 |
143 | it "should delegate may...! messages to the permissions class" do
144 | user = @user_class.new
145 | @permissions_class.should_receive(:may!).with(user, 'do_action', 'argument')
146 | user.may_do_action!('argument')
147 | end
148 |
149 | it "should retain its usual behaviour for non-permission methods" do
150 | user = @user_class.new
151 | lambda { user.nonexisting_method }.should raise_error(NoMethodError)
152 | end
153 |
154 | it 'should define a missing Aegis method so that method is used directly from there on' do
155 | user = @user_class.new
156 | user.should_receive_and_execute(:method_missing).once
157 | 2.times { user.may_do_action? }
158 | end
159 |
160 | end
161 |
162 | describe 'respond_to?' do
163 |
164 | it "should be true for all permission methods, even if they are not explicitely defined" do
165 | user = @user_class.new
166 | user.should respond_to(:may_foo?)
167 | user.should respond_to(:may_foo!)
168 | end
169 |
170 | it "should retain its usual behaviour for non-permission methods" do
171 | user = @user_class.new
172 | user.should respond_to(:to_s)
173 | user.should_not respond_to(:nonexisting_method)
174 | end
175 |
176 | end
177 |
178 | describe 'validates_role' do
179 |
180 | before(:each) do
181 | @user_class.class_eval { validates_role }
182 | end
183 |
184 | it "should run the validation callback :validate_role" do
185 | user = @user_class.new
186 | user.should_receive(:validate_role)
187 | user.run_callbacks(:validate)
188 | end
189 |
190 | it "should add an inclusion error to the role name if the role name is nil" do
191 | user = @user_class.new(:role_name => nil)
192 | user.errors.should_receive(:add).with(:role_name, I18n.translate('activerecord.errors.messages.inclusion'))
193 | user.send(:validate_role)
194 | end
195 |
196 | it "should add an inclusion error to the role name if the role name is an empty string" do
197 | user = @user_class.new(:role_name => '')
198 | user.errors.should_receive(:add).with(:role_name, I18n.translate('activerecord.errors.messages.inclusion'))
199 | user.send(:validate_role)
200 | end
201 |
202 | it "should add an inclusion error to the role name if a role name doesn't match a role" do
203 | user = @user_class.new(:role_name => 'user,nonexisting_role')
204 | user.errors.should_receive(:add).with(:role_name, I18n.translate('activerecord.errors.messages.inclusion'))
205 | user.send(:validate_role)
206 | end
207 |
208 | it "should add no error if all role names matches a role" do
209 | user = @user_class.new(:role_name => 'admin,user')
210 | user.errors.should_not_receive(:add)
211 | user.send(:validate_role)
212 | end
213 |
214 | it "should allow a custom error message with the :message options" do
215 | @user_class.class_eval { validates_role :message => "custom message" }
216 | user = @user_class.new(:role_name => '')
217 | user.errors.should_receive(:add).with(:role_name, 'custom message')
218 | user.send(:validate_role)
219 | end
220 |
221 | end
222 |
223 | end
224 |
--------------------------------------------------------------------------------
/spec/aegis/loader_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Aegis::Loader do
4 |
5 | describe 'paths' do
6 |
7 | it "should return all paths in the lib folder, except files that are optionally loaded" do
8 |
9 | root = "#{File.dirname(__FILE__)}/../lib/"
10 | Dir["#{root}*/*.rb"].each do |file|
11 | path = file.sub(root, "").sub(/\.rb$/, "")
12 | Aegis::Loader.paths.should include(path) unless path == 'aegis/loader' || path == 'aegis/spec'
13 | end
14 |
15 | end
16 |
17 | end
18 |
19 | describe 'load_paths' do
20 |
21 | it "should require all paths" do
22 |
23 | Aegis::Loader.stub(:paths => ['one', 'two'])
24 | Aegis::Loader.should_receive(:require).with('one')
25 | Aegis::Loader.should_receive(:require).with('two')
26 | Aegis::Loader.load_paths
27 |
28 | end
29 |
30 | end
31 |
32 | describe 'loaded?' do
33 |
34 | it "should be loaded by the time this test runs" do
35 | Aegis::Loader.should be_loaded
36 | end
37 |
38 | end
39 |
40 | end
--------------------------------------------------------------------------------
/spec/aegis/permissions_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Aegis::Permissions do
4 |
5 | before(:each) do
6 |
7 | permissions = @permissions = Class.new(Aegis::Permissions) do
8 | role :guest
9 | role :user
10 | role :moderator
11 | role :admin, :default_permission => :allow
12 | end
13 |
14 | @user_class = Class.new(ActiveRecord::Base) do
15 | set_table_name 'users'
16 | has_role :permissions => permissions
17 | end
18 |
19 | @user = @user_class.new(:role_name => 'user')
20 | @moderator = @user_class.new(:role_name => 'moderator')
21 | @admin = @user_class.new(:role_name => 'admin')
22 |
23 | end
24 |
25 | describe 'find_role_by_name' do
26 |
27 | it "should look up previously defined roles" do
28 |
29 | user_role = @permissions.find_role_by_name('user')
30 | admin_role = @permissions.find_role_by_name('admin')
31 |
32 | user_role.name.should == 'user'
33 | user_role.may_by_default?.should be_false
34 |
35 | admin_role.name.should == 'admin'
36 | admin_role.may_by_default?.should be_true
37 |
38 | end
39 |
40 | it "should be nil if no role with that name was defined" do
41 | @permissions.find_role_by_name('nonexisting_role').should be_nil
42 | end
43 |
44 | end
45 |
46 | describe 'permission definition' do
47 |
48 | it "should allow the definition of simple actions" do
49 |
50 | @permissions.class_eval do
51 | action :action_name do
52 | allow :user
53 | end
54 | end
55 |
56 | @permissions.may?(@user, 'action_name').should be_true
57 |
58 | end
59 |
60 | it "should allow the definition of multiple actions at once" do
61 |
62 | @permissions.class_eval do
63 | action :action1, :action2 do
64 | allow
65 | end
66 | end
67 |
68 | @permissions.may?(@user, 'action1').should be_true
69 | @permissions.may?(@user, 'action2').should be_true
70 | @permissions.may?(@user, 'action3').should be_false
71 |
72 | end
73 |
74 | it "should match an allow/deny directive to everyone is no role is named" do
75 | @permissions.class_eval do
76 | action :allowed_to_all do
77 | allow
78 | end
79 | action :denied_to_all do
80 | deny
81 | end
82 | end
83 |
84 | @permissions.may?(@user, 'allowed_to_all').should be_true
85 | @permissions.may?(@admin, 'denied_to_all').should be_false
86 | end
87 |
88 | it "should allow to grant permissions to multiple roles at once" do
89 |
90 | @permissions.class_eval do
91 | action :action_name do
92 | allow :user, :moderator
93 | end
94 | end
95 |
96 | @permissions.may?(@user, 'action_name').should be_true
97 | @permissions.may?(@moderator, 'action_name').should be_true
98 |
99 | end
100 |
101 | it "should return the default permission when queried for undefined actions" do
102 |
103 | @permissions.may?(@user, 'undefined_action').should be_false
104 | @permissions.may?(@admin, 'undefined_action').should be_true
105 |
106 | end
107 |
108 | it "should distinguish between roles" do
109 |
110 | @permissions.class_eval do
111 | action :update_news do
112 | allow :moderator
113 | end
114 | end
115 |
116 | @permissions.may?(@user, 'update_news').should be_false
117 | @permissions.may?(@moderator, 'update_news').should be_true
118 |
119 | end
120 |
121 | describe 'multiple roles' do
122 |
123 | before(:each) do
124 |
125 | @permissions.class_eval do
126 | action :update_news do
127 | allow :moderator
128 | end
129 | end
130 |
131 | end
132 |
133 | it "should allow a user with multiple roles access if at least one role passes, even if other roles don't" do
134 | person = @user_class.new(:role_names => ['user', 'moderator'])
135 | @permissions.may?(person, 'update_news').should be_true
136 | end
137 |
138 | it "should deny a user with multiple roles access if no role passes" do
139 | person = @user_class.new(:role_names => ['user', 'guest'])
140 | @permissions.may?(person, 'update_news').should be_false
141 | end
142 |
143 | it "should deny a user with no roles access" do
144 | person = @user_class.new(:role_names => [])
145 | @permissions.may?(person, 'update_news').should be_false
146 | end
147 |
148 | it "should honor default permissions" do
149 | person = @user_class.new(:role_names => ['admin'])
150 | @permissions.may?(person, 'update_news').should be_true
151 | end
152 |
153 | end
154 |
155 | it "should run sieves in a sequence, the result being the last matching sieve" do
156 |
157 | @permissions.class_eval do
158 | action :update_news do
159 | allow :everyone
160 | deny :user
161 | end
162 | end
163 |
164 | @permissions.may?(@user, 'update_news').should be_false
165 | @permissions.may?(@moderator, 'update_news').should be_true
166 |
167 | end
168 |
169 | it "should evaluate collection resources" do
170 |
171 | @permissions.class_eval do
172 | resources :posts do
173 | allow :moderator
174 | end
175 | end
176 |
177 | @permissions.may?(@moderator, 'update_post', "the post").should be_true
178 | @permissions.may?(@moderator, 'show_post', "the post").should be_true
179 | @permissions.may?(@moderator, 'create_post', "the post").should be_true
180 | @permissions.may?(@moderator, 'destroy_post', "the post").should be_true
181 | @permissions.may?(@moderator, 'index_posts').should be_true
182 |
183 | end
184 |
185 | it "should allow to configure generated resource actions" do
186 |
187 | @permissions.class_eval do
188 | resources :posts do
189 | action :index do
190 | allow :user
191 | end
192 | action :show do
193 | allow :user
194 | end
195 | end
196 | end
197 |
198 | @permissions.may?(@user, 'update_post', "the post").should be_false
199 | @permissions.may?(@user, 'show_post', "the post").should be_true
200 | @permissions.may?(@user, 'create_post', "the post").should be_false
201 | @permissions.may?(@user, 'destroy_post', "the post").should be_false
202 | @permissions.may?(@user, 'index_posts').should be_true
203 |
204 | @permissions.find_action_by_path('index_posts').takes_object.should be_false
205 | @permissions.find_action_by_path('show_post').takes_object.should be_true
206 |
207 | end
208 |
209 | it "should raise an error if an action takes an object but does not get it in the arguments" do
210 |
211 | @permissions.class_eval do
212 | resources :posts do
213 | allow :moderator
214 | end
215 | end
216 |
217 | expect do
218 | @permissions.may?(@moderator, 'update_post')
219 | end.to raise_error(ArgumentError)
220 |
221 | end
222 |
223 | it "should evaluate sieves with blocks" do
224 |
225 | @permissions.class_eval do
226 | resources :posts do
227 | allow :user do
228 | user.name == 'Waldo'
229 | end
230 | end
231 | end
232 |
233 | frank = @user_class.new(:name => 'Frank', :role_name => 'user')
234 | waldo = @user_class.new(:name => 'Waldo', :role_name => 'user')
235 |
236 | @permissions.may?(frank, 'update_post', 'the post').should be_false
237 | @permissions.may?(waldo, 'update_post', 'the post').should be_true
238 |
239 | end
240 |
241 | it "should evaluate singleton resources, which take no object" do
242 |
243 | @permissions.class_eval do
244 | resource :session do
245 | allow :moderator
246 | end
247 | end
248 |
249 | @permissions.may?(@moderator, 'update_session').should be_true
250 | @permissions.may?(@moderator, 'show_session').should be_true
251 | @permissions.may?(@moderator, 'create_session').should be_true
252 | @permissions.may?(@moderator, 'destroy_session').should be_true
253 |
254 | end
255 |
256 | it "should allow to nest resources into collection resources" do
257 |
258 | @permissions.class_eval do
259 | resources :properties do
260 | resources :comments do
261 | allow :moderator
262 | end
263 | end
264 | end
265 |
266 | @permissions.may?(@moderator, 'update_property_comment', "the property", "the comment").should be_true
267 | @permissions.may?(@moderator, 'show_property_comment', "the property", "the comment").should be_true
268 | @permissions.may?(@moderator, 'create_property_comment', "the property").should be_true
269 | @permissions.may?(@moderator, 'destroy_property_comment', "the property", "the comment").should be_true
270 | @permissions.may?(@moderator, 'index_property_comments', "the property").should be_true
271 |
272 | end
273 |
274 | it "should allow to nest resources into singleton resources" do
275 |
276 | @permissions.class_eval do
277 | resource :account do
278 | resources :bookings do
279 | allow :moderator
280 | end
281 | end
282 | end
283 |
284 | @permissions.may?(@moderator, 'update_account_booking', "the booking").should be_true
285 | @permissions.may?(@moderator, 'show_account_booking', "the booking").should be_true
286 | @permissions.may?(@moderator, 'create_account_booking').should be_true
287 | @permissions.may?(@moderator, 'destroy_account_booking', "the booking").should be_true
288 | @permissions.may?(@moderator, 'index_account_bookings').should be_true
289 |
290 | @permissions.find_action_by_path('update_account').should_not be_abstract
291 |
292 | end
293 |
294 | it "should support namespaces, which act like singleton resources but don't generate actions by default" do
295 |
296 | @permissions.class_eval do
297 | namespace :admin do
298 | resources :bookings do
299 | allow :moderator
300 | end
301 | end
302 | end
303 |
304 | @permissions.may?(@moderator, 'update_admin_booking', "the booking").should be_true
305 | @permissions.may?(@moderator, 'show_admin_booking', "the booking").should be_true
306 | @permissions.may?(@moderator, 'create_admin_booking').should be_true
307 | @permissions.may?(@moderator, 'destroy_admin_booking', "the booking").should be_true
308 | @permissions.may?(@moderator, 'index_admin_bookings').should be_true
309 |
310 | @permissions.find_action_by_path('update_admin').should be_abstract
311 |
312 | end
313 |
314 | it "should allow multiple levels of resource-nesting" do
315 |
316 | @permissions.class_eval do
317 | resources :properties do
318 | resources :reviews do
319 | resources :comments do
320 | allow :moderator
321 | end
322 | end
323 | end
324 | end
325 |
326 | @permissions.may?(@moderator, 'update_property_review_comment', "the review", "the comment").should be_true
327 | @permissions.may?(@moderator, 'show_property_review_comment', "the review", "the comment").should be_true
328 | @permissions.may?(@moderator, 'create_property_review_comment', "the review").should be_true
329 | @permissions.may?(@moderator, 'destroy_property_review_comment', "the review", "the comment").should be_true
330 | @permissions.may?(@moderator, 'index_property_review_comments', "the review").should be_true
331 |
332 | end
333 |
334 | it "should raise an error if an action takes a parent object but does not get it in the arguments" do
335 |
336 | @permissions.class_eval do
337 | resources :properties do
338 | resources :comments
339 | end
340 | end
341 |
342 | lambda { @permissions.may?(@moderator, 'update_property_comment', "the comment") }.should raise_error(ArgumentError)
343 |
344 | end
345 |
346 | it "should allow sieves with blocks and arguments" do
347 |
348 | @permissions.class_eval do
349 | action :sign_in do
350 | allow do |password|
351 | user.password == password
352 | end
353 | end
354 | end
355 |
356 | @user.stub(:password => "secret")
357 | @permissions.may?(@user, "sign_in", "wrong_password").should be_false
358 | @permissions.may?(@user, "sign_in", "secret").should be_true
359 |
360 | end
361 |
362 | it 'should raise an error when trying to define a role named "everyone"' do
363 |
364 | expect do
365 | @permissions.class_eval do
366 | role :everyone
367 | end
368 | end.to raise_error(Aegis::InvalidSyntax)
369 |
370 | end
371 |
372 | it "should raise an error if the argument is given to the action (Aegis 1) instead of the allow block (Aegis 2)" do
373 |
374 | expect do
375 | @permissions.class_eval do
376 | action :sign_in do |password|
377 | allow :everyone
378 | end
379 | end
380 | end.to raise_error(Aegis::InvalidSyntax)
381 |
382 | end
383 |
384 | it 'should raise an error if a #permission (singular) method is called (which no longer exists in Aegis 2)' do
385 |
386 | expect do
387 | @permissions.class_eval do
388 | permission :foo do
389 | allow :everyone
390 | end
391 | end
392 | end.to raise_error(Aegis::InvalidSyntax)
393 |
394 | end
395 |
396 | it "should provide the object and parent_object for a sieve block" do
397 | spy = stub("spy")
398 | @permissions.class_eval do
399 | resources :properties do
400 | resources :comments do
401 | allow :moderator do |additional_argument|
402 | spy.observe(parent_object, object, additional_argument)
403 | end
404 | end
405 | end
406 | end
407 |
408 | spy.should_receive(:observe).with("the property", "the comment", "additional argument")
409 | @permissions.may?(@moderator, "update_property_comment", "the property", "the comment", "additional argument")
410 | end
411 |
412 | it "should evaluate additional resource actions" do
413 |
414 | @permissions.class_eval do
415 | resources :properties do
416 | action :zoom_into do
417 | allow :user
418 | end
419 | action :view_all, :collection => true do
420 | allow :user
421 | end
422 | end
423 | end
424 |
425 | @permissions.may?(@user, "zoom_into_property", "the property").should be_true
426 | @permissions.may?(@user, "view_all_properties").should be_true
427 |
428 | end
429 |
430 | it "should allow rules that only affect reading actions" do
431 |
432 | @permissions.class_eval do
433 | resources :posts do
434 | action :syndicate, :writing => false
435 | action :close
436 | reading do
437 | allow :user
438 | end
439 | end
440 | end
441 |
442 | @permissions.may?(@user, 'update_post', "the post").should be_false
443 | @permissions.may?(@user, 'show_post', "the post").should be_true
444 | @permissions.may?(@user, 'create_post', "the post").should be_false
445 | @permissions.may?(@user, 'destroy_post', "the post").should be_false
446 | @permissions.may?(@user, 'index_posts').should be_true
447 | @permissions.may?(@user, 'syndicate_post', "the post").should be_true
448 | @permissions.may?(@user, 'close_post", "the post').should be_false
449 |
450 | end
451 |
452 | it "should allow rules that only affect writing actions" do
453 |
454 | @permissions.class_eval do
455 | resources :posts do
456 | action :syndicate, :writing => false
457 | action :close
458 | writing do
459 | allow :moderator
460 | end
461 | end
462 | end
463 |
464 | # debugger
465 |
466 | @permissions.may?(@moderator, 'update_post', "the post").should be_true
467 | @permissions.may?(@moderator, 'show_post', "the post").should be_false
468 | @permissions.may?(@moderator, 'create_post', "the post").should be_true
469 | @permissions.may?(@moderator, 'destroy_post', "the post").should be_true
470 | @permissions.may?(@moderator, 'index_posts').should be_false
471 | @permissions.may?(@moderator, 'syndicate_post', "the post").should be_false
472 | @permissions.may?(@moderator, "close_post", "the post").should be_true
473 |
474 | end
475 |
476 | it 'should raise an error if a #reading directive is stated without a block' do
477 | expect do
478 | @permissions.class_eval do
479 | resources :posts do
480 | reading
481 | end
482 | end
483 | end.to raise_error(Aegis::InvalidSyntax)
484 | end
485 |
486 | it 'should raise an error if a #writing directive is stated without a block' do
487 | expect do
488 | @permissions.class_eval do
489 | resources :posts do
490 | writing
491 | end
492 | end
493 | end.to raise_error(Aegis::InvalidSyntax)
494 | end
495 |
496 | it "should allow resources with only selected actions" do
497 | @permissions.class_eval do
498 | resources :posts, :only => [:show, :update]
499 | end
500 | @permissions.find_action_by_path('update_post').should_not be_abstract
501 | @permissions.find_action_by_path('show_post').should_not be_abstract
502 | @permissions.find_action_by_path('create_post').should be_abstract
503 | @permissions.find_action_by_path('destroy_post').should be_abstract
504 | @permissions.find_action_by_path('index_posts').should be_abstract
505 | end
506 |
507 | it "should allow resources with all actions except a selected few" do
508 | @permissions.class_eval do
509 | resources :posts, :except => [:show, :update]
510 | end
511 | @permissions.find_action_by_path('update_post').should be_abstract
512 | @permissions.find_action_by_path('show_post').should be_abstract
513 | @permissions.find_action_by_path('create_post').should_not be_abstract
514 | @permissions.find_action_by_path('destroy_post').should_not be_abstract
515 | @permissions.find_action_by_path('index_posts').should_not be_abstract
516 | end
517 |
518 | it 'should allow to override individual actions' do
519 | @permissions.class_eval do
520 | resources :posts do
521 | allow :everyone
522 | action :create do
523 | deny :everyone
524 | end
525 | end
526 | end
527 | @permissions.may?(@user, 'index_posts').should be_true
528 | @permissions.may?(@user, 'create_post').should be_false
529 | end
530 |
531 | it 'should allow to repeatedly define permissions for the same action, deciding for the last directive that matched' do
532 | @permissions.class_eval do
533 | resources :posts do
534 | action :create do
535 | allow :everyone
536 | end
537 | action :create do
538 | deny :user
539 | end
540 | end
541 | end
542 | @permissions.may?(@admin, 'create_posts').should be_true
543 | @permissions.may?(@user, 'create_post').should be_false
544 | end
545 |
546 | it "should alias action names for all actions and resources, aliasing #new and #edit by default" do
547 |
548 | @permissions.class_eval do
549 |
550 | alias_action :delete => :destroy
551 |
552 | resources :properties do
553 | resources :comments
554 | end
555 | end
556 |
557 | @permissions.find_action_by_path('delete_property').should_not be_abstract
558 | @permissions.find_action_by_path('new_property').should_not be_abstract
559 | @permissions.find_action_by_path('edit_property').should_not be_abstract
560 |
561 | @permissions.find_action_by_path('delete_property_comment').should_not be_abstract
562 | @permissions.find_action_by_path('new_property_comment').should_not be_abstract
563 | @permissions.find_action_by_path('edit_property_comment').should_not be_abstract
564 |
565 | end
566 | end
567 |
568 | describe 'may!' do
569 |
570 | it "should return if permission is granted" do
571 | lambda { @permissions.may!(@admin, :delete_everything) }.should_not raise_error
572 | end
573 |
574 | it "should raise an error if permission is denied" do
575 | lambda { @permissions.may!(@user, :delete_everything) }.should raise_error(Aegis::AccessDenied)
576 | end
577 |
578 | end
579 |
580 | describe 'behavior when checking permissions without a user' do
581 |
582 | it "should raise an error if no missing user strategy is defined" do
583 | expect { @permissions.may?(nil, :some_action) }.to raise_error(Aegis::MissingUser)
584 | end
585 |
586 | it 'should raise an error if the missing user strategy is :error' do
587 | @permissions.class_eval do
588 | missing_user_means :error
589 | end
590 | expect { @permissions.may?(nil, :some_action) }.to raise_error(Aegis::MissingUser)
591 | end
592 |
593 | it "should substitute the results from the missing user strategy" do
594 | @permissions.class_eval do
595 | missing_user_means { User.new(:role_name => 'user') }
596 | action :create_post do
597 | allow :moderator
598 | end
599 | action :show_post do
600 | allow :user
601 | end
602 | end
603 | @permissions.may?(nil, :create_post).should be_false
604 | @permissions.may?(nil, :show_post).should be_true
605 | end
606 |
607 | end
608 |
609 | describe 'behavior when a permission is not defined' do
610 |
611 | it "should use the default permission if the strategy is :default_permission" do
612 | @permissions.class_eval do
613 | missing_action_means :default_permission
614 | end
615 | @permissions.may?(@user, 'missing_action').should be_false
616 | @permissions.may?(@admin, 'missing_action').should be_true
617 | end
618 |
619 | it "should grant everyone access if the strategy is :allow" do
620 | @permissions.class_eval do
621 | missing_action_means :allow
622 | end
623 | @permissions.may?(@user, 'missing_action').should be_true
624 | @permissions.may?(@admin, 'missing_action').should be_true
625 | end
626 |
627 | it "should deny everyone access if the strategy is :deny" do
628 | @permissions.class_eval do
629 | missing_action_means :deny
630 | end
631 | @permissions.may?(@user, 'missing_action').should be_false
632 | @permissions.may?(@admin, 'missing_action').should be_false
633 | end
634 |
635 | it "should raise an error naming the missing action if the strategy is :error" do
636 | @permissions.class_eval do
637 | missing_action_means :error
638 | end
639 | lambda { @permissions.may?(@user, 'missing_action') }.should raise_error(Aegis::MissingAction, 'Missing action: missing_action')
640 | lambda { @permissions.may?(@admin, 'missing_action') }.should raise_error(Aegis::MissingAction, 'Missing action: missing_action')
641 | end
642 |
643 | end
644 |
645 | describe 'guess_action' do
646 |
647 | it "should guess an action based on the given resource and action name, trying both singular and plural" do
648 |
649 | @permissions.class_eval do
650 | resources :posts
651 | end
652 |
653 | @permissions.guess_action(:posts, :index).should_not be_abstract
654 | @permissions.guess_action(:posts, :update).should_not be_abstract
655 | @permissions.guess_action(:posts, :unknown_action).should be_abstract
656 |
657 | end
658 |
659 | it "should consult the actions map first and use the the default behaviour for unmapped actions" do
660 |
661 | @permissions.class_eval do
662 | resources :posts, :only => [:update] do
663 | action :view_all, :collection => true
664 | end
665 | end
666 |
667 | @permissions.guess_action(:posts, 'index', 'index' => 'view_all_posts').should_not be_abstract
668 | @permissions.guess_action(:posts, 'update', 'index' => 'view_all_posts').should_not be_abstract
669 |
670 | end
671 |
672 | it "should find a root action that has the same name as the given resource" do
673 |
674 | @permissions.class_eval do
675 | action :author_section
676 | end
677 |
678 | @permissions.guess_action(:author_section, 'irrelevant_action_name').should_not be_abstract
679 | @permissions.guess_action(:undefined_action, 'irrelevant_action_name').should be_abstract
680 |
681 | end
682 |
683 | end
684 |
685 | describe 'find_action_by_path' do
686 |
687 | before(:each) do
688 | @permissions.class_eval do
689 | action :action_name do
690 | allow :user
691 | end
692 | end
693 | end
694 |
695 | it "should find an action by a string" do
696 | @permissions.find_action_by_path('action_name').should_not be_abstract
697 | end
698 |
699 | it "should find an action by a symbol" do
700 | @permissions.find_action_by_path(:action_name).should_not be_abstract
701 | end
702 |
703 | end
704 |
705 | end
706 |
--------------------------------------------------------------------------------
/spec/aegis/sieve_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Aegis::Sieve do
4 |
5 | before(:each) do
6 | @role = stub('role', :name => 'user')
7 | # user = stub('user', :role => role)
8 | @context = OpenStruct.new(:role => @role)
9 | end
10 |
11 | describe 'may? 'do
12 |
13 | it "should use the role's name to find out if the sieve matches" do
14 | @role.should_receive(:name)
15 | Aegis::Sieve.new('moderator', true).may?(@context)
16 | end
17 |
18 | it "should return nil if the sieve doesn't match the role" do
19 | Aegis::Sieve.new('moderator', true).may?(@context).should be_nil
20 | end
21 |
22 | it "should return the effect if the sieve matches the role" do
23 | Aegis::Sieve.new('user', true).may?(@context).should be_true
24 | Aegis::Sieve.new('user', false).may?(@context).should be_false
25 | end
26 |
27 | it "should match any role if its role name is set to 'everyone'" do
28 | Aegis::Sieve.new('everyone', true).may?(@context).should be_true
29 | Aegis::Sieve.new('everyone', false).may?(@context).should be_false
30 | end
31 |
32 | context "with a block" do
33 |
34 | it "should return the effect if the block evaluates to true" do
35 | Aegis::Sieve.new('user', true, lambda { true }).may?(@context).should be_true
36 | Aegis::Sieve.new('user', false, lambda { true }).may?(@context).should be_false
37 | end
38 |
39 | it "should invert the effect if the block evaluates to false" do
40 | Aegis::Sieve.new('user', true, lambda { false }).may?(@context).should be_false
41 | Aegis::Sieve.new('user', false, lambda { false }).may?(@context).should be_true
42 | end
43 |
44 | end
45 |
46 | end
47 |
48 | end
49 |
--------------------------------------------------------------------------------
/spec/aegis/spec/matchers_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Aegis::Spec::Matchers do
4 |
5 | describe 'be_allowed_to' do
6 |
7 | before(:each) do
8 |
9 | permissions = @permissions = Class.new(Aegis::Permissions) do
10 | role :user
11 | resources :files do
12 | allow :user do
13 | object == 'allowed-file'
14 | end
15 | end
16 | end
17 |
18 | @user_class = Class.new(ActiveRecord::Base) do
19 | set_table_name 'users'
20 | has_role :permissions => permissions
21 | end
22 |
23 | @user = @user_class.new(:role_name => 'user')
24 |
25 | end
26 |
27 | it 'should match the positive case' do
28 | @user.should be_allowed_to(:update_file, 'allowed-file')
29 | end
30 |
31 | it 'should match the negative case' do
32 | @user.should_not be_allowed_to(:update_file, 'denied-file')
33 | end
34 |
35 | end
36 |
37 | describe 'check_permissions' do
38 |
39 | before(:each) do
40 | @controller = Class.new(ActionController::Base) do
41 | include Aegis::Controller
42 | permissions :post
43 | end.new
44 | end
45 |
46 | it 'should match the positive case' do
47 | @controller.should check_permissions(:post)
48 | end
49 |
50 | it 'should match the negative case' do
51 | @controller.should_not check_permissions(:reviews)
52 | end
53 |
54 | end
55 |
56 | end
57 |
--------------------------------------------------------------------------------
/spec/app_root/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | include Aegis::Controller
3 |
4 | require_permissions
5 |
6 | def current_user
7 | nil # test missing_user_means
8 | end
9 |
10 | end
11 |
--------------------------------------------------------------------------------
/spec/app_root/app/controllers/reviews_controller.rb:
--------------------------------------------------------------------------------
1 | class ReviewsController < ApplicationController
2 |
3 | permissions :property_reviews
4 |
5 | def show
6 | end
7 |
8 | def edit
9 | end
10 |
11 | def update
12 | end
13 |
14 | def new
15 | end
16 |
17 | def create
18 | end
19 |
20 | def destroy
21 | end
22 |
23 | def index
24 | end
25 |
26 | private
27 |
28 | def object
29 | @oject ||= parent_object.reviews.find(params[:id])
30 | end
31 |
32 | def parent_object
33 | @parent_object ||= Property.find(params[:id])
34 | end
35 |
36 | end
37 |
--------------------------------------------------------------------------------
/spec/app_root/app/models/permissions.rb:
--------------------------------------------------------------------------------
1 | class Permissions < Aegis::Permissions
2 |
3 | role :user
4 |
5 | missing_user_means { User.new(:role_name => 'user') }
6 |
7 | resources :properties do
8 | resources :reviews do
9 | reading do
10 | allow :user
11 | end
12 | end
13 | end
14 |
15 | end
16 |
17 |
--------------------------------------------------------------------------------
/spec/app_root/app/models/property.rb:
--------------------------------------------------------------------------------
1 | class Property < ActiveRecord::Base
2 |
3 | has_many :reviews
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/spec/app_root/app/models/review.rb:
--------------------------------------------------------------------------------
1 | class Review < ActiveRecord::Base
2 |
3 | belongs_to :property
4 |
5 | end
--------------------------------------------------------------------------------
/spec/app_root/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ActiveRecord::Base
2 |
3 | has_role
4 |
5 | end
--------------------------------------------------------------------------------
/spec/app_root/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Allow customization of the rails framework path
2 | RAILS_FRAMEWORK_ROOT = (ENV['RAILS_FRAMEWORK_ROOT'] || "#{File.dirname(__FILE__)}/../../../../../../vendor/rails") unless defined?(RAILS_FRAMEWORK_ROOT)
3 |
4 | # Don't change this file!
5 | # Configure your app in config/environment.rb and config/environments/*.rb
6 |
7 | RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
8 |
9 | module Rails
10 | class << self
11 | def boot!
12 | unless booted?
13 | preinitialize
14 | pick_boot.run
15 | end
16 | end
17 |
18 | def booted?
19 | defined? Rails::Initializer
20 | end
21 |
22 | def pick_boot
23 | (vendor_rails? ? VendorBoot : GemBoot).new
24 | end
25 |
26 | def vendor_rails?
27 | File.exist?(RAILS_FRAMEWORK_ROOT)
28 | end
29 |
30 | def preinitialize
31 | load(preinitializer_path) if File.exist?(preinitializer_path)
32 | end
33 |
34 | def preinitializer_path
35 | "#{RAILS_ROOT}/config/preinitializer.rb"
36 | end
37 | end
38 |
39 | class Boot
40 | def run
41 | load_initializer
42 | Rails::Initializer.run(:set_load_path)
43 | end
44 | end
45 |
46 | class VendorBoot < Boot
47 | def load_initializer
48 | require "#{RAILS_FRAMEWORK_ROOT}/railties/lib/initializer"
49 | Rails::Initializer.run(:install_gem_spec_stubs)
50 | end
51 | end
52 |
53 | class GemBoot < Boot
54 | def load_initializer
55 | self.class.load_rubygems
56 | load_rails_gem
57 | require 'initializer'
58 | end
59 |
60 | def load_rails_gem
61 | if version = self.class.gem_version
62 | gem 'rails', version
63 | else
64 | gem 'rails'
65 | end
66 | rescue Gem::LoadError => load_error
67 | $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
68 | exit 1
69 | end
70 |
71 | class << self
72 | def rubygems_version
73 | Gem::RubyGemsVersion rescue nil
74 | end
75 |
76 | def gem_version
77 | if defined? RAILS_GEM_VERSION
78 | RAILS_GEM_VERSION
79 | elsif ENV.include?('RAILS_GEM_VERSION')
80 | ENV['RAILS_GEM_VERSION']
81 | else
82 | parse_gem_version(read_environment_rb)
83 | end
84 | end
85 |
86 | def load_rubygems
87 | require 'rubygems'
88 | min_version = '1.1.1'
89 | unless rubygems_version >= min_version
90 | $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
91 | exit 1
92 | end
93 |
94 | rescue LoadError
95 | $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
96 | exit 1
97 | end
98 |
99 | def parse_gem_version(text)
100 | $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
101 | end
102 |
103 | private
104 | def read_environment_rb
105 | environment_rb = "#{RAILS_ROOT}/config/environment.rb"
106 | environment_rb = "#{HELPER_RAILS_ROOT}/config/environment.rb" unless File.exists?(environment_rb)
107 | File.read(environment_rb)
108 | end
109 | end
110 | end
111 | end
112 |
113 | # All that for this:
114 | Rails.boot!
115 |
--------------------------------------------------------------------------------
/spec/app_root/config/database.yml:
--------------------------------------------------------------------------------
1 | in_memory:
2 | adapter: sqlite3
3 | database: ":memory:"
4 | verbosity: quiet
5 | sqlite:
6 | adapter: sqlite
7 | dbfile: plugin_test.sqlite.db
8 | sqlite3:
9 | adapter: sqlite3
10 | dbfile: plugin_test.sqlite3.db
11 | postgresql:
12 | adapter: postgresql
13 | username: postgres
14 | password: postgres
15 | database: plugin_test
16 | mysql:
17 | adapter: mysql
18 | host: localhost
19 | username: root
20 | password:
21 | database: plugin_test
22 |
--------------------------------------------------------------------------------
/spec/app_root/config/environment.rb:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(__FILE__), 'boot')
2 |
3 | Rails::Initializer.run do |config|
4 | config.cache_classes = false
5 | config.whiny_nils = true
6 | config.action_controller.session = { :key => "_myapp_session", :secret => "gwirofjweroijger8924rt2zfwehfuiwehb1378rifowenfoqwphf23" }
7 | config.plugin_locators.unshift(
8 | Class.new(Rails::Plugin::Locator) do
9 | def plugins
10 | [Rails::Plugin.new(File.expand_path('.'))]
11 | end
12 | end
13 | ) unless defined?(PluginTestHelper::PluginLocator)
14 | end
15 |
--------------------------------------------------------------------------------
/spec/app_root/config/environments/in_memory.rb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makandra/aegis/8feac581285d38d0a9876d73f2da755ec3359678/spec/app_root/config/environments/in_memory.rb
--------------------------------------------------------------------------------
/spec/app_root/config/environments/mysql.rb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makandra/aegis/8feac581285d38d0a9876d73f2da755ec3359678/spec/app_root/config/environments/mysql.rb
--------------------------------------------------------------------------------
/spec/app_root/config/environments/postgresql.rb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makandra/aegis/8feac581285d38d0a9876d73f2da755ec3359678/spec/app_root/config/environments/postgresql.rb
--------------------------------------------------------------------------------
/spec/app_root/config/environments/sqlite.rb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makandra/aegis/8feac581285d38d0a9876d73f2da755ec3359678/spec/app_root/config/environments/sqlite.rb
--------------------------------------------------------------------------------
/spec/app_root/config/environments/sqlite3.rb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makandra/aegis/8feac581285d38d0a9876d73f2da755ec3359678/spec/app_root/config/environments/sqlite3.rb
--------------------------------------------------------------------------------
/spec/app_root/config/routes.rb:
--------------------------------------------------------------------------------
1 | ActionController::Routing::Routes.draw do |map|
2 |
3 | map.resources :properties do |properties|
4 | properties.resources :reviews
5 | end
6 |
7 | end
8 |
--------------------------------------------------------------------------------
/spec/app_root/db/migrate/001_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration
2 |
3 | def self.up
4 | create_table :users do |t|
5 | t.string :role_name
6 | t.string :name
7 | t.timestamps
8 | end
9 | end
10 |
11 | def self.down
12 | drop_table :users
13 | end
14 |
15 | end
16 |
--------------------------------------------------------------------------------
/spec/app_root/db/migrate/002_create_properties.rb:
--------------------------------------------------------------------------------
1 | class CreateProperties < ActiveRecord::Migration
2 |
3 | def self.up
4 | create_table :properties do |t|
5 | t.timestamps
6 | end
7 | end
8 |
9 | def self.down
10 | drop_table :properties
11 | end
12 |
13 | end
14 |
--------------------------------------------------------------------------------
/spec/app_root/db/migrate/003_create_reviews.rb:
--------------------------------------------------------------------------------
1 | class CreateReviews < ActiveRecord::Migration
2 |
3 | def self.up
4 | create_table :reviews do |t|
5 | t.integer :property_id
6 | t.timestamps
7 | end
8 | end
9 |
10 | def self.down
11 | drop_table :reviews
12 | end
13 |
14 | end
15 |
--------------------------------------------------------------------------------
/spec/app_root/lib/console_with_fixtures.rb:
--------------------------------------------------------------------------------
1 | # Loads fixtures into the database when running the test app via the console
2 | (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(Rails.root, '../fixtures/*.{yml,csv}'))).each do |fixture_file|
3 | Fixtures.create_fixtures(File.join(Rails.root, '../fixtures'), File.basename(fixture_file, '.*'))
4 | end
5 |
--------------------------------------------------------------------------------
/spec/app_root/log/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 |
--------------------------------------------------------------------------------
/spec/app_root/script/console:
--------------------------------------------------------------------------------
1 | irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
2 | libs = " -r irb/completion"
3 | libs << " -r test/test_helper"
4 | libs << " -r console_app"
5 | libs << " -r console_with_helpers"
6 | libs << " -r console_with_fixtures"
7 | exec "#{irb} #{libs} --simple-prompt"
8 |
--------------------------------------------------------------------------------
/spec/controllers/reviews_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../spec_helper'
2 |
3 | describe ReviewsController do
4 |
5 | before(:each) do
6 | Property.stub(:find => stub("a property", :reviews => stub("reviews", :find => "a review")))
7 | end
8 |
9 | it "should grant access in a fully integrated scenario" do
10 | lambda { get :show, :property_id => '10', :id => '20' }.should_not raise_error
11 | lambda { get :index, :property_id => '10' }.should_not raise_error
12 | end
13 |
14 | it "should deny access in a fully integrated scenario" do
15 | lambda { put :update, :property_id => '10', :id => '20' }.should raise_error(Aegis::AccessDenied)
16 | lambda { get :new, :property_id => '10' }.should raise_error(Aegis::AccessDenied)
17 | end
18 |
19 | end
20 |
--------------------------------------------------------------------------------
/spec/rcov.opts:
--------------------------------------------------------------------------------
1 | --exclude "spec/*,gems/*"
2 | --rails
--------------------------------------------------------------------------------
/spec/spec.opts:
--------------------------------------------------------------------------------
1 | --colour
2 | --format progress
3 | --loadby mtime
4 | --reverse
5 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | $: << File.join(File.dirname(__FILE__), "/../lib" )
2 |
3 | # Set the default environment to sqlite3's in_memory database
4 | ENV['RAILS_ENV'] ||= 'in_memory'
5 |
6 | # Load the Rails environment and testing framework
7 | require "#{File.dirname(__FILE__)}/app_root/config/environment"
8 | # require "#{File.dirname(__FILE__)}/../lib/aegis"
9 | require 'spec/rails'
10 |
11 | # Undo changes to RAILS_ENV
12 | silence_warnings {RAILS_ENV = ENV['RAILS_ENV']}
13 |
14 | # Run the migrations
15 | ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
16 |
17 | Spec::Runner.configure do |config|
18 | config.use_transactional_fixtures = true
19 | config.use_instantiated_fixtures = false
20 | end
21 |
22 | # Requires supporting files with custom matchers and macros, etc,
23 | # in ./support/ and its subdirectories.
24 | Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
25 |
--------------------------------------------------------------------------------
/spec/support/spec_candy.rb:
--------------------------------------------------------------------------------
1 | # Abbreviated from http://makandra.com/notes/627-the-definitive-spec_candy-rb-rspec-helper
2 |
3 | Object.class_eval do
4 |
5 | # a should receive that executes the expected method as usual. does not work with and_return
6 | def should_receive_and_execute(method)
7 | method_called = "_#{method}_called"
8 |
9 | prototype = respond_to?(:singleton_class) ? singleton_class : metaclass
10 | prototype.class_eval do
11 | define_method method do |*args|
12 | send(method_called, *args)
13 | super
14 | end
15 | end
16 |
17 | should_receive(method_called)
18 | end
19 |
20 | end
21 |
--------------------------------------------------------------------------------