mirror of
https://github.com/wahyd4/cert-manager.git
synced 2026-08-09 05:06:38 +10:00
Merge pull request #4007 from edglynes/3904_v2_API
Update Aka issuer to use v2 API
This commit is contained in:
@@ -417,6 +417,215 @@
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang licensed under: =
|
||||
|
||||
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
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
|
||||
= vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/LICENSE e3fc50a88d0a364313df4b21ef20c29e
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/aws/aws-sdk-go licensed under: =
|
||||
|
||||
@@ -11895,6 +12104,33 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/patrickmn/go-cache licensed under: =
|
||||
|
||||
Copyright (c) 2012-2017 Patrick Mylund Nielsen and the go-cache contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
= vendor/github.com/patrickmn/go-cache/LICENSE ad59632c93a4b0296b360ba542decb5e
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/pavel-v-chernykh/keystore-go licensed under: =
|
||||
|
||||
@@ -13104,6 +13340,35 @@ SOFTWARE.
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/sirupsen/logrus licensed under: =
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Simon Eskildsen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
= vendor/github.com/sirupsen/logrus/LICENSE 8dadfef729c08ec4e631c4f6fc5d43a0
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/spf13/cobra licensed under: =
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ require (
|
||||
github.com/Azure/go-autorest/autorest/to v0.2.0
|
||||
github.com/Azure/go-autorest/autorest/validation v0.1.0 // indirect
|
||||
github.com/Venafi/vcert/v4 v4.13.1
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0
|
||||
github.com/aws/aws-sdk-go v1.34.30
|
||||
github.com/cloudflare/cloudflare-go v0.13.2
|
||||
github.com/cpu/goacmedns v0.0.3
|
||||
|
||||
@@ -88,6 +88,8 @@ github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vaj
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0 h1:NNsy0ugDFZbu6S11NlsR4Kmko9hJ00gv9U2JHbwpf3g=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -454,6 +456,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78=
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
@@ -669,6 +673,8 @@ github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
@@ -708,6 +714,8 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible h1:Jd6xfriVlJ6hWPvYOE0Ni0QWcNTLRehfGPFxr3eSL80=
|
||||
github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible/go.mod h1:xlUlxe/2ItGlQyMTstqeDv9r3U4obH7xYd26TbDQutY=
|
||||
github.com/pavel-v-chernykh/keystore-go/v4 v4.1.0 h1:xKxUVGoB9VJU+lgQLPN0KURjw+XCVVSpHfQEeyxk3zo=
|
||||
@@ -863,6 +871,12 @@ github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/vektah/gqlparser v1.1.2 h1:ZsyLGn7/7jDNI+y4SEhI4yAxRChlv15pUHMjijT+e68=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||
@@ -1178,10 +1192,13 @@ gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho=
|
||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
|
||||
|
||||
+63
-2
@@ -59,6 +59,15 @@ def go_repositories():
|
||||
version = "v1.0.1",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_akamai_AkamaiOPEN-edgegrid-golang",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "github.com/akamai/AkamaiOPEN-edgegrid-golang",
|
||||
sum = "h1:NNsy0ugDFZbu6S11NlsR4Kmko9hJ00gv9U2JHbwpf3g=",
|
||||
version = "v1.1.0",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_alecthomas_template",
|
||||
build_file_generation = "on",
|
||||
@@ -1311,6 +1320,15 @@ def go_repositories():
|
||||
sum = "h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=",
|
||||
version = "v1.9.5",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_h2non_parth",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "github.com/h2non/parth",
|
||||
sum = "h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=",
|
||||
version = "v0.0.0-20190131123155-b4df798d6542",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_hailocab_go_hostpool",
|
||||
build_file_generation = "on",
|
||||
@@ -2121,6 +2139,15 @@ def go_repositories():
|
||||
sum = "h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=",
|
||||
version = "v1.0.1",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_nbio_st",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "github.com/nbio/st",
|
||||
sum = "h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=",
|
||||
version = "v0.0.0-20140626010706-e9e8d9816f32",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_niemeyer_pretty",
|
||||
build_file_generation = "on",
|
||||
@@ -2865,6 +2892,31 @@ def go_repositories():
|
||||
sum = "h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=",
|
||||
version = "v1.0.0",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_xeipuuv_gojsonpointer",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "github.com/xeipuuv/gojsonpointer",
|
||||
sum = "h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=",
|
||||
version = "v0.0.0-20180127040702-4e3ac2762d5f",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_xeipuuv_gojsonreference",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "github.com/xeipuuv/gojsonreference",
|
||||
sum = "h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=",
|
||||
version = "v0.0.0-20180127040603-bd5ef7bd5415",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_xeipuuv_gojsonschema",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "github.com/xeipuuv/gojsonschema",
|
||||
sum = "h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=",
|
||||
version = "v1.2.0",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_xiang90_probing",
|
||||
build_file_generation = "on",
|
||||
@@ -3043,6 +3095,15 @@ def go_repositories():
|
||||
sum = "h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs=",
|
||||
version = "v1.2.3",
|
||||
)
|
||||
go_repository(
|
||||
name = "in_gopkg_h2non_gock_v1",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "gopkg.in/h2non/gock.v1",
|
||||
sum = "h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=",
|
||||
version = "v1.0.15",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "in_gopkg_inf_v0",
|
||||
build_file_generation = "on",
|
||||
@@ -3056,8 +3117,8 @@ def go_repositories():
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "gopkg.in/ini.v1",
|
||||
sum = "h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=",
|
||||
version = "v1.51.0",
|
||||
sum = "h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho=",
|
||||
version = "v1.51.1",
|
||||
)
|
||||
go_repository(
|
||||
name = "in_gopkg_mgo_v2",
|
||||
|
||||
@@ -2,16 +2,14 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"akamai.go",
|
||||
"edgegridauth.go",
|
||||
],
|
||||
srcs = ["akamai.go"],
|
||||
importpath = "github.com/jetstack/cert-manager/pkg/issuer/acme/dns/akamai",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/issuer/acme/dns/util:go_default_library",
|
||||
"//pkg/logs:go_default_library",
|
||||
"//pkg/util:go_default_library",
|
||||
"@com_github_akamai_AkamaiOPEN-edgegrid-golang//configdns-v2:go_default_library",
|
||||
"@com_github_akamai_AkamaiOPEN-edgegrid-golang//edgegrid:go_default_library",
|
||||
"@com_github_go_logr_logr//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
],
|
||||
@@ -19,13 +17,11 @@ go_library(
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"akamai_test.go",
|
||||
"edgegridauth_test.go",
|
||||
],
|
||||
srcs = ["akamai_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/issuer/acme/dns/util:go_default_library",
|
||||
"@com_github_akamai_AkamaiOPEN-edgegrid-golang//configdns-v2:go_default_library",
|
||||
"@com_github_stretchr_testify//assert:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -15,51 +15,76 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
// Package akamai implements a DNS provider for solving the DNS-01
|
||||
// challenge using Akamai FastDNS.
|
||||
// See https://developer.akamai.com/api/luna/config-dns/overview.html
|
||||
// challenge using Akamai Edge DNS.
|
||||
// See https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html
|
||||
|
||||
package akamai
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
dns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v2"
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
pkgutil "github.com/jetstack/cert-manager/pkg/util"
|
||||
)
|
||||
|
||||
// Interface defined to enable mocking and required functions
|
||||
type OpenEdgegridDNSService interface {
|
||||
GetRecord(zone string, name string, record_type string) (*dns.RecordBody, error)
|
||||
RecordSave(rec *dns.RecordBody, zone string) error
|
||||
RecordUpdate(rec *dns.RecordBody, zone string) error
|
||||
RecordDelete(rec *dns.RecordBody, zone string) error
|
||||
}
|
||||
|
||||
type OpenDNSConfig struct {
|
||||
config edgegrid.Config
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
dns01Nameservers []string
|
||||
// serviceConsumerDomain as issued by Akamai Luna Control Center.
|
||||
// The ServiceConsumerDomain is the base URL.
|
||||
serviceConsumerDomain string
|
||||
|
||||
auth *EdgeGridAuth
|
||||
|
||||
transport http.RoundTripper
|
||||
dns01Nameservers []string
|
||||
serviceConsumerDomain string
|
||||
dnsclient OpenEdgegridDNSService
|
||||
TTL int
|
||||
findHostedDomainByFqdn func(string, []string) (string, error)
|
||||
isNotFound func(error) bool
|
||||
log logr.Logger
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for Akamai.
|
||||
func NewDNSProvider(serviceConsumerDomain, clientToken, clientSecret, accessToken string, dns01Nameservers []string) (*DNSProvider, error) {
|
||||
return &DNSProvider{
|
||||
dns01Nameservers,
|
||||
serviceConsumerDomain,
|
||||
NewEdgeGridAuth(clientToken, clientSecret, accessToken),
|
||||
http.DefaultTransport,
|
||||
findHostedDomainByFqdn,
|
||||
logf.Log.WithName("akamai-dns"),
|
||||
}, nil
|
||||
|
||||
// required Aka OpenEdgegrid creds + non empty dnsservers list
|
||||
if serviceConsumerDomain == "" || clientToken == "" || clientSecret == "" || accessToken == "" || len(dns01Nameservers) < 1 {
|
||||
return nil, fmt.Errorf("edgedns: Provider creation failed. Missing required arguments.")
|
||||
}
|
||||
|
||||
dnsp := &DNSProvider{
|
||||
dns01Nameservers: dns01Nameservers,
|
||||
serviceConsumerDomain: serviceConsumerDomain,
|
||||
dnsclient: &OpenDNSConfig{},
|
||||
findHostedDomainByFqdn: findHostedDomainByFqdn,
|
||||
isNotFound: isNotFound,
|
||||
log: logf.Log.WithName("akamai-dns"),
|
||||
TTL: 300,
|
||||
}
|
||||
dnsp.dnsclient.(*OpenDNSConfig).config = edgegrid.Config{
|
||||
Host: serviceConsumerDomain,
|
||||
ClientToken: clientToken,
|
||||
ClientSecret: clientSecret,
|
||||
AccessToken: accessToken,
|
||||
MaxBody: 131072,
|
||||
}
|
||||
|
||||
dns.Init(dnsp.dnsclient.(*OpenDNSConfig).config)
|
||||
|
||||
return dnsp, nil
|
||||
}
|
||||
|
||||
func findHostedDomainByFqdn(fqdn string, ns []string) (string, error) {
|
||||
@@ -71,218 +96,186 @@ func findHostedDomainByFqdn(fqdn string, ns []string) (string, error) {
|
||||
return util.UnFqdn(zone), nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||
// Present creates/updates a TXT record to fulfill the dns-01 challenge.
|
||||
func (a *DNSProvider) Present(domain, fqdn, value string) error {
|
||||
return a.setTxtRecord(fqdn, &dns01Record{value, 60})
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (a *DNSProvider) CleanUp(domain, fqdn, value string) error {
|
||||
return a.setTxtRecord(fqdn, nil)
|
||||
}
|
||||
logf.V(logf.DebugLevel).Infof("entering Present. domain: %s, fqdn: %s, value: %s", domain, fqdn, value)
|
||||
|
||||
type dns01Record struct {
|
||||
value string
|
||||
ttl int
|
||||
}
|
||||
|
||||
func (a *DNSProvider) setTxtRecord(fqdn string, dns01Record *dns01Record) error {
|
||||
hostedDomain, err := a.findHostedDomainByFqdn(fqdn, a.dns01Nameservers)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to determine hosted domain for %q", fqdn)
|
||||
}
|
||||
|
||||
zoneData, err := a.loadZoneData(hostedDomain)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to load zone data for %q", hostedDomain)
|
||||
return errors.Wrapf(err, "edgedns: failed to determine hosted domain for %q", fqdn)
|
||||
}
|
||||
hostedDomain = util.UnFqdn(hostedDomain)
|
||||
logf.V(logf.DebugLevel).Infof("hostedDomain: %s", hostedDomain)
|
||||
|
||||
recordName, err := makeTxtRecordName(fqdn, hostedDomain)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create TXT record name")
|
||||
return errors.Wrapf(err, "edgedns: failed to create TXT record name")
|
||||
}
|
||||
logf.V(logf.DebugLevel).Infof("recordName: %s", recordName)
|
||||
|
||||
record, err := a.dnsclient.GetRecord(hostedDomain, recordName, "TXT")
|
||||
if err != nil && !a.isNotFound(err) {
|
||||
return errors.Wrapf(err, "edgedns: failed to retrieve TXT record")
|
||||
}
|
||||
|
||||
if updated, err := zoneData.setTxtRecord(recordName, dns01Record); !updated || err != nil {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to set TXT record in %q", hostedDomain)
|
||||
if err == nil && record == nil {
|
||||
return fmt.Errorf("edgedns: unknown error")
|
||||
}
|
||||
|
||||
if record != nil {
|
||||
logf.V(logf.InfoLevel).Infof("edgedns: TXT record already exists. Updating target")
|
||||
|
||||
if containsValue(record.Target, value) {
|
||||
// have a record and have entry already
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Errorf("no %q TXT record found in %q", recordName, hostedDomain)
|
||||
record.Target = append(record.Target, `"`+value+`"`)
|
||||
record.TTL = a.TTL
|
||||
|
||||
err = a.dnsclient.RecordUpdate(record, hostedDomain)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "edgedns: failed to update TXT record")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
newSerial, err := zoneData.incSoaSerial()
|
||||
record = &dns.RecordBody{
|
||||
Name: recordName,
|
||||
RecordType: "TXT",
|
||||
TTL: a.TTL,
|
||||
Target: []string{`"` + value + `"`},
|
||||
}
|
||||
|
||||
err = a.dnsclient.RecordSave(record, hostedDomain)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to increment SOA serial for %q", hostedDomain)
|
||||
return errors.Wrapf(err, "edgedns: failed to create TXT record")
|
||||
}
|
||||
|
||||
if err := a.saveZoneData(hostedDomain, zoneData); err != nil {
|
||||
return errors.Wrapf(err, "failed to save zone data for %q", hostedDomain)
|
||||
}
|
||||
|
||||
logf.V(logf.DebugLevel).Infof("Updated Akamai TXT record for %q on %q using SOA serial of %d", recordName, hostedDomain, newSerial)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes/updates the TXT record matching the specified parameters.
|
||||
func (a *DNSProvider) CleanUp(domain, fqdn, value string) error {
|
||||
|
||||
logf.V(logf.DebugLevel).Infof("entering CleanUp. domain: %s, fqdn: %s, value: %s", domain, fqdn, value)
|
||||
|
||||
hostedDomain, err := a.findHostedDomainByFqdn(fqdn, a.dns01Nameservers)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "edgedns: failed to determine hosted domain for %q", fqdn)
|
||||
}
|
||||
hostedDomain = util.UnFqdn(hostedDomain)
|
||||
logf.V(logf.DebugLevel).Infof("hostedDomain: %s", hostedDomain)
|
||||
|
||||
recordName, err := makeTxtRecordName(fqdn, hostedDomain)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "edgedns: failed to create TXT record name")
|
||||
}
|
||||
logf.V(logf.DebugLevel).Infof("recordName: %s", recordName)
|
||||
|
||||
existingRec, err := a.dnsclient.GetRecord(hostedDomain, recordName, "TXT")
|
||||
if err != nil {
|
||||
if a.isNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrapf(err, "edgedns: failed to retrieve TXT record")
|
||||
}
|
||||
|
||||
if existingRec == nil {
|
||||
return fmt.Errorf("edgedns: unknown failure")
|
||||
}
|
||||
|
||||
if len(existingRec.Target) == 0 {
|
||||
return fmt.Errorf("edgedns: TXT record is invalid")
|
||||
}
|
||||
|
||||
if !containsValue(existingRec.Target, value) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var newRData []string
|
||||
for _, val := range existingRec.Target {
|
||||
tval := strings.Trim(val, `"`)
|
||||
if tval == value {
|
||||
continue
|
||||
}
|
||||
newRData = append(newRData, val)
|
||||
}
|
||||
|
||||
if len(newRData) > 0 {
|
||||
existingRec.Target = newRData
|
||||
logf.V(logf.DebugLevel).Infof("updating Akamai TXT record: %s, data: %s", existingRec.Name, newRData)
|
||||
err = a.dnsclient.RecordUpdate(existingRec, hostedDomain)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "edgedns: TXT record update failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
logf.V(logf.DebugLevel).Infof("deleting Akamai TXT record %s", existingRec.Name)
|
||||
err = a.dnsclient.RecordDelete(existingRec, hostedDomain)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "edgedns: TXT record delete failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func containsValue(values []string, value string) bool {
|
||||
for _, val := range values {
|
||||
if strings.Trim(val, `"`) == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isNotFound(err error) bool {
|
||||
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := err.(*dns.RecordError)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func makeTxtRecordName(fqdn, hostedDomain string) (string, error) {
|
||||
if !strings.HasSuffix(fqdn, "."+hostedDomain+".") {
|
||||
|
||||
recName := util.UnFqdn(fqdn)
|
||||
if !strings.HasSuffix(recName, hostedDomain) {
|
||||
return "", errors.Errorf("fqdn %q is not part of %q", fqdn, hostedDomain)
|
||||
}
|
||||
|
||||
return fqdn[0 : len(fqdn)-len(hostedDomain)-2], nil
|
||||
return recName, nil
|
||||
}
|
||||
|
||||
func (a *DNSProvider) urlForDomain(domain string) string {
|
||||
return fmt.Sprintf("https://%s/config-dns/v1/zones/%s", a.serviceConsumerDomain, domain)
|
||||
func (o OpenDNSConfig) GetRecord(zone string, name string, record_type string) (*dns.RecordBody, error) {
|
||||
|
||||
dns.Config = o.config
|
||||
|
||||
return dns.GetRecord(zone, name, record_type)
|
||||
}
|
||||
|
||||
func (a *DNSProvider) loadZoneData(domain string) (zoneData, error) {
|
||||
url := a.urlForDomain(domain)
|
||||
req, err := http.NewRequest(http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create HTTP request")
|
||||
}
|
||||
func (o OpenDNSConfig) RecordSave(rec *dns.RecordBody, zone string) error {
|
||||
|
||||
responsePayload, err := a.makeRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var zoneData map[string]interface{}
|
||||
err = json.NewDecoder(bytes.NewReader(responsePayload)).Decode(&zoneData)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode Akamai OPEN API response")
|
||||
}
|
||||
|
||||
return zoneData, nil
|
||||
return rec.Save(zone)
|
||||
}
|
||||
|
||||
func (a *DNSProvider) saveZoneData(domain string, data zoneData) error {
|
||||
body, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to encode zone data")
|
||||
}
|
||||
func (o OpenDNSConfig) RecordUpdate(rec *dns.RecordBody, zone string) error {
|
||||
|
||||
url := a.urlForDomain(domain)
|
||||
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create HTTP request")
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
if _, err := a.makeRequest(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return rec.Update(zone)
|
||||
}
|
||||
|
||||
func (a *DNSProvider) makeRequest(req *http.Request) ([]byte, error) {
|
||||
req.Header.Set("User-Agent", pkgutil.CertManagerUserAgent)
|
||||
func (o OpenDNSConfig) RecordDelete(rec *dns.RecordBody, zone string) error {
|
||||
|
||||
if err := a.auth.SignRequest(req); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to sign HTTP request")
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Transport: a.transport,
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error querying Akamai OPEN API")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNoContent {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected response from Akamai OPEN API: status_code: %d, status: %s", resp.StatusCode, resp.Status)
|
||||
}
|
||||
|
||||
responsePayload, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read response payload")
|
||||
}
|
||||
|
||||
return responsePayload, nil
|
||||
}
|
||||
|
||||
type zoneData map[string]interface{}
|
||||
|
||||
func (z zoneData) setTxtRecord(name string, dns01Record *dns01Record) (bool, error) {
|
||||
zone, ok := z["zone"].(map[string]interface{})
|
||||
if !ok {
|
||||
return false, errors.New("failed to retrieve zone from zone data")
|
||||
}
|
||||
|
||||
var txtRecords []interface{}
|
||||
if txtNode, ok := zone["txt"]; ok {
|
||||
if txtRecords, ok = txtNode.([]interface{}); !ok {
|
||||
return false, errors.New("failed to retrieve TXT records from zone data")
|
||||
}
|
||||
}
|
||||
|
||||
if dns01Record == nil {
|
||||
if txtRecords = deleteRecord(txtRecords, name); txtRecords == nil {
|
||||
return false, nil
|
||||
}
|
||||
} else {
|
||||
txtRecords = updateRecord(txtRecords, name, map[string]interface{}{
|
||||
"name": name,
|
||||
"ttl": dns01Record.ttl,
|
||||
"active": true,
|
||||
"target": dns01Record.value,
|
||||
})
|
||||
}
|
||||
|
||||
if len(txtRecords) < 1 {
|
||||
delete(zone, "txt")
|
||||
} else {
|
||||
zone["txt"] = txtRecords
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (z zoneData) incSoaSerial() (uint64, error) {
|
||||
soa, ok := z["zone"].(map[string]interface{})["soa"].(map[string]interface{})
|
||||
if !ok {
|
||||
return 0, errors.New("failed to retrieve SOA record from zone data")
|
||||
}
|
||||
|
||||
serial, ok := soa["serial"].(float64)
|
||||
if !ok {
|
||||
return 0, errors.New("failed to retrieve SOA serial from zone data")
|
||||
}
|
||||
|
||||
newSerial := uint64(serial) + 1
|
||||
soa["serial"] = newSerial
|
||||
return newSerial, nil
|
||||
}
|
||||
|
||||
func deleteRecord(records []interface{}, name string) []interface{} {
|
||||
for pos := range records {
|
||||
if recordName, ok := records[pos].(map[string]interface{})["name"]; ok && recordName == name {
|
||||
return append(records[:pos], records[pos+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateRecord(records []interface{}, name string, record map[string]interface{}) []interface{} {
|
||||
for pos := range records {
|
||||
if records[pos].(map[string]interface{})["name"] == name {
|
||||
records[pos] = record
|
||||
return records
|
||||
}
|
||||
}
|
||||
|
||||
return append(records, record)
|
||||
return rec.Delete(zone)
|
||||
}
|
||||
|
||||
@@ -17,164 +17,395 @@ limitations under the License.
|
||||
package akamai
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
dns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v2"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const sampleZoneData = `{
|
||||
"token": "a184671d5307a388180fbf7f11dbdf46",
|
||||
"zone": {
|
||||
"name": "example.com",
|
||||
"soa": {
|
||||
"contact": "hostmaster.akamai.com.",
|
||||
"expire": 604800,
|
||||
"minimum": 180,
|
||||
"originserver": "use4.akamai.com.",
|
||||
"refresh": 900,
|
||||
"retry": 300,
|
||||
"serial": 1271354824,
|
||||
"ttl": 900
|
||||
},
|
||||
"ns": [
|
||||
{
|
||||
"active": true,
|
||||
"name": "",
|
||||
"target": "use4.akam.net.",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"name": "",
|
||||
"target": "use3.akam.net.",
|
||||
"ttl": 3600
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
func testRecordBodyData() *dns.RecordBody {
|
||||
|
||||
const sampleZoneDataWithTxt = `{
|
||||
"token": "a184671d5307a388180fbf7f11dbdf46",
|
||||
"zone": {
|
||||
"name": "example.com",
|
||||
"soa": {
|
||||
"contact": "hostmaster.akamai.com.",
|
||||
"expire": 604800,
|
||||
"minimum": 180,
|
||||
"originserver": "use4.akamai.com.",
|
||||
"refresh": 900,
|
||||
"retry": 300,
|
||||
"serial": 1271354825,
|
||||
"ttl": 900
|
||||
},
|
||||
"ns": [
|
||||
{
|
||||
"active": true,
|
||||
"name": "",
|
||||
"target": "use4.akam.net.",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"name": "",
|
||||
"target": "use3.akam.net.",
|
||||
"ttl": 3600
|
||||
}
|
||||
],
|
||||
"txt": [
|
||||
{
|
||||
"active": true,
|
||||
"name" :"_acme-challenge.test",
|
||||
"target": "dns01-key",
|
||||
"ttl": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
type httpResponder func(req *http.Request) (*http.Response, error)
|
||||
|
||||
func (r httpResponder) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return r(req)
|
||||
return &dns.RecordBody{
|
||||
Name: "_acme-challenge.test.example.com",
|
||||
RecordType: "TXT",
|
||||
Target: []string{`"` + "dns01-key" + `"`},
|
||||
TTL: 300,
|
||||
}
|
||||
}
|
||||
|
||||
func TestPresent(t *testing.T) {
|
||||
func testRecordBodyDataExist() *dns.RecordBody {
|
||||
|
||||
return &dns.RecordBody{
|
||||
Name: "_acme-challenge.test.example.com",
|
||||
RecordType: "TXT",
|
||||
Target: []string{`"` + "dns01-key" + `"`, `"` + "dns01-key-stub" + `"`},
|
||||
TTL: 300,
|
||||
}
|
||||
}
|
||||
|
||||
// OpenEdggrid DNS Stub
|
||||
type StubOpenDNSConfig struct {
|
||||
FuncOutput map[string]interface{}
|
||||
FuncErrors map[string]error
|
||||
}
|
||||
|
||||
//
|
||||
func findStubHostedDomainByFqdn(fqdn string, ns []string) (string, error) {
|
||||
|
||||
return "test.example.com", nil
|
||||
|
||||
}
|
||||
|
||||
func stubIsNotFoundTrue(err error) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func stubIsNotFoundFalse(err error) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// TestNewDNSProvider performs sanity check on provider init
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
// samplee couple important fields
|
||||
assert.Equal(t, akamai.serviceConsumerDomain, "akamai.example.com")
|
||||
assert.Equal(t, fmt.Sprintf("%T", akamai.dnsclient), "*akamai.OpenDNSConfig")
|
||||
|
||||
}
|
||||
|
||||
// TestPresentBasicFlow tests basic flow, e.g. no record exists.
|
||||
func TestPresentBasicFlow(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var response []byte
|
||||
mockTransport(t, akamai, "example.com", sampleZoneData, &response)
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundTrue
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordSave"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
assert.NoError(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
|
||||
|
||||
var expected, actual map[string]interface{}
|
||||
assert.NoError(t, json.Unmarshal([]byte(sampleZoneDataWithTxt), &expected))
|
||||
assert.NoError(t, json.Unmarshal(response, &actual))
|
||||
assert.EqualValues(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestCleanUp(t *testing.T) {
|
||||
// TestPresentExists tests flow with existing record.
|
||||
func TestPresentExists(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var response []byte
|
||||
mockTransport(t, akamai, "example.com", sampleZoneDataWithTxt, &response)
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordUpdate"] = testRecordBodyDataExist()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
assert.NoError(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
|
||||
|
||||
}
|
||||
|
||||
// TestPresentValueExists tests flow with existing record.
|
||||
func TestPresentValueExists(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
assert.NoError(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
|
||||
|
||||
}
|
||||
|
||||
func TestPresentFailGetRecord(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["GetRecord"] = fmt.Errorf("Failed Get Test")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
assert.Error(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
|
||||
|
||||
}
|
||||
|
||||
func TestPresentFailSaveRecord(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundTrue
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save fail")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
assert.Error(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
|
||||
|
||||
}
|
||||
|
||||
func TestPresentFailUpdateRecord(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordUpdate"] = testRecordBodyDataExist()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update failed")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
assert.Error(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
|
||||
|
||||
}
|
||||
|
||||
// TestCleanUpBasicFlow tests flow with existing record.
|
||||
func TestCleanUpBasicFlow(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordDelete"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
|
||||
|
||||
assert.NoError(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
|
||||
|
||||
var expected, actual map[string]interface{}
|
||||
assert.NoError(t, json.Unmarshal([]byte(sampleZoneData), &expected))
|
||||
expected["zone"].(map[string]interface{})["soa"].(map[string]interface{})["serial"] = 1271354826.
|
||||
assert.NoError(t, json.Unmarshal(response, &actual))
|
||||
assert.EqualValues(t, expected, actual)
|
||||
}
|
||||
|
||||
func mockTransport(t *testing.T, akamai *DNSProvider, domain, data string, response *[]byte) {
|
||||
akamai.transport = httpResponder(func(req *http.Request) (*http.Response, error) {
|
||||
defer req.Body.Close()
|
||||
// TestPresentExists tests flow with existing record.
|
||||
func TestCleanUpExists(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if req.URL.String() != "https://akamai.example.com/config-dns/v1/zones/"+domain {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusNotFound,
|
||||
Body: http.NoBody,
|
||||
}, nil
|
||||
}
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordUpdate"] = testRecordBodyDataExist()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
if req.Method == http.MethodGet {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte(data))),
|
||||
}, nil
|
||||
}
|
||||
assert.NoError(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
|
||||
|
||||
if req.Method == http.MethodPost {
|
||||
if req.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("unsupported Content Type: %v", req.Header.Get("Content-Type"))
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
*response, err = ioutil.ReadAll(req.Body)
|
||||
assert.NoError(t, err)
|
||||
// TestCleanUpExistsNoValue tests flow with existing record.
|
||||
func TestCleanUpExistsNoValue(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusNoContent,
|
||||
Body: http.NoBody,
|
||||
}, nil
|
||||
}
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
t.Fatalf("unexpected method: %v", req.Method)
|
||||
return nil, nil
|
||||
})
|
||||
akamai.findHostedDomainByFqdn = func(fqdn string, _ []string) (string, error) {
|
||||
if !strings.HasSuffix(fqdn, domain+".") {
|
||||
t.Fatalf("unexpected fqdn: %s", fqdn)
|
||||
}
|
||||
return domain, nil
|
||||
assert.NoError(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
|
||||
|
||||
}
|
||||
|
||||
// TestCleanUpNoRecord tests flow with no existing record.
|
||||
func TestCleanUpNoRecord(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundTrue // ignored for this flow ...
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
assert.NoError(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01"))
|
||||
|
||||
}
|
||||
|
||||
func TestCleanUpFailGetRecord(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["GetRecord"] = fmt.Errorf("Failed Get Record")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
assert.Error(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
|
||||
|
||||
}
|
||||
|
||||
func TestCleanUpFailUpdateRecord(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyDataExist()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordUpdate"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update failed")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
|
||||
|
||||
assert.Error(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
|
||||
|
||||
}
|
||||
|
||||
func TestCleanUpFailDeleteRecord(t *testing.T) {
|
||||
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
|
||||
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
|
||||
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordDelete"] = testRecordBodyData()
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
|
||||
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete failed")
|
||||
|
||||
assert.Error(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
|
||||
|
||||
}
|
||||
|
||||
// Stub Get Record
|
||||
func (o StubOpenDNSConfig) GetRecord(zone string, name string, record_type string) (*dns.RecordBody, error) {
|
||||
|
||||
var rec *dns.RecordBody
|
||||
|
||||
err, ok := o.FuncErrors["GetRecord"]
|
||||
if ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exp, ok := o.FuncOutput["GetRecord"]
|
||||
if ok {
|
||||
if exp == nil {
|
||||
return nil, fmt.Errorf("GetRecord: Unexpected nil")
|
||||
}
|
||||
rec = exp.(*dns.RecordBody)
|
||||
// comare passed with expected
|
||||
if name != rec.Name {
|
||||
return nil, fmt.Errorf("GetRecord: expected/actual Name don't match")
|
||||
}
|
||||
if record_type != rec.RecordType {
|
||||
return nil, fmt.Errorf("GetRecord: expected/actual Record Type don't match")
|
||||
}
|
||||
}
|
||||
|
||||
return rec, nil
|
||||
|
||||
}
|
||||
|
||||
func (o StubOpenDNSConfig) RecordSave(rec *dns.RecordBody, zone string) error {
|
||||
|
||||
exp, ok := o.FuncOutput["RecordSave"]
|
||||
if ok {
|
||||
// comare passed with expected
|
||||
if rec.Name != exp.(*dns.RecordBody).Name {
|
||||
return fmt.Errorf("RecordSave: expected/actual Name don't match")
|
||||
}
|
||||
if rec.RecordType != exp.(*dns.RecordBody).RecordType {
|
||||
return fmt.Errorf("RecordSave: expected/actual Record Type don't match")
|
||||
}
|
||||
if !reflect.DeepEqual(rec.Target, exp.(*dns.RecordBody).Target) {
|
||||
return fmt.Errorf("RecordSave: expected/actual Target don't match")
|
||||
}
|
||||
if rec.TTL != exp.(*dns.RecordBody).TTL {
|
||||
return fmt.Errorf("RecordSave: expected/actual TTL don't match")
|
||||
}
|
||||
}
|
||||
err, ok := o.FuncErrors["RecordSave"]
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (o StubOpenDNSConfig) RecordUpdate(rec *dns.RecordBody, zone string) error {
|
||||
|
||||
exp, ok := o.FuncOutput["RecordUpdate"]
|
||||
if ok {
|
||||
// comare passed with expected
|
||||
if rec.Name != exp.(*dns.RecordBody).Name {
|
||||
return fmt.Errorf("RecordUpdate: expected/actual Name don't match")
|
||||
}
|
||||
if rec.RecordType != exp.(*dns.RecordBody).RecordType {
|
||||
return fmt.Errorf("RecordUpdate: expected/actual Record Type don't match")
|
||||
}
|
||||
if !reflect.DeepEqual(rec.Target, exp.(*dns.RecordBody).Target) {
|
||||
return fmt.Errorf("RecordUpdate: expected/actual Target don't match")
|
||||
}
|
||||
if rec.TTL != exp.(*dns.RecordBody).TTL {
|
||||
return fmt.Errorf("RecordUpdate: expected/actual TTL don't match")
|
||||
}
|
||||
}
|
||||
err, ok := o.FuncErrors["RecordUpdate"]
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o StubOpenDNSConfig) RecordDelete(rec *dns.RecordBody, zone string) error {
|
||||
|
||||
exp, ok := o.FuncOutput["RecordDelete"]
|
||||
if ok {
|
||||
// comare passed with expected
|
||||
if rec.Name != exp.(*dns.RecordBody).Name {
|
||||
return fmt.Errorf("RecordDelete: expected/actual Name don't match")
|
||||
}
|
||||
if rec.RecordType != exp.(*dns.RecordBody).RecordType {
|
||||
return fmt.Errorf("RecordDelete: expected/actual Record Type don't match")
|
||||
}
|
||||
if !reflect.DeepEqual(rec.Target, exp.(*dns.RecordBody).Target) {
|
||||
return fmt.Errorf("RecordDelete: expected/actual Target don't match")
|
||||
}
|
||||
if rec.TTL != exp.(*dns.RecordBody).TTL {
|
||||
return fmt.Errorf("RecordDelete: expected/actual TTL don't match")
|
||||
}
|
||||
}
|
||||
err, ok := o.FuncErrors["RecordDelete"]
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The cert-manager Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package akamai
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// EdgeGridAuth holds all values required to perform Akamai API Client Authentication.
|
||||
// See https://developer.akamai.com/introduction/Client_Auth.html.
|
||||
type EdgeGridAuth struct {
|
||||
ClientToken string
|
||||
ClientSecret string
|
||||
AccessToken string
|
||||
HeadersToSign []string
|
||||
MaxBody int
|
||||
|
||||
now func() time.Time
|
||||
createNonce func() (string, error)
|
||||
}
|
||||
|
||||
type signingData struct {
|
||||
timestamp string
|
||||
authHeader string
|
||||
dataToSign string
|
||||
}
|
||||
|
||||
// edgeGridAuthTimeFormat is used for timestamps in request signatures.
|
||||
const edgeGridAuthTimeFormat = "20060102T15:04:05-0700" // yyyyMMddTHH:mm:ss+0000
|
||||
|
||||
const NoMaxBody = -1
|
||||
|
||||
// NewEdgeGridAuth returns a new request signer for Akamai EdgeGrid
|
||||
func NewEdgeGridAuth(clientToken, clientSecret, accessToken string, headersToSign ...string) *EdgeGridAuth {
|
||||
return &EdgeGridAuth{
|
||||
ClientToken: clientToken,
|
||||
ClientSecret: clientSecret,
|
||||
AccessToken: accessToken,
|
||||
HeadersToSign: headersToSign,
|
||||
MaxBody: NoMaxBody,
|
||||
|
||||
now: time.Now,
|
||||
createNonce: createRandomNonce,
|
||||
}
|
||||
}
|
||||
|
||||
// SignRequest calculates the signature for Akamai Open API and adds it as the Authorization header.
|
||||
// The Authorization header starts with the signing algorithm moniker (name of the algorithm) used to sign the request.
|
||||
// The moniker below identifies EdgeGrid V1, hash message authentication code, SHA–256 as the hash standard.
|
||||
// This moniker is then followed by a space and an ordered list of name value pairs with each field separated by a semicolon.
|
||||
func (e *EdgeGridAuth) SignRequest(req *http.Request) error {
|
||||
signingData, err := e.signingData(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", fmt.Sprintf(
|
||||
"%ssignature=%s",
|
||||
signingData.authHeader,
|
||||
e.calculateRequestSignature(signingData)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EdgeGridAuth) calculateRequestSignature(signingData *signingData) string {
|
||||
return computeSignature(
|
||||
signingData.dataToSign,
|
||||
e.signingKey(signingData.timestamp))
|
||||
}
|
||||
|
||||
func (e *EdgeGridAuth) signingData(req *http.Request) (*signingData, error) {
|
||||
nonce, err := e.createNonce()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timestamp := e.now().UTC().Format(edgeGridAuthTimeFormat)
|
||||
authHeader := fmt.Sprintf("EG1-HMAC-SHA256 client_token=%s;access_token=%s;timestamp=%s;nonce=%s;",
|
||||
e.ClientToken,
|
||||
e.AccessToken,
|
||||
timestamp,
|
||||
nonce)
|
||||
|
||||
return &signingData{
|
||||
timestamp: timestamp,
|
||||
authHeader: authHeader,
|
||||
dataToSign: e.dataToSign(req, authHeader),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// dataToSign includes the information from the HTTP request that is relevant to ensuring that the request is authentic.
|
||||
// This data set comprised of the request data combined with the authorization header value (excluding the signature field,
|
||||
// but including the ; right before the signature field).
|
||||
func (e *EdgeGridAuth) dataToSign(req *http.Request, authHeader string) string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
buffer.WriteString(req.Method)
|
||||
buffer.WriteRune('\t')
|
||||
buffer.WriteString(req.URL.Scheme)
|
||||
buffer.WriteRune('\t')
|
||||
buffer.WriteString(req.URL.Host)
|
||||
buffer.WriteRune('\t')
|
||||
buffer.WriteString(relativeURL(req.URL))
|
||||
buffer.WriteRune('\t')
|
||||
buffer.WriteString(e.canonicalizedHeaders(req))
|
||||
buffer.WriteRune('\t')
|
||||
buffer.WriteString(e.computeBodyHash(req))
|
||||
buffer.WriteRune('\t')
|
||||
buffer.WriteString(authHeader)
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// signingKey is derived from the client secret.
|
||||
// The signing key is computed as the base64 encoding of the SHA–256 HMAC of the timestamp string
|
||||
// (the field value included in the HTTP authorization header described above) with the client secret as the key.
|
||||
func (e *EdgeGridAuth) signingKey(timestamp string) string {
|
||||
return computeSignature(timestamp, e.ClientSecret)
|
||||
}
|
||||
|
||||
// relativeURL is the part of the URL that starts from the root path and includes the query string, with the handling of following special cases:
|
||||
// If the path is null or empty, set it to / (forward-slash).
|
||||
// If the path does not start with /, add / to the beginning.
|
||||
func relativeURL(url *url.URL) string {
|
||||
relativeURL := url.Path
|
||||
if relativeURL == "" {
|
||||
return "/"
|
||||
}
|
||||
|
||||
if relativeURL[0] != '/' {
|
||||
relativeURL = "/" + relativeURL
|
||||
}
|
||||
|
||||
if url.RawQuery != "" {
|
||||
relativeURL += "?"
|
||||
relativeURL += url.RawQuery
|
||||
}
|
||||
|
||||
return relativeURL
|
||||
}
|
||||
|
||||
// computeBodyHash returns the base64-encoded SHA–256 hash of the POST body.
|
||||
// For any other request methods, this field is empty. But the tac separator (\t) must be included.
|
||||
// The size of the POST body must be less than or equal to the value specified by the service.
|
||||
// Any request that does not meet this criteria SHOULD be rejected during the signing process,
|
||||
// as the request will be rejected by EdgeGrid.
|
||||
func (e *EdgeGridAuth) computeBodyHash(req *http.Request) string {
|
||||
if req.Body != nil {
|
||||
bodyBytes, _ := ioutil.ReadAll(req.Body)
|
||||
req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
|
||||
if req.Method == http.MethodPost && len(bodyBytes) > 0 {
|
||||
dataToHash := bodyBytes
|
||||
if e.MaxBody != NoMaxBody && len(dataToHash) > e.MaxBody {
|
||||
dataToHash = dataToHash[0:e.MaxBody]
|
||||
}
|
||||
sha256Sum := sha256.Sum256(dataToHash)
|
||||
return base64.StdEncoding.EncodeToString(sha256Sum[:])
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// canonicalizedHeaders returns the request headers as a canonicalized string.
|
||||
//
|
||||
// The protocol does not support multiple request headers with the same header name.
|
||||
// Such requests SHOULD be rejected during the signing process. Otherwise, EdgeGrid
|
||||
// will not produce the intended results by rejecting such requests or removing all
|
||||
// (but one) duplicated headers.
|
||||
//
|
||||
// Header names are case-insensitive per rfc2616.
|
||||
//
|
||||
// For each entry in the list of headers designated by the service provider to include
|
||||
// in the signature in the specified order, the canonicalization of the request header
|
||||
// is done as follows:
|
||||
//
|
||||
// Get the first header value for the name.
|
||||
// Trim the leading and trailing white spaces.
|
||||
// Replace all repeated white spaces with a single space.
|
||||
// Concatenate the name:value pairs with the tab (\t) separator (name field is all in lower case).
|
||||
// Terminate the headers with another tab (\t) separator.
|
||||
//
|
||||
// NOTE: The canonicalized data is used for creating the signature only, as this step
|
||||
// might alter the header value. If a header in the list is not present in the request,
|
||||
// or the header value is empty, nothing for that header, neither the name nor the tab
|
||||
// separator, may be included.
|
||||
func (e *EdgeGridAuth) canonicalizedHeaders(req *http.Request) string {
|
||||
if len(e.HeadersToSign) < 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var headerNamesToSign []string
|
||||
for headerName := range req.Header {
|
||||
for _, sign := range e.HeadersToSign {
|
||||
if strings.EqualFold(sign, headerName) {
|
||||
headerNamesToSign = append(headerNamesToSign, headerName)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(headerNamesToSign) < 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
sort.Strings(headerNamesToSign)
|
||||
|
||||
var buffer bytes.Buffer
|
||||
for _, headerName := range headerNamesToSign {
|
||||
for _, c := range headerName {
|
||||
buffer.WriteRune(unicode.ToLower(c))
|
||||
}
|
||||
|
||||
buffer.WriteRune(':')
|
||||
|
||||
white := false
|
||||
empty := true
|
||||
for _, c := range req.Header.Get(headerName) {
|
||||
if unicode.IsSpace(c) {
|
||||
white = true
|
||||
} else {
|
||||
if white && !empty {
|
||||
buffer.WriteRune(' ')
|
||||
}
|
||||
buffer.WriteRune(unicode.ToLower(c))
|
||||
empty = false
|
||||
white = false
|
||||
}
|
||||
}
|
||||
|
||||
buffer.WriteRune('\t')
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// calculateSignature is the base64-encoding of the SHA–256 HMAC of the data to sign with the signing key.
|
||||
func computeSignature(message string, secret string) string {
|
||||
key := []byte(secret)
|
||||
h := hmac.New(sha256.New, key)
|
||||
h.Write([]byte(message))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func createRandomNonce() (string, error) {
|
||||
bytes := make([]byte, 18)
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.URLEncoding.EncodeToString(bytes), nil
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The cert-manager Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package akamai
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDataToSign(t *testing.T) {
|
||||
req, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"https://akaa-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net/diagnostic-tools/v1/locations",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
|
||||
auth := NewEdgeGridAuth("ClientToken", "ClientSecret", "AccessToken")
|
||||
auth.now = func() time.Time {
|
||||
return time.Unix(1396461906, 0) // 20140402T18:05:06Z
|
||||
}
|
||||
auth.createNonce = func() (string, error) {
|
||||
return "185f94eb-537c-4c01-b8cc-2fa5a06aee7f", nil
|
||||
}
|
||||
|
||||
data, err := auth.signingData(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := "GET" +
|
||||
"\thttps" +
|
||||
"\takaa-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net" +
|
||||
"\t/diagnostic-tools/v1/locations" +
|
||||
"\t" + // headers
|
||||
"\t" + // content hash
|
||||
"\tEG1-HMAC-SHA256 " +
|
||||
"client_token=ClientToken;" +
|
||||
"access_token=AccessToken;" +
|
||||
"timestamp=20140402T18:05:06+0000;" +
|
||||
"nonce=185f94eb-537c-4c01-b8cc-2fa5a06aee7f;"
|
||||
|
||||
assert.EqualValues(t, expected, data.dataToSign)
|
||||
}
|
||||
|
||||
func TestDataToSignWithHeaders(t *testing.T) {
|
||||
req, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"http://akaa-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna-dev.akamaiapis.net/sample-api/v1/property/?fields=x&format=json&cpcode=1234",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req.Header.Set("x-a", "va")
|
||||
req.Header.Set("x-c", "\" xc \"")
|
||||
req.Header.Set("x-b", " w b")
|
||||
|
||||
auth := NewEdgeGridAuth(
|
||||
"ClientToken", "ClientSecret", "AccessToken",
|
||||
"x-c", "x-b", "x-a")
|
||||
auth.now = func() time.Time {
|
||||
return time.Unix(1376917283, 0) // 20130819T13:01:23Z
|
||||
}
|
||||
auth.createNonce = func() (string, error) {
|
||||
return "ac392096-8aa1-44fd-8c3b-f797d35a6736", nil
|
||||
}
|
||||
|
||||
data, err := auth.signingData(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := "GET" +
|
||||
"\thttp" +
|
||||
"\takaa-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna-dev.akamaiapis.net" +
|
||||
"\t/sample-api/v1/property/?fields=x&format=json&cpcode=1234" +
|
||||
"\tx-a:va\tx-b:w b\tx-c:\" xc \"\t" +
|
||||
"\t" + // content hash
|
||||
"\tEG1-HMAC-SHA256 " +
|
||||
"client_token=ClientToken;" +
|
||||
"access_token=AccessToken;" +
|
||||
"timestamp=20130819T13:01:23+0000;" +
|
||||
"nonce=ac392096-8aa1-44fd-8c3b-f797d35a6736;"
|
||||
|
||||
assert.EqualValues(t, expected, data.dataToSign)
|
||||
}
|
||||
Reference in New Issue
Block a user