carddb 0.4.2 → 0.4.6
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/.rspec_status +158 -143
- data/README.md +15 -21
- data/lib/carddb/batch.rb +40 -6
- data/lib/carddb/client.rb +7 -0
- data/lib/carddb/collection.rb +112 -101
- data/lib/carddb/deck_tokens.rb +76 -61
- data/lib/carddb/query_builder.rb +318 -176
- data/lib/carddb/resources/decks.rb +94 -115
- data/lib/carddb/resources/rulesets.rb +180 -0
- data/lib/carddb/version.rb +1 -1
- data/lib/carddb.rb +8 -0
- metadata +2 -1
data/lib/carddb/collection.rb
CHANGED
|
@@ -1961,91 +1961,12 @@ module CardDB
|
|
|
1961
1961
|
def updated_at = parse_time(data['updatedAt'])
|
|
1962
1962
|
end
|
|
1963
1963
|
|
|
1964
|
-
|
|
1965
|
-
class DeckPreviewToken < Resource
|
|
1966
|
-
def id
|
|
1967
|
-
data['id']
|
|
1968
|
-
end
|
|
1969
|
-
|
|
1970
|
-
def deck_id
|
|
1971
|
-
data['deckId']
|
|
1972
|
-
end
|
|
1973
|
-
|
|
1974
|
-
def label
|
|
1975
|
-
data['label']
|
|
1976
|
-
end
|
|
1977
|
-
|
|
1978
|
-
def expires_at
|
|
1979
|
-
parse_time(data['expiresAt'])
|
|
1980
|
-
end
|
|
1981
|
-
|
|
1982
|
-
def revoked_at
|
|
1983
|
-
parse_time(data['revokedAt'])
|
|
1984
|
-
end
|
|
1985
|
-
|
|
1986
|
-
def last_used_at
|
|
1987
|
-
parse_time(data['lastUsedAt'])
|
|
1988
|
-
end
|
|
1989
|
-
|
|
1990
|
-
def created_at
|
|
1991
|
-
parse_time(data['createdAt'])
|
|
1992
|
-
end
|
|
1993
|
-
|
|
1994
|
-
def updated_at
|
|
1995
|
-
parse_time(data['updatedAt'])
|
|
1996
|
-
end
|
|
1997
|
-
|
|
1998
|
-
private
|
|
1999
|
-
|
|
2000
|
-
def parse_time(value)
|
|
2001
|
-
return nil unless value
|
|
2002
|
-
|
|
2003
|
-
Time.parse(value)
|
|
2004
|
-
rescue ArgumentError
|
|
2005
|
-
value
|
|
2006
|
-
end
|
|
2007
|
-
end
|
|
2008
|
-
|
|
2009
|
-
# Wrapper for deck preview token creation payloads
|
|
2010
|
-
class DeckPreviewTokenCreatePayload < Resource
|
|
2011
|
-
def token
|
|
2012
|
-
data['token']
|
|
2013
|
-
end
|
|
2014
|
-
|
|
2015
|
-
def preview_token
|
|
2016
|
-
@preview_token ||= data['previewToken'] ? DeckPreviewToken.new(data['previewToken'], client: client) : nil
|
|
2017
|
-
end
|
|
2018
|
-
end
|
|
2019
|
-
|
|
2020
|
-
class DeckEmbedToken < Resource
|
|
2021
|
-
def id = data['id']
|
|
2022
|
-
def deck_id = data['deckId']
|
|
2023
|
-
def api_application_id = data['apiApplicationId']
|
|
2024
|
-
def label = data['label']
|
|
2025
|
-
def read_mode = data['readMode']
|
|
2026
|
-
def allowed_origins = data['allowedOrigins'] || []
|
|
2027
|
-
def external_subject = data['externalSubject']
|
|
2028
|
-
def expires_at = parse_time(data['expiresAt'])
|
|
2029
|
-
def revoked_at = parse_time(data['revokedAt'])
|
|
2030
|
-
def last_used_at = parse_time(data['lastUsedAt'])
|
|
2031
|
-
def created_at = parse_time(data['createdAt'])
|
|
2032
|
-
def updated_at = parse_time(data['updatedAt'])
|
|
2033
|
-
end
|
|
2034
|
-
|
|
2035
|
-
class DeckEmbedTokenCreatePayload < Resource
|
|
2036
|
-
def token = data['token']
|
|
2037
|
-
|
|
2038
|
-
def embed_token
|
|
2039
|
-
@embed_token ||= data['embedToken'] ? DeckEmbedToken.new(data['embedToken'], client: client) : nil
|
|
2040
|
-
end
|
|
2041
|
-
end
|
|
2042
|
-
|
|
2043
|
-
class DeckAccessTokenIssuer < Resource
|
|
1964
|
+
class DeckTokenIssuer < Resource
|
|
2044
1965
|
def id = data['id']
|
|
2045
|
-
def deck_id = data['deckId']
|
|
2046
1966
|
def api_application_id = data['apiApplicationId']
|
|
2047
1967
|
def label = data['label']
|
|
2048
|
-
def
|
|
1968
|
+
def allowed_scopes = data['allowedScopes'] || []
|
|
1969
|
+
def allowed_representations = data['allowedRepresentations'] || []
|
|
2049
1970
|
def max_token_lifetime_seconds = data['maxTokenLifetimeSeconds']
|
|
2050
1971
|
def direct_signing_alg = data['directSigningAlg']
|
|
2051
1972
|
def direct_signing_key_id = data['directSigningKeyId']
|
|
@@ -2056,13 +1977,15 @@ module CardDB
|
|
|
2056
1977
|
def updated_at = parse_time(data['updatedAt'])
|
|
2057
1978
|
end
|
|
2058
1979
|
|
|
2059
|
-
class
|
|
1980
|
+
class DeckToken < Resource
|
|
2060
1981
|
def id = data['id']
|
|
2061
|
-
def deck_id = data['deckId']
|
|
2062
1982
|
def api_application_id = data['apiApplicationId']
|
|
2063
1983
|
def issuer_id = data['issuerId']
|
|
2064
|
-
def
|
|
1984
|
+
def deck_id = data['deckId']
|
|
1985
|
+
def game_id = data['gameId']
|
|
2065
1986
|
def external_subject = data['externalSubject']
|
|
1987
|
+
def scopes = data['scopes'] || []
|
|
1988
|
+
def representation = data['representation']
|
|
2066
1989
|
def expires_at = parse_time(data['expiresAt'])
|
|
2067
1990
|
def revoked_at = parse_time(data['revokedAt'])
|
|
2068
1991
|
def last_used_at = parse_time(data['lastUsedAt'])
|
|
@@ -2070,34 +1993,122 @@ module CardDB
|
|
|
2070
1993
|
def updated_at = parse_time(data['updatedAt'])
|
|
2071
1994
|
end
|
|
2072
1995
|
|
|
2073
|
-
class
|
|
1996
|
+
class DeckTokenExchangePayload < Resource
|
|
2074
1997
|
def token = data['token']
|
|
2075
1998
|
|
|
2076
|
-
def
|
|
2077
|
-
@
|
|
1999
|
+
def deck_token
|
|
2000
|
+
@deck_token ||= data['deckToken'] ? DeckToken.new(data['deckToken'], client: client) : nil
|
|
2078
2001
|
end
|
|
2079
2002
|
end
|
|
2080
2003
|
|
|
2081
|
-
class
|
|
2004
|
+
class Ruleset < Resource
|
|
2082
2005
|
def id = data['id']
|
|
2083
|
-
def account_id = data['accountId']
|
|
2084
|
-
def api_application_id = data['apiApplicationId']
|
|
2085
2006
|
def game_id = data['gameId']
|
|
2086
|
-
def
|
|
2087
|
-
def
|
|
2088
|
-
def
|
|
2089
|
-
def
|
|
2090
|
-
def
|
|
2091
|
-
def
|
|
2007
|
+
def game = data['game']
|
|
2008
|
+
def key = data['key']
|
|
2009
|
+
def name = data['name']
|
|
2010
|
+
def description = data['description']
|
|
2011
|
+
def tags = data['tags'] || []
|
|
2012
|
+
def sort_order = data['sortOrder']
|
|
2013
|
+
def current_version_id = data['currentVersionId']
|
|
2014
|
+
def archived_at = parse_time(data['archivedAt'])
|
|
2015
|
+
def archived? = !!data['isArchived']
|
|
2092
2016
|
def created_at = parse_time(data['createdAt'])
|
|
2093
2017
|
def updated_at = parse_time(data['updatedAt'])
|
|
2018
|
+
|
|
2019
|
+
def current_version
|
|
2020
|
+
@current_version ||= data['currentVersion'] ? RulesetVersion.new(data['currentVersion'], client: client) : nil
|
|
2021
|
+
end
|
|
2094
2022
|
end
|
|
2095
2023
|
|
|
2096
|
-
class
|
|
2097
|
-
def
|
|
2024
|
+
class RulesetVersion < Resource
|
|
2025
|
+
def id = data['id']
|
|
2026
|
+
def ruleset_id = data['rulesetId']
|
|
2027
|
+
def version_label = data['versionLabel']
|
|
2028
|
+
def status = data['status']
|
|
2029
|
+
def rules = data['rules'] || {}
|
|
2030
|
+
def card_dataset_id = data['cardDatasetId']
|
|
2031
|
+
def card_dataset_version_id = data['cardDatasetVersionId']
|
|
2032
|
+
def source_dataset_id = data['sourceDatasetId']
|
|
2033
|
+
def source_dataset_version_id = data['sourceDatasetVersionId']
|
|
2034
|
+
def source_dataset_record_id = data['sourceDatasetRecordId']
|
|
2035
|
+
def source_record_identifier = data['sourceRecordIdentifier']
|
|
2036
|
+
def rules_fingerprint = data['rulesFingerprint']
|
|
2037
|
+
def effective_at = parse_time(data['effectiveAt'])
|
|
2038
|
+
def changelog = data['changelog']
|
|
2039
|
+
def created_by_account_id = data['createdByAccountId']
|
|
2040
|
+
def published_by_account_id = data['publishedByAccountId']
|
|
2041
|
+
def published_at = parse_time(data['publishedAt'])
|
|
2042
|
+
def superseded_at = parse_time(data['supersededAt'])
|
|
2043
|
+
def created_at = parse_time(data['createdAt'])
|
|
2044
|
+
def updated_at = parse_time(data['updatedAt'])
|
|
2045
|
+
def draft? = status == 'DRAFT'
|
|
2046
|
+
def active? = status == 'ACTIVE'
|
|
2047
|
+
|
|
2048
|
+
def section_definitions
|
|
2049
|
+
@section_definitions ||= (data['sectionDefinitions'] || []).map do |definition|
|
|
2050
|
+
DeckSectionDefinition.new(definition, client: client)
|
|
2051
|
+
end
|
|
2052
|
+
end
|
|
2053
|
+
end
|
|
2054
|
+
|
|
2055
|
+
class DeckValidationRules < Resource
|
|
2056
|
+
def ruleset_id = data['rulesetId']
|
|
2057
|
+
def ruleset_version_id = data['rulesetVersionId']
|
|
2058
|
+
def ruleset_key = data['rulesetKey']
|
|
2059
|
+
def ruleset_version_label = data['rulesetVersionLabel']
|
|
2060
|
+
def rules = data['rules'] || {}
|
|
2061
|
+
|
|
2062
|
+
def sections
|
|
2063
|
+
@sections ||= (data['sections'] || []).map { |section| DeckSectionDefinition.new(section, client: client) }
|
|
2064
|
+
end
|
|
2065
|
+
end
|
|
2066
|
+
|
|
2067
|
+
class RulesetValidationImpactDeck < Resource
|
|
2068
|
+
def deck_id = data['deckId']
|
|
2069
|
+
def title = data['title']
|
|
2070
|
+
def slug = data['slug']
|
|
2071
|
+
def state = data['state']
|
|
2072
|
+
def unpublished_changes? = !!data['hasUnpublishedChanges']
|
|
2073
|
+
def valid? = !!data['valid']
|
|
2074
|
+
def blocker_count = data['blockerCount']
|
|
2075
|
+
def warning_count = data['warningCount']
|
|
2076
|
+
def affected_entry_count = data['affectedEntryCount']
|
|
2077
|
+
|
|
2078
|
+
def issues
|
|
2079
|
+
@issues ||= (data['issues'] || []).map { |issue| DeckValidationIssue.new(issue, client: client) }
|
|
2080
|
+
end
|
|
2081
|
+
end
|
|
2082
|
+
|
|
2083
|
+
class RulesetValidationImpact < Resource
|
|
2084
|
+
def ruleset_id = data['rulesetId']
|
|
2085
|
+
def ruleset_version_id = data['rulesetVersionId']
|
|
2086
|
+
def checked_at = parse_time(data['checkedAt'])
|
|
2087
|
+
def affected_deck_count = data['affectedDeckCount']
|
|
2088
|
+
def invalid_deck_count = data['invalidDeckCount']
|
|
2089
|
+
def blocker_count = data['blockerCount']
|
|
2090
|
+
def warning_count = data['warningCount']
|
|
2098
2091
|
|
|
2099
|
-
def
|
|
2100
|
-
@
|
|
2092
|
+
def results
|
|
2093
|
+
@results ||= (data['results'] || []).map { |deck| RulesetValidationImpactDeck.new(deck, client: client) }
|
|
2094
|
+
end
|
|
2095
|
+
end
|
|
2096
|
+
|
|
2097
|
+
class RulesetVersionDraftImportPreview < Resource
|
|
2098
|
+
def source_dataset_id = data['sourceDatasetId']
|
|
2099
|
+
def source_dataset_version_id = data['sourceDatasetVersionId']
|
|
2100
|
+
def source_dataset_record_id = data['sourceDatasetRecordId']
|
|
2101
|
+
def source_record_identifier = data['sourceRecordIdentifier']
|
|
2102
|
+
def rules = data['rules'] || {}
|
|
2103
|
+
def rules_fingerprint = data['rulesFingerprint']
|
|
2104
|
+
def summary = data['summary']
|
|
2105
|
+
def section_count = data['sectionCount']
|
|
2106
|
+
def copy_limit_count = data['copyLimitCount']
|
|
2107
|
+
|
|
2108
|
+
def section_definitions
|
|
2109
|
+
@section_definitions ||= (data['sectionDefinitions'] || []).map do |definition|
|
|
2110
|
+
DeckSectionDefinition.new(definition, client: client)
|
|
2111
|
+
end
|
|
2101
2112
|
end
|
|
2102
2113
|
end
|
|
2103
2114
|
|
data/lib/carddb/deck_tokens.rb
CHANGED
|
@@ -6,25 +6,19 @@ require 'json'
|
|
|
6
6
|
require 'openssl'
|
|
7
7
|
|
|
8
8
|
module CardDB
|
|
9
|
-
# Server-side helpers for minting direct signed deck
|
|
9
|
+
# Server-side helpers for minting direct signed deck tokens.
|
|
10
10
|
module DeckTokens
|
|
11
|
-
TOKEN_USE = 'carddb.
|
|
12
|
-
AUDIENCE = 'carddb.
|
|
11
|
+
TOKEN_USE = 'carddb.deck_token.v1'
|
|
12
|
+
AUDIENCE = 'carddb.deck'
|
|
13
13
|
ED25519_PKCS8_PREFIX = [
|
|
14
14
|
'302e020100300506032b657004220420'
|
|
15
15
|
].pack('H*').freeze
|
|
16
|
-
READ_MODES = {
|
|
17
|
-
'PUBLIC' => 'public',
|
|
18
|
-
'EMBED' => 'embed',
|
|
19
|
-
'FULL' => 'full'
|
|
20
|
-
}.freeze
|
|
21
|
-
private_constant :READ_MODES
|
|
22
16
|
|
|
23
17
|
class << self
|
|
24
|
-
# Generate an Ed25519 keypair for CardDB direct deck
|
|
18
|
+
# Generate an Ed25519 keypair for CardDB direct deck token signing.
|
|
25
19
|
#
|
|
26
20
|
# @return [Hash{Symbol => String}] Includes a PEM private key and the base64-encoded raw public key
|
|
27
|
-
def
|
|
21
|
+
def generate_direct_key_pair
|
|
28
22
|
signing_key = Ed25519::SigningKey.generate
|
|
29
23
|
|
|
30
24
|
{
|
|
@@ -33,43 +27,31 @@ module CardDB
|
|
|
33
27
|
}
|
|
34
28
|
end
|
|
35
29
|
|
|
36
|
-
# Sign a direct deck
|
|
30
|
+
# Sign a direct deck token that CardDB can verify with the issuer public key.
|
|
37
31
|
#
|
|
38
32
|
# @param issuer_id [String]
|
|
39
|
-
# @param deck_id [String]
|
|
40
33
|
# @param api_application_id [String]
|
|
41
34
|
# @param key_id [String]
|
|
42
35
|
# @param private_key [OpenSSL::PKey::PKey, String] Ed25519 private key as an OpenSSL key or PEM/DER string
|
|
43
|
-
# @param
|
|
36
|
+
# @param deck_id [String, nil]
|
|
37
|
+
# @param publisher_slug [String, nil]
|
|
38
|
+
# @param game_key [String, nil]
|
|
39
|
+
# @param external_subject [String, nil]
|
|
40
|
+
# @param scopes [Array<String>]
|
|
41
|
+
# @param representation [String, nil]
|
|
44
42
|
# @param subject [String, nil]
|
|
45
43
|
# @param token_id [String, nil]
|
|
46
44
|
# @param issued_at [Time]
|
|
47
45
|
# @param expires_at [Time]
|
|
48
46
|
# @return [String]
|
|
49
47
|
# @raise [ArgumentError]
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
issued_at: issued_at,
|
|
58
|
-
expires_at: expires_at
|
|
59
|
-
)
|
|
60
|
-
normalized_read_mode = normalize_read_mode!(read_mode)
|
|
61
|
-
key = normalize_private_key!(private_key)
|
|
62
|
-
header = { alg: 'EdDSA', kid: key_id.strip }
|
|
63
|
-
payload = build_payload(
|
|
64
|
-
issuer_id: issuer_id,
|
|
65
|
-
deck_id: deck_id,
|
|
66
|
-
api_application_id: api_application_id,
|
|
67
|
-
read_mode: normalized_read_mode,
|
|
68
|
-
subject: subject,
|
|
69
|
-
token_id: token_id,
|
|
70
|
-
issued_at: issued_at,
|
|
71
|
-
expires_at: expires_at
|
|
72
|
-
)
|
|
48
|
+
def sign_direct_token(**attributes)
|
|
49
|
+
attributes = validate_signing_input!(attributes)
|
|
50
|
+
normalized_scopes = normalize_scopes!(attributes[:scopes])
|
|
51
|
+
validate_token_target!(attributes)
|
|
52
|
+
key = normalize_private_key!(attributes[:private_key])
|
|
53
|
+
header = { alg: 'EdDSA', kid: attributes[:key_id].strip }
|
|
54
|
+
payload = build_payload(attributes.merge(scopes: normalized_scopes))
|
|
73
55
|
|
|
74
56
|
encoded_header = base64url_encode(JSON.generate(header))
|
|
75
57
|
encoded_payload = base64url_encode(JSON.generate(payload))
|
|
@@ -81,33 +63,52 @@ module CardDB
|
|
|
81
63
|
|
|
82
64
|
private
|
|
83
65
|
|
|
84
|
-
def validate_signing_input!(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
validate_required_string!(
|
|
88
|
-
validate_required_string!(
|
|
66
|
+
def validate_signing_input!(attributes)
|
|
67
|
+
validate_known_keys!(attributes)
|
|
68
|
+
attributes = { issued_at: Time.now }.merge(attributes)
|
|
69
|
+
validate_required_string!(attributes[:issuer_id], 'issuer_id')
|
|
70
|
+
validate_required_string!(attributes[:api_application_id], 'api_application_id')
|
|
71
|
+
validate_required_string!(attributes[:key_id], 'key_id')
|
|
89
72
|
|
|
90
|
-
issued_at = normalize_time!(issued_at, 'issued_at')
|
|
91
|
-
expires_at = normalize_time!(expires_at, 'expires_at')
|
|
73
|
+
issued_at = normalize_time!(attributes[:issued_at], 'issued_at')
|
|
74
|
+
expires_at = normalize_time!(attributes[:expires_at], 'expires_at')
|
|
92
75
|
raise ArgumentError, 'expires_at must be after issued_at' unless expires_at > issued_at
|
|
93
76
|
|
|
94
|
-
|
|
77
|
+
attributes.merge(issued_at: issued_at, expires_at: expires_at)
|
|
95
78
|
end
|
|
96
79
|
|
|
97
|
-
def
|
|
98
|
-
|
|
80
|
+
def validate_known_keys!(attributes)
|
|
81
|
+
allowed_keys = %i[
|
|
82
|
+
issuer_id api_application_id key_id private_key scopes expires_at deck_id publisher_slug game_key
|
|
83
|
+
external_subject representation subject token_id issued_at
|
|
84
|
+
]
|
|
85
|
+
unknown_keys = attributes.keys - allowed_keys
|
|
86
|
+
return if unknown_keys.empty?
|
|
87
|
+
|
|
88
|
+
raise ArgumentError, "unknown keyword: :#{unknown_keys.first}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def build_payload(attributes)
|
|
99
92
|
payload = {
|
|
100
93
|
token_use: TOKEN_USE,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
iss: issuer_id.strip,
|
|
94
|
+
app_id: attributes[:api_application_id].strip,
|
|
95
|
+
scopes: attributes[:scopes],
|
|
96
|
+
iss: attributes[:issuer_id].strip,
|
|
105
97
|
aud: AUDIENCE,
|
|
106
|
-
iat: issued_at.to_i,
|
|
107
|
-
exp: expires_at.to_i
|
|
98
|
+
iat: attributes[:issued_at].to_i,
|
|
99
|
+
exp: attributes[:expires_at].to_i
|
|
108
100
|
}
|
|
109
|
-
|
|
110
|
-
|
|
101
|
+
{
|
|
102
|
+
deck_id: :deck_id,
|
|
103
|
+
publisher_slug: :publisher_slug,
|
|
104
|
+
game_key: :game_key,
|
|
105
|
+
external_subject: :external_subject,
|
|
106
|
+
representation: :representation,
|
|
107
|
+
sub: :subject,
|
|
108
|
+
jti: :token_id
|
|
109
|
+
}.each do |claim, key|
|
|
110
|
+
add_optional_claim!(payload, claim, attributes[key])
|
|
111
|
+
end
|
|
111
112
|
payload
|
|
112
113
|
end
|
|
113
114
|
|
|
@@ -120,18 +121,32 @@ module CardDB
|
|
|
120
121
|
raise ArgumentError, "#{name} is required" unless value.is_a?(String) && !value.strip.empty?
|
|
121
122
|
end
|
|
122
123
|
|
|
124
|
+
def normalize_scopes!(scopes)
|
|
125
|
+
raise ArgumentError, 'scopes must include at least one DeckTokenScope' unless scopes.is_a?(Array) && !scopes.empty?
|
|
126
|
+
|
|
127
|
+
scopes.map do |scope|
|
|
128
|
+
validate_required_string!(scope, 'scope')
|
|
129
|
+
scope.strip
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def validate_token_target!(attributes)
|
|
134
|
+
return if string_present?(attributes[:deck_id])
|
|
135
|
+
return if %i[publisher_slug game_key external_subject].all? { |key| string_present?(attributes[key]) }
|
|
136
|
+
|
|
137
|
+
raise ArgumentError, 'deck_id or publisher_slug, game_key, and external_subject are required'
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def string_present?(value)
|
|
141
|
+
value&.strip&.length&.positive?
|
|
142
|
+
end
|
|
143
|
+
|
|
123
144
|
def normalize_time!(value, name)
|
|
124
145
|
raise ArgumentError, "#{name} must be a Time" unless value.is_a?(Time)
|
|
125
146
|
|
|
126
147
|
value
|
|
127
148
|
end
|
|
128
149
|
|
|
129
|
-
def normalize_read_mode!(read_mode)
|
|
130
|
-
READ_MODES.fetch(read_mode.to_s.upcase) do
|
|
131
|
-
raise ArgumentError, 'read_mode must be PUBLIC, EMBED, or FULL'
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
|
|
135
150
|
def normalize_private_key!(private_key)
|
|
136
151
|
return private_key if private_key.is_a?(OpenSSL::PKey::PKey)
|
|
137
152
|
|