conrad 1.0.0

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 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: []