blix-rest 0.7.2 → 0.8.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf93b3d5836172b6cf1b4ac5df21dfe14e1e998e31e774f9b4fdb4bc5bdec323
4
- data.tar.gz: 622db8bf8d0052fd25f3727656ac6b641bc63a2667e97eb3f93f7dc4884e03ee
3
+ metadata.gz: 5db97b519f38052ee2b7272f3a25d6ec4d5e3ba71cae44bdcd83e25cdd6902f2
4
+ data.tar.gz: e7fb1c1da94eb622629a718bd81763d6874dc14028bed0ba8328b42cac5c2152
5
5
  SHA512:
6
- metadata.gz: 32516afd36a4a6c36c81650e50f84970cf2436b7cf682a51cae83d1300f736d4dbfe49d04d590ac8ffde841d2dac7f236d0979b3b5c1d5bec230112584eb6e0d
7
- data.tar.gz: 7f106344ec03beb691d5870c5809f379285d578ba17cb9b8edd1ea86c5c64dfa31b0a9747a75adab7730ba514cecca3a508cb36f5560343f147cd64d866ca3d4
6
+ metadata.gz: fb48999da4fffe47e1d52cb85319c7ed6473c756a55d6e87edef288a9a118f971c90d709ea73aaf566e1f927a7779056547a91d1cfae71b2cd1a157658c3d2a4
7
+ data.tar.gz: 93d06074a5b66da9099c1d1bee4fb23a00ac18f994809efb9fb28f0be7d6bf3a889fdad45a2fb349dbabeec6c816c77a34975a8cd7de70efbcce4cff2af1acb2
data/README.md CHANGED
@@ -231,6 +231,7 @@ have access to a number of methods
231
231
  method : the request method lowercase( 'get'/'post' ..)
232
232
  req : the rack request
233
233
  body : the request body as a string
234
+ path : the request path
234
235
  query_params : a hash of parameters as passed in the url as parameters
235
236
  path_params : a hash of parameters constructed from variable parts of the path
236
237
  post_params : a hash of parameters passed in the body of the request
@@ -246,7 +247,7 @@ have access to a number of methods
246
247
  render_erb : (template_name [,:layout=>name])
247
248
  server_cache : get the server cache object
248
249
  server_cache_get : retrieve/store value in cache
249
- path_for : (path) give the correct path for an internal path
250
+ path_for : (path) give the external path for an internal path
250
251
  url_for : (path) give the full url for an internal path
251
252
  h : escape html string to avoid XSS
252
253
  escape_javascript : escape a javascript string
@@ -291,22 +292,27 @@ is ok though.
291
292
 
292
293
  the `before_route` hook can be used to modify the path or options of a route.
293
294
 
294
- *NOTE* ! when manipulating the path you have to modify the string object in place.
295
-
296
295
  the verb can not be modified
297
296
 
298
297
  example:
299
298
 
300
299
  class MyController < Blix::Rest::Controller
301
300
 
302
- before_route do |verb, path, opts|
303
- opts[:level] = :visitor unless opts[:level]
304
- path.prepend('/') unless path[0] == '/'
305
- path.prepend('/app') unless path[0, 4] == '/app'
301
+ before_route do |route|
302
+ route.default_option(:level,:visitor)
303
+ route.prefix_path('/app')
306
304
  end
307
305
  ...
308
306
  end
309
307
 
308
+ the following methods are available on the route:
309
+
310
+ verb # readonly, the 'GET','POST' etc verb of the route
311
+ path # the path of the route
312
+ options # the options associated with the route eg :accept
313
+ path_prefix('/xx') # ensure the path has the given prefix
314
+ default_option(:xxx,'foo') # ensure that option has the given default value
315
+
310
316
  ### Sessions
311
317
 
312
318
  the following methods are available in the controller for managing sessions.
@@ -321,11 +327,53 @@ this will generate a new session_id and setup the relevant headers
321
327
 
322
328
  options can include:
323
329
 
324
- :secure => true # secure cookies only
325
- :http = >true # cookies for http only not javascript requests
330
+ :secure => true # secure cookies only
331
+ :http = >true # cookies for http only not javascript requests
326
332
  :samesite =>:strict # use strict x-site policy
327
333
  :samesite =>:lax # use lax x-site policy
328
334
 
335
+ For more complete session management:
336
+
337
+ class MyController < Blix::Rest::Controller
338
+ include Blix::Rest::Session
339
+
340
+ session_name :xxx # optional default'blix'
341
+ session_opts :http=>true # optional
342
+ session_manager: MyManager.new # optional , default Blix::Redis::Store
343
+
344
+
345
+ def myroutine
346
+ @xxx = session['xxx']
347
+
348
+ session['yyy'] = true
349
+ end
350
+
351
+ end
352
+
353
+ options can include:
354
+
355
+ :secure # false
356
+ :http # false
357
+ :samesite # lax
358
+ :path # '/'
359
+ :expire_secs # 30 mins
360
+ :cleanup_every_secs # 5 minutes
361
+ :max_age # nil
362
+
363
+ the following methods are available:
364
+
365
+ reset_session # gen new session id
366
+ session # session hash
367
+ csrf_token # the session csrf token
368
+
369
+ route options that affect sessions:
370
+
371
+ :nosession=>true # no session will be set/retrieved
372
+ :cache=>true # no session will be set/retrieved
373
+ :csrf=>true # request will be validated for calid csrf token.
374
+ # token must be in header X_CSRF_TOKEN field
375
+
376
+ session configuration is inherited from superclass controllers unless overridden.
329
377
 
330
378
  ## Cache
331
379
 
@@ -400,6 +448,31 @@ the cache is not used in development/testmode , only in production mode.
400
448
 
401
449
  only cache pages with **HEADERS** and **CONTENT** that is not user specific.
402
450
 
451
+ ## CORS
452
+
453
+ cross origin site requests
454
+
455
+ MyController < Blix::Rest::Controller
456
+
457
+ get '/info/crosssite' do
458
+ set_accept_cors
459
+ {:data=>'foo'}
460
+ end
461
+
462
+ options '/info/' crosssite' do
463
+ set_accept_cors, :headers=>'Content-Type',
464
+ end
465
+
466
+ end
467
+
468
+ within an `options` response the following parameters can be passes.
469
+
470
+ :origin=>'www.othersite.com' # specify specific allowed origin.
471
+ :methods => [:get] # allowed methods
472
+ :max_age => 86400 # maximum age this auth is valid
473
+ :headers => ['Content-Type','X-OTHER'] # allow additional headers
474
+ :credentials => true # allow requests with credentials
475
+
403
476
  ## Views
404
477
 
405
478
  the location of your views defaults to `app/views` otherwise set it manually with:
@@ -488,6 +561,9 @@ NOTE : if you need to set up your database with users then you can use the follo
488
561
 
489
562
  in features/support/world.rb .........
490
563
 
564
+
565
+ require 'blix/rest/cucumber'
566
+
491
567
  class RestWorld
492
568
 
493
569
  # add a hook to create the user in the database -
@@ -94,6 +94,7 @@ module Blix::Rest
94
94
  end
95
95
  end
96
96
 
97
+
97
98
  def form_hash
98
99
  StringHash.new(req.POST)
99
100
  end
@@ -166,6 +167,14 @@ module Blix::Rest
166
167
  @_parameters
167
168
  end
168
169
 
170
+ def route_params
171
+ @_parameters
172
+ end
173
+
174
+ def route_options
175
+ @_parameters
176
+ end
177
+
169
178
  def response
170
179
  @_response
171
180
  end
@@ -232,6 +241,30 @@ module Blix::Rest
232
241
  [login, password]
233
242
  end
234
243
 
244
+ # set the cors headers
245
+ def set_accept_cors(opts={})
246
+ origin = opts[:origin] || env['HTTP_ORIGIN'] || '*'
247
+ origin = origin.to_s
248
+ if method=='options'
249
+ methods = [opts[:methods] || []].to_a.flatten
250
+ max_age = opts[:max_age] || 86400
251
+ headers = [opts[:headers] || []].to_a.flatten
252
+ credentials = opts.key?(:credentials) ? !!opts[:credentials] : true
253
+ methods = [:get] if methods.empty?
254
+ methods = methods.map{|m| m.to_s.upcase}
255
+ headers = ['Content-Type'] if headers.empty?
256
+ max_age = max_age.to_i
257
+
258
+ add_headers 'Access-Control-Allow-Origin' => origin,
259
+ 'Access-Control-Allow-Methods'=>methods.join(', '),
260
+ 'Access-Control-Allow-Headers'=>headers,
261
+ 'Access-Control-Max-Age'=>max_age, #86400,
262
+ 'Access-Control-Allow-Credentials'=>'true'
263
+ else
264
+ add_headers 'Access-Control-Allow-Origin' => origin
265
+ end
266
+ end
267
+
235
268
  def set_status(value)
236
269
  @_response.status = value.to_i
237
270
  end
@@ -392,6 +425,9 @@ module Blix::Rest
392
425
  response
393
426
  end
394
427
 
428
+ def session_before(opts); end # empty session before hooh
429
+ def session_after; end # empty session after hook
430
+
395
431
  #----------------------------------------------------------------------------------------------------------
396
432
 
397
433
  def _setup(context, _verb, _path, _parameters)
@@ -524,16 +560,18 @@ module Blix::Rest
524
560
  raise ServiceError, 'invalid format for this request' unless accept.index format
525
561
  end
526
562
 
527
- def _do_route_hook(verb, path, opts)
563
+ def _do_route_hook(route)
528
564
  if @_route_hook
529
- superclass._do_route_hook(verb, path, opts) if superclass.respond_to? :_do_route_hook
530
- @_route_hook.call(verb, path, opts)
565
+ superclass._do_route_hook(route) if superclass.respond_to? :_do_route_hook
566
+ @_route_hook.call(route)
531
567
  end
532
568
  end
533
569
 
534
570
  def route(verb, path, opts = {}, &blk)
571
+ path = '/' + path unless path[0] == '/'
535
572
  path = String.new(path) # in case frozen.
536
- _do_route_hook(verb.dup, path, opts)
573
+ route = Route.new(verb, path, opts)
574
+ _do_route_hook(route)
537
575
  proc = lambda do |context|
538
576
  unless opts[:force] && (opts[:accept] == :*)
539
577
  check_format(opts[:accept], context.format)
@@ -541,6 +579,7 @@ module Blix::Rest
541
579
  app = new
542
580
  app._setup(context, verb, path, opts)
543
581
  begin
582
+ app.session_before(opts)
544
583
  app.before(opts)
545
584
  app.__before
546
585
  context.response = app.instance_eval( &blk )
@@ -549,6 +588,7 @@ module Blix::Rest
549
588
  ensure
550
589
  app.__after
551
590
  app.after(opts, context.response)
591
+ app.session_after
552
592
  context.response
553
593
  end
554
594
  end
@@ -0,0 +1,33 @@
1
+ module Blix::Rest
2
+
3
+ # class used to represent a route and to allow manipulation of the
4
+ # options and path of the route before registrATION
5
+
6
+ class Route
7
+
8
+ attr_reader :verb, :path, :opts
9
+ alias_method :method, :verb
10
+ alias_method :options, :opts
11
+
12
+ def initialize(verb, path, opts)
13
+ @verb = verb.dup
14
+ @path = path
15
+ @opts = opts
16
+ end
17
+
18
+ def default_option(key, val)
19
+ opts[key.to_sym] = val unless opts.key?(key) || opts.key?(key.to_sym)
20
+ end
21
+
22
+ def path_prefix(prefix)
23
+ prefix = prefix.to_s
24
+ prefix = '/' + prefix unless prefix[0] == '/'
25
+ path.replace( prefix + path) unless path.start_with?(prefix)
26
+ end
27
+
28
+ def path=(val)
29
+ path.replace(val.to_s)
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,138 @@
1
+ module Blix::Rest
2
+
3
+ module Session
4
+ #----------------------------------------------------------------------------
5
+ #
6
+ # manage the session and authorization
7
+ #
8
+ #----------------------------------------------------------------------------
9
+
10
+ DAY = 24 * 60 * 60
11
+ MIN = 60
12
+ SESSION_NAME = 'blix'
13
+
14
+ SESSION_OPTS = {
15
+ #:secure=>true,
16
+ :http => false,
17
+ :samesite => :lax,
18
+ :path => Blix::Rest.full_path('/'),
19
+ :expire_secs => 30 * MIN, # 30 mins
20
+ :cleanup_every_secs => 5 * 60 # 5 minutes
21
+ #:max_age => nil # session cookie
22
+ }.freeze
23
+
24
+
25
+ def session_manager
26
+ self.class.get_session_manager
27
+ end
28
+
29
+ def session_name
30
+ self.class.get_session_name
31
+ end
32
+
33
+ def session_opts
34
+ self.class.get_session_opts
35
+ end
36
+
37
+ def session
38
+ @__session
39
+ end
40
+
41
+ def csrf_token
42
+ @__session['csrf'] ||= SecureRandom.hex(32)
43
+ end
44
+
45
+ def reset_session
46
+ raise 'login_session missing' unless @__session && @__session_id
47
+ session_manager.delete_session(@__session_id)
48
+ @__session_id = refresh_session_id(session_name, session_opts)
49
+ @__session['csrf'] = SecureRandom.hex(32)
50
+ session_manager.store_session(@__session_id, @__session)
51
+ end
52
+
53
+ # get a session id and use this to retrieve the session information - if any.
54
+ #
55
+ def session_before(opts)
56
+ @__session = {}
57
+
58
+ # do not set session on pages. that will be cached.
59
+ unless opts[:nosession] || opts[:cache]
60
+ @__session_id = get_session_id(session_name, session_opts)
61
+ @__session =
62
+ begin
63
+ session_manager.get_session(@__session_id)
64
+ rescue SessionExpiredError
65
+ @__session_id = refresh_session_id(session_name, session_opts)
66
+ session_manager.get_session(@__session_id)
67
+ end
68
+ end
69
+
70
+ if opts[:csrf]
71
+ if env["HTTP_X_CSRF_TOKEN"] != csrf_token
72
+ send_error("invalid csrf token")
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ # save the session hash before we go.
79
+ def session_after
80
+ session_manager.store_session(@__session_id, @__session) if @__session_id
81
+ end
82
+
83
+ private
84
+
85
+ def self.included(mod)
86
+ mod.extend Session::ClassMethods
87
+ end
88
+
89
+ module ClassMethods
90
+
91
+ def session_name(name)
92
+ @_sess_name = name.to_s
93
+ end
94
+
95
+ def session_opts(opts)
96
+ @_sess_custom_opts = opts
97
+ end
98
+
99
+ def session_manager(man)
100
+ raise ArgumentError, "incompatible session store" unless man.respond_to?(:get_session)
101
+ raise ArgumentError, "incompatible session store" unless man.respond_to?(:store_session)
102
+ raise ArgumentError, "incompatible session store" unless man.respond_to?(:delete_session)
103
+ @_sess_manager= man
104
+ end
105
+
106
+ def get_session_name
107
+ @_sess_name ||= begin
108
+ if superclass.respond_to?(:get_session_name)
109
+ superclass.get_session_name
110
+ else
111
+ SESSION_NAME
112
+ end
113
+ end
114
+ end
115
+
116
+ def get_session_opts
117
+ @_session_opts ||= begin
118
+ if superclass.respond_to?(:get_session_opts)
119
+ superclass.get_session_opts.merge(@_sess_custom_opts || {})
120
+ else
121
+ SESSION_OPTS.merge(@_sess_custom_opts || {})
122
+ end
123
+ end
124
+ end
125
+
126
+ def get_session_manager
127
+ @_sess_manager ||= begin
128
+ if superclass.respond_to?(:get_session_manager)
129
+ superclass.get_session_manager
130
+ else
131
+ Blix::RedisStore.new(get_session_opts)
132
+ end
133
+ end
134
+ end
135
+
136
+ end # ClassMethods
137
+ end # Session
138
+ end # Blix::Rest
@@ -1,5 +1,5 @@
1
1
  module Blix
2
2
  module Rest
3
- VERSION = "0.7.2"
3
+ VERSION = "0.8.2"
4
4
  end
5
5
  end
data/lib/blix/rest.rb CHANGED
@@ -150,7 +150,7 @@ require 'blix/rest/format_parser'
150
150
  require 'blix/rest/request_mapper'
151
151
  require 'blix/rest/cache'
152
152
  require 'blix/rest/server'
153
- # require 'blix/rest/provider'
153
+ require 'blix/rest/route'
154
154
  require 'blix/rest/controller'
155
155
  # require 'blix/rest/provider_controller'
156
156
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blix-rest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clive Andrews
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-12 00:00:00.000000000 Z
11
+ date: 2023-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httpclient
@@ -109,7 +109,9 @@ files:
109
109
  - lib/blix/rest/request_mapper.rb
110
110
  - lib/blix/rest/resource_cache.rb
111
111
  - lib/blix/rest/response.rb
112
+ - lib/blix/rest/route.rb
112
113
  - lib/blix/rest/server.rb
114
+ - lib/blix/rest/session.rb
113
115
  - lib/blix/rest/string_hash.rb
114
116
  - lib/blix/rest/version.rb
115
117
  - lib/blix/utils.rb