riddl 0.99.220 → 0.99.221

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