globalid 0.3.6 → 0.4.2

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: 14fc719e31ef97f014044c96e42be88e4b563a63
4
- data.tar.gz: ab0206d64fb1aa095311f86e4be0636d447c28d7
2
+ SHA256:
3
+ metadata.gz: 1c51189af124bc8712e1242070c33fa3bfd26c900003699a2b21328e8d197e55
4
+ data.tar.gz: b00783d7b5fd8b7def68e925d6f70e3ddfb70ad82c3603061c0d29e736dfa9f4
5
5
  SHA512:
6
- metadata.gz: fcfdb39821eb767d1b1f8d86b60813ab4c6a85f71956e294852f279252b7cb5952c5776cceaa23d578f7163ce7431e97fbc18b272254eb15a5cfd2679efc625d
7
- data.tar.gz: 2d7ac23c3a6eaf8036d2f42b12299b037438be020316622318f69c9a0df580dc87511610c55b316ba3fd0848e9f479912654979939077fb9f5aa28c712467b0d
6
+ metadata.gz: 5f5b1a859baae95d9efb693f8d9a0cfb008c82cedfbc3cc994b51cdf46d7191725317e31f8eee824ade1c04a9924d661ca1b9c04e7032ab9250aa20bf8e73614
7
+ data.tar.gz: c0ac25a21157363a3274e4e58ba99146fa2ce90e0860bbe7c21356a9af9d8013c0404bef2d6f4c793ca20aae287c26fa9625e4c74170574a1f4b048dd961fe0e
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 David Heinemeier Hansson
1
+ Copyright (c) 2014-2016 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # Global ID - Reference models by URI
2
+
3
+ A Global ID is an app wide URI that uniquely identifies a model instance:
4
+
5
+ gid://YourApp/Some::Model/id
6
+
7
+ This is helpful when you need a single identifier to reference different
8
+ classes of objects.
9
+
10
+ One example is job scheduling. We need to reference a model object rather than
11
+ serialize the object itself. We can pass a Global ID that can be used to locate
12
+ the model when it's time to perform the job. The job scheduler doesn't need to know
13
+ the details of model naming and IDs, just that it has a global identifier that
14
+ references a model.
15
+
16
+ Another example is a drop-down list of options, consisting of both Users and Groups.
17
+ Normally we'd need to come up with our own ad hoc scheme to reference them. With Global
18
+ IDs, we have a universal identifier that works for objects of both classes.
19
+
20
+
21
+ ## Usage
22
+
23
+ Mix `GlobalID::Identification` into any model with a `#find(id)` class method.
24
+ Support is automatically included in Active Record.
25
+
26
+ ```ruby
27
+ person_gid = Person.find(1).to_global_id
28
+ # => #<GlobalID ...
29
+
30
+ person_gid.uri
31
+ # => #<URI ...
32
+
33
+ person_gid.to_s
34
+ # => "gid://app/Person/1"
35
+
36
+ GlobalID::Locator.locate person_gid
37
+ # => #<Person:0x007fae94bf6298 @id="1">
38
+ ```
39
+
40
+ ### Signed Global IDs
41
+
42
+ For added security GlobalIDs can also be signed to ensure that the data hasn't been tampered with.
43
+
44
+ ```ruby
45
+ person_sgid = Person.find(1).to_signed_global_id
46
+ # => #<SignedGlobalID:0x007fea1944b410>
47
+
48
+ person_sgid = Person.find(1).to_sgid
49
+ # => #<SignedGlobalID:0x007fea1944b410>
50
+
51
+ person_sgid.to_s
52
+ # => "BAhJIh5naWQ6Ly9pZGluYWlkaS9Vc2VyLzM5NTk5BjoGRVQ=--81d7358dd5ee2ca33189bb404592df5e8d11420e"
53
+
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 ...>
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
74
+ ```
75
+
76
+ **In Rails, an auto-expiry of 1 month is set by default.** You can alter that deal
77
+ in an initializer with:
78
+
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:
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
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 ...>
105
+
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 ...>
116
+
117
+ # 1 hour later...
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
+ ### Custom App Locator
137
+
138
+ A custom locator can be set for an app by calling `GlobalID::Locator.use` and providing an app locator to use for that app.
139
+ A custom app locator is useful when different apps collaborate and reference each others' Global IDs.
140
+ When finding a Global ID's model, the locator to use is based on the app name provided in the Global ID url.
141
+
142
+ A custom locator can either be a block or a class.
143
+
144
+ Using a block:
145
+
146
+ ```ruby
147
+ GlobalID::Locator.use :foo do |gid|
148
+ FooRemote.const_get(gid.model_name).find(gid.model_id)
149
+ end
150
+ ```
151
+
152
+ Using a class:
153
+
154
+ ```ruby
155
+ GlobalID::Locator.use :bar, BarLocator.new
156
+ class BarLocator
157
+ def locate(gid)
158
+ @search_client.search name: gid.model_name, id: gid.model_id
159
+ end
160
+ end
161
+ ```
162
+
163
+ After defining locators as above, URIs like "gid://foo/Person/1" and "gid://bar/Person/1" will now use the foo block locator and `BarLocator` respectively.
164
+ Other apps will still keep using the default locator.
165
+
166
+ ## Contributing to GlobalID
167
+
168
+ GlobalID is work of many contributors. You're encouraged to submit pull requests, propose
169
+ features and discuss issues.
170
+
171
+ See [CONTRIBUTING](CONTRIBUTING.md).
172
+
173
+ ## License
174
+ GlobalID is released under the [MIT License](http://www.opensource.org/licenses/MIT).
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
@@ -5,7 +5,7 @@ class GlobalID
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  def to_global_id(options = {})
8
- @global_id ||= GlobalID.create(self, options)
8
+ GlobalID.create(self, options)
9
9
  end
10
10
  alias to_gid to_global_id
11
11
 
@@ -106,9 +106,7 @@ class GlobalID
106
106
 
107
107
  private
108
108
  def locator_for(gid)
109
- @locators.fetch(normalize_app(gid.app)) do
110
- gid.model_class.respond_to?(:unscoped) ? UNSCOPED_LOCATOR : DEFAULT_LOCATOR
111
- end
109
+ @locators.fetch(normalize_app(gid.app)) { DEFAULT_LOCATOR }
112
110
  end
113
111
 
114
112
  def find_allowed?(model_class, only = nil)
@@ -127,7 +125,7 @@ class GlobalID
127
125
  private
128
126
  @locators = {}
129
127
 
130
- class DefaultLocator
128
+ class BaseLocator
131
129
  def locate(gid)
132
130
  gid.model_class.find gid.model_id
133
131
  end
@@ -151,19 +149,26 @@ class GlobalID
151
149
  end
152
150
  end
153
151
  end
154
- DEFAULT_LOCATOR = DefaultLocator.new
155
152
 
156
- class UnscopedLocator < DefaultLocator
153
+ class UnscopedLocator < BaseLocator
157
154
  def locate(gid)
158
- gid.model_class.unscoped { super }
155
+ unscoped(gid.model_class) { super }
159
156
  end
160
157
 
161
158
  private
162
159
  def find_records(model_class, ids, options)
163
- model_class.unscoped { super }
160
+ unscoped(model_class) { super }
161
+ end
162
+
163
+ def unscoped(model_class)
164
+ if model_class.respond_to?(:unscoped)
165
+ model_class.unscoped { yield }
166
+ else
167
+ yield
168
+ end
164
169
  end
165
170
  end
166
- UNSCOPED_LOCATOR = UnscopedLocator.new
171
+ DEFAULT_LOCATOR = UnscopedLocator.new
167
172
 
168
173
  class BlockLocator
169
174
  def initialize(block)
@@ -11,18 +11,21 @@ class GlobalID
11
11
  # Set up the signed GlobalID verifier and include Active Record support.
12
12
  class Railtie < Rails::Railtie # :nodoc:
13
13
  config.global_id = ActiveSupport::OrderedOptions.new
14
+ config.eager_load_namespaces << GlobalID
14
15
 
15
16
  initializer 'global_id' do |app|
17
+ default_expires_in = 1.month
18
+ default_app_name = app.railtie_name.remove('_application').dasherize
16
19
 
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
20
+ GlobalID.app = app.config.global_id.app ||= default_app_name
21
+ SignedGlobalID.expires_in = app.config.global_id.expires_in ||= default_expires_in
22
22
 
23
23
  config.after_initialize do
24
+ GlobalID.app = app.config.global_id.app ||= default_app_name
25
+ SignedGlobalID.expires_in = app.config.global_id.expires_in ||= default_expires_in
26
+
24
27
  app.config.global_id.verifier ||= begin
25
- app.message_verifier(:signed_global_ids)
28
+ GlobalID::Verifier.new(app.key_generator.generate_key('signed_global_ids'))
26
29
  rescue ArgumentError
27
30
  nil
28
31
  end
@@ -9,11 +9,7 @@ class SignedGlobalID < GlobalID
9
9
  attr_accessor :verifier
10
10
 
11
11
  def parse(sgid, options = {})
12
- if sgid.is_a? self
13
- sgid
14
- else
15
- super verify(sgid, options), options
16
- end
12
+ super verify(sgid.to_s, options), options
17
13
  end
18
14
 
19
15
  # Grab the verifier from options and fall back to SignedGlobalID.verifier.
@@ -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.6
4
+ version: 0.4.2
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: 2015-08-04 00:00:00.000000000 Z
11
+ date: 2019-01-11 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: 4.2.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: 4.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -45,6 +45,7 @@ extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
47
  - MIT-LICENSE
48
+ - README.md
48
49
  - lib/global_id.rb
49
50
  - lib/global_id/global_id.rb
50
51
  - lib/global_id/identification.rb
@@ -52,6 +53,7 @@ files:
52
53
  - lib/global_id/railtie.rb
53
54
  - lib/global_id/signed_global_id.rb
54
55
  - lib/global_id/uri/gid.rb
56
+ - lib/global_id/verifier.rb
55
57
  - lib/globalid.rb
56
58
  homepage: http://www.rubyonrails.org
57
59
  licenses:
@@ -72,10 +74,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
74
  - !ruby/object:Gem::Version
73
75
  version: '0'
74
76
  requirements: []
75
- rubyforge_project:
76
- rubygems_version: 2.4.7
77
+ rubygems_version: 3.0.2
77
78
  signing_key:
78
79
  specification_version: 4
79
80
  summary: 'Refer to any model with a URI: gid://app/class/id'
80
81
  test_files: []
81
- has_rdoc: