apptrail-application-events-sdk 0.0.1

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: 0a56d565f6da67bd2bc4bbfb36c20f599e0ef18bd7ec0f5e969caae6c592409c
4
+ data.tar.gz: b3c67d43208b9a8eb134bb529d023c33c24bae80b4485ed41dcc6ca224865131
5
+ SHA512:
6
+ metadata.gz: 9955cd66982d262361d7cbfdd6672ce4f34ff03e4caf65fc883ddcede9ba0fc2d3386b7fe93e32e2f1cbeed1fc0ee3f1d4450c87131eb7e94491ce6f182ed5ce
7
+ data.tar.gz: 76618a0b1bd9f2800a37f167b2781210664da5bb4458a4be29c50d842d14d0e8c2f46bdafd4c3d353e1652625c08bd3062c007233a18b763c5f373a01010a0ce
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # Apptrail Application Events SDK for Ruby
2
+
3
+ You can use the Apptrail Application Events SDK for Ruby to send audit logs from your
4
+ Ruby applications to your customers.
5
+
6
+ ## Learn more
7
+
8
+ - [Working with events](https://apptrail.com/docs/applications/guide/working-with-events/overview)
9
+ - [Full SDK Reference](https://apptrail.com/docs/applications/guide/working-with-events/using-the-events-sdk/application-events-sdk-ruby)
10
+ - [Developer Guide](https://apptrail.com/docs/applications/guide)
11
+ - [Apptrail](https://apptrail.com)
12
+
13
+ ## Notes and tips
14
+
15
+ - Instantiate the client once at the top of your application and reuse it to prevent unnecessary recreation.
16
+
17
+ ## Installing
18
+
19
+ ```bash
20
+ gem "apptrail-application-events-sdk"
21
+ ```
22
+
23
+ Inside of your Ruby program, run:
24
+
25
+ ```
26
+ require "apptrail-application-events-sdk"
27
+ ```
28
+
29
+ ## Instantiating client
30
+
31
+ ```ruby
32
+ require "apptrail-application-events-sdk"
33
+
34
+ my_api_key = loadMySecretApiKey()
35
+ my_region = "us-west-2"
36
+
37
+ events_client = Apptrail::ApptrailEventsClient.new(
38
+ region: my_region,
39
+ apiKey: my_api_key,
40
+ )
41
+ ```
42
+
43
+ ## Sending an event
44
+
45
+ ```ruby
46
+ event = {
47
+ tenantId: "cust_MGY4MmYzNDMtZjEwOC00OWI",
48
+ eventName: "CreateRepository",
49
+ eventTime: "2022-01-26T06:01:00Z",
50
+ actor: {
51
+ id: "acct_MmRlODllZDctM2I0Yi0",
52
+ details: {
53
+ type: "account",
54
+ name: "API Access",
55
+ },
56
+ },
57
+ resources: [
58
+ {
59
+ id: "repo_YWI5NjkzY2UtNzI1Ny00N",
60
+ details: {
61
+ repositoryType: "V2",
62
+ },
63
+ },
64
+ ],
65
+ sourceIpAddress: "103.6.179.245",
66
+ userAgent: "Apache-HttpClient/4.5.3 (Java/11.0.11)",
67
+ tags: {
68
+ severity: "LOW",
69
+ },
70
+ eventDetails: {
71
+ request: {
72
+ repositoryName: "my-repository",
73
+ },
74
+ },
75
+ }
76
+
77
+ events_client.putEvent(event)
78
+ ```
79
+
80
+ ## Handling errors
81
+
82
+ As a best practice, you should handle errors while sending events, especially if you are sending critical logs. The Events client includes automatic retries with backoff, but errors can happen due to rare server issues or client side issues.
83
+
84
+ You can choose what to do with failing events. For example, you may sideline them to disk, or a dead letter queue for retry or remediation.
85
+
86
+ ```ruby
87
+ begin
88
+ events_client.putEvent(event);
89
+ rescue ApptrailError => e
90
+ puts e
91
+ # handle error
92
+ end
93
+ ```
@@ -0,0 +1,170 @@
1
+ require 'logger'
2
+ require 'stringio'
3
+ require "base64"
4
+ require "json"
5
+ require 'securerandom'
6
+ require "http"
7
+ require "json-schema"
8
+ require "retriable"
9
+
10
+ #HTTP.default_options = HTTP::Options.new(features: {logging: { logger: Logger.new(STDOUT)}})
11
+
12
+ module Apptrail
13
+ class << self
14
+ # Defines the logger used for debugging for the Apptrail module.
15
+ # For example, log to STDOUT by setting this to Logger.new(STDOUT).
16
+ #
17
+ # @return [Logger]
18
+ attr_accessor :logger
19
+
20
+ def logger
21
+ @logger ||= Logger.new($stdout).tap do |log|
22
+ log.progname = self.name
23
+ end
24
+ end
25
+
26
+
27
+ end
28
+
29
+ class ApptrailError < StandardError; end
30
+ class ApptrailRetryableError < ApptrailError; end
31
+
32
+ Retriable.configure do |c|
33
+ c.contexts[:s3] = {
34
+ tries: 3,
35
+ base_interval: 0.04,
36
+ on: Apptrail::ApptrailRetryableError,
37
+ on_retry: Proc.new { puts 'Retrying...' },
38
+ max_interval: 0.12,
39
+ max_elapsed_time: 4,
40
+ rand_factor: 0.2,
41
+ }
42
+ end
43
+
44
+ # You can use the Apptrail Application Events Client to send
45
+ # audit log events from your Ruby applications.
46
+ #
47
+ # See [Sending events](https://apptrail.com/docs/applications/guide/working-with-events/overview).
48
+ class ApptrailEventsClient
49
+ @_form_data = nil
50
+ @_upload_url = nil
51
+
52
+ SCHEMA = JSON.parse(File.read(
53
+ File.expand_path('../raw-event-schema.json', __FILE__)
54
+ )).freeze
55
+ private_constant :SCHEMA
56
+
57
+ def inspect
58
+ "#<ApptrailEventsClient:#{object_id}>"
59
+ end
60
+
61
+ def to_s
62
+ "ApptrailEventsClient{}"
63
+ end
64
+
65
+ def initialize(region:, api_key:)
66
+ @_region = region
67
+ begin
68
+ parsed_key = Base64.urlsafe_decode64(api_key)
69
+ application_id, *_ = parsed_key.split(',', 3)
70
+ @_application_id = application_id
71
+ rescue
72
+ raise ArgumentError, "Invalid API Key."
73
+ end
74
+
75
+ @_base_api_url = "https://events.#{region}.apptrail.com/applications/session"
76
+ @_api_key = api_key
77
+ end
78
+
79
+ # Send a single audit event to log to Apptrail.
80
+ #
81
+ # @param events [Array<ApptrailEvent>]] An array of events to log. See [Apptrail Event Format](https://apptrail.com/docs/applications/guide/event-format) for a full description of the event format.
82
+ def put_event(event)
83
+ put_events([event])
84
+ end
85
+
86
+
87
+ # Send a list of up to 1000 audit events to log to Apptrail.
88
+ #
89
+ # @param events [Array<ApptrailEvent>]] An array of events to log. See [Apptrail Event Format](https://apptrail.com/docs/applications/guide/event-format) for a full description of the event format.
90
+ def put_events(events)
91
+ unless events.is_a?(Array)
92
+ raise TypeError, "Must pass in array of events."
93
+ end
94
+ unless events.length <= 1000
95
+ raise ArgumentError, "Can not send more than 1000 events in a single PutEvents call."
96
+ end
97
+
98
+ begin
99
+ JSON::Validator.validate!(SCHEMA, events, :list => true, :parse_data => false)
100
+ rescue JSON::Schema::ValidationError => e
101
+ raise Apptrail::ApptrailError.new(e.message)
102
+ end
103
+
104
+ if @_upload_url.nil? || @_form_data.nil?
105
+ _refresh_post_policy()
106
+ end
107
+
108
+ content = ""
109
+ events.each do |evt|
110
+ content << JSON.generate(evt)
111
+ content << "\n"
112
+ end
113
+
114
+ filename = SecureRandom.uuid + ".jsonl"
115
+ s3_key = File.join(@_application_id, filename)
116
+
117
+ form_file = HTTP::FormData::File.new(StringIO.new(content), :filename => filename)
118
+
119
+ new_form_opts = {
120
+ **@_form_data,
121
+ :key => s3_key,
122
+ :file => form_file
123
+ }
124
+
125
+ begin
126
+ Retriable.with_context(:s3) do
127
+ resp = HTTP.post(@_upload_url, :form => new_form_opts)
128
+ if !resp.status.success?
129
+ if (resp.status.server_error?)
130
+ raise ApptrailRetryableError.new("Server Error while putting events.")
131
+ elsif (resp.status.client_error?)
132
+ if (resp.status.code === 403 && resp.body.to_s.include?("Policy expired"))
133
+ _refresh_post_policy()
134
+ raise ApptrailRetryableError.new("Session expired.")
135
+ else
136
+ puts resp.body.to_s
137
+ raise ApptrailError.new("Error while putting events.")
138
+ end
139
+ else
140
+ raise ApptrailError.new("Error while putting events.")
141
+ end
142
+ end
143
+ end
144
+ rescue
145
+ raise ApptrailError.new("Failed to put #{events.length} events. Encountered error.")
146
+ else
147
+ Apptrail::logger.info("Successfully wrote #{events.length} events.")
148
+ end
149
+
150
+ end
151
+
152
+ private
153
+
154
+ def _refresh_post_policy
155
+ puts @_base_api_url
156
+ resp = HTTP
157
+ .auth("Bearer #{@_api_key}")
158
+ .get(@_base_api_url)
159
+ if !resp.status.success?
160
+ raise ApptrailError.new("Error refreshing credentials, status code: #{resp.code}")
161
+ end
162
+ result = JSON.parse(resp.body.to_s)
163
+ @_upload_url = result["uploadUrl"]
164
+ @_form_data = result["form"]
165
+ end
166
+
167
+
168
+ end
169
+
170
+ end
@@ -0,0 +1,89 @@
1
+ {
2
+ "type": "object",
3
+ "description": "An Apptrail event is a record of a an activity performed on your customer resources in your application. \n\n @see https://apptrail.com/docs/applications/guide/working-with-events/overview",
4
+ "properties": {
5
+ "tenantId": {
6
+ "type": "string",
7
+ "pattern": "^[A-Za-z0-9_\\-+=]*$",
8
+ "description": "The ID of the tenant this event is associated with and will be delivered to. For more on tenants @see https://apptrail.com/docs/applications/guide/working-with-tenants."
9
+ },
10
+ "eventTime": {
11
+ "type": "string",
12
+ "format": "date-time",
13
+ "description": "The time the event occurred. Formatted as an ISO 8601 formatted string, e.g. 2021-12-06T16:09:38Z."
14
+ },
15
+ "eventName": {
16
+ "type": "string",
17
+ "description": "The name of the event as a string. You should represent unique events with unique names. For example if you are adding audit logs to an API, event names could correspond to your API methods."
18
+ },
19
+ "actor": {
20
+ "type": "object",
21
+ "description": "Actor specifies the identity that performed the action being logged. This may be a user or machine client. Consumers can filter based on this attribute.",
22
+ "required": [
23
+ "id"
24
+ ],
25
+ "properties": {
26
+ "id": {
27
+ "type": "string",
28
+ "description": "A string representing the identity of the actor."
29
+ },
30
+ "details": {
31
+ "type": "object",
32
+ "description": "Additional arbitrary metadata related to the actor."
33
+ }
34
+ }
35
+ },
36
+ "resources": {
37
+ "type": "array",
38
+ "description": "If applicable, contains information about the relevant resources that the action this event is recording is related to.",
39
+ "items": {
40
+ "type": "object",
41
+ "required": [
42
+ "id"
43
+ ],
44
+ "properties": {
45
+ "id": {
46
+ "type": "string",
47
+ "description": "A unique identifier for this resource."
48
+ },
49
+ "details": {
50
+ "type": "object",
51
+ "description": "Additional arbitrary metadata related to the resource."
52
+ }
53
+ }
54
+ }
55
+ },
56
+ "sourceIpAddress": {
57
+ "type": "string",
58
+ "description": "The IP address the activity/request being recorded was made from.",
59
+ "oneOf": [
60
+ {
61
+ "format": "ipv4"
62
+ },
63
+ {
64
+ "format": "ipv6"
65
+ }
66
+ ]
67
+ },
68
+ "userAgent": {
69
+ "type": "string",
70
+ "description": "The agent through which the request was made, e.g. `Mozilla/5.0` or `python-requests/2.20.1`."
71
+ },
72
+ "eventDetails": {
73
+ "type": "object",
74
+ "description": "Any additional custom information as a JSON object can be included in this field."
75
+ },
76
+ "tags": {
77
+ "type": "object",
78
+ "description": "Tags are key value metadata that you associate with events. Users can search and filter events based on these tags.",
79
+ "additionalProperties": {
80
+ "type": "string"
81
+ }
82
+ }
83
+ },
84
+ "required": [
85
+ "eventTime",
86
+ "eventName",
87
+ "tenantId"
88
+ ]
89
+ }
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apptrail-application-events-sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Apptrail Team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-01-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: retriable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json-schema
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.8'
55
+ description: Send audit logs from your Ruby applications using the Apptrail Application
56
+ Events SDK for Ruby.
57
+ email: support@apptrail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - README.md
63
+ - lib/apptrail-application-events-sdk.rb
64
+ - lib/raw-event-schema.json
65
+ homepage: https://apptrail.com/docs/applications/guide
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubygems_version: 3.3.3
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Apptrail Application Events SDK for Ruby
88
+ test_files: []