esp_sdk 1.1.0 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -2
- data/.rubocop.yml +53 -6
- data/.ruby-version +1 -1
- data/.travis.yml +8 -2
- data/Gemfile.lock +171 -0
- data/Guardfile +47 -0
- data/README.md +230 -5
- data/Rakefile +14 -1
- data/assets/logo.png +0 -0
- data/bin/esp_console +71 -0
- data/esp_sdk.gemspec +27 -18
- data/lib/esp/exceptions.rb +3 -0
- data/lib/esp/extensions/active_resource/formats/json_api_format.rb +105 -0
- data/lib/esp/extensions/active_resource/paginated_collection.rb +198 -0
- data/lib/esp/extensions/active_resource/validations.rb +45 -0
- data/lib/esp/resources/alert.rb +135 -0
- data/lib/esp/resources/cloud_trail_event.rb +45 -0
- data/lib/esp/resources/concerns/stat_totals.rb +79 -0
- data/lib/esp/resources/contact_request.rb +42 -0
- data/lib/esp/resources/custom_signature.rb +224 -0
- data/lib/esp/resources/dashboard.rb +31 -0
- data/lib/esp/resources/external_account.rb +89 -0
- data/lib/esp/resources/organization.rb +61 -0
- data/lib/esp/resources/region.rb +46 -0
- data/lib/esp/resources/report.rb +100 -0
- data/lib/esp/resources/resource.rb +66 -0
- data/lib/esp/resources/service.rb +31 -0
- data/lib/esp/resources/signature.rb +106 -0
- data/lib/esp/resources/stat.rb +124 -0
- data/lib/esp/resources/stat_custom_signature.rb +121 -0
- data/lib/esp/resources/stat_region.rb +121 -0
- data/lib/esp/resources/stat_service.rb +121 -0
- data/lib/esp/resources/stat_signature.rb +121 -0
- data/lib/esp/resources/sub_organization.rb +69 -0
- data/lib/esp/resources/suppression/region.rb +99 -0
- data/lib/esp/resources/suppression/signature.rb +107 -0
- data/lib/esp/resources/suppression/unique_identifier.rb +60 -0
- data/lib/esp/resources/suppression.rb +86 -0
- data/lib/esp/resources/tag.rb +45 -0
- data/lib/esp/resources/team.rb +79 -0
- data/lib/esp/resources/user.rb +46 -0
- data/lib/esp/version.rb +3 -0
- data/lib/esp.rb +95 -0
- data/lib/esp_sdk.rb +9 -45
- data/lib/tasks/rubocop.rake +2 -0
- data/lib/tasks/testing.rake +3 -0
- data/rdoc/ActiveResource/Formats.html +176 -0
- data/rdoc/ActiveResource/PaginatedCollection.html +910 -0
- data/rdoc/ActiveResource.html +180 -0
- data/rdoc/ESP/Alert.html +771 -0
- data/rdoc/ESP/CloudTrailEvent.html +375 -0
- data/rdoc/ESP/ContactRequest.html +366 -0
- data/rdoc/ESP/CustomSignature.html +746 -0
- data/rdoc/ESP/Dashboard.html +355 -0
- data/rdoc/ESP/ExternalAccount.html +565 -0
- data/rdoc/ESP/Organization.html +590 -0
- data/rdoc/ESP/Region.html +399 -0
- data/rdoc/ESP/Report.html +622 -0
- data/rdoc/ESP/Service.html +380 -0
- data/rdoc/ESP/Signature.html +555 -0
- data/rdoc/ESP/Stat.html +1778 -0
- data/rdoc/ESP/StatCustomSignature.html +1599 -0
- data/rdoc/ESP/StatRegion.html +1598 -0
- data/rdoc/ESP/StatService.html +1598 -0
- data/rdoc/ESP/StatSignature.html +1598 -0
- data/rdoc/ESP/SubOrganization.html +540 -0
- data/rdoc/ESP/Suppression/Region.html +454 -0
- data/rdoc/ESP/Suppression/Signature.html +470 -0
- data/rdoc/ESP/Suppression/UniqueIdentifier.html +417 -0
- data/rdoc/ESP/Suppression.html +649 -0
- data/rdoc/ESP/Tag.html +371 -0
- data/rdoc/ESP/Team.html +584 -0
- data/rdoc/ESP/User.html +483 -0
- data/rdoc/ESP.html +546 -0
- data/rdoc/README_md.html +501 -0
- data/rdoc/created.rid +30 -0
- data/rdoc/images/add.png +0 -0
- data/rdoc/images/arrow_up.png +0 -0
- data/rdoc/images/brick.png +0 -0
- data/rdoc/images/brick_link.png +0 -0
- data/rdoc/images/bug.png +0 -0
- data/rdoc/images/bullet_black.png +0 -0
- data/rdoc/images/bullet_toggle_minus.png +0 -0
- data/rdoc/images/bullet_toggle_plus.png +0 -0
- data/rdoc/images/date.png +0 -0
- data/rdoc/images/delete.png +0 -0
- data/rdoc/images/find.png +0 -0
- data/rdoc/images/loadingAnimation.gif +0 -0
- data/rdoc/images/macFFBgHack.png +0 -0
- data/rdoc/images/package.png +0 -0
- data/rdoc/images/page_green.png +0 -0
- data/rdoc/images/page_white_text.png +0 -0
- data/rdoc/images/page_white_width.png +0 -0
- data/rdoc/images/plugin.png +0 -0
- data/rdoc/images/ruby.png +0 -0
- data/rdoc/images/tag_blue.png +0 -0
- data/rdoc/images/tag_green.png +0 -0
- data/rdoc/images/transparent.png +0 -0
- data/rdoc/images/wrench.png +0 -0
- data/rdoc/images/wrench_orange.png +0 -0
- data/rdoc/images/zoom.png +0 -0
- data/rdoc/index.html +134 -0
- data/rdoc/js/darkfish.js +155 -0
- data/rdoc/js/jquery.js +4 -0
- data/rdoc/js/navigation.js +142 -0
- data/rdoc/js/search.js +94 -0
- data/rdoc/js/search_index.js +1 -0
- data/rdoc/js/searcher.js +228 -0
- data/rdoc/rdoc.css +595 -0
- data/rdoc/table_of_contents.html +927 -0
- data/test/esp/extensions/active_resource/formats/json_api_format_test.rb +109 -0
- data/test/esp/extensions/active_resource/paginated_collection_test.rb +435 -0
- data/test/esp/extensions/active_resource/validations_test.rb +59 -0
- data/test/esp/resources/alert_test.rb +263 -0
- data/test/esp/resources/cloud_trail_event_test.rb +98 -0
- data/test/esp/resources/contact_request_test.rb +73 -0
- data/test/esp/resources/custom_signature_test.rb +241 -0
- data/test/esp/resources/dashboard_test.rb +71 -0
- data/test/esp/resources/external_account_test.rb +125 -0
- data/test/esp/resources/organization_test.rb +175 -0
- data/test/esp/resources/region_test.rb +84 -0
- data/test/esp/resources/report_test.rb +180 -0
- data/test/esp/resources/resource_test.rb +183 -0
- data/test/esp/resources/service_test.rb +64 -0
- data/test/esp/resources/signature_test.rb +177 -0
- data/test/esp/resources/stat_custom_signature_test.rb +115 -0
- data/test/esp/resources/stat_region_test.rb +114 -0
- data/test/esp/resources/stat_service_test.rb +114 -0
- data/test/esp/resources/stat_signature_test.rb +115 -0
- data/test/esp/resources/stat_test.rb +159 -0
- data/test/esp/resources/sub_organization_test.rb +127 -0
- data/test/esp/resources/suppression/region_test.rb +115 -0
- data/test/esp/resources/suppression/signature_test.rb +117 -0
- data/test/esp/resources/suppression/unique_identifier_test.rb +79 -0
- data/test/esp/resources/suppression_test.rb +226 -0
- data/test/esp/resources/tag_test.rb +98 -0
- data/test/esp/resources/team_test.rb +140 -0
- data/test/esp/resources/user_test.rb +113 -0
- data/test/esp_test.rb +139 -0
- data/test/factories/alerts.rb +234 -0
- data/test/factories/cloud_trail_events.rb +16 -0
- data/test/factories/contact_requests.rb +14 -0
- data/test/factories/custom_signatures.rb +30 -0
- data/test/factories/dashboards.rb +91 -0
- data/test/factories/errors.rb +24 -0
- data/test/factories/external_accounts.rb +44 -0
- data/test/factories/organizations.rb +48 -0
- data/test/factories/regions.rb +9 -0
- data/test/factories/reports.rb +56 -0
- data/test/factories/services.rb +12 -0
- data/test/factories/signatures.rb +28 -0
- data/test/factories/stat_custom_signatures.rb +97 -0
- data/test/factories/stat_regions.rb +97 -0
- data/test/factories/stat_services.rb +97 -0
- data/test/factories/stat_signautures.rb +97 -0
- data/test/factories/stats.rb +129 -0
- data/test/factories/sub_organizations.rb +34 -0
- data/test/factories/suppression/regions.rb +90 -0
- data/test/factories/suppression/signatures.rb +117 -0
- data/test/factories/suppression/unique_identifiers.rb +111 -0
- data/test/factories/suppressions.rb +71 -0
- data/test/factories/tags.rb +12 -0
- data/test/factories/teams.rb +32 -0
- data/test/factories/users.rb +54 -0
- data/test/json_strategy.rb +25 -0
- data/test/test_helper.rb +44 -5
- metadata +387 -119
- data/bin/esp_repl +0 -60
- data/lib/esp_sdk/api.rb +0 -33
- data/lib/esp_sdk/client.rb +0 -62
- data/lib/esp_sdk/configure.rb +0 -40
- data/lib/esp_sdk/end_points/base.rb +0 -102
- data/lib/esp_sdk/end_points/contact_requests.rb +0 -6
- data/lib/esp_sdk/end_points/custom_signatures.rb +0 -41
- data/lib/esp_sdk/end_points/dashboard.rb +0 -35
- data/lib/esp_sdk/end_points/external_accounts.rb +0 -9
- data/lib/esp_sdk/end_points/organizations.rb +0 -6
- data/lib/esp_sdk/end_points/reports.rb +0 -6
- data/lib/esp_sdk/end_points/services.rb +0 -6
- data/lib/esp_sdk/end_points/signatures.rb +0 -39
- data/lib/esp_sdk/end_points/sub_organizations.rb +0 -6
- data/lib/esp_sdk/end_points/teams.rb +0 -6
- data/lib/esp_sdk/end_points/users.rb +0 -6
- data/lib/esp_sdk/exceptions.rb +0 -8
- data/lib/esp_sdk/extensions/rest_client/request.rb +0 -9
- data/lib/esp_sdk/repl.rb +0 -61
- data/lib/esp_sdk/version.rb +0 -3
- data/test/esp_sdk/api_test.rb +0 -36
- data/test/esp_sdk/client_test.rb +0 -129
- data/test/esp_sdk/configure_test.rb +0 -65
- data/test/esp_sdk/end_points/.keep +0 -0
- data/test/esp_sdk/end_points/base_test.rb +0 -230
- data/test/esp_sdk/end_points/custom_signatures_test.rb +0 -90
- data/test/esp_sdk/end_points/dashboard_test.rb +0 -55
- data/test/esp_sdk/end_points/external_accounts_test.rb +0 -20
- data/test/esp_sdk/end_points/signatures_test.rb +0 -83
- data/test/esp_sdk/exceptions_test.rb +0 -41
- data/test/esp_sdk_test.rb +0 -70
data/Rakefile
CHANGED
@@ -1,9 +1,22 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rake/testtask'
|
3
|
+
load 'lib/tasks/rubocop.rake'
|
4
|
+
load 'lib/tasks/testing.rake'
|
5
|
+
require 'rdoc/task'
|
3
6
|
|
4
7
|
Rake::TestTask.new do |task|
|
5
8
|
task.libs << 'test'
|
6
9
|
task.test_files = FileList['test/*_test.rb', 'test/**/*_test.rb']
|
7
10
|
end
|
8
11
|
|
9
|
-
task default: [:test]
|
12
|
+
task default: [:test]
|
13
|
+
|
14
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
15
|
+
rdoc.rdoc_dir = 'rdoc'
|
16
|
+
rdoc.title = 'ESPSDK'
|
17
|
+
rdoc.options << '--line-numbers'
|
18
|
+
rdoc.rdoc_files.include('README.md')
|
19
|
+
rdoc.rdoc_files.include('lib/esp/resources/**/*.rb')
|
20
|
+
rdoc.rdoc_files.include('lib/esp/extensions/active_resource/paginated_collection.rb')
|
21
|
+
rdoc.rdoc_files.include('lib/esp.rb')
|
22
|
+
end
|
data/assets/logo.png
ADDED
Binary file
|
data/bin/esp_console
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# https://www.fedux.org/articles/2015/08/26/creating-an-irb-based-repl-console-for-your-project.html
|
3
|
+
|
4
|
+
ENV['ESP_ENV'] = ARGV[0]
|
5
|
+
require 'irb'
|
6
|
+
require_relative '../lib/esp_sdk'
|
7
|
+
|
8
|
+
module ESP
|
9
|
+
class Console
|
10
|
+
def start
|
11
|
+
ARGV.clear
|
12
|
+
IRB.setup nil
|
13
|
+
|
14
|
+
IRB.conf[:PROMPT] = {}
|
15
|
+
IRB.conf[:IRB_NAME] = 'espsdk'
|
16
|
+
IRB.conf[:PROMPT][:ESPSDK] = {
|
17
|
+
:PROMPT_I => '%N:%03n:%i> ',
|
18
|
+
:PROMPT_N => '%N:%03n:%i> ',
|
19
|
+
:PROMPT_S => '%N:%03n:%i%l ',
|
20
|
+
:PROMPT_C => '%N:%03n:%i* ',
|
21
|
+
:RETURN => "# => %s\n"
|
22
|
+
}
|
23
|
+
IRB.conf[:PROMPT_MODE] = :ESPSDK
|
24
|
+
|
25
|
+
IRB.conf[:RC] = false
|
26
|
+
|
27
|
+
require 'irb/completion'
|
28
|
+
require 'irb/ext/save-history'
|
29
|
+
IRB.conf[:READLINE] = true
|
30
|
+
IRB.conf[:SAVE_HISTORY] = 1000
|
31
|
+
IRB.conf[:HISTORY_FILE] = '~/.esp_sdk_history'
|
32
|
+
|
33
|
+
context = Class.new do
|
34
|
+
include ESP
|
35
|
+
end
|
36
|
+
|
37
|
+
irb = IRB::Irb.new(IRB::WorkSpace.new(context.new))
|
38
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
39
|
+
|
40
|
+
trap("SIGINT") do
|
41
|
+
irb.signal_handle
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
catch(:IRB_EXIT) do
|
46
|
+
irb.eval_input
|
47
|
+
end
|
48
|
+
ensure
|
49
|
+
IRB.irb_at_exit
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
require 'catpix'
|
57
|
+
logo = File.expand_path(File.dirname(__FILE__) + '/../assets/logo.png')
|
58
|
+
Catpix::print_image logo
|
59
|
+
rescue
|
60
|
+
require 'artii'
|
61
|
+
artii = Artii::Base.new(font: 'slant')
|
62
|
+
print artii.asciify('E.S.P')
|
63
|
+
end
|
64
|
+
print <<-banner
|
65
|
+
|
66
|
+
Evident Security Platform Console #{ESP::VERSION}
|
67
|
+
Copyright (c) 2013-#{Time.now.year} Evident Security, All Rights Reserved.
|
68
|
+
http://www.evident.io
|
69
|
+
banner
|
70
|
+
ESP::Console.new.start
|
71
|
+
|
data/esp_sdk.gemspec
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require '
|
4
|
+
require 'esp/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'esp_sdk'
|
8
|
-
spec.version =
|
8
|
+
spec.version = ESP::VERSION
|
9
9
|
spec.authors = ['Evident.io']
|
10
10
|
spec.email = ['support@evident.io']
|
11
|
-
spec.summary =
|
12
|
-
spec.homepage = ''
|
11
|
+
spec.summary = "SDK for interacting with the ESP API."
|
12
|
+
spec.homepage = 'https://github.com/EvidentSecurity/esp_sdk'
|
13
13
|
spec.license = 'MIT'
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -17,19 +17,28 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ['lib']
|
19
19
|
|
20
|
-
spec.
|
21
|
-
spec.add_development_dependency 'rake', '~> 10.3.2'
|
22
|
-
spec.add_development_dependency 'rubocop', '~> 0.27.1'
|
23
|
-
spec.add_development_dependency 'minitest', '~> 5.4.2'
|
24
|
-
spec.add_development_dependency 'minitest-reporters', '~> 1.0.7'
|
25
|
-
spec.add_development_dependency 'shoulda', '~> 3.5.0'
|
26
|
-
spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.1'
|
27
|
-
spec.add_development_dependency 'mocha', '~> 1.1.0'
|
28
|
-
spec.add_development_dependency 'fakeweb', '~> 1.3'
|
20
|
+
spec.required_ruby_version = '>= 2.0.0'
|
29
21
|
|
30
|
-
spec.
|
31
|
-
spec.
|
32
|
-
spec.
|
33
|
-
spec.
|
34
|
-
spec.
|
22
|
+
spec.add_development_dependency 'bundler'
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'rubocop'
|
25
|
+
spec.add_development_dependency 'guard'
|
26
|
+
spec.add_development_dependency 'guard-minitest'
|
27
|
+
spec.add_development_dependency 'guard-rubocop'
|
28
|
+
spec.add_development_dependency 'minitest'
|
29
|
+
spec.add_development_dependency 'minitest-reporters'
|
30
|
+
spec.add_development_dependency 'shoulda'
|
31
|
+
spec.add_development_dependency 'mocha'
|
32
|
+
spec.add_development_dependency 'bourne'
|
33
|
+
spec.add_development_dependency 'webmock'
|
34
|
+
spec.add_development_dependency 'coveralls'
|
35
|
+
spec.add_development_dependency 'factory_girl'
|
36
|
+
spec.add_development_dependency 'rdoc'
|
37
|
+
|
38
|
+
spec.add_dependency 'activeresource', '~> 4.0.0'
|
39
|
+
spec.add_dependency 'api-auth'
|
40
|
+
spec.add_dependency 'rack'
|
41
|
+
spec.add_dependency 'awesome_print'
|
42
|
+
spec.add_dependency 'artii'
|
43
|
+
spec.add_dependency 'catpix'
|
35
44
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'active_support/json'
|
2
|
+
|
3
|
+
module ActiveResource
|
4
|
+
class ConnectionError
|
5
|
+
def initialize(response)
|
6
|
+
@response = if response.respond_to?(:response)
|
7
|
+
message = decoded_errors(response.response.body)
|
8
|
+
Struct.new(:body, :code, :message).new(response.response.body, response.code, message)
|
9
|
+
else
|
10
|
+
response
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def decoded_errors(json)
|
17
|
+
Array((Hash(ActiveSupport::JSON.decode(json)))['errors'].map { |e| e['title'] }).join(" ")
|
18
|
+
rescue
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Formats
|
24
|
+
module JsonAPIFormat
|
25
|
+
module_function
|
26
|
+
|
27
|
+
def extension
|
28
|
+
"json".freeze
|
29
|
+
end
|
30
|
+
|
31
|
+
def mime_type
|
32
|
+
"application/vnd.api+json".freeze
|
33
|
+
end
|
34
|
+
|
35
|
+
def encode(hash, options = nil)
|
36
|
+
ActiveSupport::JSON.encode(hash, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def decode(json)
|
40
|
+
# ap ActiveSupport::JSON.decode(json), index: false, indent: -2
|
41
|
+
Formats.remove_root(parse_json_api(ActiveSupport::JSON.decode(json)))
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def self.parse_json_api(elements)
|
47
|
+
included = elements.delete('included')
|
48
|
+
elements.tap do |e|
|
49
|
+
Array.wrap(e.fetch('data', {})).each do |object|
|
50
|
+
parse_object!(object, included)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.parse_object!(object, included = nil)
|
56
|
+
return object unless object.respond_to?(:each)
|
57
|
+
merge_attributes!(object)
|
58
|
+
parse_elements(object)
|
59
|
+
parse_relationships!(object, included)
|
60
|
+
object
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.parse_elements(object)
|
64
|
+
object.each_value do |value|
|
65
|
+
if value.is_a? Hash
|
66
|
+
parse_object!(value)
|
67
|
+
elsif value.is_a? Array
|
68
|
+
value.map! { |o| parse_object!(o) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.parse_relationships!(object, included)
|
74
|
+
object.fetch('relationships', {}).each do |assoc, details|
|
75
|
+
extract_foreign_keys!(object, assoc, details['data'])
|
76
|
+
merge_included_objects!(object, assoc, details['data'], included)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.merge_attributes!(object)
|
81
|
+
return unless object.is_a? Hash
|
82
|
+
object.merge! object.delete('attributes') unless object['attributes'].blank?
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.extract_foreign_keys!(object, assoc, data)
|
86
|
+
return if data.blank?
|
87
|
+
if data.is_a? Array
|
88
|
+
object["#{assoc.singularize}_ids"] = data.map { |d| d['id'] }
|
89
|
+
else
|
90
|
+
object["#{assoc}_id"] = data['id']
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.merge_included_objects!(object, assoc, data, included)
|
95
|
+
return if included.blank?
|
96
|
+
object[assoc] = case data
|
97
|
+
when Array
|
98
|
+
included.select { |i| data.include?(parse_object!(i).slice('type', 'id')) }
|
99
|
+
when Hash
|
100
|
+
included.detect { |i| data == parse_object!(i).slice('type', 'id') }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module ActiveResource
|
4
|
+
# Provides a mean to call the Evident.io API to easily retrieve paginated data
|
5
|
+
class PaginatedCollection < ActiveResource::Collection
|
6
|
+
attr_reader :next_page_params, :previous_page_params, :last_page_params #:nodoc:
|
7
|
+
attr_accessor :from #:nodoc:
|
8
|
+
|
9
|
+
def initialize(elements = []) #:nodoc:
|
10
|
+
# If a collection is sent without the pagination links, then elements will just be an array.
|
11
|
+
if elements.is_a? Hash
|
12
|
+
super(elements['data'])
|
13
|
+
parse_pagination_links(elements['links'])
|
14
|
+
else
|
15
|
+
super(elements)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns a new PaginatedCollection with the first page of results.
|
20
|
+
#
|
21
|
+
# Returns self when on the first page and no API call is made.
|
22
|
+
#
|
23
|
+
# ==== Example
|
24
|
+
# alerts.current_page_number # => 5
|
25
|
+
# first_page = alerts.first_page
|
26
|
+
# alerts.current_page_number # => 5
|
27
|
+
# first_page.current_page_number # => 1
|
28
|
+
def first_page
|
29
|
+
previous_page? ? resource_class.all(from: from, params: { page: { number: 1 } }) : self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Updates the existing PaginatedCollection object with the first page of data when not on the first page.
|
33
|
+
#
|
34
|
+
# ==== Example
|
35
|
+
# alerts.current_page_number # => 5
|
36
|
+
# alerts.first_page!
|
37
|
+
# alerts.current_page_number # => 1
|
38
|
+
def first_page!
|
39
|
+
first_page.tap { |page| update_self(page) }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns a new PaginatedCollection with the previous page of results.
|
43
|
+
#
|
44
|
+
# Returns self when on the first page and no API call is made.
|
45
|
+
#
|
46
|
+
# ==== Example
|
47
|
+
# alerts.current_page_number # => 5
|
48
|
+
# previous_page = alerts.previous_page
|
49
|
+
# alerts.current_page_number # => 5
|
50
|
+
# previous_page.current_page_number # => 4
|
51
|
+
def previous_page
|
52
|
+
previous_page? ? resource_class.all(from: from, params: previous_page_params) : self
|
53
|
+
end
|
54
|
+
|
55
|
+
# Updates the existing PaginatedCollection object with the previous page of data when not on the first page.
|
56
|
+
#
|
57
|
+
# ==== Example
|
58
|
+
# alerts.current_page_number # => 5
|
59
|
+
# alerts.previous_page!
|
60
|
+
# alerts.current_page_number # => 4
|
61
|
+
def previous_page!
|
62
|
+
previous_page.tap { |page| update_self(page) }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns a new PaginatedCollection with the next page of results.
|
66
|
+
#
|
67
|
+
# Returns self when on the last page and no API call is made.
|
68
|
+
#
|
69
|
+
# ==== Example
|
70
|
+
# alerts.current_page_number # => 5
|
71
|
+
# next_page = alerts.next_page
|
72
|
+
# alerts.current_page_number # => 5
|
73
|
+
# next_page.current_page_number # => 6
|
74
|
+
def next_page
|
75
|
+
next_page? ? resource_class.all(from: from, params: next_page_params) : self
|
76
|
+
end
|
77
|
+
|
78
|
+
# Updates the existing PaginatedCollection object with the last page of data when not on the last page.
|
79
|
+
#
|
80
|
+
# ==== Example
|
81
|
+
# alerts.current_page_number # => 5
|
82
|
+
# alerts.next_page!
|
83
|
+
# alerts.current_page_number # => 6
|
84
|
+
def next_page!
|
85
|
+
next_page.tap { |page| update_self(page) }
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a new PaginatedCollection with the last page of results.
|
89
|
+
#
|
90
|
+
# Returns self when on the last page and no API call is made.
|
91
|
+
#
|
92
|
+
# ==== Example
|
93
|
+
# alerts.current_page_number # => 5
|
94
|
+
# last_page = alerts.last_page
|
95
|
+
# alerts.current_page_number # => 5
|
96
|
+
# last_page.current_page_number # => 25
|
97
|
+
def last_page
|
98
|
+
!last_page? ? resource_class.all(from: from, params: last_page_params) : self
|
99
|
+
end
|
100
|
+
|
101
|
+
# Updates the existing PaginatedCollection object with the last page of data when not on the last page.
|
102
|
+
#
|
103
|
+
# ==== Example
|
104
|
+
# alerts.current_page_number # => 5
|
105
|
+
# alerts.last_page!
|
106
|
+
# alerts.current_page_number # => 25
|
107
|
+
def last_page!
|
108
|
+
last_page.tap { |page| update_self(page) }
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns a new PaginatedCollection with the +page_number+ page of data.
|
112
|
+
#
|
113
|
+
# Returns self when +page_number+ == #current_page_number
|
114
|
+
#
|
115
|
+
# ==== Attribute
|
116
|
+
#
|
117
|
+
# +page_number+ - The page number of the data wanted. +page_number+ must be between 1 and #last_page_number.
|
118
|
+
#
|
119
|
+
# ==== Example
|
120
|
+
# alerts.current_page_number # => 5
|
121
|
+
# page = alerts.page(2)
|
122
|
+
# alerts.current_page_number # => 5
|
123
|
+
# page.current_page_number # => 2
|
124
|
+
def page(page_number = nil)
|
125
|
+
fail ArgumentError, "You must supply a page number." unless page_number.present?
|
126
|
+
fail ArgumentError, "Page number cannot be less than 1." if page_number.to_i < 1
|
127
|
+
fail ArgumentError, "Page number cannot be greater than the last page number." if page_number.to_i > last_page_number.to_i
|
128
|
+
page_number.to_i != current_page_number.to_i ? resource_class.all(from: from, params: { page: { number: page_number, size: (next_page_params || previous_page_params)['page']['size'] } }) : self
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns a new PaginatedCollection with the +page_number+ page of data when not already on page +page_number+.
|
132
|
+
#
|
133
|
+
# ==== Attribute
|
134
|
+
#
|
135
|
+
# +page_number+ - The page number of the data wanted. +page_number+ must be between 1 and #last_page_number.
|
136
|
+
#
|
137
|
+
# ==== Example
|
138
|
+
# alerts.current_page_number # => 5
|
139
|
+
# alerts.page!(2)
|
140
|
+
# alerts.current_page_number # => 2
|
141
|
+
def page!(page_number)
|
142
|
+
page(page_number).tap { |page| update_self(page) }
|
143
|
+
end
|
144
|
+
|
145
|
+
# The current page number of data.
|
146
|
+
def current_page_number
|
147
|
+
(previous_page_number.to_i + 1).to_s
|
148
|
+
end
|
149
|
+
|
150
|
+
# The previous page number of data.
|
151
|
+
def previous_page_number
|
152
|
+
Hash(previous_page_params).fetch('page', {}).fetch('number', nil)
|
153
|
+
end
|
154
|
+
|
155
|
+
# The next page number of data.
|
156
|
+
def next_page_number
|
157
|
+
Hash(next_page_params).fetch('page', {}).fetch('number', nil)
|
158
|
+
end
|
159
|
+
|
160
|
+
# The last page number of data.
|
161
|
+
def last_page_number
|
162
|
+
Hash(last_page_params).fetch('page', {}).fetch('number', nil)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns whether or not there is a previous page of data in the collection.
|
166
|
+
def previous_page?
|
167
|
+
!previous_page_number.nil?
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns whether or not there is a next page of data in the collection.
|
171
|
+
def next_page?
|
172
|
+
!next_page_number.nil?
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns whether or not the collection is on the last page.
|
176
|
+
def last_page?
|
177
|
+
last_page_number.nil?
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def update_self(page)
|
183
|
+
@elements = page.elements
|
184
|
+
@next_page_params = page.next_page_params
|
185
|
+
@previous_page_params = page.previous_page_params
|
186
|
+
@last_page_params = page.last_page_params
|
187
|
+
end
|
188
|
+
|
189
|
+
def parse_pagination_links(links)
|
190
|
+
@next_page_params = links['next'] ? Rack::Utils.parse_nested_query(CGI.unescape(URI.parse(links['next']).query)) : {}
|
191
|
+
@previous_page_params = links['prev'] ? Rack::Utils.parse_nested_query(CGI.unescape(URI.parse(links['prev']).query)) : {}
|
192
|
+
@last_page_params = links['last'] ? Rack::Utils.parse_nested_query(CGI.unescape(URI.parse(links['last']).query)) : {}
|
193
|
+
# The last page may not contain the full per page number of records, and will therefore come back with an incorrect size since the
|
194
|
+
# size is based on the collection size. This will mess up further calls to previous_page or first page so remove the size so it will bring back the default size.
|
195
|
+
previous_page_params['page'].delete('size') if last_page? && previous_page_params['page']
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
module Validations
|
3
|
+
# Loads the set of remote errors into the object's Errors based on the
|
4
|
+
# content-type of the error-block received.
|
5
|
+
def load_remote_errors(remote_errors, save_cache = false) #:nodoc:
|
6
|
+
if self.class.format == ActiveResource::Formats::JsonAPIFormat
|
7
|
+
errors.from_json_api(remote_errors.response.body, save_cache)
|
8
|
+
elsif self.class.format == ActiveResource::Formats[:json]
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Errors
|
15
|
+
def from_json_api(json, save_cache = false)
|
16
|
+
raw_errors = decoded_errors(json)
|
17
|
+
errors = meta_errors(raw_errors)
|
18
|
+
if errors.present?
|
19
|
+
from_hash errors, save_cache
|
20
|
+
else
|
21
|
+
from_array raw_errors.map { |e| e['title'] }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def decoded_errors(json)
|
28
|
+
Array((Hash(ActiveSupport::JSON.decode(json)))['errors'])
|
29
|
+
rescue
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
33
|
+
def meta_errors(raw_errors)
|
34
|
+
{}.tap do |errors|
|
35
|
+
raw_errors.each do |error|
|
36
|
+
next unless error['meta']
|
37
|
+
error['meta'].map do |attr, message|
|
38
|
+
errors[attr] ||= []
|
39
|
+
errors[attr] << message
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module ESP
|
2
|
+
class Alert < ESP::Resource
|
3
|
+
##
|
4
|
+
# Returns the external account associated with this alert.
|
5
|
+
belongs_to :external_account, class_name: 'ESP::ExternalAccount'
|
6
|
+
|
7
|
+
##
|
8
|
+
# Returns the region associated with this alert.
|
9
|
+
belongs_to :region, class_name: 'ESP::Region'
|
10
|
+
|
11
|
+
##
|
12
|
+
# Returns the region associated with this alert. Either a signature or custom signature but not both will be present.
|
13
|
+
belongs_to :signature, class_name: 'ESP::Signature'
|
14
|
+
|
15
|
+
##
|
16
|
+
# Returns the custom signature associated with this alert. Either a signature or custom signature but not both will be present.
|
17
|
+
belongs_to :custom_signature, class_name: 'ESP::CustomSignature'
|
18
|
+
|
19
|
+
##
|
20
|
+
# Returns the suppression associated with this alert. If present the alert was suppressed.
|
21
|
+
belongs_to :suppression, class_name: 'ESP::Suppression'
|
22
|
+
|
23
|
+
##
|
24
|
+
# Returns the cloud trail events associated with this alert. These may be added up to 10 minutes after the alert was created
|
25
|
+
has_many :cloud_trail_events, class_name: 'ESP::CloudTrailEvent'
|
26
|
+
|
27
|
+
##
|
28
|
+
# Returns the tags associated with this alert.
|
29
|
+
has_many :tags, class_name: 'ESP::Tag'
|
30
|
+
|
31
|
+
# Not Implemented. You cannot create or update an Alert.
|
32
|
+
def save
|
33
|
+
fail ESP::NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
# Not Implemented. You cannot destroy a an Alert.
|
37
|
+
def destroy
|
38
|
+
fail ESP::NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a paginated collection of alerts for the given report_id
|
42
|
+
# Convenience method to use instead of ::find since a report_id is required to return alerts.
|
43
|
+
#
|
44
|
+
# ==== Parameters
|
45
|
+
#
|
46
|
+
# +report_id+ | Required | The ID of the report to retrieve alerts for
|
47
|
+
#
|
48
|
+
# +arguments+ | Not Required | An optional hash of search criteria to filter the returned collection
|
49
|
+
#
|
50
|
+
# ===== Valid Arguments
|
51
|
+
#
|
52
|
+
# +region_id+ | Not Required | Return only alerts for this region.
|
53
|
+
#
|
54
|
+
# +status+ | Not Required | Return only alerts for the give status(es). Valid values are fail, warn, error, pass, info
|
55
|
+
#
|
56
|
+
# +first_seen+ | Not Required | Return only alerts that have started within a number of hours of the report. For example, first_seen of 3 will return alerts that started showing up within the last 3 hours of the report.
|
57
|
+
#
|
58
|
+
# +suppressed+ | Not Required | Return only suppressed alerts
|
59
|
+
#
|
60
|
+
# +team_id+ | Not Required | Return only alerts for the given team.
|
61
|
+
#
|
62
|
+
# +external_account_id+ | Not Required | Return only alerts for the given external id.
|
63
|
+
#
|
64
|
+
# +service_id+ | Not Required | Return only alerts on signatures with the given service.
|
65
|
+
#
|
66
|
+
# +signature_severity+ | Not Required | Return only alerts for signatures with the given risk_level. Valid values are Low, Medium, High
|
67
|
+
#
|
68
|
+
# +signature_name+ | Not Required | Return only alerts for signatures with the given name.
|
69
|
+
#
|
70
|
+
# +resource+ | Not Required | Return only alerts for the given resource or tag.
|
71
|
+
#
|
72
|
+
# +signature_identifier+ | Not Required | Return only alerts for signatures with the given identifier.
|
73
|
+
#
|
74
|
+
# ==== Example
|
75
|
+
# alerts = ESP::Alert.for_report(54, status: 'fail', signature_severity: 'High')
|
76
|
+
def self.for_report(report_id = nil, arguments = {})
|
77
|
+
fail ArgumentError, "You must supply a report id." unless report_id.present?
|
78
|
+
from = "#{prefix}reports/#{report_id}/alerts.json"
|
79
|
+
all(from: from, params: arguments)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Find an Alert by id
|
83
|
+
#
|
84
|
+
# ==== Parameter
|
85
|
+
#
|
86
|
+
# +id+ | Required | The ID of the alert to retrieve
|
87
|
+
#
|
88
|
+
# :call-seq:
|
89
|
+
# find(id)
|
90
|
+
def self.find(*arguments)
|
91
|
+
scope = arguments.slice!(0)
|
92
|
+
options = (arguments.slice!(0) || {}).with_indifferent_access
|
93
|
+
return super(scope, options) if scope.is_a?(Numeric) || options[:from].present?
|
94
|
+
params = options.fetch(:params, {}).with_indifferent_access
|
95
|
+
report_id = params.delete(:report_id)
|
96
|
+
for_report(report_id, params)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Suppress the signature associated with this alert.
|
100
|
+
# ==== Parameter
|
101
|
+
#
|
102
|
+
# +reason+ | Required | The reason for creating the suppression.
|
103
|
+
def suppress_signature(reason = nil)
|
104
|
+
suppress(Suppression::Signature, reason)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Suppress the region associated with this alert.
|
108
|
+
# ==== Parameter
|
109
|
+
#
|
110
|
+
# +reason+ | Required | The reason for creating the suppression.
|
111
|
+
def suppress_region(reason = nil)
|
112
|
+
suppress(Suppression::Region, reason)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Suppress the unique identifier associated with this alert.
|
116
|
+
# ==== Parameter
|
117
|
+
#
|
118
|
+
# +reason+ | Required | The reason for creating the suppression.
|
119
|
+
def suppress_unique_identifier(reason = nil)
|
120
|
+
suppress(Suppression::UniqueIdentifier, reason)
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# Overridden because alerts does not use ransack for searching
|
126
|
+
def self.filters(params)
|
127
|
+
{ filter: params }
|
128
|
+
end
|
129
|
+
|
130
|
+
def suppress(klass, reason)
|
131
|
+
fail ArgumentError, "You must specify the reason.".freeze unless reason.present?
|
132
|
+
klass.create(alert_id: id, reason: reason)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ESP
|
2
|
+
class CloudTrailEvent < ESP::Resource
|
3
|
+
# Not Implemented. You cannot create or update a CloudTrailEvent.
|
4
|
+
def save
|
5
|
+
fail ESP::NotImplementedError
|
6
|
+
end
|
7
|
+
|
8
|
+
# Not Implemented. You cannot destroy a CloudTrailEvent.
|
9
|
+
def destroy
|
10
|
+
fail ESP::NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns a paginated collection of cloud trail events for the given alert_id
|
14
|
+
# Convenience method to use instead of ::find since an alert_id is required to return cloud trail events.
|
15
|
+
#
|
16
|
+
# ==== Parameter
|
17
|
+
#
|
18
|
+
# +alert_id+ | Required | The ID of the alert to retrieve cloud trail events for
|
19
|
+
#
|
20
|
+
# ==== Example
|
21
|
+
# alerts = ESP::CloudTrailEvent.for_alert(1194)
|
22
|
+
def self.for_alert(alert_id = nil)
|
23
|
+
fail ArgumentError, "You must supply an alert id." unless alert_id.present?
|
24
|
+
from = "#{prefix}alerts/#{alert_id}/cloud_trail_events.json"
|
25
|
+
find(:all, from: from)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Find a CloudTrailEvent by id
|
29
|
+
#
|
30
|
+
# ==== Parameter
|
31
|
+
#
|
32
|
+
# +id+ | Required | The ID of the cloud trail event to retrieve
|
33
|
+
#
|
34
|
+
# :call-seq:
|
35
|
+
# find(id)
|
36
|
+
def self.find(*arguments)
|
37
|
+
scope = arguments.slice!(0)
|
38
|
+
options = (arguments.slice!(0) || {}).with_indifferent_access
|
39
|
+
return super(scope, options) if scope.is_a?(Numeric) || options[:from].present?
|
40
|
+
params = options.fetch(:params, {}).with_indifferent_access
|
41
|
+
alert_id = params.delete(:alert_id)
|
42
|
+
for_alert(alert_id)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|