infopark_reactor 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +165 -0
  4. data/README.md +142 -0
  5. data/Rakefile +25 -0
  6. data/app/controllers/reactor_controller.rb +53 -0
  7. data/config/routes.rb +6 -0
  8. data/infopark_reactor.gemspec +30 -0
  9. data/lib/engine.rb +35 -0
  10. data/lib/infopark_reactor.rb +38 -0
  11. data/lib/reactor/already_released.rb +3 -0
  12. data/lib/reactor/attributes.rb +365 -0
  13. data/lib/reactor/attributes/date_serializer.rb +35 -0
  14. data/lib/reactor/attributes/html_serializer.rb +31 -0
  15. data/lib/reactor/attributes/link_list_extender.rb +50 -0
  16. data/lib/reactor/attributes/link_list_serializer.rb +27 -0
  17. data/lib/reactor/cache/permission.rb +36 -0
  18. data/lib/reactor/cache/user.rb +37 -0
  19. data/lib/reactor/legacy.rb +60 -0
  20. data/lib/reactor/link/external.rb +19 -0
  21. data/lib/reactor/link/internal.rb +19 -0
  22. data/lib/reactor/link/temporary_link.rb +43 -0
  23. data/lib/reactor/no_working_version.rb +1 -0
  24. data/lib/reactor/not_permitted.rb +1 -0
  25. data/lib/reactor/permission.rb +262 -0
  26. data/lib/reactor/persistence.rb +473 -0
  27. data/lib/reactor/rc_independent.rb +13 -0
  28. data/lib/reactor/session.rb +48 -0
  29. data/lib/reactor/session/observers.rb +32 -0
  30. data/lib/reactor/session/user.rb +22 -0
  31. data/lib/reactor/streaming_upload.rb +22 -0
  32. data/lib/reactor/sudo.rb +11 -0
  33. data/lib/reactor/support/link_matcher.rb +44 -0
  34. data/lib/reactor/tools/workflow_generator.rb +195 -0
  35. data/lib/reactor/validations.rb +55 -0
  36. data/lib/reactor/version.rb +3 -0
  37. data/lib/reactor/workflow.rb +19 -0
  38. data/lib/reactor/workflow/empty.rb +27 -0
  39. data/lib/reactor/workflow/standard.rb +34 -0
  40. metadata +204 -0
@@ -0,0 +1,50 @@
1
+ require 'reactor/link/temporary_link'
2
+
3
+ module Reactor
4
+ module Attributes
5
+ module LinkListExtender
6
+ def self.extend_linklist!
7
+ # this will trigger rails autoload
8
+ extend_given_linklist!(RailsConnector::LinkList)
9
+ end
10
+
11
+ def self.extend_given_linklist!(klass)
12
+ unless klass.instance_methods(false).include?("changed?")
13
+ klass.class_eval do
14
+ def <<(link_data)
15
+ super(transform_into_link(link_data))
16
+ end
17
+
18
+ # install #size_changed callback
19
+ Array.instance_methods(false).each do |meth|
20
+ old = instance_method(meth)
21
+ define_method(meth) do |*args, &block|
22
+ old_size = size
23
+ ret = old.bind(self).call(*args, &block)
24
+ size_changed(old_size, size) if old_size != size
25
+ ret
26
+ end if meth.to_sym != :size
27
+ end
28
+
29
+ def changed?
30
+ @changed == true
31
+ end
32
+
33
+ protected
34
+ def size_changed(old_size, size)
35
+ @changed = true
36
+ end
37
+
38
+ def transform_into_link(link_data)
39
+ if (link_data.respond_to?(:external?) && link_data.respond_to?(:internal?))
40
+ link_data
41
+ else
42
+ Reactor::Link::TemporaryLink.new(link_data)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,27 @@
1
+ require 'reactor/link/temporary_link'
2
+ require 'infopark_rails_connector'
3
+
4
+ module Reactor
5
+ module Attributes
6
+ class LinkListSerializer
7
+ def initialize(attr, value)
8
+ @attr, @value = attr, value
9
+ end
10
+
11
+ def serialize
12
+ linklist = RailsConnector::LinkList.new([])
13
+ enumerate(@value).each do |link_data|
14
+ linklist << link_data
15
+ end
16
+ linklist
17
+ end
18
+
19
+ private
20
+ def enumerate(value)
21
+ return [] if value.nil? || value.blank?
22
+ return [value] unless value.kind_of?(Array)
23
+ return value
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ require 'singleton'
2
+
3
+ module Reactor
4
+ module Cache
5
+ class Permission
6
+ include Singleton
7
+
8
+ def lookup(user, key, &block)
9
+ set(user, key, yield) unless set?(user, key)
10
+ get(user, key)
11
+ end
12
+
13
+ def initialize
14
+ @cache = {}
15
+ end
16
+
17
+ def invalidate(user)
18
+ @cache[user] = {}
19
+ end
20
+
21
+ protected
22
+ def set?(user, key)
23
+ @cache.key?(user) && @cache[user].include?(key)
24
+ end
25
+
26
+ def set(user, key, value)
27
+ @cache[user] ||= {}
28
+ @cache[user][key] = value
29
+ end
30
+
31
+ def get(user, key)
32
+ @cache[user][key]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ require 'singleton'
2
+
3
+ module Reactor
4
+ module Cache
5
+ class User
6
+ include Singleton
7
+
8
+ def get(user_name)
9
+ @cache ||= {}
10
+ # Rails.logger.debug "User:Cache hit: #{hit?(user_name.to_s)} [#{@cache[user_name.to_s].inspect}]"
11
+
12
+ key = user_name.to_s
13
+ if hit?(key)
14
+ @cache[key]
15
+ else
16
+ @cache[key] = Reactor::Session::User.new(key)
17
+ end
18
+ end
19
+
20
+ def set(user_name, user)
21
+ @cache ||= {}
22
+ @cache[user_name.to_s] = user
23
+ end
24
+
25
+ def invalidate(user_name)
26
+ @cache ||= {}
27
+ @cache[user_name.to_s] = nil
28
+ end
29
+
30
+ private
31
+ def hit?(user_name)
32
+ key = user_name.to_s
33
+ @cache.key?(key) && !@cache[key].nil?
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,60 @@
1
+ module Reactor
2
+ # This is a collection of legacy method,s that have not found any better place to live.
3
+ module Legacy
4
+ module Base
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ # removes CMS objects underneath current object
10
+ # @option options :no_children if true prevents deletion unless there are no children
11
+ # @option options :img_children_only if true prevents deletion unless there are exclusively img children
12
+ # @option options [Array] :children if true prevents deletion if there are other children besides the specified ones (array of names)
13
+ def delete_children!(options={})
14
+ # TODO: provide better discrimination mechanisms (blocks?)
15
+ f_nochild = options.delete(:no_children)
16
+ f_imgchild = options.delete(:img_children_only)
17
+ f_children = options.delete(:children)
18
+
19
+ mychildren = self.children
20
+ image_children, other_children = mychildren.partition {|obj| obj.obj_class == 'Image'}
21
+
22
+ # are there any links pointing to this container
23
+ return false if has_super_links?
24
+ # is the flag set and are there any children
25
+ return false if f_nochild && !mychildren.empty?
26
+ # is the flag set and are there any children besides images
27
+ return false if f_imgchild && !other_children.empty?
28
+ # is the flag set and are there any children besides the specified ones
29
+ return false if f_children && !((mychildren.map(&:name) - f_children).empty?)
30
+
31
+ # check children for any links pointing to them
32
+ return false if mychildren.detect(&:has_super_links?)
33
+ # check if there are any grandchildren
34
+ return false if mychildren.detect {|child| !child.children.empty?}
35
+
36
+ # delete children
37
+ mychildren.each(&:destroy)
38
+ return true
39
+ end
40
+ end
41
+ module ClassMethods
42
+ def path_from_anything(anything)
43
+ obj_from_anything(anything).try(:path)
44
+ end
45
+
46
+ def obj_from_anything(anything)
47
+ case anything
48
+ when Fixnum then Obj.find(anything)
49
+ when String then Obj.find_by_path(anything)
50
+ when Obj then anything
51
+ else raise ArgumentError, "Link target must Fixnum, String or Obj, but was #{anything.class}."
52
+ end
53
+ end
54
+
55
+ def obj_id_from_anything(anything)
56
+ obj_from_anything(anything).try(:obj_id)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,19 @@
1
+ module Reactor
2
+ module Link
3
+ class External
4
+ attr_accessor :url
5
+
6
+ def external? ; true ; end
7
+ def internal? ; false ; end
8
+
9
+ def initialize(url)
10
+ raise TypeError, "#{self.class.name} is deprecated!"
11
+ self.url = url
12
+ end
13
+
14
+ def id
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Reactor
2
+ module Link
3
+ class Internal
4
+ attr_accessor :destination_object
5
+
6
+ def external? ; false ; end
7
+ def internal? ; true ; end
8
+
9
+ def initialize(anything)
10
+ raise TypeError, "#{self.class.name} is deprecated!"
11
+ self.destination_object = Obj.obj_from_anything(anything)
12
+ end
13
+
14
+ def id
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ module Reactor
2
+ module Link
3
+ class TemporaryLink
4
+ attr_reader :url
5
+ attr_accessor :title
6
+
7
+ def external? ; raise TypeError, "This link needs to be persisted to gain any meaningful information" ; end
8
+ def internal? ; false ; end
9
+
10
+ def initialize(anything)
11
+ link_data = {}
12
+
13
+ case anything
14
+ when Hash
15
+ link_data = anything
16
+ else
17
+ link_data[:target] = anything
18
+ end
19
+
20
+ self.url = link_data[:target] || link_data[:url] || link_data[:destination_object]
21
+ self.title = link_data[:title] if link_data.key?(:title)
22
+ end
23
+
24
+ def url=(some_target)
25
+ @url = case some_target
26
+ when Obj
27
+ @destination_object = some_target
28
+ some_target.path
29
+ else
30
+ some_target
31
+ end
32
+ end
33
+
34
+ def destination_object
35
+ @destination_object ||= Obj.find_by_path(url)
36
+ end
37
+
38
+ def id
39
+ nil
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1 @@
1
+ class Reactor::NoWorkingVersion < StandardError ; end
@@ -0,0 +1 @@
1
+ class Reactor::NotPermitted < StandardError ; end
@@ -0,0 +1,262 @@
1
+ require 'reactor/session'
2
+ require 'reactor/cache/permission'
3
+ require 'reactor/cache/user'
4
+
5
+ module Reactor
6
+
7
+ # This module adds #permission method to obj that act as a gateway for permission checking,
8
+ # see documentation for [Permission::PermissionProxy] for more details.
9
+ # @see [Permission::PermissionProxy]
10
+ #
11
+ # Including this module also alters typical ActiveRecord call chain, so that it becomes:
12
+ # 1. permission checking
13
+ # 2. validations (if [Reactor::Validations] is included)
14
+ # 3. callbacks (before_*, around_*, after_*)
15
+ #
16
+ # Therefore if the user lacks permissions no futher actions are executed.
17
+ module Permission
18
+
19
+ module Base
20
+
21
+ # @see [PermissionProxy]
22
+ def permission
23
+ @permission ||= PermissionProxy.new(self)
24
+ end
25
+
26
+ # Wraps around Reactor::Persistence::Base#release! and ensures
27
+ # that current user has required permissions to release the object
28
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
29
+ def release!
30
+ ensure_permission_granted(:release)
31
+ return super
32
+ end
33
+
34
+ # Wraps around Reactor::Persistence::Base#take! and ensures
35
+ # that current user has required permissions to take the object
36
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
37
+ def take!
38
+ ensure_permission_granted(:take)
39
+ return super
40
+ end
41
+
42
+ # Wraps around Reactor::Persistence::Base#edit! and ensures
43
+ # that current user has required permissions to edit the object
44
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
45
+ def edit!
46
+ ensure_permission_granted(:edit)
47
+ return super
48
+ end
49
+
50
+ # Wraps around ActiveRecord::Persistence#save and ensures
51
+ # that current user has required permissions to save the object
52
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
53
+ def save
54
+ if persisted?
55
+ ensure_permission_granted(:write)
56
+ else
57
+ ensure_create_permission_granted(self.parent_obj_id)
58
+ end
59
+ return super
60
+ rescue Reactor::NotPermitted
61
+ return false
62
+ end
63
+
64
+ # Wraps around ActiveRecord::Persistence#save! and ensures
65
+ # that current user has required permissions to save the object
66
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
67
+ def save!
68
+ if persisted?
69
+ ensure_permission_granted(:write)
70
+ else
71
+ ensure_create_permission_granted(self.parent_obj_id)
72
+ end
73
+ return super
74
+ end
75
+
76
+ # Wraps around Reactor::Persistence::Base#resolve_refs! and ensures
77
+ # that current user has required permissions to call resolve refs on the object
78
+ # @raise [Reactor::NotPermitted] user lacks neccessary permission
79
+ def resolve_refs!
80
+ ensure_permission_granted(:write)
81
+ return super
82
+ end
83
+
84
+ private
85
+
86
+ def ensure_permission_granted(type)
87
+ raise Reactor::NotPermitted, "#{self.path} lacks neccessary permissions for #{type}" unless self.permission.send("#{type}?")
88
+ return true
89
+ end
90
+
91
+ def ensure_create_permission_granted(obj_id)
92
+ raise Reactor::NotPermitted, 'Obj lacks neccessary permissions for creation' unless Obj.find(obj_id).permission.create_children?
93
+ return true
94
+ end
95
+
96
+ end
97
+
98
+ # This class acts as a proxy to underlying permission checking classes.
99
+ # There are three possible cases for each permission type (live, read, write, root, create_children):
100
+ # 1. Given user is SuperUser - all permissions granted
101
+ # 2. Given user has the permission
102
+ # 3. Given user doesn't have the permission
103
+ class PermissionProxy
104
+
105
+ def initialize(obj) #:nodoc:
106
+ @obj = obj
107
+ @cache = Reactor::Cache::Permission.instance
108
+ @lookup = PermissionLookup.new(obj)
109
+ end
110
+
111
+ # Returns true if given user (or current user, if none given) has 'live' permission
112
+ def live?(user=nil)
113
+ granted?(user, :live)
114
+ end
115
+
116
+ # Returns true if given user (or current user, if none given) has 'read' permission
117
+ def read?(user = nil)
118
+ granted?(user, :read)
119
+ end
120
+
121
+ # Returns true if given user (or current user, if none given) has 'write' permission
122
+ def write?(user = nil)
123
+ granted?(user, :write)
124
+ end
125
+
126
+ # Returns true if given user (or current user, if none given) has 'root' permission
127
+ def root?(user = nil)
128
+ granted?(user, :root)
129
+ end
130
+
131
+ # Returns true if given user (or current user, if none given) has 'create_children' permission
132
+ def create_children?(user = nil)
133
+ granted?(user, :create_children)
134
+ end
135
+
136
+ # @see #root?
137
+ def delete?(user = nil)
138
+ root?(user)
139
+ end
140
+
141
+ # @see #write?
142
+ def take?(user = nil)
143
+ write?(user)
144
+ end
145
+
146
+ # @see #write?
147
+ def edit?(user = nil)
148
+ write?(user)
149
+ end
150
+
151
+ # Returns true if given user has permissions required to release an object (the exact
152
+ # permissions depend on the state of the object)
153
+ def release?(user = nil)
154
+ (has_workflow? && root?(user)) || (!has_workflow? && write?(user))
155
+ end
156
+
157
+ # Setter to overwrite the current groups for the given +permission+ with the
158
+ # given +groups+.
159
+ def set(permission, groups)
160
+ identifier = identifier(permission)
161
+
162
+ crul_obj.permission_set(identifier, groups)
163
+ end
164
+
165
+ # Grants the given +groups+ the given +permission+, without effecting
166
+ # already existing groups.
167
+ def grant(permission, groups)
168
+ identifier = identifier(permission)
169
+
170
+ crul_obj.permission_grant(identifier, groups)
171
+ end
172
+
173
+ # Takes away the given +permission+ from the given +groups+, without effecting
174
+ # already existing groups.
175
+ def revoke(permission, groups)
176
+ identifier = identifier(permission)
177
+
178
+ crul_obj.permission_revoke(identifier, groups)
179
+ end
180
+
181
+ # Takes away the given +permission+ from all groups currently set.
182
+ def clear(permission)
183
+ identifier = identifier(permission)
184
+
185
+ crul_obj.permission_clear(identifier)
186
+ end
187
+
188
+ protected
189
+
190
+ attr_reader :cache, :obj, :lookup
191
+
192
+ def crul_obj
193
+ obj.send(:crul_obj)
194
+ end
195
+
196
+ def default_user
197
+ Reactor::Session.instance.user_name || Reactor::Configuration.xml_access[:username]
198
+ end
199
+
200
+ def granted?(user, permission)
201
+ user ||= default_user
202
+ cache.lookup(user, "#{obj.path}:#{permission}") do
203
+ lookup.superuser?(user) || lookup.send("#{permission}?", user)
204
+ end
205
+ end
206
+
207
+ # A table with all available permissions and their identifier.
208
+ def self.permissions
209
+ @permissions ||= {
210
+ :read => 'permissionRead',
211
+ :root => 'permissionRoot',
212
+ :live => 'permissionLiveServerRead',
213
+ :write => 'permissionWrite',
214
+ :create_children => 'permissionCreateChildren',
215
+ }
216
+ end
217
+
218
+ def identifier(permission)
219
+ self.class.permissions[permission]
220
+ end
221
+
222
+ def has_workflow?
223
+ cache.lookup(:any, "#{obj.path}:workflow") do
224
+ RailsConnector::ObjectWithMetaData.find_by_object_id(obj.id).try(:workflow_name).present?
225
+ end
226
+ end
227
+ end
228
+
229
+ class PermissionLookup
230
+
231
+ def initialize(obj)
232
+ @obj = obj
233
+ @cache = Reactor::Cache::User.instance
234
+ end
235
+
236
+ Reactor::Permission::PermissionProxy.permissions.keys.each do |permission|
237
+ define_method "#{permission}?" do |user|
238
+ user_in_groups(user, obj.permissions.send(permission))
239
+ end
240
+ end
241
+
242
+ def superuser?(user)
243
+ cache.get(user).superuser?
244
+ end
245
+
246
+ def groups(user)
247
+ cache.get(user).groups
248
+ end
249
+
250
+ protected
251
+
252
+ attr_reader :cache, :obj
253
+
254
+ def user_in_groups(user, groups)
255
+ groups(user).detect { |user_group| groups.include?(user_group) } != nil
256
+ end
257
+
258
+ end
259
+
260
+ end
261
+
262
+ end