conrad 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: eb4af3e930e6793ecb9a869e52c0150ea2aa87753b3f062713c872b5042ea754
4
+ data.tar.gz: 73c9bdc8e25b35527cf7064c934ef4a5762c2705c2b6c1c22bcc56c102fab547
5
+ SHA512:
6
+ metadata.gz: e16f4db32806aa47a4411a6b9dbc5964eb522f477f48d2e97995839636b0b66e73bbc6392f02756d12e4a9f8244d1f17d6693a17082c08a5ac623de2408495dd
7
+ data.tar.gz: a866c01f282c5cfa5b1b71c83d9653dd697f25252f448d3df1b772d05bcc838bf127f4fb92a36f3848372ded0aac1473ca68761620a1309a0ce00456a9a18d99
@@ -0,0 +1,52 @@
1
+ require 'conrad/errors'
2
+
3
+ module Conrad
4
+ # Used to add timestamps to an audit event in seconds or milliseconds.
5
+ #
6
+ # @!attribute [r] generator
7
+ # object used to generate the timestamp
8
+ class AddTimestamp
9
+ # :nodoc:
10
+ class Error < Conrad::Error; end
11
+
12
+ # Types of units supported for generation.
13
+ ALLOWED_TIME_UNITS = %i[milliseconds seconds].freeze
14
+
15
+ attr_reader :generator, :timestamp_key
16
+
17
+ # Creates a new instance of AddTimestmap processor
18
+ #
19
+ # @param units [Symbol] type of time units for the timestamp generated.
20
+ # Allows :seconds or :milliseconds.
21
+ # @param timestamp_key [Symbol] key to add to the event hash.
22
+ # @raise [ArgumentError] if the given units value is not one of
23
+ # ALLOWED_TIME_UNITS
24
+ def initialize(units: :milliseconds, timestamp_key: :timestamp)
25
+ unless ALLOWED_TIME_UNITS.include? units
26
+ raise ArgumentError, "Provided units of `#{units}` must be one of #{ALLOWED_TIME_UNITS}"
27
+ end
28
+
29
+ @generator = generator_from_units(units)
30
+ @timestamp_key = timestamp_key
31
+ end
32
+
33
+ # Generates and adds a timestamp to the provided Hash.
34
+ #
35
+ # @param event [Hash]
36
+ # @return [Hash]
37
+ def call(event)
38
+ event.merge(timestamp_key => generator.call)
39
+ end
40
+
41
+ private
42
+
43
+ def generator_from_units(units)
44
+ case units
45
+ when :milliseconds then -> { (Time.now.to_f * 1000).to_i }
46
+ when :seconds then -> { Time.now.to_i }
47
+ else
48
+ raise UnrecognizedTimeUnit, units
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,24 @@
1
+ require 'securerandom'
2
+
3
+ module Conrad
4
+ # Generalized processor for inserting a UUID into the event. Allows
5
+ # configuring the key used for insertion.
6
+ #
7
+ # @!attribute [r] uuid_key
8
+ # The key inserted into the event hash for the generated UUID.
9
+ class AddUUID
10
+ attr_reader :uuid_key
11
+
12
+ # @param uuid_key [Symbol] key to use for the generated UUID
13
+ def initialize(uuid_key = :event_uuid)
14
+ @uuid_key = uuid_key
15
+ end
16
+
17
+ # @param event [Hash] the current event
18
+ #
19
+ # @return [Hash] the hash with the UUID inserted
20
+ def call(event)
21
+ event.merge(uuid_key => SecureRandom.uuid)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conrad
4
+ # Base error class
5
+ class Error < StandardError
6
+ # :nodoc:
7
+ def to_s
8
+ 'An unexpected error has occurred'
9
+ end
10
+ end
11
+
12
+ # Error raised when the value of an event attribute is not of one of the
13
+ # allowed types
14
+ class ForbiddenValue < Error
15
+ def initialize(key, value)
16
+ @key = key
17
+ @value = value
18
+ end
19
+
20
+ # :nodoc:
21
+ def to_s
22
+ "Key of #{@key} provided invalid value type of #{@value.class}"
23
+ end
24
+ end
25
+
26
+ # Error raised when the key of an event attribute is not of one of the
27
+ # allowed types
28
+ class ForbiddenKey < Error
29
+ def initialize(key)
30
+ @key = key
31
+ end
32
+
33
+ # :nodoc:
34
+ def to_s
35
+ "Invalid key #{@key}. Keys must be either Strings or Symbols"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Conrad
6
+ # Formats a given Hash into a presentable JSON format.
7
+ class JSONFormatter
8
+ # Formats a given Hash into a presentable JSON format.
9
+ #
10
+ # @param event [Hash] event to be formatted
11
+ #
12
+ # @return [String] JSON formatted string
13
+ def call(event)
14
+ event.to_json
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,93 @@
1
+ require 'conrad/errors'
2
+ require 'conrad/stdout_emitter'
3
+ require 'conrad/json_formatter'
4
+
5
+ module Conrad
6
+ # Provides the ability to record an event took place.
7
+ # Currently recording an event accepts a hash and passes it through the
8
+ # configured processors, formatter, and emitter. Each of these may transform,
9
+ # validate, format, and send the event as the user sees fit.
10
+ #
11
+ # @!attribute [r] formatter
12
+ # Configured formatter for creating the final event. Defaults to
13
+ # JSONFormatter.
14
+ # @see Conrad::JSONFormatter
15
+ # @!attribute [r] emitter
16
+ # Configured emitter for sending the final event. Defaults to
17
+ # StdoutEmitter.
18
+ # @see Conrad::StdoutEmitter
19
+ # @!attribute [r] processors
20
+ # Configured processors for processing the event pre-formatting and
21
+ # emission. Defaults to an empty array.
22
+ class Recorder
23
+ attr_reader :formatter, :emitter, :processors
24
+
25
+ # All arguments passed must *explicitly* respond to a `call` method.
26
+ #
27
+ # @param formatter [#call] formatter for creating the final event
28
+ # @param emitter [#call] emitter for sending the final event
29
+ # @param processors [Array<#call>] processors for processing the event
30
+ # pre-formatting and emission
31
+ #
32
+ # @raise [ArgumentError] if the formatter, emitter, or any of the
33
+ # processors do not respond_to? `call` with a truthy value.
34
+ def initialize(formatter: JSONFormatter.new, emitter: StdoutEmitter.new, processors: [])
35
+ check_callability(formatter: formatter, emitter: emitter, processors: processors)
36
+
37
+ @formatter = formatter
38
+ @emitter = emitter
39
+ @processors = processors
40
+ end
41
+
42
+ # Processes the given event, formats it, then emits it. It is possible
43
+ # to `throw :halt_conrad_processing` to stop the processing stack. There
44
+ # should be no additional arguments to the `throw` call. At this point, the
45
+ # processing will stop and the audit event will be discarded. The formatter
46
+ # and the emitter will not be called.
47
+ #
48
+ # @param event [Hash] the set of key value pairs to be emitted
49
+ # as a single audit event. It is expected that all keys will be given as
50
+ # Symbols or Strings. All values should be of a type that matches the
51
+ # SCALAR_TYPES or an array once the processor cycle is complete but before
52
+ # final formatting.
53
+ #
54
+ # @raise [ForbiddenKey] when a key is neither a Symbol nor a String
55
+ def audit_event(event)
56
+ processed_event = process_event(event)
57
+
58
+ return unless processed_event
59
+
60
+ validate_event_keys(processed_event)
61
+
62
+ format_and_emit(processed_event)
63
+ end
64
+
65
+ private
66
+
67
+ def process_event(event)
68
+ catch :halt_conrad_processing do
69
+ processors.reduce(event) do |previous_built_event, processor|
70
+ processor.call(previous_built_event)
71
+ end
72
+ end
73
+ end
74
+
75
+ def format_and_emit(event)
76
+ emitter.call(
77
+ formatter.call(event)
78
+ )
79
+ end
80
+
81
+ def check_callability(formatter:, emitter:, processors:)
82
+ [formatter, emitter, *processors].each do |callable|
83
+ raise ArgumentError, "#{callable} does not respond to `#call`" unless callable.respond_to?(:call)
84
+ end
85
+ end
86
+
87
+ def validate_event_keys(event)
88
+ event.each_key do |key|
89
+ raise ForbiddenKey, key unless key.is_a?(Symbol) || key.is_a?(String)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,9 @@
1
+ module Conrad
2
+ # Basic emitter for sending events to $stdout.
3
+ class StdoutEmitter
4
+ # Puts an event to $stdout.
5
+ def call(event)
6
+ puts event
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # :nodoc:
2
+ module Conrad
3
+ # :nodoc:
4
+ VERSION = '1.0.0'.freeze
5
+ end
data/lib/conrad.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'conrad/recorder'
2
+
3
+ # :nodoc:
4
+ module Conrad
5
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: conrad
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathon Anderson
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-12-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.60.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.60.0
55
+ description:
56
+ email:
57
+ - jonathon.anderson@outreach.io
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/conrad.rb
63
+ - lib/conrad/add_timestamp.rb
64
+ - lib/conrad/add_uuid.rb
65
+ - lib/conrad/errors.rb
66
+ - lib/conrad/json_formatter.rb
67
+ - lib/conrad/recorder.rb
68
+ - lib/conrad/stdout_emitter.rb
69
+ - lib/conrad/version.rb
70
+ homepage: https://github.com/getoutreach/conrad
71
+ licenses:
72
+ - MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.7.6
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Tool for auditing events.
94
+ test_files: []