checkpoint 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +15 -0
  3. data/.travis.yml +8 -0
  4. data/README.md +6 -0
  5. data/db/migrations/{1_create_permits.rb → 1_create_grants.rb} +1 -1
  6. data/docs/index.rst +6 -1
  7. data/lib/checkpoint/agent.rb +32 -31
  8. data/lib/checkpoint/agent/resolver.rb +45 -15
  9. data/lib/checkpoint/agent/token.rb +8 -3
  10. data/lib/checkpoint/authority.rb +126 -14
  11. data/lib/checkpoint/credential.rb +21 -8
  12. data/lib/checkpoint/credential/permission.rb +6 -5
  13. data/lib/checkpoint/credential/resolver.rb +63 -62
  14. data/lib/checkpoint/credential/role.rb +2 -3
  15. data/lib/checkpoint/credential/role_map_resolver.rb +65 -0
  16. data/lib/checkpoint/credential/token.rb +7 -2
  17. data/lib/checkpoint/db.rb +8 -1
  18. data/lib/checkpoint/db/cartesian_select.rb +71 -0
  19. data/lib/checkpoint/db/{permit.rb → grant.rb} +4 -4
  20. data/lib/checkpoint/db/params.rb +36 -0
  21. data/lib/checkpoint/db/query/ac.rb +47 -0
  22. data/lib/checkpoint/db/query/acr.rb +54 -0
  23. data/lib/checkpoint/db/query/ar.rb +47 -0
  24. data/lib/checkpoint/db/query/cr.rb +47 -0
  25. data/lib/checkpoint/grants.rb +116 -0
  26. data/lib/checkpoint/query/role_granted.rb +1 -1
  27. data/lib/checkpoint/resource.rb +16 -19
  28. data/lib/checkpoint/resource/all_of_any_type.rb +0 -8
  29. data/lib/checkpoint/resource/all_of_type.rb +0 -22
  30. data/lib/checkpoint/resource/resolver.rb +34 -11
  31. data/lib/checkpoint/resource/token.rb +7 -2
  32. data/lib/checkpoint/version.rb +1 -1
  33. metadata +12 -7
  34. data/docs/authentication.rst +0 -18
  35. data/lib/checkpoint/permission_mapper.rb +0 -29
  36. data/lib/checkpoint/permits.rb +0 -133
@@ -2,13 +2,20 @@
2
2
 
3
3
  module Checkpoint
4
4
  class Resource
5
- # A Resource Resolver takes a concrete object (like a model instance) and
6
- # resolves it into all {Resource}s for which a grant would allow an action.
5
+ # A Resource Resolver is the bridge between concrete entity objects (like
6
+ # model instances) and {Resource}s that the represent them.
7
+ #
8
+ # There are two basic operations:
9
+ #
10
+ # - Conversion maps an entity to a single Resource.
11
+ # - Expansion maps an entity to all Resources for which a grant would allow
12
+ # an action.
13
+ #
7
14
  # For example, this can be used to grant a credential on all items of a given
8
15
  # model class or to implement cascading permissions when all credentials for
9
16
  # a container should apply to the contained objects.
10
17
  #
11
- # This base implementation resolves to three agents: one for the entity
18
+ # This base implementation expands to three resources: one for the entity
12
19
  # itself, one for all entities of its type, and one for all entities of any
13
20
  # type. This provides a convenient and familiar construct, where a broader
14
21
  # grant (say, at the type level, or for "everything") implies a grant at
@@ -32,14 +39,14 @@ module Checkpoint
32
39
  # there is even more complexity, Checkpoint allows its fundamental
33
40
  # semantics to be extended by implementing a custom resolver.
34
41
  class Resolver
35
- # Resolve an application entity into a set of Resources for which a grant
42
+ # Expand an application entity into a set of Resources for which a grant
36
43
  # would allow access.
37
44
  #
38
- # The entity will be converted to a Resource with {Resource::from} and
39
- # {Resource::AllOfType::from}. That is, the Resolver does a
40
- # straightforward expansion, not applying any of its own conversion
41
- # semantics. The special {Resource::all} resource is also included to
42
- # support zone-wide grants.
45
+ # The entity will be expanded to three Resources:
46
+ #
47
+ # 1. A Resource for the specific entity, by conversion
48
+ # 2. A {Resource::AllOfType} for the type of the entity, as by conversion
49
+ # 3. A special {Resource::AllOfAnyType} to support zone-wide grants
43
50
  #
44
51
  # As an example, permission to download high quality versions of media
45
52
  # assets might be granted to a given user system wide (that is, for the
@@ -61,8 +68,24 @@ module Checkpoint
61
68
  # this approach may be more convenient than, for example, delegating to a
62
69
  # specific policy in the same way from multiple sections of the
63
70
  # application.
64
- def resolve(target)
65
- [Resource.from(target), Resource::AllOfType.from(target), Resource.all]
71
+ def expand(entity)
72
+ [convert(entity), convert(entity).all_of_type, Resource.all]
73
+ end
74
+
75
+ # Default conversion from an entity to a {Resource}.
76
+ #
77
+ # If the entity implements #to_resource, we will delegate to it. Otherwise,
78
+ # we will instantiate a {Resource} with the supplied entity.
79
+ #
80
+ # Override this method to use a different or conditional Resource type.
81
+ #
82
+ # @return [Resource] the entity converted to a resource
83
+ def convert(entity)
84
+ if entity.respond_to?(:to_resource)
85
+ entity.to_resource
86
+ else
87
+ Resource.new(entity)
88
+ end
66
89
  end
67
90
  end
68
91
  end
@@ -5,9 +5,9 @@ require 'checkpoint/resource/resolver'
5
5
  module Checkpoint
6
6
  class Resource
7
7
  # A Resource::Token is an identifier object for a Resource. It includes a
8
- # type and an identifier. A {Permit} can be granted for a Token. Concrete
8
+ # type and an identifier. A {Grant} can be created for a Token. Concrete
9
9
  # entities are resolved into a number of resources, and those resources'
10
- # tokens will be checked for matching permits.
10
+ # tokens will be checked for matching grants.
11
11
  class Token
12
12
  attr_reader :type, :id
13
13
 
@@ -58,6 +58,11 @@ module Checkpoint
58
58
  other.is_a?(Resource::Token) && type == other.type && id == other.id
59
59
  end
60
60
 
61
+ # @return [Integer] hash code based on to_s
62
+ def hash
63
+ to_s.hash
64
+ end
65
+
61
66
  alias == eql?
62
67
  alias inspect uri
63
68
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Checkpoint
4
- VERSION = "1.0.3"
4
+ VERSION = "1.1.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: checkpoint
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Botimer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-19 00:00:00.000000000 Z
11
+ date: 2018-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ettin
@@ -217,11 +217,10 @@ files:
217
217
  - bin/yard
218
218
  - bin/yardoc
219
219
  - checkpoint.gemspec
220
- - db/migrations/1_create_permits.rb
220
+ - db/migrations/1_create_grants.rb
221
221
  - docs/Makefile
222
222
  - docs/_static/.gitkeep
223
223
  - docs/_templates/.gitkeep
224
- - docs/authentication.rst
225
224
  - docs/conf.py
226
225
  - docs/index.rst
227
226
  - docs/policies.rst
@@ -235,11 +234,17 @@ files:
235
234
  - lib/checkpoint/credential/permission.rb
236
235
  - lib/checkpoint/credential/resolver.rb
237
236
  - lib/checkpoint/credential/role.rb
237
+ - lib/checkpoint/credential/role_map_resolver.rb
238
238
  - lib/checkpoint/credential/token.rb
239
239
  - lib/checkpoint/db.rb
240
- - lib/checkpoint/db/permit.rb
241
- - lib/checkpoint/permission_mapper.rb
242
- - lib/checkpoint/permits.rb
240
+ - lib/checkpoint/db/cartesian_select.rb
241
+ - lib/checkpoint/db/grant.rb
242
+ - lib/checkpoint/db/params.rb
243
+ - lib/checkpoint/db/query/ac.rb
244
+ - lib/checkpoint/db/query/acr.rb
245
+ - lib/checkpoint/db/query/ar.rb
246
+ - lib/checkpoint/db/query/cr.rb
247
+ - lib/checkpoint/grants.rb
243
248
  - lib/checkpoint/query.rb
244
249
  - lib/checkpoint/query/action_permitted.rb
245
250
  - lib/checkpoint/query/role_granted.rb
@@ -1,18 +0,0 @@
1
- Identity and Authentication
2
- ===========================
3
-
4
- Users can be identified in any number of ways and carry with them various
5
- attributes that determine the entirety of "who they are". Our typical needs
6
- include identifying a person by username or email address, and building a
7
- profile of attributes such as geographical region (as determined by IP address),
8
- or University status (student, staff, etc.). The identifiers and attributes are
9
- intrinsic to the user and do not, by themselves, grant any permissions within
10
- an application. Likewise, these attributes cannot be granted within an
11
- application, only inspected.
12
-
13
- A&E will continue to provide the identity and attributes of users. The
14
- specifics of whether this will be implemented with environment variables,
15
- HTTP headers, SAML, or other means is to be determined. An application is
16
- not expected to implement its own login process except to the degree that
17
- it can recognize the required authentication information provided to it.
18
-
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Checkpoint
4
- # A PermissionMapper translates an action into a set of permissions and roles
5
- # that would allow it. Commonly, the actions and permissions will share names
6
- # for convenience and consistency, but this is not a requirement.
7
- #
8
- # For example, it may make sense in an application that one permission
9
- # implies another, so an action may have multiple permissions that would
10
- # allow it. In another application, it may be more convenient and
11
- # understandable for users to have separate roles encapsulate that concept
12
- # (such as an editor role having all of the permissions of a reader role and
13
- # more).
14
- #
15
- # As a separate example, it may be more appropriate to implement permission
16
- # inheritance directly in policy code (as by delegating to another check or
17
- # policy), relying on the matching action and permission names with no roles
18
- # resolved, as given by the default PermissionMapper. Checkpoint does not
19
- # take an absolute position on the best pattern for a given application.
20
- class PermissionMapper
21
- def permissions_for(action)
22
- [action.to_sym]
23
- end
24
-
25
- def roles_granting(_action)
26
- []
27
- end
28
- end
29
- end
@@ -1,133 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Note: we do not require db/permit because Sequel requires the connection
4
- # to be set up before defining the model classes. The arrangment here
5
- # assumes that DB.initialize! will have been called if the default model
6
- # is to be used. In tests, that is done by spec/sequel_helper.rb. In an
7
- # application, there should be an initializer that reads whatever appropriate
8
- # configuration and does the initialization.
9
-
10
- require 'checkpoint/db'
11
-
12
- module Checkpoint
13
- # The repository of permits -- a simple wrapper for the Sequel Datastore / permits table.
14
- class Permits
15
- def initialize(permits: Checkpoint::DB::Permit)
16
- @permits = permits
17
- end
18
-
19
- def for(agents, credentials, resources)
20
- where(agents, credentials, resources).select
21
- end
22
-
23
- def any?(agents, credentials, resources)
24
- where(agents, credentials, resources).first != nil
25
- end
26
-
27
- private
28
-
29
- def where(agents, credentials, resources)
30
- Query.new(agents, credentials, resources, scope: permits)
31
- end
32
-
33
- attr_reader :permits
34
-
35
- # A query object based on agents, credentials, and resources.
36
- #
37
- # This is a helper to capture a set of agents, credentials, and resources,
38
- # and manage assembly of placeholder variables and binding expressions in
39
- # the way Sequel expects them. It can take single items or arrays and
40
- # converts them all to their tokens for query purposes.
41
- class Query
42
- attr_reader :agents, :credentials, :resources, :scope
43
-
44
- def initialize(agents, credentials, resources, scope: Checkpoint::DB::Permit)
45
- @agents = tokenize(agents)
46
- @credentials = tokenize(credentials)
47
- @resources = tokenize(resources)
48
- @scope = scope
49
- end
50
-
51
- def query
52
- scope.where(conditions)
53
- end
54
-
55
- def select
56
- exec(:select)
57
- end
58
-
59
- def first
60
- exec(:first)
61
- end
62
-
63
- def conditions
64
- {
65
- agent_token: agent_params.placeholders,
66
- credential_token: credential_params.placeholders,
67
- resource_token: resource_params.placeholders,
68
- zone_id: :$zone_id
69
- }
70
- end
71
-
72
- def parameters
73
- (agent_params.values +
74
- credential_params.values +
75
- resource_params.values +
76
- [[:zone_id, DB::Permit.default_zone]]).to_h
77
- end
78
-
79
- def agent_params
80
- Params.new(agents, 'at')
81
- end
82
-
83
- def credential_params
84
- Params.new(credentials, 'ct')
85
- end
86
-
87
- def resource_params
88
- Params.new(resources, 'rt')
89
- end
90
-
91
- private
92
-
93
- def exec(mode)
94
- query.call(mode, parameters)
95
- end
96
-
97
- def tokenize(collection)
98
- [collection].flatten.map(&:token)
99
- end
100
- end
101
-
102
- # A helper for building placeholder variable names from items in a list and
103
- # providing a corresponding hash of values. A prefix with some mnemonic
104
- # corresponding to the column is recommended. For example, if the column is
105
- # `agent_token`, using the prefix `at` will yield `$at_0`, `$at_1`, etc. for
106
- # an IN clause.
107
- class Params
108
- attr_reader :items, :prefix
109
-
110
- def initialize(items, prefix)
111
- @items = [items].flatten
112
- @prefix = prefix
113
- end
114
-
115
- def placeholders
116
- 0.upto(items.size - 1).map do |i|
117
- :"$#{prefix}_#{i}"
118
- end
119
- end
120
-
121
- def values
122
- items.map.with_index do |item, i|
123
- value = if item.respond_to?(:sql_value)
124
- item.sql_value
125
- else
126
- item.to_s
127
- end
128
- [:"#{prefix}_#{i}", value]
129
- end
130
- end
131
- end
132
- end
133
- end