globalid 0.4.1 → 0.6.0
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 +5 -5
- data/README.md +95 -49
- data/lib/global_id/fixture_set.rb +13 -0
- data/lib/global_id/global_id.rb +5 -0
- data/lib/global_id/identification.rb +1 -5
- data/lib/global_id/railtie.rb +13 -5
- data/lib/global_id/uri/gid.rb +5 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4cfa513116ef143c878f8b6eefc242f9df668572c0ed3f3b4f5f5f87736e143f
|
4
|
+
data.tar.gz: 72e073ae2eec42a6038cda7bbf77bbeed3f72899eafe801bac1aca6c6bb24079
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3527628dfa02f3099b1c4ac0f99260f9187d83702e2cca96fab261592466fba1caa82e3dc936a54660ac9c4c0a2da087ebdabfb0d21444928979dd5cc983a34
|
7
|
+
data.tar.gz: 460c643ca0f4d0a51dc9e218da9a891f05904d75e9106692d38a46fa7cc9ad1ace229e5bce37703e2f4e62aa049246628d61de9be444a32cd2a3e6a1452d0ff3
|
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
|
-
|
28
|
-
=> #<GlobalID ...
|
27
|
+
person_gid = Person.find(1).to_global_id
|
28
|
+
# => #<GlobalID ...
|
29
29
|
|
30
|
-
|
31
|
-
=> #<URI ...
|
30
|
+
person_gid.uri
|
31
|
+
# => #<URI ...
|
32
32
|
|
33
|
-
|
34
|
-
=> "gid://app/Person/1"
|
33
|
+
person_gid.to_s
|
34
|
+
# => "gid://app/Person/1"
|
35
35
|
|
36
|
-
|
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,79 +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
|
-
|
46
|
-
=> #<SignedGlobalID:0x007fea1944b410>
|
45
|
+
person_sgid = Person.find(1).to_signed_global_id
|
46
|
+
# => #<SignedGlobalID:0x007fea1944b410>
|
47
47
|
|
48
|
-
|
49
|
-
=> #<SignedGlobalID:0x007fea1944b410>
|
48
|
+
person_sgid = Person.find(1).to_sgid
|
49
|
+
# => #<SignedGlobalID:0x007fea1944b410>
|
50
50
|
|
51
|
-
|
52
|
-
=> "BAhJIh5naWQ6Ly9pZGluYWlkaS9Vc2VyLzM5NTk5BjoGRVQ=--81d7358dd5ee2ca33189bb404592df5e8d11420e"
|
53
|
-
|
54
|
-
>> GlobalID::Locator.locate_signed person_sgid
|
55
|
-
=> #<Person:0x007fae94bf6298 @id="1">
|
51
|
+
person_sgid.to_s
|
52
|
+
# => "BAhJIh5naWQ6Ly9pZGluYWlkaS9Vc2VyLzM5NTk5BjoGRVQ=--81d7358dd5ee2ca33189bb404592df5e8d11420e"
|
56
53
|
|
54
|
+
GlobalID::Locator.locate_signed person_sgid
|
55
|
+
# => #<Person:0x007fae94bf6298 @id="1">
|
57
56
|
```
|
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.
|
60
57
|
|
61
|
-
|
62
|
-
>> signup_person_sgid = Person.find(1).to_sgid(for: 'signup_form')
|
63
|
-
=> #<SignedGlobalID:0x007fea1984b520
|
64
|
-
|
65
|
-
>> GlobalID::Locator.locate_signed(signup_person_sgid.to_s, for: 'signup_form')
|
66
|
-
=> #<Person:0x007fae94bf6298 @id="1">
|
67
|
-
```
|
58
|
+
**Expiration**
|
68
59
|
|
69
|
-
|
60
|
+
Signed Global IDs can expire some time in the future. This is useful if there's a resource
|
70
61
|
people shouldn't have indefinite access to, like a share link.
|
71
62
|
|
72
63
|
```ruby
|
73
|
-
|
74
|
-
=> #<SignedGlobalID:0x008fde45df8937 ...>
|
64
|
+
expiring_sgid = Document.find(5).to_sgid(expires_in: 2.hours, for: 'sharing')
|
65
|
+
# => #<SignedGlobalID:0x008fde45df8937 ...>
|
75
66
|
|
76
67
|
# Within 2 hours...
|
77
|
-
|
78
|
-
=> #<Document:0x007fae94bf6298 @id="5">
|
68
|
+
GlobalID::Locator.locate_signed(expiring_sgid.to_s, for: 'sharing')
|
69
|
+
# => #<Document:0x007fae94bf6298 @id="5">
|
79
70
|
|
80
71
|
# More than 2 hours later...
|
81
|
-
|
82
|
-
=> nil
|
72
|
+
GlobalID::Locator.locate_signed(expiring_sgid.to_s, for: 'sharing')
|
73
|
+
# => nil
|
74
|
+
```
|
83
75
|
|
84
|
-
|
85
|
-
|
76
|
+
**In Rails, an auto-expiry of 1 month is set by default.** You can alter that deal
|
77
|
+
in an initializer with:
|
86
78
|
|
87
|
-
|
88
|
-
|
89
|
-
|
79
|
+
```ruby
|
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:
|
90
85
|
|
86
|
+
```ruby
|
87
|
+
SignedGlobalID.expires_in = 1.month
|
88
|
+
```
|
89
|
+
|
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
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
Document.find(5).to_sgid.to_s == Document.find(5).to_sgid.to_s
|
96
|
+
# => false
|
97
|
+
```
|
98
|
+
|
99
|
+
You need to explicitly pass `expires_in: nil` to generate a permanent SGID that will not expire,
|
100
|
+
|
101
|
+
```ruby
|
91
102
|
# Passing a false value to either expiry option turns off expiration entirely.
|
92
|
-
|
93
|
-
=> #<SignedGlobalID:0x008fde45df8937 ...>
|
103
|
+
never_expiring_sgid = Document.find(5).to_sgid(expires_in: nil)
|
104
|
+
# => #<SignedGlobalID:0x008fde45df8937 ...>
|
94
105
|
|
95
106
|
# Any time later...
|
96
|
-
|
97
|
-
=> #<Document:0x007fae94bf6298 @id="5">
|
107
|
+
GlobalID::Locator.locate_signed never_expiring_sgid
|
108
|
+
# => #<Document:0x007fae94bf6298 @id="5">
|
98
109
|
```
|
99
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 ...>
|
116
|
+
|
117
|
+
# 1 hour later...
|
118
|
+
GlobalID::Locator.locate_signed explicit_expiring_sgid.to_s
|
119
|
+
# => nil
|
120
|
+
```
|
100
121
|
Note that an explicit `:expires_at` takes precedence over a relative `:expires_in`.
|
101
122
|
|
102
|
-
|
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.
|
103
127
|
|
104
128
|
```ruby
|
105
|
-
|
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">
|
106
134
|
```
|
107
135
|
|
108
|
-
|
136
|
+
### Locating many Global IDs
|
109
137
|
|
110
|
-
|
111
|
-
|
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:
|
112
146
|
|
113
147
|
```ruby
|
114
|
-
|
115
|
-
|
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>]
|
116
160
|
```
|
117
161
|
|
162
|
+
Note the order is maintained in the returned results.
|
163
|
+
|
118
164
|
### Custom App Locator
|
119
165
|
|
120
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.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class GlobalID
|
4
|
+
module FixtureSet
|
5
|
+
def signed_global_id(fixture_set_name, label, column_type: :integer, **options)
|
6
|
+
identifier = identify(label, column_type)
|
7
|
+
model_name = default_fixture_model_name(fixture_set_name)
|
8
|
+
uri = URI::GID.build([GlobalID.app, model_name, identifier, {}])
|
9
|
+
|
10
|
+
SignedGlobalID.new(uri, **options)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/global_id/global_id.rb
CHANGED
@@ -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
|
-
|
4
|
+
GlobalID.create(self, options)
|
9
5
|
end
|
10
6
|
alias to_gid to_global_id
|
11
7
|
|
data/lib/global_id/railtie.rb
CHANGED
@@ -5,6 +5,7 @@ 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
|
@@ -14,14 +15,16 @@ class GlobalID
|
|
14
15
|
config.eager_load_namespaces << GlobalID
|
15
16
|
|
16
17
|
initializer 'global_id' do |app|
|
18
|
+
default_expires_in = 1.month
|
19
|
+
default_app_name = app.railtie_name.remove('_application').dasherize
|
17
20
|
|
18
|
-
app.config.global_id.app ||=
|
19
|
-
|
20
|
-
|
21
|
-
app.config.global_id.expires_in ||= 1.month
|
22
|
-
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)
|
23
23
|
|
24
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
|
+
|
25
28
|
app.config.global_id.verifier ||= begin
|
26
29
|
GlobalID::Verifier.new(app.key_generator.generate_key('signed_global_ids'))
|
27
30
|
rescue ArgumentError
|
@@ -34,6 +37,11 @@ class GlobalID
|
|
34
37
|
require 'global_id/identification'
|
35
38
|
send :include, GlobalID::Identification
|
36
39
|
end
|
40
|
+
|
41
|
+
ActiveSupport.on_load(:active_record_fixture_set) do
|
42
|
+
require 'global_id/fixture_set'
|
43
|
+
send :extend, GlobalID::FixtureSet
|
44
|
+
end
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
data/lib/global_id/uri/gid.rb
CHANGED
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.
|
4
|
+
version: 0.6.0
|
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:
|
11
|
+
date: 2021-11-24 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:
|
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:
|
26
|
+
version: '5.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -47,6 +47,7 @@ files:
|
|
47
47
|
- MIT-LICENSE
|
48
48
|
- README.md
|
49
49
|
- lib/global_id.rb
|
50
|
+
- lib/global_id/fixture_set.rb
|
50
51
|
- lib/global_id/global_id.rb
|
51
52
|
- lib/global_id/identification.rb
|
52
53
|
- lib/global_id/locator.rb
|
@@ -67,15 +68,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
68
|
requirements:
|
68
69
|
- - ">="
|
69
70
|
- !ruby/object:Gem::Version
|
70
|
-
version:
|
71
|
+
version: 2.5.0
|
71
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
73
|
requirements:
|
73
74
|
- - ">="
|
74
75
|
- !ruby/object:Gem::Version
|
75
76
|
version: '0'
|
76
77
|
requirements: []
|
77
|
-
|
78
|
-
rubygems_version: 2.6.12
|
78
|
+
rubygems_version: 3.2.22
|
79
79
|
signing_key:
|
80
80
|
specification_version: 4
|
81
81
|
summary: 'Refer to any model with a URI: gid://app/class/id'
|