helioth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f4201074868331f8ba3a418613dc76decb79e49f
4
+ data.tar.gz: 688cd395ea12d10737f0e905c5d1b8b5f8c05426
5
+ SHA512:
6
+ metadata.gz: b8726335d0d69ac6c9174567a2387eefd662468eaa4fe323682dfbff8b4ec10c7377b7dee22838411fb95d8a17e027e70364e8c117c1fd742f78c6e858392619
7
+ data.tar.gz: dc61a3a26d12f328255be0ef915427b2be1d4ed5ffc1cc07c0558e387db3ccaff7d91cfa01d7e07d4eb1ea50f6a2663f9a4263640ae301c9fd31a448f2e46990
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Guillaume Montard
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Helioth'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ Bundler::GemHelper.install_tasks
18
+
19
+ require 'rspec/core/rake_task'
20
+ require 'bundler/gem_tasks'
21
+
22
+ # Default directory to look in is `/specs`
23
+ # Run with `rake spec`
24
+ RSpec::Core::RakeTask.new(:spec) do |task|
25
+ task.rspec_opts = ['--color', '--format', 'documentation']
26
+ end
27
+
28
+ task :default => :spec
data/lib/helioth.rb ADDED
@@ -0,0 +1,25 @@
1
+ require "helioth/version"
2
+ require 'helioth/dsl'
3
+ require 'helioth/role'
4
+ require 'helioth/relation'
5
+ require 'helioth/features'
6
+ require 'helioth/feature'
7
+ require 'helioth/action'
8
+ require 'helioth/controller_additions'
9
+ require 'helioth/controller_resource'
10
+ require 'helioth/model_additions'
11
+
12
+ module Helioth
13
+
14
+ def self.dsl(file = nil)
15
+ Helioth.const_set("DSL", Helioth::Dsl.load(file)) unless const_defined?("DSL")
16
+ end
17
+
18
+ def self.const_missing(name)
19
+ if name == :DSL
20
+ Helioth.dsl
21
+ else
22
+ super
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module Helioth
2
+ class Action
3
+
4
+ attr_accessor :name, :feature
5
+
6
+ def initialize(name, &block)
7
+ @name = name
8
+ @locales = I18n.available_locales
9
+ instance_eval(&block)
10
+ end
11
+
12
+ def status(status=nil)
13
+ @status ||= status
14
+ end
15
+
16
+ def locales(*locales)
17
+ unless locales.empty?
18
+ @locales = locales
19
+ end
20
+ @locales
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ module Helioth
2
+ module ControllerAdditions
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ base.helper_method :access_to?, :locale_access_to?, :user_access_to?, :instance_access_to?
6
+ end
7
+
8
+ module ClassMethods
9
+ def load_and_authorize_for(*args)
10
+ ControllerResource.add_before_filter(self, :load_and_authorize_for, *args)
11
+ end
12
+ end
13
+
14
+ def access_to?(feature, *actions)
15
+ return false if !locale_access_to?(feature, *actions)
16
+ return true if DSL.roles.user.present? && user_access_to?(feature, *actions)
17
+ return true if DSL.roles.instance.present? && instance_access_to?(feature, *actions)
18
+ return false
19
+ end
20
+
21
+ def locale_access_to?(feature, *actions)
22
+ DSL.authorized_for_locale?(feature, actions, I18n.locale)
23
+ end
24
+
25
+ def user_access_to?(feature, *actions)
26
+ DSL.authorized_for_user?(feature, actions, current_user.helioth_role?)
27
+ end
28
+
29
+ def instance_access_to?(feature, *actions)
30
+ DSL.authorized_for_instance?(feature, actions, current_instance.helioth_role?)
31
+ end
32
+ end
33
+ end
34
+
35
+ if defined? ActionController::Base
36
+ ActionController::Base.class_eval do
37
+ include Helioth::ControllerAdditions
38
+ end
39
+ end
@@ -0,0 +1,34 @@
1
+ module Helioth
2
+
3
+ class ControllerResource
4
+
5
+ def self.add_before_filter(controller_class, method, *args)
6
+ feature = args.first
7
+ options = args.extract_options!
8
+
9
+ before_filter_method = options.delete(:prepend) ? :prepend_before_filter : :before_filter
10
+ controller_class.send(:before_filter, options.slice(:only, :except, :if, :unless)) do |controller|
11
+ ControllerResource.new(controller, feature, (options.slice(:action, :actions)).values).send(method)
12
+ end
13
+ end
14
+
15
+ def initialize(controller, feature, *actions)
16
+ @controller = controller
17
+ @feature = feature
18
+ @actions = actions.flatten
19
+ end
20
+
21
+ def load_and_authorize_for
22
+ unless @controller.access_to?(@feature, @actions)
23
+ ##TODO change the behavior based on rails env
24
+ Rails.logger.info("Access to controller forbidden for feature :#{@feature}")
25
+ @controller.render :text=>"Access forbidden", :status=>403
26
+ else
27
+ Rails.logger.debug("Access to controller granted for feature :#{@feature}")
28
+ Rails.logger.debug("Access to controller granted for actions #{@actions.inspect}") if @actions.present?
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,116 @@
1
+ module Helioth
2
+ class Dsl
3
+
4
+ DSL_FILE = Pathname.new(Rails.root || '').join("config", "helioth.rb").to_s unless defined? DSL_FILE
5
+
6
+ def initialize
7
+ Rails.logger.debug("Loading the DSL for the first time")
8
+ end
9
+
10
+ ##Should be loaded only one time using Helioth::DSL.dsl
11
+ def self.load(file = nil)
12
+ dsl = new
13
+ dsl.instance_eval(file.nil? ? File.read(DSL_FILE) : File.read(file))
14
+ return(dsl)
15
+ rescue Errno::ENOENT
16
+ raise "Helioth::DSL DSL file missing"
17
+ end
18
+
19
+ ## In case the DSL is not well used
20
+ def method_missing(method_name, *args, &block)
21
+ raise "No such method #{__method__} in #{__FILE__}"
22
+ end
23
+
24
+ ## Configure roles
25
+ def roles(&block)
26
+ @roles ||= Role.new(&block)
27
+ end
28
+
29
+ ## Configure relations
30
+ def relations(&block)
31
+ @relations ||= Relation.new(&block)
32
+ end
33
+
34
+ ## Configure features
35
+ def features(&block)
36
+ if block
37
+ @features ||= Features.new(&block)
38
+ else
39
+ @features.list
40
+ end
41
+ end
42
+
43
+ ## Get feature
44
+ def feature(feature_name)
45
+ @features.list.map{|feature|
46
+ feature if feature.name == feature_name
47
+ }.compact.first
48
+ end
49
+
50
+ ## Get feature action
51
+ def action(feature_name, action_name)
52
+ feature(feature_name).actions.map{|action|
53
+ action if action.name == action_name
54
+ }.compact.first
55
+ end
56
+
57
+ ## Check authorization
58
+ def authorized_for_locale?(feature_name, *actions_name, locale)
59
+ authorized_for(feature_name, actions_name.flatten, {locale: locale})
60
+ end
61
+
62
+ def authorized_for_user?(feature_name, *actions_name, role)
63
+ authorized_for(feature_name, actions_name.flatten, {role: role, type: :user})
64
+ end
65
+
66
+ def authorized_for_instance?(feature_name, *actions_name, role)
67
+ authorized_for(feature_name, actions_name.flatten, {role: role, type: :instance})
68
+ end
69
+
70
+ private
71
+ def authorized_for(feature_name, actions_name, options={})
72
+
73
+ role = options[:role]
74
+ type = options[:type]
75
+ locale = options[:locale]
76
+
77
+ feature, actions = process_input(feature_name, actions_name)
78
+
79
+ if feature
80
+
81
+ ## If a feature doesn"t have relation (ex: disabled feature)
82
+ return false if role.present? && relations.feature[feature.status].blank?
83
+
84
+ access = Array.new
85
+
86
+ if actions.any?
87
+ access += actions.map{|action|
88
+ if role.present?
89
+ relations.feature[action.status][type].include?(role)
90
+ elsif locale.present?
91
+ action.locales.include?(locale)
92
+ end
93
+ }
94
+ else
95
+ access << relations.feature[feature.status][type].include?(role) if role.present?
96
+ access << feature.locales.include?(locale) if locale.present?
97
+ end
98
+
99
+ access.all?
100
+ else
101
+ Rails.logger.info("Feature #{feature.try(:name)} not found")
102
+ false
103
+ end
104
+ rescue
105
+ raise "Error in method #{__method__} of #{__FILE__}"
106
+ end
107
+
108
+ def process_input(feature_name, actions_name)
109
+ feature = feature(feature_name)
110
+ actions = actions_name.flatten.map{|action_name|
111
+ action(feature_name, action_name)
112
+ }
113
+ return([feature, actions])
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,40 @@
1
+ module Helioth
2
+ class Feature
3
+
4
+ attr_accessor :name
5
+
6
+ def initialize(name, &block)
7
+ @actions = Array.new
8
+ @name = name
9
+ @locales = I18n.available_locales
10
+ instance_eval(&block)
11
+ end
12
+
13
+ def status(status = nil)
14
+ @status ||= status
15
+ end
16
+
17
+ def actions(*actions, &block)
18
+ if block.nil?
19
+ @actions
20
+ else
21
+ actions.each{|action|
22
+ @actions << Action.new(action, &block)
23
+ }
24
+ end
25
+ end
26
+
27
+ def action(action_name)
28
+ @actions.map{|action|
29
+ action if action.name == action_name
30
+ }.compact.first
31
+ end
32
+
33
+ def locales(*locales)
34
+ unless locales.empty?
35
+ @locales = locales
36
+ end
37
+ @locales
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ module Helioth
2
+ class Features
3
+
4
+ attr_accessor :list
5
+
6
+ def initialize(&block)
7
+ @list = Array.new
8
+ instance_eval(&block)
9
+ end
10
+
11
+ def feature(name, &block)
12
+ @list << Feature.new(name, &block)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,49 @@
1
+ module Helioth
2
+ module ModelAdditions
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def has_helioth_role(*args)
9
+ options = args.extract_options!
10
+ @@role_column = options[:column] || :role
11
+ @@role_instance = args.first
12
+
13
+ add_role_validation
14
+
15
+ define_method "#{@@role_column}?" do
16
+ eval("#{@@role_column}.to_sym")
17
+ end
18
+
19
+ define_method "helioth_role?" do
20
+ eval("#{@@role_column}?")
21
+ end
22
+
23
+ define_method "is_#{@@role_column}?" do |arg|
24
+ eval("self.#{@@role_column}.to_sym") == arg.to_sym
25
+ end
26
+ end
27
+
28
+ def add_role_validation
29
+ self.send(:validates, @@role_column.to_sym, inclusion: { in: available_roles, message: "%{value} is not a valid value" }, allow_blank: true)
30
+ end
31
+
32
+ def available_roles
33
+ case @@role_instance when :user
34
+ roles = DSL.roles.user.map(&:to_s)
35
+ when :instance
36
+ roles = DSL.roles.instance.map(&:to_s)
37
+ else
38
+ raise "Invalid option #{options} for method #{__method__}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ if defined? ActiveRecord::Base
46
+ ActiveRecord::Base.class_eval do
47
+ include Helioth::ModelAdditions
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ module Helioth
2
+ class Relation
3
+
4
+ def initialize(&block)
5
+ @feature = Hash.new
6
+ instance_eval(&block)
7
+ end
8
+
9
+ def feature(status = nil, &block)
10
+ if block.nil?
11
+ @feature
12
+ else
13
+ @@tmp = Hash.new
14
+ instance_eval(&block)
15
+ @feature[status] = @@tmp
16
+ end
17
+ end
18
+
19
+ def instance(*status)
20
+ @@tmp.merge!({instance: status})
21
+ end
22
+
23
+ def user(*status)
24
+ @@tmp.merge!({user: status})
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ module Helioth
2
+ class Role
3
+ def initialize(&block)
4
+ instance_eval(&block)
5
+ end
6
+
7
+ def user(*user)
8
+ @user ||= user
9
+ end
10
+
11
+ def instance(*instance)
12
+ @instance ||= instance
13
+ end
14
+
15
+ def feature(*feature)
16
+ @feature ||= feature
17
+ end
18
+ end
19
+ end