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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2f27c0cd3f279ace8c205fbf2d9d30e968031e7b
4
+ data.tar.gz: 59254fc8eae44353d53784063a4fe79c4d696df3
5
+ SHA512:
6
+ metadata.gz: e3db2ee086242a95906fee39aaece994804ab73845d507f899676a85542b9df9a6d3304aa25ee2a4147e8b88feab77c6fbd5bcff11a14fe515682f4b0a845dc8
7
+ data.tar.gz: edc3f29405ea8cc9f7b4fdffa9884a7fcc90b51782d04d4c3dd0563f58cebd30b3dec831699600c194f145a962a6a3f0e4479e2d0f794971576038614843d6d7
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .env
19
+ spec/support/json
20
+ *.swp
21
+ .ruby-version
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ Excludes:
3
+ - vendor/**
4
+ HashSyntax:
5
+ EnforcedStyle: hash_rockets
6
+ MethodLength:
7
+ Max: 30
8
+ Eval:
9
+ Enabled: false
10
+ RegexpLiteral:
11
+ Enabled: false
12
+ Documentation:
13
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :test do
4
+ gem 'bundler', '>= 1.5'
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'rubocop', :platforms => [:ruby_19, :ruby_20, :ruby_21]
8
+ gem 'sinatra'
9
+ gem 'webmock'
10
+ gem 'dotenv'
11
+ end
12
+
13
+ group :dev do
14
+ gem 'guard', :platforms => [:ruby_19, :ruby_20, :ruby_21]
15
+ gem 'guard-rspec', :platforms => [:ruby_19, :ruby_20, :ruby_21]
16
+ gem 'guard-bundler', :platforms => [:ruby_19, :ruby_20, :ruby_21]
17
+ gem 'pry-rescue', :platforms => [:ruby_19, :ruby_20, :ruby_21]
18
+ end
19
+
20
+ gemspec
@@ -0,0 +1,13 @@
1
+ guard :rspec, :cmd => 'bundle exec rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^spec/.+/.+_spec\.rb$})
4
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
+ watch(%r{^lib/.+/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
6
+ watch(%r{^lib/.+/(.+)/(.+)\.rb$}) { |m| "spec/#{m[1]}/#{m[2]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { 'spec' }
8
+ end
9
+
10
+ guard :bundler do
11
+ watch('Gemfile')
12
+ watch(/^.+\.gemspec/)
13
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Ryan Cragun
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,109 @@
1
+ # AlertLogic
2
+
3
+ AlertLogic is very early on in development. The code is not 100% tested and
4
+ many of the tests need to be refactorerd. Some helper methods have not been
5
+ added but every allowable action the API supports will eventually find itself as
6
+ an instance method on each resource. You should not consider this a stable API
7
+ until a more mature release with full feature coverage.
8
+
9
+ While the client passes the specs in Ruby 1.8.7, there's no guarantee that all
10
+ functions will work. If possible use Ruby >= 1.9.3.
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'alert_logic'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install alert_logic
25
+
26
+ ## Usage
27
+
28
+ AlertLogic only requires the API secret key to authenticate with the API.
29
+
30
+ ```ruby
31
+ require 'alert_logic'
32
+ AlertLogic.secret_key = "YOUR 50 HEX CHARACTER SECRET KEY"
33
+ ```
34
+
35
+ From there you can make raw requests with the API Client:
36
+
37
+ ```ruby
38
+ AlertLogic.api_client.get('policy', 'SOME_POLICY_ID')
39
+ ```
40
+
41
+ If you'd rather avoid the the global client you can create an instance:
42
+
43
+ ```ruby
44
+ @client = AlertLogic::Client.new("YOUR 50 HEX CHARACTER SECRET KEY")
45
+ @client.list('protectedhost')
46
+ ```
47
+
48
+ Using the raw client is fine but the real utility comes from using the resource
49
+ classes:
50
+
51
+ ```ruby
52
+ hosts = AlertLogic::ProtectedHost.find(:all)
53
+ appliance = AlertLogic::Appliance.find(:online, 'id' => 'SOME ID')
54
+ policy_ids = AlertLogic:::Policy.find(:appliance_assignment)
55
+ ```
56
+
57
+ Yeah, that's right, you can use preset filters, custom filters, preset and
58
+ custom filters together or you can even define your own:
59
+
60
+ ```ruby
61
+ my_filters = {
62
+ :us_east_appliance => {'id' => 'ID OF THE APPLIANCE IN US EAST'}
63
+ :us_west_appliance => {'id' => 'ID OF THE APPLIANCE IN US WEST'}
64
+ }
65
+ AlertLogic::Resource.filters.merge!(my_filters)
66
+ us_east_appliance = AlertLogic::Appliance.find(:us_east_appliance).first
67
+ ```
68
+
69
+ That's nice, but the real magic comes when you use the classes together:
70
+
71
+ ```ruby
72
+ linux_hosts = AlertLogic::ProtectedHost.find(:online, :linux)
73
+ us_east_appliance = AlertLogic::Appliance.find(:us_east_appliance).first
74
+ linux_hosts.each { |host| host.assign_appliance(us_east_appliance) }
75
+ ```
76
+
77
+ Another cool thing is that all of the Resource attributes become instance
78
+ variables with their own corresponding accessors:
79
+
80
+ ```ruby
81
+ host = AlertLogic::ProtectedHost.find_by_id('SOME HOST ID')
82
+ host.status #=> 'online'
83
+ host.appliance_assigned_to #=> 'XXXXXX-XXXX-XXXX-XXXX-XXXXXXXX'
84
+ host.appliance_connected_to #=> '10.10.1.1'
85
+ host.local_hostname #=> ip-10-154-155-257.ec2.internal
86
+ ```
87
+
88
+ And common actions have corresponding API methods built on:
89
+
90
+ ```ruby
91
+ host = AlertLogic::ProtectedHost.find_by_id('SOME HOST ID')
92
+ host.name #=> 'some_name'
93
+ host.name = "My New Name" #=> "My New Name"
94
+ host.name #=> 'My New Name'
95
+ host.tags #=> nil
96
+ host.tags = "my_tag some_other_tag" #=> "my_tag some_other_tag"
97
+ host.tags #=> [{"name"=>"my_tag"}, {"name"=>"some_other_tag"}]
98
+ host.config_policy_id #=> 'XXXXXX-XXXX-XXXX-XXXX-XXXXXXXX'
99
+ host.config_policy_id = 'AAAAA-AAAAA-AAAA-AAAAA-AAAAAAAA'
100
+ host.config_policy_id #=> 'AAAAA-AAAAA-AAAA-AAAAA-AAAAAAAA'
101
+ ```
102
+
103
+ ## Contributing
104
+
105
+ 1. Fork it ( http://github.com/ryancragun/alert_logic/fork )
106
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
107
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
108
+ 4. Push to the branch (`git push origin my-new-feature`)
109
+ 5. Create new Pull Request
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'rake'
4
+ require 'rspec'
5
+ require 'rspec/core/rake_task'
6
+ require 'bundler/gem_tasks'
7
+
8
+ namespace :test do
9
+ desc 'Run spec suite'
10
+ RSpec::Core::RakeTask.new(:spec) do |task|
11
+ task.pattern = FileList['spec/**/*_spec.rb']
12
+ end
13
+
14
+ desc 'Build canned JSON responses'
15
+ task :build_json do
16
+ ruby 'spec/support/build_json_responses.rb'
17
+ end
18
+
19
+ desc 'Load fake API console'
20
+ task :console do
21
+ ruby 'spec/support/fake_api_console.rb'
22
+ end
23
+
24
+ if RUBY_VERSION !~ /^1\.8\..$/
25
+ require 'rubocop/rake_task'
26
+
27
+ desc 'Run Rubocop style checks'
28
+ Rubocop::RakeTask.new do |cop|
29
+ cop.fail_on_error = true
30
+ end
31
+ end
32
+ end
33
+
34
+ if RUBY_VERSION !~ /^1\.8\..$/
35
+ desc 'Default task to run spec suite'
36
+ task :default => ['test:spec', 'test:rubocop']
37
+ else
38
+ desc 'Default task to run spec suite'
39
+ task :default => ['test:spec']
40
+ end
41
+
42
+ desc 'Load API console'
43
+ task :console do
44
+ ruby 'spec/support/api_console.rb'
45
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'alert_logic/version'
5
+ require 'base64'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'alert_logic'
9
+ spec.version = AlertLogic::VERSION
10
+ spec.authors = ['Ryan Cragun']
11
+ encoded_email = %w(cnlhbkByaWdodHNjYWxlLmNvbQ==)
12
+ spec.email = encoded_email.map { |e| Base64.decode64(e) }
13
+ message = %q(A feature rich API client for AlertLogic Threat Manager)
14
+ spec.summary = message
15
+ spec.description = message
16
+ spec.homepage = 'https://github.com/ryancragun/alert_logic-gem'
17
+ spec.license = 'MIT'
18
+
19
+ spec.files = `git ls-files`.split($ORS)
20
+ spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(/^(test|spec|features)/)
22
+ spec.require_paths = %w(lib)
23
+
24
+ spec.add_dependency 'json' if RbConfig::CONFIG['ruby_version'].to_f < 1.9
25
+ spec.add_dependency 'faraday'
26
+ end
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'logger'
3
+ require 'faraday'
4
+ require 'json'
5
+
6
+ require 'alert_logic/version'
7
+ require 'alert_logic/log'
8
+ require 'alert_logic/utils'
9
+ require 'alert_logic/client'
10
+ require 'alert_logic/resources'
11
+
12
+ # Root module methods
13
+ module AlertLogic
14
+ @api_client = nil
15
+ @secret_key = nil
16
+
17
+ # module api_client and secret_key accessors
18
+ def self.api_client(secret_key = nil, endpoint = nil)
19
+ if @api_client
20
+ if (endpoint && @api_client.endpoint != endpoint) ||
21
+ (secret_key && @api_client.secret_key != secret_key)
22
+ @api_client = nil
23
+ end
24
+ end
25
+ @api_client ||= Client.new(secret_key, endpoint)
26
+ end
27
+
28
+ def self.secret_key(secret_key = nil)
29
+ if !@secret_key && secret_key ||
30
+ (@secret_key && secret_key) && (@secret_key != secret_key)
31
+ AlertLogic.api_client(secret_key)
32
+ @secret_key = secret_key
33
+ end
34
+ @secret_key
35
+ end
36
+
37
+ def self.secret_key=(secret_key)
38
+ if !secret_key
39
+ @secret_key = nil
40
+ @api_client = nil
41
+ else
42
+ self.secret_key(secret_key)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,2 @@
1
+ require 'alert_logic/client/rest_methods'
2
+ require 'alert_logic/client/base_client'
@@ -0,0 +1,98 @@
1
+ module AlertLogic
2
+ # JSON parsing HTTP client with some helper methods
3
+ class Client
4
+ include Utils
5
+ include RestMethods
6
+
7
+ attr_reader :endpoint, :secret_key
8
+
9
+ DEFAULT_ENDPOINT = 'https://publicapi.alertlogic.net/api/tm/v1/'
10
+
11
+ def initialize(secret_key = nil, endpoint = nil)
12
+ @secret_key = secret_key || AlertLogic.secret_key
13
+ @endpoint = endpoint || DEFAULT_ENDPOINT
14
+ @logger = AlertLogic.logger
15
+ init
16
+ end
17
+
18
+ def secret_key=(secret_key)
19
+ @secret_key = secret_key
20
+ reload!
21
+ @secret_key
22
+ end
23
+
24
+ def endpoint=(endpoint)
25
+ @endpoint = endpoint
26
+ reload!
27
+ @endpoint
28
+ end
29
+
30
+ def reload!
31
+ @connection = nil
32
+ init
33
+ end
34
+
35
+ private
36
+
37
+ def init
38
+ verify_key
39
+ options = { :url => @endpoint,
40
+ :ssl => { :verify => false }
41
+ }
42
+ headers = { 'Accept' => 'application/json',
43
+ 'User-Agent' => "alert_logic gem v#{VERSION}"
44
+ }
45
+ @connection = Faraday.new(options) do |con|
46
+ con.use Faraday::Response::Logger, @logger
47
+ con.use Faraday::Response::RaiseError
48
+ con.adapter Faraday.default_adapter
49
+ con.headers = headers
50
+ con.basic_auth @secret_key, ''
51
+ end
52
+ end
53
+
54
+ def verify_key
55
+ unless @secret_key =~ /^[a-fA-F\d]{50}$/
56
+ msg = "#{@secret_key.inspect} is invalid. "
57
+ msg << 'You must supply a valid 50 character secret_key!'
58
+ fail InvalidKey, msg
59
+ end
60
+ end
61
+
62
+ def parse_response_for(resource_type, &api_call)
63
+ res = yield(api_call)
64
+ ClientResponse.new(
65
+ res.status,
66
+ normalize_response(resource_type, JSON.parse(res.body))
67
+ )
68
+ rescue => e
69
+ raise ClientError, e.message
70
+ end
71
+
72
+ def normalize_response(resource_type, js)
73
+ if js.respond_to?(:key?)
74
+ if js.key?(pluralize(resource_type))
75
+ flatten(resource_type, js[pluralize(resource_type)])
76
+ elsif js.key?(resource_type)
77
+ [js[resource_type]]
78
+ else
79
+ js.first
80
+ end
81
+ elsif js.respond_to?(:empty?) && !js.empty?
82
+ js
83
+ else
84
+ @logger.info "No #{pluralize(resource_type)} found.."
85
+ []
86
+ end
87
+ end
88
+
89
+ def flatten(type, resources)
90
+ resources.count >= 1 ? resources.map! { |item| item[type] } : []
91
+ end
92
+ end
93
+
94
+ ClientResponse = Struct.new(:status, :body)
95
+
96
+ class ClientError < Faraday::Error::ClientError; end
97
+ class InvalidKey < ClientError; end
98
+ end
@@ -0,0 +1,58 @@
1
+ module AlertLogic
2
+ # RestMethods mixin for Client class
3
+ module RestMethods
4
+ # Standard REST methods
5
+ def delete(resource_type, resource_id)
6
+ parse_response_for(resource_type) do
7
+ @connection.delete do |request|
8
+ request.url "#{pluralize(resource_type)}/#{resource_id}"
9
+ end
10
+ end
11
+ end
12
+
13
+ def get(resource_type, resource_id, params = {})
14
+ parse_response_for(resource_type) do
15
+ base_url = pluralize(resource_type)
16
+ @connection.get do |request|
17
+ request.url resource_id ? "#{base_url}/#{resource_id}" : base_url
18
+ params.each { |k, v| request.params[k] = v }
19
+ end
20
+ end
21
+ end
22
+
23
+ def post(resource_type, resource_id, payload)
24
+ parse_response_for(resource_type) do
25
+ base_url = pluralize(resource_type)
26
+ @connection.post do |request|
27
+ request.url resource_id ? "#{base_url}/#{resource_id}" : base_url
28
+ request.body = payload.to_json
29
+ request.headers['Content-Type'] = 'application/json'
30
+ end
31
+ end
32
+ end
33
+
34
+ def put(resource_type, resource_id, payload)
35
+ parse_response_for(resource_type) do
36
+ @connection.put do |request|
37
+ request.url "#{pluralize(resource_type)}/#{resource_id}"
38
+ request.body = payload.to_json
39
+ request.headers['Content-Type'] = 'application/json'
40
+ end
41
+ end
42
+ end
43
+
44
+ # REST method wrappers from AlertLogic API Documentation
45
+ # https://developer.alertlogic.net/docs/iodocs/tm
46
+ alias_method :retrieve, :get
47
+ alias_method :edit, :post
48
+ alias_method :replace, :put
49
+
50
+ def list(resource_type, params = {})
51
+ get(resource_type, nil, params)
52
+ end
53
+
54
+ def create(resource_type, payload)
55
+ post(resource_type, nil, payload)
56
+ end
57
+ end
58
+ end