cogitate 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +69 -15
- data/lib/cogitate.rb +18 -2
- data/lib/cogitate/README.md +8 -0
- data/lib/cogitate/client.rb +97 -0
- data/lib/cogitate/client/agent_builder.rb +86 -0
- data/lib/cogitate/client/data_to_object_coercer.rb +38 -0
- data/lib/cogitate/client/exceptions.rb +10 -0
- data/lib/cogitate/client/identifier_builder.rb +26 -0
- data/lib/cogitate/client/request.rb +56 -0
- data/lib/cogitate/client/response_parsers.rb +19 -0
- data/lib/cogitate/client/response_parsers/agents_with_detailed_identifiers_extractor.rb +15 -0
- data/lib/cogitate/client/response_parsers/agents_without_group_membership_extractor.rb +23 -0
- data/lib/cogitate/client/response_parsers/basic_extractor.rb +14 -0
- data/lib/cogitate/client/response_parsers/email_extractor.rb +17 -0
- data/lib/cogitate/client/retrieve_agent_from_ticket.rb +43 -0
- data/lib/cogitate/client/ticket_to_token_coercer.rb +35 -0
- data/lib/cogitate/client/token_to_object_coercer.rb +38 -0
- data/lib/cogitate/configuration.rb +99 -0
- data/lib/cogitate/exceptions.rb +25 -0
- data/lib/cogitate/generators/install_generator.rb +10 -0
- data/lib/cogitate/generators/templates/cogitate_initializer.rb.erb +17 -0
- data/lib/cogitate/interfaces.rb +40 -0
- data/lib/cogitate/models.rb +6 -0
- data/lib/cogitate/models/agent.rb +143 -0
- data/lib/cogitate/models/agent/serializer.rb +75 -0
- data/lib/cogitate/models/agent/with_token.rb +29 -0
- data/lib/cogitate/models/identifier.rb +114 -0
- data/lib/cogitate/models/identifiers.rb +7 -0
- data/lib/cogitate/models/identifiers/with_attribute_hash.rb +44 -0
- data/lib/cogitate/railtie.rb +8 -0
- data/lib/cogitate/services/identifiers_decoder.rb +61 -0
- data/lib/cogitate/services/tokenizer.rb +80 -0
- metadata +105 -5
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
2
|
+
|
3
|
+
module Cogitate
|
4
|
+
module Client
|
5
|
+
# Request from Cogitate the given identifiers and parse leveraging the custom parser.
|
6
|
+
class Request
|
7
|
+
# @api public
|
8
|
+
def self.call(identifiers:, **keywords)
|
9
|
+
new(identifiers: identifiers, **keywords).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(identifiers:, response_parser:, configuration: default_configuration)
|
13
|
+
self.identifiers = identifiers
|
14
|
+
self.configuration = configuration
|
15
|
+
self.response_parser = response_parser
|
16
|
+
initialize_urlsafe_base64_encoded_identifiers!
|
17
|
+
initialize_url_for_request!
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
response = client_request_handler.call(url: url_for_request)
|
22
|
+
response_parser.call(response: response)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
extend Forwardable
|
28
|
+
def_delegator :configuration, :client_request_handler
|
29
|
+
|
30
|
+
attr_accessor :response_parser, :configuration
|
31
|
+
|
32
|
+
attr_reader :identifiers, :urlsafe_base64_encoded_identifiers, :url_for_request
|
33
|
+
|
34
|
+
def identifiers=(input)
|
35
|
+
@identifiers = Array.wrap(input)
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize_urlsafe_base64_encoded_identifiers!
|
39
|
+
@urlsafe_base64_encoded_identifiers = Base64.urlsafe_encode64(
|
40
|
+
identifiers.map { |identifier| Base64.urlsafe_decode64(identifier) }.join("\n")
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize_url_for_request!
|
45
|
+
@url_for_request = configuration.url_for_retrieving_agents_for(
|
46
|
+
urlsafe_base64_encoded_identifiers: urlsafe_base64_encoded_identifiers
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def default_configuration
|
51
|
+
require 'cogitate'
|
52
|
+
Cogitate.configuration
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'cogitate/client/exceptions'
|
2
|
+
|
3
|
+
module Cogitate
|
4
|
+
module Client
|
5
|
+
# Responsibl
|
6
|
+
module ResponseParsers
|
7
|
+
def self.fetch(name)
|
8
|
+
return name if name.respond_to?(:call)
|
9
|
+
const_get("#{name}Extractor")
|
10
|
+
rescue NameError
|
11
|
+
raise Client::ResponseParserNotFound.new(name, self)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Dir.glob(File.expand_path('../response_parsers/**/*', __FILE__)).each do |filename|
|
18
|
+
require filename
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'cogitate/client/data_to_object_coercer'
|
3
|
+
|
4
|
+
module Cogitate
|
5
|
+
module Client
|
6
|
+
module ResponseParsers
|
7
|
+
# Responsible for parsing a Cogitate response and just getting the basic data
|
8
|
+
module AgentsWithDetailedIdentifiersExtractor
|
9
|
+
def self.call(response:)
|
10
|
+
JSON.parse(response).fetch('data').map { |datum| DataToObjectCoercer.call(datum) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'cogitate/client/data_to_object_coercer'
|
3
|
+
require 'cogitate/models/identifier'
|
4
|
+
|
5
|
+
module Cogitate
|
6
|
+
module Client
|
7
|
+
module ResponseParsers
|
8
|
+
# When you want an agent that omits identifiers associated with a group
|
9
|
+
module AgentsWithoutGroupMembershipExtractor
|
10
|
+
def self.call(response:)
|
11
|
+
identifier_guard = method(:identifier_is_not_a_group?)
|
12
|
+
JSON.parse(response).fetch('data').map { |datum| DataToObjectCoercer.call(datum, identifier_guard: identifier_guard) }
|
13
|
+
end
|
14
|
+
|
15
|
+
# @api private
|
16
|
+
# Perhaps a weird place to put this code, but it appears to work
|
17
|
+
def self.identifier_is_not_a_group?(identifier:)
|
18
|
+
identifier.strategy != Cogitate::Models::Identifier::GROUP_STRATEGY_NAME
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Cogitate
|
4
|
+
module Client
|
5
|
+
module ResponseParsers
|
6
|
+
# Responsible for parsing a Cogitate response and just getting the basic data
|
7
|
+
module BasicExtractor
|
8
|
+
def self.call(response:)
|
9
|
+
JSON.parse(response).fetch('data')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'json'
|
2
|
+
module Cogitate
|
3
|
+
module Client
|
4
|
+
module ResponseParsers
|
5
|
+
# Responsible for parsing a Cogitate response with a focus on getting emails
|
6
|
+
module EmailExtractor
|
7
|
+
def self.call(response:)
|
8
|
+
data = JSON.parse(response).fetch('data')
|
9
|
+
data.each_with_object({}) do |datum, mem|
|
10
|
+
mem[datum.fetch('id')] = datum.fetch('attributes').fetch('emails')
|
11
|
+
mem
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'cogitate/interfaces'
|
2
|
+
require 'cogitate/models/agent/with_token'
|
3
|
+
|
4
|
+
module Cogitate
|
5
|
+
module Client
|
6
|
+
# Responsible for converting a ticket into an agent
|
7
|
+
class RetrieveAgentFromTicket
|
8
|
+
# @api public
|
9
|
+
def self.call(ticket:, **keywords)
|
10
|
+
new(ticket: ticket, **keywords).call
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(ticket:, ticket_coercer: default_ticket_coercer, token_coercer: default_token_coercer)
|
14
|
+
self.ticket = ticket
|
15
|
+
self.ticket_coercer = ticket_coercer
|
16
|
+
self.token_coercer = token_coercer
|
17
|
+
end
|
18
|
+
|
19
|
+
include Contracts
|
20
|
+
Contract(Contracts::None => Cogitate::Interfaces::AgentWithTokenInterface)
|
21
|
+
def call
|
22
|
+
token = ticket_coercer.call(ticket: ticket)
|
23
|
+
agent = token_coercer.call(token: token)
|
24
|
+
Cogitate::Models::Agent::WithToken.new(token: token, agent: agent)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_accessor :ticket, :ticket_coercer, :token_coercer
|
30
|
+
|
31
|
+
def default_ticket_coercer
|
32
|
+
# Responsible for issuing a request back to the Cogitate Server and reading the body
|
33
|
+
require 'cogitate/client/ticket_to_token_coercer' unless defined?(TicketToTokenCoercer)
|
34
|
+
TicketToTokenCoercer
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_token_coercer
|
38
|
+
require 'cogitate/client/token_to_object_coercer' unless defined?(TokenToObjectCoercer)
|
39
|
+
TokenToObjectCoercer
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
module Cogitate
|
3
|
+
module Client
|
4
|
+
# Responsible for converting a ticket into a token by leveraging a remote call to a Cogitate server.
|
5
|
+
class TicketToTokenCoercer
|
6
|
+
# @api public
|
7
|
+
# @param ticket [String] A ticket issued by a Cogitate server
|
8
|
+
# @return token [String] An encoded token
|
9
|
+
#
|
10
|
+
# @see Cogitate::Services::UriSafeTicketForIdentifierCreator
|
11
|
+
def self.call(ticket:, **keywords)
|
12
|
+
new(ticket: ticket, **keywords).call
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(ticket:, configuration: default_configuration)
|
16
|
+
self.ticket = ticket
|
17
|
+
self.configuration = configuration
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
response = RestClient.get(configuration.url_for_claiming_a_ticket, params: { ticket: ticket })
|
22
|
+
response.body
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_accessor :ticket, :configuration
|
28
|
+
|
29
|
+
def default_configuration
|
30
|
+
require 'cogitate'
|
31
|
+
Cogitate.configuration
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Cogitate
|
2
|
+
module Client
|
3
|
+
# Responsible for decoding a Cogitate token into an object (or objects)
|
4
|
+
#
|
5
|
+
# @see Cogitate::Services::Tokenizer for how the tokens get encoded and decoded
|
6
|
+
class TokenToObjectCoercer
|
7
|
+
# @api public
|
8
|
+
def self.call(token:, **keywords)
|
9
|
+
new(token: token, **keywords).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(token:, token_to_data_coercer: default_token_to_data_coercer, data_to_object_coercer: default_data_to_object_coercer)
|
13
|
+
self.token = token
|
14
|
+
self.token_to_data_coercer = token_to_data_coercer
|
15
|
+
self.data_to_object_coercer = data_to_object_coercer
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
data = token_to_data_coercer.call(token: token)
|
20
|
+
data_to_object_coercer.call(data)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_accessor :token, :token_to_data_coercer, :data_to_object_coercer
|
26
|
+
|
27
|
+
def default_token_to_data_coercer
|
28
|
+
require 'cogitate/services/tokenizer' unless defined?(Cogitate::Services::Tokenizer)
|
29
|
+
Cogitate::Services::Tokenizer.method(:from_token)
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_data_to_object_coercer
|
33
|
+
require 'cogitate/client/data_to_object_coercer' unless defined?(Cogitate::Client::DataToObjectCoercer)
|
34
|
+
Cogitate::Client::DataToObjectCoercer
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Cogitate
|
2
|
+
# Responsible for containing the configuration information for Cogitate
|
3
|
+
class Configuration
|
4
|
+
# If something is not properly configured
|
5
|
+
class ConfigurationError < RuntimeError
|
6
|
+
def initialize(method_name)
|
7
|
+
super("Cogitate::Configuration##{method_name} has not been set")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
CONFIG_ATTRIBUTE_NAMES = [
|
12
|
+
# !@attribute [rw] after_authentication_callback_url
|
13
|
+
# Where should the Cogitate server redirect to after a successful authentication?
|
14
|
+
# @note Cogitate::Client configuration (and not Cogitate::Server)
|
15
|
+
:after_authentication_callback_url,
|
16
|
+
# !@attribute [rw] remote_server_base_url
|
17
|
+
# @note Cogitate::Client configuration
|
18
|
+
# @return [String] What is the URL of the Cogitate server you want to connect to?
|
19
|
+
:remote_server_base_url,
|
20
|
+
# !@attribute [rw] tokenizer_password
|
21
|
+
# What is the tokenizer password you are going to be using to:
|
22
|
+
# * create a token (i.e. a private RSA key)
|
23
|
+
# * decode a token (i.e. a public RSA key)
|
24
|
+
#
|
25
|
+
# If you are implemnting a Cogitate client, you'll want to use the public key
|
26
|
+
# @return [String]
|
27
|
+
# @note Cogitate::Client and Cogitate::Server configuration
|
28
|
+
# @see Cogitate::Services::Tokenizer
|
29
|
+
:tokenizer_password,
|
30
|
+
# !@attribute [rw] tokenizer_encryption_type
|
31
|
+
# What is the encryption type for the tokenizer. You will need to ensure that
|
32
|
+
# the Cogitate server and client are using the same encryption mechanism.
|
33
|
+
# @return [String]
|
34
|
+
# @note Cogitate::Client and Cogitate::Server configuration
|
35
|
+
# @example `configuration.tokenizer_encryption_type = 'RS256'`
|
36
|
+
# @see Cogitate::Services::Tokenizer
|
37
|
+
:tokenizer_encryption_type,
|
38
|
+
# !@attribute [rw] tokenizer_issuer_claim
|
39
|
+
# As per JSON Web Token specification, what is the Issuer Claim
|
40
|
+
# the Cogitate server and client are using the same encryption mechanism.
|
41
|
+
#
|
42
|
+
# @note Cogitate::Client and Cogitate::Server configuration
|
43
|
+
# @return [String]
|
44
|
+
# @example `configuration.tokenizer_issuer_claim = 'https://library.nd.edu'`
|
45
|
+
# @see https://tools.ietf.org/html/rfc7519#section-4.1.1
|
46
|
+
# @see https://github.com/jwt/ruby-jwt#issuer-claim
|
47
|
+
:tokenizer_issuer_claim
|
48
|
+
].freeze
|
49
|
+
|
50
|
+
def initialize(client_request_handler: default_client_request_handler, **keywords)
|
51
|
+
CONFIG_ATTRIBUTE_NAMES.each do |name|
|
52
|
+
send("#{name}=", keywords.fetch(name)) if keywords.key?(name)
|
53
|
+
end
|
54
|
+
self.client_request_handler = client_request_handler
|
55
|
+
end
|
56
|
+
|
57
|
+
CONFIG_ATTRIBUTE_NAMES.each do |method_name|
|
58
|
+
attr_writer method_name
|
59
|
+
define_method(method_name) do
|
60
|
+
instance_variable_get("@#{method_name}") || fail(ConfigurationError, method_name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# What is the authentication URL of the client's configured Cogitate server
|
65
|
+
# @note Cogitate::Client configuration (and not Cogitate::Server)
|
66
|
+
# @return String
|
67
|
+
def url_for_authentication
|
68
|
+
query_params = "?after_authentication_callback_url=#{CGI.escape(after_authentication_callback_url)}"
|
69
|
+
File.join(remote_server_base_url, '/authenticate') << query_params
|
70
|
+
end
|
71
|
+
|
72
|
+
# What is the URL for claiming a ticket
|
73
|
+
# @note Cogitate::Client configuration (and not Cogitate::Server)
|
74
|
+
# @return String
|
75
|
+
def url_for_claiming_a_ticket
|
76
|
+
File.join(remote_server_base_url, '/claim')
|
77
|
+
end
|
78
|
+
|
79
|
+
# What is the URL for retrieving the agents based on the given identifiers
|
80
|
+
# @note Cogitate::Client configuration (and not Cogitate::Server)
|
81
|
+
# @param urlsafe_base64_encoded_identifiers [String]
|
82
|
+
# @return String
|
83
|
+
def url_for_retrieving_agents_for(urlsafe_base64_encoded_identifiers:)
|
84
|
+
File.join(remote_server_base_url, '/api/agents', urlsafe_base64_encoded_identifiers)
|
85
|
+
end
|
86
|
+
|
87
|
+
# What will be negotiating the remote request to the Cogitate::Server
|
88
|
+
#
|
89
|
+
# @return [#call(url:)]
|
90
|
+
# @see #default_client_request_handler for interface
|
91
|
+
attr_accessor :client_request_handler
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def default_client_request_handler
|
96
|
+
-> (url:) { RestClient.get(url).body }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Cogitate
|
2
|
+
# When the thing we are trying to decode is improperly encoded, this exception is to provide clarity
|
3
|
+
class InvalidIdentifierEncoding < ArgumentError
|
4
|
+
def initialize(encoded_string:)
|
5
|
+
super("Unable to decode #{encoded_string}; Expected it to be URL-safe Base64 encoded (use Base64.urlsafe_encode64)")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# When the thing we have decoded is not properly formated, this exception is to provide clarity
|
10
|
+
class InvalidIdentifierFormat < RuntimeError
|
11
|
+
EXPECTED_FORMAT = "strategy\tvalue\nstrategy\tvalue".freeze
|
12
|
+
def initialize(decoded_string:)
|
13
|
+
super("Expected #{decoded_string.inspect} to be of the format #{EXPECTED_FORMAT.inspect}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# When the thing we have decoded is not properly formated, this exception is to provide clarity
|
18
|
+
class InvalidMembershipVisitationKeys < RuntimeError
|
19
|
+
def initialize(identifier:, visitation_type:)
|
20
|
+
super(
|
21
|
+
"Unable to find membership visitation service for visitation_type: #{visitation_type.inspect}, identifier: #{identifier.inspect}"
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Commitment
|
2
|
+
# :nodoc:
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root(File.expand_path("../templates", __FILE__))
|
5
|
+
|
6
|
+
def create_cogitate_initializer
|
7
|
+
template('cogitate_initializer.rb.erb', 'config/initializers/cogitate_initializer.rb')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Cogitate.configure do |config|
|
2
|
+
<% if defined?(Figaro) %>
|
3
|
+
config.tokenizer_password = Figaro.env.cogitate_services_tokenizer_public_password!
|
4
|
+
config.remote_server_base_url = Figaro.env.cogitate_services_remote_server_base_url!
|
5
|
+
config.tokenizer_encryption_type = Figaro.env.cogitate_services_tokenizer_encryption_type!
|
6
|
+
config.tokenizer_issuer_claim = Figaro.env.cogitate_services_tokenizer_issuer_claim!
|
7
|
+
config.after_authentication_callback_url = Figaro.env.cogitate_services_after_authentication_callback_url!
|
8
|
+
<% else %>
|
9
|
+
# The public key for the RSA key
|
10
|
+
config.tokenizer_password = 'You need to provide this'
|
11
|
+
config.tokenizer_encryption_type = 'RS256'
|
12
|
+
config.tokenizer_issuer_claim = 'CHANGE THIS: Cogitate Client'
|
13
|
+
# Change this
|
14
|
+
config.remote_server_base_url = "http://localhost:3001"
|
15
|
+
config.after_authentication_callback_url = "http://localhost:3000/authenticate/from/cogitate"
|
16
|
+
<% end %>
|
17
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'contracts'
|
2
|
+
|
3
|
+
module Cogitate
|
4
|
+
# Herein lies the Cogitate namespace
|
5
|
+
module Interfaces
|
6
|
+
include Contracts
|
7
|
+
IdentifierInterface = RespondTo[:strategy, :identifying_value, :<=>, :name, :id, :encoded_id]
|
8
|
+
IdentifierCollectionInterface = Contracts::ArrayOf[Cogitate::Interfaces::IdentifierInterface]
|
9
|
+
|
10
|
+
AgentInterface = RespondTo[
|
11
|
+
:with_identifiers, :with_verified_identifiers, :with_emails, :add_identifier, :add_verified_identifier, :add_email, :ids, :name
|
12
|
+
]
|
13
|
+
AgentCollectionInterface = Contracts::ArrayOf[Cogitate::Interfaces::AgentInterface]
|
14
|
+
AgentWithTokenInterface = And[AgentInterface, RespondTo[:to_token]]
|
15
|
+
|
16
|
+
VisitorInterface = RespondTo[:visit]
|
17
|
+
VisitorV2Interface = And[VisitorInterface, RespondTo[:return_from_visitations]]
|
18
|
+
AgentCollectorInitializationInterface = KeywordArgs[visitor: VisitorInterface, agent: Optional[AgentInterface]]
|
19
|
+
|
20
|
+
# All identifiers must be comparable, otherwise we could spiral into an endless visitation of related identifiers
|
21
|
+
VerifiedIdentifierInterface = IdentifierInterface
|
22
|
+
|
23
|
+
AuthenticationVectorNetidInterface = And[
|
24
|
+
RespondTo[:first_name, :last_name, :netid, :full_name, :ndguid, :strategy, :identifying_value],
|
25
|
+
IdentifierInterface
|
26
|
+
]
|
27
|
+
|
28
|
+
HostInterface = RespondTo[:invite]
|
29
|
+
|
30
|
+
IdentifierInitializationInterface = KeywordArgs[strategy: RespondTo[:to_s], identifying_value: Any]
|
31
|
+
IdentifierBuilderInterface = Func[IdentifierInitializationInterface]
|
32
|
+
|
33
|
+
FindNetidRepositoryInterface = RespondTo[:find]
|
34
|
+
|
35
|
+
MembershipVisitationStrategyInterface = RespondTo[:call]
|
36
|
+
|
37
|
+
VerifiableIdentifierInterface = And[IdentifierInterface, RespondTo[:verified?]]
|
38
|
+
VerifiedGroupInterface = And[VerifiableIdentifierInterface, RespondTo[:name, :description]]
|
39
|
+
end
|
40
|
+
end
|