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

Support AWS S3 cors configuration for buckets

This commit is contained in:
Lee Henson 2012-12-05 18:30:41 +00:00
parent bcfb5fe1df
commit c0e80549d9
7 changed files with 345 additions and 0 deletions

View file

@ -0,0 +1,41 @@
module Fog
module Parsers
module Storage
module AWS
class CorsConfiguration < Fog::Parsers::Base
def reset
@in_cors_configuration_list = false
@cors_rule = {}
@response = { 'CORSConfiguration' => [] }
end
def start_element(name, attrs = [])
super
if name == 'CORSConfiguration'
@in_cors_configuration_list = true
end
end
def end_element(name)
case name
when 'CORSConfiguration'
@in_cors_configuration_list = false
when 'CORSRule'
@response['CORSConfiguration'] << @cors_rule
@cors_rule = {}
when 'MaxAgeSeconds'
@cors_rule[name] = value.to_i
when 'ID'
@cors_rule[name] = value
when 'AllowedOrigin', 'AllowedMethod', 'AllowedHeader', 'ExposeHeader'
(@cors_rule[name] ||= []) << value
end
end
end
end
end
end
end

View file

@ -0,0 +1,41 @@
module Fog
module Storage
class AWS
require 'fog/aws/parsers/storage/cors_configuration'
private
def self.hash_to_cors(cors)
data = "<CORSConfiguration>\n"
[cors['CORSConfiguration']].flatten.compact.each do |rule|
data << " <CORSRule>\n"
['ID', 'MaxAgeSeconds'].each do |key|
data << " <#{key}>#{rule[key]}</#{key}>\n" if rule[key]
end
['AllowedOrigin', 'AllowedMethod', 'AllowedHeader', 'ExposeHeader'].each do |key|
[rule[key]].flatten.compact.each do |value|
data << " <#{key}>#{value}</#{key}>\n"
end
end
data << " </CORSRule>\n"
end
data << "</CORSConfiguration>"
data
end
def self.cors_to_hash(cors_xml)
parser = Fog::Parsers::Storage::AWS::CorsConfiguration.new
Nokogiri::XML::SAX::Parser.new(parser).parse(cors_xml)
parser.response
end
end
end
end

View file

@ -0,0 +1,32 @@
module Fog
module Storage
class AWS
class Real
# Deletes the cors configuration information set for the bucket.
#
# ==== Parameters
# * bucket_name<~String> - name of bucket to delete cors rules from
#
# ==== Returns
# * response<~Excon::Response>:
# * status<~Integer> - 204
#
# ==== See Also
# http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETEcors.html
def delete_bucket_cors(bucket_name)
request({
:expects => 204,
:headers => {},
:host => "#{bucket_name}.#{@host}",
:method => 'DELETE',
:query => {'cors' => nil}
})
end
end
end
end
end

View file

@ -0,0 +1,68 @@
module Fog
module Storage
class AWS
class Real
require 'fog/aws/parsers/storage/cors_configuration'
# Gets the CORS configuration for an S3 bucket
#
# ==== Parameters
# * bucket_name<~String> - name of bucket to get access control list for
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'CORSConfiguration'<~Array>:
# * 'CORSRule'<~Hash>:
# * 'AllowedHeader'<~String> - Which headers are allowed in a pre-flight OPTIONS request through the Access-Control-Request-Headers header.
# * 'AllowedMethod'<~String> - Identifies an HTTP method that the domain/origin specified in the rule is allowed to execute.
# * 'AllowedOrigin'<~String> - One or more response headers that you want customers to be able to access from their applications (for example, from a JavaScript XMLHttpRequest object).
# * 'ExposeHeader'<~String> - One or more headers in the response that you want customers to be able to access from their applications (for example, from a JavaScript XMLHttpRequest object).
# * 'ID'<~String> - An optional unique identifier for the rule. The ID value can be up to 255 characters long. The IDs help you find a rule in the configuration.
# * 'MaxAgeSeconds'<~Integer> - The time in seconds that your browser is to cache the preflight response for the specified resource.
#
# ==== See Also
# http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETcors.html
def get_bucket_cors(bucket_name)
unless bucket_name
raise ArgumentError.new('bucket_name is required')
end
request({
:expects => 200,
:headers => {},
:host => "#{bucket_name}.#{@host}",
:idempotent => true,
:method => 'GET',
:parser => Fog::Parsers::Storage::AWS::CorsConfiguration.new,
:query => {'cors' => nil}
})
end
end
class Mock # :nodoc:all
require 'fog/aws/requests/storage/cors_utils'
def get_bucket_cors(bucket_name)
response = Excon::Response.new
if cors = self.data[:cors][:bucket][bucket_name]
response.status = 200
if cors.is_a?(String)
response.body = Fog::Storage::AWS.cors_to_hash(cors)
else
response.body = cors
end
else
response.status = 404
raise(Excon::Errors.status_error({:expects => 200}, response))
end
response
end
end
end
end
end

View file

@ -0,0 +1,51 @@
module Fog
module Storage
class AWS
class Real
require 'fog/aws/requests/storage/cors_utils'
# Sets the cors configuration for your bucket. If the configuration exists, Amazon S3 replaces it.
#
# ==== Parameters
# * bucket_name<~String> - name of bucket to modify
# * cors<~Hash>:
# * CORSConfiguration<~Array>:
# * ID<~String>: A unique identifier for the rule.
# * AllowedMethod<~String>: An HTTP method that you want to allow the origin to execute.
# * AllowedOrigin<~String>: An origin that you want to allow cross-domain requests from.
# * AllowedHeader<~String>: Specifies which headers are allowed in a pre-flight OPTIONS request via the Access-Control-Request-Headers header.
# * MaxAgeSeconds<~String>: The time in seconds that your browser is to cache the preflight response for the specified resource.
# * ExposeHeader<~String>: One or more headers in the response that you want customers to be able to access from their applications.
#
# ==== See Also
# http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTcors.html
def put_bucket_cors(bucket_name, cors)
data = Fog::Storage::AWS.hash_to_cors(cors)
headers = {}
headers['Content-MD5'] = Base64.encode64(Digest::MD5.digest(data)).strip
headers['Content-Type'] = 'application/json'
headers['Date'] = Fog::Time.now.to_date_header
request({
:body => data,
:expects => 200,
:headers => headers,
:host => "#{bucket_name}.#{@host}",
:method => 'PUT',
:query => {'cors' => nil}
})
end
end
class Mock
def put_bucket_cors(bucket_name, cors)
self.data[:cors][:bucket][bucket_name] = Fog::Storage::AWS.hash_to_cors(cors)
end
end
end
end
end

View file

@ -22,6 +22,7 @@ module Fog
request :complete_multipart_upload
request :copy_object
request :delete_bucket
request :delete_bucket_cors
request :delete_bucket_lifecycle
request :delete_bucket_policy
request :delete_bucket_website
@ -29,6 +30,7 @@ module Fog
request :delete_multiple_objects
request :get_bucket
request :get_bucket_acl
request :get_bucket_cors
request :get_bucket_lifecycle
request :get_bucket_location
request :get_bucket_logging
@ -51,6 +53,7 @@ module Fog
request :post_object_hidden_fields
request :put_bucket
request :put_bucket_acl
request :put_bucket_cors
request :put_bucket_lifecycle
request :put_bucket_logging
request :put_bucket_policy
@ -338,6 +341,7 @@ DATA
for key in (params[:query] || {}).keys.sort
if %w{
acl
cors
delete
lifecycle
location

View file

@ -0,0 +1,108 @@
require 'fog/aws/requests/storage/cors_utils'
Shindo.tests('Fog::Storage::AWS | CORS utils', ["aws"]) do
tests(".hash_to_cors") do
tests(".hash_to_cors({}) at xpath //CORSConfiguration").returns("", "has an empty CORSConfiguration") do
xml = Fog::Storage::AWS.hash_to_cors({})
Nokogiri::XML(xml).xpath("//CORSConfiguration").first.content.chomp
end
tests(".hash_to_cors({}) at xpath //CORSConfiguration/CORSRule").returns(nil, "has no CORSRules") do
xml = Fog::Storage::AWS.hash_to_cors({})
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule").first
end
cors = {
'CORSConfiguration' => [
{
'AllowedOrigin' => ['origin_123', 'origin_456'],
'AllowedMethod' => ['GET', 'POST'],
'AllowedHeader' => ['Accept', 'Content-Type'],
'ID' => 'blah-888',
'MaxAgeSeconds' => 2500,
'ExposeHeader' => ['x-some-header', 'x-other-header']
}
]
}
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/AllowedOrigin").returns("origin_123", "returns the CORSRule AllowedOrigin") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/AllowedOrigin")[0].content
end
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/AllowedOrigin").returns("origin_456", "returns the CORSRule AllowedOrigin") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/AllowedOrigin")[1].content
end
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/AllowedMethod").returns("GET", "returns the CORSRule AllowedMethod") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/AllowedMethod")[0].content
end
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/AllowedMethod").returns("POST", "returns the CORSRule AllowedMethod") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/AllowedMethod")[1].content
end
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/AllowedHeader").returns("Accept", "returns the CORSRule AllowedHeader") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/AllowedHeader")[0].content
end
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/AllowedHeader").returns("Content-Type", "returns the CORSRule AllowedHeader") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/AllowedHeader")[1].content
end
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/ID").returns("blah-888", "returns the CORSRule ID") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/ID")[0].content
end
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/MaxAgeSeconds").returns("2500", "returns the CORSRule MaxAgeSeconds") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/MaxAgeSeconds")[0].content
end
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/ExposeHeader").returns("x-some-header", "returns the CORSRule ExposeHeader") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/ExposeHeader")[0].content
end
tests(".hash_to_cors(#{cors.inspect}) at xpath //CORSConfiguration/CORSRule/ExposeHeader").returns("x-other-header", "returns the CORSRule ExposeHeader") do
xml = Fog::Storage::AWS.hash_to_cors(cors)
Nokogiri::XML(xml).xpath("//CORSConfiguration/CORSRule/ExposeHeader")[1].content
end
end
tests(".cors_to_hash") do
cors_xml = <<-XML
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>http://www.example.com</AllowedOrigin>
<AllowedOrigin>http://www.example2.com</AllowedOrigin>
<AllowedHeader>Content-Length</AllowedHeader>
<AllowedHeader>X-Foobar</AllowedHeader>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>x-amz-server-side-encryption</ExposeHeader>
<ExposeHeader>x-amz-balls</ExposeHeader>
</CORSRule>
</CORSConfiguration>
XML
tests(".cors_to_hash(#{cors_xml.inspect})").returns({
"CORSConfiguration" => [{
"AllowedOrigin" => ["http://www.example.com", "http://www.example2.com"],
"AllowedHeader" => ["Content-Length", "X-Foobar"],
"AllowedMethod" => ["PUT", "GET"],
"MaxAgeSeconds" => 3000,
"ExposeHeader" => ["x-amz-server-side-encryption", "x-amz-balls"]
}]
}, 'returns hash of CORS XML') do
Fog::Storage::AWS.cors_to_hash(cors_xml)
end
end
end