globalid 0.3.7 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of globalid might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 16fab50b5f1ed999f159d4e28507577d1e240e2a
4
- data.tar.gz: 00c55d5c1b1b3b09178e3c1e75dc343389acb92f
2
+ SHA256:
3
+ metadata.gz: 91ad4d8c34957591df10703702ee81e259afaebe296bf0163788952bc3acdcd6
4
+ data.tar.gz: 1bd763051bf53016df8faf674dfab0f38aae178db11d0b66ca416cdd9ba0910d
5
5
  SHA512:
6
- metadata.gz: ee2a00103d0a6890701acd2674a7817335391f54e67d44746c1883711329e5707d3c26089f5fe9823322fe044d52e9b7167ae8f0b7d86af1c88ff379d269f6e1
7
- data.tar.gz: 5da58eabadc4ddc3af4231520db1c3b09bc780ac8b842e4a93f70a6ab1523a81c7112d5377569b2643b755e4c06cb9e764b75e9b1078fa41b48f909f02ed25d3
6
+ metadata.gz: 00ece65591def5d2420aa732d8a7482c14922f45bd10c6b3be55f4340587d90985e967e9f68951fb840440c1584785a6c9a5f6ff0db8c38e2539cbd6ea842c94
7
+ data.tar.gz: c34a0627c13e63e3535be150787eca885d0bbb717e66f5b0700cb5a9ccb30446af260defa7b3a79ad0371f4f1235d77079520c6e67148d10edc6f2bf458a700a
data/README.md CHANGED
@@ -24,17 +24,17 @@ Mix `GlobalID::Identification` into any model with a `#find(id)` class method.
24
24
  Support is automatically included in Active Record.
25
25
 
26
26
  ```ruby
27
- >> person_gid = Person.find(1).to_global_id
28
- => #<GlobalID ...
27
+ person_gid = Person.find(1).to_global_id
28
+ # => #<GlobalID ...
29
29
 
30
- >> person_gid.uri
31
- => #<URI ...
30
+ person_gid.uri
31
+ # => #<URI ...
32
32
 
33
- >> person_gid.to_s
34
- => "gid://app/Person/1"
33
+ person_gid.to_s
34
+ # => "gid://app/Person/1"
35
35
 
36
- >> GlobalID::Locator.locate person_gid
37
- => #<Person:0x007fae94bf6298 @id="1">
36
+ GlobalID::Locator.locate person_gid
37
+ # => #<Person:0x007fae94bf6298 @id="1">
38
38
  ```
39
39
 
40
40
  ### Signed Global IDs
@@ -42,53 +42,125 @@ Support is automatically included in Active Record.
42
42
  For added security GlobalIDs can also be signed to ensure that the data hasn't been tampered with.
43
43
 
44
44
  ```ruby
45
- >> person_sgid = Person.find(1).to_signed_global_id
46
- => #<SignedGlobalID:0x007fea1944b410
45
+ person_sgid = Person.find(1).to_signed_global_id
46
+ # => #<SignedGlobalID:0x007fea1944b410>
47
47
 
48
- >> person_sgid = Person.find(1).to_sgid
49
- => #<SignedGlobalID:0x007fea1944b410
48
+ person_sgid = Person.find(1).to_sgid
49
+ # => #<SignedGlobalID:0x007fea1944b410>
50
50
 
51
- >> person_sgid.to_s
52
- => "BAhJIh5naWQ6Ly9pZGluYWlkaS9Vc2VyLzM5NTk5BjoGRVQ=--81d7358dd5ee2ca33189bb404592df5e8d11420e"
51
+ person_sgid.to_s
52
+ # => "BAhJIh5naWQ6Ly9pZGluYWlkaS9Vc2VyLzM5NTk5BjoGRVQ=--81d7358dd5ee2ca33189bb404592df5e8d11420e"
53
53
 
54
- >> GlobalID::Locator.locate_signed person_sgid
55
- => #<Person:0x007fae94bf6298 @id="1">
54
+ GlobalID::Locator.locate_signed person_sgid
55
+ # => #<Person:0x007fae94bf6298 @id="1">
56
+ ```
57
+
58
+ **Expiration**
59
+
60
+ Signed Global IDs can expire some time in the future. This is useful if there's a resource
61
+ people shouldn't have indefinite access to, like a share link.
62
+
63
+ ```ruby
64
+ expiring_sgid = Document.find(5).to_sgid(expires_in: 2.hours, for: 'sharing')
65
+ # => #<SignedGlobalID:0x008fde45df8937 ...>
56
66
 
67
+ # Within 2 hours...
68
+ GlobalID::Locator.locate_signed(expiring_sgid.to_s, for: 'sharing')
69
+ # => #<Document:0x007fae94bf6298 @id="5">
70
+
71
+ # More than 2 hours later...
72
+ GlobalID::Locator.locate_signed(expiring_sgid.to_s, for: 'sharing')
73
+ # => nil
57
74
  ```
58
- You can even bump the security up some more by explaining what purpose a Signed Global ID is for.
59
- In this way evildoers can't reuse a sign-up form's SGID on the login page. For example.
75
+
76
+ **In Rails, an auto-expiry of 1 month is set by default.** You can alter that deal
77
+ in an initializer with:
60
78
 
61
79
  ```ruby
62
- >> signup_person_sgid = Person.find(1).to_sgid(for: 'signup_form')
63
- => #<SignedGlobalID:0x007fea1984b520
80
+ # config/initializers/global_id.rb
81
+ Rails.application.config.global_id.expires_in = 3.months
82
+ ```
83
+
84
+ You can assign a default SGID lifetime like so:
64
85
 
65
- >> GlobalID::Locator.locate_signed(signup_person_sgid.to_s, for: 'signup_form')
66
- => #<Person:0x007fae94bf6298 @id="1">
86
+ ```ruby
87
+ SignedGlobalID.expires_in = 1.month
67
88
  ```
68
89
 
69
- You can also have SGIDs that expire some time in the future. This is useful if there's a resource,
70
- people shouldn't have indefinite access to, like a share link.
90
+ This way any generated SGID will use that relative expiry.
91
+
92
+ It's worth noting that _expiring SGIDs are not idempotent_ because they encode the current timestamp; repeated calls to `to_sgid` will produce different results. For example, in Rails
71
93
 
72
94
  ```ruby
73
- >> expiring_sgid = Document.find(5).to_sgid(expires_in: 2.hours, for: 'sharing')
74
- => #<SignedGlobalID:0x008fde45df8937
95
+ Document.find(5).to_sgid.to_s == Document.find(5).to_sgid.to_s
96
+ # => false
97
+ ```
75
98
 
76
- # Within 2 hours...
77
- >> GlobalID::Locator.locate_signed(expiring_sgid.to_s, for: 'sharing')
78
- => #<Document:0x007fae94bf6298 @id="5">
99
+ You need to explicitly pass `expires_in: nil` to generate a permanent SGID that will not expire,
79
100
 
80
- # More than 2 hours later...
81
- >> GlobalID::Locator.locate_signed(expiring_sgid.to_s, for: 'sharing')
82
- => nil
101
+ ```ruby
102
+ # Passing a false value to either expiry option turns off expiration entirely.
103
+ never_expiring_sgid = Document.find(5).to_sgid(expires_in: nil)
104
+ # => #<SignedGlobalID:0x008fde45df8937 ...>
83
105
 
84
- >> explicit_expiring_sgid = SecretAgentMessage.find(5).to_sgid(expires_at: Time.now.advance(hours: 1))
85
- => #<SignedGlobalID:0x008fde45df8937
106
+ # Any time later...
107
+ GlobalID::Locator.locate_signed never_expiring_sgid
108
+ # => #<Document:0x007fae94bf6298 @id="5">
109
+ ```
110
+
111
+ It's also possible to pass a specific expiry time
112
+
113
+ ```ruby
114
+ explicit_expiring_sgid = SecretAgentMessage.find(5).to_sgid(expires_at: Time.now.advance(hours: 1))
115
+ # => #<SignedGlobalID:0x008fde45df8937 ...>
86
116
 
87
117
  # 1 hour later...
88
- >> GlobalID::Locator.locate_signed explicit_expiring_sgid.to_s
89
- => nil
118
+ GlobalID::Locator.locate_signed explicit_expiring_sgid.to_s
119
+ # => nil
120
+ ```
121
+ Note that an explicit `:expires_at` takes precedence over a relative `:expires_in`.
122
+
123
+ **Purpose**
124
+
125
+ You can even bump the security up some more by explaining what purpose a Signed Global ID is for.
126
+ In this way evildoers can't reuse a sign-up form's SGID on the login page. For example.
127
+
128
+ ```ruby
129
+ signup_person_sgid = Person.find(1).to_sgid(for: 'signup_form')
130
+ # => #<SignedGlobalID:0x007fea1984b520
131
+
132
+ GlobalID::Locator.locate_signed(signup_person_sgid.to_s, for: 'signup_form')
133
+ # => #<Person:0x007fae94bf6298 @id="1">
134
+ ```
135
+
136
+ ### Locating many Global IDs
137
+
138
+ When needing to locate many Global IDs use `GlobalID::Locator.locate_many` or `GlobalID::Locator.locate_many_signed` for Signed Global IDs to allow loading
139
+ Global IDs more efficiently.
140
+
141
+ For instance, the default locator passes every `model_id` per `model_name` thus
142
+ using `model_name.where(id: model_ids)` versus `GlobalID::Locator.locate`'s `model_name.find(id)`.
143
+
144
+ In the case of looking up Global IDs from a database, it's only necessary to query
145
+ once per `model_name` as shown here:
146
+
147
+ ```ruby
148
+ gids = users.concat(people).sort_by(&:id).map(&:to_global_id)
149
+ # => [#<GlobalID:0x00007ffd6a8411a0 @uri=#<URI::GID gid://app/User/1>>,
150
+ #<GlobalID:0x00007ffd675d32b8 @uri=#<URI::GID gid://app/Student/1>>,
151
+ #<GlobalID:0x00007ffd6a840b10 @uri=#<URI::GID gid://app/User/2>>,
152
+ #<GlobalID:0x00007ffd675d2c28 @uri=#<URI::GID gid://app/Student/2>>,
153
+ #<GlobalID:0x00007ffd6a840480 @uri=#<URI::GID gid://app/User/3>>,
154
+ #<GlobalID:0x00007ffd675d2598 @uri=#<URI::GID gid://app/Student/3>>]
155
+
156
+ GlobalID::Locator.locate_many gids
157
+ # SELECT "users".* FROM "users" WHERE "users"."id" IN ($1, $2, $3) [["id", 1], ["id", 2], ["id", 3]]
158
+ # SELECT "students".* FROM "students" WHERE "students"."id" IN ($1, $2, $3) [["id", 1], ["id", 2], ["id", 3]]
159
+ # => [#<User id: 1>, #<Student id: 1>, #<User id: 2>, #<Student id: 2>, #<User id: 3>, #<Student id: 3>]
90
160
  ```
91
161
 
162
+ Note the order is maintained in the returned results.
163
+
92
164
  ### Custom App Locator
93
165
 
94
166
  A custom locator can be set for an app by calling `GlobalID::Locator.use` and providing an app locator to use for that app.
data/lib/global_id.rb CHANGED
@@ -1,8 +1,19 @@
1
1
  require 'global_id/global_id'
2
+ require 'active_support'
2
3
 
3
4
  autoload :SignedGlobalID, 'global_id/signed_global_id'
4
5
 
5
6
  class GlobalID
6
- autoload :Locator, 'global_id/locator'
7
- autoload :Identification, 'global_id/identification'
7
+ extend ActiveSupport::Autoload
8
+
9
+ eager_autoload do
10
+ autoload :Locator
11
+ autoload :Identification
12
+ autoload :Verifier
13
+ end
14
+
15
+ def self.eager_load!
16
+ super
17
+ require 'global_id/signed_global_id'
18
+ end
8
19
  end
@@ -63,6 +63,11 @@ class GlobalID
63
63
  def ==(other)
64
64
  other.is_a?(GlobalID) && @uri == other.uri
65
65
  end
66
+ alias_method :eql?, :==
67
+
68
+ def hash
69
+ self.class.hash | @uri.hash
70
+ end
66
71
 
67
72
  def to_param
68
73
  # remove the = padding character for a prettier param -- it'll be added back in parse_encoded_gid
@@ -1,11 +1,7 @@
1
- require 'active_support/concern'
2
-
3
1
  class GlobalID
4
2
  module Identification
5
- extend ActiveSupport::Concern
6
-
7
3
  def to_global_id(options = {})
8
- @global_id ||= GlobalID.create(self, options)
4
+ GlobalID.create(self, options)
9
5
  end
10
6
  alias to_gid to_global_id
11
7
 
@@ -5,24 +5,28 @@ else
5
5
  require 'global_id'
6
6
  require 'active_support'
7
7
  require 'active_support/core_ext/string/inflections'
8
+ require 'active_support/core_ext/integer/time'
8
9
 
9
10
  class GlobalID
10
11
  # = GlobalID Railtie
11
12
  # Set up the signed GlobalID verifier and include Active Record support.
12
13
  class Railtie < Rails::Railtie # :nodoc:
13
14
  config.global_id = ActiveSupport::OrderedOptions.new
15
+ config.eager_load_namespaces << GlobalID
14
16
 
15
17
  initializer 'global_id' do |app|
18
+ default_expires_in = 1.month
19
+ default_app_name = app.railtie_name.remove('_application').dasherize
16
20
 
17
- app.config.global_id.app ||= app.railtie_name.remove('_application').dasherize
18
- GlobalID.app = app.config.global_id.app
19
-
20
- app.config.global_id.expires_in ||= 1.month
21
- SignedGlobalID.expires_in = app.config.global_id.expires_in
21
+ GlobalID.app = app.config.global_id.app ||= default_app_name
22
+ SignedGlobalID.expires_in = app.config.global_id.fetch(:expires_in, default_expires_in)
22
23
 
23
24
  config.after_initialize do
25
+ GlobalID.app = app.config.global_id.app ||= default_app_name
26
+ SignedGlobalID.expires_in = app.config.global_id.fetch(:expires_in, default_expires_in)
27
+
24
28
  app.config.global_id.verifier ||= begin
25
- app.message_verifier(:signed_global_ids)
29
+ GlobalID::Verifier.new(app.key_generator.generate_key('signed_global_ids'))
26
30
  rescue ArgumentError
27
31
  nil
28
32
  end
@@ -173,5 +173,9 @@ module URI
173
173
  end
174
174
  end
175
175
 
176
- @@schemes['GID'] = GID
176
+ if respond_to?(:register_scheme)
177
+ register_scheme('GID', GID)
178
+ else
179
+ @@schemes['GID'] = GID
180
+ end
177
181
  end
@@ -0,0 +1,15 @@
1
+ require 'active_support'
2
+ require 'active_support/message_verifier'
3
+
4
+ class GlobalID
5
+ class Verifier < ActiveSupport::MessageVerifier
6
+ private
7
+ def encode(data)
8
+ ::Base64.urlsafe_encode64(data)
9
+ end
10
+
11
+ def decode(data)
12
+ ::Base64.urlsafe_decode64(data)
13
+ end
14
+ end
15
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: globalid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-26 00:00:00.000000000 Z
11
+ date: 2021-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.1.0
19
+ version: '5.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 4.1.0
26
+ version: '5.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -53,6 +53,7 @@ files:
53
53
  - lib/global_id/railtie.rb
54
54
  - lib/global_id/signed_global_id.rb
55
55
  - lib/global_id/uri/gid.rb
56
+ - lib/global_id/verifier.rb
56
57
  - lib/globalid.rb
57
58
  homepage: http://www.rubyonrails.org
58
59
  licenses:
@@ -66,15 +67,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
67
  requirements:
67
68
  - - ">="
68
69
  - !ruby/object:Gem::Version
69
- version: 1.9.3
70
+ version: 2.6.0
70
71
  required_rubygems_version: !ruby/object:Gem::Requirement
71
72
  requirements:
72
73
  - - ">="
73
74
  - !ruby/object:Gem::Version
74
75
  version: '0'
75
76
  requirements: []
76
- rubyforge_project:
77
- rubygems_version: 2.4.5.1
77
+ rubygems_version: 3.2.24
78
78
  signing_key:
79
79
  specification_version: 4
80
80
  summary: 'Refer to any model with a URI: gid://app/class/id'