sessel 0.0.1
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 +7 -0
- data/.gitignore +50 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +9 -0
- data/Rakefile +10 -0
- data/bin/sessel +5 -0
- data/lib/sessel.rb +4 -0
- data/lib/sessel/ask.rb +73 -0
- data/lib/sessel/configuration_set.rb +20 -0
- data/lib/sessel/configuration_set_creator.rb +58 -0
- data/lib/sessel/io.rb +39 -0
- data/lib/sessel/main.rb +70 -0
- data/lib/sessel/receipt_rule.rb +35 -0
- data/lib/sessel/receipt_rule_creator.rb +204 -0
- data/lib/sessel/version.rb +3 -0
- data/sessel.png +0 -0
- data/swaggerless.gemspec +31 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1310706b7c390a978b139bd724238ab1a5d1a467
|
4
|
+
data.tar.gz: 7f543438b2ef606892240352007b57aefab6f826
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b0996edb14235359fb5e33af12e94ef1cf7e128865bd1421a13ef58d7ce6c0aff6e39c12887e6dc5f291158ee075e68721c6966f1efa92ebd31c8f866584afa9
|
7
|
+
data.tar.gz: bdccf81a1e11afe1de0050a7283cb5a0819732c559c69dd0589956d910adbc2522c026e116fe114811fbb0256ba9126d16e034e757dd81b3dac33e3e05752b8c
|
data/.gitignore
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
|
13
|
+
# Used by dotenv library to load environment variables.
|
14
|
+
# .env
|
15
|
+
|
16
|
+
## Specific to RubyMotion:
|
17
|
+
.dat*
|
18
|
+
.repl_history
|
19
|
+
build/
|
20
|
+
*.bridgesupport
|
21
|
+
build-iPhoneOS/
|
22
|
+
build-iPhoneSimulator/
|
23
|
+
|
24
|
+
## Specific to RubyMotion (use of CocoaPods):
|
25
|
+
#
|
26
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
27
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
28
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
29
|
+
#
|
30
|
+
# vendor/Pods/
|
31
|
+
|
32
|
+
## Documentation cache and generated files:
|
33
|
+
/.yardoc/
|
34
|
+
/_yardoc/
|
35
|
+
/doc/
|
36
|
+
/rdoc/
|
37
|
+
|
38
|
+
## Environment normalization:
|
39
|
+
/.bundle/
|
40
|
+
/vendor/bundle
|
41
|
+
/lib/bundler/man/
|
42
|
+
|
43
|
+
# for a library or gem, you might want to ignore these files since the code is
|
44
|
+
# intended to run in multiple environments; otherwise, check them in:
|
45
|
+
# Gemfile.lock
|
46
|
+
# .ruby-version
|
47
|
+
# .ruby-gemset
|
48
|
+
|
49
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
50
|
+
.rvmrc
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Rafal Nowosielski
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# sessel
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
Sessel - an AWS SES configuration tool
|
6
|
+
|
7
|
+
The idea behind this gem is to privide a way to automate configuration of Simple Email Service in AWS, so that the complete solution that one builds in AWS could be stored in the repository and deployed automatically.
|
8
|
+
|
9
|
+
## Installation
|
data/Rakefile
ADDED
data/bin/sessel
ADDED
data/lib/sessel.rb
ADDED
data/lib/sessel/ask.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'highline'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Sessel
|
5
|
+
|
6
|
+
class Ask
|
7
|
+
|
8
|
+
@@cli = HighLine.new
|
9
|
+
@@ses = nil
|
10
|
+
@@s3 = nil
|
11
|
+
|
12
|
+
def self.for_region()
|
13
|
+
region = nil
|
14
|
+
@@cli.choose do |region_menu|
|
15
|
+
region_menu.prompt = "Choose AWS region you want to setup SES in?"
|
16
|
+
region_menu.choices("us-east-1", "us-west-2", "eu-west-1") { |r| region = r }
|
17
|
+
end
|
18
|
+
@@ses = Aws::SES::Client.new(region: region)
|
19
|
+
@@s3 = Aws::S3::Client.new(region: region)
|
20
|
+
return region
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.for_rule_set_name(solution_name)
|
24
|
+
chosen_rule_set_name = nil
|
25
|
+
@@cli.choose do |rule_set_menu|
|
26
|
+
solution_rule_set_name = "#{solution_name}RuleSet"
|
27
|
+
rule_set_menu.prompt = 'Choose receipt rule-set?'
|
28
|
+
resp = @@ses.list_receipt_rule_sets
|
29
|
+
add_new_rule_set = true
|
30
|
+
resp.to_h[:rule_sets].each { |rule_set|
|
31
|
+
rule_set_menu.choice(rule_set[:name]) { chosen_rule_set_name = rule_set[:name] }
|
32
|
+
add_new_rule_set = false if (rule_set[:name] =~ /^#{Regexp.escape(solution_rule_set_name)}$/)
|
33
|
+
}
|
34
|
+
rule_set_menu.choice("Create new: #{solution_rule_set_name}") {
|
35
|
+
chosen_rule_set_name = solution_rule_set_name
|
36
|
+
} if add_new_rule_set
|
37
|
+
end
|
38
|
+
return chosen_rule_set_name
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.for_email_address()
|
42
|
+
return @@cli.ask('What is the email address you would like to receive emails at?') {
|
43
|
+
|q| q.validate = /\w@[a-z0-9_-].[a-z]/ }
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.for_s3_bucket(solution_name, region)
|
47
|
+
chosen_bucket = nil
|
48
|
+
@@cli.choose do |s3_bucket_menu|
|
49
|
+
solution_bucket_name = solution_name.downcase
|
50
|
+
s3_bucket_menu.prompt = "Choose S3 bucket to put the emails in"
|
51
|
+
resp = @@s3.list_buckets()
|
52
|
+
add_new_bucket = true
|
53
|
+
resp.to_h[:buckets].each { |bucket|
|
54
|
+
s3_bucket_menu.choice(bucket[:name]) { chosen_bucket = bucket[:name] }
|
55
|
+
add_new_bucket = false if (bucket[:name] =~ /^#{Regexp.escape(solution_bucket_name)}$/)
|
56
|
+
}
|
57
|
+
s3_bucket_menu.choice("Create new: #{solution_bucket_name}") {
|
58
|
+
@@s3.create_bucket({
|
59
|
+
acl: "private",
|
60
|
+
bucket: solution_bucket_name,
|
61
|
+
create_bucket_configuration: {
|
62
|
+
location_constraint: region
|
63
|
+
}
|
64
|
+
})
|
65
|
+
chosen_bucket = solution_name
|
66
|
+
} if add_new_bucket
|
67
|
+
end
|
68
|
+
return chosen_bucket
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sessel
|
2
|
+
|
3
|
+
class ConfigurationSet
|
4
|
+
|
5
|
+
def initialize(region, configuration_set_name)
|
6
|
+
@configuration_set_name = configuration_set_name
|
7
|
+
@region = region
|
8
|
+
end
|
9
|
+
|
10
|
+
def configuration_set_name
|
11
|
+
return @configuration_set_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def region
|
15
|
+
return @region
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Sessel
|
4
|
+
|
5
|
+
# Takes care of creating the configuration set and making sure all the prerequisites are in place
|
6
|
+
class ConfigurationSetCreator
|
7
|
+
|
8
|
+
def initialize(configuration_set)
|
9
|
+
@configuration_set = configuration_set
|
10
|
+
@ses = Aws::SES::Client.new(region: configuration_set.region)
|
11
|
+
end
|
12
|
+
|
13
|
+
def configuration_set
|
14
|
+
return @configuration_set
|
15
|
+
end
|
16
|
+
|
17
|
+
# Creates the configuration set in SES accoring to the paramteres passed when the object was initialised
|
18
|
+
def create()
|
19
|
+
|
20
|
+
rule = {
|
21
|
+
configuration_set: {
|
22
|
+
name: @configuration_set.configuration_set_name,
|
23
|
+
}
|
24
|
+
}
|
25
|
+
begin
|
26
|
+
@ses.create_configuration_set(rule)
|
27
|
+
rescue Aws::SES::Errors::ConfigurationSetAlreadyExists
|
28
|
+
puts "#{@configuration_set.configuration_set_name} already exists. Skipping."
|
29
|
+
end
|
30
|
+
|
31
|
+
event_destinaton = {
|
32
|
+
configuration_set_name: @configuration_set.configuration_set_name,
|
33
|
+
event_destination: {
|
34
|
+
name: "#{@configuration_set.configuration_set_name}EventName",
|
35
|
+
enabled: true,
|
36
|
+
matching_event_types: ['send', 'reject', 'bounce', 'complaint', 'delivery'],
|
37
|
+
cloud_watch_destination: {
|
38
|
+
dimension_configurations: [
|
39
|
+
{
|
40
|
+
dimension_name: 'To',
|
41
|
+
dimension_value_source: 'emailHeader',
|
42
|
+
default_dimension_value: 'DestinationUnknown'
|
43
|
+
}
|
44
|
+
]
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
begin
|
49
|
+
@ses.create_configuration_set_event_destination(event_destinaton)
|
50
|
+
rescue Aws::SES::Errors::EventDestinationAlreadyExists
|
51
|
+
@ses.update_configuration_set_event_destination(event_destinaton)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/sessel/io.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Sessel
|
2
|
+
|
3
|
+
class IO
|
4
|
+
|
5
|
+
def self.announce()
|
6
|
+
puts 'Question:'
|
7
|
+
ret = yield
|
8
|
+
puts "Answer: #{ret}\n\n"
|
9
|
+
return ret
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.read_config_from_file()
|
13
|
+
config = {}
|
14
|
+
if File.file?(SESSEL_YAML) then
|
15
|
+
config = YAML.load File.read(SESSEL_YAML);
|
16
|
+
end
|
17
|
+
unless config[:receipt_rules] then
|
18
|
+
config[:receipt_rules] = []
|
19
|
+
end
|
20
|
+
unless config[:configuration_sets] then
|
21
|
+
config[:configuration_sets] = []
|
22
|
+
end
|
23
|
+
return config
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.append_receipt_rule_to_file(receipt_rule)
|
27
|
+
config = read_config_from_file
|
28
|
+
config[:receipt_rules].push(receipt_rule)
|
29
|
+
File.open(SESSEL_YAML, 'w') { |file| file.write(config.to_yaml) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.append_configuration_set_to_file(configuration_set)
|
33
|
+
config = read_config_from_file
|
34
|
+
config[:configuration_sets].push(configuration_set)
|
35
|
+
File.open(SESSEL_YAML, 'w') { |file| file.write(config.to_yaml) }
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/sessel/main.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'aws-sdk'
|
3
|
+
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Sessel
|
7
|
+
|
8
|
+
SESSEL_YAML = 'sessel.yaml'
|
9
|
+
|
10
|
+
class Add < Thor
|
11
|
+
|
12
|
+
desc "receipt_rule SOLUTION_NAME", "Start setup of SES receipt rule for your particular SOLUTION_NAME"
|
13
|
+
long_desc <<-LONGDESC
|
14
|
+
Adds a new receipt rule to the configuration.
|
15
|
+
LONGDESC
|
16
|
+
def receipt_rule(solution_name)
|
17
|
+
region = IO.announce { Ask.for_region }
|
18
|
+
chosen_rule_set_name = IO.announce { Ask.for_rule_set_name(solution_name) }
|
19
|
+
email_address = IO.announce { Ask.for_email_address }
|
20
|
+
s3_bucket = IO.announce { Ask.for_s3_bucket(solution_name, region) }
|
21
|
+
rule_creator = Sessel::ReceiptRuleCreator.new(
|
22
|
+
ReceiptRule.new(region, [email_address], s3_bucket, solution_name, chosen_rule_set_name)
|
23
|
+
)
|
24
|
+
rule_creator.create
|
25
|
+
IO.append_receipt_rule_to_file(rule_creator.receipt_rule)
|
26
|
+
puts "That's it!"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "configuration_set SOLUTION_NAME", "Start setup of SES configuration set for your particular SOLUTION_NAME"
|
30
|
+
long_desc <<-LONGDESC
|
31
|
+
Adds a new configuration set into the configuration.
|
32
|
+
LONGDESC
|
33
|
+
def configuration_set(solution_name)
|
34
|
+
region = IO.announce { Ask.for_region }
|
35
|
+
configuration_set_creator = Sessel::ConfigurationSetCreator.new(
|
36
|
+
ConfigurationSet.new(region, "#{solution_name}ConfigurationSet")
|
37
|
+
)
|
38
|
+
configuration_set_creator.create
|
39
|
+
IO.append_configuration_set_to_file(configuration_set_creator.configuration_set)
|
40
|
+
puts "That's it!"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Main < Thor
|
45
|
+
|
46
|
+
option :interactive, :type => :boolean
|
47
|
+
desc 'apply', 'Uses the sessle.yaml to providsion resources in the AWS account'
|
48
|
+
long_desc <<-LONGDESC
|
49
|
+
Uses the configuration stored in sessle.yaml to provision or uptate the recourses in the AWS account
|
50
|
+
LONGDESC
|
51
|
+
def apply
|
52
|
+
config = IO.read_config_from_file
|
53
|
+
config[:receipt_rules].each do |receipt_rule|
|
54
|
+
puts "Applying receipt rule: #{receipt_rule.rule_set_name} / #{receipt_rule.rule_name}"
|
55
|
+
rule_creator = Sessel::ReceiptRuleCreator.new(receipt_rule)
|
56
|
+
rule_creator.create
|
57
|
+
end
|
58
|
+
config[:configuration_sets].each do |configuration_set|
|
59
|
+
puts "Applying configuration set: #{configuration_set.configuration_set_name}"
|
60
|
+
configuration_set_creator = Sessel::ConfigurationSetCreator.new(configuration_set)
|
61
|
+
configuration_set_creator.create
|
62
|
+
end
|
63
|
+
puts 'Done'
|
64
|
+
end
|
65
|
+
|
66
|
+
desc 'add SUBCOMMAND ...ARGS', 'Add configuration items to the sessle.yaml.'
|
67
|
+
subcommand 'add', Add
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Sessel
|
2
|
+
|
3
|
+
class ReceiptRule
|
4
|
+
|
5
|
+
def initialize(region, email_addresses, s3_bucket, rule_name, rule_set_name)
|
6
|
+
@email_addresses = email_addresses
|
7
|
+
@region = region
|
8
|
+
@s3_bucket = s3_bucket
|
9
|
+
@rule_name = rule_name
|
10
|
+
@rule_set_name = rule_set_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def email_addresses
|
14
|
+
return @email_addresses
|
15
|
+
end
|
16
|
+
|
17
|
+
def region
|
18
|
+
return @region
|
19
|
+
end
|
20
|
+
|
21
|
+
def s3_bucket
|
22
|
+
return @s3_bucket
|
23
|
+
end
|
24
|
+
|
25
|
+
def rule_name
|
26
|
+
return @rule_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def rule_set_name
|
30
|
+
return @rule_set_name
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Sessel
|
4
|
+
|
5
|
+
# Takes care of creating the receipt rule and making sure all the prerequisites are in place
|
6
|
+
class ReceiptRuleCreator
|
7
|
+
|
8
|
+
def initialize(receipt_rule)
|
9
|
+
@receipt_rule = receipt_rule
|
10
|
+
@s3 = Aws::S3::Client.new(region: receipt_rule.region)
|
11
|
+
@sts = Aws::STS::Client.new(region: receipt_rule.region)
|
12
|
+
@ses = Aws::SES::Client.new(region: receipt_rule.region)
|
13
|
+
@route53 = Aws::Route53::Client.new(region: receipt_rule.region)
|
14
|
+
end
|
15
|
+
|
16
|
+
def receipt_rule
|
17
|
+
return @receipt_rule
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates the receipt rule in SES accorind to the paramteres passed when the object was initialised
|
21
|
+
def create()
|
22
|
+
|
23
|
+
ensure_prerequsites
|
24
|
+
|
25
|
+
rule = {
|
26
|
+
rule: {
|
27
|
+
recipients: @receipt_rule.email_addresses,
|
28
|
+
actions: [
|
29
|
+
{
|
30
|
+
s3_action: {
|
31
|
+
bucket_name: @receipt_rule.s3_bucket,
|
32
|
+
object_key_prefix: 'email',
|
33
|
+
},
|
34
|
+
},
|
35
|
+
],
|
36
|
+
enabled: true,
|
37
|
+
name: @receipt_rule.rule_name,
|
38
|
+
scan_enabled: false,
|
39
|
+
tls_policy: 'Optional',
|
40
|
+
},
|
41
|
+
rule_set_name: @receipt_rule.rule_set_name,
|
42
|
+
}
|
43
|
+
begin
|
44
|
+
@ses.create_receipt_rule(rule)
|
45
|
+
rescue Aws::SES::Errors::AlreadyExists
|
46
|
+
@ses.update_receipt_rule(rule)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Makes sure that the chosen bucket has the required policy
|
53
|
+
def ensure_s3_bucket_policy()
|
54
|
+
|
55
|
+
resp = @sts.get_caller_identity({})
|
56
|
+
account = resp.to_h[:account]
|
57
|
+
|
58
|
+
#TODO: Merge existing policies
|
59
|
+
policy = {
|
60
|
+
:Version => "2008-10-17",
|
61
|
+
:Statement => [
|
62
|
+
{
|
63
|
+
:Sid => "GiveSESPermissionToWriteEmail",
|
64
|
+
:Effect => "Allow",
|
65
|
+
:Principal => {
|
66
|
+
:Service => [
|
67
|
+
"ses.amazonaws.com"
|
68
|
+
]
|
69
|
+
},
|
70
|
+
:Action => [
|
71
|
+
"s3:PutObject"
|
72
|
+
],
|
73
|
+
:Resource => "arn:aws:s3:::#{@receipt_rule.s3_bucket}/*",
|
74
|
+
:Condition => {
|
75
|
+
:StringEquals => {
|
76
|
+
'aws:Referer' => account
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
]
|
81
|
+
}
|
82
|
+
|
83
|
+
@s3.put_bucket_policy({
|
84
|
+
bucket: @receipt_rule.s3_bucket,
|
85
|
+
policy: policy.to_json
|
86
|
+
})
|
87
|
+
end
|
88
|
+
|
89
|
+
def ensure_address_domains()
|
90
|
+
@receipt_rule.email_addresses.each { |address|
|
91
|
+
ensure_address_domain address
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def ensure_address_domain(address)
|
96
|
+
domain = address.split('@').last
|
97
|
+
|
98
|
+
resp = @route53.list_hosted_zones_by_name({})
|
99
|
+
hosted_zones = resp.to_h[:hosted_zones]
|
100
|
+
hosted_zone = hosted_zones.find { |s| s[:name] == "#{domain}." }
|
101
|
+
unless hosted_zone then
|
102
|
+
raise Exception.new("Route53 doesn't contain domain entry for the #{domain}. If you are parking your domain outside AWS, you will need to set the DNS entries yourself")
|
103
|
+
end
|
104
|
+
resp = @ses.list_identities({
|
105
|
+
identity_type: 'Domain'
|
106
|
+
})
|
107
|
+
identities = resp.to_h[:identities]
|
108
|
+
domain_identity = identities.find { |s| s == domain }
|
109
|
+
if domain_identity then
|
110
|
+
resp = @ses.get_identity_verification_attributes({
|
111
|
+
identities: [
|
112
|
+
domain,
|
113
|
+
]
|
114
|
+
})
|
115
|
+
domain_identity = resp.to_h[:verification_attributes][domain]
|
116
|
+
unless domain_identity[:verification_status] == 'Success' then
|
117
|
+
ensure_route53_hosted_zone_entries(hosted_zone, domain_identity[:verification_token])
|
118
|
+
end
|
119
|
+
else
|
120
|
+
resp = @ses.verify_domain_identity({
|
121
|
+
domain: domain,
|
122
|
+
})
|
123
|
+
verification_token = resp.to_h[:verification_token]
|
124
|
+
ensure_route53_hosted_zone_entries(hosted_zone, verification_token)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def ensure_route53_hosted_zone_entries(hosted_zone, verification_token)
|
129
|
+
request = {
|
130
|
+
hosted_zone_id: hosted_zone[:id],
|
131
|
+
change_batch: {
|
132
|
+
comment: 'Entries required for SES setup',
|
133
|
+
changes: [
|
134
|
+
{
|
135
|
+
action: 'UPSERT',
|
136
|
+
resource_record_set: {
|
137
|
+
name: hosted_zone[:name],
|
138
|
+
type: 'TXT',
|
139
|
+
ttl: 300,
|
140
|
+
resource_records: [
|
141
|
+
{
|
142
|
+
value: "\"#{verification_token}\""
|
143
|
+
},
|
144
|
+
]
|
145
|
+
},
|
146
|
+
},
|
147
|
+
{
|
148
|
+
action: "UPSERT",
|
149
|
+
resource_record_set: {
|
150
|
+
name: hosted_zone[:name],
|
151
|
+
type: 'MX',
|
152
|
+
ttl: 300,
|
153
|
+
resource_records: [
|
154
|
+
{
|
155
|
+
value: '10 inbound-smtp.eu-west-1.amazonaws.com'
|
156
|
+
}
|
157
|
+
]
|
158
|
+
}
|
159
|
+
}
|
160
|
+
]
|
161
|
+
}
|
162
|
+
}
|
163
|
+
resp = @route53.change_resource_record_sets(request)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Ensure the chosen rule set exists
|
167
|
+
def ensure_rule_set()
|
168
|
+
resp = @ses.list_receipt_rule_sets()
|
169
|
+
add_new_rule_set = true
|
170
|
+
resp.to_h[:rule_sets].each { |rule_set|
|
171
|
+
add_new_rule_set = false if (rule_set[:name] =~ /^#{Regexp.escape(@receipt_rule.rule_set_name)}$/)
|
172
|
+
}
|
173
|
+
@ses.create_receipt_rule_set({rule_set_name: @receipt_rule.rule_set_name}) if add_new_rule_set
|
174
|
+
end
|
175
|
+
|
176
|
+
# Ensure the bucket exists
|
177
|
+
def ensure_s3_bucket()
|
178
|
+
resp = @s3.list_buckets()
|
179
|
+
add_new_bucket = true
|
180
|
+
resp.to_h[:buckets].each { |bucket|
|
181
|
+
add_new_bucket = false if (bucket[:name] =~ /^#{Regexp.escape(@receipt_rule.s3_bucket)}$/)
|
182
|
+
}
|
183
|
+
@s3.create_bucket({
|
184
|
+
acl: "private",
|
185
|
+
bucket: @receipt_rule.s3_bucket,
|
186
|
+
create_bucket_configuration: {
|
187
|
+
location_constraint: @receipt_rule.region
|
188
|
+
}
|
189
|
+
}) if add_new_bucket
|
190
|
+
end
|
191
|
+
|
192
|
+
# Execute all the assurances
|
193
|
+
def ensure_prerequsites()
|
194
|
+
|
195
|
+
ensure_rule_set
|
196
|
+
ensure_s3_bucket
|
197
|
+
ensure_s3_bucket_policy
|
198
|
+
ensure_address_domains
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
data/sessel.png
ADDED
Binary file
|
data/swaggerless.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sessel/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sessel"
|
8
|
+
spec.version = Sessel::VERSION
|
9
|
+
spec.authors = ["Rafal Nowosielski"]
|
10
|
+
spec.email = ["rafal@nowosielski.email"]
|
11
|
+
|
12
|
+
spec.summary = "The gem includes a tool to store SES configuration in a scriptable form"
|
13
|
+
spec.description = "When writing solutions in the cloud, it is often valuable to have all configuraiton in code. Sonce CloutFormation doesn't support SES, this gem provides a way to store this confiuration in a scriptable form"
|
14
|
+
spec.homepage = "https://nowosielski.website"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = "bin"
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
26
|
+
|
27
|
+
spec.add_runtime_dependency "aws-sdk", "~> 2.6"
|
28
|
+
spec.add_runtime_dependency "thor"
|
29
|
+
spec.add_runtime_dependency "highline", "2.0.0.pre.develop.6"
|
30
|
+
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sessel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rafal Nowosielski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
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: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: aws-sdk
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.6'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: thor
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
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: highline
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.0.0.pre.develop.6
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.0.0.pre.develop.6
|
97
|
+
description: When writing solutions in the cloud, it is often valuable to have all
|
98
|
+
configuraiton in code. Sonce CloutFormation doesn't support SES, this gem provides
|
99
|
+
a way to store this confiuration in a scriptable form
|
100
|
+
email:
|
101
|
+
- rafal@nowosielski.email
|
102
|
+
executables:
|
103
|
+
- sessel
|
104
|
+
extensions: []
|
105
|
+
extra_rdoc_files: []
|
106
|
+
files:
|
107
|
+
- .gitignore
|
108
|
+
- Gemfile
|
109
|
+
- LICENSE
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- bin/sessel
|
113
|
+
- lib/sessel.rb
|
114
|
+
- lib/sessel/ask.rb
|
115
|
+
- lib/sessel/configuration_set.rb
|
116
|
+
- lib/sessel/configuration_set_creator.rb
|
117
|
+
- lib/sessel/io.rb
|
118
|
+
- lib/sessel/main.rb
|
119
|
+
- lib/sessel/receipt_rule.rb
|
120
|
+
- lib/sessel/receipt_rule_creator.rb
|
121
|
+
- lib/sessel/version.rb
|
122
|
+
- sessel.png
|
123
|
+
- swaggerless.gemspec
|
124
|
+
homepage: https://nowosielski.website
|
125
|
+
licenses: []
|
126
|
+
metadata: {}
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - '>='
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubyforge_project:
|
143
|
+
rubygems_version: 2.0.14.1
|
144
|
+
signing_key:
|
145
|
+
specification_version: 4
|
146
|
+
summary: The gem includes a tool to store SES configuration in a scriptable form
|
147
|
+
test_files: []
|