Ruby-ACL 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"