checkpoint 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +15 -0
- data/.travis.yml +8 -0
- data/README.md +6 -0
- data/db/migrations/{1_create_permits.rb → 1_create_grants.rb} +1 -1
- data/docs/index.rst +6 -1
- data/lib/checkpoint/agent.rb +32 -31
- data/lib/checkpoint/agent/resolver.rb +45 -15
- data/lib/checkpoint/agent/token.rb +8 -3
- data/lib/checkpoint/authority.rb +126 -14
- data/lib/checkpoint/credential.rb +21 -8
- data/lib/checkpoint/credential/permission.rb +6 -5
- data/lib/checkpoint/credential/resolver.rb +63 -62
- data/lib/checkpoint/credential/role.rb +2 -3
- data/lib/checkpoint/credential/role_map_resolver.rb +65 -0
- data/lib/checkpoint/credential/token.rb +7 -2
- data/lib/checkpoint/db.rb +8 -1
- data/lib/checkpoint/db/cartesian_select.rb +71 -0
- data/lib/checkpoint/db/{permit.rb → grant.rb} +4 -4
- data/lib/checkpoint/db/params.rb +36 -0
- data/lib/checkpoint/db/query/ac.rb +47 -0
- data/lib/checkpoint/db/query/acr.rb +54 -0
- data/lib/checkpoint/db/query/ar.rb +47 -0
- data/lib/checkpoint/db/query/cr.rb +47 -0
- data/lib/checkpoint/grants.rb +116 -0
- data/lib/checkpoint/query/role_granted.rb +1 -1
- data/lib/checkpoint/resource.rb +16 -19
- data/lib/checkpoint/resource/all_of_any_type.rb +0 -8
- data/lib/checkpoint/resource/all_of_type.rb +0 -22
- data/lib/checkpoint/resource/resolver.rb +34 -11
- data/lib/checkpoint/resource/token.rb +7 -2
- data/lib/checkpoint/version.rb +1 -1
- metadata +12 -7
- data/docs/authentication.rst +0 -18
- data/lib/checkpoint/permission_mapper.rb +0 -29
- data/lib/checkpoint/permits.rb +0 -133
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Checkpoint
|
4
|
+
module DB
|
5
|
+
# A helper for building placeholder variable names from items in a list and
|
6
|
+
# providing a corresponding hash of values. A prefix with some mnemonic
|
7
|
+
# corresponding to the column is recommended. For example, if the column is
|
8
|
+
# `agent_token`, using the prefix `at` will yield `$at_0`, `$at_1`, etc. for
|
9
|
+
# an IN clause.
|
10
|
+
class Params
|
11
|
+
attr_reader :items, :prefix
|
12
|
+
|
13
|
+
def initialize(items, prefix)
|
14
|
+
@items = [items].flatten
|
15
|
+
@prefix = prefix
|
16
|
+
end
|
17
|
+
|
18
|
+
def placeholders
|
19
|
+
0.upto(items.size - 1).map do |i|
|
20
|
+
:"$#{prefix}_#{i}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def values
|
25
|
+
items.map.with_index do |item, i|
|
26
|
+
value = if item.respond_to?(:sql_value)
|
27
|
+
item.sql_value
|
28
|
+
else
|
29
|
+
item.to_s
|
30
|
+
end
|
31
|
+
[:"#{prefix}_#{i}", value]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Checkpoint::DB
|
4
|
+
module Query
|
5
|
+
# A query object based on agents and credentials.
|
6
|
+
#
|
7
|
+
# This query finds grants for any supplied agents, for any supplied
|
8
|
+
# credentials. Its primary purpose is to find which resources for which an
|
9
|
+
# agent has been granted a given credential.
|
10
|
+
#
|
11
|
+
# It can take single items or arrays and converts them all to their tokens
|
12
|
+
# for query purposes.
|
13
|
+
class AC < CartesianSelect
|
14
|
+
attr_reader :agents, :credentials
|
15
|
+
|
16
|
+
def initialize(agents, credentials, scope: Grant)
|
17
|
+
super(scope: scope)
|
18
|
+
@agents = tokenize(agents)
|
19
|
+
@credentials = tokenize(credentials)
|
20
|
+
end
|
21
|
+
|
22
|
+
def conditions
|
23
|
+
super.merge(
|
24
|
+
agent_token: agent_params.placeholders,
|
25
|
+
credential_token: credential_params.placeholders
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parameters
|
30
|
+
super.merge(Hash[
|
31
|
+
agent_params.values +
|
32
|
+
credential_params.values
|
33
|
+
])
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def agent_params
|
39
|
+
Params.new(agents, 'at')
|
40
|
+
end
|
41
|
+
|
42
|
+
def credential_params
|
43
|
+
Params.new(credentials, 'ct')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Checkpoint::DB
|
4
|
+
module Query
|
5
|
+
# A query object based on agents, credentials, and resources.
|
6
|
+
#
|
7
|
+
# This query mirrors the essence of the Checkpoint semantics; that is, it
|
8
|
+
# finds grants for any supplied agents, for any supplied credentials, for
|
9
|
+
# any supplied resources.
|
10
|
+
#
|
11
|
+
# It can take single items or arrays and converts them all to their tokens
|
12
|
+
# for query purposes.
|
13
|
+
class ACR < CartesianSelect
|
14
|
+
attr_reader :agents, :credentials, :resources
|
15
|
+
|
16
|
+
def initialize(agents, credentials, resources, scope: Grant)
|
17
|
+
super(scope: scope)
|
18
|
+
@agents = tokenize(agents)
|
19
|
+
@credentials = tokenize(credentials)
|
20
|
+
@resources = tokenize(resources)
|
21
|
+
end
|
22
|
+
|
23
|
+
def conditions
|
24
|
+
super.merge(
|
25
|
+
agent_token: agent_params.placeholders,
|
26
|
+
credential_token: credential_params.placeholders,
|
27
|
+
resource_token: resource_params.placeholders
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def parameters
|
32
|
+
super.merge(Hash[
|
33
|
+
agent_params.values +
|
34
|
+
credential_params.values +
|
35
|
+
resource_params.values
|
36
|
+
])
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def agent_params
|
42
|
+
Params.new(agents, 'at')
|
43
|
+
end
|
44
|
+
|
45
|
+
def credential_params
|
46
|
+
Params.new(credentials, 'ct')
|
47
|
+
end
|
48
|
+
|
49
|
+
def resource_params
|
50
|
+
Params.new(resources, 'rt')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Checkpoint::DB
|
4
|
+
module Query
|
5
|
+
# A query object based on agents and resources.
|
6
|
+
#
|
7
|
+
# This query finds grants for any supplied agents, for any supplied
|
8
|
+
# resources. Its primary purpose is to find which credentials have been
|
9
|
+
# granted to an agent on a given resource.
|
10
|
+
#
|
11
|
+
# It can take single items or arrays and converts them all to their tokens
|
12
|
+
# for query purposes.
|
13
|
+
class AR < CartesianSelect
|
14
|
+
attr_reader :agents, :resources
|
15
|
+
|
16
|
+
def initialize(agents, resources, scope: Grant)
|
17
|
+
super(scope: scope)
|
18
|
+
@agents = tokenize(agents)
|
19
|
+
@resources = tokenize(resources)
|
20
|
+
end
|
21
|
+
|
22
|
+
def conditions
|
23
|
+
super.merge(
|
24
|
+
agent_token: agent_params.placeholders,
|
25
|
+
resource_token: resource_params.placeholders
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parameters
|
30
|
+
super.merge(Hash[
|
31
|
+
agent_params.values +
|
32
|
+
resource_params.values
|
33
|
+
])
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def agent_params
|
39
|
+
Params.new(agents, 'at')
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource_params
|
43
|
+
Params.new(resources, 'rt')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Checkpoint::DB
|
4
|
+
module Query
|
5
|
+
# A query object based on credentials and resources.
|
6
|
+
#
|
7
|
+
# This query finds grants for any supplied credentials, for any supplied
|
8
|
+
# resources. Its primary purpose is to find which agents have been granted
|
9
|
+
# a given credential on a resource.
|
10
|
+
#
|
11
|
+
# It can take single items or arrays and converts them all to their tokens
|
12
|
+
# for query purposes.
|
13
|
+
class CR < CartesianSelect
|
14
|
+
attr_reader :credentials, :resources
|
15
|
+
|
16
|
+
def initialize(credentials, resources, scope: Grant)
|
17
|
+
super(scope: scope)
|
18
|
+
@credentials = tokenize(credentials)
|
19
|
+
@resources = tokenize(resources)
|
20
|
+
end
|
21
|
+
|
22
|
+
def conditions
|
23
|
+
super.merge(
|
24
|
+
credential_token: credential_params.placeholders,
|
25
|
+
resource_token: resource_params.placeholders
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parameters
|
30
|
+
super.merge(Hash[
|
31
|
+
credential_params.values +
|
32
|
+
resource_params.values
|
33
|
+
])
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def credential_params
|
39
|
+
Params.new(credentials, 'ct')
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource_params
|
43
|
+
Params.new(resources, 'rt')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Note: we do not require db/grant 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 grants -- a simple wrapper for the Sequel Datastore / grants table.
|
14
|
+
class Grants
|
15
|
+
def initialize(grants: Checkpoint::DB::Grant)
|
16
|
+
@grants = grants
|
17
|
+
end
|
18
|
+
|
19
|
+
def for(agents, credentials, resources)
|
20
|
+
where(agents, credentials, resources).all
|
21
|
+
end
|
22
|
+
|
23
|
+
def any?(agents, credentials, resources)
|
24
|
+
where(agents, credentials, resources).first != nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Find grants of the given credentials on the given resources.
|
28
|
+
#
|
29
|
+
# This is useful for finding who should have particular access. Note that
|
30
|
+
# this low-level interface returns the full grants, rather than a unique
|
31
|
+
# set of agents.
|
32
|
+
#
|
33
|
+
# @return [Array<Grant>] the set of grants of any of the credentials on
|
34
|
+
# any of the resources
|
35
|
+
def who(credentials, resources)
|
36
|
+
DB::Query::CR.new(credentials, resources, **scope).all
|
37
|
+
end
|
38
|
+
|
39
|
+
# Find grants to the given agents on the given resources.
|
40
|
+
#
|
41
|
+
# This is useful for finding what actions may be taken on particular items.
|
42
|
+
# Note that this low-level interface returns the full grants, rather than a
|
43
|
+
# unique set of credentials.
|
44
|
+
#
|
45
|
+
# @return [Array<Grant>] the set of grants to any of the agents on any of
|
46
|
+
# the resources
|
47
|
+
def what(agents, resources)
|
48
|
+
DB::Query::AR.new(agents, resources, **scope).all
|
49
|
+
end
|
50
|
+
|
51
|
+
# Find grants to the given agents of the given credentials.
|
52
|
+
#
|
53
|
+
# This is useful for finding which resources may acted upon. Note that this
|
54
|
+
# low-level interface returns the full grants, rather than a unique set of
|
55
|
+
# resources.
|
56
|
+
#
|
57
|
+
# @return [Array<Grant>] the set of grants of any of the credentials to
|
58
|
+
# any of the agents
|
59
|
+
def which(agents, credentials)
|
60
|
+
DB::Query::AC.new(agents, credentials, **scope).all
|
61
|
+
end
|
62
|
+
|
63
|
+
# Grant a credential.
|
64
|
+
#
|
65
|
+
# This method takes a single agent, credential, and resource to create a
|
66
|
+
# grant. They are not expanded, though they may be general (e.g., an
|
67
|
+
# agent for users of an instituion or a wildcard for resources of some type).
|
68
|
+
#
|
69
|
+
# @param agent [Agent] the agent to whom the credential should be granted
|
70
|
+
# @param credential [Credential] the credential to grant
|
71
|
+
# @param resource [Resource] the resource to which the credential should apply
|
72
|
+
# @return [Grant] the saved Grant; nil if the save fails
|
73
|
+
def grant!(agent, credential, resource)
|
74
|
+
grants.from(agent, credential, resource).save
|
75
|
+
end
|
76
|
+
|
77
|
+
# Revoke a credential.
|
78
|
+
#
|
79
|
+
# Take care to note that this follows the same matching semantics as
|
80
|
+
# {.for}. There is no expansion done here, but anything that matches what
|
81
|
+
# is supplied will be deleted. Of particular note is the default wildcard
|
82
|
+
# behavior of {Checkpoint::Resource::Resolver}: if a specific resource has
|
83
|
+
# been expanded by the resolver, and the array of the resource, a type
|
84
|
+
# wildcard, and the any-resource wildcard (as used for inherited matching)
|
85
|
+
# is supplied, the results may be surprising where there are grants at
|
86
|
+
# specific and general levels.
|
87
|
+
#
|
88
|
+
# In general, the parameters should not have been expanded. If the intent
|
89
|
+
# is to revoke a general grant, the general details should be supplied,
|
90
|
+
# and likewise for the specific case.
|
91
|
+
#
|
92
|
+
# Applications should interact with the {Checkpoint::Authority}, which
|
93
|
+
# exposes a more application-oriented interface. This repository should be
|
94
|
+
# considered internal to Checkpoint.
|
95
|
+
#
|
96
|
+
# @param agents [Agent|Array] the agent or agents to match for deletion
|
97
|
+
# @param credentials [Credential|Array] the credential or credentials to match for deletion
|
98
|
+
# @param resources [Resource|Array] the resource or resources to match for deletion
|
99
|
+
# @return [Integer] the number of Grants deleted
|
100
|
+
def revoke!(agents, credentials, resources)
|
101
|
+
where(agents, credentials, resources).delete
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def scope
|
107
|
+
{ scope: grants }
|
108
|
+
end
|
109
|
+
|
110
|
+
def where(agents, credentials, resources)
|
111
|
+
DB::Query::ACR.new(agents, credentials, resources, **scope)
|
112
|
+
end
|
113
|
+
|
114
|
+
attr_reader :grants
|
115
|
+
end
|
116
|
+
end
|
@@ -16,7 +16,7 @@ module Checkpoint
|
|
16
16
|
# to examine. Tests can validate system behavior at development time
|
17
17
|
# because it is static.
|
18
18
|
#
|
19
|
-
# 2. Implementing a {Checkpoint::Credential::
|
19
|
+
# 2. Implementing a {Checkpoint::Credential::Resolver} that maps backward
|
20
20
|
# from actions to named permissions and roles that would allow them.
|
21
21
|
# The policy rules would only authorize actions, leaving the mapping
|
22
22
|
# outside to accommodate configuration or runtime modification. This has
|
data/lib/checkpoint/resource.rb
CHANGED
@@ -35,7 +35,7 @@ module Checkpoint
|
|
35
35
|
class Resource
|
36
36
|
attr_reader :entity
|
37
37
|
|
38
|
-
# Special string to be used when
|
38
|
+
# Special string to be used when granting or searching for grants on all
|
39
39
|
# types or all resources
|
40
40
|
ALL = '(all)'
|
41
41
|
|
@@ -46,19 +46,6 @@ module Checkpoint
|
|
46
46
|
@entity = entity
|
47
47
|
end
|
48
48
|
|
49
|
-
# Default conversion from an entity to a Resource. Prefer this to creating
|
50
|
-
# new instances by hand.
|
51
|
-
#
|
52
|
-
# If the entity implements #to_resource, we will delegate to it. Otherwise,
|
53
|
-
# we will return a Resource for this entity.
|
54
|
-
def self.from(entity)
|
55
|
-
if entity.respond_to?(:to_resource)
|
56
|
-
entity.to_resource
|
57
|
-
else
|
58
|
-
new(entity)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
49
|
# Covenience factory method to get a Resource that will match all entities
|
63
50
|
# of any type.
|
64
51
|
#
|
@@ -67,11 +54,22 @@ module Checkpoint
|
|
67
54
|
AllOfAnyType.new
|
68
55
|
end
|
69
56
|
|
57
|
+
# Convert this object to a Resource.
|
58
|
+
#
|
59
|
+
# For Checkpoint-supplied Resources, this is an identity operation,
|
60
|
+
# but it allows consistent handling of the built-in types and
|
61
|
+
# application-supplied types that will either implement this interface or
|
62
|
+
# convert themselves to a built-in type. This removes the requirement to
|
63
|
+
# extend Checkpoint types or bind to a specific conversion method.
|
64
|
+
def to_resource
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
70
68
|
# Get the resource type.
|
71
69
|
#
|
72
70
|
# Note that this is not necessarily a class/model type name. It can be
|
73
71
|
# whatever type name is most useful for building tokens and inspecting
|
74
|
-
#
|
72
|
+
# grants for this types. For example, there may be objects that have
|
75
73
|
# subtypes that are not modeled as objects, decorators, or collection
|
76
74
|
# objects (like a specialized type for the root of a tree) that should
|
77
75
|
# be treated as the element type.
|
@@ -127,12 +125,11 @@ module Checkpoint
|
|
127
125
|
other.is_a?(Resource) && entity.eql?(other.entity)
|
128
126
|
end
|
129
127
|
|
130
|
-
# Check whether two Resources refer to the same entity.
|
128
|
+
# Check whether two Resources refer to the same entity by type and id.
|
131
129
|
# @param other [Resource] Another Resource to compare with
|
132
|
-
# @return [Boolean] true when the other Resource's
|
133
|
-
# determined by comparing them with `#==`.
|
130
|
+
# @return [Boolean] true when the other Resource's type and id are equal.
|
134
131
|
def ==(other)
|
135
|
-
other.is_a?(Resource) &&
|
132
|
+
other.is_a?(Resource) && type == other.type && id == other.id
|
136
133
|
end
|
137
134
|
end
|
138
135
|
end
|
@@ -12,14 +12,6 @@ module Checkpoint
|
|
12
12
|
@entity = AnyEntity.new
|
13
13
|
end
|
14
14
|
|
15
|
-
# Create a wildcard Resource "from" an entity. The entity disregarded and
|
16
|
-
# {AnyEntity} is substituted.
|
17
|
-
#
|
18
|
-
# @return [AllOfAnyType] a wildcard Resource instance
|
19
|
-
def self.from(_entity)
|
20
|
-
new
|
21
|
-
end
|
22
|
-
|
23
15
|
# The special ALL type
|
24
16
|
def type
|
25
17
|
Resource::ALL
|
@@ -10,16 +10,6 @@ module Checkpoint
|
|
10
10
|
@type = type
|
11
11
|
end
|
12
12
|
|
13
|
-
# Create a type-specific wildcard Resource from a given entity
|
14
|
-
#
|
15
|
-
# When the entity implements to #to_resource, we convert it first and take
|
16
|
-
# the type from the result. Otherwise, when it implements #resource_type,
|
17
|
-
# we use that result. Otherwise, we take the class name of the entity.
|
18
|
-
# Regardless of the source, the type is forced to a string.
|
19
|
-
def self.from(entity)
|
20
|
-
new(type_of(entity))
|
21
|
-
end
|
22
|
-
|
23
13
|
# This is always the special ALL resource ID
|
24
14
|
def id
|
25
15
|
Resource::ALL
|
@@ -32,18 +22,6 @@ module Checkpoint
|
|
32
22
|
other.is_a?(Resource) && type == other.type
|
33
23
|
end
|
34
24
|
|
35
|
-
# Private type name extraction
|
36
|
-
def self.type_of(entity)
|
37
|
-
if entity.respond_to?(:to_resource)
|
38
|
-
entity.to_resource.type
|
39
|
-
elsif entity.respond_to?(:resource_type)
|
40
|
-
entity.resource_type
|
41
|
-
else
|
42
|
-
entity.class
|
43
|
-
end.to_s
|
44
|
-
end
|
45
|
-
|
46
|
-
private_class_method :type_of
|
47
25
|
alias == eql?
|
48
26
|
end
|
49
27
|
end
|