Merge pull request 'Switch to AWS v4 signatures' (#5) from feature/aws_v4_signatures into master
	
		
			
	
		
	
	
		
	
		
			Some checks reported warnings
		
		
	
	
		
			
				
	
				Tests / test (6, 2.7) (push) Has been cancelled
				
					
					
				
			
		
			
				
	
				Tests / test (6, 3.0) (push) Has been cancelled
				
					
					
				
			
		
			
				
	
				Tests / test (6, 3.1) (push) Has been cancelled
				
					
					
				
			
		
			
				
	
				Tests / test (7, 2.7) (push) Has been cancelled
				
					
					
				
			
		
			
				
	
				Tests / test (7, 3.0) (push) Has been cancelled
				
					
					
				
			
		
			
				
	
				Tests / test (7, 3.1) (push) Has been cancelled
				
					
					
				
			
		
		
	
	
				
					
				
			
		
			Some checks reported warnings
		
		
	
	Tests / test (6, 2.7) (push) Has been cancelled
				Tests / test (6, 3.0) (push) Has been cancelled
				Tests / test (6, 3.1) (push) Has been cancelled
				Tests / test (7, 2.7) (push) Has been cancelled
				Tests / test (7, 3.0) (push) Has been cancelled
				Tests / test (7, 3.1) (push) Has been cancelled
				Reviewed-on: #5
This commit is contained in:
		
						commit
						8b6f201a0b
					
				@ -1,8 +1,7 @@
 | 
			
		||||
# FROM ruby:3.1.4
 | 
			
		||||
FROM ruby:2.7.8
 | 
			
		||||
FROM ruby:3.1.4
 | 
			
		||||
 | 
			
		||||
WORKDIR /liquorcabinet
 | 
			
		||||
ENV RACK_ENV=staging
 | 
			
		||||
ENV RACK_ENV=production
 | 
			
		||||
 | 
			
		||||
COPY Gemfile Gemfile.lock /liquorcabinet/
 | 
			
		||||
RUN bundle install
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Gemfile
									
									
									
									
									
								
							@ -3,8 +3,9 @@ source "https://rubygems.org"
 | 
			
		||||
gem "sinatra", "~> 2.2.0"
 | 
			
		||||
gem "sinatra-contrib", "~> 2.2.0"
 | 
			
		||||
gem "activesupport", "~> 6.1.0"
 | 
			
		||||
gem "rest-client", "~> 2.1.0"
 | 
			
		||||
gem "redis", "~> 4.6.0"
 | 
			
		||||
gem "rest-client", "~> 2.1.0"
 | 
			
		||||
gem "aws-sigv4", "~> 1.0.0"
 | 
			
		||||
# Remove require when we can update to 3.0, which sets the new storage
 | 
			
		||||
# format to columnar by default. Increases performance
 | 
			
		||||
gem "mime-types"
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ GEM
 | 
			
		||||
      zeitwerk (~> 2.3)
 | 
			
		||||
    addressable (2.8.5)
 | 
			
		||||
      public_suffix (>= 2.0.2, < 6.0)
 | 
			
		||||
    aws-sigv4 (1.0.3)
 | 
			
		||||
    base64 (0.1.1)
 | 
			
		||||
    concurrent-ruby (1.2.2)
 | 
			
		||||
    crack (0.4.5)
 | 
			
		||||
@ -93,6 +94,7 @@ PLATFORMS
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES
 | 
			
		||||
  activesupport (~> 6.1.0)
 | 
			
		||||
  aws-sigv4 (~> 1.0.0)
 | 
			
		||||
  m
 | 
			
		||||
  mime-types
 | 
			
		||||
  minitest
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,11 @@ development: &defaults
 | 
			
		||||
  maintenance: false
 | 
			
		||||
  redis:
 | 
			
		||||
    host: <%= ENV["REDIS_HOST"] || "localhost" %>
 | 
			
		||||
    port: <%= ENV["REDIS_PORT"] || "6379" %>
 | 
			
		||||
    db: <%= ENV["REDIS_DB"] || 0 %>
 | 
			
		||||
    port: <%= ENV["REDIS_PORT"] || 6379 %>
 | 
			
		||||
    db: <%= ENV["REDIS_DB"] || 1 %>
 | 
			
		||||
  s3: &s3_defaults
 | 
			
		||||
    endpoint: <%= ENV["S3_ENDPOINT"] || "http://127.0.0.1:9000" %>
 | 
			
		||||
    region: <%= ENV["S3_REGION"] %>
 | 
			
		||||
    region: <%= ENV["S3_REGION"] || "us-east-1" %>
 | 
			
		||||
    access_key_id: <%= ENV["S3_ACCESS_KEY"] || "minioadmin" %>
 | 
			
		||||
    secret_key_id: <%= ENV["S3_SECRET_KEY"] || "minioadmin" %>
 | 
			
		||||
    bucket: <%= ENV["S3_BUCKET"] || "rs-development" %>
 | 
			
		||||
 | 
			
		||||
@ -10,18 +10,26 @@ module RemoteStorage
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    # S3 already wraps the ETag around quotes
 | 
			
		||||
    def s3_signer
 | 
			
		||||
      signer ||= Aws::Sigv4::Signer.new(
 | 
			
		||||
        service: 's3',
 | 
			
		||||
        region: settings.s3["region"],
 | 
			
		||||
        access_key_id: settings.s3["access_key_id"].to_s,
 | 
			
		||||
        secret_access_key: settings.s3["secret_key_id"].to_s
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # S3 already wraps the ETag with quotes
 | 
			
		||||
    def format_etag(etag)
 | 
			
		||||
      etag
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def do_put_request(url, data, content_type)
 | 
			
		||||
      deal_with_unauthorized_requests do
 | 
			
		||||
        md5 = Digest::MD5.base64digest(data)
 | 
			
		||||
        authorization_headers = authorization_headers_for(
 | 
			
		||||
          "PUT", url, md5, content_type
 | 
			
		||||
        ).merge({ "Content-Type" => content_type, "Content-Md5" => md5 })
 | 
			
		||||
        res = RestClient.put(url, data, authorization_headers)
 | 
			
		||||
        headers = { "Content-Type" => content_type }
 | 
			
		||||
        auth_headers = auth_headers_for("PUT", url, headers, data)
 | 
			
		||||
 | 
			
		||||
        res = RestClient.put(url, data, headers.merge(auth_headers))
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
          res.headers[:etag].delete('"'),
 | 
			
		||||
@ -32,24 +40,24 @@ module RemoteStorage
 | 
			
		||||
 | 
			
		||||
    def do_get_request(url, &block)
 | 
			
		||||
      deal_with_unauthorized_requests do
 | 
			
		||||
        headers = { }
 | 
			
		||||
        headers = {}
 | 
			
		||||
        headers["Range"] = server.env["HTTP_RANGE"] if server.env["HTTP_RANGE"]
 | 
			
		||||
        authorization_headers = authorization_headers_for("GET", url)
 | 
			
		||||
        RestClient.get(url, authorization_headers.merge(headers), &block)
 | 
			
		||||
        auth_headers = auth_headers_for("GET", url, headers)
 | 
			
		||||
        RestClient.get(url, headers.merge(auth_headers), &block)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def do_head_request(url, &block)
 | 
			
		||||
      deal_with_unauthorized_requests do
 | 
			
		||||
        authorization_headers = authorization_headers_for("HEAD", url)
 | 
			
		||||
        RestClient.head(url, authorization_headers, &block)
 | 
			
		||||
        auth_headers = auth_headers_for("HEAD", url)
 | 
			
		||||
        RestClient.head(url, auth_headers, &block)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def do_delete_request(url)
 | 
			
		||||
      deal_with_unauthorized_requests do
 | 
			
		||||
        authorization_headers = authorization_headers_for("DELETE", url)
 | 
			
		||||
        RestClient.delete(url, authorization_headers)
 | 
			
		||||
        auth_headers = auth_headers_for("DELETE", url)
 | 
			
		||||
        RestClient.delete(url, auth_headers)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@ -67,40 +75,17 @@ module RemoteStorage
 | 
			
		||||
      return found
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # This is using the S3 authorizations, not the newer AW V4 Signatures
 | 
			
		||||
    # (https://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html)
 | 
			
		||||
    def authorization_headers_for(http_verb, url, md5 = nil, content_type = nil)
 | 
			
		||||
      url = File.join("/", url.gsub(base_url, ""))
 | 
			
		||||
      date = Time.now.httpdate
 | 
			
		||||
      signed_data = generate_s3_signature(http_verb, md5, content_type, date, url)
 | 
			
		||||
      {
 | 
			
		||||
        "Authorization" => "AWS #{credentials[:access_key_id]}:#{signed_data}",
 | 
			
		||||
        "Date" => date
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def credentials
 | 
			
		||||
      @credentials ||= { access_key_id: settings.s3["access_key_id"], secret_key_id: settings.s3["secret_key_id"] }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def digest(secret, string_to_sign)
 | 
			
		||||
      Base64.encode64(hmac(secret, string_to_sign)).strip
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def hmac(key, value)
 | 
			
		||||
      OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), key, value)
 | 
			
		||||
    def auth_headers_for(http_method, url, headers = {}, data = nil)
 | 
			
		||||
      signature = s3_signer.sign_request(
 | 
			
		||||
        http_method: http_method, url: url, headers: headers, body: data
 | 
			
		||||
      )
 | 
			
		||||
      signature.headers
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def uri_escape(s)
 | 
			
		||||
      CGI.escape(s).gsub('%5B', '[').gsub('%5D', ']')
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def generate_s3_signature(http_verb, md5, content_type, date, url)
 | 
			
		||||
      string_to_sign = [http_verb, md5, content_type, date, url].join "\n"
 | 
			
		||||
      signature = digest(credentials[:secret_key_id], string_to_sign)
 | 
			
		||||
      uri_escape(signature)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def base_url
 | 
			
		||||
      @base_url ||= settings.s3["endpoint"]
 | 
			
		||||
    end
 | 
			
		||||
@ -109,5 +94,4 @@ module RemoteStorage
 | 
			
		||||
      "#{base_url}/#{settings.s3["bucket"]}/#{user}"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -64,7 +64,7 @@ class LiquorCabinet < Sinatra::Base
 | 
			
		||||
      headers 'Access-Control-Allow-Origin' => '*',
 | 
			
		||||
              'Access-Control-Allow-Methods' => 'GET, PUT, DELETE',
 | 
			
		||||
              'Access-Control-Allow-Headers' => 'Authorization, Content-Type, Origin, If-Match, If-None-Match, Range',
 | 
			
		||||
              'Access-Control-Expose-Headers' => 'ETag, Content-Length, Content-Range',
 | 
			
		||||
              'Access-Control-Expose-Headers' => 'ETag, Content-Length, Content-Range, Content-Type',
 | 
			
		||||
              'Accept-Ranges' => 'bytes'
 | 
			
		||||
      headers['Access-Control-Allow-Origin'] = env["HTTP_ORIGIN"] if env["HTTP_ORIGIN"]
 | 
			
		||||
      headers['Cache-Control'] = 'no-cache'
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								run-dev.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										11
									
								
								run-dev.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
RACK_ENV=development \
 | 
			
		||||
REDIS_HOST=localhost \
 | 
			
		||||
REDIS_PORT=6379 \
 | 
			
		||||
REDIS_DB=1 \
 | 
			
		||||
S3_ENDPOINT='http://localhost:9000' \
 | 
			
		||||
S3_ACCESS_KEY='dev-key' \
 | 
			
		||||
S3_SECRET_KEY='123456789' \
 | 
			
		||||
S3_BUCKET=remotestorage \
 | 
			
		||||
bundle exec rackup -p 4567
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user