gcs-signer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +57 -0
  3. data/lib/gcs_signer.rb +122 -0
  4. metadata +90 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d9da9710c3ec0d2781bb3d0002fc104a9f898ad9
4
+ data.tar.gz: 8027a48a1276f076f9d0a46a5bd9aa0841da712e
5
+ SHA512:
6
+ metadata.gz: 3b1303e21df8a93d2cd23a8ef1636a30d79ff3238643524adfc52efa7b082df53fdcb2d5fbdaa85e6a89b2ef06323d31d481a906106a895ca42bc6b7fb4687d5
7
+ data.tar.gz: 8e37da7d200265cdeb64ae82a0395a6c66a26ae92b1f9bb7d1e25698b30a349be783d372114cacb2605a3238e9afcb99fa8909fd8f4586421dbe7edb77edda6f
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # gcs-signer
2
+
3
+ Simple signed URL generator for Google Cloud Storage.
4
+
5
+ ## Features
6
+
7
+ * No additional gems required.
8
+ * No network connection required to generate signed URL.
9
+ * Can read JSON service_account credentials from environment variables. So it can be used with [google-cloud-ruby](https://github.com/GoogleCloudPlatform/google-cloud-ruby) without additional configurations.
10
+
11
+ ## Installation
12
+
13
+ ```shell
14
+ gem install gcs-signer
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ If you already configured `GOOGLE_CLOUD_KEYFILE` or `GOOGLE_CLOUD_KEYFILE_JSON` for google-cloud-ruby gem, just
20
+
21
+ ```ruby
22
+ signer = GcsSigner.new
23
+ ```
24
+
25
+ or you can give path of the service_account json file, or contents of it.
26
+
27
+ ```ruby
28
+ signer = GcsSigner.new path: "/home/leo/path/to/service_account.json"
29
+
30
+ signer = GcsSigner.new json_string: '{ "type": "service_account", ...'
31
+ ```
32
+
33
+ then `#sign_url` to generate signed URL.
34
+
35
+ ```ruby
36
+ # The signed URL is valid for 5 minutes by default.
37
+ signer.sign_url "bucket-name", "path/to/object"
38
+
39
+ # You can specify timestamps or how many seconds the signed URL is valid for.
40
+ signer.sign_url "bucket-name", "object-name",
41
+ expires: Time.new(2016, 12, 26, 14, 31, 48)
42
+
43
+ signer.sign_url "bucket-name", "object_name", valid_for: 600
44
+
45
+ # If you use AcriveSupport in your project, you can also do some magic like:
46
+ signer.sign_url "buekct", "object", valid_for: 45.minutes
47
+
48
+ # See https://cloud.google.com/storage/docs/access-control/signed-urls
49
+ # for other avaliable options.
50
+ signer.sign_url "buekct", "object", google_access_id: "sangwon@sha.kr",
51
+ method: "PUT", content_type: "text/plain",
52
+ md5: "beefbeef..."
53
+ ```
54
+
55
+ ## License
56
+
57
+ gcs-signer is distributed under the MIT License.
data/lib/gcs_signer.rb ADDED
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+ require "uri"
3
+ require "openssl"
4
+ require "base64"
5
+ require "json"
6
+
7
+ # Creates signed_url for a file on Google Cloud Storage.
8
+ #
9
+ # signer = GcsSigner.new(path: "/Users/leo/private/service_account.json")
10
+ # signer.sign "your-bucket", "object/name"
11
+ # # => "https://storage.googleapis.com/your-bucket/object/name?..."
12
+ class GcsSigner
13
+ VERSION = "0.1.0"
14
+
15
+ # gcs-signer requires credential that can access to GCS.
16
+ # [path] the path of the service_account json file.
17
+ # [json_string] ...or the content of the service_account json file.
18
+ #
19
+ # or if you also use \+google-cloud+ gem. you can authenticate
20
+ # using environment variable that uses.
21
+ def initialize(path: nil, json_string: nil)
22
+ json_string ||= File.read(path) unless path.nil?
23
+ json_string = look_for_environment_variables if json_string.nil?
24
+
25
+ fail AuthError, "No credentials given." if json_string.nil?
26
+ @credentials = JSON.parse(json_string)
27
+ @key = OpenSSL::PKey::RSA.new(@credentials["private_key"])
28
+ end
29
+
30
+ # @return [String] Signed url
31
+ # Generates signed url.
32
+ # [bucket] the name of the Cloud Storage bucket that contains the object.
33
+ # [object_name] the name of the object for signed url..
34
+ # Variable options are available:
35
+ # [expires] Time(stamp in UTC) when the signed url expires.
36
+ # [valid_for] ...or how much seconds is the signed url available.
37
+ # If you don't set those values, it will set to 300 seconds by default.
38
+ #
39
+ # # default is 5 minutes
40
+ # signer.sign_url("bucket-name", "path/to/file")
41
+ #
42
+ # # You can give Time object.
43
+ # signer.sign_url("bucket-name", "path/to/file",
44
+ # expires: Time.new(2016, 12, 26, 14, 31, 48, "+09:00"))
45
+ #
46
+ # # You can give how much seconds is the signed url valid.
47
+ # signer.sign_url("bucket", "path/to/file", valid_for: 30 * 60)
48
+ #
49
+ # # If you use ActiveSupport, you can also do some magic.
50
+ # signer.sign_url("bucket", "path/to/file", valid_for: 40.minutes)
51
+ #
52
+ # For details information of another options,
53
+ # like \+method+, \+md5+, and \+content_type+. See:
54
+ # https://cloud.google.com/storage/docs/access-control/signed-urls
55
+ #
56
+ # Just in case if you want to change \+GoogleAccessId+ in the signed url,
57
+ # You can set \+google_access_id+ as an option.
58
+ def sign_url(bucket, object_name, options = {})
59
+ options = apply_default_options(options)
60
+
61
+ url = URI.join "https://storage.googleapis.com",
62
+ URI.escape("/#{bucket}/"), URI.escape(object_name)
63
+ signature = sign string_that_will_be_signed(url, options)
64
+ url.query = query_for_signed_url(signature, options)
65
+ url.to_s
66
+ end
67
+
68
+ # @return [String] contains \+project_id+ and \+client_email+
69
+ # Prevents confidential information (like private key) from exposing
70
+ # when used with interactive shell such as \+pry+ and \+irb+.
71
+ def inspect
72
+ "#<GcsSigner " \
73
+ "project_id: #{@credentials['project_id']} " \
74
+ "client_email: #{@credentials['client_email']}>"
75
+ end
76
+
77
+ private
78
+
79
+ # Look for environment variable which stores service_account.
80
+ def look_for_environment_variables
81
+ unless ENV["GOOGLE_CLOUD_KEYFILE"].nil?
82
+ return File.read(ENV["GOOGLE_CLOUD_KEYFILE"])
83
+ end
84
+
85
+ ENV["GOOGLE_CLOUD_KEYFILE_JSON"]
86
+ end
87
+
88
+ # Signs the string with the given private key.
89
+ def sign(string)
90
+ @key.sign OpenSSL::Digest::SHA256.new, string
91
+ end
92
+
93
+ def apply_default_options(options)
94
+ {
95
+ method: "GET", content_md5: nil,
96
+ content_type: nil,
97
+ expires: Time.now.utc.to_i + (options[:valid_for] || 300).to_i,
98
+ google_access_id: @credentials["client_email"]
99
+ }.merge(options)
100
+ end
101
+
102
+ def string_that_will_be_signed(url, options)
103
+ [
104
+ options[:method],
105
+ options[:content_md5],
106
+ options[:content_type],
107
+ options[:expires].to_i,
108
+ url.path
109
+ ].join "\n"
110
+ end
111
+
112
+ # Escapes and generates query string for actual result.
113
+ def query_for_signed_url(signature, options)
114
+ URI.encode_www_form GoogleAccessId: options[:google_access_id],
115
+ Expires: options[:expires].to_i,
116
+ Signature: Base64.strict_encode64(signature)
117
+ end
118
+
119
+ # raised When GcsSigner could not find service_account JSON file.
120
+ class AuthError < StandardError
121
+ end
122
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gcs-signer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sangwon Yi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '12.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '12.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.40.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.40.0
55
+ description: |2
56
+ Simple signed URL generator for Google Cloud Storage.
57
+ No additional gems and API requests required to generate signed URL.
58
+ email:
59
+ - sangwon@sha.kr
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - README.md
65
+ - lib/gcs_signer.rb
66
+ homepage: https://github.com/shakrmedia/gcs-signer
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: '2.2'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.5.2
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: Simple signed URL generator for Google Cloud Storage.
90
+ test_files: []