Remove "docker engine" subcommands
These subcommands were created to allow upgrading a Docker Community engine to Docker Enterprise, but never really took off. This patch removes the `docker engine` subcommands, as they added quite some complexity / additional code. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
191
vendor/github.com/docker/libtrust/LICENSE
generated
vendored
191
vendor/github.com/docker/libtrust/LICENSE
generated
vendored
@ -1,191 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2014 Docker, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
18
vendor/github.com/docker/libtrust/README.md
generated
vendored
18
vendor/github.com/docker/libtrust/README.md
generated
vendored
@ -1,18 +0,0 @@
|
||||
# libtrust
|
||||
|
||||
Libtrust is library for managing authentication and authorization using public key cryptography.
|
||||
|
||||
Authentication is handled using the identity attached to the public key.
|
||||
Libtrust provides multiple methods to prove possession of the private key associated with an identity.
|
||||
- TLS x509 certificates
|
||||
- Signature verification
|
||||
- Key Challenge
|
||||
|
||||
Authorization and access control is managed through a distributed trust graph.
|
||||
Trust servers are used as the authorities of the trust graph and allow caching portions of the graph for faster access.
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license.
|
||||
Docs released under Creative commons.
|
||||
|
||||
175
vendor/github.com/docker/libtrust/certificates.go
generated
vendored
175
vendor/github.com/docker/libtrust/certificates.go
generated
vendored
@ -1,175 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type certTemplateInfo struct {
|
||||
commonName string
|
||||
domains []string
|
||||
ipAddresses []net.IP
|
||||
isCA bool
|
||||
clientAuth bool
|
||||
serverAuth bool
|
||||
}
|
||||
|
||||
func generateCertTemplate(info *certTemplateInfo) *x509.Certificate {
|
||||
// Generate a certificate template which is valid from the past week to
|
||||
// 10 years from now. The usage of the certificate depends on the
|
||||
// specified fields in the given certTempInfo object.
|
||||
var (
|
||||
keyUsage x509.KeyUsage
|
||||
extKeyUsage []x509.ExtKeyUsage
|
||||
)
|
||||
|
||||
if info.isCA {
|
||||
keyUsage = x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
if info.clientAuth {
|
||||
extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageClientAuth)
|
||||
}
|
||||
|
||||
if info.serverAuth {
|
||||
extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageServerAuth)
|
||||
}
|
||||
|
||||
return &x509.Certificate{
|
||||
SerialNumber: big.NewInt(0),
|
||||
Subject: pkix.Name{
|
||||
CommonName: info.commonName,
|
||||
},
|
||||
NotBefore: time.Now().Add(-time.Hour * 24 * 7),
|
||||
NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10),
|
||||
DNSNames: info.domains,
|
||||
IPAddresses: info.ipAddresses,
|
||||
IsCA: info.isCA,
|
||||
KeyUsage: keyUsage,
|
||||
ExtKeyUsage: extKeyUsage,
|
||||
BasicConstraintsValid: info.isCA,
|
||||
}
|
||||
}
|
||||
|
||||
func generateCert(pub PublicKey, priv PrivateKey, subInfo, issInfo *certTemplateInfo) (cert *x509.Certificate, err error) {
|
||||
pubCertTemplate := generateCertTemplate(subInfo)
|
||||
privCertTemplate := generateCertTemplate(issInfo)
|
||||
|
||||
certDER, err := x509.CreateCertificate(
|
||||
rand.Reader, pubCertTemplate, privCertTemplate,
|
||||
pub.CryptoPublicKey(), priv.CryptoPrivateKey(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create certificate: %s", err)
|
||||
}
|
||||
|
||||
cert, err = x509.ParseCertificate(certDER)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GenerateSelfSignedServerCert creates a self-signed certificate for the
|
||||
// given key which is to be used for TLS servers with the given domains and
|
||||
// IP addresses.
|
||||
func GenerateSelfSignedServerCert(key PrivateKey, domains []string, ipAddresses []net.IP) (*x509.Certificate, error) {
|
||||
info := &certTemplateInfo{
|
||||
commonName: key.KeyID(),
|
||||
domains: domains,
|
||||
ipAddresses: ipAddresses,
|
||||
serverAuth: true,
|
||||
}
|
||||
|
||||
return generateCert(key.PublicKey(), key, info, info)
|
||||
}
|
||||
|
||||
// GenerateSelfSignedClientCert creates a self-signed certificate for the
|
||||
// given key which is to be used for TLS clients.
|
||||
func GenerateSelfSignedClientCert(key PrivateKey) (*x509.Certificate, error) {
|
||||
info := &certTemplateInfo{
|
||||
commonName: key.KeyID(),
|
||||
clientAuth: true,
|
||||
}
|
||||
|
||||
return generateCert(key.PublicKey(), key, info, info)
|
||||
}
|
||||
|
||||
// GenerateCACert creates a certificate which can be used as a trusted
|
||||
// certificate authority.
|
||||
func GenerateCACert(signer PrivateKey, trustedKey PublicKey) (*x509.Certificate, error) {
|
||||
subjectInfo := &certTemplateInfo{
|
||||
commonName: trustedKey.KeyID(),
|
||||
isCA: true,
|
||||
}
|
||||
issuerInfo := &certTemplateInfo{
|
||||
commonName: signer.KeyID(),
|
||||
}
|
||||
|
||||
return generateCert(trustedKey, signer, subjectInfo, issuerInfo)
|
||||
}
|
||||
|
||||
// GenerateCACertPool creates a certificate authority pool to be used for a
|
||||
// TLS configuration. Any self-signed certificates issued by the specified
|
||||
// trusted keys will be verified during a TLS handshake
|
||||
func GenerateCACertPool(signer PrivateKey, trustedKeys []PublicKey) (*x509.CertPool, error) {
|
||||
certPool := x509.NewCertPool()
|
||||
|
||||
for _, trustedKey := range trustedKeys {
|
||||
cert, err := GenerateCACert(signer, trustedKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate CA certificate: %s", err)
|
||||
}
|
||||
|
||||
certPool.AddCert(cert)
|
||||
}
|
||||
|
||||
return certPool, nil
|
||||
}
|
||||
|
||||
// LoadCertificateBundle loads certificates from the given file. The file should be pem encoded
|
||||
// containing one or more certificates. The expected pem type is "CERTIFICATE".
|
||||
func LoadCertificateBundle(filename string) ([]*x509.Certificate, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certificates := []*x509.Certificate{}
|
||||
var block *pem.Block
|
||||
block, b = pem.Decode(b)
|
||||
for ; block != nil; block, b = pem.Decode(b) {
|
||||
if block.Type == "CERTIFICATE" {
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certificates = append(certificates, cert)
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid pem block type: %s", block.Type)
|
||||
}
|
||||
}
|
||||
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
// LoadCertificatePool loads a CA pool from the given file. The file should be pem encoded
|
||||
// containing one or more certificates. The expected pem type is "CERTIFICATE".
|
||||
func LoadCertificatePool(filename string) (*x509.CertPool, error) {
|
||||
certs, err := LoadCertificateBundle(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pool := x509.NewCertPool()
|
||||
for _, cert := range certs {
|
||||
pool.AddCert(cert)
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
9
vendor/github.com/docker/libtrust/doc.go
generated
vendored
9
vendor/github.com/docker/libtrust/doc.go
generated
vendored
@ -1,9 +0,0 @@
|
||||
/*
|
||||
Package libtrust provides an interface for managing authentication and
|
||||
authorization using public key cryptography. Authentication is handled
|
||||
using the identity attached to the public key and verified through TLS
|
||||
x509 certificates, a key challenge, or signature. Authorization and
|
||||
access control is managed through a trust graph distributed between
|
||||
both remote trust servers and locally cached and managed data.
|
||||
*/
|
||||
package libtrust
|
||||
428
vendor/github.com/docker/libtrust/ec_key.go
generated
vendored
428
vendor/github.com/docker/libtrust/ec_key.go
generated
vendored
@ -1,428 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
/*
|
||||
* EC DSA PUBLIC KEY
|
||||
*/
|
||||
|
||||
// ecPublicKey implements a libtrust.PublicKey using elliptic curve digital
|
||||
// signature algorithms.
|
||||
type ecPublicKey struct {
|
||||
*ecdsa.PublicKey
|
||||
curveName string
|
||||
signatureAlgorithm *signatureAlgorithm
|
||||
extended map[string]interface{}
|
||||
}
|
||||
|
||||
func fromECPublicKey(cryptoPublicKey *ecdsa.PublicKey) (*ecPublicKey, error) {
|
||||
curve := cryptoPublicKey.Curve
|
||||
|
||||
switch {
|
||||
case curve == elliptic.P256():
|
||||
return &ecPublicKey{cryptoPublicKey, "P-256", es256, map[string]interface{}{}}, nil
|
||||
case curve == elliptic.P384():
|
||||
return &ecPublicKey{cryptoPublicKey, "P-384", es384, map[string]interface{}{}}, nil
|
||||
case curve == elliptic.P521():
|
||||
return &ecPublicKey{cryptoPublicKey, "P-521", es512, map[string]interface{}{}}, nil
|
||||
default:
|
||||
return nil, errors.New("unsupported elliptic curve")
|
||||
}
|
||||
}
|
||||
|
||||
// KeyType returns the key type for elliptic curve keys, i.e., "EC".
|
||||
func (k *ecPublicKey) KeyType() string {
|
||||
return "EC"
|
||||
}
|
||||
|
||||
// CurveName returns the elliptic curve identifier.
|
||||
// Possible values are "P-256", "P-384", and "P-521".
|
||||
func (k *ecPublicKey) CurveName() string {
|
||||
return k.curveName
|
||||
}
|
||||
|
||||
// KeyID returns a distinct identifier which is unique to this Public Key.
|
||||
func (k *ecPublicKey) KeyID() string {
|
||||
return keyIDFromCryptoKey(k)
|
||||
}
|
||||
|
||||
func (k *ecPublicKey) String() string {
|
||||
return fmt.Sprintf("EC Public Key <%s>", k.KeyID())
|
||||
}
|
||||
|
||||
// Verify verifyies the signature of the data in the io.Reader using this
|
||||
// PublicKey. The alg parameter should identify the digital signature
|
||||
// algorithm which was used to produce the signature and should be supported
|
||||
// by this public key. Returns a nil error if the signature is valid.
|
||||
func (k *ecPublicKey) Verify(data io.Reader, alg string, signature []byte) error {
|
||||
// For EC keys there is only one supported signature algorithm depending
|
||||
// on the curve parameters.
|
||||
if k.signatureAlgorithm.HeaderParam() != alg {
|
||||
return fmt.Errorf("unable to verify signature: EC Public Key with curve %q does not support signature algorithm %q", k.curveName, alg)
|
||||
}
|
||||
|
||||
// signature is the concatenation of (r, s), base64Url encoded.
|
||||
sigLength := len(signature)
|
||||
expectedOctetLength := 2 * ((k.Params().BitSize + 7) >> 3)
|
||||
if sigLength != expectedOctetLength {
|
||||
return fmt.Errorf("signature length is %d octets long, should be %d", sigLength, expectedOctetLength)
|
||||
}
|
||||
|
||||
rBytes, sBytes := signature[:sigLength/2], signature[sigLength/2:]
|
||||
r := new(big.Int).SetBytes(rBytes)
|
||||
s := new(big.Int).SetBytes(sBytes)
|
||||
|
||||
hasher := k.signatureAlgorithm.HashID().New()
|
||||
_, err := io.Copy(hasher, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading data to sign: %s", err)
|
||||
}
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
if !ecdsa.Verify(k.PublicKey, hash, r, s) {
|
||||
return errors.New("invalid signature")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CryptoPublicKey returns the internal object which can be used as a
|
||||
// crypto.PublicKey for use with other standard library operations. The type
|
||||
// is either *rsa.PublicKey or *ecdsa.PublicKey
|
||||
func (k *ecPublicKey) CryptoPublicKey() crypto.PublicKey {
|
||||
return k.PublicKey
|
||||
}
|
||||
|
||||
func (k *ecPublicKey) toMap() map[string]interface{} {
|
||||
jwk := make(map[string]interface{})
|
||||
for k, v := range k.extended {
|
||||
jwk[k] = v
|
||||
}
|
||||
jwk["kty"] = k.KeyType()
|
||||
jwk["kid"] = k.KeyID()
|
||||
jwk["crv"] = k.CurveName()
|
||||
|
||||
xBytes := k.X.Bytes()
|
||||
yBytes := k.Y.Bytes()
|
||||
octetLength := (k.Params().BitSize + 7) >> 3
|
||||
// MUST include leading zeros in the output so that x, y are each
|
||||
// *octetLength* bytes long.
|
||||
xBuf := make([]byte, octetLength-len(xBytes), octetLength)
|
||||
yBuf := make([]byte, octetLength-len(yBytes), octetLength)
|
||||
xBuf = append(xBuf, xBytes...)
|
||||
yBuf = append(yBuf, yBytes...)
|
||||
|
||||
jwk["x"] = joseBase64UrlEncode(xBuf)
|
||||
jwk["y"] = joseBase64UrlEncode(yBuf)
|
||||
|
||||
return jwk
|
||||
}
|
||||
|
||||
// MarshalJSON serializes this Public Key using the JWK JSON serialization format for
|
||||
// elliptic curve keys.
|
||||
func (k *ecPublicKey) MarshalJSON() (data []byte, err error) {
|
||||
return json.Marshal(k.toMap())
|
||||
}
|
||||
|
||||
// PEMBlock serializes this Public Key to DER-encoded PKIX format.
|
||||
func (k *ecPublicKey) PEMBlock() (*pem.Block, error) {
|
||||
derBytes, err := x509.MarshalPKIXPublicKey(k.PublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to serialize EC PublicKey to DER-encoded PKIX format: %s", err)
|
||||
}
|
||||
k.extended["kid"] = k.KeyID() // For display purposes.
|
||||
return createPemBlock("PUBLIC KEY", derBytes, k.extended)
|
||||
}
|
||||
|
||||
func (k *ecPublicKey) AddExtendedField(field string, value interface{}) {
|
||||
k.extended[field] = value
|
||||
}
|
||||
|
||||
func (k *ecPublicKey) GetExtendedField(field string) interface{} {
|
||||
v, ok := k.extended[field]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func ecPublicKeyFromMap(jwk map[string]interface{}) (*ecPublicKey, error) {
|
||||
// JWK key type (kty) has already been determined to be "EC".
|
||||
// Need to extract 'crv', 'x', 'y', and 'kid' and check for
|
||||
// consistency.
|
||||
|
||||
// Get the curve identifier value.
|
||||
crv, err := stringFromMap(jwk, "crv")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK EC Public Key curve identifier: %s", err)
|
||||
}
|
||||
|
||||
var (
|
||||
curve elliptic.Curve
|
||||
sigAlg *signatureAlgorithm
|
||||
)
|
||||
|
||||
switch {
|
||||
case crv == "P-256":
|
||||
curve = elliptic.P256()
|
||||
sigAlg = es256
|
||||
case crv == "P-384":
|
||||
curve = elliptic.P384()
|
||||
sigAlg = es384
|
||||
case crv == "P-521":
|
||||
curve = elliptic.P521()
|
||||
sigAlg = es512
|
||||
default:
|
||||
return nil, fmt.Errorf("JWK EC Public Key curve identifier not supported: %q\n", crv)
|
||||
}
|
||||
|
||||
// Get the X and Y coordinates for the public key point.
|
||||
xB64Url, err := stringFromMap(jwk, "x")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK EC Public Key x-coordinate: %s", err)
|
||||
}
|
||||
x, err := parseECCoordinate(xB64Url, curve)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK EC Public Key x-coordinate: %s", err)
|
||||
}
|
||||
|
||||
yB64Url, err := stringFromMap(jwk, "y")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK EC Public Key y-coordinate: %s", err)
|
||||
}
|
||||
y, err := parseECCoordinate(yB64Url, curve)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK EC Public Key y-coordinate: %s", err)
|
||||
}
|
||||
|
||||
key := &ecPublicKey{
|
||||
PublicKey: &ecdsa.PublicKey{Curve: curve, X: x, Y: y},
|
||||
curveName: crv, signatureAlgorithm: sigAlg,
|
||||
}
|
||||
|
||||
// Key ID is optional too, but if it exists, it should match the key.
|
||||
_, ok := jwk["kid"]
|
||||
if ok {
|
||||
kid, err := stringFromMap(jwk, "kid")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK EC Public Key ID: %s", err)
|
||||
}
|
||||
if kid != key.KeyID() {
|
||||
return nil, fmt.Errorf("JWK EC Public Key ID does not match: %s", kid)
|
||||
}
|
||||
}
|
||||
|
||||
key.extended = jwk
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* EC DSA PRIVATE KEY
|
||||
*/
|
||||
|
||||
// ecPrivateKey implements a JWK Private Key using elliptic curve digital signature
|
||||
// algorithms.
|
||||
type ecPrivateKey struct {
|
||||
ecPublicKey
|
||||
*ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
func fromECPrivateKey(cryptoPrivateKey *ecdsa.PrivateKey) (*ecPrivateKey, error) {
|
||||
publicKey, err := fromECPublicKey(&cryptoPrivateKey.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ecPrivateKey{*publicKey, cryptoPrivateKey}, nil
|
||||
}
|
||||
|
||||
// PublicKey returns the Public Key data associated with this Private Key.
|
||||
func (k *ecPrivateKey) PublicKey() PublicKey {
|
||||
return &k.ecPublicKey
|
||||
}
|
||||
|
||||
func (k *ecPrivateKey) String() string {
|
||||
return fmt.Sprintf("EC Private Key <%s>", k.KeyID())
|
||||
}
|
||||
|
||||
// Sign signs the data read from the io.Reader using a signature algorithm supported
|
||||
// by the elliptic curve private key. If the specified hashing algorithm is
|
||||
// supported by this key, that hash function is used to generate the signature
|
||||
// otherwise the the default hashing algorithm for this key is used. Returns
|
||||
// the signature and the name of the JWK signature algorithm used, e.g.,
|
||||
// "ES256", "ES384", "ES512".
|
||||
func (k *ecPrivateKey) Sign(data io.Reader, hashID crypto.Hash) (signature []byte, alg string, err error) {
|
||||
// Generate a signature of the data using the internal alg.
|
||||
// The given hashId is only a suggestion, and since EC keys only support
|
||||
// on signature/hash algorithm given the curve name, we disregard it for
|
||||
// the elliptic curve JWK signature implementation.
|
||||
hasher := k.signatureAlgorithm.HashID().New()
|
||||
_, err = io.Copy(hasher, data)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error reading data to sign: %s", err)
|
||||
}
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
r, s, err := ecdsa.Sign(rand.Reader, k.PrivateKey, hash)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error producing signature: %s", err)
|
||||
}
|
||||
rBytes, sBytes := r.Bytes(), s.Bytes()
|
||||
octetLength := (k.ecPublicKey.Params().BitSize + 7) >> 3
|
||||
// MUST include leading zeros in the output
|
||||
rBuf := make([]byte, octetLength-len(rBytes), octetLength)
|
||||
sBuf := make([]byte, octetLength-len(sBytes), octetLength)
|
||||
|
||||
rBuf = append(rBuf, rBytes...)
|
||||
sBuf = append(sBuf, sBytes...)
|
||||
|
||||
signature = append(rBuf, sBuf...)
|
||||
alg = k.signatureAlgorithm.HeaderParam()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CryptoPrivateKey returns the internal object which can be used as a
|
||||
// crypto.PublicKey for use with other standard library operations. The type
|
||||
// is either *rsa.PublicKey or *ecdsa.PublicKey
|
||||
func (k *ecPrivateKey) CryptoPrivateKey() crypto.PrivateKey {
|
||||
return k.PrivateKey
|
||||
}
|
||||
|
||||
func (k *ecPrivateKey) toMap() map[string]interface{} {
|
||||
jwk := k.ecPublicKey.toMap()
|
||||
|
||||
dBytes := k.D.Bytes()
|
||||
// The length of this octet string MUST be ceiling(log-base-2(n)/8)
|
||||
// octets (where n is the order of the curve). This is because the private
|
||||
// key d must be in the interval [1, n-1] so the bitlength of d should be
|
||||
// no larger than the bitlength of n-1. The easiest way to find the octet
|
||||
// length is to take bitlength(n-1), add 7 to force a carry, and shift this
|
||||
// bit sequence right by 3, which is essentially dividing by 8 and adding
|
||||
// 1 if there is any remainder. Thus, the private key value d should be
|
||||
// output to (bitlength(n-1)+7)>>3 octets.
|
||||
n := k.ecPublicKey.Params().N
|
||||
octetLength := (new(big.Int).Sub(n, big.NewInt(1)).BitLen() + 7) >> 3
|
||||
// Create a buffer with the necessary zero-padding.
|
||||
dBuf := make([]byte, octetLength-len(dBytes), octetLength)
|
||||
dBuf = append(dBuf, dBytes...)
|
||||
|
||||
jwk["d"] = joseBase64UrlEncode(dBuf)
|
||||
|
||||
return jwk
|
||||
}
|
||||
|
||||
// MarshalJSON serializes this Private Key using the JWK JSON serialization format for
|
||||
// elliptic curve keys.
|
||||
func (k *ecPrivateKey) MarshalJSON() (data []byte, err error) {
|
||||
return json.Marshal(k.toMap())
|
||||
}
|
||||
|
||||
// PEMBlock serializes this Private Key to DER-encoded PKIX format.
|
||||
func (k *ecPrivateKey) PEMBlock() (*pem.Block, error) {
|
||||
derBytes, err := x509.MarshalECPrivateKey(k.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to serialize EC PrivateKey to DER-encoded PKIX format: %s", err)
|
||||
}
|
||||
k.extended["keyID"] = k.KeyID() // For display purposes.
|
||||
return createPemBlock("EC PRIVATE KEY", derBytes, k.extended)
|
||||
}
|
||||
|
||||
func ecPrivateKeyFromMap(jwk map[string]interface{}) (*ecPrivateKey, error) {
|
||||
dB64Url, err := stringFromMap(jwk, "d")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK EC Private Key: %s", err)
|
||||
}
|
||||
|
||||
// JWK key type (kty) has already been determined to be "EC".
|
||||
// Need to extract the public key information, then extract the private
|
||||
// key value 'd'.
|
||||
publicKey, err := ecPublicKeyFromMap(jwk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d, err := parseECPrivateParam(dB64Url, publicKey.Curve)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK EC Private Key d-param: %s", err)
|
||||
}
|
||||
|
||||
key := &ecPrivateKey{
|
||||
ecPublicKey: *publicKey,
|
||||
PrivateKey: &ecdsa.PrivateKey{
|
||||
PublicKey: *publicKey.PublicKey,
|
||||
D: d,
|
||||
},
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Key Generation Functions.
|
||||
*/
|
||||
|
||||
func generateECPrivateKey(curve elliptic.Curve) (k *ecPrivateKey, err error) {
|
||||
k = new(ecPrivateKey)
|
||||
k.PrivateKey, err = ecdsa.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.ecPublicKey.PublicKey = &k.PrivateKey.PublicKey
|
||||
k.extended = make(map[string]interface{})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GenerateECP256PrivateKey generates a key pair using elliptic curve P-256.
|
||||
func GenerateECP256PrivateKey() (PrivateKey, error) {
|
||||
k, err := generateECPrivateKey(elliptic.P256())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating EC P-256 key: %s", err)
|
||||
}
|
||||
|
||||
k.curveName = "P-256"
|
||||
k.signatureAlgorithm = es256
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// GenerateECP384PrivateKey generates a key pair using elliptic curve P-384.
|
||||
func GenerateECP384PrivateKey() (PrivateKey, error) {
|
||||
k, err := generateECPrivateKey(elliptic.P384())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating EC P-384 key: %s", err)
|
||||
}
|
||||
|
||||
k.curveName = "P-384"
|
||||
k.signatureAlgorithm = es384
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// GenerateECP521PrivateKey generates aß key pair using elliptic curve P-521.
|
||||
func GenerateECP521PrivateKey() (PrivateKey, error) {
|
||||
k, err := generateECPrivateKey(elliptic.P521())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating EC P-521 key: %s", err)
|
||||
}
|
||||
|
||||
k.curveName = "P-521"
|
||||
k.signatureAlgorithm = es512
|
||||
|
||||
return k, nil
|
||||
}
|
||||
50
vendor/github.com/docker/libtrust/filter.go
generated
vendored
50
vendor/github.com/docker/libtrust/filter.go
generated
vendored
@ -1,50 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FilterByHosts filters the list of PublicKeys to only those which contain a
|
||||
// 'hosts' pattern which matches the given host. If *includeEmpty* is true,
|
||||
// then keys which do not specify any hosts are also returned.
|
||||
func FilterByHosts(keys []PublicKey, host string, includeEmpty bool) ([]PublicKey, error) {
|
||||
filtered := make([]PublicKey, 0, len(keys))
|
||||
|
||||
for _, pubKey := range keys {
|
||||
var hosts []string
|
||||
switch v := pubKey.GetExtendedField("hosts").(type) {
|
||||
case []string:
|
||||
hosts = v
|
||||
case []interface{}:
|
||||
for _, value := range v {
|
||||
h, ok := value.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
hosts = append(hosts, h)
|
||||
}
|
||||
}
|
||||
|
||||
if len(hosts) == 0 {
|
||||
if includeEmpty {
|
||||
filtered = append(filtered, pubKey)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if any hosts match pattern
|
||||
for _, hostPattern := range hosts {
|
||||
match, err := filepath.Match(hostPattern, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if match {
|
||||
filtered = append(filtered, pubKey)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, nil
|
||||
}
|
||||
56
vendor/github.com/docker/libtrust/hash.go
generated
vendored
56
vendor/github.com/docker/libtrust/hash.go
generated
vendored
@ -1,56 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
_ "crypto/sha256" // Registrer SHA224 and SHA256
|
||||
_ "crypto/sha512" // Registrer SHA384 and SHA512
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type signatureAlgorithm struct {
|
||||
algHeaderParam string
|
||||
hashID crypto.Hash
|
||||
}
|
||||
|
||||
func (h *signatureAlgorithm) HeaderParam() string {
|
||||
return h.algHeaderParam
|
||||
}
|
||||
|
||||
func (h *signatureAlgorithm) HashID() crypto.Hash {
|
||||
return h.hashID
|
||||
}
|
||||
|
||||
var (
|
||||
rs256 = &signatureAlgorithm{"RS256", crypto.SHA256}
|
||||
rs384 = &signatureAlgorithm{"RS384", crypto.SHA384}
|
||||
rs512 = &signatureAlgorithm{"RS512", crypto.SHA512}
|
||||
es256 = &signatureAlgorithm{"ES256", crypto.SHA256}
|
||||
es384 = &signatureAlgorithm{"ES384", crypto.SHA384}
|
||||
es512 = &signatureAlgorithm{"ES512", crypto.SHA512}
|
||||
)
|
||||
|
||||
func rsaSignatureAlgorithmByName(alg string) (*signatureAlgorithm, error) {
|
||||
switch {
|
||||
case alg == "RS256":
|
||||
return rs256, nil
|
||||
case alg == "RS384":
|
||||
return rs384, nil
|
||||
case alg == "RS512":
|
||||
return rs512, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("RSA Digital Signature Algorithm %q not supported", alg)
|
||||
}
|
||||
}
|
||||
|
||||
func rsaPKCS1v15SignatureAlgorithmForHashID(hashID crypto.Hash) *signatureAlgorithm {
|
||||
switch {
|
||||
case hashID == crypto.SHA512:
|
||||
return rs512
|
||||
case hashID == crypto.SHA384:
|
||||
return rs384
|
||||
case hashID == crypto.SHA256:
|
||||
fallthrough
|
||||
default:
|
||||
return rs256
|
||||
}
|
||||
}
|
||||
657
vendor/github.com/docker/libtrust/jsonsign.go
generated
vendored
657
vendor/github.com/docker/libtrust/jsonsign.go
generated
vendored
@ -1,657 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidSignContent is used when the content to be signed is invalid.
|
||||
ErrInvalidSignContent = errors.New("invalid sign content")
|
||||
|
||||
// ErrInvalidJSONContent is used when invalid json is encountered.
|
||||
ErrInvalidJSONContent = errors.New("invalid json content")
|
||||
|
||||
// ErrMissingSignatureKey is used when the specified signature key
|
||||
// does not exist in the JSON content.
|
||||
ErrMissingSignatureKey = errors.New("missing signature key")
|
||||
)
|
||||
|
||||
type jsHeader struct {
|
||||
JWK PublicKey `json:"jwk,omitempty"`
|
||||
Algorithm string `json:"alg"`
|
||||
Chain []string `json:"x5c,omitempty"`
|
||||
}
|
||||
|
||||
type jsSignature struct {
|
||||
Header jsHeader `json:"header"`
|
||||
Signature string `json:"signature"`
|
||||
Protected string `json:"protected,omitempty"`
|
||||
}
|
||||
|
||||
type jsSignaturesSorted []jsSignature
|
||||
|
||||
func (jsbkid jsSignaturesSorted) Swap(i, j int) { jsbkid[i], jsbkid[j] = jsbkid[j], jsbkid[i] }
|
||||
func (jsbkid jsSignaturesSorted) Len() int { return len(jsbkid) }
|
||||
|
||||
func (jsbkid jsSignaturesSorted) Less(i, j int) bool {
|
||||
ki, kj := jsbkid[i].Header.JWK.KeyID(), jsbkid[j].Header.JWK.KeyID()
|
||||
si, sj := jsbkid[i].Signature, jsbkid[j].Signature
|
||||
|
||||
if ki == kj {
|
||||
return si < sj
|
||||
}
|
||||
|
||||
return ki < kj
|
||||
}
|
||||
|
||||
type signKey struct {
|
||||
PrivateKey
|
||||
Chain []*x509.Certificate
|
||||
}
|
||||
|
||||
// JSONSignature represents a signature of a json object.
|
||||
type JSONSignature struct {
|
||||
payload string
|
||||
signatures []jsSignature
|
||||
indent string
|
||||
formatLength int
|
||||
formatTail []byte
|
||||
}
|
||||
|
||||
func newJSONSignature() *JSONSignature {
|
||||
return &JSONSignature{
|
||||
signatures: make([]jsSignature, 0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// Payload returns the encoded payload of the signature. This
|
||||
// payload should not be signed directly
|
||||
func (js *JSONSignature) Payload() ([]byte, error) {
|
||||
return joseBase64UrlDecode(js.payload)
|
||||
}
|
||||
|
||||
func (js *JSONSignature) protectedHeader() (string, error) {
|
||||
protected := map[string]interface{}{
|
||||
"formatLength": js.formatLength,
|
||||
"formatTail": joseBase64UrlEncode(js.formatTail),
|
||||
"time": time.Now().UTC().Format(time.RFC3339),
|
||||
}
|
||||
protectedBytes, err := json.Marshal(protected)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return joseBase64UrlEncode(protectedBytes), nil
|
||||
}
|
||||
|
||||
func (js *JSONSignature) signBytes(protectedHeader string) ([]byte, error) {
|
||||
buf := make([]byte, len(js.payload)+len(protectedHeader)+1)
|
||||
copy(buf, protectedHeader)
|
||||
buf[len(protectedHeader)] = '.'
|
||||
copy(buf[len(protectedHeader)+1:], js.payload)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Sign adds a signature using the given private key.
|
||||
func (js *JSONSignature) Sign(key PrivateKey) error {
|
||||
protected, err := js.protectedHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signBytes, err := js.signBytes(protected)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sigBytes, algorithm, err := key.Sign(bytes.NewReader(signBytes), crypto.SHA256)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
js.signatures = append(js.signatures, jsSignature{
|
||||
Header: jsHeader{
|
||||
JWK: key.PublicKey(),
|
||||
Algorithm: algorithm,
|
||||
},
|
||||
Signature: joseBase64UrlEncode(sigBytes),
|
||||
Protected: protected,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignWithChain adds a signature using the given private key
|
||||
// and setting the x509 chain. The public key of the first element
|
||||
// in the chain must be the public key corresponding with the sign key.
|
||||
func (js *JSONSignature) SignWithChain(key PrivateKey, chain []*x509.Certificate) error {
|
||||
// Ensure key.Chain[0] is public key for key
|
||||
//key.Chain.PublicKey
|
||||
//key.PublicKey().CryptoPublicKey()
|
||||
|
||||
// Verify chain
|
||||
protected, err := js.protectedHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signBytes, err := js.signBytes(protected)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sigBytes, algorithm, err := key.Sign(bytes.NewReader(signBytes), crypto.SHA256)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header := jsHeader{
|
||||
Chain: make([]string, len(chain)),
|
||||
Algorithm: algorithm,
|
||||
}
|
||||
|
||||
for i, cert := range chain {
|
||||
header.Chain[i] = base64.StdEncoding.EncodeToString(cert.Raw)
|
||||
}
|
||||
|
||||
js.signatures = append(js.signatures, jsSignature{
|
||||
Header: header,
|
||||
Signature: joseBase64UrlEncode(sigBytes),
|
||||
Protected: protected,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify verifies all the signatures and returns the list of
|
||||
// public keys used to sign. Any x509 chains are not checked.
|
||||
func (js *JSONSignature) Verify() ([]PublicKey, error) {
|
||||
keys := make([]PublicKey, len(js.signatures))
|
||||
for i, signature := range js.signatures {
|
||||
signBytes, err := js.signBytes(signature.Protected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var publicKey PublicKey
|
||||
if len(signature.Header.Chain) > 0 {
|
||||
certBytes, err := base64.StdEncoding.DecodeString(signature.Header.Chain[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publicKey, err = FromCryptoPublicKey(cert.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if signature.Header.JWK != nil {
|
||||
publicKey = signature.Header.JWK
|
||||
} else {
|
||||
return nil, errors.New("missing public key")
|
||||
}
|
||||
|
||||
sigBytes, err := joseBase64UrlDecode(signature.Signature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = publicKey.Verify(bytes.NewReader(signBytes), signature.Header.Algorithm, sigBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keys[i] = publicKey
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// VerifyChains verifies all the signatures and the chains associated
|
||||
// with each signature and returns the list of verified chains.
|
||||
// Signatures without an x509 chain are not checked.
|
||||
func (js *JSONSignature) VerifyChains(ca *x509.CertPool) ([][]*x509.Certificate, error) {
|
||||
chains := make([][]*x509.Certificate, 0, len(js.signatures))
|
||||
for _, signature := range js.signatures {
|
||||
signBytes, err := js.signBytes(signature.Protected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var publicKey PublicKey
|
||||
if len(signature.Header.Chain) > 0 {
|
||||
certBytes, err := base64.StdEncoding.DecodeString(signature.Header.Chain[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publicKey, err = FromCryptoPublicKey(cert.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
intermediates := x509.NewCertPool()
|
||||
if len(signature.Header.Chain) > 1 {
|
||||
intermediateChain := signature.Header.Chain[1:]
|
||||
for i := range intermediateChain {
|
||||
certBytes, err := base64.StdEncoding.DecodeString(intermediateChain[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
intermediate, err := x509.ParseCertificate(certBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
intermediates.AddCert(intermediate)
|
||||
}
|
||||
}
|
||||
|
||||
verifyOptions := x509.VerifyOptions{
|
||||
Intermediates: intermediates,
|
||||
Roots: ca,
|
||||
}
|
||||
|
||||
verifiedChains, err := cert.Verify(verifyOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chains = append(chains, verifiedChains...)
|
||||
|
||||
sigBytes, err := joseBase64UrlDecode(signature.Signature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = publicKey.Verify(bytes.NewReader(signBytes), signature.Header.Algorithm, sigBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return chains, nil
|
||||
}
|
||||
|
||||
// JWS returns JSON serialized JWS according to
|
||||
// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-7.2
|
||||
func (js *JSONSignature) JWS() ([]byte, error) {
|
||||
if len(js.signatures) == 0 {
|
||||
return nil, errors.New("missing signature")
|
||||
}
|
||||
|
||||
sort.Sort(jsSignaturesSorted(js.signatures))
|
||||
|
||||
jsonMap := map[string]interface{}{
|
||||
"payload": js.payload,
|
||||
"signatures": js.signatures,
|
||||
}
|
||||
|
||||
return json.MarshalIndent(jsonMap, "", " ")
|
||||
}
|
||||
|
||||
func notSpace(r rune) bool {
|
||||
return !unicode.IsSpace(r)
|
||||
}
|
||||
|
||||
func detectJSONIndent(jsonContent []byte) (indent string) {
|
||||
if len(jsonContent) > 2 && jsonContent[0] == '{' && jsonContent[1] == '\n' {
|
||||
quoteIndex := bytes.IndexRune(jsonContent[1:], '"')
|
||||
if quoteIndex > 0 {
|
||||
indent = string(jsonContent[2 : quoteIndex+1])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type jsParsedHeader struct {
|
||||
JWK json.RawMessage `json:"jwk"`
|
||||
Algorithm string `json:"alg"`
|
||||
Chain []string `json:"x5c"`
|
||||
}
|
||||
|
||||
type jsParsedSignature struct {
|
||||
Header jsParsedHeader `json:"header"`
|
||||
Signature string `json:"signature"`
|
||||
Protected string `json:"protected"`
|
||||
}
|
||||
|
||||
// ParseJWS parses a JWS serialized JSON object into a Json Signature.
|
||||
func ParseJWS(content []byte) (*JSONSignature, error) {
|
||||
type jsParsed struct {
|
||||
Payload string `json:"payload"`
|
||||
Signatures []jsParsedSignature `json:"signatures"`
|
||||
}
|
||||
parsed := &jsParsed{}
|
||||
err := json.Unmarshal(content, parsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(parsed.Signatures) == 0 {
|
||||
return nil, errors.New("missing signatures")
|
||||
}
|
||||
payload, err := joseBase64UrlDecode(parsed.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
js, err := NewJSONSignature(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
js.signatures = make([]jsSignature, len(parsed.Signatures))
|
||||
for i, signature := range parsed.Signatures {
|
||||
header := jsHeader{
|
||||
Algorithm: signature.Header.Algorithm,
|
||||
}
|
||||
if signature.Header.Chain != nil {
|
||||
header.Chain = signature.Header.Chain
|
||||
}
|
||||
if signature.Header.JWK != nil {
|
||||
publicKey, err := UnmarshalPublicKeyJWK([]byte(signature.Header.JWK))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header.JWK = publicKey
|
||||
}
|
||||
js.signatures[i] = jsSignature{
|
||||
Header: header,
|
||||
Signature: signature.Signature,
|
||||
Protected: signature.Protected,
|
||||
}
|
||||
}
|
||||
|
||||
return js, nil
|
||||
}
|
||||
|
||||
// NewJSONSignature returns a new unsigned JWS from a json byte array.
|
||||
// JSONSignature will need to be signed before serializing or storing.
|
||||
// Optionally, one or more signatures can be provided as byte buffers,
|
||||
// containing serialized JWS signatures, to assemble a fully signed JWS
|
||||
// package. It is the callers responsibility to ensure uniqueness of the
|
||||
// provided signatures.
|
||||
func NewJSONSignature(content []byte, signatures ...[]byte) (*JSONSignature, error) {
|
||||
var dataMap map[string]interface{}
|
||||
err := json.Unmarshal(content, &dataMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
js := newJSONSignature()
|
||||
js.indent = detectJSONIndent(content)
|
||||
|
||||
js.payload = joseBase64UrlEncode(content)
|
||||
|
||||
// Find trailing } and whitespace, put in protected header
|
||||
closeIndex := bytes.LastIndexFunc(content, notSpace)
|
||||
if content[closeIndex] != '}' {
|
||||
return nil, ErrInvalidJSONContent
|
||||
}
|
||||
lastRuneIndex := bytes.LastIndexFunc(content[:closeIndex], notSpace)
|
||||
if content[lastRuneIndex] == ',' {
|
||||
return nil, ErrInvalidJSONContent
|
||||
}
|
||||
js.formatLength = lastRuneIndex + 1
|
||||
js.formatTail = content[js.formatLength:]
|
||||
|
||||
if len(signatures) > 0 {
|
||||
for _, signature := range signatures {
|
||||
var parsedJSig jsParsedSignature
|
||||
|
||||
if err := json.Unmarshal(signature, &parsedJSig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(stevvooe): A lot of the code below is repeated in
|
||||
// ParseJWS. It will require more refactoring to fix that.
|
||||
jsig := jsSignature{
|
||||
Header: jsHeader{
|
||||
Algorithm: parsedJSig.Header.Algorithm,
|
||||
},
|
||||
Signature: parsedJSig.Signature,
|
||||
Protected: parsedJSig.Protected,
|
||||
}
|
||||
|
||||
if parsedJSig.Header.Chain != nil {
|
||||
jsig.Header.Chain = parsedJSig.Header.Chain
|
||||
}
|
||||
|
||||
if parsedJSig.Header.JWK != nil {
|
||||
publicKey, err := UnmarshalPublicKeyJWK([]byte(parsedJSig.Header.JWK))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsig.Header.JWK = publicKey
|
||||
}
|
||||
|
||||
js.signatures = append(js.signatures, jsig)
|
||||
}
|
||||
}
|
||||
|
||||
return js, nil
|
||||
}
|
||||
|
||||
// NewJSONSignatureFromMap returns a new unsigned JSONSignature from a map or
|
||||
// struct. JWS will need to be signed before serializing or storing.
|
||||
func NewJSONSignatureFromMap(content interface{}) (*JSONSignature, error) {
|
||||
switch content.(type) {
|
||||
case map[string]interface{}:
|
||||
case struct{}:
|
||||
default:
|
||||
return nil, errors.New("invalid data type")
|
||||
}
|
||||
|
||||
js := newJSONSignature()
|
||||
js.indent = " "
|
||||
|
||||
payload, err := json.MarshalIndent(content, "", js.indent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
js.payload = joseBase64UrlEncode(payload)
|
||||
|
||||
// Remove '\n}' from formatted section, put in protected header
|
||||
js.formatLength = len(payload) - 2
|
||||
js.formatTail = payload[js.formatLength:]
|
||||
|
||||
return js, nil
|
||||
}
|
||||
|
||||
func readIntFromMap(key string, m map[string]interface{}) (int, bool) {
|
||||
value, ok := m[key]
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
return v, true
|
||||
case float64:
|
||||
return int(v), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func readStringFromMap(key string, m map[string]interface{}) (v string, ok bool) {
|
||||
value, ok := m[key]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
v, ok = value.(string)
|
||||
return
|
||||
}
|
||||
|
||||
// ParsePrettySignature parses a formatted signature into a
|
||||
// JSON signature. If the signatures are missing the format information
|
||||
// an error is thrown. The formatted signature must be created by
|
||||
// the same method as format signature.
|
||||
func ParsePrettySignature(content []byte, signatureKey string) (*JSONSignature, error) {
|
||||
var contentMap map[string]json.RawMessage
|
||||
err := json.Unmarshal(content, &contentMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling content: %s", err)
|
||||
}
|
||||
sigMessage, ok := contentMap[signatureKey]
|
||||
if !ok {
|
||||
return nil, ErrMissingSignatureKey
|
||||
}
|
||||
|
||||
var signatureBlocks []jsParsedSignature
|
||||
err = json.Unmarshal([]byte(sigMessage), &signatureBlocks)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling signatures: %s", err)
|
||||
}
|
||||
|
||||
js := newJSONSignature()
|
||||
js.signatures = make([]jsSignature, len(signatureBlocks))
|
||||
|
||||
for i, signatureBlock := range signatureBlocks {
|
||||
protectedBytes, err := joseBase64UrlDecode(signatureBlock.Protected)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("base64 decode error: %s", err)
|
||||
}
|
||||
var protectedHeader map[string]interface{}
|
||||
err = json.Unmarshal(protectedBytes, &protectedHeader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling protected header: %s", err)
|
||||
}
|
||||
|
||||
formatLength, ok := readIntFromMap("formatLength", protectedHeader)
|
||||
if !ok {
|
||||
return nil, errors.New("missing formatted length")
|
||||
}
|
||||
encodedTail, ok := readStringFromMap("formatTail", protectedHeader)
|
||||
if !ok {
|
||||
return nil, errors.New("missing formatted tail")
|
||||
}
|
||||
formatTail, err := joseBase64UrlDecode(encodedTail)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("base64 decode error on tail: %s", err)
|
||||
}
|
||||
if js.formatLength == 0 {
|
||||
js.formatLength = formatLength
|
||||
} else if js.formatLength != formatLength {
|
||||
return nil, errors.New("conflicting format length")
|
||||
}
|
||||
if len(js.formatTail) == 0 {
|
||||
js.formatTail = formatTail
|
||||
} else if bytes.Compare(js.formatTail, formatTail) != 0 {
|
||||
return nil, errors.New("conflicting format tail")
|
||||
}
|
||||
|
||||
header := jsHeader{
|
||||
Algorithm: signatureBlock.Header.Algorithm,
|
||||
Chain: signatureBlock.Header.Chain,
|
||||
}
|
||||
if signatureBlock.Header.JWK != nil {
|
||||
publicKey, err := UnmarshalPublicKeyJWK([]byte(signatureBlock.Header.JWK))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling public key: %s", err)
|
||||
}
|
||||
header.JWK = publicKey
|
||||
}
|
||||
js.signatures[i] = jsSignature{
|
||||
Header: header,
|
||||
Signature: signatureBlock.Signature,
|
||||
Protected: signatureBlock.Protected,
|
||||
}
|
||||
}
|
||||
if js.formatLength > len(content) {
|
||||
return nil, errors.New("invalid format length")
|
||||
}
|
||||
formatted := make([]byte, js.formatLength+len(js.formatTail))
|
||||
copy(formatted, content[:js.formatLength])
|
||||
copy(formatted[js.formatLength:], js.formatTail)
|
||||
js.indent = detectJSONIndent(formatted)
|
||||
js.payload = joseBase64UrlEncode(formatted)
|
||||
|
||||
return js, nil
|
||||
}
|
||||
|
||||
// PrettySignature formats a json signature into an easy to read
|
||||
// single json serialized object.
|
||||
func (js *JSONSignature) PrettySignature(signatureKey string) ([]byte, error) {
|
||||
if len(js.signatures) == 0 {
|
||||
return nil, errors.New("no signatures")
|
||||
}
|
||||
payload, err := joseBase64UrlDecode(js.payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload = payload[:js.formatLength]
|
||||
|
||||
sort.Sort(jsSignaturesSorted(js.signatures))
|
||||
|
||||
var marshalled []byte
|
||||
var marshallErr error
|
||||
if js.indent != "" {
|
||||
marshalled, marshallErr = json.MarshalIndent(js.signatures, js.indent, js.indent)
|
||||
} else {
|
||||
marshalled, marshallErr = json.Marshal(js.signatures)
|
||||
}
|
||||
if marshallErr != nil {
|
||||
return nil, marshallErr
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(make([]byte, 0, len(payload)+len(marshalled)+34))
|
||||
buf.Write(payload)
|
||||
buf.WriteByte(',')
|
||||
if js.indent != "" {
|
||||
buf.WriteByte('\n')
|
||||
buf.WriteString(js.indent)
|
||||
buf.WriteByte('"')
|
||||
buf.WriteString(signatureKey)
|
||||
buf.WriteString("\": ")
|
||||
buf.Write(marshalled)
|
||||
buf.WriteByte('\n')
|
||||
} else {
|
||||
buf.WriteByte('"')
|
||||
buf.WriteString(signatureKey)
|
||||
buf.WriteString("\":")
|
||||
buf.Write(marshalled)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// Signatures provides the signatures on this JWS as opaque blobs, sorted by
|
||||
// keyID. These blobs can be stored and reassembled with payloads. Internally,
|
||||
// they are simply marshaled json web signatures but implementations should
|
||||
// not rely on this.
|
||||
func (js *JSONSignature) Signatures() ([][]byte, error) {
|
||||
sort.Sort(jsSignaturesSorted(js.signatures))
|
||||
|
||||
var sb [][]byte
|
||||
for _, jsig := range js.signatures {
|
||||
p, err := json.Marshal(jsig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sb = append(sb, p)
|
||||
}
|
||||
|
||||
return sb, nil
|
||||
}
|
||||
|
||||
// Merge combines the signatures from one or more other signatures into the
|
||||
// method receiver. If the payloads differ for any argument, an error will be
|
||||
// returned and the receiver will not be modified.
|
||||
func (js *JSONSignature) Merge(others ...*JSONSignature) error {
|
||||
merged := js.signatures
|
||||
for _, other := range others {
|
||||
if js.payload != other.payload {
|
||||
return fmt.Errorf("payloads differ from merge target")
|
||||
}
|
||||
merged = append(merged, other.signatures...)
|
||||
}
|
||||
|
||||
js.signatures = merged
|
||||
return nil
|
||||
}
|
||||
253
vendor/github.com/docker/libtrust/key.go
generated
vendored
253
vendor/github.com/docker/libtrust/key.go
generated
vendored
@ -1,253 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PublicKey is a generic interface for a Public Key.
|
||||
type PublicKey interface {
|
||||
// KeyType returns the key type for this key. For elliptic curve keys,
|
||||
// this value should be "EC". For RSA keys, this value should be "RSA".
|
||||
KeyType() string
|
||||
// KeyID returns a distinct identifier which is unique to this Public Key.
|
||||
// The format generated by this library is a base32 encoding of a 240 bit
|
||||
// hash of the public key data divided into 12 groups like so:
|
||||
// ABCD:EFGH:IJKL:MNOP:QRST:UVWX:YZ23:4567:ABCD:EFGH:IJKL:MNOP
|
||||
KeyID() string
|
||||
// Verify verifyies the signature of the data in the io.Reader using this
|
||||
// Public Key. The alg parameter should identify the digital signature
|
||||
// algorithm which was used to produce the signature and should be
|
||||
// supported by this public key. Returns a nil error if the signature
|
||||
// is valid.
|
||||
Verify(data io.Reader, alg string, signature []byte) error
|
||||
// CryptoPublicKey returns the internal object which can be used as a
|
||||
// crypto.PublicKey for use with other standard library operations. The type
|
||||
// is either *rsa.PublicKey or *ecdsa.PublicKey
|
||||
CryptoPublicKey() crypto.PublicKey
|
||||
// These public keys can be serialized to the standard JSON encoding for
|
||||
// JSON Web Keys. See section 6 of the IETF draft RFC for JOSE JSON Web
|
||||
// Algorithms.
|
||||
MarshalJSON() ([]byte, error)
|
||||
// These keys can also be serialized to the standard PEM encoding.
|
||||
PEMBlock() (*pem.Block, error)
|
||||
// The string representation of a key is its key type and ID.
|
||||
String() string
|
||||
AddExtendedField(string, interface{})
|
||||
GetExtendedField(string) interface{}
|
||||
}
|
||||
|
||||
// PrivateKey is a generic interface for a Private Key.
|
||||
type PrivateKey interface {
|
||||
// A PrivateKey contains all fields and methods of a PublicKey of the
|
||||
// same type. The MarshalJSON method also outputs the private key as a
|
||||
// JSON Web Key, and the PEMBlock method outputs the private key as a
|
||||
// PEM block.
|
||||
PublicKey
|
||||
// PublicKey returns the PublicKey associated with this PrivateKey.
|
||||
PublicKey() PublicKey
|
||||
// Sign signs the data read from the io.Reader using a signature algorithm
|
||||
// supported by the private key. If the specified hashing algorithm is
|
||||
// supported by this key, that hash function is used to generate the
|
||||
// signature otherwise the the default hashing algorithm for this key is
|
||||
// used. Returns the signature and identifier of the algorithm used.
|
||||
Sign(data io.Reader, hashID crypto.Hash) (signature []byte, alg string, err error)
|
||||
// CryptoPrivateKey returns the internal object which can be used as a
|
||||
// crypto.PublicKey for use with other standard library operations. The
|
||||
// type is either *rsa.PublicKey or *ecdsa.PublicKey
|
||||
CryptoPrivateKey() crypto.PrivateKey
|
||||
}
|
||||
|
||||
// FromCryptoPublicKey returns a libtrust PublicKey representation of the given
|
||||
// *ecdsa.PublicKey or *rsa.PublicKey. Returns a non-nil error when the given
|
||||
// key is of an unsupported type.
|
||||
func FromCryptoPublicKey(cryptoPublicKey crypto.PublicKey) (PublicKey, error) {
|
||||
switch cryptoPublicKey := cryptoPublicKey.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
return fromECPublicKey(cryptoPublicKey)
|
||||
case *rsa.PublicKey:
|
||||
return fromRSAPublicKey(cryptoPublicKey), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("public key type %T is not supported", cryptoPublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
// FromCryptoPrivateKey returns a libtrust PrivateKey representation of the given
|
||||
// *ecdsa.PrivateKey or *rsa.PrivateKey. Returns a non-nil error when the given
|
||||
// key is of an unsupported type.
|
||||
func FromCryptoPrivateKey(cryptoPrivateKey crypto.PrivateKey) (PrivateKey, error) {
|
||||
switch cryptoPrivateKey := cryptoPrivateKey.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
return fromECPrivateKey(cryptoPrivateKey)
|
||||
case *rsa.PrivateKey:
|
||||
return fromRSAPrivateKey(cryptoPrivateKey), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("private key type %T is not supported", cryptoPrivateKey)
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalPublicKeyPEM parses the PEM encoded data and returns a libtrust
|
||||
// PublicKey or an error if there is a problem with the encoding.
|
||||
func UnmarshalPublicKeyPEM(data []byte) (PublicKey, error) {
|
||||
pemBlock, _ := pem.Decode(data)
|
||||
if pemBlock == nil {
|
||||
return nil, errors.New("unable to find PEM encoded data")
|
||||
} else if pemBlock.Type != "PUBLIC KEY" {
|
||||
return nil, fmt.Errorf("unable to get PublicKey from PEM type: %s", pemBlock.Type)
|
||||
}
|
||||
|
||||
return pubKeyFromPEMBlock(pemBlock)
|
||||
}
|
||||
|
||||
// UnmarshalPublicKeyPEMBundle parses the PEM encoded data as a bundle of
|
||||
// PEM blocks appended one after the other and returns a slice of PublicKey
|
||||
// objects that it finds.
|
||||
func UnmarshalPublicKeyPEMBundle(data []byte) ([]PublicKey, error) {
|
||||
pubKeys := []PublicKey{}
|
||||
|
||||
for {
|
||||
var pemBlock *pem.Block
|
||||
pemBlock, data = pem.Decode(data)
|
||||
if pemBlock == nil {
|
||||
break
|
||||
} else if pemBlock.Type != "PUBLIC KEY" {
|
||||
return nil, fmt.Errorf("unable to get PublicKey from PEM type: %s", pemBlock.Type)
|
||||
}
|
||||
|
||||
pubKey, err := pubKeyFromPEMBlock(pemBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubKeys = append(pubKeys, pubKey)
|
||||
}
|
||||
|
||||
return pubKeys, nil
|
||||
}
|
||||
|
||||
// UnmarshalPrivateKeyPEM parses the PEM encoded data and returns a libtrust
|
||||
// PrivateKey or an error if there is a problem with the encoding.
|
||||
func UnmarshalPrivateKeyPEM(data []byte) (PrivateKey, error) {
|
||||
pemBlock, _ := pem.Decode(data)
|
||||
if pemBlock == nil {
|
||||
return nil, errors.New("unable to find PEM encoded data")
|
||||
}
|
||||
|
||||
var key PrivateKey
|
||||
|
||||
switch {
|
||||
case pemBlock.Type == "RSA PRIVATE KEY":
|
||||
rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode RSA Private Key PEM data: %s", err)
|
||||
}
|
||||
key = fromRSAPrivateKey(rsaPrivateKey)
|
||||
case pemBlock.Type == "EC PRIVATE KEY":
|
||||
ecPrivateKey, err := x509.ParseECPrivateKey(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode EC Private Key PEM data: %s", err)
|
||||
}
|
||||
key, err = fromECPrivateKey(ecPrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to get PrivateKey from PEM type: %s", pemBlock.Type)
|
||||
}
|
||||
|
||||
addPEMHeadersToKey(pemBlock, key.PublicKey())
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// UnmarshalPublicKeyJWK unmarshals the given JSON Web Key into a generic
|
||||
// Public Key to be used with libtrust.
|
||||
func UnmarshalPublicKeyJWK(data []byte) (PublicKey, error) {
|
||||
jwk := make(map[string]interface{})
|
||||
|
||||
err := json.Unmarshal(data, &jwk)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"decoding JWK Public Key JSON data: %s\n", err,
|
||||
)
|
||||
}
|
||||
|
||||
// Get the Key Type value.
|
||||
kty, err := stringFromMap(jwk, "kty")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK Public Key type: %s", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case kty == "EC":
|
||||
// Call out to unmarshal EC public key.
|
||||
return ecPublicKeyFromMap(jwk)
|
||||
case kty == "RSA":
|
||||
// Call out to unmarshal RSA public key.
|
||||
return rsaPublicKeyFromMap(jwk)
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"JWK Public Key type not supported: %q\n", kty,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalPublicKeyJWKSet parses the JSON encoded data as a JSON Web Key Set
|
||||
// and returns a slice of Public Key objects.
|
||||
func UnmarshalPublicKeyJWKSet(data []byte) ([]PublicKey, error) {
|
||||
rawKeys, err := loadJSONKeySetRaw(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubKeys := make([]PublicKey, 0, len(rawKeys))
|
||||
|
||||
for _, rawKey := range rawKeys {
|
||||
pubKey, err := UnmarshalPublicKeyJWK(rawKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKeys = append(pubKeys, pubKey)
|
||||
}
|
||||
|
||||
return pubKeys, nil
|
||||
}
|
||||
|
||||
// UnmarshalPrivateKeyJWK unmarshals the given JSON Web Key into a generic
|
||||
// Private Key to be used with libtrust.
|
||||
func UnmarshalPrivateKeyJWK(data []byte) (PrivateKey, error) {
|
||||
jwk := make(map[string]interface{})
|
||||
|
||||
err := json.Unmarshal(data, &jwk)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"decoding JWK Private Key JSON data: %s\n", err,
|
||||
)
|
||||
}
|
||||
|
||||
// Get the Key Type value.
|
||||
kty, err := stringFromMap(jwk, "kty")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK Private Key type: %s", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case kty == "EC":
|
||||
// Call out to unmarshal EC private key.
|
||||
return ecPrivateKeyFromMap(jwk)
|
||||
case kty == "RSA":
|
||||
// Call out to unmarshal RSA private key.
|
||||
return rsaPrivateKeyFromMap(jwk)
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"JWK Private Key type not supported: %q\n", kty,
|
||||
)
|
||||
}
|
||||
}
|
||||
255
vendor/github.com/docker/libtrust/key_files.go
generated
vendored
255
vendor/github.com/docker/libtrust/key_files.go
generated
vendored
@ -1,255 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrKeyFileDoesNotExist indicates that the private key file does not exist.
|
||||
ErrKeyFileDoesNotExist = errors.New("key file does not exist")
|
||||
)
|
||||
|
||||
func readKeyFileBytes(filename string) ([]byte, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = ErrKeyFileDoesNotExist
|
||||
} else {
|
||||
err = fmt.Errorf("unable to read key file %s: %s", filename, err)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Loading and Saving of Public and Private Keys in either PEM or JWK format.
|
||||
*/
|
||||
|
||||
// LoadKeyFile opens the given filename and attempts to read a Private Key
|
||||
// encoded in either PEM or JWK format (if .json or .jwk file extension).
|
||||
func LoadKeyFile(filename string) (PrivateKey, error) {
|
||||
contents, err := readKeyFileBytes(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var key PrivateKey
|
||||
|
||||
if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
|
||||
key, err = UnmarshalPrivateKeyJWK(contents)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode private key JWK: %s", err)
|
||||
}
|
||||
} else {
|
||||
key, err = UnmarshalPrivateKeyPEM(contents)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode private key PEM: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// LoadPublicKeyFile opens the given filename and attempts to read a Public Key
|
||||
// encoded in either PEM or JWK format (if .json or .jwk file extension).
|
||||
func LoadPublicKeyFile(filename string) (PublicKey, error) {
|
||||
contents, err := readKeyFileBytes(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var key PublicKey
|
||||
|
||||
if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
|
||||
key, err = UnmarshalPublicKeyJWK(contents)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode public key JWK: %s", err)
|
||||
}
|
||||
} else {
|
||||
key, err = UnmarshalPublicKeyPEM(contents)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode public key PEM: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// SaveKey saves the given key to a file using the provided filename.
|
||||
// This process will overwrite any existing file at the provided location.
|
||||
func SaveKey(filename string, key PrivateKey) error {
|
||||
var encodedKey []byte
|
||||
var err error
|
||||
|
||||
if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
|
||||
// Encode in JSON Web Key format.
|
||||
encodedKey, err = json.MarshalIndent(key, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to encode private key JWK: %s", err)
|
||||
}
|
||||
} else {
|
||||
// Encode in PEM format.
|
||||
pemBlock, err := key.PEMBlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to encode private key PEM: %s", err)
|
||||
}
|
||||
encodedKey = pem.EncodeToMemory(pemBlock)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filename, encodedKey, os.FileMode(0600))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write private key file %s: %s", filename, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SavePublicKey saves the given public key to the file.
|
||||
func SavePublicKey(filename string, key PublicKey) error {
|
||||
var encodedKey []byte
|
||||
var err error
|
||||
|
||||
if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
|
||||
// Encode in JSON Web Key format.
|
||||
encodedKey, err = json.MarshalIndent(key, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to encode public key JWK: %s", err)
|
||||
}
|
||||
} else {
|
||||
// Encode in PEM format.
|
||||
pemBlock, err := key.PEMBlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to encode public key PEM: %s", err)
|
||||
}
|
||||
encodedKey = pem.EncodeToMemory(pemBlock)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filename, encodedKey, os.FileMode(0644))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write public key file %s: %s", filename, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Public Key Set files
|
||||
|
||||
type jwkSet struct {
|
||||
Keys []json.RawMessage `json:"keys"`
|
||||
}
|
||||
|
||||
// LoadKeySetFile loads a key set
|
||||
func LoadKeySetFile(filename string) ([]PublicKey, error) {
|
||||
if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
|
||||
return loadJSONKeySetFile(filename)
|
||||
}
|
||||
|
||||
// Must be a PEM format file
|
||||
return loadPEMKeySetFile(filename)
|
||||
}
|
||||
|
||||
func loadJSONKeySetRaw(data []byte) ([]json.RawMessage, error) {
|
||||
if len(data) == 0 {
|
||||
// This is okay, just return an empty slice.
|
||||
return []json.RawMessage{}, nil
|
||||
}
|
||||
|
||||
keySet := jwkSet{}
|
||||
|
||||
err := json.Unmarshal(data, &keySet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode JSON Web Key Set: %s", err)
|
||||
}
|
||||
|
||||
return keySet.Keys, nil
|
||||
}
|
||||
|
||||
func loadJSONKeySetFile(filename string) ([]PublicKey, error) {
|
||||
contents, err := readKeyFileBytes(filename)
|
||||
if err != nil && err != ErrKeyFileDoesNotExist {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return UnmarshalPublicKeyJWKSet(contents)
|
||||
}
|
||||
|
||||
func loadPEMKeySetFile(filename string) ([]PublicKey, error) {
|
||||
data, err := readKeyFileBytes(filename)
|
||||
if err != nil && err != ErrKeyFileDoesNotExist {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return UnmarshalPublicKeyPEMBundle(data)
|
||||
}
|
||||
|
||||
// AddKeySetFile adds a key to a key set
|
||||
func AddKeySetFile(filename string, key PublicKey) error {
|
||||
if strings.HasSuffix(filename, ".json") || strings.HasSuffix(filename, ".jwk") {
|
||||
return addKeySetJSONFile(filename, key)
|
||||
}
|
||||
|
||||
// Must be a PEM format file
|
||||
return addKeySetPEMFile(filename, key)
|
||||
}
|
||||
|
||||
func addKeySetJSONFile(filename string, key PublicKey) error {
|
||||
encodedKey, err := json.Marshal(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to encode trusted client key: %s", err)
|
||||
}
|
||||
|
||||
contents, err := readKeyFileBytes(filename)
|
||||
if err != nil && err != ErrKeyFileDoesNotExist {
|
||||
return err
|
||||
}
|
||||
|
||||
rawEntries, err := loadJSONKeySetRaw(contents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawEntries = append(rawEntries, json.RawMessage(encodedKey))
|
||||
entriesWrapper := jwkSet{Keys: rawEntries}
|
||||
|
||||
encodedEntries, err := json.MarshalIndent(entriesWrapper, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to encode trusted client keys: %s", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filename, encodedEntries, os.FileMode(0644))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write trusted client keys file %s: %s", filename, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func addKeySetPEMFile(filename string, key PublicKey) error {
|
||||
// Encode to PEM, open file for appending, write PEM.
|
||||
file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.FileMode(0644))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open trusted client keys file %s: %s", filename, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
pemBlock, err := key.PEMBlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to encoded trusted key: %s", err)
|
||||
}
|
||||
|
||||
_, err = file.Write(pem.EncodeToMemory(pemBlock))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write trusted keys file: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
175
vendor/github.com/docker/libtrust/key_manager.go
generated
vendored
175
vendor/github.com/docker/libtrust/key_manager.go
generated
vendored
@ -1,175 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ClientKeyManager manages client keys on the filesystem
|
||||
type ClientKeyManager struct {
|
||||
key PrivateKey
|
||||
clientFile string
|
||||
clientDir string
|
||||
|
||||
clientLock sync.RWMutex
|
||||
clients []PublicKey
|
||||
|
||||
configLock sync.Mutex
|
||||
configs []*tls.Config
|
||||
}
|
||||
|
||||
// NewClientKeyManager loads a new manager from a set of key files
|
||||
// and managed by the given private key.
|
||||
func NewClientKeyManager(trustKey PrivateKey, clientFile, clientDir string) (*ClientKeyManager, error) {
|
||||
m := &ClientKeyManager{
|
||||
key: trustKey,
|
||||
clientFile: clientFile,
|
||||
clientDir: clientDir,
|
||||
}
|
||||
if err := m.loadKeys(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO Start watching file and directory
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *ClientKeyManager) loadKeys() (err error) {
|
||||
// Load authorized keys file
|
||||
var clients []PublicKey
|
||||
if c.clientFile != "" {
|
||||
clients, err = LoadKeySetFile(c.clientFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load authorized keys: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add clients from authorized keys directory
|
||||
files, err := ioutil.ReadDir(c.clientDir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("unable to open authorized keys directory: %s", err)
|
||||
}
|
||||
for _, f := range files {
|
||||
if !f.IsDir() {
|
||||
publicKey, err := LoadPublicKeyFile(path.Join(c.clientDir, f.Name()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load authorized key file: %s", err)
|
||||
}
|
||||
clients = append(clients, publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
c.clientLock.Lock()
|
||||
c.clients = clients
|
||||
c.clientLock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterTLSConfig registers a tls configuration to manager
|
||||
// such that any changes to the keys may be reflected in
|
||||
// the tls client CA pool
|
||||
func (c *ClientKeyManager) RegisterTLSConfig(tlsConfig *tls.Config) error {
|
||||
c.clientLock.RLock()
|
||||
certPool, err := GenerateCACertPool(c.key, c.clients)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CA pool generation error: %s", err)
|
||||
}
|
||||
c.clientLock.RUnlock()
|
||||
|
||||
tlsConfig.ClientCAs = certPool
|
||||
|
||||
c.configLock.Lock()
|
||||
c.configs = append(c.configs, tlsConfig)
|
||||
c.configLock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewIdentityAuthTLSConfig creates a tls.Config for the server to use for
|
||||
// libtrust identity authentication for the domain specified
|
||||
func NewIdentityAuthTLSConfig(trustKey PrivateKey, clients *ClientKeyManager, addr string, domain string) (*tls.Config, error) {
|
||||
tlsConfig := newTLSConfig()
|
||||
|
||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
if err := clients.RegisterTLSConfig(tlsConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate cert
|
||||
ips, domains, err := parseAddr(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// add domain that it expects clients to use
|
||||
domains = append(domains, domain)
|
||||
x509Cert, err := GenerateSelfSignedServerCert(trustKey, domains, ips)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("certificate generation error: %s", err)
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{{
|
||||
Certificate: [][]byte{x509Cert.Raw},
|
||||
PrivateKey: trustKey.CryptoPrivateKey(),
|
||||
Leaf: x509Cert,
|
||||
}}
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
// NewCertAuthTLSConfig creates a tls.Config for the server to use for
|
||||
// certificate authentication
|
||||
func NewCertAuthTLSConfig(caPath, certPath, keyPath string) (*tls.Config, error) {
|
||||
tlsConfig := newTLSConfig()
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?", certPath, keyPath, err)
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
|
||||
// Verify client certificates against a CA?
|
||||
if caPath != "" {
|
||||
certPool := x509.NewCertPool()
|
||||
file, err := ioutil.ReadFile(caPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't read CA certificate: %s", err)
|
||||
}
|
||||
certPool.AppendCertsFromPEM(file)
|
||||
|
||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
tlsConfig.ClientCAs = certPool
|
||||
}
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
func newTLSConfig() *tls.Config {
|
||||
return &tls.Config{
|
||||
NextProtos: []string{"http/1.1"},
|
||||
// Avoid fallback on insecure SSL protocols
|
||||
MinVersion: tls.VersionTLS10,
|
||||
}
|
||||
}
|
||||
|
||||
// parseAddr parses an address into an array of IPs and domains
|
||||
func parseAddr(addr string) ([]net.IP, []string, error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var domains []string
|
||||
var ips []net.IP
|
||||
ip := net.ParseIP(host)
|
||||
if ip != nil {
|
||||
ips = []net.IP{ip}
|
||||
} else {
|
||||
domains = []string{host}
|
||||
}
|
||||
return ips, domains, nil
|
||||
}
|
||||
427
vendor/github.com/docker/libtrust/rsa_key.go
generated
vendored
427
vendor/github.com/docker/libtrust/rsa_key.go
generated
vendored
@ -1,427 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
/*
|
||||
* RSA DSA PUBLIC KEY
|
||||
*/
|
||||
|
||||
// rsaPublicKey implements a JWK Public Key using RSA digital signature algorithms.
|
||||
type rsaPublicKey struct {
|
||||
*rsa.PublicKey
|
||||
extended map[string]interface{}
|
||||
}
|
||||
|
||||
func fromRSAPublicKey(cryptoPublicKey *rsa.PublicKey) *rsaPublicKey {
|
||||
return &rsaPublicKey{cryptoPublicKey, map[string]interface{}{}}
|
||||
}
|
||||
|
||||
// KeyType returns the JWK key type for RSA keys, i.e., "RSA".
|
||||
func (k *rsaPublicKey) KeyType() string {
|
||||
return "RSA"
|
||||
}
|
||||
|
||||
// KeyID returns a distinct identifier which is unique to this Public Key.
|
||||
func (k *rsaPublicKey) KeyID() string {
|
||||
return keyIDFromCryptoKey(k)
|
||||
}
|
||||
|
||||
func (k *rsaPublicKey) String() string {
|
||||
return fmt.Sprintf("RSA Public Key <%s>", k.KeyID())
|
||||
}
|
||||
|
||||
// Verify verifyies the signature of the data in the io.Reader using this Public Key.
|
||||
// The alg parameter should be the name of the JWA digital signature algorithm
|
||||
// which was used to produce the signature and should be supported by this
|
||||
// public key. Returns a nil error if the signature is valid.
|
||||
func (k *rsaPublicKey) Verify(data io.Reader, alg string, signature []byte) error {
|
||||
// Verify the signature of the given date, return non-nil error if valid.
|
||||
sigAlg, err := rsaSignatureAlgorithmByName(alg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to verify Signature: %s", err)
|
||||
}
|
||||
|
||||
hasher := sigAlg.HashID().New()
|
||||
_, err = io.Copy(hasher, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading data to sign: %s", err)
|
||||
}
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
err = rsa.VerifyPKCS1v15(k.PublicKey, sigAlg.HashID(), hash, signature)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid %s signature: %s", sigAlg.HeaderParam(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CryptoPublicKey returns the internal object which can be used as a
|
||||
// crypto.PublicKey for use with other standard library operations. The type
|
||||
// is either *rsa.PublicKey or *ecdsa.PublicKey
|
||||
func (k *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
|
||||
return k.PublicKey
|
||||
}
|
||||
|
||||
func (k *rsaPublicKey) toMap() map[string]interface{} {
|
||||
jwk := make(map[string]interface{})
|
||||
for k, v := range k.extended {
|
||||
jwk[k] = v
|
||||
}
|
||||
jwk["kty"] = k.KeyType()
|
||||
jwk["kid"] = k.KeyID()
|
||||
jwk["n"] = joseBase64UrlEncode(k.N.Bytes())
|
||||
jwk["e"] = joseBase64UrlEncode(serializeRSAPublicExponentParam(k.E))
|
||||
|
||||
return jwk
|
||||
}
|
||||
|
||||
// MarshalJSON serializes this Public Key using the JWK JSON serialization format for
|
||||
// RSA keys.
|
||||
func (k *rsaPublicKey) MarshalJSON() (data []byte, err error) {
|
||||
return json.Marshal(k.toMap())
|
||||
}
|
||||
|
||||
// PEMBlock serializes this Public Key to DER-encoded PKIX format.
|
||||
func (k *rsaPublicKey) PEMBlock() (*pem.Block, error) {
|
||||
derBytes, err := x509.MarshalPKIXPublicKey(k.PublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to serialize RSA PublicKey to DER-encoded PKIX format: %s", err)
|
||||
}
|
||||
k.extended["kid"] = k.KeyID() // For display purposes.
|
||||
return createPemBlock("PUBLIC KEY", derBytes, k.extended)
|
||||
}
|
||||
|
||||
func (k *rsaPublicKey) AddExtendedField(field string, value interface{}) {
|
||||
k.extended[field] = value
|
||||
}
|
||||
|
||||
func (k *rsaPublicKey) GetExtendedField(field string) interface{} {
|
||||
v, ok := k.extended[field]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func rsaPublicKeyFromMap(jwk map[string]interface{}) (*rsaPublicKey, error) {
|
||||
// JWK key type (kty) has already been determined to be "RSA".
|
||||
// Need to extract 'n', 'e', and 'kid' and check for
|
||||
// consistency.
|
||||
|
||||
// Get the modulus parameter N.
|
||||
nB64Url, err := stringFromMap(jwk, "n")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Public Key modulus: %s", err)
|
||||
}
|
||||
|
||||
n, err := parseRSAModulusParam(nB64Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Public Key modulus: %s", err)
|
||||
}
|
||||
|
||||
// Get the public exponent E.
|
||||
eB64Url, err := stringFromMap(jwk, "e")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Public Key exponent: %s", err)
|
||||
}
|
||||
|
||||
e, err := parseRSAPublicExponentParam(eB64Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Public Key exponent: %s", err)
|
||||
}
|
||||
|
||||
key := &rsaPublicKey{
|
||||
PublicKey: &rsa.PublicKey{N: n, E: e},
|
||||
}
|
||||
|
||||
// Key ID is optional, but if it exists, it should match the key.
|
||||
_, ok := jwk["kid"]
|
||||
if ok {
|
||||
kid, err := stringFromMap(jwk, "kid")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Public Key ID: %s", err)
|
||||
}
|
||||
if kid != key.KeyID() {
|
||||
return nil, fmt.Errorf("JWK RSA Public Key ID does not match: %s", kid)
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := jwk["d"]; ok {
|
||||
return nil, fmt.Errorf("JWK RSA Public Key cannot contain private exponent")
|
||||
}
|
||||
|
||||
key.extended = jwk
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* RSA DSA PRIVATE KEY
|
||||
*/
|
||||
|
||||
// rsaPrivateKey implements a JWK Private Key using RSA digital signature algorithms.
|
||||
type rsaPrivateKey struct {
|
||||
rsaPublicKey
|
||||
*rsa.PrivateKey
|
||||
}
|
||||
|
||||
func fromRSAPrivateKey(cryptoPrivateKey *rsa.PrivateKey) *rsaPrivateKey {
|
||||
return &rsaPrivateKey{
|
||||
*fromRSAPublicKey(&cryptoPrivateKey.PublicKey),
|
||||
cryptoPrivateKey,
|
||||
}
|
||||
}
|
||||
|
||||
// PublicKey returns the Public Key data associated with this Private Key.
|
||||
func (k *rsaPrivateKey) PublicKey() PublicKey {
|
||||
return &k.rsaPublicKey
|
||||
}
|
||||
|
||||
func (k *rsaPrivateKey) String() string {
|
||||
return fmt.Sprintf("RSA Private Key <%s>", k.KeyID())
|
||||
}
|
||||
|
||||
// Sign signs the data read from the io.Reader using a signature algorithm supported
|
||||
// by the RSA private key. If the specified hashing algorithm is supported by
|
||||
// this key, that hash function is used to generate the signature otherwise the
|
||||
// the default hashing algorithm for this key is used. Returns the signature
|
||||
// and the name of the JWK signature algorithm used, e.g., "RS256", "RS384",
|
||||
// "RS512".
|
||||
func (k *rsaPrivateKey) Sign(data io.Reader, hashID crypto.Hash) (signature []byte, alg string, err error) {
|
||||
// Generate a signature of the data using the internal alg.
|
||||
sigAlg := rsaPKCS1v15SignatureAlgorithmForHashID(hashID)
|
||||
hasher := sigAlg.HashID().New()
|
||||
|
||||
_, err = io.Copy(hasher, data)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error reading data to sign: %s", err)
|
||||
}
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
signature, err = rsa.SignPKCS1v15(rand.Reader, k.PrivateKey, sigAlg.HashID(), hash)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error producing signature: %s", err)
|
||||
}
|
||||
|
||||
alg = sigAlg.HeaderParam()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CryptoPrivateKey returns the internal object which can be used as a
|
||||
// crypto.PublicKey for use with other standard library operations. The type
|
||||
// is either *rsa.PublicKey or *ecdsa.PublicKey
|
||||
func (k *rsaPrivateKey) CryptoPrivateKey() crypto.PrivateKey {
|
||||
return k.PrivateKey
|
||||
}
|
||||
|
||||
func (k *rsaPrivateKey) toMap() map[string]interface{} {
|
||||
k.Precompute() // Make sure the precomputed values are stored.
|
||||
jwk := k.rsaPublicKey.toMap()
|
||||
|
||||
jwk["d"] = joseBase64UrlEncode(k.D.Bytes())
|
||||
jwk["p"] = joseBase64UrlEncode(k.Primes[0].Bytes())
|
||||
jwk["q"] = joseBase64UrlEncode(k.Primes[1].Bytes())
|
||||
jwk["dp"] = joseBase64UrlEncode(k.Precomputed.Dp.Bytes())
|
||||
jwk["dq"] = joseBase64UrlEncode(k.Precomputed.Dq.Bytes())
|
||||
jwk["qi"] = joseBase64UrlEncode(k.Precomputed.Qinv.Bytes())
|
||||
|
||||
otherPrimes := k.Primes[2:]
|
||||
|
||||
if len(otherPrimes) > 0 {
|
||||
otherPrimesInfo := make([]interface{}, len(otherPrimes))
|
||||
for i, r := range otherPrimes {
|
||||
otherPrimeInfo := make(map[string]string, 3)
|
||||
otherPrimeInfo["r"] = joseBase64UrlEncode(r.Bytes())
|
||||
crtVal := k.Precomputed.CRTValues[i]
|
||||
otherPrimeInfo["d"] = joseBase64UrlEncode(crtVal.Exp.Bytes())
|
||||
otherPrimeInfo["t"] = joseBase64UrlEncode(crtVal.Coeff.Bytes())
|
||||
otherPrimesInfo[i] = otherPrimeInfo
|
||||
}
|
||||
jwk["oth"] = otherPrimesInfo
|
||||
}
|
||||
|
||||
return jwk
|
||||
}
|
||||
|
||||
// MarshalJSON serializes this Private Key using the JWK JSON serialization format for
|
||||
// RSA keys.
|
||||
func (k *rsaPrivateKey) MarshalJSON() (data []byte, err error) {
|
||||
return json.Marshal(k.toMap())
|
||||
}
|
||||
|
||||
// PEMBlock serializes this Private Key to DER-encoded PKIX format.
|
||||
func (k *rsaPrivateKey) PEMBlock() (*pem.Block, error) {
|
||||
derBytes := x509.MarshalPKCS1PrivateKey(k.PrivateKey)
|
||||
k.extended["keyID"] = k.KeyID() // For display purposes.
|
||||
return createPemBlock("RSA PRIVATE KEY", derBytes, k.extended)
|
||||
}
|
||||
|
||||
func rsaPrivateKeyFromMap(jwk map[string]interface{}) (*rsaPrivateKey, error) {
|
||||
// The JWA spec for RSA Private Keys (draft rfc section 5.3.2) states that
|
||||
// only the private key exponent 'd' is REQUIRED, the others are just for
|
||||
// signature/decryption optimizations and SHOULD be included when the JWK
|
||||
// is produced. We MAY choose to accept a JWK which only includes 'd', but
|
||||
// we're going to go ahead and not choose to accept it without the extra
|
||||
// fields. Only the 'oth' field will be optional (for multi-prime keys).
|
||||
privateExponent, err := parseRSAPrivateKeyParamFromMap(jwk, "d")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Private Key exponent: %s", err)
|
||||
}
|
||||
firstPrimeFactor, err := parseRSAPrivateKeyParamFromMap(jwk, "p")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Private Key prime factor: %s", err)
|
||||
}
|
||||
secondPrimeFactor, err := parseRSAPrivateKeyParamFromMap(jwk, "q")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Private Key prime factor: %s", err)
|
||||
}
|
||||
firstFactorCRT, err := parseRSAPrivateKeyParamFromMap(jwk, "dp")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Private Key CRT exponent: %s", err)
|
||||
}
|
||||
secondFactorCRT, err := parseRSAPrivateKeyParamFromMap(jwk, "dq")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Private Key CRT exponent: %s", err)
|
||||
}
|
||||
crtCoeff, err := parseRSAPrivateKeyParamFromMap(jwk, "qi")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Private Key CRT coefficient: %s", err)
|
||||
}
|
||||
|
||||
var oth interface{}
|
||||
if _, ok := jwk["oth"]; ok {
|
||||
oth = jwk["oth"]
|
||||
delete(jwk, "oth")
|
||||
}
|
||||
|
||||
// JWK key type (kty) has already been determined to be "RSA".
|
||||
// Need to extract the public key information, then extract the private
|
||||
// key values.
|
||||
publicKey, err := rsaPublicKeyFromMap(jwk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privateKey := &rsa.PrivateKey{
|
||||
PublicKey: *publicKey.PublicKey,
|
||||
D: privateExponent,
|
||||
Primes: []*big.Int{firstPrimeFactor, secondPrimeFactor},
|
||||
Precomputed: rsa.PrecomputedValues{
|
||||
Dp: firstFactorCRT,
|
||||
Dq: secondFactorCRT,
|
||||
Qinv: crtCoeff,
|
||||
},
|
||||
}
|
||||
|
||||
if oth != nil {
|
||||
// Should be an array of more JSON objects.
|
||||
otherPrimesInfo, ok := oth.([]interface{})
|
||||
if !ok {
|
||||
return nil, errors.New("JWK RSA Private Key: Invalid other primes info: must be an array")
|
||||
}
|
||||
numOtherPrimeFactors := len(otherPrimesInfo)
|
||||
if numOtherPrimeFactors == 0 {
|
||||
return nil, errors.New("JWK RSA Privake Key: Invalid other primes info: must be absent or non-empty")
|
||||
}
|
||||
otherPrimeFactors := make([]*big.Int, numOtherPrimeFactors)
|
||||
productOfPrimes := new(big.Int).Mul(firstPrimeFactor, secondPrimeFactor)
|
||||
crtValues := make([]rsa.CRTValue, numOtherPrimeFactors)
|
||||
|
||||
for i, val := range otherPrimesInfo {
|
||||
otherPrimeinfo, ok := val.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, errors.New("JWK RSA Private Key: Invalid other prime info: must be a JSON object")
|
||||
}
|
||||
|
||||
otherPrimeFactor, err := parseRSAPrivateKeyParamFromMap(otherPrimeinfo, "r")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Private Key prime factor: %s", err)
|
||||
}
|
||||
otherFactorCRT, err := parseRSAPrivateKeyParamFromMap(otherPrimeinfo, "d")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Private Key CRT exponent: %s", err)
|
||||
}
|
||||
otherCrtCoeff, err := parseRSAPrivateKeyParamFromMap(otherPrimeinfo, "t")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWK RSA Private Key CRT coefficient: %s", err)
|
||||
}
|
||||
|
||||
crtValue := crtValues[i]
|
||||
crtValue.Exp = otherFactorCRT
|
||||
crtValue.Coeff = otherCrtCoeff
|
||||
crtValue.R = productOfPrimes
|
||||
otherPrimeFactors[i] = otherPrimeFactor
|
||||
productOfPrimes = new(big.Int).Mul(productOfPrimes, otherPrimeFactor)
|
||||
}
|
||||
|
||||
privateKey.Primes = append(privateKey.Primes, otherPrimeFactors...)
|
||||
privateKey.Precomputed.CRTValues = crtValues
|
||||
}
|
||||
|
||||
key := &rsaPrivateKey{
|
||||
rsaPublicKey: *publicKey,
|
||||
PrivateKey: privateKey,
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Key Generation Functions.
|
||||
*/
|
||||
|
||||
func generateRSAPrivateKey(bits int) (k *rsaPrivateKey, err error) {
|
||||
k = new(rsaPrivateKey)
|
||||
k.PrivateKey, err = rsa.GenerateKey(rand.Reader, bits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.rsaPublicKey.PublicKey = &k.PrivateKey.PublicKey
|
||||
k.extended = make(map[string]interface{})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GenerateRSA2048PrivateKey generates a key pair using 2048-bit RSA.
|
||||
func GenerateRSA2048PrivateKey() (PrivateKey, error) {
|
||||
k, err := generateRSAPrivateKey(2048)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating RSA 2048-bit key: %s", err)
|
||||
}
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// GenerateRSA3072PrivateKey generates a key pair using 3072-bit RSA.
|
||||
func GenerateRSA3072PrivateKey() (PrivateKey, error) {
|
||||
k, err := generateRSAPrivateKey(3072)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating RSA 3072-bit key: %s", err)
|
||||
}
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// GenerateRSA4096PrivateKey generates a key pair using 4096-bit RSA.
|
||||
func GenerateRSA4096PrivateKey() (PrivateKey, error) {
|
||||
k, err := generateRSAPrivateKey(4096)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating RSA 4096-bit key: %s", err)
|
||||
}
|
||||
|
||||
return k, nil
|
||||
}
|
||||
363
vendor/github.com/docker/libtrust/util.go
generated
vendored
363
vendor/github.com/docker/libtrust/util.go
generated
vendored
@ -1,363 +0,0 @@
|
||||
package libtrust
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/elliptic"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LoadOrCreateTrustKey will load a PrivateKey from the specified path
|
||||
func LoadOrCreateTrustKey(trustKeyPath string) (PrivateKey, error) {
|
||||
if err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trustKey, err := LoadKeyFile(trustKeyPath)
|
||||
if err == ErrKeyFileDoesNotExist {
|
||||
trustKey, err = GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating key: %s", err)
|
||||
}
|
||||
|
||||
if err := SaveKey(trustKeyPath, trustKey); err != nil {
|
||||
return nil, fmt.Errorf("error saving key file: %s", err)
|
||||
}
|
||||
|
||||
dir, file := filepath.Split(trustKeyPath)
|
||||
if err := SavePublicKey(filepath.Join(dir, "public-"+file), trustKey.PublicKey()); err != nil {
|
||||
return nil, fmt.Errorf("error saving public key file: %s", err)
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("error loading key file: %s", err)
|
||||
}
|
||||
return trustKey, nil
|
||||
}
|
||||
|
||||
// NewIdentityAuthTLSClientConfig returns a tls.Config configured to use identity
|
||||
// based authentication from the specified dockerUrl, the rootConfigPath and
|
||||
// the server name to which it is connecting.
|
||||
// If trustUnknownHosts is true it will automatically add the host to the
|
||||
// known-hosts.json in rootConfigPath.
|
||||
func NewIdentityAuthTLSClientConfig(dockerUrl string, trustUnknownHosts bool, rootConfigPath string, serverName string) (*tls.Config, error) {
|
||||
tlsConfig := newTLSConfig()
|
||||
|
||||
trustKeyPath := filepath.Join(rootConfigPath, "key.json")
|
||||
knownHostsPath := filepath.Join(rootConfigPath, "known-hosts.json")
|
||||
|
||||
u, err := url.Parse(dockerUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse machine url")
|
||||
}
|
||||
|
||||
if u.Scheme == "unix" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
addr := u.Host
|
||||
proto := "tcp"
|
||||
|
||||
trustKey, err := LoadOrCreateTrustKey(trustKeyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load trust key: %s", err)
|
||||
}
|
||||
|
||||
knownHosts, err := LoadKeySetFile(knownHostsPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load trusted hosts file: %s", err)
|
||||
}
|
||||
|
||||
allowedHosts, err := FilterByHosts(knownHosts, addr, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error filtering hosts: %s", err)
|
||||
}
|
||||
|
||||
certPool, err := GenerateCACertPool(trustKey, allowedHosts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not create CA pool: %s", err)
|
||||
}
|
||||
|
||||
tlsConfig.ServerName = serverName
|
||||
tlsConfig.RootCAs = certPool
|
||||
|
||||
x509Cert, err := GenerateSelfSignedClientCert(trustKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("certificate generation error: %s", err)
|
||||
}
|
||||
|
||||
tlsConfig.Certificates = []tls.Certificate{{
|
||||
Certificate: [][]byte{x509Cert.Raw},
|
||||
PrivateKey: trustKey.CryptoPrivateKey(),
|
||||
Leaf: x509Cert,
|
||||
}}
|
||||
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
|
||||
testConn, err := tls.Dial(proto, addr, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tls Handshake error: %s", err)
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: tlsConfig.RootCAs,
|
||||
CurrentTime: time.Now(),
|
||||
DNSName: tlsConfig.ServerName,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
|
||||
certs := testConn.ConnectionState().PeerCertificates
|
||||
for i, cert := range certs {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
|
||||
if _, err := certs[0].Verify(opts); err != nil {
|
||||
if _, ok := err.(x509.UnknownAuthorityError); ok {
|
||||
if trustUnknownHosts {
|
||||
pubKey, err := FromCryptoPublicKey(certs[0].PublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error extracting public key from cert: %s", err)
|
||||
}
|
||||
|
||||
pubKey.AddExtendedField("hosts", []string{addr})
|
||||
|
||||
if err := AddKeySetFile(knownHostsPath, pubKey); err != nil {
|
||||
return nil, fmt.Errorf("error adding machine to known hosts: %s", err)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("unable to connect. unknown host: %s", addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testConn.Close()
|
||||
tlsConfig.InsecureSkipVerify = false
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
// joseBase64UrlEncode encodes the given data using the standard base64 url
|
||||
// encoding format but with all trailing '=' characters ommitted in accordance
|
||||
// with the jose specification.
|
||||
// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2
|
||||
func joseBase64UrlEncode(b []byte) string {
|
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
|
||||
}
|
||||
|
||||
// joseBase64UrlDecode decodes the given string using the standard base64 url
|
||||
// decoder but first adds the appropriate number of trailing '=' characters in
|
||||
// accordance with the jose specification.
|
||||
// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2
|
||||
func joseBase64UrlDecode(s string) ([]byte, error) {
|
||||
s = strings.Replace(s, "\n", "", -1)
|
||||
s = strings.Replace(s, " ", "", -1)
|
||||
switch len(s) % 4 {
|
||||
case 0:
|
||||
case 2:
|
||||
s += "=="
|
||||
case 3:
|
||||
s += "="
|
||||
default:
|
||||
return nil, errors.New("illegal base64url string")
|
||||
}
|
||||
return base64.URLEncoding.DecodeString(s)
|
||||
}
|
||||
|
||||
func keyIDEncode(b []byte) string {
|
||||
s := strings.TrimRight(base32.StdEncoding.EncodeToString(b), "=")
|
||||
var buf bytes.Buffer
|
||||
var i int
|
||||
for i = 0; i < len(s)/4-1; i++ {
|
||||
start := i * 4
|
||||
end := start + 4
|
||||
buf.WriteString(s[start:end] + ":")
|
||||
}
|
||||
buf.WriteString(s[i*4:])
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func keyIDFromCryptoKey(pubKey PublicKey) string {
|
||||
// Generate and return a 'libtrust' fingerprint of the public key.
|
||||
// For an RSA key this should be:
|
||||
// SHA256(DER encoded ASN1)
|
||||
// Then truncated to 240 bits and encoded into 12 base32 groups like so:
|
||||
// ABCD:EFGH:IJKL:MNOP:QRST:UVWX:YZ23:4567:ABCD:EFGH:IJKL:MNOP
|
||||
derBytes, err := x509.MarshalPKIXPublicKey(pubKey.CryptoPublicKey())
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
hasher := crypto.SHA256.New()
|
||||
hasher.Write(derBytes)
|
||||
return keyIDEncode(hasher.Sum(nil)[:30])
|
||||
}
|
||||
|
||||
func stringFromMap(m map[string]interface{}, key string) (string, error) {
|
||||
val, ok := m[key]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%q value not specified", key)
|
||||
}
|
||||
|
||||
str, ok := val.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%q value must be a string", key)
|
||||
}
|
||||
delete(m, key)
|
||||
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func parseECCoordinate(cB64Url string, curve elliptic.Curve) (*big.Int, error) {
|
||||
curveByteLen := (curve.Params().BitSize + 7) >> 3
|
||||
|
||||
cBytes, err := joseBase64UrlDecode(cB64Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid base64 URL encoding: %s", err)
|
||||
}
|
||||
cByteLength := len(cBytes)
|
||||
if cByteLength != curveByteLen {
|
||||
return nil, fmt.Errorf("invalid number of octets: got %d, should be %d", cByteLength, curveByteLen)
|
||||
}
|
||||
return new(big.Int).SetBytes(cBytes), nil
|
||||
}
|
||||
|
||||
func parseECPrivateParam(dB64Url string, curve elliptic.Curve) (*big.Int, error) {
|
||||
dBytes, err := joseBase64UrlDecode(dB64Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid base64 URL encoding: %s", err)
|
||||
}
|
||||
|
||||
// The length of this octet string MUST be ceiling(log-base-2(n)/8)
|
||||
// octets (where n is the order of the curve). This is because the private
|
||||
// key d must be in the interval [1, n-1] so the bitlength of d should be
|
||||
// no larger than the bitlength of n-1. The easiest way to find the octet
|
||||
// length is to take bitlength(n-1), add 7 to force a carry, and shift this
|
||||
// bit sequence right by 3, which is essentially dividing by 8 and adding
|
||||
// 1 if there is any remainder. Thus, the private key value d should be
|
||||
// output to (bitlength(n-1)+7)>>3 octets.
|
||||
n := curve.Params().N
|
||||
octetLength := (new(big.Int).Sub(n, big.NewInt(1)).BitLen() + 7) >> 3
|
||||
dByteLength := len(dBytes)
|
||||
|
||||
if dByteLength != octetLength {
|
||||
return nil, fmt.Errorf("invalid number of octets: got %d, should be %d", dByteLength, octetLength)
|
||||
}
|
||||
|
||||
return new(big.Int).SetBytes(dBytes), nil
|
||||
}
|
||||
|
||||
func parseRSAModulusParam(nB64Url string) (*big.Int, error) {
|
||||
nBytes, err := joseBase64UrlDecode(nB64Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid base64 URL encoding: %s", err)
|
||||
}
|
||||
|
||||
return new(big.Int).SetBytes(nBytes), nil
|
||||
}
|
||||
|
||||
func serializeRSAPublicExponentParam(e int) []byte {
|
||||
// We MUST use the minimum number of octets to represent E.
|
||||
// E is supposed to be 65537 for performance and security reasons
|
||||
// and is what golang's rsa package generates, but it might be
|
||||
// different if imported from some other generator.
|
||||
buf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buf, uint32(e))
|
||||
var i int
|
||||
for i = 0; i < 8; i++ {
|
||||
if buf[i] != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buf[i:]
|
||||
}
|
||||
|
||||
func parseRSAPublicExponentParam(eB64Url string) (int, error) {
|
||||
eBytes, err := joseBase64UrlDecode(eB64Url)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid base64 URL encoding: %s", err)
|
||||
}
|
||||
// Only the minimum number of bytes were used to represent E, but
|
||||
// binary.BigEndian.Uint32 expects at least 4 bytes, so we need
|
||||
// to add zero padding if necassary.
|
||||
byteLen := len(eBytes)
|
||||
buf := make([]byte, 4-byteLen, 4)
|
||||
eBytes = append(buf, eBytes...)
|
||||
|
||||
return int(binary.BigEndian.Uint32(eBytes)), nil
|
||||
}
|
||||
|
||||
func parseRSAPrivateKeyParamFromMap(m map[string]interface{}, key string) (*big.Int, error) {
|
||||
b64Url, err := stringFromMap(m, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paramBytes, err := joseBase64UrlDecode(b64Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invaled base64 URL encoding: %s", err)
|
||||
}
|
||||
|
||||
return new(big.Int).SetBytes(paramBytes), nil
|
||||
}
|
||||
|
||||
func createPemBlock(name string, derBytes []byte, headers map[string]interface{}) (*pem.Block, error) {
|
||||
pemBlock := &pem.Block{Type: name, Bytes: derBytes, Headers: map[string]string{}}
|
||||
for k, v := range headers {
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
pemBlock.Headers[k] = val
|
||||
case []string:
|
||||
if k == "hosts" {
|
||||
pemBlock.Headers[k] = strings.Join(val, ",")
|
||||
} else {
|
||||
// Return error, non-encodable type
|
||||
}
|
||||
default:
|
||||
// Return error, non-encodable type
|
||||
}
|
||||
}
|
||||
|
||||
return pemBlock, nil
|
||||
}
|
||||
|
||||
func pubKeyFromPEMBlock(pemBlock *pem.Block) (PublicKey, error) {
|
||||
cryptoPublicKey, err := x509.ParsePKIXPublicKey(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode Public Key PEM data: %s", err)
|
||||
}
|
||||
|
||||
pubKey, err := FromCryptoPublicKey(cryptoPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addPEMHeadersToKey(pemBlock, pubKey)
|
||||
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
func addPEMHeadersToKey(pemBlock *pem.Block, pubKey PublicKey) {
|
||||
for key, value := range pemBlock.Headers {
|
||||
var safeVal interface{}
|
||||
if key == "hosts" {
|
||||
safeVal = strings.Split(value, ",")
|
||||
} else {
|
||||
safeVal = value
|
||||
}
|
||||
pubKey.AddExtendedField(key, safeVal)
|
||||
}
|
||||
}
|
||||
191
vendor/github.com/docker/licensing/LICENSE
generated
vendored
191
vendor/github.com/docker/licensing/LICENSE
generated
vendored
@ -1,191 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2013-2017 Docker, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
90
vendor/github.com/docker/licensing/README.md
generated
vendored
90
vendor/github.com/docker/licensing/README.md
generated
vendored
@ -1,90 +0,0 @@
|
||||
docker/licensing
|
||||
=========
|
||||
|
||||
## Overview
|
||||
|
||||
*licensing* is a library for interacting with Docker issued product licenses. It facilitates user's authentication to the [Docker Hub](https://hub.docker.com), provides a mechanism for retrieving a user's existing docker-issued subscriptions/licenses, detects and verifies locally stored licenses, and can be used to provision trial licenses for [Docker Enterprise Edition](https://www.docker.com/enterprise-edition).
|
||||
|
||||
License
|
||||
=========
|
||||
docker/licensing is licensed under the Apache License, Version 2.0. See
|
||||
[LICENSE](https://github.com/docker/licensing/blob/master/LICENSE) for the full
|
||||
license text.
|
||||
|
||||
Usage
|
||||
========
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/licensing"
|
||||
"github.com/docker/licensing/model"
|
||||
)
|
||||
|
||||
const (
|
||||
hubURL = "https://hub.docker.com"
|
||||
pubKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0Ka2lkOiBKN0xEOjY3VlI6TDVIWjpVN0JBOjJPNEc6NEFMMzpPRjJOOkpIR0I6RUZUSDo1Q1ZROk1GRU86QUVJVAoKTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUF5ZEl5K2xVN283UGNlWSs0K3MrQwpRNU9FZ0N5RjhDeEljUUlXdUs4NHBJaVpjaVk2NzMweUNZbndMU0tUbHcrVTZVQy9RUmVXUmlvTU5ORTVEczVUCllFWGJHRzZvbG0ycWRXYkJ3Y0NnKzJVVUgvT2NCOVd1UDZnUlBIcE1GTXN4RHpXd3ZheThKVXVIZ1lVTFVwbTEKSXYrbXE3bHA1blEvUnhyVDBLWlJBUVRZTEVNRWZHd20zaE1PL2dlTFBTK2hnS1B0SUhsa2c2L1djb3hUR29LUAo3OWQvd2FIWXhHTmw3V2hTbmVpQlN4YnBiUUFLazIxbGc3OThYYjd2WnlFQVRETXJSUjlNZUU2QWRqNUhKcFkzCkNveVJBUENtYUtHUkNLNHVvWlNvSXUwaEZWbEtVUHliYncwMDBHTyt3YTJLTjhVd2dJSW0waTVJMXVXOUdrcTQKempCeTV6aGdxdVVYYkc5YldQQU9ZcnE1UWE4MUR4R2NCbEp5SFlBcCtERFBFOVRHZzR6WW1YakpueFpxSEVkdQpHcWRldlo4WE1JMHVrZmtHSUkxNHdVT2lNSUlJclhsRWNCZi80Nkk4Z1FXRHp4eWNaZS9KR1grTEF1YXlYcnlyClVGZWhWTlVkWlVsOXdYTmFKQitrYUNxejVRd2FSOTNzR3crUVNmdEQwTnZMZTdDeU9IK0U2dmc2U3QvTmVUdmcKdjhZbmhDaVhJbFo4SE9mSXdOZTd0RUYvVWN6NU9iUHlrbTN0eWxyTlVqdDBWeUFtdHRhY1ZJMmlHaWhjVVBybQprNGxWSVo3VkQvTFNXK2k3eW9TdXJ0cHNQWGNlMnBLRElvMzBsSkdoTy8zS1VtbDJTVVpDcXpKMXlFbUtweXNICjVIRFc5Y3NJRkNBM2RlQWpmWlV2TjdVQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo="
|
||||
username = "docker username"
|
||||
password = "your password"
|
||||
appFeature = "jump"
|
||||
)
|
||||
|
||||
func panicOnErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
hubURI, err := url.Parse(hubURL)
|
||||
panicOnErr(err)
|
||||
|
||||
// setup client
|
||||
c, err := licensing.New(&licensing.Config{
|
||||
BaseURI: *hubURI,
|
||||
HTTPClient: nil,
|
||||
PublicKeys: []string{pubKey},
|
||||
})
|
||||
panicOnErr(err)
|
||||
|
||||
// grab token
|
||||
ctx := context.Background()
|
||||
token, err := c.LoginViaAuth(ctx, username, password)
|
||||
panicOnErr(err)
|
||||
|
||||
// fetch dockerID, if not already known
|
||||
id, err := c.GetHubUserByName(ctx, username)
|
||||
panicOnErr(err)
|
||||
|
||||
subs, err := c.ListSubscriptions(ctx, token, id.ID)
|
||||
panicOnErr(err)
|
||||
|
||||
// find first available subscription with given feature
|
||||
var featuredSub *model.Subscription
|
||||
for _, sub := range subs {
|
||||
_, ok := sub.GetFeatureValue(appFeature)
|
||||
if ok {
|
||||
featuredSub = sub
|
||||
break
|
||||
}
|
||||
}
|
||||
if featuredSub == nil {
|
||||
fmt.Println("account has no subscriptions with the desired feature entitlements")
|
||||
return
|
||||
}
|
||||
|
||||
// download license file for this subscription
|
||||
subLic, err := c.DownloadLicenseFromHub(ctx, token, featuredSub.ID)
|
||||
panicOnErr(err)
|
||||
|
||||
// verify license is issued by corresponding keypair and is not expired
|
||||
licFile, err := c.VerifyLicense(ctx, *subLic)
|
||||
panicOnErr(err)
|
||||
|
||||
fmt.Println("license summary: ", c.SummarizeLicense(licFile))
|
||||
}
|
||||
```
|
||||
276
vendor/github.com/docker/licensing/client.go
generated
vendored
276
vendor/github.com/docker/licensing/client.go
generated
vendored
@ -1,276 +0,0 @@
|
||||
package licensing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/docker/licensing/lib/errors"
|
||||
"github.com/docker/licensing/lib/go-auth/jwt"
|
||||
"github.com/docker/licensing/lib/go-clientlib"
|
||||
"github.com/docker/licensing/model"
|
||||
)
|
||||
|
||||
const (
|
||||
trialProductID = "docker-ee-trial"
|
||||
trialRatePlanID = "free-trial"
|
||||
)
|
||||
|
||||
// Client represents the licensing package interface, including methods for authentication and interaction with Docker
|
||||
// licensing, accounts, and billing services
|
||||
type Client interface {
|
||||
LoginViaAuth(ctx context.Context, username, password string) (authToken string, err error)
|
||||
GetHubUserOrgs(ctx context.Context, authToken string) (orgs []model.Org, err error)
|
||||
GetHubUserByName(ctx context.Context, username string) (user *model.User, err error)
|
||||
VerifyLicense(ctx context.Context, license model.IssuedLicense) (res *model.CheckResponse, err error)
|
||||
GenerateNewTrialSubscription(ctx context.Context, authToken, dockerID string) (subscriptionID string, err error)
|
||||
ListSubscriptions(ctx context.Context, authToken, dockerID string) (response []*model.Subscription, err error)
|
||||
ListSubscriptionsDetails(ctx context.Context, authToken, dockerID string) (response []*model.SubscriptionDetail, err error)
|
||||
DownloadLicenseFromHub(ctx context.Context, authToken, subscriptionID string) (license *model.IssuedLicense, err error)
|
||||
ParseLicense(license []byte) (parsedLicense *model.IssuedLicense, err error)
|
||||
StoreLicense(ctx context.Context, dclnt WrappedDockerClient, licenses *model.IssuedLicense, localRootDir string) error
|
||||
LoadLocalLicense(ctx context.Context, dclnt WrappedDockerClient) (*model.Subscription, error)
|
||||
SummarizeLicense(res *model.CheckResponse) *model.Subscription
|
||||
}
|
||||
|
||||
func (c *client) LoginViaAuth(ctx context.Context, username, password string) (string, error) {
|
||||
creds, err := c.login(ctx, username, password)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, errors.Fields{
|
||||
"username": username,
|
||||
})
|
||||
}
|
||||
|
||||
return creds.Token, nil
|
||||
}
|
||||
|
||||
func (c *client) GetHubUserOrgs(ctx context.Context, authToken string) ([]model.Org, error) {
|
||||
ctx = jwt.NewContext(ctx, authToken)
|
||||
|
||||
orgs, err := c.getUserOrgs(ctx, model.PaginationParams{})
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "Failed to get orgs for user")
|
||||
}
|
||||
|
||||
return orgs, nil
|
||||
}
|
||||
|
||||
func (c *client) GetHubUserByName(ctx context.Context, username string) (*model.User, error) {
|
||||
user, err := c.getUserByName(ctx, username)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, errors.Fields{
|
||||
"username": username,
|
||||
})
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (c *client) VerifyLicense(ctx context.Context, license model.IssuedLicense) (*model.CheckResponse, error) {
|
||||
res, err := c.check(ctx, license)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "Failed to verify license")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *client) GenerateNewTrialSubscription(ctx context.Context, authToken, dockerID string) (string, error) {
|
||||
ctx = jwt.NewContext(ctx, authToken)
|
||||
|
||||
sub, err := c.createSubscription(ctx, &model.SubscriptionCreationRequest{
|
||||
Name: "Docker Enterprise Free Trial",
|
||||
DockerID: dockerID,
|
||||
ProductID: trialProductID,
|
||||
ProductRatePlan: trialRatePlanID,
|
||||
Eusa: &model.EusaState{
|
||||
Accepted: true,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, errors.Fields{
|
||||
"docker_id": dockerID,
|
||||
})
|
||||
}
|
||||
|
||||
return sub.ID, nil
|
||||
}
|
||||
|
||||
// ListSubscriptions returns basic descriptions of all subscriptions to docker enterprise products for the given dockerID
|
||||
func (c *client) ListSubscriptions(ctx context.Context, authToken, dockerID string) ([]*model.Subscription, error) {
|
||||
ctx = jwt.NewContext(ctx, authToken)
|
||||
|
||||
subs, err := c.listSubscriptions(ctx, map[string]string{"docker_id": dockerID})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, errors.Fields{
|
||||
"docker_id": dockerID,
|
||||
})
|
||||
}
|
||||
|
||||
// filter out non docker licenses
|
||||
dockerSubs := []*model.Subscription{}
|
||||
for _, sub := range subs {
|
||||
if !strings.HasPrefix(sub.ProductID, "docker") {
|
||||
continue
|
||||
}
|
||||
|
||||
dockerSubs = append(dockerSubs, sub)
|
||||
}
|
||||
|
||||
return dockerSubs, nil
|
||||
}
|
||||
|
||||
// ListDetailedSubscriptions returns detailed subscriptions to docker enterprise products for the given dockerID
|
||||
func (c *client) ListSubscriptionsDetails(ctx context.Context, authToken, dockerID string) ([]*model.SubscriptionDetail, error) {
|
||||
ctx = jwt.NewContext(ctx, authToken)
|
||||
|
||||
subs, err := c.listSubscriptionsDetails(ctx, map[string]string{"docker_id": dockerID})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, errors.Fields{
|
||||
"docker_id": dockerID,
|
||||
})
|
||||
}
|
||||
|
||||
// filter out non docker licenses
|
||||
dockerSubs := []*model.SubscriptionDetail{}
|
||||
for _, sub := range subs {
|
||||
if !strings.HasPrefix(sub.ProductID, "docker") {
|
||||
continue
|
||||
}
|
||||
|
||||
dockerSubs = append(dockerSubs, sub)
|
||||
}
|
||||
|
||||
return dockerSubs, nil
|
||||
}
|
||||
|
||||
func (c *client) DownloadLicenseFromHub(ctx context.Context, authToken, subscriptionID string) (*model.IssuedLicense, error) {
|
||||
ctx = jwt.NewContext(ctx, authToken)
|
||||
|
||||
license, err := c.getLicenseFile(ctx, subscriptionID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, errors.Fields{
|
||||
"subscriptionID": subscriptionID,
|
||||
})
|
||||
}
|
||||
|
||||
return license, nil
|
||||
}
|
||||
|
||||
func (c *client) ParseLicense(license []byte) (*model.IssuedLicense, error) {
|
||||
parsedLicense := &model.IssuedLicense{}
|
||||
// The file may contain a leading BOM, which will choke the
|
||||
// json deserializer.
|
||||
license = bytes.Trim(license, "\xef\xbb\xbf")
|
||||
|
||||
if err := json.Unmarshal(license, &parsedLicense); err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to parse license")
|
||||
}
|
||||
|
||||
return parsedLicense, nil
|
||||
}
|
||||
|
||||
type client struct {
|
||||
publicKeys []libtrust.PublicKey
|
||||
hclient *http.Client
|
||||
baseURI url.URL
|
||||
}
|
||||
|
||||
// Config holds licensing client configuration
|
||||
type Config struct {
|
||||
BaseURI url.URL
|
||||
HTTPClient *http.Client
|
||||
// used by licensing client to validate an issued license
|
||||
PublicKeys []string
|
||||
}
|
||||
|
||||
func errorSummary(body []byte) string {
|
||||
var be struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
jsonErr := json.Unmarshal(body, &be)
|
||||
if jsonErr != nil {
|
||||
return clientlib.DefaultErrorSummary(body)
|
||||
}
|
||||
|
||||
return be.Message
|
||||
}
|
||||
|
||||
// New creates a new licensing Client
|
||||
func New(config *Config) (Client, error) {
|
||||
publicKeys, err := unmarshalPublicKeys(config.PublicKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hclient := config.HTTPClient
|
||||
if hclient == nil {
|
||||
hclient = &http.Client{}
|
||||
}
|
||||
|
||||
return &client{
|
||||
baseURI: config.BaseURI,
|
||||
hclient: hclient,
|
||||
publicKeys: publicKeys,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func unmarshalPublicKeys(publicKeys []string) ([]libtrust.PublicKey, error) {
|
||||
trustKeys := make([]libtrust.PublicKey, len(publicKeys))
|
||||
|
||||
for i, publicKey := range publicKeys {
|
||||
trustKey, err := unmarshalPublicKey(publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trustKeys[i] = trustKey
|
||||
}
|
||||
return trustKeys, nil
|
||||
}
|
||||
|
||||
func unmarshalPublicKey(publicKey string) (libtrust.PublicKey, error) {
|
||||
pemBytes, err := base64.StdEncoding.DecodeString(publicKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.Fields{
|
||||
"public_key": publicKey,
|
||||
}, "decode public key failed")
|
||||
}
|
||||
|
||||
key, err := libtrust.UnmarshalPublicKeyPEM(pemBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.Fields{
|
||||
"public_key": publicKey,
|
||||
}, "unmarshal public key failed")
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (c *client) doReq(ctx context.Context, method string, url *url.URL, opts ...clientlib.RequestOption) (*http.Request, *http.Response, error) {
|
||||
return clientlib.Do(ctx, method, url.String(), append(c.requestDefaults(), opts...)...)
|
||||
}
|
||||
|
||||
func (c *client) doRequestNoAuth(ctx context.Context, method string, url *url.URL, opts ...clientlib.RequestOption) (*http.Request, *http.Response, error) {
|
||||
return clientlib.Do(ctx, method, url.String(), append(c.requestDefaults(), opts...)...)
|
||||
}
|
||||
|
||||
func (c *client) requestDefaults() []clientlib.RequestOption {
|
||||
return []clientlib.RequestOption{
|
||||
func(req *clientlib.Request) {
|
||||
tok, _ := jwt.FromContext(req.Context())
|
||||
req.Header.Add("Authorization", "Bearer "+tok)
|
||||
req.ErrorSummary = errorSummary
|
||||
req.Client = c.hclient
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) StoreLicense(ctx context.Context, dclnt WrappedDockerClient, licenses *model.IssuedLicense, localRootDir string) error {
|
||||
return StoreLicense(ctx, dclnt, licenses, localRootDir)
|
||||
}
|
||||
112
vendor/github.com/docker/licensing/lib/errors/error.go
generated
vendored
112
vendor/github.com/docker/licensing/lib/errors/error.go
generated
vendored
@ -1,112 +0,0 @@
|
||||
// Package errors provides error and error wrapping facilities that allow
|
||||
// for the easy reporting of call stacks and structured error annotations.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// New returns a base error that captures the call stack.
|
||||
func New(text string) error {
|
||||
return NewBase(1, text)
|
||||
}
|
||||
|
||||
// Base is an error type that supports capturing the call stack at creation
|
||||
// time, and storing separate text & data to allow structured logging.
|
||||
// While it could be used directly, it may make more sense as an
|
||||
// anonymous inside a package/application specific error struct.
|
||||
type Base struct {
|
||||
Text string
|
||||
Fields Fields
|
||||
CallStack CallStack
|
||||
}
|
||||
|
||||
// Fields holds the annotations for an error.
|
||||
type Fields map[string]interface{}
|
||||
|
||||
// NewBase creates a new Base, capturing a call trace starting
|
||||
// at "skip" calls above.
|
||||
func NewBase(skip int, text string) *Base {
|
||||
return &Base{
|
||||
Text: text,
|
||||
CallStack: CurrentCallStack(skip + 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Base) Error() string {
|
||||
return textAndFields(e.Text, e.Fields)
|
||||
}
|
||||
|
||||
// AddFields allows a Error message to be further annotated with
|
||||
// a set of key,values, to add more context when inspecting
|
||||
// Error messages.
|
||||
func (e *Base) AddFields(fields Fields) {
|
||||
e.Fields = combineFields(e.Fields, fields)
|
||||
}
|
||||
|
||||
// Location returns the location info (file, line, ...) for the place
|
||||
// where this Error error was created.
|
||||
func (e *Base) Location() *Location {
|
||||
return e.CallStack[0].Location()
|
||||
}
|
||||
|
||||
func (e *Base) String() string {
|
||||
var td string
|
||||
loc := e.Location()
|
||||
|
||||
if e.Text != "" || len(e.Fields) > 0 {
|
||||
td = fmt.Sprintf(": %s", textAndFields(e.Text, e.Fields))
|
||||
}
|
||||
return fmt.Sprintf("%s:%d%s", loc.File, loc.Line, td)
|
||||
}
|
||||
|
||||
// MarshalJSON creates a JSON representation of a Error.
|
||||
func (e *Base) MarshalJSON() ([]byte, error) {
|
||||
m := make(Fields)
|
||||
loc := e.Location()
|
||||
m["file"] = loc.File
|
||||
m["line"] = loc.Line
|
||||
m["func"] = loc.Func
|
||||
if e.Text != "" {
|
||||
m["text"] = e.Text
|
||||
}
|
||||
if len(e.Fields) > 0 {
|
||||
m["fields"] = e.Fields
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// Stack returns the call stack from where this Error was created.
|
||||
func (e *Base) Stack() CallStack {
|
||||
return e.CallStack
|
||||
}
|
||||
|
||||
func combineFields(f1 Fields, f2 Fields) Fields {
|
||||
data := make(Fields, len(f1)+len(f2))
|
||||
for k, v := range f1 {
|
||||
data[k] = v
|
||||
}
|
||||
for k, v := range f2 {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func textAndFields(text string, fields Fields) string {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if text != "" {
|
||||
buf.WriteString(text)
|
||||
}
|
||||
|
||||
for k, v := range fields {
|
||||
buf.WriteByte(' ')
|
||||
buf.WriteString(k)
|
||||
buf.WriteByte('=')
|
||||
fmt.Fprintf(buf, "%+v", v)
|
||||
}
|
||||
|
||||
return string(buf.Bytes())
|
||||
}
|
||||
101
vendor/github.com/docker/licensing/lib/errors/herror.go
generated
vendored
101
vendor/github.com/docker/licensing/lib/errors/herror.go
generated
vendored
@ -1,101 +0,0 @@
|
||||
package errors
|
||||
|
||||
import "net/http"
|
||||
|
||||
// HTTPStatus is a convenience checker for a (possibly wrapped) HTTPStatus
|
||||
// interface error. If err doesn't support the HTTPStatus interface, it will
|
||||
// default to either StatusOK or StatusInternalServerError as appropriate.
|
||||
func HTTPStatus(err error) (status int, ok bool) {
|
||||
type httperror interface {
|
||||
HTTPStatus() int
|
||||
}
|
||||
|
||||
_, _, cause := Cause(err)
|
||||
he, ok := cause.(httperror)
|
||||
if ok {
|
||||
return he.HTTPStatus(), true
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return http.StatusOK, false
|
||||
}
|
||||
return http.StatusInternalServerError, false
|
||||
}
|
||||
|
||||
// HTTPError provides an easy way to specify the http status code equivalent
|
||||
// for an error at error creation time, as well as inheriting the useful
|
||||
// features from Base (call stacks, structured text & data).
|
||||
type HTTPError struct {
|
||||
*Base
|
||||
Status int
|
||||
}
|
||||
|
||||
// HTTPStatus returns the appropriate http status code for this error.
|
||||
func (e *HTTPError) HTTPStatus() int {
|
||||
if e == nil {
|
||||
return http.StatusOK
|
||||
}
|
||||
if e.Status == 0 {
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
return e.Status
|
||||
}
|
||||
|
||||
// With allows additional structured data fields to be added to this HTTPError.
|
||||
func (e *HTTPError) With(fields Fields) *HTTPError {
|
||||
e.AddFields(fields)
|
||||
return e
|
||||
}
|
||||
|
||||
// WithStatus allows setting the http status.
|
||||
func (e *HTTPError) WithStatus(status int) *HTTPError {
|
||||
e.Status = status
|
||||
return e
|
||||
}
|
||||
|
||||
// NewHTTPError constructs an error with the given http status.
|
||||
func NewHTTPError(status int, text string) *HTTPError {
|
||||
return newHTTPErrorWithDepth(status, text)
|
||||
}
|
||||
|
||||
// newHTTPErrorWithDepth constructs an error with the given http status and
|
||||
// depth
|
||||
func newHTTPErrorWithDepth(status int, text string) *HTTPError {
|
||||
return &HTTPError{
|
||||
Status: status,
|
||||
Base: NewBase(2, text),
|
||||
}
|
||||
}
|
||||
|
||||
// NotFound is returned when a resource was not found.
|
||||
func NotFound(fields Fields, text string) *HTTPError {
|
||||
return newHTTPErrorWithDepth(http.StatusNotFound, text).With(fields)
|
||||
}
|
||||
|
||||
// BadRequest is returned when a request did not pass validation, or is
|
||||
// in appropriate for the state of the resource it would affect.
|
||||
func BadRequest(fields Fields, text string) *HTTPError {
|
||||
return newHTTPErrorWithDepth(http.StatusBadRequest, text).With(fields)
|
||||
}
|
||||
|
||||
// Conflict is returned when request could not be completed due to a conflict with
|
||||
// the current state of the resource.
|
||||
func Conflict(fields Fields, text string) *HTTPError {
|
||||
return newHTTPErrorWithDepth(http.StatusConflict, text).With(fields)
|
||||
}
|
||||
|
||||
// PaymentRequired is returned if the requested resource must be purchased.
|
||||
func PaymentRequired(fields Fields, text string) *HTTPError {
|
||||
return newHTTPErrorWithDepth(http.StatusPaymentRequired, text).With(fields)
|
||||
}
|
||||
|
||||
// Forbidden is returned if the requesting user does not have the required
|
||||
// permissions for a request.
|
||||
func Forbidden(fields Fields, text string) *HTTPError {
|
||||
return newHTTPErrorWithDepth(http.StatusForbidden, text).With(fields)
|
||||
}
|
||||
|
||||
// InternalError should only be returned if no other specific error applies.
|
||||
func InternalError(fields Fields, text string) *HTTPError {
|
||||
return newHTTPErrorWithDepth(http.StatusInternalServerError, text).With(fields)
|
||||
}
|
||||
65
vendor/github.com/docker/licensing/lib/errors/stack.go
generated
vendored
65
vendor/github.com/docker/licensing/lib/errors/stack.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Call holds a call frame
|
||||
type Call runtime.Frame
|
||||
|
||||
// Location is the parsed file, line, and other info we can determine from
|
||||
// a specific gostack.Call.
|
||||
type Location struct {
|
||||
File string
|
||||
Line int
|
||||
Func string
|
||||
}
|
||||
|
||||
// Location uses the gostack package to construct file, line, and other
|
||||
// info about this call.
|
||||
func (c Call) Location() *Location {
|
||||
return &Location{
|
||||
File: c.File,
|
||||
Line: c.Line,
|
||||
Func: c.Function,
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON representation.
|
||||
func (c Call) MarshalJSON() ([]byte, error) {
|
||||
m := make(map[string]interface{})
|
||||
loc := c.Location()
|
||||
m["file"] = loc.File
|
||||
m["line"] = loc.Line
|
||||
m["func"] = loc.Func
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (c Call) String() string {
|
||||
return fmt.Sprintf("%v:%v", c.File, c.Line)
|
||||
}
|
||||
|
||||
// CallStack is a convenience alias for a call stack.
|
||||
type CallStack []Call
|
||||
|
||||
// CurrentCallStack returns the call stack, skipping the specified
|
||||
// depth of calls.
|
||||
func CurrentCallStack(skip int) CallStack {
|
||||
var pcs [128]uintptr
|
||||
n := runtime.Callers(skip+2, pcs[:])
|
||||
|
||||
callersFrames := runtime.CallersFrames(pcs[:n])
|
||||
cs := make([]Call, 0, n)
|
||||
|
||||
for {
|
||||
frame, more := callersFrames.Next()
|
||||
cs = append(cs, Call(frame))
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return cs
|
||||
}
|
||||
106
vendor/github.com/docker/licensing/lib/errors/wrap.go
generated
vendored
106
vendor/github.com/docker/licensing/lib/errors/wrap.go
generated
vendored
@ -1,106 +0,0 @@
|
||||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Wrapf takes an originating "cause" error and annotates
|
||||
// it with text and the source file & line of the wrap point.
|
||||
func Wrapf(err error, fields Fields, format string, args ...interface{}) *Wrapped {
|
||||
w := &Wrapped{
|
||||
Base: NewBase(1, fmt.Sprintf(format, args...)),
|
||||
cause: err,
|
||||
}
|
||||
w.AddFields(fields)
|
||||
return w
|
||||
}
|
||||
|
||||
// Wrap takes an originating "cause" error and annotates
|
||||
// it just with the source file & line of the wrap point.
|
||||
func Wrap(err error, fields Fields) *Wrapped {
|
||||
w := &Wrapped{
|
||||
Base: NewBase(1, err.Error()),
|
||||
cause: err,
|
||||
}
|
||||
w.AddFields(fields)
|
||||
return w
|
||||
}
|
||||
|
||||
// WithMessage takes an originating cause and text description,
|
||||
// returning a wrapped error with the text and stack.
|
||||
func WithMessage(err error, text string) *Wrapped {
|
||||
return &Wrapped{
|
||||
Base: NewBase(1, text),
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack takes an originating cause and returns
|
||||
// a wrapped error that just records the stack.
|
||||
func WithStack(err error) *Wrapped {
|
||||
return &Wrapped{
|
||||
Base: NewBase(1, err.Error()),
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapped provides a way to add additional context when passing back
|
||||
// an error to a caller. It inherits the useful features from Base
|
||||
// (call stacks, structured text & data).
|
||||
type Wrapped struct {
|
||||
*Base
|
||||
cause error
|
||||
}
|
||||
|
||||
func (w *Wrapped) Error() string {
|
||||
msg := textAndFields(w.Text, w.Fields)
|
||||
return msg + ": " + w.cause.Error()
|
||||
}
|
||||
|
||||
// With allows adding additional structured data fields.
|
||||
func (w *Wrapped) With(fields Fields) *Wrapped {
|
||||
w.AddFields(fields)
|
||||
return w
|
||||
}
|
||||
|
||||
// Unwrap extracts any layered Wrapped errors inside of this one,
|
||||
// returning the first non-Wrapped error found as the original cause.
|
||||
func (w *Wrapped) Unwrap() (wraps []*Base, cause error) {
|
||||
for w != nil {
|
||||
cause = w.cause
|
||||
wraps = append(wraps, w.Base)
|
||||
w, _ = w.cause.(*Wrapped)
|
||||
}
|
||||
// For consistency, wraps should be in same order as stacks:
|
||||
// first element is from the innermost wrap
|
||||
// Hence we need to reverse the append operation above.
|
||||
for i := len(wraps)/2 - 1; i >= 0; i-- {
|
||||
j := len(wraps) - 1 - i
|
||||
wraps[i], wraps[j] = wraps[j], wraps[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Cause checks if the passed in error is a Wrapped. If so, it will
|
||||
// extract & return information about all the wrapped errors inside.
|
||||
// It will return the CallStack of cause, if it supports the errors.Stack()
|
||||
// interface, or the innermost wrap (which should be the closest wrap to cause.)
|
||||
// If error is not a wrapped, cause is same as input err.
|
||||
func Cause(err error) (stack CallStack, wraps []*Base, cause error) {
|
||||
cause = err
|
||||
if w, ok := err.(*Wrapped); ok {
|
||||
wraps, cause = w.Unwrap()
|
||||
}
|
||||
|
||||
type stacker interface {
|
||||
Stack() CallStack
|
||||
}
|
||||
|
||||
if s, ok := cause.(stacker); ok {
|
||||
stack = s.Stack()
|
||||
} else {
|
||||
if len(wraps) > 0 {
|
||||
stack = wraps[0].Stack()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
21
vendor/github.com/docker/licensing/lib/go-auth/README.md
generated
vendored
21
vendor/github.com/docker/licensing/lib/go-auth/README.md
generated
vendored
@ -1,21 +0,0 @@
|
||||
This is a library for handling JWT authentication tokens
|
||||
|
||||
### Generating new test certificates
|
||||
|
||||
In the event that the test certificates expire, regenerate them with OpenSSL
|
||||
|
||||
cd jwt/testdata
|
||||
|
||||
Generate new root CA cert and private key
|
||||
|
||||
openssl req -newkey rsa:4096 -nodes -keyout root_key.pem -x509 -days 3650 -out root-certs
|
||||
|
||||
|
||||
Generate new intermediate CA cert and private key
|
||||
|
||||
openssl req -new -key private-key -out inter.csr
|
||||
|
||||
openssl x509 -req -days 3650 -in inter.csr -CA root-certs -CAkey root_key.pem -CAcreateserial -out trusted-cert
|
||||
|
||||
|
||||
|
||||
45
vendor/github.com/docker/licensing/lib/go-auth/identity/identity.go
generated
vendored
45
vendor/github.com/docker/licensing/lib/go-auth/identity/identity.go
generated
vendored
@ -1,45 +0,0 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// DockerIdentity identifies a Docker user.
|
||||
type DockerIdentity struct {
|
||||
DockerID string
|
||||
Username string
|
||||
FullName string
|
||||
Email string
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
func (di DockerIdentity) String() string {
|
||||
return fmt.Sprintf("{docker_id=%v, username=%v, email=%v, scopes=%v}",
|
||||
di.DockerID, di.Username, di.Email, di.Scopes)
|
||||
}
|
||||
|
||||
// HasScope returns true if the exact input scope is present in the scopes list.
|
||||
func (di DockerIdentity) HasScope(scope string) bool {
|
||||
for i := range di.Scopes {
|
||||
if di.Scopes[i] == scope {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type keyType int
|
||||
|
||||
var identityContextKey keyType
|
||||
|
||||
// FromContext returns the DockerIdentity value stored in ctx, if any.
|
||||
func FromContext(ctx context.Context) (*DockerIdentity, bool) {
|
||||
identity, ok := ctx.Value(identityContextKey).(*DockerIdentity)
|
||||
return identity, ok
|
||||
}
|
||||
|
||||
// NewContext returns a new Context that carries value identity.
|
||||
func NewContext(ctx context.Context, identity *DockerIdentity) context.Context {
|
||||
return context.WithValue(ctx, identityContextKey, identity)
|
||||
}
|
||||
20
vendor/github.com/docker/licensing/lib/go-auth/jwt/context.go
generated
vendored
20
vendor/github.com/docker/licensing/lib/go-auth/jwt/context.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type key int
|
||||
|
||||
var jwtContextKey key
|
||||
|
||||
// FromContext returns the token value stored in ctx, if any.
|
||||
func FromContext(ctx context.Context) (string, bool) {
|
||||
token, ok := ctx.Value(jwtContextKey).(string)
|
||||
return token, ok
|
||||
}
|
||||
|
||||
// NewContext returns a new Context that carries value token.
|
||||
func NewContext(ctx context.Context, token string) context.Context {
|
||||
return context.WithValue(ctx, jwtContextKey, token)
|
||||
}
|
||||
284
vendor/github.com/docker/licensing/lib/go-auth/jwt/jwt.go
generated
vendored
284
vendor/github.com/docker/licensing/lib/go-auth/jwt/jwt.go
generated
vendored
@ -1,284 +0,0 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/docker/licensing/lib/go-auth/identity"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
// x509 cert chain header field
|
||||
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41#section-4.7
|
||||
x5c = "x5c"
|
||||
|
||||
// non standard username claim
|
||||
username = "username"
|
||||
|
||||
// non standard email claim
|
||||
email = "email"
|
||||
|
||||
// subject claim
|
||||
// https://tools.ietf.org/html/rfc7519#section-4.1.2
|
||||
sub = "sub"
|
||||
|
||||
// jwt id claim
|
||||
// https://tools.ietf.org/html/rfc7519#section-4.1.7
|
||||
jti = "jti"
|
||||
|
||||
// issued at claim
|
||||
// https://tools.ietf.org/html/rfc7519#section-4.1.6
|
||||
iat = "iat"
|
||||
|
||||
// expiration time claim
|
||||
// https://tools.ietf.org/html/rfc7519#section-4.1.4
|
||||
exp = "exp"
|
||||
|
||||
// Legacy claims the gateways are still using to validate a JWT.
|
||||
sessionid = "session_id" // same as a `jti`
|
||||
userid = "user_id" // same as a `sub`
|
||||
|
||||
// non standard scope claim
|
||||
scope = "scope"
|
||||
)
|
||||
|
||||
// EncodeOptions holds JWT encoding options
|
||||
type EncodeOptions struct {
|
||||
// The token expiration time, represented as a UNIX timestamp
|
||||
Expiration int64
|
||||
|
||||
// TODO would be good to add a leeway option, but go-jwt
|
||||
// does not support this. see: https://github.com/dgrijalva/jwt-go/issues/131
|
||||
|
||||
// The private key with which to sign the token
|
||||
SigningKey []byte
|
||||
|
||||
// The x509 certificate associated with the signing key
|
||||
Certificate []byte
|
||||
|
||||
// Identifier for the JWT. If this is empty, a random UUID will be generated.
|
||||
Jti string
|
||||
|
||||
// Whether or not to include legacy claims that the gateways are still using to validate a JWT.
|
||||
IncludeLegacyClaims bool
|
||||
}
|
||||
|
||||
// Encode creates a JWT string for the given identity.DockerIdentity.
|
||||
func Encode(identity identity.DockerIdentity, options EncodeOptions) (string, error) {
|
||||
block, _ := pem.Decode(options.Certificate)
|
||||
if block == nil {
|
||||
return "", fmt.Errorf("invalid key: failed to parse header")
|
||||
}
|
||||
|
||||
encodedCert := base64.StdEncoding.EncodeToString(block.Bytes)
|
||||
x5cCerts := [1]string{encodedCert}
|
||||
|
||||
// non standard fields
|
||||
// Note: this is a required field
|
||||
claims := make(map[string]interface{})
|
||||
claims[username] = identity.Username
|
||||
claims[email] = identity.Email
|
||||
|
||||
// standard JWT fields, consult the JWT spec for details
|
||||
claims[sub] = identity.DockerID
|
||||
|
||||
if len(identity.Scopes) > 0 {
|
||||
claims[scope] = strings.Join(identity.Scopes, " ")
|
||||
}
|
||||
|
||||
jtiStr := options.Jti
|
||||
if len(jtiStr) == 0 {
|
||||
jtiStr = "jti-" + uuid.New().String()
|
||||
}
|
||||
claims[jti] = jtiStr
|
||||
|
||||
claims[iat] = time.Now().Unix()
|
||||
claims[exp] = options.Expiration
|
||||
|
||||
if options.IncludeLegacyClaims {
|
||||
claims[sessionid] = jtiStr
|
||||
claims[userid] = identity.DockerID
|
||||
}
|
||||
|
||||
// Note: we only support a RS256 signing method right now. If we want to support
|
||||
// additional signing methods (for example, HS256), this could be specified as an
|
||||
// encoding option.
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims(claims))
|
||||
token.Header[x5c] = x5cCerts
|
||||
|
||||
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(options.SigningKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token.SignedString(privateKey)
|
||||
}
|
||||
|
||||
// DecodeOptions holds JWT decoding options
|
||||
type DecodeOptions struct {
|
||||
CertificateChain *x509.CertPool
|
||||
}
|
||||
|
||||
// Decode decodes the given JWT string, returning the decoded identity.DockerIdentity
|
||||
func Decode(tokenStr string, options DecodeOptions) (*identity.DockerIdentity, error) {
|
||||
token, err := jwt.Parse(tokenStr, keyFunc(options.CertificateChain))
|
||||
|
||||
if err != nil {
|
||||
if ve, ok := err.(*jwt.ValidationError); ok {
|
||||
return nil, &ValidationError{VError: ve}
|
||||
}
|
||||
return nil, fmt.Errorf("error decoding token: %s", err)
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
username, ok := claims[username].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%v claim not present", username)
|
||||
}
|
||||
dockerID, ok := claims[sub].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%v claim not present", sub)
|
||||
}
|
||||
|
||||
// email is optional
|
||||
email, _ := claims[email].(string)
|
||||
|
||||
var scopes []string
|
||||
if scopeClaim, ok := claims[scope]; ok {
|
||||
sstr, ok := scopeClaim.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("scope claim invalid")
|
||||
}
|
||||
scopes = strings.Split(sstr, " ")
|
||||
}
|
||||
|
||||
return &identity.DockerIdentity{
|
||||
Username: username,
|
||||
DockerID: dockerID,
|
||||
Email: email,
|
||||
Scopes: scopes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// no error but an invalid token seems like a corner case, but just to be sure
|
||||
return nil, fmt.Errorf("token was invalid")
|
||||
}
|
||||
|
||||
// IsExpired returns true if the token has expired, false otherwise
|
||||
func IsExpired(tokenStr string, options DecodeOptions) (bool, error) {
|
||||
rootCerts := options.CertificateChain
|
||||
_, err := jwt.Parse(tokenStr, keyFunc(rootCerts))
|
||||
if err == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if ve, ok := err.(*jwt.ValidationError); ok {
|
||||
if ve.Errors&(jwt.ValidationErrorExpired) != 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
// keyFunc returns the jwt.KeyFunc with which to validate the token
|
||||
func keyFunc(roots *x509.CertPool) jwt.Keyfunc {
|
||||
return func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
// x5c holds a base64 encoded DER encoded x509 certificate
|
||||
// associated with the private key used to sign the token.
|
||||
|
||||
// For more information, see:
|
||||
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41#page-9
|
||||
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41#appendix-B
|
||||
x5c, ok := token.Header[x5c].([]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("x5c token header not present")
|
||||
}
|
||||
|
||||
if len(x5c) == 0 {
|
||||
return nil, fmt.Errorf("x5c token header was empty")
|
||||
}
|
||||
|
||||
x5cString, ok := x5c[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("x5c token header was not a string")
|
||||
}
|
||||
|
||||
decodedCert, err := base64.StdEncoding.DecodeString(x5cString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, err := validateCert(decodedCert, roots)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
||||
|
||||
return jwt.ParseRSAPublicKeyFromPEM(key)
|
||||
}
|
||||
}
|
||||
|
||||
// validateCert validates the ASN.1 DER encoded cert using the given x509.CertPool root
|
||||
// certificate chain. If valid, the parsed x509.Certificate is returned.
|
||||
func validateCert(derData []byte, roots *x509.CertPool) (*x509.Certificate, error) {
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: roots,
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(derData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: [%v]", err)
|
||||
}
|
||||
|
||||
_, err = cert.Verify(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to verify certificate: [%v]", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// ValidationError interrogates the jwt.ValidationError, returning
|
||||
// a more detailed error message.
|
||||
type ValidationError struct {
|
||||
VError *jwt.ValidationError
|
||||
}
|
||||
|
||||
func (e *ValidationError) Error() string {
|
||||
errs := e.VError.Errors
|
||||
|
||||
if errs&jwt.ValidationErrorMalformed != 0 {
|
||||
return fmt.Sprintf("malformed token error: [%v]", e.VError)
|
||||
}
|
||||
|
||||
if errs&jwt.ValidationErrorUnverifiable != 0 {
|
||||
return fmt.Sprintf("token signature error: [%v]", e.VError)
|
||||
}
|
||||
|
||||
if errs&jwt.ValidationErrorSignatureInvalid != 0 {
|
||||
return fmt.Sprintf("token signature error: [%v]", e.VError)
|
||||
}
|
||||
|
||||
if errs&jwt.ValidationErrorExpired != 0 {
|
||||
return fmt.Sprintf("token expiration error: [%v]", e.VError)
|
||||
}
|
||||
|
||||
if errs&jwt.ValidationErrorNotValidYet != 0 {
|
||||
return fmt.Sprintf("token NBF validation error: [%v]", e.VError)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("token validation error: [%v]", e.VError)
|
||||
}
|
||||
5
vendor/github.com/docker/licensing/lib/go-clientlib/README.md
generated
vendored
5
vendor/github.com/docker/licensing/lib/go-clientlib/README.md
generated
vendored
@ -1,5 +0,0 @@
|
||||
# go-clientlib
|
||||
|
||||
## Overview
|
||||
|
||||
`go-clientlib` is used to reduce much of the boilerplate needed for sending and receiving http requests and responses in client libraries.
|
||||
305
vendor/github.com/docker/licensing/lib/go-clientlib/client.go
generated
vendored
305
vendor/github.com/docker/licensing/lib/go-clientlib/client.go
generated
vendored
@ -1,305 +0,0 @@
|
||||
package clientlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/licensing/lib/errors"
|
||||
)
|
||||
|
||||
// Do is a shortcut for creating and executing an http request.
|
||||
func Do(ctx context.Context, method, urlStr string, opts ...RequestOption) (*http.Request, *http.Response, error) {
|
||||
r, err := New(ctx, method, urlStr, opts...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
res, err := r.Do()
|
||||
return r.Request, res, err
|
||||
}
|
||||
|
||||
// New creates and returns a new Request, potentially configured via a vector of RequestOption's.
|
||||
func New(ctx context.Context, method, urlStr string, opts ...RequestOption) (*Request, error) {
|
||||
req, err := http.NewRequest(method, urlStr, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
r := &Request{
|
||||
Request: req,
|
||||
Client: &http.Client{},
|
||||
ErrorCheck: DefaultErrorCheck,
|
||||
ErrorBodyMaxLength: defaultErrBodyMaxLength,
|
||||
ErrorSummary: DefaultErrorSummary,
|
||||
RequestPrepare: DefaultRequestPrepare,
|
||||
ResponseHandle: DefaultResponseHandle,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(r)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Request encompasses an http.Request, plus configured behavior options.
|
||||
type Request struct {
|
||||
*http.Request
|
||||
Client *http.Client
|
||||
ErrorCheck ErrorCheck
|
||||
ErrorBodyMaxLength int64
|
||||
ErrorSummary ErrorSummary
|
||||
ResponseHandle ResponseHandle
|
||||
RequestPrepare RequestPrepare
|
||||
}
|
||||
|
||||
// Do executes the Request. The Request.ErrorCheck to determine
|
||||
// if this attempt has failed, and transform the returned error.
|
||||
// Otherwise, Request.ResponseHandler will examine the response.
|
||||
// It's expected that the ResponseHandler has been configured via
|
||||
// a RequestOption to perform response parsing and storing.
|
||||
func (r *Request) Do() (*http.Response, error) {
|
||||
err := r.RequestPrepare(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := r.Client.Do(r.Request)
|
||||
err = r.ErrorCheck(r, err, res)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return res, r.ResponseHandle(r, res)
|
||||
}
|
||||
|
||||
// SetBody mirrors the ReadCloser config in http.NewRequest,
|
||||
// ensuring that a ReadCloser is used for http.Request.Body.
|
||||
func (r *Request) SetBody(body io.Reader) {
|
||||
rc, ok := body.(io.ReadCloser)
|
||||
if !ok && body != nil {
|
||||
rc = ioutil.NopCloser(body)
|
||||
}
|
||||
r.Body = rc
|
||||
}
|
||||
|
||||
// ErrorFields returns error annotation fields for the request.
|
||||
func (r *Request) ErrorFields() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"url": r.URL.String(),
|
||||
"method": r.Method,
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorCheck is the signature for the function that is passed
|
||||
// the error & response immediately from http.Client.Do().
|
||||
type ErrorCheck func(r *Request, doErr error, res *http.Response) error
|
||||
|
||||
// DefaultErrorCheck is the default error checker used if none is
|
||||
// configured on a Request. doErr and res are the return values of
|
||||
// executing http.Client.Do(), so any implementation should first
|
||||
// check doErr for non-nill & react appropriately. If an http response
|
||||
// was received, if a non-200 class status was also received, then
|
||||
// the response body will be read (up to a const limit) and passed
|
||||
// to request.ErrorSummary to attempt to parse out the error body,
|
||||
// which will be passed as the "detail" flag on the returned error.
|
||||
func DefaultErrorCheck(r *Request, doErr error, res *http.Response) error {
|
||||
if doErr != nil {
|
||||
return errors.Wrap(doErr, r.ErrorFields())
|
||||
}
|
||||
status := res.StatusCode
|
||||
if status >= 200 && status < 300 {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
body, _ := ioutil.ReadAll(io.LimitReader(res.Body, r.ErrorBodyMaxLength))
|
||||
detail := r.ErrorSummary(body)
|
||||
|
||||
message := fmt.Sprintf("%s %s returned %d : %s", r.Method, r.URL.String(), status, detail)
|
||||
return errors.NewHTTPError(status, message).
|
||||
With(r.ErrorFields()).
|
||||
With(map[string]interface{}{
|
||||
"http_status": status,
|
||||
"detail": detail,
|
||||
})
|
||||
}
|
||||
|
||||
// Default error response max length, in bytes
|
||||
const defaultErrBodyMaxLength = 256
|
||||
|
||||
// ErrorSummary is the signature for the function that is passed
|
||||
// the fully read body of an error response.
|
||||
type ErrorSummary func([]byte) string
|
||||
|
||||
// DefaultErrorSummary just returns the string of the received
|
||||
// error body. Note that the body passed in is potentially truncated
|
||||
// before this call.
|
||||
func DefaultErrorSummary(body []byte) string {
|
||||
return string(body)
|
||||
}
|
||||
|
||||
// RequestPrepare is the signature for the function called
|
||||
// before calling http.Client.Do, to perform any preparation
|
||||
// needed before executing the request, eg. marshaling the body.
|
||||
type RequestPrepare func(r *Request) error
|
||||
|
||||
// DefaultRequestPrepare does nothing.
|
||||
func DefaultRequestPrepare(*Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResponseHandle is the signature for the function called if
|
||||
// ErrorCheck returns a nil error, and is responsible for performing
|
||||
// any reads or stores from the request & response.
|
||||
type ResponseHandle func(*Request, *http.Response) error
|
||||
|
||||
// DefaultResponseHandle merely closes the response body.
|
||||
func DefaultResponseHandle(r *Request, res *http.Response) error {
|
||||
res.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestOption is the signature for functions that can perform
|
||||
// some configuration of a Request.
|
||||
type RequestOption func(*Request)
|
||||
|
||||
// SendJSON returns a RequestOption that will marshal
|
||||
// and set the json body & headers on a request.
|
||||
func SendJSON(sends interface{}) RequestOption {
|
||||
return func(r *Request) {
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
r.RequestPrepare = func(r *Request) error {
|
||||
bits, err := json.Marshal(sends)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, r.ErrorFields())
|
||||
}
|
||||
body := bytes.NewReader(bits)
|
||||
r.SetBody(body)
|
||||
r.ContentLength = int64(body.Len())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RecvJSON returns a RequestOption that will set the json headers
|
||||
// on a request, and set a ResponseHandler that will unmarshal
|
||||
// the response body to the given interface{}.
|
||||
func RecvJSON(recvs interface{}) RequestOption {
|
||||
return func(r *Request) {
|
||||
r.Header.Set("Accept", "application/json")
|
||||
r.Header.Set("Accept-Charset", "utf-8")
|
||||
r.ResponseHandle = func(r *Request, res *http.Response) error {
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, r.ErrorFields())
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, recvs)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, r.ErrorFields())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SendXML returns a RequestOption that will marshal
|
||||
// and set the xml body & headers on a request.
|
||||
func SendXML(sends interface{}) RequestOption {
|
||||
return func(r *Request) {
|
||||
r.Header.Set("Content-Type", "application/xml")
|
||||
r.RequestPrepare = func(r *Request) error {
|
||||
bits, err := xml.Marshal(sends)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, r.ErrorFields())
|
||||
}
|
||||
body := bytes.NewReader(bits)
|
||||
r.SetBody(body)
|
||||
r.ContentLength = int64(body.Len())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RecvXML returns a RequestOption that will set the xml headers
|
||||
// on a request, and set a ResponseHandler that will unmarshal
|
||||
// the response body to the given interface{}.
|
||||
func RecvXML(recvs interface{}) RequestOption {
|
||||
return func(r *Request) {
|
||||
r.Header.Set("Accept", "application/xml")
|
||||
r.Header.Set("Accept-Charset", "utf-8")
|
||||
r.ResponseHandle = func(r *Request, res *http.Response) error {
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, r.ErrorFields())
|
||||
}
|
||||
|
||||
err = xml.Unmarshal(body, recvs)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, r.ErrorFields())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SendText returns a RequestOption that will marshal
|
||||
// and set the text body & headers on a request.
|
||||
func SendText(sends string) RequestOption {
|
||||
return func(r *Request) {
|
||||
r.Header.Set("Content-Type", "text/plain")
|
||||
r.RequestPrepare = func(r *Request) error {
|
||||
body := strings.NewReader(sends)
|
||||
r.SetBody(body)
|
||||
r.ContentLength = int64(body.Len())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RecvText returns a RequestOption that will set the text headers
|
||||
// on a request, and set a ResponseHandler that will unmarshal
|
||||
// the response body to the given string.
|
||||
func RecvText(recvs *string) RequestOption {
|
||||
return func(r *Request) {
|
||||
r.Header.Set("Accept", "text/plain")
|
||||
r.Header.Set("Accept-Charset", "utf-8")
|
||||
r.ResponseHandle = func(r *Request, res *http.Response) error {
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, r.ErrorFields())
|
||||
}
|
||||
|
||||
sbody := string(body)
|
||||
*recvs = sbody
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DontClose sets the ResponseBody to an empty function, so that the
|
||||
// response body is not automatically closed. Users of this should be
|
||||
// sure to call res.Body.Close().
|
||||
func DontClose() RequestOption {
|
||||
return func(r *Request) {
|
||||
r.ResponseHandle = func(*Request, *http.Response) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
5
vendor/github.com/docker/licensing/lib/go-validation/README.md
generated
vendored
5
vendor/github.com/docker/licensing/lib/go-validation/README.md
generated
vendored
@ -1,5 +0,0 @@
|
||||
# go-validation
|
||||
|
||||
## Overview
|
||||
|
||||
`go-validation` provides struct validation utilities. See `validation_test.go` for sample usage.
|
||||
141
vendor/github.com/docker/licensing/lib/go-validation/validation.go
generated
vendored
141
vendor/github.com/docker/licensing/lib/go-validation/validation.go
generated
vendored
@ -1,141 +0,0 @@
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
validator "github.com/asaskevich/govalidator"
|
||||
)
|
||||
|
||||
// ErrorKind represents a validation error type
|
||||
type ErrorKind int
|
||||
|
||||
// ErrorKind enumeration
|
||||
const (
|
||||
// ErrorGeneral represents a catchall validation error. That is,
|
||||
// an error that does not match any ErrorKind.
|
||||
ErrorGeneral ErrorKind = iota
|
||||
// ErrorEmpty represents an empty field validation error.
|
||||
ErrorEmpty
|
||||
// ErrorInvalidEmail represents an invalid email validation error.
|
||||
ErrorInvalidEmail
|
||||
// ErrorInvalidURL represents an invalid url validation error.
|
||||
ErrorInvalidURL
|
||||
)
|
||||
|
||||
// Error represents a validation error
|
||||
type Error struct {
|
||||
FieldName string
|
||||
FieldValue interface{}
|
||||
kind ErrorKind
|
||||
}
|
||||
|
||||
// Error returns the string representation of the error
|
||||
func (e *Error) Error() string {
|
||||
msg := e.MsgForCode()
|
||||
return fmt.Sprintf("%v invalid: %v", e.FieldName, msg)
|
||||
}
|
||||
|
||||
// MsgForCode returns a human readable message for the given ErrorKind.
|
||||
// The message may optionally include the given field value in the message.
|
||||
func (e *Error) MsgForCode() string {
|
||||
switch e.kind {
|
||||
case ErrorEmpty:
|
||||
return "not provided"
|
||||
case ErrorInvalidEmail:
|
||||
return fmt.Sprintf("%v is an invalid email address", e.FieldValue)
|
||||
case ErrorInvalidURL:
|
||||
return fmt.Sprintf("%v is an invalid url", e.FieldValue)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("'%v' is not a valid value", e.FieldValue)
|
||||
}
|
||||
|
||||
// Errors is a list of validation Errors
|
||||
type Errors []*Error
|
||||
|
||||
// Validater has a single function Validate, which can be used to determine
|
||||
// if the interface implementation is valid. Validate should return true if the implementation
|
||||
// passes validation, false otherwise. If invalid, a list of one or more validation
|
||||
// Errors should be returned.
|
||||
type Validater interface {
|
||||
Validate() (bool, Errors)
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the given interface is empty, false otherwise
|
||||
func IsEmpty(s interface{}) bool {
|
||||
v := reflect.ValueOf(s)
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.String,
|
||||
reflect.Array:
|
||||
return v.Len() == 0
|
||||
case reflect.Map,
|
||||
reflect.Slice:
|
||||
return v.Len() == 0 || v.IsNil()
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int,
|
||||
reflect.Int8,
|
||||
reflect.Int16,
|
||||
reflect.Int32,
|
||||
reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint,
|
||||
reflect.Uint8,
|
||||
reflect.Uint16,
|
||||
reflect.Uint32,
|
||||
reflect.Uint64,
|
||||
reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32,
|
||||
reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface,
|
||||
reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
|
||||
}
|
||||
|
||||
// IsEmail returns true if the given email address is valid, false otherwise
|
||||
func IsEmail(str string) bool {
|
||||
return validator.IsEmail(str)
|
||||
}
|
||||
|
||||
// IsURL returns true if the given url is valid, false otherwise
|
||||
func IsURL(str string) bool {
|
||||
return validator.IsURL(str)
|
||||
}
|
||||
|
||||
// Matches returns true if the given string matches the given pattern, false otherwise
|
||||
func Matches(str, pattern string) bool {
|
||||
return validator.Matches(str, pattern)
|
||||
}
|
||||
|
||||
// InvalidEmpty returns an empty validation Error for the given field name
|
||||
func InvalidEmpty(fieldName string) *Error {
|
||||
return &Error{
|
||||
FieldName: fieldName,
|
||||
kind: ErrorEmpty,
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidEmail returns an invalid validation error for the given field name and value
|
||||
func InvalidEmail(fieldName, fieldValue string) *Error {
|
||||
return &Error{
|
||||
FieldName: fieldName,
|
||||
FieldValue: fieldValue,
|
||||
kind: ErrorInvalidEmail,
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidURL returns an invalid validation error for the given field name and value
|
||||
func InvalidURL(fieldName, fieldValue string) *Error {
|
||||
return &Error{
|
||||
FieldName: fieldName,
|
||||
FieldValue: fieldValue,
|
||||
kind: ErrorInvalidURL,
|
||||
}
|
||||
}
|
||||
158
vendor/github.com/docker/licensing/license.go
generated
vendored
158
vendor/github.com/docker/licensing/license.go
generated
vendored
@ -1,158 +0,0 @@
|
||||
package licensing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/docker/licensing/lib/errors"
|
||||
"github.com/docker/licensing/lib/go-clientlib"
|
||||
"github.com/docker/licensing/model"
|
||||
)
|
||||
|
||||
func (c *client) getLicenseFile(ctx context.Context, subID string) (*model.IssuedLicense, error) {
|
||||
url := c.baseURI
|
||||
url.Path += fmt.Sprintf("/api/billing/v4/subscriptions/%s/license-file", subID)
|
||||
|
||||
license := new(model.IssuedLicense)
|
||||
if _, _, err := c.doReq(ctx, "GET", &url, clientlib.RecvJSON(license)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return license, nil
|
||||
}
|
||||
|
||||
// Check verifies that the license identified by the given key id is valid. Note that it does not
|
||||
// interrogate the contents of the license.
|
||||
func (c *client) check(ctx context.Context, license model.IssuedLicense) (*model.CheckResponse, error) {
|
||||
keyID := license.KeyID
|
||||
privateKey := license.PrivateKey
|
||||
|
||||
authorization, err := c.getAuthorization(ctx, license)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Mason - replace this parseJWS with a non libtrust lib
|
||||
signature, err := libtrust.ParseJWS(authorization)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.Fields{
|
||||
"key_id": keyID,
|
||||
}, "license parse JWS failed")
|
||||
}
|
||||
|
||||
keys, err := signature.Verify()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.Fields{
|
||||
"key_id": keyID,
|
||||
}, "license signature verification failed")
|
||||
}
|
||||
|
||||
keyCnt := len(keys)
|
||||
if keyCnt != 1 {
|
||||
err = fmt.Errorf("unexpected number of signing keys (%d)", keyCnt)
|
||||
return nil, errors.WithStack(err).With(errors.Fields{
|
||||
"key_id": keyID,
|
||||
})
|
||||
}
|
||||
|
||||
key := keys[0]
|
||||
|
||||
if !c.recognizedSigningKey(key) {
|
||||
return nil, errors.New("unrecognized signing key")
|
||||
}
|
||||
|
||||
payload, err := signature.Payload()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.Fields{
|
||||
"key_id": keyID,
|
||||
}, "malformed signature payload")
|
||||
}
|
||||
|
||||
checkRes := new(model.CheckResponse)
|
||||
|
||||
err = json.Unmarshal(payload, &checkRes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.Fields{
|
||||
"key_id": keyID,
|
||||
}, "license payload unmarshal failed")
|
||||
}
|
||||
|
||||
msg := checkRes.Expiration.Format(time.RFC3339)
|
||||
if err := checkToken(msg, checkRes.Token, privateKey); err != nil {
|
||||
return nil, errors.Wrap(err, errors.Fields{
|
||||
"key_id": keyID,
|
||||
})
|
||||
}
|
||||
|
||||
return checkRes, nil
|
||||
}
|
||||
|
||||
// recognizedSigningKey returns true if the given key is signed with a recognized signing key, false otherwise
|
||||
func (c *client) recognizedSigningKey(key libtrust.PublicKey) bool {
|
||||
for _, publicKey := range c.publicKeys {
|
||||
if key.KeyID() == publicKey.KeyID() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getAuthorization returns the decoded license authorization
|
||||
func (c *client) getAuthorization(ctx context.Context, license model.IssuedLicense) ([]byte, error) {
|
||||
decoded, err := base64.StdEncoding.DecodeString(license.Authorization)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.Fields{
|
||||
"key_id": license.KeyID,
|
||||
}, "decoding license authorization failed")
|
||||
}
|
||||
return decoded, nil
|
||||
}
|
||||
|
||||
// All of the functions in this file assume that they are receiving a properly
|
||||
// formatted private key.
|
||||
|
||||
// checkToken performs a MAC algorithm (where token is generated by hashing the
|
||||
// message with the privateKey via GenerateToken) with the purpose of authenticating
|
||||
// the validity of both the message and the private key of the person who generated
|
||||
// the token.
|
||||
func checkToken(message, token, privateKey string) error {
|
||||
tokenBytes, err := base64.URLEncoding.DecodeString(token)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, errors.Fields{"token": token})
|
||||
}
|
||||
|
||||
generatedToken, err := generateToken(message, privateKey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, errors.Fields{"token": token})
|
||||
}
|
||||
|
||||
generatedBytes, err := base64.URLEncoding.DecodeString(generatedToken)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, errors.Fields{"token": token})
|
||||
}
|
||||
|
||||
if !hmac.Equal(tokenBytes, generatedBytes) {
|
||||
return errors.Forbidden(errors.Fields{"token": token}, "invalid token")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateToken generates a hash of the message with the privateKey via the
|
||||
// sha256 algorithm.
|
||||
func generateToken(message, privateKey string) (string, error) {
|
||||
key, err := base64.URLEncoding.DecodeString(privateKey)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, errors.Fields{"msg": message})
|
||||
}
|
||||
|
||||
h := hmac.New(sha256.New, key)
|
||||
h.Write([]byte(message))
|
||||
return base64.URLEncoding.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
53
vendor/github.com/docker/licensing/model/license.go
generated
vendored
53
vendor/github.com/docker/licensing/model/license.go
generated
vendored
@ -1,53 +0,0 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// A CheckResponse is the internal content of the PublicCheckResponse signed
|
||||
// json blob.
|
||||
type CheckResponse struct {
|
||||
Expiration time.Time `json:"expiration"`
|
||||
Token string `json:"token"`
|
||||
MaxEngines int `json:"maxEngines"`
|
||||
ScanningEnabled bool `json:"scanningEnabled"`
|
||||
Type string `json:"licenseType"`
|
||||
Tier string `json:"tier"`
|
||||
|
||||
SubscriptionID string `json:"subscription_id,omitempty"`
|
||||
ProductID string `json:"product_id,omitempty"`
|
||||
RatePlanID string `json:"rate_plan_id,omitempty"`
|
||||
Version int `json:"version"`
|
||||
GraceDays int `json:"grace_days,omitempty"`
|
||||
Metadata *Metadata `json:"metadata,omitempty"`
|
||||
PricingComponents PricingComponents `json:"pricing_components,omitempty"`
|
||||
}
|
||||
|
||||
// Metadata holds non-essential license information, that is, anything that is not required by clients to ensure
|
||||
// the license is valid
|
||||
type Metadata struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Company string `json:"company,omitempty"`
|
||||
}
|
||||
|
||||
// IssuedLicense represents an issued license
|
||||
type IssuedLicense struct {
|
||||
KeyID string `json:"key_id"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
Authorization string `json:"authorization"`
|
||||
}
|
||||
|
||||
// Valid returns true if the License is syntactically valid, false otherwise
|
||||
func (l *IssuedLicense) Valid() (bool, string) {
|
||||
if l.KeyID == "" {
|
||||
return false, "empty key_id"
|
||||
}
|
||||
|
||||
if l.PrivateKey == "" {
|
||||
return false, "empty private_key"
|
||||
}
|
||||
|
||||
if l.Authorization == "" {
|
||||
return false, "empty authorization"
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
202
vendor/github.com/docker/licensing/model/subscriptions.go
generated
vendored
202
vendor/github.com/docker/licensing/model/subscriptions.go
generated
vendored
@ -1,202 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
validation "github.com/docker/licensing/lib/go-validation"
|
||||
"github.com/docker/licensing/types"
|
||||
)
|
||||
|
||||
// PricingComponents represents a collection of pricing components
|
||||
type PricingComponents []*SubscriptionPricingComponent
|
||||
|
||||
func (comps PricingComponents) Len() int { return len(comps) }
|
||||
func (comps PricingComponents) Swap(i, j int) { comps[i], comps[j] = comps[j], comps[i] }
|
||||
|
||||
// always sorting by name
|
||||
func (comps PricingComponents) Less(i, j int) bool { return comps[i].Name < comps[j].Name }
|
||||
|
||||
// Subscription includes the base fields that will be meaningful to most of the clients consuming this api
|
||||
type Subscription struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"subscription_id"`
|
||||
DockerID string `json:"docker_id"`
|
||||
ProductID string `json:"product_id"`
|
||||
ProductRatePlan string `json:"product_rate_plan"`
|
||||
ProductRatePlanID string `json:"product_rate_plan_id"`
|
||||
Start *time.Time `json:"current_period_start,omitempty"`
|
||||
Expires *time.Time `json:"current_period_end,omitempty"`
|
||||
State string `json:"state"`
|
||||
Eusa *EusaState `json:"eusa,omitempty"`
|
||||
PricingComponents PricingComponents `json:"pricing_components"`
|
||||
GraceDays int `json:"grace_days"`
|
||||
}
|
||||
|
||||
// GetFeatureValue returns true if a given feature is among a subscription's pricing component entitlements along with
|
||||
// it's corresponding value and false if it is not found
|
||||
func (s *Subscription) GetFeatureValue(featureName string) (int, bool) {
|
||||
for _, component := range s.PricingComponents {
|
||||
if component.Name == featureName && component.Value > 0 {
|
||||
return component.Value, true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (s *Subscription) String() string {
|
||||
storeURL := "https://docker.com/licensing"
|
||||
|
||||
var nameMsg, expirationMsg, statusMsg string
|
||||
switch types.State(s.State) {
|
||||
case types.Cancelled:
|
||||
statusMsg = fmt.Sprintf("\tCancelled! You will no longer receive updates. To purchase go to %s", storeURL)
|
||||
expirationMsg = fmt.Sprintf("Expiration date: %s", s.Expires.Format("2006-01-02"))
|
||||
case types.Expired:
|
||||
statusMsg = fmt.Sprintf("\tExpired! You will no longer receive updates. Please renew at %s", storeURL)
|
||||
expirationMsg = fmt.Sprintf("Expiration date: %s", s.Expires.Format("2006-01-02"))
|
||||
case types.Preparing:
|
||||
statusMsg = "\tYour subscription has not yet begun"
|
||||
expirationMsg = fmt.Sprintf("Activation date: %s", s.Start.Format("2006-01-02"))
|
||||
case types.Failed:
|
||||
statusMsg = "\tOops, this subscription did not get setup properly!"
|
||||
expirationMsg = ""
|
||||
case types.Active:
|
||||
statusMsg = "\tLicense is currently active"
|
||||
expirationMsg = fmt.Sprintf("Expiration date: %s", s.Expires.Format("2006-01-02"))
|
||||
default:
|
||||
expirationMsg = fmt.Sprintf("Expiration date: %s", s.Expires.Format("2006-01-02"))
|
||||
}
|
||||
|
||||
pcStrs := make([]string, len(s.PricingComponents))
|
||||
for i, pc := range s.PricingComponents {
|
||||
pcStrs[i] = fmt.Sprintf("%d %s", pc.Value, pc.Name)
|
||||
}
|
||||
componentsMsg := "Components: " + strings.Join(pcStrs, ", ")
|
||||
if s.Name != "" {
|
||||
nameMsg = fmt.Sprintf("License Name: %s\t", s.Name)
|
||||
} else if s.ProductRatePlan == "free-trial" {
|
||||
// TODO - consider a humanized formatting for expiration time on trials (e.g., "10 days remaining")
|
||||
nameMsg = "Free trial\t"
|
||||
statusMsg = fmt.Sprintf("\tTo purchase go to %s", storeURL)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s\t%s%s", nameMsg, componentsMsg, expirationMsg, statusMsg)
|
||||
}
|
||||
|
||||
// SubscriptionDetail presents Subscription information to billing service clients.
|
||||
type SubscriptionDetail struct {
|
||||
Subscription
|
||||
Origin string `json:"origin,omitempty"`
|
||||
OrderID string `json:"order_id,omitempty"`
|
||||
OrderItemID string `json:"order_item_id,omitempty"`
|
||||
InitialPeriodStart time.Time `json:"initial_period_start"`
|
||||
CreatedByID string `json:"created_by_docker_id"`
|
||||
|
||||
// If true, the product for this subscription uses product keys. To
|
||||
// obtain the keys, the frontend or billing client will need to
|
||||
// make additional calls to the fulfillment service.
|
||||
UsesProductKeys bool `json:"uses_product_keys,omitempty"`
|
||||
|
||||
// If non-empty, this is a managed subscription, and this identifier is
|
||||
// known to the fulfillment service as a means to uniquely identify the
|
||||
// partner that manages this subscription.
|
||||
//
|
||||
// Different permissions checking will be used to authorize changes and
|
||||
// cancellation; the entity entitled to this subscription (represented
|
||||
// by DockerID) may not change or cancel it directly.
|
||||
ManagingPartnerID string `json:"managing_partner_id,omitempty"`
|
||||
|
||||
// If non-empty, this is a managed subscription, and this ID belongs to the
|
||||
// account of a user within a partner's account system.
|
||||
PartnerAccountID string `json:"partner_account_id,omitempty"`
|
||||
|
||||
// Marketing opt-in for the subscription. This means customer agrees to receive additional marketing emails
|
||||
MarketingOptIn bool `json:"marketing_opt_in"`
|
||||
}
|
||||
|
||||
// SubscriptionPricingComponent captures pricing component values that have been selected by the user.
|
||||
type SubscriptionPricingComponent struct {
|
||||
Name string `json:"name"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
// SubscriptionCreationRequest represents a subscription creation request
|
||||
type SubscriptionCreationRequest struct {
|
||||
Name string `json:"name"`
|
||||
DockerID string `json:"docker_id"`
|
||||
|
||||
ProductID string `json:"product_id"`
|
||||
ProductRatePlan string `json:"product_rate_plan"`
|
||||
Eusa *EusaState `json:"eusa,omitempty"`
|
||||
Origin string `json:"origin,omitempty"`
|
||||
OrderID string `json:"order_id,omitempty"`
|
||||
OrderItemID string `json:"order_item_id,omitempty"`
|
||||
|
||||
End *time.Time `json:"end,omitempty"`
|
||||
Start *time.Time `json:"start,omitempty"`
|
||||
|
||||
CouponCodes []string `json:"coupon_codes"`
|
||||
|
||||
PricingComponents PricingComponents `json:"pricing_components"`
|
||||
|
||||
// If true, the product for this subscription uses product keys. To
|
||||
// obtain the keys, the frontend or billing client will need to
|
||||
// make additional calls to the fulfillment service.
|
||||
UsesProductKeys bool `json:"uses_product_keys,omitempty"`
|
||||
|
||||
// Should be non-empty only if creating a managed subscription that will
|
||||
// be controlled by a partner or publisher. This identifier matches
|
||||
// whatever the fulfillment service uses as guid's for partners.
|
||||
ManagingPartnerID string `json:"managing_partner_id,omitempty"`
|
||||
|
||||
// Should be non-empty only if creating a managed subscription on behalf
|
||||
// of a partner, and this ID represent's a partner's user's account id.
|
||||
PartnerAccountID string `json:"partner_account_id,omitempty"`
|
||||
|
||||
// Marketing opt-in for the subscription. This means customer agrees to receive additional marketing emails
|
||||
MarketingOptIn bool `json:"marketing_opt_in"`
|
||||
}
|
||||
|
||||
// Validate returns true if the subscription request is valid, false otherwise.
|
||||
// If invalid, one or more validation Errors will be returned.
|
||||
func (s *SubscriptionCreationRequest) Validate() (bool, validation.Errors) {
|
||||
var errs validation.Errors
|
||||
|
||||
if validation.IsEmpty(s.Name) {
|
||||
errs = append(errs, validation.InvalidEmpty("name"))
|
||||
}
|
||||
|
||||
if validation.IsEmpty(s.DockerID) {
|
||||
errs = append(errs, validation.InvalidEmpty("docker_id"))
|
||||
}
|
||||
|
||||
if validation.IsEmpty(s.ProductID) {
|
||||
errs = append(errs, validation.InvalidEmpty("product_id"))
|
||||
}
|
||||
|
||||
if validation.IsEmpty(s.ProductRatePlan) {
|
||||
errs = append(errs, validation.InvalidEmpty("product_rate_plan"))
|
||||
}
|
||||
|
||||
for i, component := range s.PricingComponents {
|
||||
if validation.IsEmpty(component.Name) {
|
||||
name := fmt.Sprintf("pricing_component[%v]/name", i)
|
||||
errs = append(errs, validation.InvalidEmpty(name))
|
||||
}
|
||||
}
|
||||
|
||||
valid := len(errs) == 0
|
||||
return valid, errs
|
||||
}
|
||||
|
||||
// EusaState encodes whether the subscription's EUSA has been accepted,
|
||||
// and if so, by whom and when.
|
||||
// See json marshal & unmarshal below.
|
||||
type EusaState struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
AcceptedBy string `json:"accepted_by,omitempty"`
|
||||
AcceptedOn string `json:"accepted_on,omitempty"`
|
||||
}
|
||||
91
vendor/github.com/docker/licensing/model/users.go
generated
vendored
91
vendor/github.com/docker/licensing/model/users.go
generated
vendored
@ -1,91 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/docker/licensing/lib/errors"
|
||||
)
|
||||
|
||||
// User details a Docker user
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
DateJoined time.Time `json:"date_joined"`
|
||||
|
||||
// The user type. Is either 'User' or 'Organization'
|
||||
Type string `json:"type"`
|
||||
|
||||
FullName string `json:"full_name,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
Company string `json:"company,omitempty"`
|
||||
ProfileURL string `json:"profile_url,omitempty"`
|
||||
GravatarURL string `json:"gravatar_url,omitempty"`
|
||||
}
|
||||
|
||||
// Org details a Docker organization
|
||||
type Org struct {
|
||||
ID string `json:"id"`
|
||||
Orgname string `json:"orgname"`
|
||||
DateJoined time.Time `json:"date_joined"`
|
||||
|
||||
Type string `json:"type"`
|
||||
|
||||
FullName string `json:"full_name,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
Company string `json:"company,omitempty"`
|
||||
ProfileURL string `json:"profile_url,omitempty"`
|
||||
GravatarURL string `json:"gravatar_url,omitempty"`
|
||||
}
|
||||
|
||||
// PaginationParams is used for specifying pagination in requests to accounts
|
||||
type PaginationParams struct {
|
||||
PageSize int
|
||||
Page int
|
||||
}
|
||||
|
||||
// PaginatedMeta describes fields contained in a paginated response body
|
||||
type PaginatedMeta struct {
|
||||
Count int `json:"count"`
|
||||
PageSize int `json:"page_size,omitempty"`
|
||||
Next *string `json:"next,omitempty"`
|
||||
Previous *string `json:"previous,omitempty"`
|
||||
}
|
||||
|
||||
// LoginResult holds the response of the login endpoint
|
||||
type LoginResult struct {
|
||||
// JWT associated with the authenticated user
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// LoginRequest holds a hub user's username and password
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// LoginError wraps both the http error and raw hub login error
|
||||
type LoginError struct {
|
||||
*errors.HTTPError
|
||||
// Raw is the raw error from Accounts service
|
||||
Raw *RawLoginError
|
||||
}
|
||||
|
||||
var _ error = (*LoginError)(nil)
|
||||
|
||||
func (e *LoginError) Error() string {
|
||||
msg := e.HTTPError.Error()
|
||||
if e.Raw != nil {
|
||||
msg = fmt.Sprintf("%s (raw: %+v)", msg, e.Raw)
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
// RawLoginError is the raw format of errors returned from the Accounts service.
|
||||
type RawLoginError struct {
|
||||
Detail string `json:"detail,omitempty"`
|
||||
// These fields wil be populated if it's a validation error
|
||||
Username []string `json:"username,omitempty"`
|
||||
Password []string `json:"password,omitempty"`
|
||||
}
|
||||
205
vendor/github.com/docker/licensing/storage.go
generated
vendored
205
vendor/github.com/docker/licensing/storage.go
generated
vendored
@ -1,205 +0,0 @@
|
||||
package licensing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/licensing/model"
|
||||
)
|
||||
|
||||
var (
|
||||
licenseNamePrefix = "com.docker.license"
|
||||
licenseFilename = "docker.lic"
|
||||
|
||||
// ErrWorkerNode returned on a swarm worker node - lookup licenses on swarm managers
|
||||
ErrWorkerNode = fmt.Errorf("this node is not a swarm manager - check license status on a manager node")
|
||||
// ErrUnlicensed returned when no license found
|
||||
ErrUnlicensed = fmt.Errorf("no license found")
|
||||
)
|
||||
|
||||
// WrappedDockerClient provides methods useful for installing licenses to the wrapped docker engine or cluster
|
||||
type WrappedDockerClient interface {
|
||||
Info(ctx context.Context) (types.Info, error)
|
||||
NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error)
|
||||
ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error)
|
||||
ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error)
|
||||
ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error)
|
||||
}
|
||||
|
||||
// StoreLicense will store the license on the host filesystem and swarm (if swarm is active)
|
||||
func StoreLicense(ctx context.Context, clnt WrappedDockerClient, license *model.IssuedLicense, rootDir string) error {
|
||||
|
||||
licenseData, err := json.Marshal(*license)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// First determine if we're in swarm-mode or a stand-alone engine
|
||||
_, err = clnt.NodeList(ctx, types.NodeListOptions{})
|
||||
if err != nil { // TODO - check for the specific error message
|
||||
return writeLicenseToHost(ctx, clnt, licenseData, rootDir)
|
||||
}
|
||||
// Load this in the latest license index
|
||||
latestVersion, err := getLatestNamedConfig(clnt, licenseNamePrefix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get latest license version: %s", err)
|
||||
}
|
||||
spec := swarm.ConfigSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: fmt.Sprintf("%s-%d", licenseNamePrefix, latestVersion+1),
|
||||
Labels: map[string]string{
|
||||
"com.docker.ucp.access.label": "/",
|
||||
"com.docker.ucp.collection": "swarm",
|
||||
"com.docker.ucp.collection.root": "true",
|
||||
"com.docker.ucp.collection.swarm": "true",
|
||||
},
|
||||
},
|
||||
Data: licenseData,
|
||||
}
|
||||
_, err = clnt.ConfigCreate(context.Background(), spec)
|
||||
if err != nil {
|
||||
|
||||
return fmt.Errorf("Failed to create license: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) LoadLocalLicense(ctx context.Context, clnt WrappedDockerClient) (*model.Subscription, error) {
|
||||
info, err := clnt.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var licenseData []byte
|
||||
if info.Swarm.LocalNodeState != "active" {
|
||||
licenseData, err = readLicenseFromHost(ctx, info.DockerRootDir)
|
||||
} else {
|
||||
// Load the latest license index
|
||||
var latestVersion int
|
||||
|
||||
// check if node is swarm manager
|
||||
if !info.Swarm.ControlAvailable {
|
||||
return nil, ErrWorkerNode
|
||||
}
|
||||
|
||||
latestVersion, err = getLatestNamedConfig(clnt, licenseNamePrefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get latest license version: %s", err)
|
||||
}
|
||||
if latestVersion >= 0 {
|
||||
cfg, _, err := clnt.ConfigInspectWithRaw(ctx, fmt.Sprintf("%s-%d", licenseNamePrefix, latestVersion))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load license from swarm config: %s", err)
|
||||
}
|
||||
licenseData = cfg.Spec.Data
|
||||
} else {
|
||||
licenseData, err = readLicenseFromHost(ctx, info.DockerRootDir)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, ErrUnlicensed
|
||||
}
|
||||
return nil, fmt.Errorf("Failed to create license: %s", err)
|
||||
}
|
||||
|
||||
parsedLicense, err := c.ParseLicense(licenseData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
checkResponse, err := c.VerifyLicense(ctx, *parsedLicense)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return checkResponseToSubscription(checkResponse), nil
|
||||
}
|
||||
|
||||
func checkResponseToSubscription(checkResponse *model.CheckResponse) *model.Subscription {
|
||||
// Determine if the license has already expired
|
||||
var state string
|
||||
if checkResponse.Expiration.Before(time.Now()) {
|
||||
state = "expired"
|
||||
} else {
|
||||
state = "active"
|
||||
}
|
||||
|
||||
// For backward compatibility, show information about old (per node) licenses
|
||||
components := checkResponse.PricingComponents
|
||||
if checkResponse.MaxEngines > 0 {
|
||||
components = append(components, &model.SubscriptionPricingComponent{
|
||||
Name: "Nodes",
|
||||
Value: checkResponse.MaxEngines,
|
||||
})
|
||||
}
|
||||
|
||||
// Translate the legacy structure into the new Subscription fields
|
||||
return &model.Subscription{
|
||||
ID: checkResponse.SubscriptionID,
|
||||
ProductID: checkResponse.ProductID,
|
||||
ProductRatePlanID: checkResponse.RatePlanID,
|
||||
Expires: &checkResponse.Expiration,
|
||||
State: state,
|
||||
PricingComponents: components,
|
||||
GraceDays: checkResponse.GraceDays,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) SummarizeLicense(checkResponse *model.CheckResponse) *model.Subscription {
|
||||
return checkResponseToSubscription(checkResponse)
|
||||
}
|
||||
|
||||
// getLatestNamedConfig looks for versioned instances of configs with the
|
||||
// given name prefix which have a `-NUM` integer version suffix. Returns the
|
||||
// config with the higest version number found or nil if no such configs exist
|
||||
// along with its version number.
|
||||
func getLatestNamedConfig(dclient WrappedDockerClient, namePrefix string) (int, error) {
|
||||
latestVersion := -1
|
||||
// List any/all existing configs so that we create a newer version than
|
||||
// any that already exist.
|
||||
filter := filters.NewArgs()
|
||||
filter.Add("name", namePrefix)
|
||||
existingConfigs, err := dclient.ConfigList(context.Background(), types.ConfigListOptions{Filters: filter})
|
||||
if err != nil {
|
||||
return latestVersion, fmt.Errorf("unable to list existing configs: %s", err)
|
||||
}
|
||||
|
||||
for _, existingConfig := range existingConfigs {
|
||||
existingConfigName := existingConfig.Spec.Name
|
||||
nameSuffix := strings.TrimPrefix(existingConfigName, namePrefix)
|
||||
if nameSuffix == "" || nameSuffix[0] != '-' {
|
||||
continue // No version specifier?
|
||||
}
|
||||
|
||||
versionSuffix := nameSuffix[1:] // Trim the version separator.
|
||||
existingVersion, err := strconv.Atoi(versionSuffix)
|
||||
if err != nil {
|
||||
continue // Unable to parse version as integer.
|
||||
}
|
||||
if existingVersion > latestVersion {
|
||||
latestVersion = existingVersion
|
||||
}
|
||||
}
|
||||
|
||||
return latestVersion, nil
|
||||
}
|
||||
|
||||
func writeLicenseToHost(ctx context.Context, dclient WrappedDockerClient, license []byte, rootDir string) error {
|
||||
// TODO we should write the file out over the clnt instead of to the local filesystem
|
||||
return ioutil.WriteFile(filepath.Join(rootDir, licenseFilename), license, 0644)
|
||||
}
|
||||
|
||||
func readLicenseFromHost(ctx context.Context, rootDir string) ([]byte, error) {
|
||||
// TODO we should read the file in over the clnt instead of to the local filesystem
|
||||
return ioutil.ReadFile(filepath.Join(rootDir, licenseFilename))
|
||||
}
|
||||
75
vendor/github.com/docker/licensing/subscriptions.go
generated
vendored
75
vendor/github.com/docker/licensing/subscriptions.go
generated
vendored
@ -1,75 +0,0 @@
|
||||
package licensing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/licensing/lib/go-clientlib"
|
||||
"github.com/docker/licensing/model"
|
||||
)
|
||||
|
||||
// RequestParams holds request parameters
|
||||
type RequestParams struct {
|
||||
DockerID string
|
||||
PartnerAccountID string
|
||||
Origin string
|
||||
}
|
||||
|
||||
func (c *client) createSubscription(ctx context.Context, request *model.SubscriptionCreationRequest) (*model.SubscriptionDetail, error) {
|
||||
url := c.baseURI
|
||||
url.Path += "/api/billing/v4/subscriptions"
|
||||
response := new(model.SubscriptionDetail)
|
||||
if _, _, err := c.doReq(ctx, "POST", &url, clientlib.SendJSON(request), clientlib.RecvJSON(response)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *client) getSubscription(ctx context.Context, id string) (*model.SubscriptionDetail, error) {
|
||||
url := c.baseURI
|
||||
url.Path += "/api/billing/v4/subscriptions/" + id
|
||||
response := new(model.SubscriptionDetail)
|
||||
if _, _, err := c.doReq(ctx, "GET", &url, clientlib.RecvJSON(response)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *client) listSubscriptions(ctx context.Context, params map[string]string) ([]*model.Subscription, error) {
|
||||
values := url.Values{}
|
||||
values.Set("docker_id", params["docker_id"])
|
||||
values.Set("partner_account_id", params["partner_account_id"])
|
||||
values.Set("origin", params["origin"])
|
||||
values.Set("include_orgs", "true")
|
||||
|
||||
url := c.baseURI
|
||||
url.Path += "/api/billing/v4/subscriptions"
|
||||
url.RawQuery = values.Encode()
|
||||
|
||||
response := make([]*model.Subscription, 0)
|
||||
if _, _, err := c.doReq(ctx, "GET", &url, clientlib.RecvJSON(&response)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *client) listSubscriptionsDetails(ctx context.Context, params map[string]string) ([]*model.SubscriptionDetail, error) {
|
||||
values := url.Values{}
|
||||
values.Set("docker_id", params["docker_id"])
|
||||
values.Set("partner_account_id", params["partner_account_id"])
|
||||
values.Set("origin", params["origin"])
|
||||
|
||||
url := c.baseURI
|
||||
url.Path += "/api/billing/v4/subscriptions"
|
||||
url.RawQuery = values.Encode()
|
||||
|
||||
response := make([]*model.SubscriptionDetail, 0)
|
||||
if _, _, err := c.doReq(ctx, "GET", &url, clientlib.RecvJSON(&response)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
17
vendor/github.com/docker/licensing/types/types.go
generated
vendored
17
vendor/github.com/docker/licensing/types/types.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
package types
|
||||
|
||||
// State represents a given subscription's current status
|
||||
type State string
|
||||
|
||||
const (
|
||||
// Active means a subscription is currently in a working, live state
|
||||
Active State = "active"
|
||||
// Expired means a subscription's end date is in the past
|
||||
Expired State = "expired"
|
||||
// Cancelled means the subscription has been cancelled
|
||||
Cancelled State = "cancelled"
|
||||
// Preparing means that the subscription's payment (if any) is being still processed
|
||||
Preparing State = "preparing"
|
||||
// Failed means that there was a problem creating the subscription
|
||||
Failed State = "failed"
|
||||
)
|
||||
90
vendor/github.com/docker/licensing/users.go
generated
vendored
90
vendor/github.com/docker/licensing/users.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
package licensing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/licensing/lib/errors"
|
||||
"github.com/docker/licensing/lib/go-clientlib"
|
||||
"github.com/docker/licensing/model"
|
||||
)
|
||||
|
||||
func (c *client) getUserByName(ctx context.Context, username string) (*model.User, error) {
|
||||
url := c.baseURI
|
||||
url.Path += fmt.Sprintf("/v2/users/%s/", username)
|
||||
response := new(model.User)
|
||||
_, _, err := c.doRequestNoAuth(ctx, "GET", &url, clientlib.RecvJSON(response))
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (c *client) getUserOrgs(ctx context.Context, params model.PaginationParams) ([]model.Org, error) {
|
||||
values := url.Values{}
|
||||
|
||||
if params.PageSize != 0 {
|
||||
values.Set("page_size",
|
||||
fmt.Sprintf("%v", params.PageSize))
|
||||
}
|
||||
|
||||
if params.Page != 0 {
|
||||
values.Set("page",
|
||||
fmt.Sprintf("%v", params.Page))
|
||||
}
|
||||
|
||||
requrl := c.baseURI
|
||||
requrl.Path = "/v2/user/orgs/"
|
||||
requrl.RawQuery = values.Encode()
|
||||
|
||||
var response struct {
|
||||
model.PaginatedMeta
|
||||
Results []model.Org `json:"results"`
|
||||
}
|
||||
_, _, err := c.doReq(ctx, "GET", &requrl, clientlib.RecvJSON(&response))
|
||||
return response.Results, err
|
||||
}
|
||||
|
||||
// login calls the login endpoint
|
||||
// If an error is returned by the Accounts service (as opposed to connection error, json unmarshalling error etc.),
|
||||
// `error` will be of type `LoginError`
|
||||
func (c *client) login(ctx context.Context, username string, password string) (*model.LoginResult, error) {
|
||||
url := c.baseURI
|
||||
url.Path += "/v2/users/login/"
|
||||
request := model.LoginRequest{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
response := new(model.LoginResult)
|
||||
_, _, err := c.doRequestNoAuth(ctx, "POST", &url, clientlib.SendJSON(request), clientlib.RecvJSON(response), loginErrorCheckOpt)
|
||||
return response, err
|
||||
}
|
||||
|
||||
// loginErrorCheckOpt works similarly to `clientlib.DefaultErrorCheck`, except it parses the error response
|
||||
func loginErrorCheckOpt(r *clientlib.Request) {
|
||||
r.ErrorCheck = func(r *clientlib.Request, doErr error, res *http.Response) error {
|
||||
if doErr != nil {
|
||||
return errors.Wrap(doErr, r.ErrorFields())
|
||||
}
|
||||
status := res.StatusCode
|
||||
if status >= 200 && status < 300 {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
lError := new(model.LoginError)
|
||||
|
||||
message := fmt.Sprintf("%s %s returned %d", r.Method, r.URL.String(), status)
|
||||
lError.HTTPError = errors.NewHTTPError(status, message).
|
||||
With(r.ErrorFields())
|
||||
|
||||
var rawLoginErr model.RawLoginError
|
||||
err := json.NewDecoder(res.Body).Decode(&rawLoginErr)
|
||||
if err == nil {
|
||||
lError.Raw = &rawLoginErr
|
||||
}
|
||||
|
||||
return lError
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user