riddl 0.99.220 → 0.99.221

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.
@@ -1,45 +0,0 @@
1
- require 'mime/types'
2
- require 'charlock_holmes'
3
- require 'digest/md5'
4
-
5
- module Riddl
6
- module Utils
7
- class FileServe < Riddl::Implementation
8
- def response
9
- path = File.file?(@a[0]) ? @a[0] : "#{@a[0]}/#{@r[@match.length-1..-1].join('/')}".gsub(/\/+/,'/')
10
-
11
- if File.directory?(path)
12
- @status = 404
13
- return []
14
- end
15
- if File.exists?(path)
16
- mtime = File.mtime(path)
17
- @headers << Riddl::Header.new("Last-Modified",mtime.httpdate)
18
- @headers << Riddl::Header.new("ETag",Digest::MD5.hexdigest(mtime.httpdate))
19
- htime = @env["HTTP_IF_MODIFIED_SINCE"].nil? ? Time.at(0) : Time.parse(@env["HTTP_IF_MODIFIED_SINCE"])
20
- if htime == mtime
21
- @headers << Riddl::Header.new("Connection","close")
22
- @status = 304 # Not modified
23
- return []
24
- else
25
- fmt = @a[1] || begin
26
- mt = MIME::Types.type_for(path).first
27
- if mt.nil?
28
- 'text/plain;charset=utf-8'
29
- else
30
- apx = ''
31
- if mt.ascii?
32
- tstr = File.read(path,CharlockHolmes::EncodingDetector::DEFAULT_BINARY_SCAN_LEN)
33
- apx = ';charset=' + CharlockHolmes::EncodingDetector.detect(tstr)[:encoding]
34
- end
35
- mt.to_s + apx
36
- end
37
- end
38
- return Riddl::Parameter::Complex.new('file',fmt,File.open(path,'r'))
39
- end
40
- end
41
- @status = 404
42
- end
43
- end
44
- end
45
- end
@@ -1,328 +0,0 @@
1
- module Riddl
2
- module Utils
3
- module Notifications
4
-
5
- module Producer
6
-
7
- def self::implementation(backend,handler=nil,details=:production)
8
- unless handler.nil? || (handler.is_a? Riddl::Utils::Notifications::Producer::HandlerBase)
9
- raise "handler not a subclass of HandlerBase"
10
- end
11
- Proc.new do
12
- on resource "notifications" do
13
- run Riddl::Utils::Notifications::Producer::Overview if get
14
- on resource "topics" do
15
- run Riddl::Utils::Notifications::Producer::Topics, backend if get
16
- end
17
- on resource "subscriptions" do
18
- run Riddl::Utils::Notifications::Producer::Subscriptions, backend, details if get
19
- run Riddl::Utils::Notifications::Producer::CreateSubscription, backend, handler if post 'subscribe'
20
- on resource do
21
- run Riddl::Utils::Notifications::Producer::Subscription, backend, details if get 'request'
22
- run Riddl::Utils::Notifications::Producer::UpdateSubscription, backend, handler if put 'details'
23
- run Riddl::Utils::Notifications::Producer::DeleteSubscription, backend, handler if delete 'delete'
24
- on resource 'ws' do
25
- run Riddl::Utils::Notifications::Producer::WS, backend, handler if websocket
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end
32
-
33
- class HandlerBase #{{{
34
- def initialize(data)
35
- @data = data
36
- @key = nil
37
- @topics = []
38
- end
39
- def key(k)
40
- @key = k
41
- self
42
- end
43
- def topics(t)
44
- @topics = t
45
- self
46
- end
47
- def ws_open(socket); end
48
- def ws_close; end
49
- def ws_message(socket,data); end
50
- def create; end
51
- def delete; end
52
- def update; end
53
- end #}}}
54
-
55
- class Backend #{{{
56
- attr_reader :topics
57
-
58
- class Sub #{{{
59
- def initialize(name)
60
- @name = name
61
- end
62
- def modify(&block)
63
- XML::Smart.modify(@name,"<subscription xmlns='http://riddl.org/ns/common-patterns/notifications-producer/1.0'/>") do |doc|
64
- doc.register_namespace 'n', 'http://riddl.org/ns/common-patterns/notifications-producer/1.0'
65
- block.call doc
66
- end
67
- end
68
- def delete
69
- FileUtils::rm_rf(File.dirname(@name))
70
- end
71
- def to_s
72
- File.read(@name)
73
- end
74
- def read(&block)
75
- XML::Smart.open_unprotected(@name) do |doc|
76
- doc.register_namespace 'n', 'http://riddl.org/ns/common-patterns/notifications-producer/1.0'
77
- block.call doc
78
- end
79
- end
80
- end #}}}
81
-
82
- class Subs #{{{
83
- def initialize(target)
84
- @target = target
85
- end
86
-
87
- def each(&block)
88
- keys.each do |key|
89
- f = @target + '/' + key + '/subscription.xml'
90
- block.call Sub.new(f), key if File.exists? f
91
- end
92
- end
93
-
94
- def include?(key)
95
- f = @target + '/' + key + '/subscription.xml'
96
- File.exists?(f)
97
- end
98
-
99
- def [](key)
100
- f = @target + '/' + key + '/subscription.xml'
101
- File.exists?(f) ? Sub.new(f) : nil
102
- end
103
-
104
- def create(&block)
105
- key = nil
106
- begin
107
- continue = true
108
- key = Digest::MD5.hexdigest(Kernel::rand().to_s)
109
- Dir.mkdir(@target + '/' + key) rescue continue = false
110
- end until continue
111
- producer_secret = Digest::MD5.hexdigest(Kernel::rand().to_s)
112
- consumer_secret = Digest::MD5.hexdigest(Kernel::rand().to_s)
113
- File.open(@target + '/' + key + '/producer-secret','w') { |f| f.write producer_secret }
114
- File.open(@target + '/' + key + '/consumer-secret','w') { |f| f.write consumer_secret }
115
- XML::Smart::modify(@target + '/' + key + '/subscription.xml',"<subscription xmlns='http://riddl.org/ns/common-patterns/notifications-producer/1.0'/>") do |doc|
116
- doc.register_namespace 'n', 'http://riddl.org/ns/common-patterns/notifications-producer/1.0'
117
- block.call doc, key
118
- end
119
- [key, producer_secret, consumer_secret]
120
- end
121
-
122
- def keys
123
- if File.directory?(@target)
124
- Dir[@target + '/*'].map do |d|
125
- File.directory?(d) ? File.basename(d) : nil
126
- end.compact
127
- else
128
- []
129
- end
130
- end
131
- end #}}}
132
-
133
- def initialize(topics,target)
134
- @target = target.gsub(/^\/+/,'/')
135
-
136
- FileUtils::mkdir_p(@target) unless File.exists?(@target)
137
-
138
- raise "topics file not found" unless File.exists?(topics)
139
- @topics = XML::Smart.open_unprotected(topics.gsub(/^\/+/,'/'))
140
- @topics.register_namespace 'n', 'http://riddl.org/ns/common-patterns/notifications-producer/1.0'
141
-
142
- subscriptions.each do |sub,key|
143
- sub.read do |doc|
144
- if doc.find('/*[@url]').empty?
145
- sub.delete
146
- end
147
- end
148
- end
149
- end
150
-
151
- def subscriptions
152
- Subs.new(@target)
153
- end
154
-
155
- end #}}}
156
-
157
- class Overview < Riddl::Implementation #{{{
158
- def response
159
- Riddl::Parameter::Complex.new("overview","text/xml") do
160
- <<-END
161
- <overview xmlns='http://riddl.org/ns/common-patterns/notifications-producer/1.0'>
162
- <topics/>
163
- <subscriptions/>
164
- </overview>
165
- END
166
- end
167
-
168
- end
169
- end #}}}
170
-
171
- class Topics < Riddl::Implementation #{{{
172
- def response
173
- backend = @a[0]
174
- Riddl::Parameter::Complex.new("overview","text/xml") do
175
- backend.topics.to_s
176
- end
177
- end
178
- end #}}}
179
-
180
- class Subscriptions < Riddl::Implementation #{{{
181
- def response
182
- backend = @a[0]
183
- details = @a[1]
184
- Riddl::Parameter::Complex.new("subscriptions","text/xml") do
185
- ret = XML::Smart::string <<-END
186
- <subscriptions details='#{details}' xmlns='http://riddl.org/ns/common-patterns/notifications-producer/1.0'/>
187
- END
188
- backend.subscriptions.each do |sub,key|
189
- sub.read do |doc|
190
- if doc.root.attributes['url']
191
- ret.root.add('subscription', :id => key, :url => doc.root.attributes['url'])
192
- else
193
- ret.root.add('subscription', :id => key)
194
- end
195
- end
196
- end
197
- ret.to_s
198
- end
199
- end
200
- end #}}}
201
-
202
- class Subscription < Riddl::Implementation #{{{
203
- def response
204
- backend = @a[0]
205
- if(backend.subscriptions.include?(@r.last))
206
- Riddl::Parameter::Complex.new("subscription","text/xml") do
207
- backend.subscriptions[@r.last].to_s
208
- end
209
- else
210
- @status = 404
211
- end
212
- end
213
- end #}}}
214
-
215
- class CreateSubscription < Riddl::Implementation #{{{
216
- def response
217
- backend = @a[0]
218
- handler = @a[1]
219
-
220
- url = @p[0].name == 'url' ? @p.shift.value : nil
221
-
222
- topics = []
223
- key, consumer_secret, producer_secret = backend.subscriptions.create do |doc,key|
224
- doc.root.attributes['url'] = url if url
225
- while @p.length > 0
226
- topic = @p.shift.value
227
- base = @p.shift
228
- type = base.name
229
- items = base.value.split(',')
230
- t = if topics.include?(topic)
231
- doc.find("/n:subscription/n:topic[@id='#{topic}']").first
232
- else
233
- topics << topic
234
- doc.root.add('topic', :id => topic)
235
- end
236
- items.each do |i|
237
- t.add(type[0..-2], i)
238
- end
239
- end
240
- end
241
-
242
- handler.key(key).topics(topics).create unless handler.nil?
243
- [
244
- Riddl::Parameter::Simple.new('key',key),
245
- Riddl::Parameter::Simple.new('producer-secret',producer_secret),
246
- Riddl::Parameter::Simple.new('consumer-secret',consumer_secret)
247
- ]
248
- end
249
- end #}}}
250
-
251
- class DeleteSubscription < Riddl::Implementation #{{{
252
- def response
253
- backend = @a[0]
254
- handler = @a[1]
255
- key = @r.last
256
-
257
- backend.subscriptions[key].delete
258
- handler.key(key).delete unless handler.nil?
259
- return
260
- end
261
- end #}}}
262
-
263
- class UpdateSubscription < Riddl::Implementation #{{{
264
- def response
265
- backend = @a[0]
266
- handler = @a[1]
267
- key = @r.last
268
-
269
- url = @p[0].name == 'url' ? @p.shift.value : nil
270
-
271
- # TODO check if message is valid (with producer secret)
272
- unless backend.subscriptions[key]
273
- @status = 404
274
- return # subscription not found
275
- end
276
-
277
- topics = []
278
- backend.subscriptions[key].modify do |doc|
279
- if url.nil?
280
- doc.find('/n:subscription/@url').delete_all!
281
- else
282
- doc.root.attributes['url'] = url
283
- end
284
- doc.root.children.delete_all!
285
- while @p.length > 1
286
- topic = @p.shift.value
287
- base = @p.shift
288
- type = base.name
289
- items = base.value.split(',')
290
- t = if topics.include?(topic)
291
- doc.find("/n:subscription/n:topic[@id='#{topic}']").first
292
- else
293
- topics << topic
294
- doc.root.add('topic', :id => topic)
295
- end
296
- items.each do |i|
297
- t.add(type[0..-2], i)
298
- end
299
- end
300
- end
301
-
302
- handler.key(key).topics(topics).update unless handler.nil?
303
- nil
304
- end
305
- end #}}}
306
-
307
- class WS < Riddl::WebSocketImplementation #{{{
308
- def onopen
309
- @backend = @a[0]
310
- @handler = @a[1]
311
- @key = @r[-2]
312
- @handler.key(@key).ws_open(self) unless @handler.nil?
313
- end
314
-
315
- def onmessage(data)
316
- @handler.key(@key).ws_message(data) unless @handler.nil?
317
- end
318
-
319
- def onclose
320
- @handler.key(@key).ws_close() unless @handler.nil?
321
- end
322
- end #}}}
323
-
324
- end
325
-
326
- end
327
- end
328
- end
@@ -1,135 +0,0 @@
1
- require 'openssl'
2
- require 'base64'
3
- require 'securerandom'
4
- require 'json'
5
-
6
- module Riddl
7
- module Utils
8
- module OAuth2
9
-
10
- module Helper
11
- class Tokens #{{{
12
- def initialize(tfile)
13
- @tfile = tfile
14
- @changed = changed
15
- read
16
- end
17
-
18
- def [](name)
19
- read if changed != @changed
20
- @tokens[name]
21
- end
22
-
23
- def method_missing(name,*opts)
24
- @tokens.send(name,*opts)
25
- end
26
-
27
- def []=(name,value)
28
- @tokens[name] = value
29
- write
30
- nil
31
- end
32
-
33
- def changed
34
- if File.exists?(@tfile)
35
- File.stat(@tfile).mtime
36
- else
37
- @tokens = {}
38
- write
39
- end
40
- end
41
- private :changed
42
-
43
- def write
44
- EM.defer {
45
- File.write(@tfile, JSON::pretty_generate(@tokens)) rescue {}
46
- }
47
- @changed = changed
48
- end
49
- private :write
50
-
51
- def read
52
- @tokens = JSON::parse(File.read(@tfile)) rescue {}
53
- end
54
- private :read
55
-
56
- def delete(token)
57
- deleted = @tokens.delete(token)
58
- write
59
- deleted
60
- end
61
-
62
- def delete_by_value(user_id)
63
- deleted = @tokens.delete_if { |_, v| v == user_id }
64
- write
65
- deleted
66
- end
67
- end #}}}
68
-
69
- def self::header #{{{
70
- {
71
- :alg => 'HS256',
72
- :typ => 'JWT'
73
- }.to_json
74
- end #}}}
75
-
76
- def self::nonce
77
- SecureRandom::hex(32)
78
- end
79
-
80
- def self::sign(secret, what) #{{{
81
- Base64::urlsafe_encode64 OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), secret, what)
82
- end #}}}
83
-
84
- def self::generate_access_token(client_id, secret, dur=3600)# {{{
85
- h = Base64::urlsafe_encode64 header
86
- p = Base64::urlsafe_encode64( {
87
- :iss => client_id,
88
- :sub => nonce,
89
- :aud => client_id,
90
- :exp => Time.now.to_i + dur
91
- }.to_json)
92
- s = sign(secret, "#{h}.#{p}")
93
- "#{h}.#{p}.#{s}"
94
- end# }}}
95
- def self::generate_refresh_token(client_id, secret, dur=7776000) # {{{
96
- token = Base64::urlsafe_encode64({
97
- :iss => client_id,
98
- :sub => nonce,
99
- :exp => Time.now.to_i + dur
100
- }.to_json)
101
- "#{token}.#{sign(secret,token)}"
102
- end# }}}
103
- def self::generate_optimistic_token(client_id, secret, adur=3600, rdur=7776000) #{{{
104
- t = generate_access_token(client_id, secret, adur)
105
- r = generate_refresh_token(client_id, secret, rdur)
106
- [t, r]
107
- end #}}}
108
-
109
- def self::decrypt_with_shared_secret(data, secret) #{{{
110
- # extract initialization vector from encrypted data for further shenanigans
111
- iv, encr = data[0...16], data[16..-1]
112
-
113
- decipher = OpenSSL::Cipher::Cipher.new 'aes-256-cbc'
114
- decipher.decrypt
115
-
116
- decipher.key = Digest::SHA256.hexdigest secret
117
- decipher.iv = iv
118
-
119
- decipher.update(encr) + decipher.final rescue nil
120
- end #}}}
121
- def self::encrypt_with_shared_secret(data, secret) #{{{
122
- cipher = OpenSSL::Cipher::Cipher.new 'aes-256-cbc'
123
- cipher.encrypt
124
-
125
- key = Digest::SHA256.hexdigest secret
126
- iv = cipher.random_iv
127
- cipher.key = key
128
- cipher.iv = iv
129
-
130
- Base64::urlsafe_encode64(iv + cipher.update(data) + cipher.final) rescue nil
131
- end #}}}
132
- end
133
- end
134
- end
135
- end