whowas 0.1.4

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: 23b13b6e3c208d2ed5d32406ee185c7d9042edb9
4
+ data.tar.gz: 6d5ebc269ba4ad75720d5b71fe80c7c85d2bb01d
5
+ SHA512:
6
+ metadata.gz: 2d7885df76e22f2f4f4d3fe213387a001992ad446c43656971d19ffeeee67e1f2a8968294c8ffec0341926b4bc83456e24c1d4fd8732e851795a4beb21867606
7
+ data.tar.gz: 0354e38f5de7f29de16db613edc10ad9e2dcf83ace7e19135b88125bd9c2efbccda0d56d0aba34df1f2b666a6187e24c40671e07d02a389b18ce42635d051525
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ install:
5
+ - travis_retry bundle install
6
+ sudo: false
7
+ cache: bundler
8
+
9
+ addons:
10
+ code_climate:
11
+ repo_token: bd0689fbbea3942f98293e557a67ab25be700ef489f28e680a74e3761b50c3fa
12
+
13
+ script:
14
+ - bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jess Frisch, Tufts University
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ # Whowas
2
+
3
+ [![Code Climate](https://codeclimate.com/github/TuftsUniversity/whowas/badges/gpa.svg)](https://codeclimate.com/github/TuftsUniversity/whowas) [![Test Coverage](https://codeclimate.com/github/TuftsUniversity/whowas/badges/coverage.svg)](https://codeclimate.com/github/TuftsUniversity/whowas/coverage) [![Build Status](https://travis-ci.org/TuftsUniversity/whowas.svg?branch=master)](https://travis-ci.org/TuftsUniversity/whowas) [![Dependency Status](https://gemnasium.com/TuftsUniversity/whowas.svg)](https://gemnasium.com/TuftsUniversity/whowas)
4
+
5
+ ## Description
6
+
7
+ Whowas is a simple tool for chaining third-party API searches together. It is called "Whowas" because it was initially developed to answer the question, "Who was the person (username) using this IP address at that time?"
8
+
9
+ Whowas can be used to match any piece of data with another through an arbitrary number of API searches, where the result of each search becomes the input to the next search.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'whowas'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install whowas
26
+
27
+ ## Usage
28
+
29
+ Please see the [wiki](https://github.com/TuftsUniversity/whowas/wiki) for basic usage and other documentation.
30
+
31
+ ## Development
32
+
33
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
34
+
35
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
36
+
37
+ ## Contributing
38
+
39
+ Bug reports and pull requests are welcome on GitHub at https://github.com/TuftsUniversity/whowas.
40
+
41
+ ## License
42
+
43
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
44
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "whowas"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,52 @@
1
+ require "splunk-sdk-ruby"
2
+
3
+ module Whowas
4
+ class Splunk
5
+ include Whowas::Api
6
+
7
+ @@connection = nil
8
+
9
+ private
10
+
11
+ # Whowas.splunk_config is configured via define_setting in the initializer.
12
+ # See configuration.rb for more information.
13
+ def self.connection(config: Whowas.splunk_config)
14
+ @@connection ||= ::Splunk::connect(config)
15
+ rescue => e
16
+ raise Whowas::Errors::ServiceUnavailable, e
17
+ end
18
+
19
+ def validate(input)
20
+ (input[:query] &&
21
+ !input[:query].empty? &&
22
+ input[:offset].is_a?(Integer) &&
23
+ DateTime.parse(input[:timestamp]) &&
24
+ true) ||
25
+ (raise Whowas::Errors::InvalidInput, "Invalid input for Splunk API")
26
+ end
27
+
28
+ def format(input)
29
+ input = {
30
+ query: "search #{input[:query]}",
31
+ args: {
32
+ earliest_time: format_timestamp(input[:timestamp], input[:offset]),
33
+ latest_time: format_timestamp(input[:timestamp], 0.1)
34
+ }
35
+ }
36
+ end
37
+
38
+ def format_timestamp(timestamp, offset)
39
+ (DateTime.parse(timestamp).to_time + offset).strftime("%Y-%m-%dT%H:%M:%S.%L%z")
40
+ end
41
+
42
+ def search_api(input)
43
+ puts input
44
+ stream = self.class.connection.create_export(input[:query], input[:args])
45
+ if results = ::Splunk::MultiResultsReader.new(stream).first.first
46
+ results["_raw"]
47
+ else
48
+ ""
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ module Whowas
2
+ class ApiGenerator < Rails::Generators::NamedBase
3
+ source_root File.expand_path("../templates", __FILE__)
4
+
5
+ def create
6
+ copy_file "api.rb", "app/apis/#{file_name}.rb"
7
+ end
8
+
9
+ def rename_class
10
+ gsub_file "app/apis/#{file_name}.rb", /MyApi/, name.camelize
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Whowas
2
+ class InstallGenerator < Rails::Generators::Base
3
+ source_root File.expand_path("../templates", __FILE__)
4
+
5
+ def copy_initializer
6
+ copy_file "initializer.rb", "config/initializers/whowas.rb"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Whowas
2
+ class RecipeGenerator < Rails::Generators::NamedBase
3
+ source_root File.expand_path("../templates", __FILE__)
4
+
5
+ def create
6
+ copy_file "recipe.rb", "app/recipes/#{file_name}.rb"
7
+ end
8
+
9
+ def rename_method
10
+ gsub_file "app/recipes/#{file_name}.rb", /name_this_recipe/, name.underscore
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Whowas
2
+ class SearchMethodGenerator < Rails::Generators::NamedBase
3
+ source_root File.expand_path("../templates", __FILE__)
4
+
5
+ def create
6
+ copy_file "search_method.rb", "app/search_methods/#{file_name}.rb"
7
+ end
8
+
9
+ def rename_class
10
+ gsub_file "app/search_methods/#{file_name}.rb", /MySearchMethod/, name.camelize
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ module Whowas
2
+ class MyApi
3
+ # Whowas::Api provides the public interface to your API, accessed through the
4
+ # "search" instance method.
5
+ include Whowas::Api
6
+
7
+ # All custom API code is defined in the private methods below. Naturally,
8
+ # you can add private methods as needed for connecting to the API, etc.
9
+ private
10
+
11
+ ## Required
12
+
13
+ # Sends a search query with provided input to your API and returns results
14
+ # as a string.
15
+ def search_api(input)
16
+ ""
17
+ end
18
+
19
+ ## Optional
20
+
21
+ # Validates input to avoid unnecessary API calls.
22
+ # MUST return true or raise a Whowas::Errors::InvalidInput error.
23
+ # Replace "true" with your validation code.
24
+ def validate(input)
25
+ true ||
26
+ (raise Whowas::Errors::InvalidInput, "Invalid input for #{self.class.name}")
27
+ end
28
+
29
+ # Transforms input one last time before API call.
30
+ # Will be called on input for all search_methods using this API.
31
+ # For search_method-specific transformations, use the format_input method
32
+ # in your search_method.
33
+ def format(input)
34
+ input
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ # WhoWas configuration options
2
+ Whowas.configuration do |config|
3
+ ## Recipe Table
4
+ # You MUST define your default recipe and any other recipes you may use
5
+ # here. Viable keys include "default", "ip_default", "mac_default", and
6
+ # valid IP addresses or CIDR blocks.
7
+ #
8
+ # An example of a full recipe table:
9
+ #
10
+ # config.recipe_table = {
11
+ # "192.168.1.0/24": Whowas.home_wireless,
12
+ # "10.0.0.0/8": Whowas.internal_wired,
13
+ # ip_default: Whowas.other_ips,
14
+ # mac_default: Whowas.search_by_mac,
15
+ # default: Whowas.other_ips
16
+ # }
17
+ config.recipe_table = {}
18
+
19
+ ## Recipes class
20
+ # You can change this to a custom defined class if you need to extend the
21
+ # recipe selection algorithm beyond IP and mac addresses.
22
+ config.recipe_class = Whowas::Recipes
23
+
24
+ ## API configuration and credentials
25
+ # If you are not using a bundled API, you can safely ignore this section.
26
+
27
+ # Splunk API configuration
28
+ # The recommended method is to store your Splunk credentials in environment
29
+ # variables using a gem such as dotenv.
30
+ #
31
+ # config.splunk_config = {
32
+ # scheme: :https,
33
+ # host: ENV['SPLUNK_HOST'],
34
+ # port: ENV['SPLUNK_PORT'],
35
+ # username: ENV['SPLUNK_USERNAME'],
36
+ # password: ENV['SPLUNK_PASSWORD']
37
+ # }
38
+ #
39
+ config.splunk_config = {}
40
+ end
@@ -0,0 +1,14 @@
1
+ require "middleware"
2
+
3
+ module Whowas
4
+ def self.name_this_recipe
5
+ # All you have to do is specify the search method classes in the order
6
+ # they should be called. The output for each search method should match
7
+ # the input of the next.
8
+ Middleware::Builder.new do
9
+ # use MySearchMethod1
10
+ # use MySearchMethod2
11
+ # use MySearchMethod3
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,62 @@
1
+ module Whowas
2
+ class MySearchMethod
3
+ # All required public methods are contained in the Middleware package. It
4
+ # initializes the search method, calls search on the API with the provided
5
+ # input, and returns the output to the next search method or the caller.
6
+ #
7
+ # The Searchable modules (Validatable, Formattable, and Parsable) are
8
+ # technically optional but in practice necessary to ensure usable input and
9
+ # outputs.
10
+ include Whowas::Middleware
11
+ include Whowas::Searchable
12
+
13
+ ## API
14
+ # You MUST set this to the name of a bundled or custom API class.
15
+ @@api = API_CLASS_HERE
16
+
17
+ private
18
+
19
+ ## Validatable
20
+ # Optional but useful to prevent making unnecessary API calls when given
21
+ # invalid input.
22
+
23
+ # Defines required elements of the input hash.
24
+ # This should be an array containing the required inputs as symbols.
25
+ def required_inputs
26
+ [
27
+ # :ip,
28
+ # :timestamp
29
+ ]
30
+ end
31
+
32
+ # Validates the values of required inputs.
33
+ # This should be a hash containing the required input as key, and a lambda
34
+ # taking input and returning a boolean as value.
35
+ def input_formats
36
+ {
37
+ # timestamp: lambda { |input| DateTime.parse(input) && true rescue false }
38
+ }
39
+ end
40
+
41
+ ## Formattable
42
+
43
+ # Search method-wide transformations to the input. For example, if all
44
+ # mac addresses given as input to this search method should use colons as
45
+ # separators, perform that transformation here.
46
+ #
47
+ # API-wide transformations to the input can be made in the API format method.
48
+ def format_input(input)
49
+ input
50
+ end
51
+
52
+ ## Parsable
53
+
54
+ # Extract pieces of the results string from the API using regex to form the
55
+ # input hash for the next search method or the final result.
56
+ def output_formats
57
+ {
58
+ # username: /User <\K\w*/
59
+ }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,62 @@
1
+ require "middleware"
2
+
3
+ require "whowas/api"
4
+ require "whowas/configuration"
5
+ require "whowas/errors"
6
+ require "whowas/formattable"
7
+ require "whowas/middleware"
8
+ require "whowas/parsable"
9
+ require "whowas/recipes"
10
+ require "whowas/searchable"
11
+ require "whowas/validatable"
12
+ require "whowas/version"
13
+
14
+ # bundled apis
15
+ require "apis/splunk"
16
+
17
+
18
+ module Whowas
19
+ extend Configuration
20
+
21
+ # TODO: create a more sophisticated system for creating and retrieving
22
+ # recipes
23
+ #
24
+ # For now, users must provide their own recipes, which is a Middleware stack.
25
+ # For documentation, see https://github.com/mitchellh/middleware
26
+ #
27
+ # To create compatible middleware classes, include Whowas::Middleware.
28
+ # This will create the necessary initialize and call classes.
29
+ # It requires the underlying class to have a "#search" function which takes
30
+ # a hash and returns a string.
31
+ #
32
+ # Recipes are automatically selected using the recipe_table attribute, which
33
+ # can be configured by the user, and the Recipes.select algorithm.
34
+ #
35
+ # For more search support, include the Whowas::Searchable module, which adds
36
+ # validation, formatting, and parsing to the search methods. Check the source
37
+ # for more documentation.
38
+
39
+ define_setting :recipe_table, {}
40
+ define_setting :recipe_class, Whowas::Recipes
41
+
42
+ # configuration defaults
43
+ define_setting :splunk_config, nil
44
+
45
+ def self.search(input)
46
+ recipe = recipe_class.select(input) || Whowas.recipe_table[:default]
47
+ env = {input: input, results: []}
48
+ recipe.call(env)
49
+ env
50
+ end
51
+
52
+ private
53
+
54
+ # Uses the recipe_table hash to match up the input to the correct recipe type.
55
+ # If there is an IP address in the input, it tries to match the IP address
56
+ # against any keys that are valid CIDR blocks.
57
+ # If there is a mac address, it looks for the key :mac.
58
+ # Otherwise, it falls back to :default.
59
+ def select_recipe(input)
60
+ end
61
+
62
+ end
@@ -0,0 +1,30 @@
1
+ module Whowas
2
+ module Api
3
+ # All APIs use (api_instance).search as the only public method
4
+ # and follow the pattern below.
5
+ # Validation and formatting are optional.
6
+ # search_api must contain the core api search code and return the results.
7
+ def search(input)
8
+ validate(input)
9
+ input = format(input)
10
+ search_api(input)
11
+ end
12
+
13
+ private
14
+
15
+ # default methods for APIs to use or override
16
+ #:nocov:
17
+ def validate(input)
18
+ true
19
+ end
20
+
21
+ def format(input)
22
+ input
23
+ end
24
+
25
+ # MUST be overridden (core search functionality)
26
+ def search_api(input)
27
+ raise Errors::SubclassResponsibility
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ module Whowas
2
+ # This is just a simple way to allow gem users to define their own variables
3
+ # on whatever this module extends -- mainly the Whowas module itself.
4
+ #
5
+ # see the following article:
6
+ # https://www.viget.com/articles/easy-gem-configuration-variables-with-defaults
7
+ module Configuration
8
+ def configuration
9
+ yield self
10
+ end
11
+
12
+ def define_setting(name, default = nil)
13
+ class_variable_set("@@#{name}", default)
14
+
15
+ define_class_method "#{name}=" do |value|
16
+ class_variable_set("@@#{name}", value)
17
+ end
18
+
19
+ define_class_method name do
20
+ class_variable_get("@@#{name}")
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def define_class_method(name, &block)
27
+ (class << self; self; end).instance_eval do
28
+ define_method name, &block
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ module Whowas
2
+ # A base error class for Whowas. Most of the errors that will be thrown
3
+ # from Whowas will inherit from this class.
4
+ class Error < StandardError
5
+ end
6
+
7
+ module Errors
8
+ # Will be thrown when input is invalid.
9
+ class InvalidInput < Whowas::Error
10
+ end
11
+
12
+ # Will be thrown when an external service is unavailable.
13
+ class ServiceUnavailable < Whowas::Error
14
+ end
15
+
16
+ # Will be thrown when a subclass or including class doesn't define
17
+ # a required method.
18
+ class SubclassResponsibility < Whowas::Error
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module Whowas
2
+ module Formattable
3
+ def format(input)
4
+ format_input(input)
5
+ end
6
+
7
+ # :nocov:
8
+ private
9
+ # This is a hook for the including class to use to modify the input before
10
+ # the api gets it.
11
+ #
12
+ # For example, when searching in Splunk, the Firewall class (which includes
13
+ # Searchable) must create the query string from the ip and port, plus any
14
+ # other parameters like the Splunk index it wants to search.
15
+ #
16
+ # By default, this method just returns the arguments given, but note that
17
+ # most classes _will have_ to modify the input to make it usable by the
18
+ # API modification method.
19
+ def format_input(input)
20
+ input
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Whowas
2
+ module Middleware
3
+ def initialize(app = nil)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ env[:results] ||= Array.new
9
+ if results = self.search(env[:input])
10
+ if results[:raw] && !results[:raw].empty?
11
+ env[:results] << {results[:method] => results[:raw]}
12
+ env[:input] = results[:input].merge({timestamp: env[:input][:timestamp]})
13
+ @app.call(env) unless !@app
14
+ else
15
+ env[:results] << { results[:method] => "No results found." }
16
+ end
17
+ end
18
+ env[:results]
19
+ rescue Whowas::Error => e
20
+ env[:error] = e
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ module Whowas
2
+ module Parsable
3
+ # Parses a raw result into a hash including the raw result and
4
+ # other data extracted from the raw result to be used as potential inputs
5
+ # for other searches.
6
+ #
7
+ # Example:
8
+ # Given the raw string "I am a string, but I also have an IP -- 192.168.1.1"
9
+ # The result should be:
10
+ # {
11
+ # raw: "I am a string, but I also have an IP -- 192.168.1.1",
12
+ # input: { ip: "192.168.1.1" }
13
+ # }
14
+ def parse(raw)
15
+ {
16
+ raw: raw,
17
+ input: parse_for_input(raw),
18
+ method: self.class.name
19
+ }
20
+ end
21
+
22
+ # :nocov:
23
+ private
24
+
25
+ def parse_for_input(raw)
26
+ if raw && !raw.empty?
27
+ output_formats.map { |k, v| [k, raw[v]] }.to_h.delete_if{ |k, v| v.nil? }
28
+ else
29
+ {}
30
+ end
31
+ end
32
+
33
+ # A hook for each including class to define the input names and formats the
34
+ # parser should look for in the raw result.
35
+ #
36
+ # This should be a hash of name -> regex, for example:
37
+ # {
38
+ # ip: /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/,
39
+ # username: /Username=\K\w*/
40
+ # }
41
+ def input_formats
42
+ {}
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,32 @@
1
+ require "ipaddr"
2
+
3
+ module Whowas
4
+ class Recipes
5
+ def self.select(input)
6
+ if input[:ip]
7
+ select_by_ip(input[:ip])
8
+ elsif input[:mac]
9
+ Whowas.recipe_table[:mac_default]
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def self.select_by_ip(ip)
16
+ ip = IPAddr.new(ip)
17
+
18
+ results = Whowas.recipe_table.select do |key, value|
19
+ if subnet = (IPAddr.new(key.to_s) rescue nil)
20
+ subnet.include?(ip)
21
+ end
22
+ end
23
+
24
+ if results.empty?
25
+ Whowas.recipe_table[:ip_default]
26
+ else
27
+ results.values.first
28
+ end
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,26 @@
1
+ module Whowas
2
+ module Searchable
3
+ @@api = nil
4
+
5
+ # The including class *must* set the api class in a class constant.
6
+ def api
7
+ @@api || (raise Errors::SubclassResponsibility)
8
+ end
9
+
10
+ # extend the including class with the searchable sub-modules
11
+ def self.included klass
12
+ klass.class_eval do
13
+ include Validatable
14
+ include Formattable
15
+ include Parsable
16
+ end
17
+ end
18
+
19
+ def search(input)
20
+ validate(input)
21
+ input = format(input)
22
+ result = api.new.search(input)
23
+ parse(result)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ module Whowas
2
+ module Validatable
3
+ # Checks for required inputs and input formats
4
+ # that the API will need to process the search.
5
+ #
6
+ # It does *not* matter if there are other, non-required parameters
7
+ # in the input hash; they will be ignored later.
8
+ def validate(input)
9
+ (check_exists(required_inputs, input) &&
10
+ check_format(input_formats, input)) ||
11
+ (raise Errors::InvalidInput, "Invalid input for #{self.class.name}")
12
+ end
13
+
14
+ # :nocov:
15
+ private
16
+
17
+ # hooks for required_inputs and input_formats
18
+ # must be set by including class
19
+ def required_inputs
20
+ []
21
+ end
22
+
23
+ def input_formats
24
+ {}
25
+ end
26
+
27
+ # Required keys must exist in the input hash and must have a non-nil,
28
+ # non-empty value.
29
+ def check_exists(required, input)
30
+ required.inject(true) do |result, key|
31
+ input[key] && result
32
+ end
33
+ end
34
+
35
+ # Format is a lambda defined in the api class which returns true or false.
36
+ # This allows for flexible format checking:
37
+ # e.g. regex for ip addresses and DateTime.parse calls for timestamps
38
+ def check_format(formats, input)
39
+ formats.inject(true) do |result, (key, format)|
40
+ format.call(input[key]) && result
41
+ end
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,3 @@
1
+ module Whowas
2
+ VERSION = "0.1.4"
3
+ end
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'whowas/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "whowas"
8
+ spec.version = Whowas::VERSION
9
+ spec.authors = ["Jess Frisch"]
10
+ spec.email = ["jess.frisch@tufts.edu"]
11
+
12
+ spec.summary = %q{Match an IP address and timestamp to a username.}
13
+ spec.homepage = "https://github.com/TuftsUniversity/whowas"
14
+ spec.license = "MIT"
15
+
16
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
17
+ # delete this section to allow pushing this gem to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
22
+ end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "middleware"
30
+ spec.add_dependency "splunk-sdk-ruby"
31
+
32
+ spec.add_development_dependency "bundler", "~> 1.10"
33
+ spec.add_development_dependency "codeclimate-test-reporter"
34
+ spec.add_development_dependency "pry-byebug"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "rspec"
37
+ spec.add_development_dependency "simplecov"
38
+ end
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: whowas
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Jess Frisch
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-06-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: middleware
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: splunk-sdk-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: codeclimate-test-reporter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description:
126
+ email:
127
+ - jess.frisch@tufts.edu
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - ".travis.yml"
135
+ - Gemfile
136
+ - LICENSE.txt
137
+ - README.md
138
+ - Rakefile
139
+ - bin/console
140
+ - bin/setup
141
+ - lib/apis/splunk.rb
142
+ - lib/generators/whowas/api_generator.rb
143
+ - lib/generators/whowas/install_generator.rb
144
+ - lib/generators/whowas/recipe_generator.rb
145
+ - lib/generators/whowas/search_method_generator.rb
146
+ - lib/generators/whowas/templates/api.rb
147
+ - lib/generators/whowas/templates/initializer.rb
148
+ - lib/generators/whowas/templates/recipe.rb
149
+ - lib/generators/whowas/templates/search_method.rb
150
+ - lib/whowas.rb
151
+ - lib/whowas/api.rb
152
+ - lib/whowas/configuration.rb
153
+ - lib/whowas/errors.rb
154
+ - lib/whowas/formattable.rb
155
+ - lib/whowas/middleware.rb
156
+ - lib/whowas/parsable.rb
157
+ - lib/whowas/recipes.rb
158
+ - lib/whowas/searchable.rb
159
+ - lib/whowas/validatable.rb
160
+ - lib/whowas/version.rb
161
+ - whowas.gemspec
162
+ homepage: https://github.com/TuftsUniversity/whowas
163
+ licenses:
164
+ - MIT
165
+ metadata:
166
+ allowed_push_host: https://rubygems.org
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ requirements: []
182
+ rubyforge_project:
183
+ rubygems_version: 2.4.8
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: Match an IP address and timestamp to a username.
187
+ test_files: []