Skip to content

Commit 427cfdb

Browse files
authored
Add DynamoDB session store (v2 session store gem) (#38)
1 parent f420a1a commit 427cfdb

File tree

13 files changed

+350
-86
lines changed

13 files changed

+350
-86
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
coverage
66
doc
77
Gemfile.lock
8-
test/dummy/log/
9-
test/dummy/tmp/
8+
spec/dummy/log/
9+
spec/dummy/tmp/
1010
vendor

README.md

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# AWS SDK for Ruby Rails Plugin
22

3-
[![Build
4-
Status](https://travis-ci.org/aws/aws-sdk-rails.png?branch=master)](https://travis-ci.org/aws/aws-sdk-rails)
5-
[![Code
6-
Climate](https://codeclimate.com/github/aws/aws-sdk-rails.png)](https://codeclimate.com/github/aws/aws-sdk-rails)
3+
[![Gem Version](https://badge.fury.io/rb/aws-sdk-rails.svg)](https://badge.fury.io/rb/aws-sdk-rails) [![Build Status](https://travis-ci.com/aws/aws-sdk-rails.svg?branch=master)](https://travis-ci.com/aws/aws-sdk-rails) [![Github forks](https://img.shields.io/github/forks/aws/aws-sdk-rails.svg)](https://github.com/aws/aws-sdk-rails/network)
4+
[![Github stars](https://img.shields.io/github/stars/aws/aws-sdk-rails.svg)](https://github.com/aws/aws-sdk-rails/stargazers)
5+
[![Gitter](https://badges.gitter.im/aws/aws-sdk-rails.svg)](https://gitter.im/aws/aws-sdk-rails?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
76

87
A Ruby on Rails plugin that integrates AWS services with your application using
98
the latest version of [AWS SDK For Ruby](https://github.com/aws/aws-sdk-ruby).
@@ -30,14 +29,14 @@ latest [AWS SDK for Ruby Docs](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/i
3029
for details.
3130

3231
If you're running your Rails application on Amazon EC2, the AWS SDK will
33-
automatically check Amazon EC2 instance metadata for credentials. Learn more:
32+
check Amazon EC2 instance metadata for credentials to load. Learn more:
3433
[IAM Roles for Amazon EC2](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html)
3534

3635
# Features
3736

3837
## AWS SDK uses the Rails logger
3938

40-
The AWS SDK is automatically configured to use the built-in Rails logger for any
39+
The AWS SDK is configured to use the built-in Rails logger for any
4140
SDK log output. The logger is configured to use the `:info` log level. You can
4241
change the log level by setting `:log_level` in the
4342
[Aws.config](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws.html) hash.
@@ -48,10 +47,8 @@ Aws.config.update(log_level: :debug)
4847

4948
## Rails 5.2+ Encrypted Credentials
5049

51-
If you are using Rails 5.2+ [Encrypted
52-
Credentials](http://guides.rubyonrails.org/security.html#custom-credentials),
53-
the credentials will be automatically loaded assuming the decrypted contents
54-
are provided as such:
50+
If you are using Rails 5.2+ [Encrypted Credentials](http://guides.rubyonrails.org/security.html#custom-credentials),
51+
the credentials will be decrypted and loaded under the `:aws` top level key:
5552

5653
```yml
5754
# config/credentials.yml.enc
@@ -61,6 +58,84 @@ aws:
6158
secret_access_key: YOUR_ACCESS_KEY
6259
```
6360
61+
## DynamoDB Session Store
62+
63+
You can configure session storage in Rails to use DynamoDB instead of cookies,
64+
allowing access to sessions from other applications and devices. You will need
65+
to have an existing Amazon DynamoDB session table to use this feature.
66+
67+
You can generate a migration file for the session table using the following
68+
command (<MigrationName> is optional):
69+
70+
```bash
71+
rails generate dynamo_db:session_store_migration <MigrationName>
72+
```
73+
74+
The session store migration generator command will generate two files: a
75+
migration file, `db/migration/#{VERSION}_#{MIGRATION_NAME}.rb`, and a
76+
configuration YAML file, `config/dynamo_db_session_store.yml`.
77+
78+
The migration file will create and delete a table with default options. These
79+
options can be changed prior to running the migration and are documented in the
80+
[Table](https://docs.aws.amazon.com/sdk-for-ruby/aws-sessionstore-dynamodb/api/Aws/SessionStore/DynamoDB/Table.html) class.
81+
82+
To create the table, run migrations as normal with:
83+
84+
```bash
85+
rake db:migrate
86+
```
87+
88+
Next, configure the Rails session store to be `:dynamodb_store` by editing
89+
`config/initializers/session_store.rb` to contain the following:
90+
91+
```ruby
92+
# config/initializers/session_store.rb
93+
Rails.application.config.session_store :dynamodb_store, key: '_your_app_session'
94+
```
95+
96+
You can now start your Rails application with session support.
97+
98+
### Configuration
99+
100+
You can configure the session store with code, YAML files, or ENV, in this order
101+
of precedence. To configure in code, you can directly pass options to your
102+
initializer like so:
103+
104+
```ruby
105+
# config/initializers/session_store.rb
106+
Rails.application.config.session_store :dynamodb_store,
107+
key: '_your_app_session',
108+
table_name: 'foo'
109+
```
110+
111+
Alternatively, you can use the generated YAML configuration file
112+
`config/dynamo_db_session_store.yml`. YAML configuration may also be specified
113+
per environment, with environment configuration having precedence. To do this,
114+
create `config/dynamo_db_session_store/#{Rails.env}.yml` files as needed.
115+
116+
For configuration options, see the [Configuration](https://docs.aws.amazon.com/sdk-for-ruby/aws-sessionstore-dynamodb/api/Aws/SessionStore/DynamoDB/Configuration.html) class.
117+
118+
#### Rack Configuration
119+
120+
DynamoDB session storage is implemented in the [`aws-sessionstore-dynamodb`](https://github.com/aws/aws-sessionstore-dynamodb-ruby)
121+
gem. The Rack middleware inherits from the [`Rack::Session::Abstract::Persisted`](https://www.rubydoc.info/github/rack/rack/Rack/Session/Abstract/Persisted)
122+
class, which also includes additional options (such as `:key`) that can be
123+
passed into the Rails initializer.
124+
125+
### Cleaning old sessions
126+
127+
By default sessions do not expire. See `config/dynamo_db_session_store.yml` to
128+
configure the max age or stale period of a session.
129+
130+
You can use the DynamoDB [Time to Live (TTL) feature](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html)
131+
on the `expire_at` attribute to automatically delete expired items.
132+
133+
Alternatively, a Rake task for garbage collection is provided:
134+
135+
```bash
136+
rake dynamo_db:collect_garbage
137+
```
138+
64139
## Amazon Simple Email Service (SES) as an ActionMailer Delivery Method
65140

66141
This gem will automatically register SES as an ActionMailer delivery method. You
@@ -71,9 +146,7 @@ simply need to configure Rails to use it in your environment configuration:
71146
config.action_mailer.delivery_method = :ses
72147
```
73148

74-
# Other Usage
75-
76-
## Manually setting Action Mailer credentials
149+
### Manually setting credentials
77150

78151
If you need to provide different credentials for Action Mailer, you can call
79152
client-creating actions manually. For example, you can create an initializer
@@ -98,7 +171,7 @@ Aws::Rails.add_action_mailer_delivery_method(
98171
)
99172
```
100173

101-
## Using ARNs with SES
174+
### Using ARNs with SES
102175

103176
This gem uses [`Aws::SES::Client#send_raw_email`](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/SES/Client.html#send_raw_email-instance_method)
104177
to send emails. This operation allows you to specify a cross-account identity

aws-sdk-rails.gemspec

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ Gem::Specification.new do |spec|
66
spec.name = 'aws-sdk-rails'
77
spec.version = version
88
spec.authors = ['Amazon Web Services']
9-
9+
10+
1011
spec.summary = 'AWS SDK for Ruby on Rails Plugin'
1112
spec.description = 'Integrates the AWS Ruby SDK with Ruby on Rails'
1213
spec.homepage = 'https://github.com/aws/aws-sdk-rails'
1314
spec.license = 'Apache-2.0'
1415

1516
spec.require_paths = ['lib']
16-
spec.files += Dir['lib/**/*.rb', 'lib/aws-sdk-rails.rb']
17+
spec.files += Dir['lib/**/*.rb']
1718

18-
spec.add_dependency('aws-sdk-ses', '~> 1')
19-
spec.add_dependency('railties', '>= 5.2.0')
19+
spec.add_dependency('aws-sdk-ses', '~> 1') # for ActionMailer
20+
spec.add_dependency('aws-sessionstore-dynamodb', '~> 2') # includes dynamo db
21+
spec.add_dependency('railties', '>= 5.2.0') # encrypted credentials
2022

2123
spec.add_development_dependency('rails')
2224
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require 'aws-sessionstore-dynamodb'
2+
3+
module ActionDispatch
4+
module Session
5+
# Uses the Dynamo DB Session Store implementation to create a class that
6+
# extends ActionDispatch::Session. Rails will create a :dynamodb_store
7+
# configuration for session_store from this class name.
8+
#
9+
# This class will use the Rails secret_key_base unless otherwise provided.
10+
#
11+
# Configuration can also be provided in YAML files from Rails config, either
12+
# in "config/session_store.yml" or "config/session_store/#{Rails.env}.yml".
13+
# Configuration files that are environment-specific will take precedence.
14+
#
15+
# @see https://docs.aws.amazon.com/sdk-for-ruby/aws-sessionstore-dynamodb/api/Aws/SessionStore/DynamoDB/Configuration.html
16+
class DynamodbStore < Aws::SessionStore::DynamoDB::RackMiddleware
17+
def initialize(app, options = {})
18+
options[:config_file] ||= config_file if config_file.exist?
19+
options[:secret_key] ||= Rails.application.secret_key_base
20+
super
21+
end
22+
23+
private
24+
25+
def config_file
26+
file = Rails.root.join("config/dynamo_db_session_store/#{Rails.env}.yml")
27+
file = Rails.root.join('config/dynamo_db_session_store.yml') unless file.exist?
28+
file
29+
end
30+
end
31+
end
32+
end

lib/aws-sdk-rails.rb

Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,7 @@
11
# frozen_string_literal: true
22

33
require_relative 'aws/rails/mailer'
4-
require_relative 'aws/rails/notifications_instrument_plugin'
4+
require_relative 'aws/rails/railtie'
5+
require_relative 'aws/rails/notifications_instrument'
56

6-
module Aws
7-
8-
# Use the Rails namespace.
9-
module Rails
10-
11-
# @api private
12-
class Railtie < ::Rails::Railtie
13-
initializer 'aws-sdk-rails.initialize',
14-
before: :load_config_initializers do
15-
# Initialization Actions
16-
Aws::Rails.use_rails_encrypted_credentials
17-
Aws::Rails.add_action_mailer_delivery_method
18-
Aws::Rails.log_to_rails_logger
19-
end
20-
end
21-
22-
# This is called automatically from the SDK's Railtie, but can be manually
23-
# called if you want to specify options for building the Aws::SES::Client.
24-
#
25-
# @param [Symbol] name The name of the ActionMailer delivery method to
26-
# register.
27-
# @param [Hash] options The options you wish to pass on to the
28-
# Aws::SES::Client initialization method.
29-
def self.add_action_mailer_delivery_method(name = :ses, options = {})
30-
ActiveSupport.on_load(:action_mailer) do
31-
add_delivery_method(name, Aws::Rails::Mailer, options)
32-
end
33-
end
34-
35-
# Configures the AWS SDK for Ruby's logger to use the Rails logger.
36-
def self.log_to_rails_logger
37-
Aws.config[:logger] = ::Rails.logger
38-
nil
39-
end
40-
41-
# Configures the AWS SDK with credentials from Rails encrypted credentials.
42-
def self.use_rails_encrypted_credentials
43-
# limit the config keys we merge to credentials only
44-
aws_credential_keys = %i[access_key_id secret_access_key session_token]
45-
46-
Aws.config.merge!(
47-
::Rails.application
48-
.try(:credentials)
49-
.try(:aws)
50-
.to_h.slice(*aws_credential_keys)
51-
)
52-
end
53-
54-
# Adds ActiveSupport Notifications instrumentation to AWS SDK
55-
# client operations. Each operation will produce an event with a name:
56-
# <operation>.<service>.aws. For example, S3's put_object has an event
57-
# name of: put_object.S3.aws
58-
def self.instrument_sdk_operations
59-
Aws.constants.each do|c|
60-
m = Aws.const_get(c)
61-
if m.is_a?(Module) &&
62-
m.const_defined?(:Client) &&
63-
m.const_get(:Client).superclass == Seahorse::Client::Base
64-
m.const_get(:Client).add_plugin(Aws::Rails::Notifications)
65-
end
66-
end
67-
end
68-
end
69-
end
7+
require_relative 'action_dispatch/session/dynamodb_store'

lib/aws/rails/railtie.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# frozen_string_literal: true
2+
3+
module Aws
4+
# Use the Rails namespace.
5+
module Rails
6+
# @api private
7+
class Railtie < ::Rails::Railtie
8+
initializer 'aws-sdk-rails.initialize',
9+
before: :load_config_initializers do
10+
# Initialization Actions
11+
Aws::Rails.use_rails_encrypted_credentials
12+
Aws::Rails.add_action_mailer_delivery_method
13+
Aws::Rails.log_to_rails_logger
14+
end
15+
16+
rake_tasks do
17+
load 'tasks/dynamo_db/session_store/clean.rake'
18+
end
19+
end
20+
21+
# This is called automatically from the SDK's Railtie, but can be manually
22+
# called if you want to specify options for building the Aws::SES::Client.
23+
#
24+
# @param [Symbol] name The name of the ActionMailer delivery method to
25+
# register.
26+
# @param [Hash] options The options you wish to pass on to the
27+
# Aws::SES::Client initialization method.
28+
def self.add_action_mailer_delivery_method(name = :ses, options = {})
29+
ActiveSupport.on_load(:action_mailer) do
30+
add_delivery_method(name, Aws::Rails::Mailer, options)
31+
end
32+
end
33+
34+
# Configures the AWS SDK for Ruby's logger to use the Rails logger.
35+
def self.log_to_rails_logger
36+
Aws.config[:logger] = ::Rails.logger
37+
nil
38+
end
39+
40+
# Configures the AWS SDK with credentials from Rails encrypted credentials.
41+
def self.use_rails_encrypted_credentials
42+
# limit the config keys we merge to credentials only
43+
aws_credential_keys = %i[access_key_id secret_access_key session_token]
44+
45+
Aws.config.merge!(
46+
::Rails.application
47+
.try(:credentials)
48+
.try(:aws)
49+
.to_h.slice(*aws_credential_keys)
50+
)
51+
end
52+
53+
# Adds ActiveSupport Notifications instrumentation to AWS SDK
54+
# client operations. Each operation will produce an event with a name:
55+
# <operation>.<service>.aws. For example, S3's put_object has an event
56+
# name of: put_object.S3.aws
57+
def self.instrument_sdk_operations
58+
Aws.constants.each do|c|
59+
m = Aws.const_get(c)
60+
if m.is_a?(Module) &&
61+
m.const_defined?(:Client) &&
62+
m.const_get(:Client).superclass == Seahorse::Client::Base
63+
m.const_get(:Client).add_plugin(Aws::Rails::Notifications)
64+
end
65+
end
66+
end
67+
end
68+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Description:
2+
Generates a migration file for deleting and a creating a DynamoDB
3+
sessions table, and a configuration file for the session store.
4+
5+
Example:
6+
rails generate dynamo_db:session_store_migration <MIGRATION_NAME>
7+
8+
This will create:
9+
db/migrate/#{VERSION}_#{MIGRATION_NAME}.rb
10+
config/dynamo_db_session_store.yml
11+
12+
The migration will be run when the command rake db:migrate is run
13+
in the command line.

0 commit comments

Comments
 (0)