API-Docker
view release on metacpan or search on metacpan
lib/API/Docker/Role/RegistryAuth.pm view on Meta::CPAN
unless ($auth =~ /^\s*\{/) {
my $b64 = $auth;
$b64 =~ tr{-_}{+/};
# decode_base64 tolerates missing padding, so a value that lost its '='
# somewhere still decodes here; the header side is where the pad matters.
$json = decode_base64($b64);
}
my $config = eval { decode_json($json) };
croak __PACKAGE__ . '->_registry_auth_config could not read auth as an '
. 'AuthConfig: ' . $@ unless ref $config eq 'HASH';
return $config;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
API::Docker::Role::RegistryAuth - AuthConfig encoding shared by the registry-facing endpoints
=head1 VERSION
version 0.004
=head1 SYNOPSIS
package API::Docker::API::Whatever;
use Moo;
with 'API::Docker::Role::RegistryAuth';
# The header form: X-Registry-Auth on a registry-facing request
my $header = $self->_registry_auth_header($opts{auth});
# The map-header form: X-Registry-Config on /build, hostname -> AuthConfig
my $cfg_header = $self->_registry_config_header($opts{registry_config});
# The body form: the same credentials as a plain HashRef
my $config = $self->_registry_auth_config($opts{auth});
=head1 DESCRIPTION
One AuthConfig, three carriers. The Docker Engine takes registry credentials
as a JSON object with the keys C<username>, C<password>, C<serveraddress>,
C<identitytoken> and C<email>, and moves it around in three shapes:
=over
=item * base64url-encoded in the C<X-Registry-Auth> request header, for
C<< POST /images/{name}/push >>, C<< POST /images/create >>,
C<< GET /distribution/{name}/json >> and the C</plugins> family
=item * as a base64url-encoded B<map> of registry hostname to AuthConfig in
the C<X-Registry-Config> request header, for C<< POST /build >> -- one build
may pull base images from several registries, so it carries a set of
credentials rather than one
=item * as the plain JSON request body of C<< POST /auth >>
=back
This role carries the conversion in both directions so every class that
speaks to a registry agrees on it, and so a caller can hand the same C<auth>
argument to any of them.
B<It carries the encoding, not the policy.> Whether a header is sent at all
differs per endpoint on purpose and stays with the endpoint:
L<API::Docker::API::Images/push> sends C<X-Registry-Auth> on B<every> push
because the engine rejects an image push without it, while an anonymous
plugin or distribution call sends B<no> header -- their routers decode the
header and discard the error, so an absent one is the anonymous case rather
than a failure.
=head2 The padding is not optional
The engine decodes C<X-Registry-Auth> with Go's C<base64.URLEncoding>, not
C<RawURLEncoding>, so the C<=> padding is required. Stripping it makes every
push fail with
C<< failed to parse "X-Registry-Auth" header ... unexpected EOF >> -- the
anonymous case included, where the payload C<{}> encodes to C<e30=>: three
characters and one pad.
=head1 METHODS
These are private and composed into the resource classes; they are documented
here because the shapes are one decision, not several.
C<_registry_auth_header($auth)> returns the padded base64url value for
C<X-Registry-Auth>. C<undef> gives the anonymous encoding C<e30=>, a HashRef
is JSON-encoded, and a string that already looks base64-encoded is passed
through -- but respelled into the URL-safe alphabet, so a value pre-encoded in
standard base64 (with C<+> or C</>) reaches the wire as the C<->/C<_> the
engine's C<base64.URLEncoding> decoder expects rather than failing there.
C<_registry_config_header($map)> is the same encoding for C<X-Registry-Config>
on C<< POST /build >>. It takes the same shapes, but the HashRef it JSON-encodes
is a B<map> of registry hostname to AuthConfig
(C<< { 'registry.example:5000' => { username => ..., password => ... } } >>),
not a single AuthConfig.
C<_registry_auth_config($auth)> returns the same credentials as a plain
HashRef for a JSON request body. C<undef> gives C<undef> -- whether that is
an error is the endpoint's call, not this role's. A HashRef is copied, a JSON
object is decoded, and a base64url string is decoded back through both
layers. Anything that does not read as an AuthConfig croaks.
=head1 SEE ALSO
=over
=item * L<API::Docker::API::Images> - C<push>, which always sends the header
=item * L<API::Docker::API::System> - C<auth>, which sends the body form
=item * L<API::Docker::API::Distribution> - registry manifest lookups
( run in 0.919 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )