permit 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. data/.gitignore +5 -0
  2. data/.yardopts +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.mkd +238 -0
  5. data/Rakefile +69 -0
  6. data/VERSION.yml +5 -0
  7. data/generators/permit/USAGE +40 -0
  8. data/generators/permit/permit_generator.rb +25 -0
  9. data/generators/permit/templates/authorization.rb +2 -0
  10. data/generators/permit/templates/initializer.rb +37 -0
  11. data/generators/permit/templates/migration.rb +28 -0
  12. data/generators/permit/templates/role.rb +2 -0
  13. data/init.rb +1 -0
  14. data/install.rb +1 -0
  15. data/lib/models/association.rb +89 -0
  16. data/lib/models/authorizable.rb +31 -0
  17. data/lib/models/authorization.rb +54 -0
  18. data/lib/models/person.rb +148 -0
  19. data/lib/models/role.rb +59 -0
  20. data/lib/permit/controller.rb +132 -0
  21. data/lib/permit/permit_rule.rb +198 -0
  22. data/lib/permit/permit_rules.rb +141 -0
  23. data/lib/permit/support.rb +67 -0
  24. data/lib/permit.rb +134 -0
  25. data/permit.gemspec +91 -0
  26. data/rails/init.rb +7 -0
  27. data/spec/models/alternate_models_spec.rb +54 -0
  28. data/spec/models/authorizable_spec.rb +78 -0
  29. data/spec/models/authorization_spec.rb +77 -0
  30. data/spec/models/person_spec.rb +278 -0
  31. data/spec/models/role_spec.rb +121 -0
  32. data/spec/permit/controller_spec.rb +308 -0
  33. data/spec/permit/permit_rule_spec.rb +452 -0
  34. data/spec/permit/permit_rules_spec.rb +273 -0
  35. data/spec/permit_spec.rb +58 -0
  36. data/spec/spec_helper.rb +73 -0
  37. data/spec/support/helpers.rb +13 -0
  38. data/spec/support/models.rb +38 -0
  39. data/spec/support/permits_controller.rb +7 -0
  40. data/tasks/permit_tasks.rake +4 -0
  41. data/uninstall.rb +1 -0
  42. metadata +107 -0
@@ -0,0 +1,67 @@
1
+ module Permit
2
+ module Support
3
+ module ClassMethods
4
+ def class_symbol
5
+ class_name.underscore.to_sym
6
+ end
7
+
8
+ def plural_class_symbol
9
+ class_name.pluralize.underscore.to_sym
10
+ end
11
+
12
+ private
13
+ def permit_authorized_model(custom_options = {})
14
+ options = {:class_name => Permit::Config.authorization_class.name, :extend => Permit::Models::AssociationExtensions}.merge(custom_options)
15
+ has_many Permit::Config.authorization_class.plural_class_symbol, options
16
+
17
+ class_eval <<-END
18
+ protected
19
+ def permit_authorizations_proxy
20
+ #{Permit::Config.authorization_class.plural_class_symbol}
21
+ end
22
+ END
23
+ end
24
+ end
25
+
26
+ # Converts an object to an array of that object if it is not already one.
27
+ #
28
+ # @param [Object] o the object to be made into an Array
29
+ # @return [Array] the object as an array.
30
+ def permit_arrayify(o)
31
+ Array===o ? o : [o]
32
+ end
33
+
34
+ protected
35
+ def authorization_conditions(role, resource, person = nil)
36
+ conditions = {}
37
+ conditions[Permit::Config.person_class.name.foreign_key] = person.id if person
38
+ conditions.merge! role_condition(role)
39
+ conditions.merge! resource_conditions(resource)
40
+ end
41
+
42
+ def role_condition(roles)
43
+ return {} unless roles
44
+
45
+ r = get_roles(roles)
46
+ ids = r.collect {|role| role.id}
47
+
48
+ return (ids.empty? ? {} : {Permit::Config.role_class.name.foreign_key => ids})
49
+ end
50
+
51
+ def resource_conditions(resource)
52
+ case resource
53
+ when :any then {}
54
+ when nil then {:resource_type => nil, :resource_id => nil}
55
+ else {:resource_type => resource.class.resource_type, :resource_id => resource.id}
56
+ end
57
+ end
58
+
59
+ def get_role(role)
60
+ role.is_a?(Permit::Config.role_class) ? role : Permit::Config.role_class.find_by_key(role)
61
+ end
62
+
63
+ def get_roles(roles)
64
+ permit_arrayify(roles).collect {|r| get_role(r)}.compact
65
+ end
66
+ end
67
+ end
data/lib/permit.rb ADDED
@@ -0,0 +1,134 @@
1
+ require File.dirname(__FILE__) + '/permit/support'
2
+ require File.dirname(__FILE__) + '/permit/permit_rule'
3
+ require File.dirname(__FILE__) + '/permit/permit_rules'
4
+ require File.dirname(__FILE__) + '/permit/controller'
5
+ require File.dirname(__FILE__) + '/models/association'
6
+ require File.dirname(__FILE__) + '/models/role'
7
+ require File.dirname(__FILE__) + '/models/person'
8
+ require File.dirname(__FILE__) + '/models/authorization'
9
+ require File.dirname(__FILE__) + '/models/authorizable'
10
+
11
+ module Permit
12
+ # allow|deny [:named_role|:person|:all|:guest|[]],
13
+ # (([:who|:that] => :method,)
14
+ # [:of|:on] => :instance_var,)
15
+ # :to => [:action|[]]
16
+ # (,:if => <method_name or proc>)
17
+ # (,:unless => <method_name or proc>)
18
+ #
19
+
20
+ # Raised when a {PermitRule} cannot be configured.
21
+ class PermitConfigurationError < StandardError; end
22
+
23
+ # Raised when an error occurs during evaluation of a {PermitRule}.
24
+ class PermitEvaluationError < StandardError; end
25
+
26
+ # Contains the configuration rules that Permit will apply during its
27
+ # processing.
28
+ #
29
+ # +role_class+, +authorization_class+, and +person_class+ are the model
30
+ # classes defined as representing their respective names by defining the
31
+ # corresponding <tt>permit_*</tt> method. +authorizable_classes+ is an array
32
+ # of all classes that are authorizable to roles by having defined
33
+ # +permit_authorizable+.
34
+ class Config
35
+ @@authorization_class, @@person_class, @@role_class = nil, nil, nil
36
+ @@models_defined = false
37
+ @@authorizable_classes = []
38
+ @@controller_subject_method = nil
39
+
40
+ @@action_aliases = {
41
+ :create => [:new, :create],
42
+ :update => [:edit, :update],
43
+ :destroy => [:delete, :destroy],
44
+ :read => [:index, :show],
45
+ :write => [:new, :create, :edit, :update]
46
+ }
47
+
48
+ # Indicates the response returned by {PermitRules#permitted?} when no rules
49
+ # match. If set to +:allow+ then the person will be granted access. If set
50
+ # to anything else, they will be denied.
51
+ @@default_access = :deny
52
+
53
+ class << self
54
+ # The class that currently represents authorizations in the system, as set
55
+ # by {set_core_models}.
56
+ def authorization_class; @@authorization_class; end
57
+ # The class that currently represents authorization subjects in the
58
+ # system, as set by {set_core_models}.
59
+ def person_class; @@person_class; end
60
+ # The class that curretly represents roles in the system, as set by
61
+ # {set_core_models}.
62
+ def role_class; @@role_class; end
63
+ # Classes that are marked as authorizable resources using
64
+ # {Permit::Models::AuthorizableExtensions::AuthorizableClassMethods#permit_authorizable permit_authorizable}.
65
+ def authorizable_classes; @@authorizable_classes; end
66
+
67
+ # Actions that when given to {PermitRules#allow}, and {PermitRules#deny}
68
+ # will be expanded into the actions given in the value array.
69
+ def action_aliases; @@action_aliases; end
70
+
71
+ # Indicates the response that PermitRules will take if no
72
+ # authorizations match. If set to +:allow+ then a subject will be given
73
+ # access unless denied. By default this is set to +:deny+
74
+ #
75
+ # @return the current default access.
76
+ def default_access; @@default_access; end
77
+
78
+ # Sets the response that PermitRules will use when no rules match.
79
+ #
80
+ # @param [:allow, :deny] access the default response to use.
81
+ def default_access=(access); @@default_access = access; end
82
+
83
+ # The method to use to retrieve the current authorization subject when
84
+ # rules are being evaluated. If nil, then the method will be inferred from
85
+ # the subject set in the call to {set_core_models}.
86
+ #
87
+ # @return [Symbol, nil]
88
+ def controller_subject_method; @@controller_subject_method; end
89
+
90
+ # Sets the name of the method to use to retrieve the current subject
91
+ # while checking authorizations. Set to nil, to infer the value from the
92
+ # subject set in {set_core_models}, or :current_person if named
93
+ # authorizations are not being used.
94
+ #
95
+ # @param [nil, Symbol] method a symbol representing the method to use.
96
+ def controller_subject_method=(method); @@controller_subject_method = method; end
97
+
98
+ # Sets the core authorization, person, and role models to be used for
99
+ # named authorizations, and configures them with their respective permit_*
100
+ # methods.
101
+ #
102
+ # @param [Class] authorization an ActiveRecord model representing
103
+ # authorizations.
104
+ # @param [Class] person an ActiveRecord model representing
105
+ # people.
106
+ # @param [Class] role an ActiveRecord model representing
107
+ # roles.
108
+ def set_core_models(authorization, person, role)
109
+ #raise PermitConfigurationError, "Core models cannot be redefined." if @@models_defined
110
+
111
+ @@authorization_class = authorization
112
+ @@person_class = person
113
+ @@role_class = role
114
+
115
+ @@authorization_class.send :permit_authorization
116
+ @@person_class.send :permit_person
117
+ @@role_class.send :permit_role
118
+ end
119
+
120
+ # Forces Permit to reload its core classes based off of those given in the
121
+ # initial call to Permit::Config.set_core_models. This is primarily needed
122
+ # so that Permit will work in Rails development mode because of class
123
+ # caching/reloading. These variables hang onto the original models as they
124
+ # were defined and end up in a weird state. Production does not experience
125
+ # this problem.
126
+ def reset_core_models
127
+ authz = Object.const_get authorization_class.name
128
+ person = Object.const_get person_class.name
129
+ role = Object.const_get role_class.name
130
+ Permit::Config.set_core_models(authz, person, role)
131
+ end
132
+ end
133
+ end
134
+ end
data/permit.gemspec ADDED
@@ -0,0 +1,91 @@
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{permit}
8
+ s.version = "0.9.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Steve Valaitis"]
12
+ s.date = %q{2010-03-27}
13
+ s.email = %q{steve@digitalnothing.com}
14
+ s.extra_rdoc_files = [
15
+ "README.mkd"
16
+ ]
17
+ s.files = [
18
+ ".gitignore",
19
+ ".yardopts",
20
+ "MIT-LICENSE",
21
+ "README.mkd",
22
+ "Rakefile",
23
+ "VERSION.yml",
24
+ "generators/permit/USAGE",
25
+ "generators/permit/permit_generator.rb",
26
+ "generators/permit/templates/authorization.rb",
27
+ "generators/permit/templates/initializer.rb",
28
+ "generators/permit/templates/migration.rb",
29
+ "generators/permit/templates/role.rb",
30
+ "init.rb",
31
+ "install.rb",
32
+ "lib/models/association.rb",
33
+ "lib/models/authorizable.rb",
34
+ "lib/models/authorization.rb",
35
+ "lib/models/person.rb",
36
+ "lib/models/role.rb",
37
+ "lib/permit.rb",
38
+ "lib/permit/controller.rb",
39
+ "lib/permit/permit_rule.rb",
40
+ "lib/permit/permit_rules.rb",
41
+ "lib/permit/support.rb",
42
+ "permit.gemspec",
43
+ "rails/init.rb",
44
+ "spec/models/alternate_models_spec.rb",
45
+ "spec/models/authorizable_spec.rb",
46
+ "spec/models/authorization_spec.rb",
47
+ "spec/models/person_spec.rb",
48
+ "spec/models/role_spec.rb",
49
+ "spec/permit/controller_spec.rb",
50
+ "spec/permit/permit_rule_spec.rb",
51
+ "spec/permit/permit_rules_spec.rb",
52
+ "spec/permit_spec.rb",
53
+ "spec/spec_helper.rb",
54
+ "spec/support/helpers.rb",
55
+ "spec/support/models.rb",
56
+ "spec/support/permits_controller.rb",
57
+ "tasks/permit_tasks.rake",
58
+ "uninstall.rb"
59
+ ]
60
+ s.homepage = %q{http://github.com/dnd/permit}
61
+ s.rdoc_options = ["--charset=UTF-8"]
62
+ s.require_paths = ["lib"]
63
+ s.rubygems_version = %q{1.3.5}
64
+ s.summary = %q{A flexible authorization plugin for Ruby on Rails.}
65
+ s.test_files = [
66
+ "spec/spec_helper.rb",
67
+ "spec/support/helpers.rb",
68
+ "spec/support/models.rb",
69
+ "spec/support/permits_controller.rb",
70
+ "spec/models/alternate_models_spec.rb",
71
+ "spec/models/person_spec.rb",
72
+ "spec/models/role_spec.rb",
73
+ "spec/models/authorizable_spec.rb",
74
+ "spec/models/authorization_spec.rb",
75
+ "spec/permit_spec.rb",
76
+ "spec/permit/permit_rules_spec.rb",
77
+ "spec/permit/controller_spec.rb",
78
+ "spec/permit/permit_rule_spec.rb"
79
+ ]
80
+
81
+ if s.respond_to? :specification_version then
82
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
83
+ s.specification_version = 3
84
+
85
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
86
+ else
87
+ end
88
+ else
89
+ end
90
+ end
91
+
data/rails/init.rb ADDED
@@ -0,0 +1,7 @@
1
+ #require 'permit'
2
+
3
+ ActiveRecord::Base.send :include,
4
+ Permit::Models::AuthorizationExtensions,
5
+ Permit::Models::AuthorizableExtensions,
6
+ Permit::Models::RoleExtensions,
7
+ Permit::Models::PersonExtensions
@@ -0,0 +1,54 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module Permit::Specs
4
+ class User < ActiveRecord::Base; end
5
+ class Entitlement < ActiveRecord::Base; end
6
+ class Job < ActiveRecord::Base; end
7
+
8
+ describe "Alternate core models" do
9
+ before :all do
10
+ @auth = Permit::Config.authorization_class
11
+ @person = Permit::Config.person_class
12
+ @role = Permit::Config.role_class
13
+
14
+ Permit::Config.set_core_models(Permit::Specs::Entitlement, Permit::Specs::User, Permit::Specs::Job)
15
+ end
16
+
17
+ before do
18
+ @sam = User.create! :name => 'sam'
19
+ @superapp = Project.create! :name => 'superapp'
20
+ @lameapp = Project.create! :name => 'lameapp'
21
+ @dev = Job.create! :key => :developer, :name => 'developer'
22
+ @tester = Job.create! :key => :tester, :name => 'tester'
23
+
24
+ @sam.authorize [@dev, @tester], @superapp
25
+ @sam.authorize @dev, @lameapp
26
+ end
27
+
28
+ it "subject model should have an entitlements association" do
29
+ @sam.should have(3).entitlements
30
+ end
31
+
32
+ it "role model should have an entitlements association" do
33
+ @dev.should have(2).entitlements
34
+ end
35
+
36
+ describe "entitlements association" do
37
+ it "should have a users_as method" do
38
+ @dev.entitlements.users_as(@dev).should have(1).item
39
+ end
40
+
41
+ it "should have a users_for method" do
42
+ @dev.entitlements.users_for(@superapp).should have(1).item
43
+ end
44
+
45
+ it "should have a jobs_for method" do
46
+ @sam.entitlements.jobs_for(@superapp).should have(2).item
47
+ end
48
+ end
49
+
50
+ after :all do
51
+ Permit::Config.set_core_models(@auth, @person, @role)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,78 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module Permit::Specs
4
+ describe "Permit::Models::Authorizable#permit_authorizable" do
5
+ describe "defined method self#resource_type" do
6
+ it "should return the db resource_type value" do
7
+ Project.resource_type.should == 'Permit::Specs::Project'
8
+ end
9
+ end
10
+
11
+ context "created instance methods" do
12
+ before do
13
+ @bob = Person.create :name => "bob"
14
+ @tom = Person.create :name => "tom"
15
+ @hotness = Project.create(:name => "hotness")
16
+ @maintenance = Project.create(:name => "maintenance")
17
+ Role.create :key => :site_admin, :name => 'site admin', :authorize_resource => false, :requires_resource => false
18
+ new_authz @bob, :site_admin, nil
19
+ new_authz @bob, :admin, @maintenance
20
+ new_authz @bob, :developer, @maintenance
21
+ new_authz @bob, :team_lead, @hotness
22
+
23
+ new_authz @tom, :admin, @hotness
24
+ end
25
+
26
+ context "authorizations" do
27
+ it "should have all authorizations that apply to the current resource" do
28
+ @hotness.should have(2).authorizations
29
+ @hotness.authorizations[0].person.should == @bob
30
+ @hotness.authorizations[0].role.key.should == 'team_lead'
31
+ @hotness.authorizations[1].person.should == @tom
32
+ @hotness.authorizations[1].role.key.should == 'admin'
33
+ end
34
+ end
35
+
36
+ context "for finding authorizations for given roles" do
37
+ before do
38
+ @bob = Person.create :name => "bob"
39
+ @tom = Person.create :name => "tom"
40
+ @hotness = Project.create(:name => "hotness")
41
+ @maintenance = Project.create(:name => "maintenance")
42
+ Role.create :key => :site_admin, :name => 'site admin', :authorize_resource => false, :requires_resource => false
43
+ new_authz @bob, :site_admin, nil
44
+ new_authz @bob, :admin, @maintenance
45
+ new_authz @bob, :developer, @maintenance
46
+ new_authz @bob, :team_lead, @hotness
47
+
48
+ new_authz @tom, :admin, @hotness
49
+ end
50
+
51
+ it "#authorizations.people_as should return the people as the roles for the current resource" do
52
+ @jack = Person.create :name => 'jack'
53
+ @jack.authorize :admin, @maintenance
54
+ people = @maintenance.authorizations.people_as :admin
55
+ people.should have(2).items
56
+ people[0].should == @bob
57
+ people[1].should == @jack
58
+ end
59
+
60
+ it "#authorizations.people_as should not contain duplicates" do
61
+ people = @maintenance.authorizations.people_as [:admin, :developer]
62
+ people.should have(1).item
63
+ people[0].should == @bob
64
+ end
65
+
66
+ it "#authorizations.as should return the authorizations for given roles on the current resource" do
67
+ authz = @maintenance.authorizations.as [:admin, :developer]
68
+ authz.should have(2).items
69
+ authz[0].person.should == @bob
70
+ authz[0].role.key.should == 'admin'
71
+ authz[1].person.should == @bob
72
+ authz[1].role.key.should == 'developer'
73
+
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,77 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module Permit::Specs
4
+ describe "Permit::Models::AuthorizationExtensions#permit_authorization" do
5
+ describe "validations" do
6
+ it "should be invalid without a role" do
7
+ a = Authorization.new
8
+ a.should_not be_valid
9
+ a.should have(1).error_on(:role)
10
+ a.errors[:role].should == "can't be blank"
11
+ end
12
+
13
+ it "should be invalid without a person" do
14
+ a = Authorization.new
15
+ a.should_not be_valid
16
+ a.should have(1).error_on(:person)
17
+ a.errors[:person].should == "can't be blank"
18
+ end
19
+
20
+ it "should be invalid without a resource if the role requires one" do
21
+ r = Role.new :requires_resource => true
22
+ a = Authorization.new :role => r
23
+ a.should_not be_valid
24
+ a.should have(1).error_on(:resource)
25
+ a.errors[:resource].should == "can't be blank"
26
+ end
27
+
28
+ it "should be invalid with a resource if the role doesn't allow one" do
29
+ r = Role.new :authorize_resource => false, :requires_resource => false
30
+ a = Authorization.new :role => r
31
+ a.resource = Project.create :name => "First"
32
+
33
+ a.should_not be_valid
34
+ a.should have(1).error_on(:resource)
35
+ a.errors[:resource].should == "Specific resources may not be granted for this role."
36
+ end
37
+
38
+ it "should allow a resource if the role allows but doesn't require a resource" do
39
+ r = Role.new :authorize_resource => true, :requires_resource => false
40
+ a = Authorization.new :role => r
41
+ a.resource = Project.create :name => "First"
42
+
43
+ a.valid?
44
+ a.should have(0).errors_on(:resource)
45
+ end
46
+
47
+ it "should allow no resource if the role allows but doesn't require a resource" do
48
+ r = Role.new :authorize_resource => true, :requires_resource => false
49
+ a = Authorization.new :role => r
50
+
51
+ a.valid?
52
+ a.should have(0).errors_on(:resource)
53
+ end
54
+
55
+ it "should be invalid if the person is already authorized for the given role and resource" do
56
+ p = Person.create :name => 'bob'
57
+ r = Role.create :key => :admin, :name => "Admin"
58
+ project = Project.create! :name => "First"
59
+ Authorization.create! :role => r, :person => p, :resource => project
60
+
61
+ a = Authorization.new :role => r, :person => p, :resource => project
62
+ a.should_not be_valid
63
+ a.should have(1).error_on(:role)
64
+ a.errors[:role].should == "This person is already authorized for this resource"
65
+ end
66
+
67
+ it "should be valid with valid attributes" do
68
+ p = Person.create :name => 'bob'
69
+ r = Role.create :key => :admin, :name => "Admin"
70
+ a = Authorization.new :role => r, :person => p
71
+ a.resource = Project.create :name => "First"
72
+
73
+ a.save.should be_true
74
+ end
75
+ end
76
+ end
77
+ end