bullet_train-outgoing_webhooks 1.0.2 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/app/assets/config/{bullet_train_outgoing_webhooks_web_manifest.js → bullet_train_outgoing_webhooks_manifest.js} +0 -0
  4. data/app/controllers/account/webhooks/outgoing/endpoints_controller.rb +4 -3
  5. data/app/controllers/api/v1/webhooks/outgoing/deliveries_endpoint.rb +63 -0
  6. data/app/controllers/api/v1/webhooks/outgoing/delivery_attempts_endpoint.rb +65 -0
  7. data/app/controllers/api/v1/webhooks/outgoing/endpoints_endpoint.rb +119 -0
  8. data/app/jobs/webhooks/outgoing/delivery_job.rb +7 -0
  9. data/app/jobs/webhooks/outgoing/generate_job.rb +7 -0
  10. data/app/models/concerns/webhooks/outgoing/delivery_attempt_support.rb +64 -0
  11. data/app/models/concerns/webhooks/outgoing/delivery_support.rb +66 -0
  12. data/app/models/concerns/webhooks/outgoing/endpoint_support.rb +37 -0
  13. data/app/models/concerns/webhooks/outgoing/event_support.rb +45 -0
  14. data/app/models/concerns/webhooks/outgoing/issuing_model.rb +67 -0
  15. data/app/models/concerns/webhooks/outgoing/team_support.rb +31 -0
  16. data/app/models/concerns/webhooks/outgoing/uri_filtering.rb +149 -0
  17. data/app/models/webhooks/outgoing/delivery.rb +20 -0
  18. data/app/models/webhooks/outgoing/delivery_attempt.rb +20 -0
  19. data/app/models/webhooks/outgoing/endpoint.rb +20 -0
  20. data/app/models/webhooks/outgoing/event.rb +3 -0
  21. data/app/models/webhooks/outgoing/event_type.rb +13 -0
  22. data/app/models/webhooks/outgoing.rb +5 -0
  23. data/app/models/webhooks.rb +5 -0
  24. data/app/serializers/api/v1/webhooks/outgoing/delivery_attempt_serializer.rb +16 -0
  25. data/app/serializers/api/v1/webhooks/outgoing/delivery_serializer.rb +14 -0
  26. data/app/serializers/api/v1/webhooks/outgoing/endpoint_serializer.rb +14 -0
  27. data/app/views/account/webhooks/outgoing/deliveries/_menu_item.html.erb +2 -2
  28. data/app/views/account/webhooks/outgoing/delivery_attempts/_menu_item.html.erb +2 -2
  29. data/app/views/account/webhooks/outgoing/endpoints/_breadcrumbs.html.erb +1 -1
  30. data/app/views/account/webhooks/outgoing/endpoints/_form.html.erb +2 -2
  31. data/app/views/account/webhooks/outgoing/endpoints/_index.html.erb +3 -3
  32. data/app/views/account/webhooks/outgoing/endpoints/_menu_item.html.erb +2 -2
  33. data/app/views/account/webhooks/outgoing/endpoints/show.html.erb +1 -1
  34. data/config/routes.rb +1 -1
  35. data/db/migrate/20180420013127_create_webhooks_outgoing_endpoints.rb +10 -0
  36. data/db/migrate/20180420013505_create_webhooks_outgoing_endpoint_event_types.rb +10 -0
  37. data/db/migrate/20180420013852_create_webhooks_outgoing_event_types.rb +9 -0
  38. data/db/migrate/20180420014623_create_webhooks_outgoing_events.rb +12 -0
  39. data/db/migrate/20180420021016_create_webhooks_outgoing_deliveries.rb +11 -0
  40. data/db/migrate/20180420022027_add_team_to_webhooks_outgoing_events.rb +5 -0
  41. data/db/migrate/20180420165345_create_webhooks_outgoing_delivery_attempts.rb +11 -0
  42. data/db/migrate/20180420172112_add_error_message_to_webhooks_outgoing_delivery_attempt.rb +6 -0
  43. data/db/migrate/20181012234232_add_name_to_webhooks_outgoing_endpoints.rb +5 -0
  44. data/db/migrate/20181013030208_add_delivered_at_to_webhooks_outgoing_deliveries.rb +5 -0
  45. data/db/migrate/20181013165056_add_uuid_to_webhooks_outgoing_events.rb +5 -0
  46. data/db/migrate/20181013174539_add_payload_to_webhooks_outgoing_events.rb +5 -0
  47. data/db/migrate/20181013192951_add_attempt_number_to_webhooks_outgoing_delivery_attempts.rb +5 -0
  48. data/db/migrate/20210805060451_change_body_to_data_on_webhooks_outgoing_events.rb +5 -0
  49. data/db/migrate/20211126230846_add_event_type_ids_to_webhooks_outgoing_endpoints.rb +5 -0
  50. data/db/migrate/20211126230847_migrate_event_type_ids_on_webhooks_outgoing_endpoints.rb +8 -0
  51. data/db/migrate/20211127013539_remove_event_type_from_webhooks_outgoing_events.rb +5 -0
  52. data/db/migrate/20211127013800_add_new_event_type_id_to_webhooks_outgoing_events.rb +5 -0
  53. data/db/migrate/20211127014001_migrate_event_types_on_webhooks_outgoing_events.rb +10 -0
  54. data/db/migrate/20211127015712_drop_webhooks_outgoing_endpoint_event_types.rb +9 -0
  55. data/db/migrate/20211127015713_drop_webhooks_outgoing_event_types.rb +9 -0
  56. data/lib/bullet_train/outgoing_webhooks/engine.rb +32 -0
  57. data/lib/bullet_train/outgoing_webhooks/version.rb +1 -1
  58. data/lib/bullet_train/outgoing_webhooks.rb +28 -3
  59. metadata +81 -8
@@ -0,0 +1,149 @@
1
+ require "resolv"
2
+ require "public_suffix"
3
+
4
+ module Webhooks::Outgoing::UriFiltering
5
+ extend ActiveSupport::Concern
6
+
7
+ # WEBHOOK SECURITY PRIMER
8
+ # =============================================================================
9
+ # Outgoing webhooks can be dangerous. By allowing your users to set
10
+ # up outgoing webhooks, you"re giving them permission to call arbitrary
11
+ # URLs from your server, including URLs that could represent resources
12
+ # internal to your company. Malicious actors can use this permission to
13
+ # examine your infrastructure, call internal APIs, and generally cause
14
+ # havok.
15
+
16
+ # This module attempts to block malicious actors with the following algorithm
17
+ # 1. Block anything but http and https requests
18
+ # 2. Block or allow defined hostnames, both regex and strings
19
+ # 3. Block if `custom_block_callback` returns true (args: self, uri)
20
+ # 4. Allow if `custom_allow_callback` returns true (args: self, uri)
21
+ # 5. Resolve the IP associated with the webhook"s host directly from
22
+ # the authoritative name server for the host"s domain. This IP
23
+ # is cached for the returned DNS TTL
24
+ # 6. Match the given IP against lists of allowed and blocked cidr ranges.
25
+ # The blocked list by default includes all of the defined private address
26
+ # ranges, localhost, the private IPv6 prefix, and the AWS metadata
27
+ # API endpoint.
28
+
29
+ # If at any point a URI is determined to be blocked we call `audit_callback`
30
+ # (args: self, uri) so it can be logged for auditing.
31
+
32
+ # We resolve the IP from the authoritative name server directly so we can avoid
33
+ # certain classes of DNS poisoning attacks.
34
+
35
+ # Users of this gem are _strongly_ enouraged to add additional cidr ranges
36
+ # and hostnames to the appropriate lists and/or implement `custom_block_callback`.
37
+ # At the very least you should add the public hostname that your
38
+ # application uses to the blocked_hostnames list.
39
+
40
+ class AllowedUriValidator < ActiveModel::EachValidator
41
+ def validate_each(record, attribute, value)
42
+ uri = URI.parse(value)
43
+ unless record.allowed_uri?(uri)
44
+ record.errors.add attribute, "is not an allowed uri"
45
+ end
46
+ end
47
+ end
48
+
49
+ def resolve_ip_from_authoritative(hostname)
50
+ begin
51
+ ip = IPAddr.new(hostname)
52
+ return ip.to_s
53
+ rescue IPAddr::InvalidAddressError
54
+ # this is fine, proceed with resolver path
55
+ end
56
+
57
+ cache_key = "#{cache_key_with_version}/uri_ip/#{Digest::SHA2.hexdigest(hostname)}"
58
+
59
+ cached = Rails.cache.read(cache_key)
60
+ if cached
61
+ return cached == "invalid" ? nil : cached
62
+ end
63
+
64
+ begin
65
+ # This is sort of a half-recursive DNS resolver.
66
+ # We can't implement a full recursive resolver using just Resolv::DNS so instead
67
+ # this asks a public cache for the NS record for the given domain. Then it asks
68
+ # the authoritative nameserver directly for the address and caches it according
69
+ # to the returned TTL.
70
+
71
+ config = Rails.configuration.outgoing_webhooks
72
+ ns_resolver = Resolv::DNS.new(nameserver: config[:public_resolvers])
73
+ ns_resolver.timeouts = 1
74
+
75
+ domain = PublicSuffix.domain(hostname)
76
+ authoritative = ns_resolver.getresource(domain, Resolv::DNS::Resource::IN::NS)
77
+
78
+ authoritative_resolver = Resolv::DNS.new(nameserver: [authoritative.name.to_s])
79
+ authoritative_resolver.timeouts = 1
80
+
81
+ resource = authoritative_resolver.getresource(hostname, Resolv::DNS::Resource::IN::A)
82
+ Rails.cache.write(cache_key, resource.address.to_s, expires_in: resource.ttl, race_condition_ttl: 5)
83
+ resource.address.to_s
84
+ rescue IPAddr::InvalidAddressError, ArgumentError # standard:disable Lint/ShadowedException
85
+ Rails.cache.write(cache_key, "invalid", expires_in: 10.minutes, race_condition_ttl: 5)
86
+ nil
87
+ end
88
+ end
89
+
90
+ def allowed_uri?(uri)
91
+ unless _allowed_uri?(uri)
92
+ config = Rails.configuration.outgoing_webhooks
93
+ if config[:audit_callback].present?
94
+ config[:audit_callback].call(self, uri)
95
+ end
96
+ return false
97
+ end
98
+
99
+ true
100
+ end
101
+
102
+ def _allowed_uri?(uri)
103
+ config = Rails.configuration.outgoing_webhooks
104
+ hostname = uri.hostname.downcase
105
+
106
+ return false unless config[:allowed_schemes].include?(uri.scheme)
107
+
108
+ config[:blocked_hostnames].each do |blocked|
109
+ if blocked.is_a?(Regexp)
110
+ return false if blocked.match?(hostname)
111
+ end
112
+
113
+ return false if blocked == hostname
114
+ end
115
+
116
+ config[:allowed_hostnames].each do |allowed|
117
+ if allowed.is_a?(Regexp)
118
+ return true if allowed.match?(hostname)
119
+ end
120
+
121
+ return true if allowed == hostname
122
+ end
123
+
124
+ if config[:custom_allow_callback].present?
125
+ return true if config[:custom_allow_callback].call(self, uri)
126
+ end
127
+
128
+ if config[:custom_block_callback].present?
129
+ return false if config[:custom_block_callback].call(self, uri)
130
+ end
131
+
132
+ resolved_ip = resolve_ip_from_authoritative(hostname)
133
+ return false if resolved_ip.nil?
134
+
135
+ begin
136
+ config[:allowed_cidrs].each do |cidr|
137
+ return true if IPAddr.new(cidr).include?(resolved_ip)
138
+ end
139
+
140
+ config[:blocked_cidrs].each do |cidr|
141
+ return false if IPAddr.new(cidr).include?(resolved_ip)
142
+ end
143
+ rescue IPAddr::InvalidAddressError
144
+ return false
145
+ end
146
+
147
+ true
148
+ end
149
+ end
@@ -0,0 +1,20 @@
1
+ class Webhooks::Outgoing::Delivery < BulletTrain::OutgoingWebhooks.base_class.constantize
2
+ include Webhooks::Outgoing::DeliverySupport
3
+ # 🚅 add concerns above.
4
+
5
+ # 🚅 add belongs_to associations above.
6
+
7
+ # 🚅 add has_many associations above.
8
+
9
+ # 🚅 add has_one associations above.
10
+
11
+ # 🚅 add scopes above.
12
+
13
+ # 🚅 add validations above.
14
+
15
+ # 🚅 add callbacks above.
16
+
17
+ # 🚅 add delegations above.
18
+
19
+ # 🚅 add methods above.
20
+ end
@@ -0,0 +1,20 @@
1
+ class Webhooks::Outgoing::DeliveryAttempt < BulletTrain::OutgoingWebhooks.base_class.constantize
2
+ include Webhooks::Outgoing::DeliveryAttemptSupport
3
+ # 🚅 add concerns above.
4
+
5
+ # 🚅 add belongs_to associations above.
6
+
7
+ # 🚅 add has_many associations above.
8
+
9
+ # 🚅 add has_one associations above.
10
+
11
+ # 🚅 add scopes above.
12
+
13
+ # 🚅 add validations above.
14
+
15
+ # 🚅 add callbacks above.
16
+
17
+ # 🚅 add delegations above.
18
+
19
+ # 🚅 add methods above.
20
+ end
@@ -0,0 +1,20 @@
1
+ class Webhooks::Outgoing::Endpoint < BulletTrain::OutgoingWebhooks.base_class.constantize
2
+ include Webhooks::Outgoing::EndpointSupport
3
+ # 🚅 add concerns above.
4
+
5
+ # 🚅 add belongs_to associations above.
6
+
7
+ # 🚅 add has_many associations above.
8
+
9
+ # 🚅 add has_one associations above.
10
+
11
+ # 🚅 add scopes above.
12
+
13
+ # 🚅 add validations above.
14
+
15
+ # 🚅 add callbacks above.
16
+
17
+ # 🚅 add delegations above.
18
+
19
+ # 🚅 add methods above.
20
+ end
@@ -0,0 +1,3 @@
1
+ class Webhooks::Outgoing::Event < BulletTrain::OutgoingWebhooks.base_class.constantize
2
+ include Webhooks::Outgoing::EventSupport
3
+ end
@@ -0,0 +1,13 @@
1
+ class Webhooks::Outgoing::EventType < ApplicationHash
2
+ self.data = YAML.load_file("config/models/webhooks/outgoing/event_types.yml").map do |topic, events|
3
+ events.map { |event| event == "crud" ? ["created", "updated", "deleted"] : event }.flatten.map { |event| {id: "#{topic}.#{event}"} }
4
+ end.flatten
5
+
6
+ def label_string
7
+ name
8
+ end
9
+
10
+ def name
11
+ id
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Webhooks::Outgoing
2
+ def self.table_name_prefix
3
+ "webhooks_outgoing_"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Webhooks
2
+ def self.table_name_prefix
3
+ "webhooks_"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ class Api::V1::Webhooks::Outgoing::DeliveryAttemptSerializer < Api::V1::ApplicationSerializer
2
+ set_type "webhooks/outgoing/delivery_attempt"
3
+
4
+ attributes :id,
5
+ :delivery_id,
6
+ :response_code,
7
+ :response_body,
8
+ :response_message,
9
+ :error_message,
10
+ :attempt_number,
11
+ # 🚅 super scaffolding will insert new fields above this line.
12
+ :created_at,
13
+ :updated_at
14
+
15
+ belongs_to :delivery, serializer: Api::V1::Webhooks::Outgoing::DeliverySerializer
16
+ end
@@ -0,0 +1,14 @@
1
+ class Api::V1::Webhooks::Outgoing::DeliverySerializer < Api::V1::ApplicationSerializer
2
+ set_type "webhooks/outgoing/delivery"
3
+
4
+ attributes :id,
5
+ :endpoint_id,
6
+ :event_id,
7
+ :endpoint_url,
8
+ :delivered_at,
9
+ # 🚅 super scaffolding will insert new fields above this line.
10
+ :created_at,
11
+ :updated_at
12
+
13
+ belongs_to :endpoint, serializer: Api::V1::Webhooks::Outgoing::EndpointSerializer
14
+ end
@@ -0,0 +1,14 @@
1
+ class Api::V1::Webhooks::Outgoing::EndpointSerializer < Api::V1::ApplicationSerializer
2
+ set_type "webhooks/outgoing/endpoint"
3
+
4
+ attributes :id,
5
+ :team_id,
6
+ :name,
7
+ :url,
8
+ :event_type_ids,
9
+ # 🚅 super scaffolding will insert new fields above this line.
10
+ :created_at,
11
+ :updated_at
12
+
13
+ belongs_to :team, serializer: Api::V1::TeamSerializer
14
+ end
@@ -1,6 +1,6 @@
1
- <% if can? :read, Webhooks::Outgoing::Delivery.new(team: current_team) %>
1
+ <% if can? :read, Webhooks::Outgoing::Delivery.new(BulletTrain::OutgoingWebhooks.parent_association => send(BulletTrain::OutgoingWebhooks.current_parent_method)) %>
2
2
  <%= render 'account/shared/menu/item', {
3
- url: main_app.polymorphic_path([:account, current_team, :webhooks_outgoing_deliveries]),
3
+ url: main_app.polymorphic_path([:account, send(BulletTrain::OutgoingWebhooks.current_parent_method), :webhooks_outgoing_deliveries]),
4
4
  label: t('webhooks/outgoing/deliveries.navigation.label'),
5
5
  } do |p| %>
6
6
  <% p.content_for :icon do %>
@@ -1,6 +1,6 @@
1
- <% if can? :read, Webhooks::Outgoing::DeliveryAttempt.new(team: current_team) %>
1
+ <% if can? :read, Webhooks::Outgoing::DeliveryAttempt.new(BulletTrain::OutgoingWebhooks.parent_association => send(BulletTrain::OutgoingWebhooks.current_parent_method)) %>
2
2
  <%= render 'account/shared/menu/item', {
3
- url: main_app.polymorphic_path([:account, current_team, :webhooks_outgoing_delivery_attempts]),
3
+ url: main_app.polymorphic_path([:account, send(BulletTrain::OutgoingWebhooks.current_parent_method), :webhooks_outgoing_delivery_attempts]),
4
4
  label: t('webhooks/outgoing/delivery_attempts.navigation.label'),
5
5
  } do |p| %>
6
6
  <% p.content_for :icon do %>
@@ -1,5 +1,5 @@
1
1
  <% endpoint ||= @endpoint %>
2
- <% team ||= @team || endpoint&.team %>
2
+ <% team ||= @parent || endpoint&.send(BulletTrain::OutgoingWebhooks.parent_association) %>
3
3
  <%= render 'account/teams/breadcrumbs', team: team %>
4
4
  <%= render 'account/shared/breadcrumb', label: t('.label'), url: [:account, team, :webhooks_outgoing_endpoints] %>
5
5
  <% if endpoint&.persisted? %>
@@ -1,4 +1,4 @@
1
- <%= form_with model: endpoint, url: (endpoint.persisted? ? [:account, endpoint] : [:account, @team, :webhooks_outgoing_endpoints]), local: true, class: 'form' do |form| %>
1
+ <%= form_with model: endpoint, url: (endpoint.persisted? ? [:account, endpoint] : [:account, @parent, :webhooks_outgoing_endpoints]), local: true, class: 'form' do |form| %>
2
2
  <%= render 'account/shared/forms/errors', form: form %>
3
3
 
4
4
  <% with_field_settings form: form do %>
@@ -14,7 +14,7 @@
14
14
  <% if form.object.persisted? %>
15
15
  <%= link_to t('global.buttons.cancel'), [:account, endpoint], class: "button-secondary" %>
16
16
  <% else %>
17
- <%= link_to t('global.buttons.cancel'), [:account, @team, :webhooks_outgoing_endpoints], class: "button-secondary" %>
17
+ <%= link_to t('global.buttons.cancel'), [:account, @parent, :webhooks_outgoing_endpoints], class: "button-secondary" %>
18
18
  <% end %>
19
19
  </div>
20
20
 
@@ -1,4 +1,4 @@
1
- <% team = @team || @team %>
1
+ <% team = @parent %>
2
2
  <% context ||= team %>
3
3
  <% collection ||= :webhooks_outgoing_endpoints %>
4
4
  <% hide_actions ||= false %>
@@ -49,8 +49,8 @@
49
49
  <% p.content_for :actions do %>
50
50
  <% unless hide_actions %>
51
51
  <% if context == team %>
52
- <% if can? :create, Webhooks::Outgoing::Endpoint.new(team: team) %>
53
- <%= link_to t('.buttons.new'), [:new, :account, team, :webhooks_outgoing_endpoint], class: "#{first_button_primary(:webhooks_outgoing_endpoint)} new" %>
52
+ <% if can? :create, Webhooks::Outgoing::Endpoint.new(BulletTrain::OutgoingWebhooks.parent_association => @parent) %>
53
+ <%= link_to t('.buttons.new'), [:new, :account, @parent, :webhooks_outgoing_endpoint], class: "#{first_button_primary(:webhooks_outgoing_endpoint)} new" %>
54
54
  <% end %>
55
55
  <% end %>
56
56
 
@@ -1,6 +1,6 @@
1
- <% if can? :read, Webhooks::Outgoing::Endpoint.new(team: current_team) %>
1
+ <% if can? :read, Webhooks::Outgoing::Endpoint.new(BulletTrain::OutgoingWebhooks.parent_association => send(BulletTrain::OutgoingWebhooks.current_parent_method)) %>
2
2
  <%= render 'account/shared/menu/item', {
3
- url: main_app.polymorphic_path([:account, current_team, :webhooks_outgoing_endpoints]),
3
+ url: main_app.polymorphic_path([:account, send(BulletTrain::OutgoingWebhooks.current_parent_method), :webhooks_outgoing_endpoints]),
4
4
  label: t('webhooks/outgoing/endpoints.navigation.label'),
5
5
  } do |p| %>
6
6
  <% p.content_for :icon do %>
@@ -32,7 +32,7 @@
32
32
  <% p.content_for :actions do %>
33
33
  <%= link_to t('.buttons.edit'), [:edit, :account, @endpoint], class: first_button_primary if can? :edit, @endpoint %>
34
34
  <%= button_to t('.buttons.destroy'), [:account, @endpoint], method: :delete, class: first_button_primary, data: { confirm: t('.buttons.confirmations.destroy', model_locales(@endpoint)) } if can? :destroy, @endpoint %>
35
- <%= link_to t('global.buttons.back'), [:account, @team, :webhooks_outgoing_endpoints], class: first_button_primary %>
35
+ <%= link_to t('global.buttons.back'), [:account, @parent, :webhooks_outgoing_endpoints], class: first_button_primary %>
36
36
  <% end %>
37
37
  <% end %>
38
38
 
data/config/routes.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  Rails.application.routes.draw do
2
2
  namespace :account do
3
3
  shallow do
4
- resources :teams do
4
+ resources BulletTrain::OutgoingWebhooks.parent_resource do
5
5
  namespace :webhooks do
6
6
  namespace :outgoing do
7
7
  resources :events
@@ -0,0 +1,10 @@
1
+ class CreateWebhooksOutgoingEndpoints < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_endpoints do |t|
4
+ t.references :team, foreign_key: true
5
+ t.text :url
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class CreateWebhooksOutgoingEndpointEventTypes < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_endpoint_event_types do |t|
4
+ t.integer :endpoint_id
5
+ t.integer :event_type_id
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class CreateWebhooksOutgoingEventTypes < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_event_types do |t|
4
+ t.string :name
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ class CreateWebhooksOutgoingEvents < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_events do |t|
4
+ t.integer :event_type_id
5
+ t.integer :subject_id
6
+ t.string :subject_type
7
+ t.jsonb :body
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ class CreateWebhooksOutgoingDeliveries < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_deliveries do |t|
4
+ t.integer :endpoint_id
5
+ t.integer :event_id
6
+ t.text :endpoint_url
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ class AddTeamToWebhooksOutgoingEvents < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_reference :webhooks_outgoing_events, :team, foreign_key: true
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ class CreateWebhooksOutgoingDeliveryAttempts < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_delivery_attempts do |t|
4
+ t.integer :delivery_id
5
+ t.integer :response_code
6
+ t.text :response_body
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ class AddErrorMessageToWebhooksOutgoingDeliveryAttempt < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_delivery_attempts, :response_message, :text
4
+ add_column :webhooks_outgoing_delivery_attempts, :error_message, :text
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddNameToWebhooksOutgoingEndpoints < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_endpoints, :name, :string
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddDeliveredAtToWebhooksOutgoingDeliveries < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_deliveries, :delivered_at, :datetime
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddUuidToWebhooksOutgoingEvents < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_events, :uuid, :string
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddPayloadToWebhooksOutgoingEvents < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_events, :payload, :jsonb
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddAttemptNumberToWebhooksOutgoingDeliveryAttempts < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_delivery_attempts, :attempt_number, :integer
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class ChangeBodyToDataOnWebhooksOutgoingEvents < ActiveRecord::Migration[6.1]
2
+ def change
3
+ rename_column :webhooks_outgoing_events, :body, :data
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddEventTypeIdsToWebhooksOutgoingEndpoints < ActiveRecord::Migration[6.1]
2
+ def change
3
+ add_column :webhooks_outgoing_endpoints, :event_type_ids, :jsonb, default: []
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class MigrateEventTypeIdsOnWebhooksOutgoingEndpoints < ActiveRecord::Migration[6.1]
2
+ def change
3
+ Webhooks::Outgoing::Endpoint.find_each do |endpoint|
4
+ event_type_ids = ActiveRecord::Base.connection.execute("SELECT * FROM webhooks_outgoing_endpoint_event_types JOIN webhooks_outgoing_event_types ON webhooks_outgoing_endpoint_event_types.event_type_id = webhooks_outgoing_event_types.id WHERE endpoint_id = #{endpoint.id}").to_a.map { |result| result.dig("name") }
5
+ endpoint.update(event_type_ids: event_type_ids)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ class RemoveEventTypeFromWebhooksOutgoingEvents < ActiveRecord::Migration[6.1]
2
+ def change
3
+ remove_reference :webhooks_outgoing_events, :event_type, null: false, foreign_key: false
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddNewEventTypeIdToWebhooksOutgoingEvents < ActiveRecord::Migration[6.1]
2
+ def change
3
+ add_column :webhooks_outgoing_events, :event_type_id, :string
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ class MigrateEventTypesOnWebhooksOutgoingEvents < ActiveRecord::Migration[6.1]
2
+ def up
3
+ Webhooks::Outgoing::Event.find_each do |event|
4
+ event.update_column(:event_type_id, event.payload.dig("event_type"))
5
+ end
6
+ end
7
+
8
+ def down
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class DropWebhooksOutgoingEndpointEventTypes < ActiveRecord::Migration[6.1]
2
+ def up
3
+ drop_table :webhooks_outgoing_endpoint_event_types
4
+ end
5
+
6
+ def down
7
+ raise ActiveRecord::IrreversibleMigration
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class DropWebhooksOutgoingEventTypes < ActiveRecord::Migration[6.1]
2
+ def up
3
+ drop_table :webhooks_outgoing_event_types
4
+ end
5
+
6
+ def down
7
+ raise ActiveRecord::IrreversibleMigration
8
+ end
9
+ end
@@ -1,6 +1,38 @@
1
1
  module BulletTrain
2
2
  module OutgoingWebhooks
3
3
  class Engine < ::Rails::Engine
4
+ config.before_configuration do
5
+ default_blocked_cidrs = %w[
6
+ 10.0.0.0/8
7
+ 172.16.0.0/12
8
+ 192.168.0.0/16
9
+ 100.64.0.0/10
10
+ 127.0.0.0/8
11
+ 169.254.169.254/32
12
+ fc00::/7
13
+ ::1
14
+ ]
15
+
16
+ config.outgoing_webhooks = {
17
+ blocked_cidrs: default_blocked_cidrs,
18
+ allowed_cidrs: [],
19
+ blocked_hostnames: %w[localhost],
20
+ allowed_hostnames: [],
21
+ public_resolvers: %w[8.8.8.8 1.1.1.1],
22
+ allowed_schemes: %w[http https],
23
+ custom_block_callback: nil,
24
+ custom_allow_callback: nil,
25
+ audit_callback: ->(obj, uri) { Rails.logger.error("BlockedURI obj=#{obj.persisted? ? obj.to_global_id : "New #{obj.class}"} uri=#{uri}") }
26
+ }
27
+ end
28
+
29
+ initializer "bullet_train.outgoing_webhooks.register_api_endpoints" do |app|
30
+ if Object.const_defined?("BulletTrain::Api")
31
+ BulletTrain::Api.endpoints << "Api::V1::Webhooks::Outgoing::EndpointsEndpoint"
32
+ BulletTrain::Api.endpoints << "Api::V1::Webhooks::Outgoing::DeliveriesEndpoint"
33
+ BulletTrain::Api.endpoints << "Api::V1::Webhooks::Outgoing::DeliveryAttemptsEndpoint"
34
+ end
35
+ end
4
36
  end
5
37
  end
6
38
  end
@@ -1,5 +1,5 @@
1
1
  module BulletTrain
2
2
  module OutgoingWebhooks
3
- VERSION = "1.0.2"
3
+ VERSION = "1.0.5"
4
4
  end
5
5
  end