castle-rb 8.1.0 → 9.0.0

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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +317 -0
  3. data/LICENSE +21 -0
  4. data/README.md +290 -112
  5. data/lib/castle/api/filter.rb +3 -1
  6. data/lib/castle/api/list_items/create_batch.rb +22 -0
  7. data/lib/castle/api/lists/create.rb +1 -1
  8. data/lib/castle/api/log.rb +2 -1
  9. data/lib/castle/api/privacy/delete_data.rb +23 -0
  10. data/lib/castle/api/privacy/request_data.rb +23 -0
  11. data/lib/castle/api/risk.rb +2 -1
  12. data/lib/castle/client.rb +12 -52
  13. data/lib/castle/client_actions/list_items.rb +7 -2
  14. data/lib/castle/client_actions/privacy.rb +20 -0
  15. data/lib/castle/commands/list_items/create_batch.rb +22 -0
  16. data/lib/castle/commands/privacy/delete_data.rb +20 -0
  17. data/lib/castle/commands/privacy/request_data.rb +20 -0
  18. data/lib/castle/core/get_connection.rb +5 -1
  19. data/lib/castle/core/process_response.rb +1 -0
  20. data/lib/castle/core/process_webhook.rb +1 -1
  21. data/lib/castle/core/send_request.rb +2 -1
  22. data/lib/castle/errors.rb +1 -5
  23. data/lib/castle/headers/filter.rb +1 -1
  24. data/lib/castle/version.rb +1 -1
  25. data/lib/castle.rb +8 -17
  26. metadata +34 -218
  27. data/lib/castle/api/approve_device.rb +0 -20
  28. data/lib/castle/api/authenticate.rb +0 -28
  29. data/lib/castle/api/end_impersonation.rb +0 -22
  30. data/lib/castle/api/get_device.rb +0 -20
  31. data/lib/castle/api/get_devices_for_user.rb +0 -20
  32. data/lib/castle/api/report_device.rb +0 -20
  33. data/lib/castle/api/start_impersonation.rb +0 -22
  34. data/lib/castle/api/track.rb +0 -19
  35. data/lib/castle/commands/approve_device.rb +0 -17
  36. data/lib/castle/commands/authenticate.rb +0 -23
  37. data/lib/castle/commands/end_impersonation.rb +0 -25
  38. data/lib/castle/commands/get_device.rb +0 -17
  39. data/lib/castle/commands/get_devices_for_user.rb +0 -17
  40. data/lib/castle/commands/report_device.rb +0 -17
  41. data/lib/castle/commands/start_impersonation.rb +0 -25
  42. data/lib/castle/commands/track.rb +0 -22
  43. data/lib/castle/support/hanami.rb +0 -15
  44. data/lib/castle/support/padrino.rb +0 -19
  45. data/spec/integration/rails/rails_spec.rb +0 -95
  46. data/spec/integration/rails/support/all.rb +0 -6
  47. data/spec/integration/rails/support/application.rb +0 -17
  48. data/spec/integration/rails/support/home_controller.rb +0 -39
  49. data/spec/lib/castle/api/approve_device_spec.rb +0 -17
  50. data/spec/lib/castle/api/authenticate_spec.rb +0 -133
  51. data/spec/lib/castle/api/end_impersonation_spec.rb +0 -59
  52. data/spec/lib/castle/api/filter_spec.rb +0 -5
  53. data/spec/lib/castle/api/get_device_spec.rb +0 -17
  54. data/spec/lib/castle/api/get_devices_for_user_spec.rb +0 -17
  55. data/spec/lib/castle/api/list_items/archive_spec.rb +0 -18
  56. data/spec/lib/castle/api/list_items/count_spec.rb +0 -21
  57. data/spec/lib/castle/api/list_items/create_spec.rb +0 -22
  58. data/spec/lib/castle/api/list_items/get_spec.rb +0 -18
  59. data/spec/lib/castle/api/list_items/query_spec.rb +0 -21
  60. data/spec/lib/castle/api/list_items/unarchive_spec.rb +0 -18
  61. data/spec/lib/castle/api/list_items/update_spec.rb +0 -22
  62. data/spec/lib/castle/api/lists/create_spec.rb +0 -21
  63. data/spec/lib/castle/api/lists/delete_spec.rb +0 -17
  64. data/spec/lib/castle/api/lists/get_all_spec.rb +0 -17
  65. data/spec/lib/castle/api/lists/get_spec.rb +0 -17
  66. data/spec/lib/castle/api/lists/query_spec.rb +0 -21
  67. data/spec/lib/castle/api/lists/update_spec.rb +0 -21
  68. data/spec/lib/castle/api/log_spec.rb +0 -5
  69. data/spec/lib/castle/api/report_device_spec.rb +0 -17
  70. data/spec/lib/castle/api/risk_spec.rb +0 -5
  71. data/spec/lib/castle/api/start_impersonation_spec.rb +0 -59
  72. data/spec/lib/castle/api/track_spec.rb +0 -65
  73. data/spec/lib/castle/api_spec.rb +0 -36
  74. data/spec/lib/castle/client_id/extract_spec.rb +0 -47
  75. data/spec/lib/castle/client_spec.rb +0 -342
  76. data/spec/lib/castle/command_spec.rb +0 -9
  77. data/spec/lib/castle/commands/approve_device_spec.rb +0 -24
  78. data/spec/lib/castle/commands/authenticate_spec.rb +0 -86
  79. data/spec/lib/castle/commands/end_impersonation_spec.rb +0 -72
  80. data/spec/lib/castle/commands/filter_spec.rb +0 -72
  81. data/spec/lib/castle/commands/get_device_spec.rb +0 -24
  82. data/spec/lib/castle/commands/get_devices_for_user_spec.rb +0 -24
  83. data/spec/lib/castle/commands/list_items/archive_spec.rb +0 -21
  84. data/spec/lib/castle/commands/list_items/count_spec.rb +0 -21
  85. data/spec/lib/castle/commands/list_items/create_spec.rb +0 -22
  86. data/spec/lib/castle/commands/list_items/get_spec.rb +0 -21
  87. data/spec/lib/castle/commands/list_items/query_spec.rb +0 -27
  88. data/spec/lib/castle/commands/list_items/unarchive_spec.rb +0 -21
  89. data/spec/lib/castle/commands/list_items/update_spec.rb +0 -21
  90. data/spec/lib/castle/commands/lists/create_spec.rb +0 -33
  91. data/spec/lib/castle/commands/lists/delete_spec.rb +0 -21
  92. data/spec/lib/castle/commands/lists/get_all_spec.rb +0 -11
  93. data/spec/lib/castle/commands/lists/get_spec.rb +0 -21
  94. data/spec/lib/castle/commands/lists/query_spec.rb +0 -27
  95. data/spec/lib/castle/commands/lists/update_spec.rb +0 -29
  96. data/spec/lib/castle/commands/log_spec.rb +0 -73
  97. data/spec/lib/castle/commands/report_device_spec.rb +0 -24
  98. data/spec/lib/castle/commands/risk_spec.rb +0 -73
  99. data/spec/lib/castle/commands/start_impersonation_spec.rb +0 -72
  100. data/spec/lib/castle/commands/track_spec.rb +0 -89
  101. data/spec/lib/castle/configuration_spec.rb +0 -14
  102. data/spec/lib/castle/context/get_default_spec.rb +0 -41
  103. data/spec/lib/castle/context/merge_spec.rb +0 -23
  104. data/spec/lib/castle/context/prepare_spec.rb +0 -42
  105. data/spec/lib/castle/context/sanitize_spec.rb +0 -27
  106. data/spec/lib/castle/core/get_connection_spec.rb +0 -43
  107. data/spec/lib/castle/core/process_response_spec.rb +0 -103
  108. data/spec/lib/castle/core/process_webhook_spec.rb +0 -52
  109. data/spec/lib/castle/core/send_request_spec.rb +0 -97
  110. data/spec/lib/castle/failover/strategy_spec.rb +0 -12
  111. data/spec/lib/castle/headers/extract_spec.rb +0 -103
  112. data/spec/lib/castle/headers/filter_spec.rb +0 -42
  113. data/spec/lib/castle/headers/format_spec.rb +0 -25
  114. data/spec/lib/castle/ips/extract_spec.rb +0 -91
  115. data/spec/lib/castle/logger_spec.rb +0 -39
  116. data/spec/lib/castle/payload/prepare_spec.rb +0 -52
  117. data/spec/lib/castle/secure_mode_spec.rb +0 -7
  118. data/spec/lib/castle/session_spec.rb +0 -61
  119. data/spec/lib/castle/singleton_configuration_spec.rb +0 -14
  120. data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +0 -69
  121. data/spec/lib/castle/utils/clone_spec.rb +0 -19
  122. data/spec/lib/castle/utils/deep_symbolize_keys_spec.rb +0 -50
  123. data/spec/lib/castle/utils/get_timestamp_spec.rb +0 -16
  124. data/spec/lib/castle/utils/merge_spec.rb +0 -13
  125. data/spec/lib/castle/validators/not_supported_spec.rb +0 -19
  126. data/spec/lib/castle/validators/present_spec.rb +0 -25
  127. data/spec/lib/castle/verdict_spec.rb +0 -9
  128. data/spec/lib/castle/version_spec.rb +0 -5
  129. data/spec/lib/castle/webhooks/verify_spec.rb +0 -59
  130. data/spec/lib/castle_spec.rb +0 -58
  131. data/spec/spec_helper.rb +0 -26
  132. data/spec/support/shared_examples/action_request.rb +0 -167
  133. data/spec/support/shared_examples/configuration.rb +0 -99
  134. data/spec/support/shared_examples/list_items.rb +0 -52
  135. data/spec/support/shared_examples/lists.rb +0 -45
data/lib/castle/client.rb CHANGED
@@ -5,6 +5,7 @@ module Castle
5
5
  class Client
6
6
  include Castle::ClientActions::ListItems
7
7
  include Castle::ClientActions::Lists
8
+ include Castle::ClientActions::Privacy
8
9
 
9
10
  class << self
10
11
  def from_request(request, options = {})
@@ -22,37 +23,11 @@ module Castle
22
23
  @context = options.fetch(:context) { {} }
23
24
  end
24
25
 
25
- # @param options [Hash]
26
- def authenticate(options = {})
27
- options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
28
-
29
- return generate_do_not_track_response(options[:user_id]) unless tracked?
30
-
31
- add_timestamp_if_necessary(options)
32
-
33
- new_context = Castle::Context::Merge.call(@context, options[:context])
34
-
35
- Castle::API::Authenticate.call(options.merge(context: new_context, no_symbolize: true))
36
- end
37
-
38
- # @param options [Hash]
39
- def track(options = {})
40
- options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
41
-
42
- return unless tracked?
43
-
44
- add_timestamp_if_necessary(options)
45
-
46
- new_context = Castle::Context::Merge.call(@context, options[:context])
47
-
48
- Castle::API::Track.call(options.merge(context: new_context, no_symbolize: true))
49
- end
50
-
51
26
  # @param options [Hash]
52
27
  def filter(options = {})
53
28
  options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
54
29
 
55
- return generate_do_not_track_response(options[:user][:id]) unless tracked?
30
+ return generate_do_not_track_response(failover_user_id(options)) unless tracked?
56
31
 
57
32
  add_timestamp_if_necessary(options)
58
33
 
@@ -65,7 +40,7 @@ module Castle
65
40
  def risk(options = {})
66
41
  options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
67
42
 
68
- return generate_do_not_track_response(options[:user][:id]) unless tracked?
43
+ return generate_do_not_track_response(failover_user_id(options)) unless tracked?
69
44
 
70
45
  add_timestamp_if_necessary(options)
71
46
 
@@ -78,7 +53,7 @@ module Castle
78
53
  def log(options = {})
79
54
  options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
80
55
 
81
- return generate_do_not_track_response(options[:user][:id]) unless tracked?
56
+ return generate_do_not_track_response(failover_user_id(options)) unless tracked?
82
57
 
83
58
  add_timestamp_if_necessary(options)
84
59
 
@@ -87,28 +62,6 @@ module Castle
87
62
  Castle::API::Log.call(options.merge(context: new_context, no_symbolize: true))
88
63
  end
89
64
 
90
- # @param options [Hash]
91
- def start_impersonation(options = {})
92
- options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
93
-
94
- add_timestamp_if_necessary(options)
95
-
96
- new_context = Castle::Context::Merge.call(@context, options[:context])
97
-
98
- Castle::API::StartImpersonation.call(options.merge(context: new_context, no_symbolize: true))
99
- end
100
-
101
- # @param options [Hash]
102
- def end_impersonation(options = {})
103
- options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
104
-
105
- add_timestamp_if_necessary(options)
106
-
107
- new_context = Castle::Context::Merge.call(@context, options[:context])
108
-
109
- Castle::API::EndImpersonation.call(options.merge(context: new_context, no_symbolize: true))
110
- end
111
-
112
65
  def disable_tracking
113
66
  @do_not_track = true
114
67
  end
@@ -124,11 +77,18 @@ module Castle
124
77
 
125
78
  private
126
79
 
127
- # @param user_id [String, Boolean]
80
+ # @param user_id [String, Boolean, nil]
128
81
  def generate_do_not_track_response(user_id)
129
82
  Castle::Failover::PrepareResponse.new(user_id, strategy: :allow, reason: 'Castle is set to do not track.').call
130
83
  end
131
84
 
85
+ # Safely pull the user identifier for a failover/do-not-track response.
86
+ # `user` is optional on /v1/filter (#279) and may be omitted entirely on
87
+ # /v1/log; fall back to `matching_user_id` then nil.
88
+ def failover_user_id(options)
89
+ options.dig(:user, :id) || options[:matching_user_id]
90
+ end
91
+
132
92
  # @param options [Hash]
133
93
  def add_timestamp_if_necessary(options)
134
94
  options[:timestamp] ||= @timestamp if @timestamp
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Castle
4
- # Namesapce for client actions
4
+ # Namespace for client actions
5
5
  module ClientActions
6
6
  # Client actions for list items
7
7
  module ListItems
@@ -20,6 +20,11 @@ module Castle
20
20
  Castle::API::ListItems::Create.call(options)
21
21
  end
22
22
 
23
+ # @param options [Hash]
24
+ def create_batch_list_items(options = {})
25
+ Castle::API::ListItems::CreateBatch.call(options)
26
+ end
27
+
23
28
  # @param options [Hash]
24
29
  def get_list_item(options = {})
25
30
  Castle::API::ListItems::Get.call(options)
@@ -31,7 +36,7 @@ module Castle
31
36
  end
32
37
 
33
38
  # @param options [Hash]
34
- def unarchive_list_item(options)
39
+ def unarchive_list_item(options = {})
35
40
  Castle::API::ListItems::Unarchive.call(options)
36
41
  end
37
42
 
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castle
4
+ module ClientActions
5
+ # Client actions for the Privacy API (GDPR Articles 15 & 17).
6
+ module Privacy
7
+ # Triggers a "right of access" data export.
8
+ # @param options [Hash] must include :identifier and :identifier_type ($id or $email)
9
+ def request_user_data(options = {})
10
+ Castle::API::Privacy::RequestData.call(options)
11
+ end
12
+
13
+ # Triggers a "right to be forgotten" data purge.
14
+ # @param options [Hash] must include :identifier and :identifier_type ($id or $email)
15
+ def delete_user_data(options = {})
16
+ Castle::API::Privacy::DeleteData.call(options)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castle
4
+ module Commands
5
+ module ListItems
6
+ # Builds the command to create or update multiple list items in a single call
7
+ class CreateBatch
8
+ class << self
9
+ # @param options [Hash]
10
+ # @return [Castle::Command]
11
+ def build(options = {})
12
+ Castle::Validators::Present.call(options, %i[list_id items])
13
+
14
+ list_id = options.delete(:list_id)
15
+
16
+ Castle::Command.new("lists/#{list_id}/items/batch", options, :post)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castle
4
+ module Commands
5
+ module Privacy
6
+ # Builds the command for DELETE /v1/privacy/users — GDPR Article 17 (right to be forgotten).
7
+ class DeleteData
8
+ class << self
9
+ # @param options [Hash] must include :identifier and :identifier_type ($id or $email)
10
+ # @return [Castle::Command]
11
+ def build(options = {})
12
+ Castle::Validators::Present.call(options, %i[identifier identifier_type])
13
+
14
+ Castle::Command.new('privacy/users', options, :delete)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castle
4
+ module Commands
5
+ module Privacy
6
+ # Builds the command for POST /v1/privacy/users — GDPR Article 15 (right of access).
7
+ class RequestData
8
+ class << self
9
+ # @param options [Hash] must include :identifier and :identifier_type ($id or $email)
10
+ # @return [Castle::Command]
11
+ def build(options = {})
12
+ Castle::Validators::Present.call(options, %i[identifier identifier_type])
13
+
14
+ Castle::Command.new('privacy/users', options, :post)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -12,7 +12,11 @@ module Castle
12
12
  def call(config = nil)
13
13
  config ||= Castle.config
14
14
  http = Net::HTTP.new(config.base_url.host, config.base_url.port)
15
- http.read_timeout = config.request_timeout / 1000.0
15
+ # `request_timeout` is in milliseconds for historical reasons; both
16
+ # Net::HTTP timeouts take seconds.
17
+ timeout_seconds = config.request_timeout / 1000.0
18
+ http.open_timeout = timeout_seconds
19
+ http.read_timeout = timeout_seconds
16
20
 
17
21
  if config.base_url.scheme == HTTPS_SCHEME
18
22
  http.use_ssl = true
@@ -57,6 +57,7 @@ module Castle
57
57
  raise Castle::InvalidParametersError, parsed_body[:message]
58
58
  end
59
59
  rescue JSON::ParserError
60
+ # body wasn't valid JSON; fall through to the generic 422 error below
60
61
  end
61
62
  end
62
63
 
@@ -11,7 +11,7 @@ module Castle
11
11
  # @return [String]
12
12
  def call(webhook, config = nil)
13
13
  webhook.body.read.tap do |result|
14
- raise Castle::ApiError, 'Invalid webhook from Castle API' if result.blank?
14
+ raise Castle::ApiError, 'Invalid webhook from Castle API' if result.nil? || result.empty?
15
15
 
16
16
  Castle::Logger.call('webhook:', result.to_s, config)
17
17
  end
@@ -15,7 +15,8 @@ module Castle
15
15
  # @param http [Net::HTTP]
16
16
  # @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
17
17
  def call(command, headers, http = nil, config = nil)
18
- (http || Castle::Core::GetConnection.call).request(build(command, headers.merge(DEFAULT_HEADERS), config))
18
+ connection = http || Castle::Core::GetConnection.call(config)
19
+ connection.request(build(command, headers.merge(DEFAULT_HEADERS), config))
19
20
  end
20
21
 
21
22
  # @param command [String]
data/lib/castle/errors.rb CHANGED
@@ -12,7 +12,7 @@ module Castle
12
12
  attr_reader :reason
13
13
 
14
14
  # @param reason [Exception] the core exception that causes this error
15
- def initialize(reason)
15
+ def initialize(reason) # rubocop:disable Lint/MissingSuper -- preserves legacy `to_s` (returns class name)
16
16
  @reason = reason
17
17
  end
18
18
  end
@@ -68,8 +68,4 @@ module Castle
68
68
  # all internal server errors
69
69
  class InternalServerError < Castle::ApiError
70
70
  end
71
-
72
- # impersonation command failed
73
- class ImpersonationFailed < Castle::ApiError
74
- end
75
71
  end
@@ -12,7 +12,7 @@ module Castle
12
12
  HTTP(?:_|-).*|
13
13
  CONTENT(?:_|-)LENGTH|
14
14
  REMOTE(?:_|-)ADDR
15
- $/xi.freeze
15
+ $/xi
16
16
 
17
17
  private_constant :VALUABLE_HEADERS
18
18
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Castle
4
- VERSION = '8.1.0'
4
+ VERSION = '9.0.0'
5
5
  end
data/lib/castle.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- %w[openssl net/http json time].each(&method(:require))
3
+ %w[openssl net/http json time base64].each(&method(:require))
4
4
 
5
5
  %w[
6
6
  castle/version
@@ -20,17 +20,9 @@
20
20
  castle/context/sanitize
21
21
  castle/context/get_default
22
22
  castle/context/prepare
23
- castle/commands/approve_device
24
- castle/commands/authenticate
25
- castle/commands/end_impersonation
26
23
  castle/commands/filter
27
- castle/commands/get_device
28
- castle/commands/get_devices_for_user
29
24
  castle/commands/log
30
- castle/commands/report_device
31
25
  castle/commands/risk
32
- castle/commands/start_impersonation
33
- castle/commands/track
34
26
  castle/commands/lists/get_all
35
27
  castle/commands/lists/create
36
28
  castle/commands/lists/delete
@@ -39,22 +31,17 @@
39
31
  castle/commands/lists/update
40
32
  castle/commands/list_items/archive
41
33
  castle/commands/list_items/create
34
+ castle/commands/list_items/create_batch
42
35
  castle/commands/list_items/count
43
36
  castle/commands/list_items/get
44
37
  castle/commands/list_items/query
45
38
  castle/commands/list_items/unarchive
46
39
  castle/commands/list_items/update
47
- castle/api/approve_device
48
- castle/api/authenticate
49
- castle/api/end_impersonation
40
+ castle/commands/privacy/request_data
41
+ castle/commands/privacy/delete_data
50
42
  castle/api/filter
51
- castle/api/get_device
52
- castle/api/get_devices_for_user
53
43
  castle/api/log
54
- castle/api/report_device
55
44
  castle/api/risk
56
- castle/api/start_impersonation
57
- castle/api/track
58
45
  castle/api/lists/get_all
59
46
  castle/api/lists/create
60
47
  castle/api/lists/delete
@@ -63,11 +50,14 @@
63
50
  castle/api/lists/update
64
51
  castle/api/list_items/archive
65
52
  castle/api/list_items/create
53
+ castle/api/list_items/create_batch
66
54
  castle/api/list_items/count
67
55
  castle/api/list_items/get
68
56
  castle/api/list_items/query
69
57
  castle/api/list_items/unarchive
70
58
  castle/api/list_items/update
59
+ castle/api/privacy/request_data
60
+ castle/api/privacy/delete_data
71
61
  castle/payload/prepare
72
62
  castle/configuration
73
63
  castle/singleton_configuration
@@ -76,6 +66,7 @@
76
66
  castle/failover/strategy
77
67
  castle/client_actions/lists
78
68
  castle/client_actions/list_items
69
+ castle/client_actions/privacy
79
70
  castle/client
80
71
  castle/headers/filter
81
72
  castle/headers/format