Ruby-ACL 1.0.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.
@@ -0,0 +1,224 @@
1
+
2
+ class ACL_Object
3
+
4
+ attr_reader :doc
5
+ attr_reader :col_path
6
+
7
+ def initialize(connector, col_path, report = false)
8
+ @connector = connector
9
+ @col_path = col_path
10
+ @report = report
11
+ end
12
+
13
+ private #--------------PRIVATE-----------------------------------------------
14
+
15
+ def generate_expr(name)
16
+ expr = <<END
17
+ <#{self.class.name} id="#{name}">
18
+ <membership>
19
+
20
+ </membership>
21
+ </#{self.class.name}>
22
+ END
23
+ return expr
24
+ rescue => e
25
+ raise e
26
+ end
27
+
28
+ def exists?(name, query = "#{@doc}//node()[@id=\"#{name}\"]")
29
+ handle = @connector.execute_query(query)
30
+ hits = @connector.get_hits(handle)
31
+ if(hits >= 1)
32
+ return true
33
+ else
34
+ return false
35
+ end
36
+ rescue => e
37
+ raise e
38
+ end
39
+
40
+ def exist_membership?(name, member_of) #Is <name> member of <member_of>
41
+ expr = "#{@doc}//#{self.class.name}s/descendant::*[@id=\"#{name}\"]/membership/mgroup[@idref=\"#{member_of}\"]"
42
+ exists?(name, expr)
43
+ rescue => e
44
+ raise e
45
+ end
46
+
47
+ public #------------------PUBLIC----------------------------------------------
48
+
49
+ def create_new(name, groups)
50
+ bool=true
51
+ if(name == nil || name == '')
52
+ bool = false
53
+ raise RubyACLException.new(self.class.name, __method__,
54
+ "Name is empty", 110), caller
55
+ end
56
+ if(exists?(name))
57
+ bool = false
58
+ raise RubyACLException.new(self.class.name, __method__,
59
+ "#{self.class.name} \"#{name}\" already exist(s)", 111), caller
60
+ end
61
+
62
+ if(bool)
63
+ expr = generate_expr(name)
64
+ expr_loc = "#{@doc}//#{self.class.name}s/#{self.class.name}[last()]"
65
+ @connector.update_insert(expr, "following", expr_loc)
66
+ if(exists?(name))
67
+ puts "New #{self.class.name} \"#{name}\" created." if @report
68
+ else
69
+ raise RubyACLException.new(self.class.name, __method__,
70
+ "#{self.class.name} \"#{name}\" was not able to create", 112), caller
71
+ end
72
+ end
73
+ if(groups.length > 0)
74
+ add_membership(name, groups, true)
75
+ end
76
+ return name
77
+ rescue XMLRPC::FaultException => e
78
+ raise e
79
+ rescue => e
80
+ raise e
81
+ end
82
+
83
+ #adds acl object into group(s); if you know prin exists set true for prin_exists
84
+ def add_membership(name, groups, ob_exists = false)
85
+ if(ob_exists || exists?(name))#, "#{@doc}//Individual[@id=\"#{name}\"]"))
86
+ for group in groups
87
+ if(!exists?(group))#, "#{@doc}//Group[@id=\"#{group}\"]"))
88
+ raise RubyACLException.new(self.class.name, __method__,
89
+ "Failed to add membership. Group \"#{group}\" does not exist.", 113), caller
90
+ end
91
+ query = "#{@doc}//node()[@id=\"#{name}\"]/membership/mgroup[@idref=\"#{group}\"]"
92
+ handle = @connector.execute_query(query)
93
+ hits = @connector.get_hits(handle)
94
+ if(hits == 0)
95
+ #protection against cycle in membership
96
+ if(find_parents(group).find_index(name).nil?)
97
+ expr = "<mgroup idref=\"#{group}\"/>"
98
+ expr_single = "#{@doc}//node()[@id=\"#{name}\"]/membership"
99
+ @connector.update_insert(expr, "into", expr_single)
100
+ else
101
+ raise RubyACLException.new(self.class.name, __method__,
102
+ "Failed to add membership. Membership is in cycle.", 118), caller
103
+ end
104
+ else
105
+ puts "Membership already exists." if @report
106
+ end
107
+ end
108
+ else
109
+ raise RubyACLException.new(self.class.name, __method__,
110
+ "Failed to add membership. #{self.class.name} \"#{name}\" does not exist.", 114), caller
111
+ end
112
+ return name
113
+ rescue => e
114
+ raise e
115
+ end #def add_membership
116
+
117
+ def del_membership(name, groups) #deletes prin_name from group(s)
118
+ if(exists?(name))
119
+ for group in groups
120
+ expr = "#{@doc}//#{self.class.name}s/descendant::*[@id=\"#{name}\"]/membership/mgroup[@idref=\"#{group}\"]"
121
+ @connector.update_delete(expr)
122
+ #end
123
+ if(!exists?(group))
124
+ raise RubyACLException.new(self.class.name, __method__,
125
+ "Failed to delete membership. Group \"#{group}\" does not exist.", 115), caller
126
+ end
127
+ end
128
+ else
129
+ raise RubyACLException.new(self.class.name, __method__,
130
+ "Failed to delete membership. #{self.class.name} \"#{name}\" does not exist.", 116), caller
131
+ end
132
+ return name
133
+ rescue => e
134
+ raise e
135
+ end #def del_membership
136
+
137
+ def delete(name)
138
+ if(exists?(name))
139
+ #deletes ACL_Object
140
+ expr = "#{@doc}//#{self.class.name}s/descendant::*[@id=\"#{name}\"]"
141
+ @connector.update_delete(expr)
142
+ #deletes references at ACL_Object
143
+ expr = "#{@doc}//node()[@idref=\"#{name}\"]"
144
+ @connector.update_delete(expr)
145
+ #deletes ACE mentioning ACL_Object
146
+ expr = "doc(\"#{@col_path}acl.xml\")//node()[@idref=\"#{name}\"]/parent::node()"
147
+ @connector.update_delete(expr)
148
+ else
149
+ raise RubyACLException.new(self.class.name, __method__,
150
+ "Failed to delete #{self.class.name}. #{self.class.name} \"#{name}\" does not exist.", 117), caller
151
+ end
152
+ return name
153
+ rescue => e
154
+ raise e
155
+ end #def delete
156
+
157
+ def rename(old_name, new_name)
158
+ if(!exists?(new_name))
159
+
160
+ expr = "#{@doc}//node()[@id=\"#{old_name}\"]/@id"
161
+ expr_single = "\"#{new_name}\""
162
+ @connector.update_value(expr, expr_single)
163
+
164
+ expr = "#{@doc}//node()[@idref=\"#{old_name}\"]/@idref"
165
+ expr_single = "\"#{new_name}\""
166
+ @connector.update_value(expr, expr_single)
167
+
168
+ expr = "doc(\"#{@col_path}acl.xml\")//node()[@idref=\"#{old_name}\"]/@idref"
169
+ expr_single = "\"#{new_name}\""
170
+ @connector.update_value(expr, expr_single)
171
+
172
+ query = "doc(\"#{@col_path}acl.xml\")//node()[@id=\"#{old_name}\"]"
173
+ handle = @connector.execute_query(query)
174
+ hits = @connector.get_hits(handle)
175
+ query = "#{@doc}//node()[@id=\"#{old_name}\"]"
176
+ handle = @connector.execute_query(query)
177
+ hits += @connector.get_hits(handle)
178
+ if(hits == 0)
179
+ puts "Rename succeeded." if @report
180
+ else
181
+ raise RubyACLException.new(self.class.name, __method__,
182
+ "Failed to rename", 120), caller
183
+ end
184
+ else
185
+ raise RubyACLException.new(self.class.name, __method__,
186
+ "Failed to rename #{self.class.name}. #{self.class.name} \"#{new_name}\" already exists.", 119), caller
187
+ end
188
+ return old_name
189
+ rescue XMLRPC::FaultException => e
190
+ raise e
191
+ rescue => e
192
+ raise e
193
+ end
194
+
195
+ def ge(t, f)
196
+ if(t >= f)
197
+ return true
198
+ else
199
+ return false
200
+ end
201
+ end
202
+
203
+ #finds membership parrent and returns in sorted array by level,
204
+ #Root is first leaf is last.
205
+ #e.g. dog's parrent is mammal.
206
+ def find_parents(id)
207
+ query = "#{@doc}//node()[@id=\"#{id}\"]/membership/*/string(@idref)"
208
+ ids = []
209
+ handle = @connector.execute_query(query)
210
+ hits = @connector.get_hits(handle)
211
+ hits.times {
212
+ |i|
213
+ id_ref = @connector.retrieve(handle, i)
214
+ if(id_ref=="")
215
+ next #for unknown reason eXist returns 1 empty hit even any exists therefore unite is skipped (e.g. //node()[@id="all"]/membership/*/string(@idref)
216
+ end
217
+ ids = find_parents(id_ref) | ids | [id_ref] #unite arrays
218
+ }
219
+ return ids
220
+ rescue => e
221
+ raise e
222
+ end
223
+
224
+ end
@@ -0,0 +1,908 @@
1
+ $:.unshift("./lib")
2
+ $:.unshift(".")
3
+ $:.unshift('D:\\eXistAPI\\lib\\')
4
+ require 'ACL_Object'
5
+ require 'Principal'
6
+ require 'individual'
7
+ require 'group'
8
+ require 'privilege'
9
+ require 'resource_object'
10
+ require 'ace'
11
+ require 'date'
12
+ require 'ace_rule'
13
+ require 'rubyacl_exception'
14
+
15
+ #RubyACL is library that handles access permisions. RubyACL offers to create and modify three ACL objects - three dimensions: Principal, Privilege, Resource object.
16
+ #Principal is someone or something that want to access.
17
+ #Privilege is level of access. (read, write etc.).
18
+ #Resource object is what is principal accessing.
19
+ #RubyACL uses API interface to communicate with database. This interface is described by class API_inteface.
20
+ #At the end of the class you can see set of examples. Also good source of information are testcases.
21
+ class RubyACL
22
+
23
+ # name of the ACL
24
+ attr_reader :name
25
+ # collection path in db, where ACL will be stored.
26
+ attr_reader :col_path
27
+
28
+ #Creates new instance of Ruby-ACL. Ruby-ACL works if principals, privileges and resource object.
29
+ #With Ruby-ACL you can manage permsion to person for resource objects.
30
+ #
31
+ # * *Args* :
32
+ # - +name+ -> name of the ACL
33
+ # - +connector+ -> instance of connection manager with db. e.g. ExistAPI.new("http://localhost:8080/exist/xmlrpc", "admin", "admin")
34
+ # - +colpath+ -> path of collection in db. e.g. "/db/acl/"
35
+ # - +src_files_path+ -> path of source files in local location.
36
+ # - +report+ -> boolean - if true Ruby-ACL
37
+ # * *Returns* :
38
+ # - instance of RubyACL
39
+ # * *Raises* :
40
+ # - +RubyACLException+ -> Name is empty
41
+ def initialize(name, connector, colpath = "/db/acl/", src_files_path = "./src_files/", report = false)
42
+ if(name == "")
43
+ raise RubyACLException.new(self.class.name, __method__, "Name is empty", 0), caller
44
+ end
45
+ @name = name
46
+ @connector = connector
47
+ if(colpath[-1] != "/")
48
+ colpath += "/"
49
+ end
50
+ @col_path = colpath
51
+ @src_files_path = src_files_path
52
+ @report = report
53
+ @prin = Principal.new(@connector, @col_path, @report)
54
+ @indi = Individual.new(@connector, @col_path, @report)
55
+ @group = Group.new(@connector, @col_path, @report)
56
+ @priv = Privilege.new(@connector, @col_path, @report)
57
+ @res_obj = ResourceObject.new(@connector, @col_path, @report)
58
+ @ace = Ace.new(@connector, @col_path, @report)
59
+ create_acl_in_db()
60
+ rename(name)
61
+ rescue => e
62
+ raise e
63
+ end
64
+
65
+ private # private methods follow
66
+
67
+ def create_acl_in_db()
68
+ if(!@connector.existscollection?(@col_path))
69
+ #puts "Collection doesn't exist. Creating collection."
70
+ @connector.createcollection(@col_path)
71
+ end
72
+ col = @connector.getcollection(@col_path)
73
+ sfs = missing_src_files(col)
74
+ if(sfs != []) #creates array of documents that dont exist in collection
75
+ missing_files = ""
76
+ sfs.each { |sf|
77
+ missing_files = missing_files + '"'+sf+'"'
78
+ if(sf != sfs.last())
79
+ missing_files = missing_files + ", "
80
+ end
81
+ }
82
+ #Creating new source file(s)
83
+ for sfile in sfs
84
+ xmlfile = File.read(@src_files_path + sfile)
85
+ @connector.storeresource(xmlfile, @col_path + sfile)
86
+ end
87
+ col = @connector.getcollection(@col_path)
88
+ end
89
+
90
+ if(col.name == @col_path && missing_src_files(col) == [])
91
+ puts "ACL collection with source files created." if @report
92
+ else
93
+ raise RubyACLException.new(self.class.name, __method__, "Failed to create ACL in database", 1), caller
94
+ end
95
+ rescue XMLRPC::FaultException => e
96
+ raise e
97
+ rescue => e
98
+ raise e
99
+ end
100
+
101
+ def missing_src_files(col) #returns array of files that are not present in acl collection
102
+ files = []
103
+ if(col['acl.xml'] == nil)
104
+ files.push('acl.xml')
105
+ end
106
+ if(col['Principals.xml'] == nil)
107
+ files.push('Principals.xml')
108
+ end
109
+ if(col['Privileges.xml'] == nil)
110
+ files.push('Privileges.xml')
111
+ end
112
+ if(col['ResourceObjects.xml'] == nil)
113
+ files.push('ResourceObjects.xml')
114
+ end
115
+ return files
116
+ end
117
+
118
+ def decide(res)
119
+ #puts "decide"
120
+ if(res == "allow")
121
+ return true
122
+ elsif(res == "deny")
123
+ return false
124
+ end
125
+ end
126
+
127
+ #Returns ACE that has higher priority.
128
+ #Lowest number has highest priority:
129
+ #1. Owner - owner is handled in method check
130
+ #2. Explicit Deny
131
+ #3. Explicit Allow
132
+ #4. Inherited Deny
133
+ #5. Inherited Allow
134
+ #6. If not found then Access denied
135
+ #
136
+ # * *Args* :
137
+ # - +final_ace+ -> first ace to compare
138
+ # - +temp_ace+ -> second ace to compare
139
+ # * *Returns* :
140
+ # - returns ace that has higher priority
141
+ # * *Raises* :
142
+ # - +RubyACLException+ -> Failed to create ACL in database
143
+ #
144
+ def compare(final_ace, temp_ace) #returns ace that has higher priority
145
+ if(final_ace == nil)
146
+ return temp_ace
147
+ end
148
+
149
+ if(@prin.eq(temp_ace, final_ace) && final_ace.acc_type == "deny")
150
+ if(@priv.ge(temp_ace, final_ace, @privs) && @res_obj.ge(temp_ace, final_ace, @res_obs))
151
+ final_ace = temp_ace
152
+ end
153
+ end
154
+
155
+ if(@prin.ne(temp_ace, final_ace))
156
+ if(@priv.ge(temp_ace, final_ace, @privs) && @res_obj.ge(temp_ace, final_ace, @res_obs))
157
+ final_ace = temp_ace
158
+ end
159
+ end
160
+ return final_ace
161
+ rescue => e
162
+ raise e
163
+ end
164
+
165
+ #Prepares query in xQuery language. This query selects all ace that has
166
+ #principale && one of the privileges && one of the resource objects.
167
+ #
168
+ #
169
+ # * *Args* :
170
+ # - +prin+ -> principal
171
+ # - +privs+ -> privilege and all parental privileges
172
+ # - +res_obs+ -> resource object and all parental resource objects
173
+ # * *Returns* :
174
+ # - string, created query
175
+ # * *Raises* :
176
+ # - +nothing+
177
+ #
178
+ def prepare_query(prin, privs, res_obs)
179
+ query = <<END
180
+ for $ace in #{@ace.doc}//Ace
181
+ where (
182
+ END
183
+
184
+ query += "($ace/Principal/@idref=\"#{prin}\")"
185
+
186
+ query += " and ("
187
+ for priv in privs
188
+ query += "$ace/Privilege/@idref=\"#{priv}\""
189
+ if(priv != privs.last)
190
+ query+=" or "
191
+ else
192
+ query+=") "
193
+ end
194
+ end
195
+
196
+ query += " and ("
197
+ for res_ob in res_obs
198
+ query += "$ace/ResourceObject/@idref=\"#{res_ob}\""
199
+ if(res_ob != res_obs.last)
200
+ query+=" or "
201
+ else
202
+ query+=") "
203
+ end
204
+ end
205
+ query+=")"
206
+ query += "return $ace/string(@id)"
207
+ return query
208
+ rescue => e
209
+ raise e
210
+ end
211
+
212
+ #In array res_obs are all optencial resource objects.
213
+ #It transform array into string (id="something" or id=... and so on)
214
+ def is_owner?(prin_name, res_obs)
215
+ res_obs = prepare_res_obs(res_obs)
216
+ #Select only those resOb that have wanted owner.
217
+ query = "#{@res_obj.doc}//ResourceObject[#{res_obs} and owner/string(@idref)=\"#{prin_name}\"]"
218
+ #puts query
219
+ handle = @connector.execute_query(query)
220
+ hits = @connector.get_hits(handle)
221
+ if(hits>0) #if owner exist, lets grand access
222
+ return true
223
+ else
224
+ return false
225
+ end
226
+ rescue => e
227
+ raise e
228
+ end
229
+
230
+ #Creates part of the query. Inserts "or" between each of resource object and whole string is in ()
231
+ #
232
+ # * *Args* :
233
+ # - +res_obs+ -> array of resource objects
234
+ # * *Returns* :
235
+ # -string
236
+ # * *Raises* :
237
+ # - +nothing+
238
+ #
239
+ def prepare_res_obs(res_obs)
240
+ str = "("
241
+ for res_ob in res_obs
242
+ str = str + "@id=\""
243
+ str += res_ob
244
+ str += "\" or "
245
+ end
246
+ str = str[0..-4] #delete last " OR "
247
+ str += ")"
248
+ return str
249
+ end
250
+
251
+ #Creates ace in acl.xml in db
252
+ #
253
+ # * *Args* :
254
+ # - +prin_name+ -> name of the principal
255
+ # - +acc_type+ -> access type. allow to allow. deny to revoke
256
+ # - +priv_name+ -> name of the privilege
257
+ # - +res_ob_type+ -> type of resource object
258
+ # - +res_ob_adrs+ -> address of resource object.
259
+ # * *Returns* :
260
+ # -id of ace
261
+ # * *Raises* :
262
+ # - +nothing+
263
+ #
264
+ def insert_ace(prin_name, acc_type, priv_name, res_ob_type, res_ob_adrs)
265
+ res_ob_id = @res_obj.find_res_ob(res_ob_type, res_ob_adrs)
266
+ if(res_ob_id == nil)
267
+ res_ob_id = @res_obj.create_new(res_ob_type, res_ob_adrs, prin_name)
268
+ end
269
+ id = @ace.create_new(prin_name, acc_type, priv_name, res_ob_id)
270
+ return id
271
+ end
272
+
273
+ protected
274
+
275
+ public # follow public methods
276
+
277
+ #Renames acl (also in db)
278
+ #
279
+ # * *Args* :
280
+ # - +new_name+ -> new name of acl
281
+ # * *Returns* :
282
+ # -int, handle of the query
283
+ # * *Raises* :
284
+ # - +RubyACLException+ -> Failed to set new name
285
+ #
286
+ def rename(new_name)
287
+ query = "update value doc(\"#{@col_path}acl.xml\")/acl/@aclname with \"#{new_name}\""
288
+ @connector.execute_query(query)
289
+ query = "doc(\"#{@col_path}acl.xml\")/acl/string(@aclname)"
290
+ handle = @connector.execute_query(query)
291
+ if(new_name != @connector.retrieve(handle, 0))
292
+ raise RubyACLException.new(self.class.name, __method__, "Failed to set new name", 2), caller
293
+ end
294
+ rescue XMLRPC::FaultException => e
295
+ raise e
296
+ rescue => e
297
+ raise e
298
+ end
299
+
300
+ #It saves/backs up acl
301
+ #
302
+ # * *Args* :
303
+ # - +path+ -> to local location
304
+ # * *Returns* :
305
+ # - array of documents in ACL working collection
306
+ # * *Raises* :
307
+ # - +nothing+
308
+ #
309
+ def save(path, date = false)
310
+ col = @connector.getcollection(@col_path)
311
+ docs = col.docs()
312
+ if(date)
313
+ path = path + Date.today.to_s + "/"
314
+ end
315
+ if(!File.exists?(path))
316
+ Dir.mkdir(path)
317
+ end
318
+ docs.each{|doc|
319
+ document = col[doc]
320
+ #doc = doc + "_BU"
321
+ filename = path + doc
322
+ f = File.new(filename, 'w')
323
+ f.puts(document.content)
324
+ f.close
325
+ }
326
+ rescue => e
327
+ raise e
328
+ end
329
+
330
+ #It loads backuped acl data.
331
+ #
332
+ # * *Args* :
333
+ # - +connector+ -> instance of connection manager with db. e.g. ExistAPI.new("http://localhost:8080/exist/xmlrpc", "admin", "admin")
334
+ # - +colpath+ -> path of collection in db. e.g. "/db/acl/"
335
+ # - +src_files_path+ -> path of source files in local location.
336
+ # - +report+ -> boolean - if true Ruby-ACL
337
+ # * *Returns* :
338
+ # -new instance of Ruby-ACL
339
+ # * *Raises* :
340
+ # - +nothin+
341
+ #
342
+ def RubyACL.load(connector, colpath, src_files_path, report = false)
343
+ xmlfile = File.read(src_files_path+"acl.xml")
344
+ startindex = xmlfile.index('"', xmlfile.index("aclname="))
345
+ endindex = xmlfile.index('"', startindex+1)
346
+ name = xmlfile[startindex+1..endindex-1]
347
+ newacl = RubyACL.new(name, connector, colpath, src_files_path, report)
348
+ return newacl
349
+ rescue => e
350
+ raise e
351
+ end
352
+
353
+ #Decides whether principal has privilege or not to resource object identified
354
+ #by access type and address. If the accessType of ace is allow - returns true.
355
+ #If accessType of ace is deny return false.
356
+ #
357
+ #Priority of decision is as follows lower.
358
+ #Lowest number has highest priority:
359
+ #1. Owner
360
+ #2. Explicit Deny
361
+ #3. Explicit Allow
362
+ #4. Inherited Deny
363
+ #5. Inherited Allow
364
+ #6. If not found then Deny
365
+ #
366
+ # * *Args* :
367
+ # - +prin_name+ -> name of the principal
368
+ # - +priv_name+ -> name of the privilege
369
+ # - +res_ob_type+ -> type of the resource object
370
+ # - +res_ob_adr+ -> address of the resource object
371
+ # * *Returns* :
372
+ # -boolean - true - has privilege to resource object, false - has not
373
+ # * *Raises* :
374
+ # - +nothing+
375
+ #
376
+ def check(prin_name, priv_name, res_ob_type, res_ob_adr)
377
+ res_ob_id = @res_obj.find_res_ob(res_ob_type, res_ob_adr)
378
+ #creates the set of resOb (wanted resOb and all resOb from root to address, unsorted)
379
+ @res_obs = @res_obj.find_res_ob_parents(res_ob_type, res_ob_adr)
380
+ #adds resOb, which ends with /* + wanted resOb
381
+ @res_obs = @res_obj.res_obs_grand2children(@res_obs) + [res_ob_id]
382
+
383
+ if(is_owner?(prin_name, @res_obs))
384
+ #puts "owner"
385
+ return true #access allowed - owner can do everything
386
+ end
387
+
388
+ #creates the set of principals {wanted principal and all groups wanted principal is member of}
389
+ prins = @prin.find_parents(prin_name) + [prin_name]
390
+ @privs = @priv.find_parents(priv_name) + [priv_name] #same for privilege
391
+
392
+ final_ace = nil
393
+ for prin in prins
394
+ #ask for principal with privilege or higher privileges and and resob or higher resob.
395
+ query = prepare_query(prin, @privs, @res_obs)
396
+ #puts query
397
+ handle = @connector.execute_query(query)
398
+ hits = @connector.get_hits(handle)
399
+ #puts "hits #{hits}"
400
+ if(hits > 0)
401
+ temp_id = @connector.retrieve(handle, 0) #retrieve id of first Ace
402
+ temp_ace = AceRule.new(temp_id, @ace, @connector)
403
+ if(hits == 1)
404
+ final_ace = compare(final_ace, temp_ace)
405
+ else #there are more rules
406
+ hits.times { |i|
407
+ temp_id = @connector.retrieve(handle, i) #retrieve id of next Ace
408
+ temp_ace.reload!(temp_id)
409
+ final_ace = compare(final_ace, temp_ace)
410
+ }
411
+ end
412
+ end
413
+ end
414
+
415
+ if(final_ace == nil) #Rule doesnt exist = access denied
416
+ #puts "nil"
417
+ puts "Required rule
418
+ (#{prin_name}, #{priv_name}, #{res_ob_type}, #{res_ob_adr}) does not exist.
419
+ Access denied." if @report
420
+ return false
421
+ else
422
+ return decide(final_ace.acc_type)
423
+ end
424
+ rescue => e
425
+ raise e
426
+ #puts e.backtrace.join("\n")
427
+ #raise RubyACLException.new(self.class.name, __method__, "Failed to check ACE", 3), caller
428
+ end
429
+
430
+ #It shows permissions for principal all its parents.
431
+ #
432
+ # * *Args* :
433
+ # - +prin_name+ -> name of the principal
434
+ # * *Returns* :
435
+ # - string -> all aces that has principal and all its parents.
436
+ # * *Raises* :
437
+ # - +nothing+
438
+ #
439
+ def show_permissions_for(prin_name)
440
+ #creates the set of principals {wanted principal and all groups wanted principal is member of}
441
+ prins = [prin_name] + @prin.find_parents(prin_name)
442
+
443
+ #creates query, ask for principal permisions and all his parents permisions
444
+ query = "for $ace in #{@ace.doc}//Ace where ("
445
+ for prin in prins
446
+ query += "($ace/Principal/@idref=\"#{prin}\") or"
447
+ end
448
+ query = query[0..-4] #delete last or
449
+ query += ") return $ace"
450
+
451
+ ace = ""
452
+ handle = @connector.execute_query(query)
453
+ hits = @connector.get_hits(handle)
454
+ hits.times { |i|
455
+ ace = ace + @connector.retrieve(handle, i) + "\n" #retrieve and add next ACE
456
+ }
457
+ ace = ace[0..-2] #delete last \n
458
+ return ace
459
+ rescue => e
460
+ raise e
461
+ end
462
+
463
+ #Creates ace in acl. If resource object doesn't exist it creates resource object first.
464
+ #
465
+ #Address:
466
+ #If is used:
467
+ # resource address /something/somethingelse and grand2children = true
468
+ # then will be created access for somthingelse and its children
469
+ #If is used:
470
+ # only /something/somethingelse/*
471
+ # then will be created access only to somethingelse's children
472
+ #
473
+ #
474
+ # * *Args* :
475
+ # - +prin_name+ -> name of the principal
476
+ # - +acc_type+ -> access type. True = grant, false = revoke
477
+ # - +priv_name+ -> name of the privilege
478
+ # - +res_ob_type+ -> type of the resource object
479
+ # - +res_ob_adr+ -> address of the resource object
480
+ # - +grand2children+ -> boolean.
481
+ # True = grant to all children.
482
+ # False = grant only to specified resource object
483
+ # * *Returns* :
484
+ # - string, id of the created ace
485
+ # * *Raises* :
486
+ # - +nothing+
487
+ #
488
+ def create_ace(prin_name, acc_type, priv_name, res_ob_type, res_ob_adr, grand2children = false)
489
+ if(res_ob_adr[-2..-1] == "/*")
490
+ id = insert_ace(prin_name, acc_type, priv_name, res_ob_type, res_ob_adr)
491
+ else
492
+ id = insert_ace(prin_name, acc_type, priv_name, res_ob_type, res_ob_adr)
493
+ if(grand2children)
494
+ res_ob_adr = res_ob_adr + "/*"
495
+ insert_ace(prin_name, acc_type, priv_name, res_ob_type, res_ob_adr)
496
+ end
497
+ end
498
+ return id
499
+ rescue => e
500
+ raise e
501
+ end
502
+
503
+ #It creates principal with name and membership in groups.
504
+ #
505
+ # * *Args* :
506
+ # - +name+ -> name of the princpal
507
+ # - +groups+ -> array of groups where principal will be member.
508
+ # * *Returns* :
509
+ # - string, name of the principal
510
+ # * *Raises* :
511
+ # - +nothing+
512
+ #
513
+ def create_principal(name, groups = ["ALL"])
514
+ @indi.create_new(name, groups)
515
+ rescue => e
516
+ raise e
517
+ end
518
+
519
+ #Create group with name and membership in groups and members as groups or individual
520
+ #
521
+ #Note:
522
+ # members can be groups or individuals;
523
+ # At first method check if the name already exist. Or if groups and members exist at all
524
+ #
525
+ # * *Args* :
526
+ # - +name+ -> name of the new group
527
+ # - +member_of+ -> array of groups where new group will be member.
528
+ # - +members+ -> array of principals, who will be members of new group
529
+ # members can be groups or individuals;
530
+ # check if it the name already exist. Or if groups and members exist at all
531
+ # * *Returns* :
532
+ # - string, name of the group
533
+ # * *Raises* :
534
+ # - +nothing+
535
+ #
536
+ def create_group(name, member_of = ["ALL"], members = [])
537
+ @group.create_new(name, member_of, members)
538
+ rescue => e
539
+ raise e
540
+ end
541
+
542
+ #It creates privilege with name and membership in specified privileges.
543
+ #
544
+ # * *Args* :
545
+ # - +name+ -> name of the privilege
546
+ # - +member_of+ -> array of privileges where new privilege will be member.
547
+ # * *Returns* :
548
+ # - string, name of the privilege
549
+ # * *Raises* :
550
+ # - +nothing+
551
+ #
552
+ def create_privilege(name, member_of = ["ALL_PRIVILEGES"])
553
+ @priv.create_new(name, member_of)
554
+ rescue => e
555
+ raise e
556
+ end
557
+
558
+ #It creates resource object with type, address and owner.
559
+ #
560
+ # * *Args* :
561
+ # - +type+ -> type of resource object. e.g. doc, room, file, collection
562
+ # - +address+ -> address of the resource object. If it ends with /*, it means all children
563
+ # - +owner+ -> owner of the resource object
564
+ # * *Returns* :
565
+ # - string, id of the created resource object
566
+ # * *Raises* :
567
+ # - +nothing+
568
+ #
569
+ def create_resource_object(type, address, owner)
570
+ #puts "type #{type} add #{address}"
571
+ id = @res_obj.create_new(type, address, owner)
572
+ return id
573
+ rescue => e
574
+ raise e
575
+ end
576
+
577
+ #It changes resource object's type.
578
+ #Address and type must be specified to identify resource object.
579
+ #
580
+ # * *Args* :
581
+ # - +type+ -> type of resource object
582
+ # - +address+ -> address of resource object
583
+ # - +new_type+ -> new_type of resource object
584
+ # * *Returns* :
585
+ # - string, original type
586
+ # * *Raises* :
587
+ # - +nothing+
588
+ #
589
+ def change_res_ob_type(type, address, new_type)
590
+ @res_obj.change_type(type, address, new_type)
591
+ rescue => e
592
+ raise e
593
+ end
594
+
595
+ #It changes resource object's address.
596
+ #Address and type must be specified to identify resource object.
597
+ #
598
+ # * *Args* :
599
+ # - +type+ -> type of resource object
600
+ # - +address+ -> address of resource object
601
+ # - +new_address+ -> new address of resource object
602
+ # * *Returns* :
603
+ # - string, original address
604
+ # * *Raises* :
605
+ # - +nothing+
606
+ #
607
+ def change_of_res_ob_address(type, address, new_address)
608
+ @res_obj.change_address(type, address, new_address)
609
+ rescue => e
610
+ raise e
611
+ end
612
+
613
+ #It changes resource object's owner.
614
+ #Address and type must be specified to identify resource object.
615
+ #
616
+ # * *Args* :
617
+ # - +type+ -> type of resource object
618
+ # - +address+ -> address of resource object
619
+ # - +new_owner+ -> new owner of resource object
620
+ # * *Returns* :
621
+ # - string, original owner
622
+ # * *Raises* :
623
+ # - +nothing+
624
+ #
625
+ def change_of_res_ob_owner(type, address, new_owner)
626
+ @res_obj.change_owner(type, address, new_owner)
627
+ rescue => e
628
+ raise e
629
+ end
630
+
631
+ #It renames any principal. It means either individual or group.
632
+ #
633
+ # * *Args* :
634
+ # - +old_name+ -> old name of principal
635
+ # - +new_name+ -> new name of principal
636
+ # * *Returns* :
637
+ # - string, original name of the principal
638
+ # * *Raises* :
639
+ # - +nothing+
640
+ #
641
+ def rename_principal(old_name, new_name)
642
+ @prin.rename(old_name, new_name)
643
+ end
644
+
645
+ #It renames privilege.
646
+ #
647
+ # * *Args* :
648
+ # - +old_name+ -> old name of privilege
649
+ # - +new_name+ -> new name of privilege
650
+ # * *Returns* :
651
+ # - string, original name of the privilege
652
+ # * *Raises* :
653
+ # - +nothing+
654
+ #
655
+ def rename_privilege(old_name, new_name)
656
+ @priv.rename(old_name, new_name)
657
+ end
658
+
659
+ #It adds principal into group(s) as member.
660
+ #
661
+ # * *Args* :
662
+ # - +name+ -> name of the principal
663
+ # - +groups+ -> array of groups, where principal will be member.
664
+ # - +existance+ -> boolean. If you know that principal exists set true for existance.
665
+ # * *Returns* :
666
+ # - string, name of the principal
667
+ # * *Raises* :
668
+ # - +nothing+
669
+ #
670
+ def add_membership_principal(name, groups, existance = false)
671
+ @prin.add_membership(name, groups, existance)
672
+ rescue => e
673
+ raise e
674
+ end
675
+
676
+ #It adds privilege into privilege(s). So you can gather privileges into tree.
677
+ #
678
+ # * *Args* :
679
+ # - +name+ -> name of the privilege
680
+ # - +groups+ -> array of privileges, where privilege will be member.
681
+ # - +existance+ -> boolean. If you know that privielge exists set true for existance.
682
+ # * *Returns* :
683
+ # - string, name of the privilege
684
+ # * *Raises* :
685
+ # - +nothing+
686
+ #
687
+ def add_membership_privilege (name, groups, existance = false)
688
+ @priv.add_membership(name, groups, existance)
689
+ rescue => e
690
+ raise e
691
+ end
692
+
693
+ #It removes principal from group(s) where principal is member.
694
+ #
695
+ # * *Args* :
696
+ # - +name+ -> name of the principal
697
+ # - +groups+ -> array of groups, where principal is member.
698
+ # * *Returns* :
699
+ # - string, name of the principal
700
+ # * *Raises* :
701
+ # - +nothing+
702
+ #
703
+ def del_membership_principal(prin_name, groups) #deletes prin_name from group(s)
704
+ @prin.del_membership(prin_name, groups)
705
+ rescue => e
706
+ raise e
707
+ end
708
+
709
+ #It removes the privilege from parental privilege(s) where the privilege is member.
710
+ #
711
+ # * *Args* :
712
+ # - +name+ -> name of the privilege
713
+ # - +groups+ -> array of groups, where privilege is member.
714
+ # * *Returns* :
715
+ # - string, name of the privilege
716
+ # * *Raises* :
717
+ # - +nothing+
718
+ #
719
+ def del_membership_privilege(priv_name, groups) #deletes prin_name from group(s)
720
+ @priv.del_membership(priv_name, groups)
721
+ rescue => e
722
+ raise e
723
+ end
724
+
725
+ #It deletes principal from ACL. It also deletes all linked ACEs.
726
+ #
727
+ # * *Args* :
728
+ # - +name+ -> name of the principal
729
+ # * *Returns* :
730
+ # -string, name of the pricipal
731
+ # * *Raises* :
732
+ # - +nothing+
733
+ #
734
+ def delete_principal(name)
735
+ @prin.delete(name)
736
+ rescue => e
737
+ raise e
738
+ end
739
+
740
+ #It deletes privilege from ACL. It also deletes all linked ACEs.
741
+ #
742
+ # * *Args* :
743
+ # - +name+ -> name of the privilege
744
+ # * *Returns* :
745
+ # -string, name of the privilege
746
+ # * *Raises* :
747
+ # - +nothing+
748
+ #
749
+ def delete_privilege(name)
750
+ @priv.delete(name)
751
+ rescue => e
752
+ raise e
753
+ end
754
+
755
+ #It deletes resource object from ACL. It also deletes all linked ACEs.
756
+ #
757
+ # * *Args* :
758
+ # - +type+ -> type of the resource object
759
+ # - +address+ -> address of the resource object
760
+ # * *Returns* :
761
+ # -string, name of the resource object
762
+ # * *Raises* :
763
+ # - +nothing+
764
+ #
765
+ def delete_res_object(type, address)
766
+ res_ob_id = @res_obj.find_res_ob(type, address)
767
+ @res_obj.delete(res_ob_id)
768
+ return res_ob_id
769
+ rescue => e
770
+ raise e
771
+ end
772
+
773
+ #It deletes resource object from ACL by id. It also deletes all linked ACEs.
774
+ #
775
+ # * *Args* :
776
+ # - +id+ -> id of the resource object
777
+ # * *Returns* :
778
+ # -string, name of the resource object
779
+ # * *Raises* :
780
+ # - +nothing+
781
+ #
782
+ def delete_res_object_by_id(id)
783
+ @res_obj.delete(id)
784
+ rescue => e
785
+ raise e
786
+ end
787
+
788
+ #It deletes ACE from ACL.
789
+ #
790
+ # * *Args* :
791
+ # - +ace_id+ -> id of the ACE
792
+ # * *Returns* :
793
+ # -string, name of the
794
+ # * *Raises* :
795
+ # - +nothing+
796
+ #
797
+ def delete_ace(ace_id)
798
+ @ace.delete(ace_id)
799
+ rescue => e
800
+ raise e
801
+ end
802
+
803
+
804
+ end
805
+
806
+ #TODO Vzorovou tridu API s vyhazovanim vyjimek must be implemented
807
+ #TODO ptam se jestli nekdo kdo neexistuje ma pristup. Pozor na vyjimku, mel bych vratit rovnou false.
808
+ #TODO Lze pridat privilege do principal a naopak? test na to by byl peknej :)
809
+
810
+ ##Usage example. Also very good source of information are test cases.
811
+ #puts "start"
812
+ #$:.unshift("../../eXistAPI/lib")
813
+ #require 'eXistAPI' #must require 'eXistAPI' to comunicated with eXist-db
814
+ #
815
+ ##create instance of ExistAPI
816
+ #@db = ExistAPI.new("http://localhost:8080/exist/xmlrpc", "admin", "admin")
817
+ #@col_path = "/db/test_acl/" #sets the collection where you want to have ACL in db
818
+ #@src_files_path = "./src_files/" #path to source files
819
+ #if(@db.existscollection?(@col_path))
820
+ # @db.remove_collection(@col_path) #Deleting old ACL from db
821
+ #end
822
+ #report = false
823
+ #@my_acl = RubyACL.new("my_acl", @db, @col_path, @src_files_path, report)
824
+ #
825
+ ##it's good to create some principals at the begging
826
+ #@my_acl.create_principal("Sheldon")
827
+ #@my_acl.create_principal("Leonard")
828
+ #@my_acl.create_principal("Rajesh")
829
+ #@my_acl.create_principal("Howarda")
830
+ #@my_acl.create_principal("Penny")
831
+ #@my_acl.create_principal("Kripkie")
832
+ #
833
+ ##Besides given privileges you can create your owns
834
+ #@my_acl.create_privilege("WATCH")
835
+ #@my_acl.create_privilege("SIT")
836
+ #
837
+ ##You can create resource object and get id of it.
838
+ #resource_id = @my_acl.create_resource_object("mov", "/Movies", "Sheldon")
839
+ #@my_acl.create_resource_object("couch", "/livingroom", "Sheldon")
840
+ #@my_acl.create_resource_object("seat", "/livingroom/couch/Sheldon's_spot", "Sheldon")
841
+ #
842
+ ##Now we have everything we need to create a rule.
843
+ ##Lets see what we must hand over
844
+ ##1) One individual or group that (principal)
845
+ ##2) will or won't have access (access type = {allow, deny})
846
+ ##3) to do something with (privilege)
847
+ ##4) which type of (resource type)
848
+ ##5) resource. (resource object)
849
+ ##6) And if we needs to grand all this to children of resource. (grant to children)
850
+ #@my_acl.create_ace("Sheldon", "allow", "DELETE", "mov", "/Movies", true)
851
+ #@my_acl.create_ace("Sheldon", "allow", "SIT", "seat", "/livingroom/couch/Sheldon's_spot")
852
+ #
853
+ #
854
+ ##You can easily check e.g. if Penny may delete all movies.
855
+ #@my_acl.check("Penny", "DELETE", "mov", "/Movies")
856
+ #
857
+ ##Next method call returns deny
858
+ #@my_acl.check("Penny", "SIT", "seat", "/livingroom/couch/Sheldon's_spot")
859
+ #
860
+ ##You can create group and immidiatly insert members or do it later.
861
+ #@my_acl.create_group("4th_floor", ["ALL"], ["Sheldon","Leonard","Penny"])
862
+ ##Here are other possible ways of creating group
863
+ ##@my_acl.create_group("4th_floor")
864
+ ##@my_acl.create_group("4th_floor", ["ALL"])
865
+ ##
866
+ ##Create access control entry with group as principal.
867
+ #ace_id = @my_acl.create_ace("4th_floor", "allow", "WATCH", "mov", "/Movies/*")
868
+ #
869
+ ##You can show all privileges connected with principal
870
+ #perm = @my_acl.show_permissions_for("Penny")
871
+ #puts perm
872
+ #
873
+ ##EXCEPTION EXAMPLE
874
+ #@my_acl.create_group("Scientists") #you must create group before you use it
875
+ #@my_acl.add_membership_principal("Sheldon", ["Scientists"])
876
+ #
877
+ ##You also must create privileges before you use them
878
+ #@my_acl.create_privilege("TALK")
879
+ #@my_acl.create_privilege("COMUNICATE")
880
+ ##You can gather privileges in treelike structure
881
+ #@my_acl.add_membership_privilege("TALK", ["COMUNICATE"])
882
+ #
883
+ ##You can delete membership of principal and privilege,
884
+ ##ace, principal, privilege, resource object, if exists.
885
+ ##Otherwise you will get exception
886
+ #@my_acl.del_membership_principal("Sheldon", ["Scientists"])
887
+ #@my_acl.del_membership_privilege("TALK", ["COMUNICATE"])
888
+ #@my_acl.delete_ace(ace_id)
889
+ #@my_acl.delete_principal("Kripkie")
890
+ #@my_acl.delete_privilege("SIT")
891
+ #@my_acl.delete_res_object("couch", "/livingroom")
892
+ #@my_acl.delete_res_object_by_id(resource_id)
893
+ #
894
+ #@my_acl.create_resource_object("mov", "/Movies", "Sheldon") #create again for demonstration purposes
895
+ ##You can rename principal, privilege and change every part of resource object.
896
+ #@my_acl.rename_principal("Kripkie", "Kwipkie")
897
+ #@my_acl.rename_privilege("TALK", "CHAT")
898
+ #@my_acl.change_of_res_ob_address("mov", "/Movies", "/Films")
899
+ #@my_acl.change_of_res_ob_owner("mov", "/Films", "Leonard")
900
+ #@my_acl.change_res_ob_type("mov", "/Films", "motion picture")
901
+ #
902
+ #You can save or load ACL
903
+ #@my_acl.save('C:\\storage')
904
+ #RubyACL.load(@db, "C:\\backup")
905
+ #
906
+ ##You can rename ACL
907
+ #@my_acl.rename("my_beloved_acl")
908
+ #puts "finished"