cogitate 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|