gcs-signer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []