nakamura 0.1
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.
- data/lib/nakamura/authz.rb +93 -0
- data/lib/nakamura/contacts.rb +83 -0
- data/lib/nakamura/file.rb +81 -0
- data/lib/nakamura/full_group_creator.rb +290 -0
- data/lib/nakamura/message.rb +46 -0
- data/lib/nakamura/search.rb +39 -0
- data/lib/nakamura/test.rb +92 -0
- data/lib/nakamura/users.rb +312 -0
- data/lib/nakamura.rb +412 -0
- metadata +55 -0
data/lib/nakamura.rb
ADDED
@@ -0,0 +1,412 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
require 'cgi'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'json'
|
7
|
+
require 'yaml'
|
8
|
+
require 'nakamura/users'
|
9
|
+
require 'logger'
|
10
|
+
|
11
|
+
class String
|
12
|
+
def base64_decode
|
13
|
+
unpack('m').first
|
14
|
+
end
|
15
|
+
|
16
|
+
def base64_encode
|
17
|
+
[self].pack('m').chop
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class Hash
|
23
|
+
|
24
|
+
def dump
|
25
|
+
return keys.collect{|k| "#{k} => #{self[k]}"}.join(", ")
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
## Fix array handling
|
31
|
+
module Net::HTTPHeader
|
32
|
+
def set_form_data(params, sep = '&')
|
33
|
+
self.body = params.map {|k, v| encode_kvpair(k, v) }.flatten.join(sep)
|
34
|
+
self.content_type = 'application/x-www-form-urlencoded'
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def initialize_http_header(initheader)
|
39
|
+
@header = { "Referer" => ["/dev/integrationtests"] }
|
40
|
+
return unless initheader
|
41
|
+
initheader.each do |key, value|
|
42
|
+
warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
|
43
|
+
@header[key.downcase] = [value.strip]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def encode_kvpair(k, vs)
|
48
|
+
if vs.nil? or vs == '' then
|
49
|
+
"#{urlencode(k)}="
|
50
|
+
else
|
51
|
+
Array(vs).map {|v| "#{urlencode(k)}=#{urlencode(v.to_s)}" }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class WrappedCurlResponse
|
57
|
+
|
58
|
+
def initialize(response)
|
59
|
+
@response = response
|
60
|
+
end
|
61
|
+
|
62
|
+
def code
|
63
|
+
return @response.response_code
|
64
|
+
end
|
65
|
+
|
66
|
+
def message
|
67
|
+
return @response.response_code
|
68
|
+
end
|
69
|
+
|
70
|
+
def body
|
71
|
+
return @response.body_str
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
module SlingInterface
|
77
|
+
|
78
|
+
class Sling
|
79
|
+
|
80
|
+
attr_accessor :log, :trustedauth
|
81
|
+
|
82
|
+
def initialize(server="http://localhost:8080/", trustedauth=false)
|
83
|
+
@server = server
|
84
|
+
@serveruri = URI.parse(server)
|
85
|
+
@user = SlingUsers::User.admin_user()
|
86
|
+
@trustedauth = trustedauth
|
87
|
+
@cookies = Hash.new()
|
88
|
+
@loggedin = false
|
89
|
+
@log = Logger.new(STDOUT)
|
90
|
+
@log.level = Logger::WARN
|
91
|
+
end
|
92
|
+
|
93
|
+
def dump_response(response)
|
94
|
+
@log.info "Response: #{response.code} #{response.message}"
|
95
|
+
@log.debug "#{response.body}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def switch_user(user)
|
99
|
+
@log.info "Switched user to #{user}"
|
100
|
+
@cookies = Hash.new()
|
101
|
+
@user = user
|
102
|
+
if ( @trustedauth )
|
103
|
+
@loggedin = false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def text_to_multipart(key,value)
|
109
|
+
return "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"\r\n" +
|
110
|
+
"\r\n" +
|
111
|
+
"#{value}\r\n"
|
112
|
+
end
|
113
|
+
|
114
|
+
def file_to_multipart(key,filename,mime_type,content)
|
115
|
+
if ( filename != nil )
|
116
|
+
return "Content-Disposition: form-data; name=\"*\"; filename=\"#{filename}\"\r\n" +
|
117
|
+
"Content-Transfer-Encoding: binary\r\n" +
|
118
|
+
"Content-Type: #{mime_type}\r\n" +
|
119
|
+
"\r\n" +
|
120
|
+
"#{content}\r\n"
|
121
|
+
else
|
122
|
+
return "Content-Disposition: form-data; name=\"jcr:content\"\r\n" +
|
123
|
+
"Content-Transfer-Encoding: binary\r\n" +
|
124
|
+
"Content-Type: #{mime_type}\r\n" +
|
125
|
+
"\r\n" +
|
126
|
+
"#{content}\r\n"
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
def execute_file_post(path, fieldname, filename, data, content_type)
|
132
|
+
uri = URI.parse(path)
|
133
|
+
fileTypeHint = "Content-Disposition: form-data; name=\"*@TypeHint\"\r\n\r\n" +
|
134
|
+
"nt:file\r\n"
|
135
|
+
|
136
|
+
params = [fileTypeHint,file_to_multipart(fieldname, filename, content_type, data)]
|
137
|
+
boundary = '349832898984244898448024464570528145'
|
138
|
+
post_body = params.collect {|p| '--' + boundary + "\r\n" + p}.join('') + "--" + boundary + "--\r\n"
|
139
|
+
req = Net::HTTP::Post.new(uri.path)
|
140
|
+
req.set_content_type("multipart/form-data", {"boundary" => boundary})
|
141
|
+
req.body = post_body
|
142
|
+
res = sendRequest(uri, req)
|
143
|
+
dump_response(res)
|
144
|
+
return res
|
145
|
+
end
|
146
|
+
|
147
|
+
def createHttp(uri)
|
148
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
149
|
+
if (uri.scheme == 'https')
|
150
|
+
http.use_ssl = true
|
151
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
152
|
+
end
|
153
|
+
return http
|
154
|
+
end
|
155
|
+
|
156
|
+
def sendRequest(uri, req)
|
157
|
+
# Not all browsers take port number into account when setting cookies.
|
158
|
+
isSlingReq = uri.host && (uri.host == @serveruri.host)
|
159
|
+
if (isSlingReq)
|
160
|
+
if ( @trustedauth )
|
161
|
+
if ( ! @loggedin )
|
162
|
+
do_login()
|
163
|
+
end
|
164
|
+
else
|
165
|
+
@user.do_request_auth(req)
|
166
|
+
end
|
167
|
+
set_cookies(req)
|
168
|
+
else
|
169
|
+
@log.warn("Path #{uri} is not a Sling URL, not sending state")
|
170
|
+
end
|
171
|
+
res = createHttp(uri).start { |http| http.request(req ) }
|
172
|
+
save_cookies(res) if (isSlingReq)
|
173
|
+
return res
|
174
|
+
end
|
175
|
+
|
176
|
+
def delete_file(path)
|
177
|
+
uri = URI.parse(path)
|
178
|
+
req = Net::HTTP::Delete.new(uri.path)
|
179
|
+
res = sendRequest(uri, req)
|
180
|
+
dump_response(res)
|
181
|
+
return res
|
182
|
+
end
|
183
|
+
|
184
|
+
def execute_put_file(path, data)
|
185
|
+
@log.debug "URL: #{path}"
|
186
|
+
@log.debug("PUTFILE: #{path} (as '#{@user.name}')")
|
187
|
+
uri = URI.parse(path)
|
188
|
+
req = Net::HTTP::Put.new(uri.path)
|
189
|
+
req.body = data
|
190
|
+
res = sendRequest(uri, req)
|
191
|
+
dump_response(res)
|
192
|
+
return res
|
193
|
+
end
|
194
|
+
|
195
|
+
def execute_post(path, post_params={})
|
196
|
+
# We always post with utf-8
|
197
|
+
if ( post_params["_charset_"] == nil)
|
198
|
+
post_params["_charset_"] = "utf-8"
|
199
|
+
end
|
200
|
+
@log.debug("POST: #{path} (as '#{@user.name}')\n\tparams: #{post_params.dump}")
|
201
|
+
uri = URI.parse(path)
|
202
|
+
req = Net::HTTP::Post.new(uri.path)
|
203
|
+
req.set_form_data(post_params)
|
204
|
+
res = sendRequest(uri, req)
|
205
|
+
dump_response(res)
|
206
|
+
return res
|
207
|
+
end
|
208
|
+
|
209
|
+
def execute_get(path, query_params=nil)
|
210
|
+
if (query_params != nil)
|
211
|
+
param_string = query_params.collect { |k,v|
|
212
|
+
val = case v
|
213
|
+
when String then
|
214
|
+
v
|
215
|
+
when Numeric then
|
216
|
+
v.to_s
|
217
|
+
else
|
218
|
+
v.to_json
|
219
|
+
end
|
220
|
+
CGI.escape(k) + "=" + CGI.escape(val)
|
221
|
+
}.join("&")
|
222
|
+
path = "#{path}?#{param_string}"
|
223
|
+
end
|
224
|
+
@log.debug "URL: #{path}"
|
225
|
+
uri = URI.parse(path)
|
226
|
+
path = uri.path
|
227
|
+
path = path + "?" + uri.query if uri.query
|
228
|
+
@log.debug("GET: #{path} (as '#{@user.name}')")
|
229
|
+
req = Net::HTTP::Get.new(path)
|
230
|
+
res = sendRequest(uri, req)
|
231
|
+
res = followRedirects(res)
|
232
|
+
dump_response(res)
|
233
|
+
return res
|
234
|
+
end
|
235
|
+
|
236
|
+
def followRedirects(res)
|
237
|
+
lastlocation = ''
|
238
|
+
while (res.header['location'] && res.header['location'] != lastlocation)
|
239
|
+
lastlocation = res.header['location']
|
240
|
+
uri = URI.parse(res.header['location'])
|
241
|
+
@log.info("Redirecting to #{uri}")
|
242
|
+
req = Net::HTTP::Get.new(uri.request_uri())
|
243
|
+
res = sendRequest(uri, req)
|
244
|
+
end
|
245
|
+
return res
|
246
|
+
end
|
247
|
+
|
248
|
+
def set_cookies(req)
|
249
|
+
req["Cookie"] = @cookies.values * ", "
|
250
|
+
@log.debug("Cookie is #{req["Cookie"]}")
|
251
|
+
end
|
252
|
+
|
253
|
+
def save_cookies(res)
|
254
|
+
if ( res["Set-Cookie"] )
|
255
|
+
setcookie = res.get_fields('Set-Cookie')
|
256
|
+
@log.debug("Set-Cookie header #{setcookie} ")
|
257
|
+
setcookie.each do |cookieitem|
|
258
|
+
cookie = cookieitem.split(/;/)[0]
|
259
|
+
cookiename = cookie.split(/=/)[0]
|
260
|
+
value = cookie.split(/=/)[1]
|
261
|
+
cookievalue = "#{cookiename}=#{value}"
|
262
|
+
if ( @cookies[cookiename] != cookievalue )
|
263
|
+
@cookies[cookiename] = cookievalue
|
264
|
+
@log.info("New Cookie Value #{@cookies.values * "' "} ")
|
265
|
+
else
|
266
|
+
@log.debug("No Change to Cookie #{@cookies}")
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def do_login()
|
273
|
+
path = url_for("/system/sling/formlogin")
|
274
|
+
req = Net::HTTP::Post.new(path)
|
275
|
+
uri = URI.parse(path)
|
276
|
+
req.set_form_data({ "sakaiauth:un" => @user.name, "sakaiauth:pw" => @user.password, "sakaiauth:login" => 1 })
|
277
|
+
res = createHttp(uri).start{ |http| http.request(req) }
|
278
|
+
if ( res.code == "200" )
|
279
|
+
save_cookies(res)
|
280
|
+
@log.info("Login Ok, cookie was {#@cookies}")
|
281
|
+
@loggedin = true
|
282
|
+
else
|
283
|
+
@log.info("Failed to perform login, got "+res.code+" response code")
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def url_for(path)
|
288
|
+
if (path.slice(0,1) == '/')
|
289
|
+
path = path[1..-1]
|
290
|
+
end
|
291
|
+
return "#{@server}#{path}"
|
292
|
+
end
|
293
|
+
|
294
|
+
def update_properties(principal, props)
|
295
|
+
principal.update_properties(self, props)
|
296
|
+
end
|
297
|
+
|
298
|
+
def delete_node(path)
|
299
|
+
result = execute_post(url_for(path), ":operation" => "delete")
|
300
|
+
end
|
301
|
+
|
302
|
+
def create_file_node(path, fieldname, filename, data, content_type="text/plain")
|
303
|
+
result = execute_file_post(url_for(path), fieldname, filename, data, content_type)
|
304
|
+
end
|
305
|
+
|
306
|
+
def create_node(path, params)
|
307
|
+
result = execute_post(url_for(path), params)
|
308
|
+
end
|
309
|
+
|
310
|
+
def get_user()
|
311
|
+
return @user
|
312
|
+
end
|
313
|
+
|
314
|
+
def get_node_props_json(path)
|
315
|
+
@log.debug "Getting props for path: #{path}"
|
316
|
+
result = execute_get(url_for("#{path}.json"))
|
317
|
+
if ( result.code == "200" )
|
318
|
+
return result.body
|
319
|
+
end
|
320
|
+
@log.info("Failed to get properties for "+path+" cause "+result.code+"\n"+result.body)
|
321
|
+
return "{}"
|
322
|
+
end
|
323
|
+
|
324
|
+
def get_node_props(path)
|
325
|
+
return JSON.parse(get_node_props_json(path))
|
326
|
+
end
|
327
|
+
|
328
|
+
def update_node_props(path, props)
|
329
|
+
return execute_post(url_for(path), props)
|
330
|
+
end
|
331
|
+
|
332
|
+
def get_node_acl_json(path)
|
333
|
+
return execute_get(url_for("#{path}.acl.json")).body
|
334
|
+
end
|
335
|
+
|
336
|
+
def get_node_ruleacl_json(path)
|
337
|
+
return execute_get(url_for("#{path}.ruleacl.json")).body
|
338
|
+
end
|
339
|
+
|
340
|
+
def get_node_acl(path)
|
341
|
+
return JSON.parse(get_node_acl_json(path))
|
342
|
+
end
|
343
|
+
|
344
|
+
def get_node_ruleacl(path)
|
345
|
+
return JSON.parse(get_node_ruleacl_json(path))
|
346
|
+
end
|
347
|
+
|
348
|
+
def set_node_acl_entries(path, principal, privs)
|
349
|
+
@log.info "Setting node acl for: #{principal} to #{privs.dump}"
|
350
|
+
res = execute_post(url_for("#{path}.modifyAce.html"),
|
351
|
+
{ "principalId" => principal.name }.update(
|
352
|
+
privs.keys.inject(Hash.new) do |n,k|
|
353
|
+
n.update("privilege@#{k}" => privs[k])
|
354
|
+
end))
|
355
|
+
return res
|
356
|
+
end
|
357
|
+
|
358
|
+
def set_node_acl_rule_entries(path, principal, privs, props)
|
359
|
+
@log.info "Setting node acl for: #{principal} to #{privs.dump}"
|
360
|
+
props["principalId"] = principal.name
|
361
|
+
res = execute_post(url_for("#{path}.modifyRuleAce.html"),
|
362
|
+
props.update(
|
363
|
+
privs.keys.inject(Hash.new) do |n,k|
|
364
|
+
n.update("privilege@#{k}" => privs[k])
|
365
|
+
end))
|
366
|
+
return res
|
367
|
+
end
|
368
|
+
|
369
|
+
def delete_node_acl_entries(path, principal)
|
370
|
+
res = execute_post(url_for("#{path}.deleteAce.html"), {
|
371
|
+
":applyTo" => principal
|
372
|
+
})
|
373
|
+
end
|
374
|
+
|
375
|
+
def clear_acl(path)
|
376
|
+
acl = JSON.parse(get_node_acl_json(path))
|
377
|
+
acl.keys.each { |p| delete_node_acl_entries(path, p) }
|
378
|
+
end
|
379
|
+
|
380
|
+
def save_node(path)
|
381
|
+
res = execute_post(url_for("#{path}.save.json"), {})
|
382
|
+
if (res.code == "200")
|
383
|
+
return JSON.parse(res.body)["versionName"]
|
384
|
+
end
|
385
|
+
return nil
|
386
|
+
end
|
387
|
+
|
388
|
+
def versions(path)
|
389
|
+
return get_node_props("#{path}.versions")["versions"].keys
|
390
|
+
end
|
391
|
+
|
392
|
+
def version(path, version, extension)
|
393
|
+
return execute_get(url_for("#{path}.version.,#{version},.#{extension}"))
|
394
|
+
end
|
395
|
+
|
396
|
+
end
|
397
|
+
|
398
|
+
end
|
399
|
+
|
400
|
+
if __FILE__ == $0
|
401
|
+
@log.info "Sling test"
|
402
|
+
s = SlingInterface::Sling.new("http://localhost:8080/", false)
|
403
|
+
um = SlingUsers::UserManager.new(s)
|
404
|
+
um.create_group(10)
|
405
|
+
user = um.create_test_user(10)
|
406
|
+
s.create_node("fish", { "foo" => "bar", "baz" => "jim" })
|
407
|
+
@log.info s.get_node_props_json("fish")
|
408
|
+
@log.info s.get_node_acl_json("fish")
|
409
|
+
|
410
|
+
s.set_node_acl_entries("fish", user, { "jcr:write" => "granted" })
|
411
|
+
@log.info s.get_node_acl_json("fish")
|
412
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nakamura
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sakai Project
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-30 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: Ruby library for interacting with Sakai Nakamura. Provides convenience
|
15
|
+
methods for adding users and groups and other similar tasks.
|
16
|
+
email: oae-dev@collab.sakaiproject.org
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/nakamura/test.rb
|
22
|
+
- lib/nakamura/authz.rb
|
23
|
+
- lib/nakamura/full_group_creator.rb
|
24
|
+
- lib/nakamura/users.rb
|
25
|
+
- lib/nakamura/contacts.rb
|
26
|
+
- lib/nakamura/message.rb
|
27
|
+
- lib/nakamura/search.rb
|
28
|
+
- lib/nakamura/file.rb
|
29
|
+
- lib/nakamura.rb
|
30
|
+
homepage: http://sakaiproject.org
|
31
|
+
licenses: []
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
requirements:
|
49
|
+
- none
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 1.8.6
|
52
|
+
signing_key:
|
53
|
+
specification_version: 3
|
54
|
+
summary: Ruby library for interacting with Sakai Nakamura.
|
55
|
+
test_files: []
|