Class: Shojiku::Lockdown

Inherits:
Object
  • Object
show all
Defined in:
lib/shojiku/lockdown.rb

Overview

The input ceiling an operator can declare, and the named signing providers that go with it.

Once signing is in the loop, template input is a security boundary: whoever controls the bytes controls what gets signed. A strict client therefore narrows where signable input may come from.

  • The bytes-first entrance (Client#generate_source) is refused, so every document this client signs came from the configured template root, with its containment rules.
  • An artifact this client did not render (Client#artifact) may not be signed — those bytes are the caller's, exactly like a bytes-first template.
  • Signing material must be a provider REGISTERED in configuration and named at the call site, so a key path never appears in request-handling code and the material is loaded by one object rather than rebuilt per request.

Verification is never restricted. Verifying bytes of unknown provenance is the entire point of verify, and a locked-down deployment is precisely the one that needs to check an archived document it did not produce.

Refusals raise UsageError rather than returning a failed Result: strict disables an ENTRANCE, so calling it is the program contradicting its own deployment's configuration — not a fact about a document — and a failed result is something if result.success? can swallow.

The six other SDKs mirror this with identical semantics. It is contract, not ecosystem idiom.

Instance Method Summary collapse

Constructor Details

#initialize(strict:, providers: nil) ⇒ Lockdown

Returns a new instance of Lockdown.



35
36
37
38
39
40
41
42
# File 'lib/shojiku/lockdown.rb', line 35

def initialize(strict:, providers: nil)
  @strict = strict
  # Registered under symbols whatever the caller wrote them as. A
  # configuration hash keyed by strings is the ordinary Ruby spelling,
  # and looking it up only by symbol would answer "no signing provider
  # named `invoice` is registered" for a provider named exactly that.
  @providers = (providers || {}).transform_keys(&:to_sym)
end

Instance Method Details

#provider!(provider) ⇒ Object

The provider to sign with.

A Symbol or String is a registered name, in strict mode and out of it — naming providers is good practice everywhere, and only the REFUSAL of the alternative is strict's. A provider object is accepted only when this client is not strict.

Raises:



78
79
80
81
82
83
84
85
# File 'lib/shojiku/lockdown.rb', line 78

def provider!(provider)
  return registered!(provider) if provider.is_a?(Symbol) || provider.is_a?(String)
  return provider unless @strict

  raise UsageError,
        "this client is strict: sign with the name of a provider registered in " \
        "configuration, not with a provider object."
end

#signable!(artifact) ⇒ Object

An artifact about to be signed. Only a document laid out from a template the ROOT resolved qualifies — bytes handed over whole, and bytes laid out from a caller's own template, are the same trust class here. That closes the gap a boolean "was it loaded" would leave open: an artifact from another client's bytes-first render is not this deployment's document either.

Raises:



63
64
65
66
67
68
69
70
# File 'lib/shojiku/lockdown.rb', line 63

def signable!(artifact)
  return unless @strict && artifact.origin != :rendered

  raise UsageError,
        "this client is strict: only a document rendered from its own template " \
        "root may be signed (this one is #{artifact.origin}). It can still be " \
        "verified."
end

#source_entrance!Object

The bytes-first entrance.

Raises:



49
50
51
52
53
54
55
# File 'lib/shojiku/lockdown.rb', line 49

def source_entrance!
  return unless @strict

  raise UsageError,
        "this client is strict: templates must come from the template root, so " \
        "`generate_source` is disabled. Use `generate(name, params)`."
end

#strict?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/shojiku/lockdown.rb', line 44

def strict?
  @strict
end