1
0
Fork 0
mirror of https://github.com/fog/fog-aws.git synced 2022-11-09 13:50:52 -05:00

kms service

* list, describe, and create keys
This commit is contained in:
Josh Lane 2015-05-11 10:13:37 -07:00
parent 956f53e274
commit f402dce5aa
14 changed files with 567 additions and 30 deletions

View file

@ -20,7 +20,7 @@ module Fog
module Storage
autoload :AWS, File.expand_path('../aws/storage', __FILE__)
end
module AWS
extend Fog::Provider
@ -31,49 +31,51 @@ module Fog
autoload :SignatureV4, File.expand_path('../aws/signaturev4', __FILE__)
# Services
autoload :AutoScaling, File.expand_path('../aws/auto_scaling', __FILE__)
autoload :AutoScaling, File.expand_path('../aws/auto_scaling', __FILE__)
autoload :CloudFormation, File.expand_path('../aws/cloud_formation', __FILE__)
autoload :CloudWatch, File.expand_path('../aws/cloud_watch', __FILE__)
autoload :DataPipeline, File.expand_path('../aws/data_pipeline', __FILE__)
autoload :DynamoDB, File.expand_path('../aws/dynamodb', __FILE__)
autoload :ELB, File.expand_path('../aws/elb', __FILE__)
autoload :EMR, File.expand_path('../aws/emr', __FILE__)
autoload :ElasticBeanstalk, File.expand_path('../aws/beanstalk', __FILE__)
autoload :CloudFormation, File.expand_path('../aws/cloud_formation', __FILE__)
autoload :CloudWatch, File.expand_path('../aws/cloud_watch', __FILE__)
autoload :DataPipeline, File.expand_path('../aws/data_pipeline', __FILE__)
autoload :DynamoDB, File.expand_path('../aws/dynamodb', __FILE__)
autoload :Elasticache, File.expand_path('../aws/elasticache', __FILE__)
autoload :ELB, File.expand_path('../aws/elb', __FILE__)
autoload :EMR, File.expand_path('../aws/emr', __FILE__)
autoload :Federation, File.expand_path('../aws/federation', __FILE__)
autoload :Glacier, File.expand_path('../aws/glacier', __FILE__)
autoload :IAM, File.expand_path('../aws/iam', __FILE__)
autoload :RDS, File.expand_path('../aws/rds', __FILE__)
autoload :Redshift, File.expand_path('../aws/redshift', __FILE__)
autoload :SES, File.expand_path('../aws/ses', __FILE__)
autoload :SimpleDB, File.expand_path('../aws/simpledb', __FILE__)
autoload :SNS, File.expand_path('../aws/sns', __FILE__)
autoload :SQS, File.expand_path('../aws/sqs', __FILE__)
autoload :STS, File.expand_path('../aws/sts', __FILE__)
autoload :Elasticache, File.expand_path('../aws/elasticache', __FILE__)
autoload :Federation, File.expand_path('../aws/federation', __FILE__)
autoload :Glacier, File.expand_path('../aws/glacier', __FILE__)
autoload :IAM, File.expand_path('../aws/iam', __FILE__)
autoload :KMS, File.expand_path('../aws/kms', __FILE__)
autoload :RDS, File.expand_path('../aws/rds', __FILE__)
autoload :Redshift, File.expand_path('../aws/redshift', __FILE__)
autoload :SES, File.expand_path('../aws/ses', __FILE__)
autoload :SNS, File.expand_path('../aws/sns', __FILE__)
autoload :SQS, File.expand_path('../aws/sqs', __FILE__)
autoload :STS, File.expand_path('../aws/sts', __FILE__)
autoload :SimpleDB, File.expand_path('../aws/simpledb', __FILE__)
service(:auto_scaling, 'AutoScaling')
service(:beanstalk, 'ElasticBeanstalk')
service(:cdn, 'CDN')
service(:compute, 'Compute')
service(:cloud_formation, 'CloudFormation')
service(:cloud_watch, 'CloudWatch')
service(:compute, 'Compute')
service(:data_pipeline, 'DataPipeline')
service(:dynamodb, 'DynamoDB')
service(:dns, 'DNS')
service(:dynamodb, 'DynamoDB')
service(:elasticache, 'Elasticache')
service(:elb, 'ELB')
service(:emr, 'EMR')
service(:federation, 'Federation')
service(:glacier, 'Glacier')
service(:iam, 'IAM')
service(:kms, 'KMS')
service(:rds, 'RDS')
service(:redshift, 'Redshift')
service(:ses, 'SES')
service(:simpledb, 'SimpleDB')
service(:sns, 'SNS')
service(:sqs, 'SQS')
service(:sts, 'STS')
service(:storage, 'Storage')
service(:sts, 'STS')
def self.indexed_param(key, values)
params = {}
@ -206,4 +208,4 @@ module Fog
options
end
end
end
end

View file

@ -96,7 +96,6 @@ module Fog
@aws_secret_access_key = options[:aws_secret_access_key]
@signer = Fog::AWS::SignatureV4.new( @aws_access_key_id, @aws_secret_access_key,@region,'elasticloadbalancing')
end
def data

180
lib/fog/aws/kms.rb Normal file
View file

@ -0,0 +1,180 @@
module Fog
module AWS
class KMS < Fog::Service
extend Fog::AWS::CredentialFetcher::ServiceMethods
DependencyTimeoutException = Class.new(Fog::Errors::Error)
DisabledException = Class.new(Fog::Errors::Error)
InvalidArnException = Class.new(Fog::Errors::Error)
InvalidGrantTokenException = Class.new(Fog::Errors::Error)
InvalidKeyUsageException = Class.new(Fog::Errors::Error)
KMSInternalException = Class.new(Fog::Errors::Error)
KeyUnavailableException = Class.new(Fog::Errors::Error)
MalformedPolicyDocumentException = Class.new(Fog::Errors::Error)
NotFoundException = Class.new(Fog::Errors::Error)
requires :aws_access_key_id, :aws_secret_access_key
recognizes :region, :host, :path, :port, :scheme, :persistent, :use_iam_profile, :aws_session_token, :instrumentor, :instrumentor_name
request_path 'fog/aws/requests/kms'
request :list_keys
request :create_key
request :describe_key
model_path 'fog/aws/models/kms'
model :key
collection :keys
class Mock
def self.data
@data ||= Hash.new do |hash, region|
hash[region] = Hash.new do |region_hash, access_key|
region_hash[access_key] = {
:keys => {},
}
end
end
end
def self.reset
@data.clear
end
attr_reader :account_id
def initialize(options={})
@use_iam_profile = options[:use_iam_profile]
@account_id = Fog::AWS::Mock.owner_id
@region = options[:region] || 'us-east-1'
setup_credentials(options)
unless ['ap-northeast-1', 'ap-southeast-1', 'ap-southeast-2', 'eu-central-1', 'eu-west-1', 'us-east-1', 'us-west-1', 'us-west-2', 'sa-east-1'].include?(@region)
raise ArgumentError, "Unknown region: #{@region.inspect}"
end
end
def setup_credentials(options)
@aws_access_key_id = options[:aws_access_key_id]
@aws_secret_access_key = options[:aws_secret_access_key]
@signer = Fog::AWS::SignatureV4.new(@aws_access_key_id, @aws_secret_access_key, @region, 'kms')
end
def data
self.class.data[@region][@aws_access_key_id]
end
def reset_data
self.class.data[@region].delete(@aws_access_key_id)
end
end
class Real
include Fog::AWS::CredentialFetcher::ConnectionMethods
# Initialize connection to KMS
#
# ==== Notes
# options parameter must include values for :aws_access_key_id and
# :aws_secret_access_key in order to create a connection
#
# ==== Examples
# kms = KMS.new(
# :aws_access_key_id => your_aws_access_key_id,
# :aws_secret_access_key => your_aws_secret_access_key
# )
#
# ==== Parameters
# * options<~Hash> - config arguments for connection. Defaults to {}.
# * region<~String> - optional region to use. For instance, 'eu-west-1', 'us-east-1', etc.
#
# ==== Returns
# * KMS object with connection to AWS.
def initialize(options={})
@use_iam_profile = options[:use_iam_profile]
@connection_options = options[:connection_options] || {}
@instrumentor = options[:instrumentor]
@instrumentor_name = options[:instrumentor_name] || 'fog.aws.kms'
options[:region] ||= 'us-east-1'
@region = options[:region]
@host = options[:host] || "kms.#{@region}.amazonaws.com"
@path = options[:path] || '/'
@persistent = options[:persistent] || false
@port = options[:port] || 443
@scheme = options[:scheme] || 'https'
@connection = Fog::XML::Connection.new("#{@scheme}://#{@host}:#{@port}#{@path}", @persistent, @connection_options)
setup_credentials(options)
end
def reload
@connection.reset
end
private
def setup_credentials(options={})
@aws_access_key_id = options[:aws_access_key_id]
@aws_secret_access_key = options[:aws_secret_access_key]
@aws_session_token = options[:aws_session_token]
@signer = Fog::AWS::SignatureV4.new(@aws_access_key_id, @aws_secret_access_key, @region, 'kms')
end
def request(params)
refresh_credentials_if_expired
idempotent = params.delete(:idempotent)
parser = params.delete(:parser)
body, headers = Fog::AWS.signed_params_v4(
params,
{ 'Content-Type' => 'application/x-www-form-urlencoded' },
{
:aws_session_token => @aws_session_token,
:signer => @signer,
:host => @host,
:path => @path,
:port => @port,
:version => '2014-11-01',
:method => 'POST'
}
)
if @instrumentor
@instrumentor.instrument("#{@instrumentor_name}.request", params) do
_request(body, headers, idempotent, parser)
end
else
_request(body, headers, idempotent, parser)
end
end
def _request(body, headers, idempotent, parser)
@connection.request({
:body => body,
:expects => 200,
:headers => headers,
:idempotent => idempotent,
:method => 'POST',
:parser => parser
})
rescue Excon::Errors::HTTPStatusError => error
match = Fog::AWS::Errors.match_error(error)
if match.empty?
raise
elsif Fog::AWS::KMS.const_defined?(match[:code])
raise Fog::AWS::KMS.const_get(match[:code]).slurp(error, match[:message])
else
raise Fog::AWS::KMS::Error.slurp(error, "#{match[:code]} => #{match[:message]}")
end
end
end
end
end
end

View file

@ -0,0 +1,34 @@
module Fog
module AWS
class KMS
class Key < Fog::Model
identity :id, :aliases => 'KeyId'
attribute :account_id, :aliases => 'AWSAccountId'
attribute :arn, :aliases => 'KeyArn'
attribute :created_at, :aliases => 'CreationDate', :type => :time
attribute :description, :aliases => 'Description'
attribute :enabled, :aliases => 'Enabled', :type => :boolean
attribute :usage, :aliases => 'KeyUsage'
attr_writer :policy
def reload
requires :identity
data = service.describe_key(self.identity)
merge_attributes(data.body['KeyMetadata'])
self
end
def save
data = service.create_key(@policy, description, usage)
merge_attributes(data.body['KeyMetadata'])
true
end
end
end
end
end

View file

@ -0,0 +1,29 @@
require 'fog/aws/models/kms/key'
module Fog
module AWS
class KMS
class Keys < Fog::PagedCollection
attribute :filters
attribute :truncated
model Fog::AWS::KMS::Key
def initialize(attributes)
self.filters ||= {}
super
end
# This method deliberately returns only a single page of results
def all(filters_arg = filters)
filters.merge!(filters_arg)
result = service.list_keys(filters).body
filters[:marker] = result['Marker']
self.truncated = result['Truncated']
load(result['Keys'])
end
end
end
end
end

View file

@ -0,0 +1,34 @@
module Fog
module Parsers
module AWS
module KMS
class DescribeKey < Fog::Parsers::Base
def reset
@response = { 'KeyMetadata' => {} }
end
def start_element(name, attrs = [])
super
case name
when 'KeyMetadata'
@key = {}
end
end
def end_element(name)
case name
when 'KeyUsage', 'AWSAccountId', 'Description', 'KeyId', 'Arn'
@key[name] = value
when 'CreationDate'
@key[name] = Time.parse(value)
when 'Enabled'
@key[name] = (value == 'true')
when 'KeyMetadata'
@response['KeyMetadata'] = @key
end
end
end
end
end
end
end

View file

@ -0,0 +1,38 @@
module Fog
module Parsers
module AWS
module KMS
class ListKeys < Fog::Parsers::Base
def reset
@response = { 'Keys' => [] }
end
def start_element(name, attrs = [])
super
case name
when 'Keys'
@keys = []
when 'member'
@key = {}
end
end
def end_element(name)
case name
when 'KeyId', 'KeyArn'
@key[name] = value
when 'member'
@keys << @key
when 'Keys'
@response['Keys'] = @keys
when 'Truncated'
@response['Truncated'] = (value == 'true')
when 'NextMarker'
@response['Marker'] = value
end
end
end
end
end
end
end

View file

@ -42,11 +42,11 @@ module Fog
#
def create_role(role_name, assume_role_policy_document, path = '/')
request(
'Action' => 'CreateRole',
'RoleName' => role_name,
'AssumeRolePolicyDocument' => assume_role_policy_document,
'Path' => path,
:parser => Fog::Parsers::AWS::IAM::SingleRole.new
'Action' => 'CreateRole',
'RoleName' => role_name,
'AssumeRolePolicyDocument' => assume_role_policy_document,
'Path' => path,
:parser => Fog::Parsers::AWS::IAM::SingleRole.new
)
end
end

View file

@ -0,0 +1,62 @@
module Fog
module AWS
class KMS
class Real
DEFAULT_KEY_POLICY = <<-JSON
{
"Version": "2012-10-17",
"Id": "key-default-1",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::915445820265:root"
},
"Action": "kms:*",
"Resource": "*"
}
]
}
JSON
require 'fog/aws/parsers/kms/describe_key'
def create_key(policy = nil, description = nil, usage = "ENCRYPT_DECRYPT")
request(
'Action' => 'CreateKey',
'Description' => description,
'KeyUsage' => usage,
'Policy' => policy,
:parser => Fog::Parsers::AWS::KMS::DescribeKey.new
)
end
end
class Mock
def create_key(policy = nil, description = nil, usage = "ENCRYPT_DECRYPT")
response = Excon::Response.new
key_id = UUID.uuid
key_arn = Fog::AWS::Mock.arn("kms", self.account_id, "key/#{key_id}", @region)
key = {
"KeyUsage" => usage,
"AWSAccountId" => self.account_id,
"KeyId" => key_id,
"Description" => description,
"CreationDate" => Time.now,
"Arn" => key_arn,
"Enabled" => true,
}
# @todo use default policy
self.data[:keys][key_id] = key
response.body = { "KeyMetadata" => key }
response
end
end
end
end
end

View file

@ -0,0 +1,27 @@
module Fog
module AWS
class KMS
class Real
require 'fog/aws/parsers/kms/describe_key'
def describe_key(identifier)
request(
'Action' => 'DescribeKey',
'KeyId' => identifier,
:parser => Fog::Parsers::AWS::KMS::DescribeKey.new
)
end
end
class Mock
def describe_key(identifier)
response = Excon::Response.new
key = self.data[:keys][identifier]
response.body = { "KeyMetadata" => key }
response
end
end
end
end
end

View file

@ -0,0 +1,82 @@
module Fog
module AWS
class KMS
class Real
require 'fog/aws/parsers/kms/list_keys'
def list_keys(options={})
params = {}
if options[:marker]
params['Marker'] = options[:marker]
end
if options[:limit]
params['Limit'] = options[:limit]
end
request({
'Action' => 'ListKeys',
:parser => Fog::Parsers::AWS::KMS::ListKeys.new
}.merge(params))
end
end
class Mock
def list_keys(options={})
limit = options[:limit]
marker = options[:marker]
if limit
if limit > 1_000
raise Fog::AWS::KMS::Error.new(
"ValidationError => 1 validation error detected: Value '#{limit}' at 'limit' failed to satisfy constraint: Member must have value less than or equal to 1000"
)
elsif limit < 1
raise Fog::AWS::KMS::Error.new(
"ValidationError => 1 validation error detected: Value '#{limit}' at 'limit' failed to satisfy constraint: Member must have value greater than or equal to 1"
)
end
end
key_set = if marker
self.data[:markers][marker] || []
else
self.data[:keys].inject([]) { |r,(k,v)|
r << { "KeyId" => k, "KeyArn" => v["Arn"] }
}
end
keys = if limit
key_set.slice!(0, limit)
else
key_set
end
truncated = keys.size < key_set.size
marker = truncated && "metadata/l/#{account_id}/#{UUID.uuid}"
response = Excon::Response.new
body = {
'Keys' => keys,
'Truncated' => truncated,
'RequestId' => Fog::AWS::Mock.request_id
}
if marker
self.data[:markers][marker] = key_set
body.merge!('Marker' => marker)
end
response.body = body
response.status = 200
response
end
end
end
end
end

View file

@ -65,7 +65,7 @@ module Fog
:aws_secret_access_key => SecureRandom.hex(3)
).send_message(endpoint, Fog::JSON.encode(
"Type" => "SubscriptionConfirmation",
"MessageId" => SecureRandom.uuid,
"MessageId" => UUID.uuid,
"Token" => token,
"TopicArn" => arn,
"Message" => message,

View file

@ -0,0 +1,27 @@
class AWS
module KMS
module Formats
BASIC = {
'ResponseMetadata' => { 'RequestId' => String }
}
DESCRIBE_KEY = {
"KeyMetadata" => {
"KeyUsage" => String,
"AWSAccountId" => String,
"KeyId" => String,
"Description" => Fog::Nullable::String,
"CreationDate" => Time,
"Arn" => String,
"Enabled" => Fog::Boolean
}
}
LIST_KEYS = {
"Keys" => [{ "KeyId" => String, "KeyArn" => String }],
"Truncated" => Fog::Boolean,
"Marker" => Fog::Nullable::String
}
end
end
end

View file

@ -0,0 +1,23 @@
Shindo.tests('AWS::KMS | key requests', ['aws', 'kms']) do
key_id = nil
tests('success') do
tests("#create_key").data_matches_schema(AWS::KMS::Formats::DESCRIBE_KEY) do
result = Fog::AWS[:kms].create_key.body
key_id = result["KeyMetadata"]["KeyId"]
result
end
end
tests("#describe_key").data_matches_schema(AWS::KMS::Formats::DESCRIBE_KEY) do
result = Fog::AWS[:kms].describe_key(key_id).body
returns(key_id) { result['KeyMetadata']['KeyId'] }
result
end
tests("#list_keys").data_matches_schema(AWS::KMS::Formats::LIST_KEYS) do
Fog::AWS[:kms].list_keys.body
end
end