alert_logic 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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