unrestful 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1f85b74343e706d02705c4994bb54bf9433697b273eff7f28e451c21516fea8
4
- data.tar.gz: b274e109d89b2d89806269627fde55a916bf0b83e4e68f4bea6fe73bbd224caa
3
+ metadata.gz: 8ed14da4951593e6b5461d2d776a16097b46ed7495ab60ca7cae9c24fc778bdc
4
+ data.tar.gz: 2f9dd412dcae54e6a86fff857752898d1f654f2d6acf7cf5f1d7e6d8f6c87bf1
5
5
  SHA512:
6
- metadata.gz: 8889f208b5d6fc05ec1516fcd14b85c0392fef74f379f4f35d7ce8d66404741d93ee3f7d0171f7d777312c90c6235ead34085f52a8628da841b680ad99a9ccf3
7
- data.tar.gz: 45f21e549c14e1497776463adf39704002ea6e2e0351be0224da1095b29ec71fdb93a7ad0d88e462be96bbb4620f87d2fcbe9bfa74fd4053b1a36fba788a2998
6
+ metadata.gz: f58bde1f04744005aeec13d1e46c1002c66de852086b1da36843d66fe501320339f1d8e63e2c5c6380e9943b0abaa13f16a8035a6e9f7d5a0ee55e1622e05f33
7
+ data.tar.gz: d816f606a7aa1e36857c046e76df687d7866d4df5b35085f58642ac7ad9a39b430f737ea1dc6f16ecee0c599acfb85bb4e1a3191b4a131f5f139f1f48ac5fffe
@@ -1,111 +1,111 @@
1
1
  require 'redis'
2
2
 
3
3
  module Unrestful
4
- class AsyncJob
5
- include ActiveModel::Serializers::JSON
6
-
7
- ALLOCATED = 0
8
- RUNNING = 1
9
- FAILED = 2
10
- SUCCESS = 3
11
-
12
- KEY_TIMEOUT = 3600
13
- KEY_LENGTH = 10
14
- CHANNEL_TIMEOUT = 10
15
-
16
- attr_reader :job_id
17
-
18
- def attributes
19
- {
20
- job_id: job_id,
21
- state: state,
22
- last_message: last_message,
23
- ttl: ttl
24
- }
25
- end
26
-
27
- def initialize(job_id: nil)
28
- if job_id.nil?
29
- @job_id = SecureRandom.hex(KEY_LENGTH)
30
- else
31
- @job_id = job_id
32
- end
33
- end
34
-
35
- def update(state, message: '')
36
- raise ArgumentError, 'failed states must have a message' if message.blank? && state == FAILED
37
-
38
- redis.set(job_key, state)
39
- redis.set(job_message, message) unless message.blank?
40
-
41
- if state == ALLOCATED
42
- redis.expire(job_key, KEY_TIMEOUT)
43
- redis.expire(job_message, KEY_TIMEOUT)
44
- end
45
- end
46
-
47
- def ttl
48
- redis.ttl(job_key)
49
- end
50
-
51
- def state
52
- redis.get(job_key)
53
- end
54
-
55
- def last_message
56
- redis.get(job_message)
57
- end
58
-
59
- def delete
60
- redis.del(job_key)
61
- redis.del(job_message)
62
- end
63
-
64
- def subscribe(timeout: CHANNEL_TIMEOUT, &block)
65
- raise AsyncError, "job #{job_key} doesn't exist" unless valid?
66
-
67
- redis.subscribe_with_timeout(timeout, job_channel, &block)
68
- end
69
-
70
- def publish(message)
71
- raise AsyncError, "job #{job_key} doesn't exist" unless valid?
72
-
73
- redis.publish(job_channel, message)
74
- end
75
-
76
- def valid?
77
- redis.exists(job_key)
78
- end
79
-
80
- def unsubscribe
81
- redis.unsubscribe(job_channel)
82
- rescue
83
- # ignore unsub errors
84
- end
85
-
86
- def redis
87
- @redis ||= Redis.new(url: Unrestful.configuration.redis_address)
88
- end
89
-
90
- def close
91
- redis.unsubscribe(job_channel) if redis.subscribed?
92
- ensure
93
- @redis.quit
94
- end
95
-
96
- private
97
-
98
- def job_key
99
- "unrestful:job:state:#{@job_id}"
100
- end
101
-
102
- def job_channel
103
- "unrestful:job:channel:#{@job_id}"
104
- end
105
-
106
- def job_message
107
- "unrestful:job:message:#{@job_id}"
108
- end
109
-
110
- end
4
+ class AsyncJob
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ ALLOCATED = 0
8
+ RUNNING = 1
9
+ FAILED = 2
10
+ SUCCESS = 3
11
+
12
+ KEY_TIMEOUT = 3600
13
+ KEY_LENGTH = 10
14
+ CHANNEL_TIMEOUT = 10
15
+
16
+ attr_reader :job_id
17
+
18
+ def attributes
19
+ {
20
+ job_id: job_id,
21
+ state: state,
22
+ last_message: last_message,
23
+ ttl: ttl
24
+ }
25
+ end
26
+
27
+ def initialize(job_id: nil)
28
+ if job_id.nil?
29
+ @job_id = SecureRandom.hex(KEY_LENGTH)
30
+ else
31
+ @job_id = job_id
32
+ end
33
+ end
34
+
35
+ def update(state, message: '')
36
+ raise ArgumentError, 'failed states must have a message' if message.blank? && state == FAILED
37
+
38
+ redis.set(job_key, state)
39
+ redis.set(job_message, message) unless message.blank?
40
+
41
+ if state == ALLOCATED
42
+ redis.expire(job_key, KEY_TIMEOUT)
43
+ redis.expire(job_message, KEY_TIMEOUT)
44
+ end
45
+ end
46
+
47
+ def ttl
48
+ redis.ttl(job_key)
49
+ end
50
+
51
+ def state
52
+ redis.get(job_key)
53
+ end
54
+
55
+ def last_message
56
+ redis.get(job_message)
57
+ end
58
+
59
+ def delete
60
+ redis.del(job_key)
61
+ redis.del(job_message)
62
+ end
63
+
64
+ def subscribe(timeout: CHANNEL_TIMEOUT, &block)
65
+ raise AsyncError, "job #{job_key} doesn't exist" unless valid?
66
+
67
+ redis.subscribe_with_timeout(timeout, job_channel, &block)
68
+ end
69
+
70
+ def publish(message)
71
+ raise AsyncError, "job #{job_key} doesn't exist" unless valid?
72
+
73
+ redis.publish(job_channel, message)
74
+ end
75
+
76
+ def valid?
77
+ redis.exists(job_key)
78
+ end
79
+
80
+ def unsubscribe
81
+ redis.unsubscribe(job_channel)
82
+ rescue
83
+ # ignore unsub errors
84
+ end
85
+
86
+ def redis
87
+ @redis ||= Redis.new(url: Unrestful.configuration.redis_address)
88
+ end
89
+
90
+ def close
91
+ redis.unsubscribe(job_channel) if redis.subscribed?
92
+ ensure
93
+ @redis.quit
94
+ end
95
+
96
+ private
97
+
98
+ def job_key
99
+ "unrestful:job:state:#{@job_id}"
100
+ end
101
+
102
+ def job_channel
103
+ "unrestful:job:channel:#{@job_id}"
104
+ end
105
+
106
+ def job_message
107
+ "unrestful:job:message:#{@job_id}"
108
+ end
109
+
110
+ end
111
111
  end
@@ -1,9 +1,16 @@
1
1
  module Unrestful
2
- class Error < StandardError; end
3
- class FailError < Error; end
4
- class AuthError < Error; end
5
- class NotLiveError < Error; end
6
- class LiveError < Error; end
7
- class AsyncError < Error; end
8
- class StreamInterrupted < Error; end
2
+ class Error < StandardError;
3
+ end
4
+ class FailError < Error;
5
+ end
6
+ class AuthError < Error;
7
+ end
8
+ class NotLiveError < Error;
9
+ end
10
+ class LiveError < Error;
11
+ end
12
+ class AsyncError < Error;
13
+ end
14
+ class StreamInterrupted < Error;
15
+ end
9
16
  end
@@ -2,25 +2,25 @@ require_relative 'response'
2
2
  require 'json/add/exception'
3
3
 
4
4
  module Unrestful
5
- class FailResponse < Unrestful::Response
6
-
7
- attr_accessor :message
8
- attr_accessor :exception
5
+ class FailResponse < Unrestful::Response
9
6
 
10
- def self.render(message, exc: nil)
11
- obj = Unrestful::FailResponse.new
12
- obj.message = message
13
- obj.exception = exc if !exc.nil? && Rails.env.development?
14
- obj.ok = false
15
-
16
- return obj.as_json
17
- end
18
-
19
- def as_json
20
- result = { message: message }
21
- result.merge!({ exception: exception }) unless exception.nil?
22
- super.merge(result)
23
- end
24
-
25
- end
7
+ attr_accessor :message
8
+ attr_accessor :exception
9
+
10
+ def self.render(message, exc: nil)
11
+ obj = Unrestful::FailResponse.new
12
+ obj.message = message
13
+ obj.exception = exc if !exc.nil? && Rails.env.development?
14
+ obj.ok = false
15
+
16
+ return obj.as_json
17
+ end
18
+
19
+ def as_json
20
+ result = {message: message}
21
+ result.merge!({exception: exception}) unless exception.nil?
22
+ super.merge(result)
23
+ end
24
+
25
+ end
26
26
  end
@@ -3,32 +3,32 @@ require 'net/http'
3
3
  require 'uri'
4
4
 
5
5
  module Unrestful
6
- class JsonWebToken
7
- LEEWAY = 30
6
+ class JsonWebToken
7
+ LEEWAY = 30
8
8
 
9
- def self.verify(token)
10
- JWT.decode(token, nil,
11
- true,
12
- algorithm: 'RS256',
13
- iss: Unrestful.configuration.issuer,
14
- verify_iss: true,
15
- aud: Unrestful.configuration.audience,
16
- verify_aud: true) do |header|
17
- jwks_hash[header['kid']]
18
- end
19
- end
9
+ def self.verify(token)
10
+ JWT.decode(token, nil,
11
+ true,
12
+ algorithm: 'RS256',
13
+ iss: Unrestful.configuration.issuer,
14
+ verify_iss: true,
15
+ aud: Unrestful.configuration.audience,
16
+ verify_aud: true) do |header|
17
+ jwks_hash[header['kid']]
18
+ end
19
+ end
20
20
 
21
- def self.jwks_hash
22
- jwks_raw = Net::HTTP.get URI("#{Unrestful.configuration.issuer}.well-known/jwks.json")
23
- jwks_keys = Array(JSON.parse(jwks_raw)['keys'])
24
- Hash[
25
- jwks_keys.map do |k|
26
- [
27
- k['kid'],
28
- OpenSSL::X509::Certificate.new(Base64.decode64(k['x5c'].first)).public_key
29
- ]
30
- end
31
- ]
32
- end
33
- end
21
+ def self.jwks_hash
22
+ jwks_raw = Net::HTTP.get URI("#{Unrestful.configuration.issuer}.well-known/jwks.json")
23
+ jwks_keys = Array(JSON.parse(jwks_raw)['keys'])
24
+ Hash[
25
+ jwks_keys.map do |k|
26
+ [
27
+ k['kid'],
28
+ OpenSSL::X509::Certificate.new(Base64.decode64(k['x5c'].first)).public_key
29
+ ]
30
+ end
31
+ ]
32
+ end
33
+ end
34
34
  end
@@ -2,33 +2,43 @@
2
2
  require 'jwt'
3
3
 
4
4
  module Unrestful
5
- module JwtSecured
6
- extend ActiveSupport::Concern
7
-
8
- private
9
-
10
- def authenticate_request!
11
- @auth_payload, @auth_header = auth_token
12
-
13
- raise AuthError, 'Insufficient scope' unless scope_included
14
- end
5
+ module JwtSecured
6
+ extend ActiveSupport::Concern
15
7
 
16
- def http_token
17
- if request.headers['Authorization'].present?
18
- request.headers['Authorization'].split(' ').last
19
- end
20
- end
21
-
22
- def auth_token
23
- JsonWebToken.verify(http_token)
24
- end
8
+ private
25
9
 
26
- def scope_included
27
- if self.class.assigned_scopes[@method] == nil
28
- false
29
- else
30
- (String(@auth_payload['scope']).split(' ') & (self.class.assigned_scopes[@method])).any?
31
- end
32
- end
33
- end
34
- end
10
+ def authenticate_request!
11
+ @auth_payload, @auth_header = auth_token
12
+ raise AuthError, 'Insufficient scope' unless scope_included
13
+ end
14
+
15
+ def http_token
16
+ if request.headers['Authorization'].present?
17
+ # strip off the Bearer
18
+ request.headers['Authorization'].split(' ').last
19
+ end
20
+ end
21
+
22
+ def auth_token
23
+ JsonWebToken.verify(http_token)
24
+ end
25
+
26
+ def scope_included
27
+ permissions_required = class_assigned_scopes
28
+ permissions_present = @auth_payload['permissions'] || []
29
+ # ensure that we have the required permission to call the method
30
+ (permissions_present & permissions_required).any?
31
+ end
32
+
33
+ def class_assigned_scopes
34
+ class_assigned_scopes = self.class.assigned_scopes[@method] || []
35
+ # ensure that we have a scope defined for this method, and that it has an array value
36
+ if class_assigned_scopes.nil? || class_assigned_scopes.empty? || !class_assigned_scopes.is_a?(Array)
37
+ raise "#{self.class.name} MUST declare a \"scopes\" hash that INCLUDES key \"#{@method}\" with value of an array of permissions"
38
+ end
39
+ class_assigned_scopes
40
+ rescue NoMethodError
41
+ raise "#{self.class.name} MUST implement ::Unrestful::RpcController AND declare \"scopes\" for each method request, with its corresponding array of permissions"
42
+ end
43
+ end
44
+ end
@@ -4,8 +4,8 @@ module Unrestful
4
4
  attr_accessor :ok
5
5
 
6
6
  def as_json
7
- { ok: @ok }
7
+ {ok: @ok}
8
8
  end
9
9
 
10
10
  end
11
- end
11
+ end
@@ -2,18 +2,18 @@ module Unrestful
2
2
  class RpcController
3
3
 
4
4
  attr_reader :service
5
- attr_reader :method
6
- attr_reader :request
7
- attr_reader :response
8
- attr_reader :live
9
- attr_reader :async
10
- attr_reader :job
5
+ attr_reader :method
6
+ attr_reader :request
7
+ attr_reader :response
8
+ attr_reader :live
9
+ attr_reader :async
10
+ attr_reader :job
11
11
 
12
12
  class_attribute :before_method_callbacks, default: ActiveSupport::HashWithIndifferentAccess.new
13
- class_attribute :after_method_callbacks, default: ActiveSupport::HashWithIndifferentAccess.new
14
- class_attribute :assigned_scopes, default: ActiveSupport::HashWithIndifferentAccess.new
15
- class_attribute :live_methods, default: []
16
- class_attribute :async_methods, default: []
13
+ class_attribute :after_method_callbacks, default: ActiveSupport::HashWithIndifferentAccess.new
14
+ class_attribute :assigned_scopes, default: ActiveSupport::HashWithIndifferentAccess.new
15
+ class_attribute :live_methods, default: []
16
+ class_attribute :async_methods, default: []
17
17
 
18
18
  def before_callbacks
19
19
  self.class.before_method_callbacks.each do |k, v|
@@ -26,36 +26,36 @@ module Unrestful
26
26
  self.class.after_method_callbacks.each do |k, v|
27
27
  self.send(k)
28
28
  end
29
- end
29
+ end
30
+
31
+ def write(message)
32
+ raise NotLiveError unless live
33
+ msg = message.end_with?("\n") ? message : "#{message}\n"
34
+ response.stream.write msg
35
+ end
30
36
 
31
- def write(message)
32
- raise NotLiveError unless live
33
- msg = message.end_with?("\n") ? message : "#{message}\n"
34
- response.stream.write msg
35
- end
36
-
37
37
  protected
38
38
 
39
39
  def self.before_method(method, options = {})
40
- self.before_method_callbacks = { method => options }
40
+ self.before_method_callbacks = {method => options}
41
41
  end
42
42
 
43
43
  def self.after_method(method, options = {})
44
- self.after_method_callbacks = { method => options }
45
- end
44
+ self.after_method_callbacks = {method => options}
45
+ end
46
46
 
47
- def self.scopes(scope_list)
48
- self.assigned_scopes = ActiveSupport::HashWithIndifferentAccess.new(scope_list)
49
- end
47
+ def self.scopes(scope_list)
48
+ self.assigned_scopes = ActiveSupport::HashWithIndifferentAccess.new(scope_list)
49
+ end
50
+
51
+ def self.live(live_list)
52
+ self.live_methods = live_list
53
+ end
50
54
 
51
- def self.live(live_list)
52
- self.live_methods = live_list
53
- end
55
+ def self.async(async_list)
56
+ self.async_methods = async_list
57
+ end
54
58
 
55
- def self.async(async_list)
56
- self.async_methods = async_list
57
- end
58
-
59
59
  def fail!(message = "")
60
60
  raise ::Unrestful::FailError, message
61
61
  end
@@ -14,7 +14,7 @@ module Unrestful
14
14
  end
15
15
 
16
16
  def as_json
17
- super.merge({ payload: payload })
17
+ super.merge({payload: payload})
18
18
  end
19
19
  end
20
- end
20
+ end
@@ -1,19 +1,19 @@
1
1
  module Unrestful
2
- module Utils
2
+ module Utils
3
3
 
4
- def watchdog(last_words)
5
- yield
6
- rescue Exception => exc
7
- Rails.logger.debug "#{last_words}: #{exc}"
8
- #raise exc
9
- end
10
-
11
- def safe_thread(name, &block)
12
- Thread.new do
13
- Thread.current['unrestful_name'.freeze] = name
14
- watchdog(name, &block)
15
- end
16
- end
17
-
18
- end
4
+ def watchdog(last_words)
5
+ yield
6
+ rescue Exception => exc
7
+ Rails.logger.debug "#{last_words}: #{exc}"
8
+ #raise exc
9
+ end
10
+
11
+ def safe_thread(name, &block)
12
+ Thread.new do
13
+ Thread.current['unrestful_name'.freeze] = name
14
+ watchdog(name, &block)
15
+ end
16
+ end
17
+
18
+ end
19
19
  end
@@ -1,3 +1,3 @@
1
1
  module Unrestful
2
- VERSION = '0.1.3'
2
+ VERSION = '0.1.4'
3
3
  end
data/lib/unrestful.rb CHANGED
@@ -3,43 +3,43 @@ require "unrestful/engine"
3
3
  Dir[File.join(__dir__, 'unrestful', '*.rb')].each {|file| require file}
4
4
 
5
5
  module Unrestful
6
- def self.configure(options = {}, &block)
7
- @config = Unrestful::Config.new(options)
8
- block.call(@config) if block.present?
9
- @config
10
- end
11
-
12
- def self.configuration
13
- @config || Unrestful::Config.new({})
14
- end
15
-
16
- class Config
17
- def initialize(options)
18
- @options = options
19
- end
20
-
21
- def issuer
22
- @options[:issuer] || ENV.fetch("ISSUER")
23
- end
24
-
25
- def issuer=(value)
26
- @options[:issuer] = value
27
- end
28
-
29
- def audience
30
- @options[:audience] || ENV.fetch("AUDIENCE")
31
- end
32
-
33
- def audience=(value)
34
- @options[:audience] = value
35
- end
36
-
37
- def redis_address
38
- @options[:redis_address] || ENV.fetch("REDIS_URL") {"redis://localhost:6379/1"}
39
- end
40
-
41
- def redis_address=(value)
42
- @options[:redis_address] = value
43
- end
44
- end
6
+ def self.configure(options = {}, &block)
7
+ @config = Unrestful::Config.new(options)
8
+ block.call(@config) if block.present?
9
+ @config
10
+ end
11
+
12
+ def self.configuration
13
+ @config || Unrestful::Config.new({})
14
+ end
15
+
16
+ class Config
17
+ def initialize(options)
18
+ @options = options
19
+ end
20
+
21
+ def issuer
22
+ @options[:issuer] || ENV.fetch("ISSUER")
23
+ end
24
+
25
+ def issuer=(value)
26
+ @options[:issuer] = value
27
+ end
28
+
29
+ def audience
30
+ @options[:audience] || ENV.fetch("AUDIENCE")
31
+ end
32
+
33
+ def audience=(value)
34
+ @options[:audience] = value
35
+ end
36
+
37
+ def redis_address
38
+ @options[:redis_address] || ENV.fetch("REDIS_URL") {"redis://localhost:6379/1"}
39
+ end
40
+
41
+ def redis_address=(value)
42
+ @options[:redis_address] = value
43
+ end
44
+ end
45
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unrestful
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Khash Sajadi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-16 00:00:00.000000000 Z
11
+ date: 2019-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -134,8 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
134
  - !ruby/object:Gem::Version
135
135
  version: '0'
136
136
  requirements: []
137
- rubyforge_project:
138
- rubygems_version: 2.7.9
137
+ rubygems_version: 3.0.4
139
138
  signing_key:
140
139
  specification_version: 4
141
140
  summary: Unrestful is a simple RPC framework for Rails