alert_logic 0.1.1
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 +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.rubocop.yml +13 -0
- data/Gemfile +20 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +109 -0
- data/Rakefile +45 -0
- data/alert_logic.gemspec +26 -0
- data/lib/alert_logic.rb +45 -0
- data/lib/alert_logic/client.rb +2 -0
- data/lib/alert_logic/client/base_client.rb +98 -0
- data/lib/alert_logic/client/rest_methods.rb +58 -0
- data/lib/alert_logic/log.rb +23 -0
- data/lib/alert_logic/resources.rb +5 -0
- data/lib/alert_logic/resources/appliance.rb +6 -0
- data/lib/alert_logic/resources/base_resource.rb +123 -0
- data/lib/alert_logic/resources/filters.rb +22 -0
- data/lib/alert_logic/resources/policy.rb +6 -0
- data/lib/alert_logic/resources/protected_host.rb +67 -0
- data/lib/alert_logic/utils.rb +11 -0
- data/lib/alert_logic/version.rb +4 -0
- data/spec/alert_logic_spec.rb +80 -0
- data/spec/client/base_client_spec.rb +51 -0
- data/spec/client/rest_methods_spec.rb +48 -0
- data/spec/log_spec.rb +36 -0
- data/spec/resources/appliance_spec.rb +1 -0
- data/spec/resources/base_resource_spec.rb +1 -0
- data/spec/resources/filters_spec.rb +1 -0
- data/spec/resources/policy_spec.rb +1 -0
- data/spec/resources/protected_host_spec.rb +1 -0
- data/spec/spec_helper.rb +80 -0
- data/spec/support/api_console.rb +38 -0
- data/spec/support/build_json_responses.rb +59 -0
- data/spec/support/fake_alert_logic_api.rb +39 -0
- data/spec/support/fake_api_console.rb +18 -0
- data/spec/utils_spec.rb +27 -0
- metadata +110 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# Module accessor for logger instance
|
2
|
+
module AlertLogic
|
3
|
+
@logger = nil
|
4
|
+
|
5
|
+
# Set or return a logger instance
|
6
|
+
def self.logger(logger_file = nil)
|
7
|
+
if !@logger || logger_file
|
8
|
+
if defined?(Chef::Log.logger) && !logger_file
|
9
|
+
@logger = Chef::Log.logger
|
10
|
+
else
|
11
|
+
@logger = Logger.new(logger_file || $stdout)
|
12
|
+
@logger.level = Logger::INFO
|
13
|
+
end
|
14
|
+
end
|
15
|
+
@logger
|
16
|
+
end
|
17
|
+
|
18
|
+
# Set logger instance to another instance
|
19
|
+
def self.logger=(logger)
|
20
|
+
logger.is_a?(String) ? self.logger(logger) : @logger = logger
|
21
|
+
@logger
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module AlertLogic
|
2
|
+
# Common methods that we'll mix into our resource classes
|
3
|
+
module Resource
|
4
|
+
include Utils
|
5
|
+
|
6
|
+
def self.included(klass)
|
7
|
+
klass.extend Resource
|
8
|
+
end
|
9
|
+
|
10
|
+
def find_by_id(id)
|
11
|
+
resource_type = name.split('::').last
|
12
|
+
klass = AlertLogic.const_get(resource_type.to_sym)
|
13
|
+
resource = AlertLogic \
|
14
|
+
.api_client \
|
15
|
+
.retrieve(resource_type.downcase, id) \
|
16
|
+
.body \
|
17
|
+
.first
|
18
|
+
klass.new(resource)
|
19
|
+
end
|
20
|
+
|
21
|
+
def find(*params)
|
22
|
+
resource_type = name.split('::').last
|
23
|
+
options = params.empty? ? {} : eval_filters(params)
|
24
|
+
klass = AlertLogic.const_get(resource_type.to_sym)
|
25
|
+
resources = AlertLogic \
|
26
|
+
.api_client \
|
27
|
+
.list(resource_type.downcase, options) \
|
28
|
+
.body
|
29
|
+
resources.map! { |resource_hash| klass.new(resource_hash) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(resource_hash)
|
33
|
+
@resource_type = self.class.to_s.downcase.split('::').last
|
34
|
+
objectify(resource_hash)
|
35
|
+
end
|
36
|
+
|
37
|
+
def name=(name)
|
38
|
+
payload = { @resource_type => { 'name' => name } }
|
39
|
+
AlertLogic.api_client.edit(@resource_type, id, payload)
|
40
|
+
reload!
|
41
|
+
end
|
42
|
+
|
43
|
+
def tags=(tags)
|
44
|
+
msg = 'Tags must be a space separated string'
|
45
|
+
fail ClientError, msg unless tags.is_a?(String)
|
46
|
+
tags = tags.split.map! { |tag| { 'name' => tag } }
|
47
|
+
payload = { @resource_type => { 'tags' => tags } }
|
48
|
+
AlertLogic.api_client.edit(@resource_type, id, payload)
|
49
|
+
reload!
|
50
|
+
end
|
51
|
+
|
52
|
+
def reload!
|
53
|
+
objectify(
|
54
|
+
AlertLogic \
|
55
|
+
.api_client \
|
56
|
+
.list(@resource_type, 'id' => id) \
|
57
|
+
.body \
|
58
|
+
.first
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def eval_filters(params)
|
65
|
+
filters = Resource.filters
|
66
|
+
unknown_filters = []
|
67
|
+
params = params.map do |param|
|
68
|
+
if param.is_a?(Symbol)
|
69
|
+
if filters.key?(param)
|
70
|
+
filters[param]
|
71
|
+
else
|
72
|
+
unknown_filters << param
|
73
|
+
{}
|
74
|
+
end
|
75
|
+
elsif param.is_a?(Hash)
|
76
|
+
param
|
77
|
+
else
|
78
|
+
{}
|
79
|
+
end
|
80
|
+
end.reduce(&:merge)
|
81
|
+
msg = "Unknown filter(s) passed: #{unknown_filters.inspect}."
|
82
|
+
msg << " Valid filters: #{filters.keys.inspect}"
|
83
|
+
AlertLogic.logger.warn(msg) unless unknown_filters.empty?
|
84
|
+
params
|
85
|
+
end
|
86
|
+
|
87
|
+
# Recursively traverse the hash and create an instance variable and
|
88
|
+
# accessor method for each key/value pair.
|
89
|
+
def objectify(hash)
|
90
|
+
hash.each do |name, value|
|
91
|
+
if value.is_a?(Hash)
|
92
|
+
value = uniquify_keys(name, value)
|
93
|
+
objectify(value)
|
94
|
+
else
|
95
|
+
def_method(name, value)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Some hash pairs have similar namespaces. We want to rename those to
|
101
|
+
# avoid a naming collision.
|
102
|
+
def uniquify_keys(name, hash)
|
103
|
+
conflicts = /^config$|^appliance$|^created$|
|
104
|
+
^modified$|^config_policy$|^appliance_policy$/
|
105
|
+
return hash unless name =~ conflicts
|
106
|
+
hash.map { |k, v| { "#{name}_#{k}" => v } }.reduce(&:merge)
|
107
|
+
end
|
108
|
+
|
109
|
+
# create and set an instance variable and define an accessor method if it's
|
110
|
+
# not already there.
|
111
|
+
def def_method(name, value)
|
112
|
+
instance_variable_set("@#{name}", value)
|
113
|
+
# Don't overwrite existing methods unless it's id. id isnt fully
|
114
|
+
# deprecatedin 1.8.7
|
115
|
+
(return true) if respond_to?(name.to_sym) && name != 'id'
|
116
|
+
self.class.send(
|
117
|
+
:define_method,
|
118
|
+
name,
|
119
|
+
proc { instance_variable_get("@#{name}") }
|
120
|
+
)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AlertLogic
|
2
|
+
# Resource Filters
|
3
|
+
module Resource
|
4
|
+
@filters = nil
|
5
|
+
# Resource filters accessor
|
6
|
+
def self.filters(filters = nil)
|
7
|
+
defaults = {
|
8
|
+
:ok => { 'status.status' => 'ok' },
|
9
|
+
:online => { 'status.status' => 'ok' },
|
10
|
+
:offline => { 'status.status' => 'offline' },
|
11
|
+
:error => { 'status.status' => 'error' },
|
12
|
+
:windows => { 'metadata.os_type' => 'windows' },
|
13
|
+
:linux => { 'metadata.os_type' => 'unix' },
|
14
|
+
:unix => { 'metadata.os_type' => 'unix' },
|
15
|
+
:appliance_assignment => { 'type' => 'appliance_assignment' },
|
16
|
+
:all => {}
|
17
|
+
}
|
18
|
+
filters && @filters = filters
|
19
|
+
@filters ||= defaults
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module AlertLogic
|
2
|
+
# AlertLogic ProtectedHost built from a JSON API response.
|
3
|
+
class ProtectedHost
|
4
|
+
include Resource
|
5
|
+
|
6
|
+
def appliance_policy_id=(policy_id)
|
7
|
+
update_policy('appliance', policy_id)
|
8
|
+
end
|
9
|
+
|
10
|
+
def config_policy_id=(policy_id)
|
11
|
+
update_policy('config', policy_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def appliance?(appliance)
|
15
|
+
# sometimes appliance_assigned_to wont exist if the Protected Host isn't
|
16
|
+
# already assigned to an appliance.
|
17
|
+
if respond_to?(:appliance_assigned_to)
|
18
|
+
appliance = find_appliance(appliance) if appliance.is_a?(String)
|
19
|
+
appliance.id == appliance_assigned_to
|
20
|
+
else
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def assign_appliance(appliance)
|
26
|
+
if appliance?(appliance)
|
27
|
+
AlertLogic.logger.info 'Host is already assigned to that Appliance'
|
28
|
+
else
|
29
|
+
appliance = find_appliance(appliance) if appliance.is_a?(String)
|
30
|
+
policy = AlertLogic::Policy \
|
31
|
+
.find('type' => 'appliance_assignment') \
|
32
|
+
.select { |pol| pol.appliances.any? { |ap| ap == appliance.id } } \
|
33
|
+
.first
|
34
|
+
self.appliance_policy_id = policy.id
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def reload!
|
39
|
+
[:@appliance_assigned_to,
|
40
|
+
:@appliance_connected_to,
|
41
|
+
:@appliance_policy_id,
|
42
|
+
:@config_policy_id
|
43
|
+
].each do |var|
|
44
|
+
remove_instance_variable(var) if instance_variable_defined?(var)
|
45
|
+
end
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def find_appliance(name)
|
52
|
+
msg = "Searching for an appliance with name: #{name}"
|
53
|
+
AlertLogic.logger.info msg
|
54
|
+
Appliance.find.select { |ap| ap.name == name }.first
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_policy(policy_type, policy_id)
|
58
|
+
payload = { 'protectedhost' =>
|
59
|
+
{ policy_type => { 'policy' => { 'id' => policy_id } } }
|
60
|
+
}
|
61
|
+
res = AlertLogic.api_client.edit('protectedhost', id, payload)
|
62
|
+
AlertLogic.logger.debug res.body.inspect
|
63
|
+
reload!
|
64
|
+
AlertLogic.logger.info "#{policy_type} policy updated!"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module AlertLogic
|
2
|
+
# common utility methods that are required in multiple classes
|
3
|
+
module Utils
|
4
|
+
private
|
5
|
+
|
6
|
+
# simple string pluralizer to translate singular Alert Logic resources
|
7
|
+
def pluralize(resource)
|
8
|
+
resource =~ /^\w+y$/ ? resource.sub(/y$/, 'ies') : "#{resource}s"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module AlertLogic
|
4
|
+
describe AlertLogic, '.api_client' do
|
5
|
+
before(:each) do
|
6
|
+
@client = double(:secret_key => '3234', :endpoint => 'https://fake.com')
|
7
|
+
AlertLogic.instance_variable_set(:@api_client, @client)
|
8
|
+
allow(Client).to receive(:new).and_return(@client)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'accepts no params and returns a client' do
|
12
|
+
AlertLogic.instance_variable_set(:@api_client, nil)
|
13
|
+
AlertLogic.api_client.should eq(@client)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'accepts a non-matching secret_key param and returns a new client' do
|
17
|
+
new_key = '8F1E9F714E7C70DADF5C26BDC89618999E211FA45766279f07'
|
18
|
+
Client.should_receive(:new).with(new_key, nil)
|
19
|
+
AlertLogic.api_client(new_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'accepts a matching secret_key param and returns an existing client' do
|
23
|
+
AlertLogic.api_client('3234').should eq(@client)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'accepts a non-matching endpoint param and returns a new client' do
|
27
|
+
endpoint = 'https://differentfake.com'
|
28
|
+
Client.should_receive(:new).with(nil, endpoint)
|
29
|
+
AlertLogic.api_client(nil, endpoint)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'accepts a matching endpoint param and returns an existing client' do
|
33
|
+
AlertLogic.api_client(nil, '3234').should eq(@client)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'accepts a non-matching endpoint/key params and returns a new client' do
|
37
|
+
endpoint = 'https://differentfake.com'
|
38
|
+
new_key = '8F1E9F714E7C70DADF5C26BDC89618999E211FA45766279f07'
|
39
|
+
Client.should_receive(:new).with(new_key, endpoint)
|
40
|
+
AlertLogic.api_client(new_key, endpoint)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'accepts a matching endpoint param and returns an existing client' do
|
44
|
+
AlertLogic.api_client('https://fake.com', '3234').should eq(@client)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe AlertLogic, '.secret_key' do
|
49
|
+
before(:each) do
|
50
|
+
AlertLogic.instance_variable_set(:@secret_key, '1234')
|
51
|
+
allow(AlertLogic).to receive(:api_client).with(/\d+/)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'returns nil when not set' do
|
55
|
+
AlertLogic.instance_variable_set(:@secret_key, nil)
|
56
|
+
AlertLogic.secret_key.should be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns a value when set' do
|
60
|
+
AlertLogic.secret_key.should_not be_nil
|
61
|
+
AlertLogic.secret_key.should eq('1234')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'configures a new api_client when the key doesnt match' do
|
65
|
+
AlertLogic.should_receive(:api_client).with('5678')
|
66
|
+
AlertLogic.secret_key('5678')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns the existing key when the keys match' do
|
70
|
+
AlertLogic.secret_key('1234').should eq('1234')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe AlertLogic, '.secret_key=' do
|
75
|
+
it 'should proxy request to #secret_key' do
|
76
|
+
AlertLogic.should_receive(:secret_key)
|
77
|
+
AlertLogic.secret_key = '1234'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module AlertLogic
|
4
|
+
describe Client, '.new' do
|
5
|
+
before(:each) do
|
6
|
+
AlertLogic.secret_key = Test.secret_key
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'initializes a new client with no params and a secret_key set' do
|
10
|
+
client = Client.new
|
11
|
+
client.secret_key.should eq(AlertLogic.secret_key)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'raises an exception when no key is configure or passed' do
|
15
|
+
AlertLogic.secret_key = nil
|
16
|
+
expect { Client.new }.to raise_error(InvalidKey)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'initializes a new client with a key param' do
|
20
|
+
key = '8F1E9F714E7C70DADF5C26BDC89618999E211FA45766279f07'
|
21
|
+
client = Client.new(key)
|
22
|
+
client.secret_key.should eq(key)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'initializes a new client with an endpoint param' do
|
26
|
+
endpoint = 'https://fake.com'
|
27
|
+
client = Client.new(nil, endpoint)
|
28
|
+
client.endpoint.should eq(endpoint)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'initializes a new client with both params' do
|
32
|
+
endpoint = 'https://fake.com'
|
33
|
+
key = '8F1E9F714E7C70DADF5C26BDC89618999E211FA45766279f07'
|
34
|
+
client = Client.new(key, endpoint)
|
35
|
+
client.endpoint.should eq(endpoint)
|
36
|
+
client.secret_key.should eq(key)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'creates all the required instance variables' do
|
40
|
+
# ruby 1.8.7 can't sort an array of symbols so we have to define <=>
|
41
|
+
# to convert them to strings and compare against an array of strings.
|
42
|
+
vars = ['@secret_key', '@endpoint', '@logger', '@connection']
|
43
|
+
client = Client.new
|
44
|
+
client.instance_variables.map(&:to_s).sort \
|
45
|
+
.should eq(vars.sort)
|
46
|
+
client \
|
47
|
+
.instance_variable_get(:@connection) \
|
48
|
+
.should be_instance_of(Faraday::Connection)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module AlertLogic
|
4
|
+
describe RestMethods, :env do
|
5
|
+
let(:client) { AlertLogic.api_client(Test.secret_key) }
|
6
|
+
|
7
|
+
Test.all_resources.each do |resource, id|
|
8
|
+
describe '.delete' do
|
9
|
+
it 'interpolates the params and calls the proper client method' do
|
10
|
+
client.should_receive(:delete).with('resource', 'resource_id')
|
11
|
+
client.delete('resource', 'resource_id')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.get' do
|
16
|
+
it 'interpolates the params and calls the proper client method' do
|
17
|
+
client.should_receive(:get).with(resource, id)
|
18
|
+
client.get(resource, id)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "properly parses #{resource} index" do
|
22
|
+
res = client.get(resource, nil)
|
23
|
+
expect(res.body).to be_instance_of(Array)
|
24
|
+
expect(res.status).to eq(200)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "properly parses a singular #{resource}" do
|
28
|
+
res = client.get(resource, id)
|
29
|
+
res.body.first['id'].should eq(id)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '.put' do
|
34
|
+
it 'interpolates the params and calls the proper client method' do
|
35
|
+
client.should_receive(:put).with('resource', 'resource_id')
|
36
|
+
client.put('resource', 'resource_id')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '.post' do
|
41
|
+
it 'interpolates the params and calls the proper client method' do
|
42
|
+
client.should_receive(:post).with('resource', 'resource_id')
|
43
|
+
client.post('resource', 'resource_id')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|