Skip to content

Commit

Permalink
feat: metadata TOC objects
Browse files Browse the repository at this point in the history
  • Loading branch information
bdewater committed May 20, 2019
1 parent f5ced19 commit 5851044
Show file tree
Hide file tree
Showing 16 changed files with 448 additions and 0 deletions.
38 changes: 38 additions & 0 deletions lib/webauthn/metadata/attributes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module WebAuthn
module Metadata
module Attributes
def underscore_name(name)
name
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
.downcase
.to_sym
end
private :underscore_name

def json_accessor(name, coercer = nil)
underscored_name = underscore_name(name)
attr_accessor underscored_name

if coercer
define_method(:"#{underscored_name}=") do |value|
coerced_value = coercer.coerce(value)
instance_variable_set(:"@#{underscored_name}", coerced_value)
end
end
end

def from_json(hash = {})
instance = new
hash.each do |k, v|
method_name = :"#{underscore_name(k)}="
instance.public_send(method_name, v) if instance.respond_to?(method_name)
end

instance
end
end
end
end
20 changes: 20 additions & 0 deletions lib/webauthn/metadata/biometric_status_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

require "webauthn/metadata/attributes"
require "webauthn/metadata/coercer/date"

module WebAuthn
module Metadata
class BiometricStatusReport
extend Attributes

json_accessor("certLevel")
json_accessor("modality")
json_accessor("effectiveDate", Coercer::Date)
json_accessor("certificationDescriptor")
json_accessor("certificateNumber")
json_accessor("certificationPolicyVersion")
json_accessor("certificationRequirementsVersion")
end
end
end
18 changes: 18 additions & 0 deletions lib/webauthn/metadata/coercer/biometric_status_reports.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

require "webauthn/metadata/biometric_status_report"

module WebAuthn
module Metadata
module Coercer
module BiometricStatusReports
def self.coerce(values)
return unless values.is_a?(Array)
return values if values.all? { |value| value.is_a?(WebAuthn::Metadata::BiometricStatusReport) }

values.map { |value| WebAuthn::Metadata::BiometricStatusReport.from_json(value) }
end
end
end
end
end
15 changes: 15 additions & 0 deletions lib/webauthn/metadata/coercer/date.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module WebAuthn
module Metadata
module Coercer
module Date
def self.coerce(value)
return value if value.is_a?(::Date)

::Date.iso8601(value) if value
end
end
end
end
end
18 changes: 18 additions & 0 deletions lib/webauthn/metadata/coercer/entries.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

require "webauthn/metadata/entry"

module WebAuthn
module Metadata
module Coercer
module Entries
def self.coerce(values)
return unless values.is_a?(Array)
return values if values.all? { |value| value.is_a?(WebAuthn::Metadata::Entry) }

values.map { |value| WebAuthn::Metadata::Entry.from_json(value) }
end
end
end
end
end
19 changes: 19 additions & 0 deletions lib/webauthn/metadata/coercer/escaped_uri.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

require "uri"

module WebAuthn
module Metadata
module Coercer
module EscapedURI
# The character # is a reserved character and not allowed in URLs, it is replaced by its hex value %x23.
# https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-metadata-service-v2.0-rd-20180702.html#idl-def-MetadataTOCPayloadEntry
def self.coerce(value)
return value if value.is_a?(URI)

URI(value.gsub(/%x23/, '#')) if value
end
end
end
end
end
18 changes: 18 additions & 0 deletions lib/webauthn/metadata/coercer/status_reports.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

require "webauthn/metadata/status_report"

module WebAuthn
module Metadata
module Coercer
module StatusReports
def self.coerce(values)
return unless values.is_a?(Array)
return values if values.all? { |value| value.is_a?(WebAuthn::Metadata::StatusReport) }

values.map { |value| WebAuthn::Metadata::StatusReport.from_json(value) }
end
end
end
end
end
26 changes: 26 additions & 0 deletions lib/webauthn/metadata/entry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require "webauthn/metadata/attributes"
require "webauthn/metadata/coercer/biometric_status_reports"
require "webauthn/metadata/coercer/date"
require "webauthn/metadata/coercer/escaped_uri"
require "webauthn/metadata/coercer/status_reports"

module WebAuthn
module Metadata
class Entry
extend Attributes

json_accessor("aaid")
json_accessor("aaguid")
json_accessor("attestationCertificateKeyIdentifiers")
json_accessor("hash")
json_accessor("url", Coercer::EscapedURI)
json_accessor("biometricStatusReports", Coercer::BiometricStatusReports)
json_accessor("statusReports", Coercer::StatusReports)
json_accessor("timeOfLastStatusChange", Coercer::Date)
json_accessor("rogueListURL", Coercer::EscapedURI)
json_accessor("rogueListHash")
end
end
end
22 changes: 22 additions & 0 deletions lib/webauthn/metadata/status_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

require "webauthn/metadata/attributes"
require "webauthn/metadata/coercer/date"
require "webauthn/metadata/coercer/escaped_uri"

module WebAuthn
module Metadata
class StatusReport
extend Attributes

json_accessor("status")
json_accessor("effectiveDate", Coercer::Date)
json_accessor("certificate")
json_accessor("url", Coercer::EscapedURI)
json_accessor("certificationDescriptor")
json_accessor("certificateNumber")
json_accessor("certificationPolicyVersion")
json_accessor("certificationRequirementsVersion")
end
end
end
18 changes: 18 additions & 0 deletions lib/webauthn/metadata/table_of_contents.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

require "webauthn/metadata/attributes"
require "webauthn/metadata/coercer/date"
require "webauthn/metadata/coercer/entries"

module WebAuthn
module Metadata
class TableOfContents
extend Attributes

json_accessor("legalHeader")
json_accessor("nextUpdate", Coercer::Date)
json_accessor("entries", Coercer::Entries)
json_accessor("no")
end
end
end
56 changes: 56 additions & 0 deletions spec/webauthn/metadata/attributes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

require "spec_helper"
require "webauthn/metadata/attributes"

RSpec.describe WebAuthn::Metadata::Attributes do
subject do
double = Class.new(Object)
double.extend WebAuthn::Metadata::Attributes
double
end

context ".json_accessor" do
it "generates snake cased accessor methods for camel cased keys" do
subject.public_send(:json_accessor, "fooBarBaz")

expect(subject.new).to respond_to(:foo_bar_baz, :foo_bar_baz=)
end

it "keeps accessor methods snake cased if they already were so" do
subject.public_send(:json_accessor, "snake_case")

expect(subject.new).to respond_to(:snake_case, :snake_case=)
end

it "generates a setter method that sends 'coerce' to the specified class or module" do
coercer = instance_spy("Coercer")
subject.public_send(:json_accessor, "quxQuz", coercer)

instance = subject.new
instance.qux_quz = "testing"

expect(coercer).to have_received(:coerce).with("testing")
end
end

context ".from_json" do
before(:each) { subject.public_send(:json_accessor, "fooBar") }

it "sends messages to snake cased setter methods from camel case keyed hashes" do
instance = subject.from_json({"fooBar" => 123})

expect(instance).to have_attributes(foo_bar: 123)
end

it "sends messages to snake cased setter methods from snake case keyed hashes" do
instance = subject.from_json({"foo_bar" => 123})

expect(instance).to have_attributes(foo_bar: 123)
end

it "does not send messages if the instance does not respond to it" do
subject.from_json({"shouldBeSafe" => 123})
end
end
end
32 changes: 32 additions & 0 deletions spec/webauthn/metadata/coercer/biometric_status_reports_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require "spec_helper"
require "webauthn/metadata/coercer/biometric_status_reports"

RSpec.describe WebAuthn::Metadata::Coercer::BiometricStatusReports do
subject { described_class.coerce(value) }

context "when the value is an array of BiometricStatusReport" do
let(:value) { [WebAuthn::Metadata::BiometricStatusReport.new] }

it "returns the same value" do
expect(subject).to eq(value)
end
end

context "when the value is nil" do
let(:value) { nil }

specify do
expect(subject).to be_nil
end
end

context "when the value is an array of Hash" do
let(:value) { [{ "certLevel" => 1, "modality" => 2 }] }

it "returns a BiometricStatusReport" do
expect(subject).to all(be_a(WebAuthn::Metadata::BiometricStatusReport))
end
end
end
42 changes: 42 additions & 0 deletions spec/webauthn/metadata/coercer/date_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

require "spec_helper"
require "webauthn/metadata/coercer/date"

RSpec.describe WebAuthn::Metadata::Coercer::Date do
subject { described_class.coerce(value) }

context "when the value is a Date" do
let(:value) { Date.new(2019, 5, 20) }

it "returns the same value" do
expect(subject).to eq(value)
end
end

context "when the value is nil" do
let(:value) { nil }

specify do
expect(subject).to be_nil
end
end

context "when the value is a String" do
context "containing a valid ISO-8601 value" do
let(:value) { "2019-05-20" }

specify do
expect(subject).to be_a(Date)
end
end

context "not containing an invalid ISO-8601 value" do
let(:value) { "2019-20-05" }

specify do
expect { subject }.to raise_error(ArgumentError)
end
end
end
end
32 changes: 32 additions & 0 deletions spec/webauthn/metadata/coercer/entries_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require "spec_helper"
require "webauthn/metadata/coercer/entries"

RSpec.describe WebAuthn::Metadata::Coercer::Entries do
subject { described_class.coerce(value) }

context "when the value is an array of Entry" do
let(:value) { [WebAuthn::Metadata::Entry.new] }

it "returns the same value" do
expect(subject).to eq(value)
end
end

context "when the value is nil" do
let(:value) { nil }

specify do
expect(subject).to be_nil
end
end

context "when the value is an array of Hash" do
let(:value) { [{ "aaguid" => "1234", "hash" => "abcde" }] }

it "returns a BiometricStatusReport" do
expect(subject).to all(be_a(WebAuthn::Metadata::Entry))
end
end
end
Loading

0 comments on commit 5851044

Please sign in to comment.