mirror of
https://github.com/go-acme/lego.git
synced 2024-12-26 03:09:37 +02:00
Add DNS Provider for TransIP (#703)
This commit is contained in:
parent
42d8637d87
commit
1837a3bb1c
14
Gopkg.lock
generated
14
Gopkg.lock
generated
@ -446,6 +446,18 @@
|
||||
pruneopts = "NUT"
|
||||
revision = "37e84520dcf74488f67654f9c775b9752c232dc1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3b236e8930d31aeb375fe405c15c2afc581e04bd6cb68da4723e1aa8d2e2da37"
|
||||
name = "github.com/transip/gotransip"
|
||||
packages = [
|
||||
".",
|
||||
"domain",
|
||||
"util",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "1dc93a7db3567a5ccf865106afac88278ba940cf"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5dba68a1600a235630e208cb7196b24e58fcbb77bb7a6bec08fcd23f081b0a58"
|
||||
name = "github.com/urfave/cli"
|
||||
@ -659,6 +671,8 @@
|
||||
"github.com/stretchr/testify/suite",
|
||||
"github.com/timewasted/linode",
|
||||
"github.com/timewasted/linode/dns",
|
||||
"github.com/transip/gotransip",
|
||||
"github.com/transip/gotransip/domain",
|
||||
"github.com/urfave/cli",
|
||||
"golang.org/x/crypto/ocsp",
|
||||
"golang.org/x/net/context",
|
||||
|
@ -85,6 +85,10 @@
|
||||
branch = "master"
|
||||
name = "github.com/sacloud/libsacloud"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/transip/gotransip"
|
||||
|
||||
[[constraint]]
|
||||
version = "0.11.1"
|
||||
name = "github.com/exoscale/egoscale"
|
||||
|
@ -48,6 +48,7 @@ import (
|
||||
"github.com/xenolf/lego/providers/dns/sakuracloud"
|
||||
"github.com/xenolf/lego/providers/dns/selectel"
|
||||
"github.com/xenolf/lego/providers/dns/stackpath"
|
||||
"github.com/xenolf/lego/providers/dns/transip"
|
||||
"github.com/xenolf/lego/providers/dns/vegadns"
|
||||
"github.com/xenolf/lego/providers/dns/vultr"
|
||||
)
|
||||
@ -145,6 +146,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
||||
return stackpath.NewDNSProvider()
|
||||
case "selectel":
|
||||
return selectel.NewDNSProvider()
|
||||
case "transip":
|
||||
return transip.NewDNSProvider()
|
||||
case "vegadns":
|
||||
return vegadns.NewDNSProvider()
|
||||
case "vultr":
|
||||
|
0
providers/dns/transip/fixtures/private.key
Normal file
0
providers/dns/transip/fixtures/private.key
Normal file
150
providers/dns/transip/transip.go
Normal file
150
providers/dns/transip/transip.go
Normal file
@ -0,0 +1,150 @@
|
||||
// Package transip implements a DNS provider for solving the DNS-01 challenge using TransIP.
|
||||
package transip
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/transip/gotransip"
|
||||
transipdomain "github.com/transip/gotransip/domain"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/platform/config/env"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider
|
||||
type Config struct {
|
||||
AccountName string
|
||||
PrivateKeyPath string
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int64
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: int64(env.GetOrDefaultInt("TRANSIP_TTL", 10)),
|
||||
PropagationTimeout: env.GetOrDefaultSecond("TRANSIP_PROPAGATION_TIMEOUT", 10*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond("TRANSIP_POLLING_INTERVAL", 10*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider describes a provider for TransIP
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
client gotransip.SOAPClient
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for TransIP.
|
||||
// Credentials must be passed in the environment variables:
|
||||
// TRANSIP_ACCOUNTNAME, TRANSIP_PRIVATEKEYPATH.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get("TRANSIP_ACCOUNT_NAME", "TRANSIP_PRIVATE_KEY_PATH")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("transip: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AccountName = values["TRANSIP_ACCOUNT_NAME"]
|
||||
config.PrivateKeyPath = values["TRANSIP_PRIVATE_KEY_PATH"]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for TransIP.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("transip: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client, err := gotransip.NewSOAPClient(gotransip.ClientConfig{
|
||||
AccountName: config.AccountName,
|
||||
PrivateKeyPath: config.PrivateKeyPath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("transip: %v", err)
|
||||
}
|
||||
|
||||
return &DNSProvider{client: client, config: config}, nil
|
||||
}
|
||||
|
||||
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||
// Adjusting here to cope with spikes in propagation times.
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill the dns-01 challenge
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainName := acme.UnFqdn(authZone)
|
||||
|
||||
// get the subDomain
|
||||
subDomain := strings.TrimSuffix(acme.UnFqdn(fqdn), "."+domainName)
|
||||
|
||||
// get all DNS entries
|
||||
info, err := transipdomain.GetInfo(d.client, domainName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("transip: error for %s in Present: %v", domain, err)
|
||||
}
|
||||
|
||||
// include the new DNS entry
|
||||
dnsEntries := append(info.DNSEntries, transipdomain.DNSEntry{
|
||||
Name: subDomain,
|
||||
TTL: d.config.TTL,
|
||||
Type: transipdomain.DNSEntryTypeTXT,
|
||||
Content: value,
|
||||
})
|
||||
|
||||
// set the updated DNS entries
|
||||
err = transipdomain.SetDNSEntries(d.client, domainName, dnsEntries)
|
||||
if err != nil {
|
||||
return fmt.Errorf("transip: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainName := acme.UnFqdn(authZone)
|
||||
|
||||
// get the subDomain
|
||||
subDomain := strings.TrimSuffix(acme.UnFqdn(fqdn), "."+domainName)
|
||||
|
||||
// get all DNS entries
|
||||
info, err := transipdomain.GetInfo(d.client, domainName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("transip: error for %s in CleanUp: %v", fqdn, err)
|
||||
}
|
||||
|
||||
// loop through the existing entries and remove the specific record
|
||||
updatedEntries := info.DNSEntries[:0]
|
||||
for _, e := range info.DNSEntries {
|
||||
if e.Name != subDomain {
|
||||
updatedEntries = append(updatedEntries, e)
|
||||
}
|
||||
}
|
||||
|
||||
// set the updated DNS entries
|
||||
err = transipdomain.SetDNSEntries(d.client, domainName, updatedEntries)
|
||||
if err != nil {
|
||||
return fmt.Errorf("transip: couldn't get Record ID in CleanUp: %sv", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
164
providers/dns/transip/transip_test.go
Normal file
164
providers/dns/transip/transip_test.go
Normal file
@ -0,0 +1,164 @@
|
||||
package transip
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/xenolf/lego/platform/tester"
|
||||
)
|
||||
|
||||
var envTest = tester.NewEnvTest(
|
||||
"TRANSIP_ACCOUNT_NAME",
|
||||
"TRANSIP_PRIVATE_KEY_PATH").
|
||||
WithDomain("TRANSIP_DOMAIN")
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
envVars map[string]string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
envVars: map[string]string{
|
||||
"TRANSIP_ACCOUNT_NAME": "johndoe",
|
||||
"TRANSIP_PRIVATE_KEY_PATH": "./fixtures/private.key",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "missing all credentials",
|
||||
envVars: map[string]string{
|
||||
"TRANSIP_ACCOUNT_NAME": "",
|
||||
"TRANSIP_PRIVATE_KEY_PATH": "",
|
||||
},
|
||||
expected: "transip: some credentials information are missing: TRANSIP_ACCOUNT_NAME,TRANSIP_PRIVATE_KEY_PATH",
|
||||
},
|
||||
{
|
||||
desc: "missing account name",
|
||||
envVars: map[string]string{
|
||||
"TRANSIP_ACCOUNT_NAME": "",
|
||||
"TRANSIP_PRIVATE_KEY_PATH": "./fixtures/private.key",
|
||||
},
|
||||
expected: "transip: some credentials information are missing: TRANSIP_ACCOUNT_NAME",
|
||||
},
|
||||
{
|
||||
desc: "missing private key path",
|
||||
envVars: map[string]string{
|
||||
"TRANSIP_ACCOUNT_NAME": "johndoe",
|
||||
"TRANSIP_PRIVATE_KEY_PATH": "",
|
||||
},
|
||||
expected: "transip: some credentials information are missing: TRANSIP_PRIVATE_KEY_PATH",
|
||||
},
|
||||
{
|
||||
desc: "could not open private key path",
|
||||
envVars: map[string]string{
|
||||
"TRANSIP_ACCOUNT_NAME": "johndoe",
|
||||
"TRANSIP_PRIVATE_KEY_PATH": "./fixtures/non/existent/private.key",
|
||||
},
|
||||
expected: "transip: could not open private key: stat ./fixtures/non/existent/private.key: no such file or directory",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
defer envTest.RestoreEnv()
|
||||
envTest.ClearEnv()
|
||||
|
||||
envTest.Apply(test.envVars)
|
||||
|
||||
p, err := NewDNSProvider()
|
||||
|
||||
if len(test.expected) == 0 {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
require.NotNil(t, p.config)
|
||||
require.NotNil(t, p.client)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDNSProviderConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
accountName string
|
||||
privateKeyPath string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
accountName: "johndoe",
|
||||
privateKeyPath: "./fixtures/private.key",
|
||||
},
|
||||
{
|
||||
desc: "missing all credentials",
|
||||
expected: "transip: AccountName is required",
|
||||
},
|
||||
{
|
||||
desc: "missing account name",
|
||||
privateKeyPath: "./fixtures/private.key",
|
||||
expected: "transip: AccountName is required",
|
||||
},
|
||||
{
|
||||
desc: "missing private key path",
|
||||
accountName: "johndoe",
|
||||
expected: "transip: PrivateKeyPath or PrivateKeyBody is required",
|
||||
},
|
||||
{
|
||||
desc: "could not open private key path",
|
||||
accountName: "johndoe",
|
||||
privateKeyPath: "./fixtures/non/existent/private.key",
|
||||
expected: "transip: could not open private key: stat ./fixtures/non/existent/private.key: no such file or directory",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
config := NewDefaultConfig()
|
||||
config.AccountName = test.accountName
|
||||
config.PrivateKeyPath = test.privateKeyPath
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
|
||||
if len(test.expected) == 0 {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
require.NotNil(t, p.config)
|
||||
require.NotNil(t, p.client)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLivePresent(t *testing.T) {
|
||||
if !envTest.IsLiveTest() {
|
||||
t.Skip("skipping live test")
|
||||
}
|
||||
|
||||
envTest.RestoreEnv()
|
||||
provider, err := NewDNSProvider()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = provider.Present(envTest.GetDomain(), "", "123d==")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestLiveCleanUp(t *testing.T) {
|
||||
if !envTest.IsLiveTest() {
|
||||
t.Skip("skipping live test")
|
||||
}
|
||||
|
||||
envTest.RestoreEnv()
|
||||
provider, err := NewDNSProvider()
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
|
||||
require.NoError(t, err)
|
||||
}
|
21
vendor/github.com/transip/gotransip/LICENSE
generated
vendored
Normal file
21
vendor/github.com/transip/gotransip/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 TransIP B.V.
|
||||
|
||||
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.
|
12
vendor/github.com/transip/gotransip/api.go
generated
vendored
Normal file
12
vendor/github.com/transip/gotransip/api.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package gotransip
|
||||
|
||||
// CancellationTime represents the possible ways of canceling a contract
|
||||
type CancellationTime string
|
||||
|
||||
var (
|
||||
// CancellationTimeEnd specifies to cancel the contract when the contract was
|
||||
// due to end anyway
|
||||
CancellationTimeEnd CancellationTime = "end"
|
||||
// CancellationTimeImmediately specifies to cancel the contract immediately
|
||||
CancellationTimeImmediately CancellationTime = "immediately"
|
||||
)
|
119
vendor/github.com/transip/gotransip/client.go
generated
vendored
Normal file
119
vendor/github.com/transip/gotransip/client.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
package gotransip
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
transipAPIHost = "api.transip.nl"
|
||||
transipAPINamespace = "http://www.transip.nl/soap"
|
||||
)
|
||||
|
||||
// APIMode specifies in which mode the API is used. Currently this is only
|
||||
// supports either readonly or readwrite
|
||||
type APIMode string
|
||||
|
||||
var (
|
||||
// APIModeReadOnly specifies that no changes can be made from API calls
|
||||
APIModeReadOnly APIMode = "readonly"
|
||||
// APIModeReadWrite specifies that changes can be made from API calls
|
||||
APIModeReadWrite APIMode = "readwrite"
|
||||
)
|
||||
|
||||
// ClientConfig is a tool to easily create a new Client object
|
||||
type ClientConfig struct {
|
||||
AccountName string
|
||||
PrivateKeyPath string
|
||||
PrivateKeyBody []byte
|
||||
Mode APIMode
|
||||
}
|
||||
|
||||
// Client is the interface which all clients should implement
|
||||
type Client interface {
|
||||
Call(SoapRequest, interface{}) error // execute request on client
|
||||
}
|
||||
|
||||
// SOAPClient represents a TransIP API SOAP client, implementing the Client
|
||||
// interface
|
||||
type SOAPClient struct {
|
||||
soapClient soapClient
|
||||
}
|
||||
|
||||
// Call performs given SOAP request and fills the response into result
|
||||
func (c SOAPClient) Call(req SoapRequest, result interface{}) error {
|
||||
return c.soapClient.call(req, result)
|
||||
}
|
||||
|
||||
// NewSOAPClient returns a new SOAPClient object for given config
|
||||
// ClientConfig's PrivateKeyPath will override potentially given PrivateKeyBody
|
||||
func NewSOAPClient(c ClientConfig) (SOAPClient, error) {
|
||||
// check account name
|
||||
if len(c.AccountName) == 0 {
|
||||
return SOAPClient{}, errors.New("AccountName is required")
|
||||
}
|
||||
|
||||
// check if private key was given in any form
|
||||
if len(c.PrivateKeyPath) == 0 && len(c.PrivateKeyBody) == 0 {
|
||||
return SOAPClient{}, errors.New("PrivateKeyPath or PrivateKeyBody is required")
|
||||
}
|
||||
|
||||
// if PrivateKeyPath was set, this will override any given PrivateKeyBody
|
||||
if len(c.PrivateKeyPath) > 0 {
|
||||
// try to open private key and read contents
|
||||
if _, err := os.Stat(c.PrivateKeyPath); err != nil {
|
||||
return SOAPClient{}, fmt.Errorf("could not open private key: %s", err.Error())
|
||||
}
|
||||
|
||||
// read private key so we can pass the body to the soapClient
|
||||
var err error
|
||||
c.PrivateKeyBody, err = ioutil.ReadFile(c.PrivateKeyPath)
|
||||
if err != nil {
|
||||
return SOAPClient{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// default to APIMode read/write
|
||||
if len(c.Mode) == 0 {
|
||||
c.Mode = APIModeReadWrite
|
||||
}
|
||||
|
||||
// create soapClient and pass it to a new Client pointer
|
||||
sc := soapClient{
|
||||
Login: c.AccountName,
|
||||
Mode: c.Mode,
|
||||
PrivateKey: c.PrivateKeyBody,
|
||||
}
|
||||
|
||||
return SOAPClient{
|
||||
soapClient: sc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FakeSOAPClient is a client doing nothing except implementing the gotransip.Client
|
||||
// interface
|
||||
// you can however set a fixture XML body which Call will try to Unmarshal into
|
||||
// result
|
||||
// useful for testing
|
||||
type FakeSOAPClient struct {
|
||||
fixture []byte // preset this fixture so Call can use it to Unmarshal
|
||||
}
|
||||
|
||||
// FixtureFromFile reads file and sets content as FakeSOAPClient's fixture
|
||||
func (f *FakeSOAPClient) FixtureFromFile(file string) (err error) {
|
||||
// read fixture file
|
||||
f.fixture, err = ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not read fixture from file %s: %s", file, err.Error())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Call doesn't do anything except fill the XML unmarshalled result
|
||||
func (f FakeSOAPClient) Call(req SoapRequest, result interface{}) error {
|
||||
// this fake client just parses given fixture as if it was a SOAP response
|
||||
return parseSoapResponse(f.fixture, req.Padding, 200, result)
|
||||
}
|
314
vendor/github.com/transip/gotransip/domain/api.go
generated
vendored
Normal file
314
vendor/github.com/transip/gotransip/domain/api.go
generated
vendored
Normal file
@ -0,0 +1,314 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"github.com/transip/gotransip"
|
||||
)
|
||||
|
||||
// This file holds all DomainService methods directly ported from TransIP API
|
||||
|
||||
// BatchCheckAvailability checks the availability of multiple domains
|
||||
func BatchCheckAvailability(c gotransip.Client, domainNames []string) ([]CheckResult, error) {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "batchCheckAvailability",
|
||||
}
|
||||
sr.AddArgument("domainNames", domainNames)
|
||||
|
||||
var v struct {
|
||||
V []CheckResult `xml:"item"`
|
||||
}
|
||||
|
||||
err := c.Call(sr, &v)
|
||||
return v.V, err
|
||||
}
|
||||
|
||||
// CheckAvailability returns the availability status of a domain.
|
||||
func CheckAvailability(c gotransip.Client, domainName string) (Status, error) {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "checkAvailability",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
|
||||
var v Status
|
||||
err := c.Call(sr, &v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// GetWhois returns the whois of a domain name
|
||||
func GetWhois(c gotransip.Client, domainName string) (string, error) {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "getWhois",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
|
||||
var v string
|
||||
err := c.Call(sr, &v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// GetDomainNames returns list with domain names or error when this failed
|
||||
func GetDomainNames(c gotransip.Client) ([]string, error) {
|
||||
var d = struct {
|
||||
D []string `xml:"item"`
|
||||
}{}
|
||||
err := c.Call(gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "getDomainNames",
|
||||
}, &d)
|
||||
|
||||
return d.D, err
|
||||
}
|
||||
|
||||
// GetInfo returns Domain for given name or error when this failed
|
||||
func GetInfo(c gotransip.Client, domainName string) (Domain, error) {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "getInfo",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
|
||||
var d Domain
|
||||
err := c.Call(sr, &d)
|
||||
|
||||
return d, err
|
||||
}
|
||||
|
||||
// BatchGetInfo returns array of Domain for given name or error when this failed
|
||||
func BatchGetInfo(c gotransip.Client, domainNames []string) ([]Domain, error) {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "batchGetInfo",
|
||||
}
|
||||
sr.AddArgument("domainNames", domainNames)
|
||||
|
||||
var d = struct {
|
||||
D []Domain `xml:"item"`
|
||||
}{}
|
||||
err := c.Call(sr, &d)
|
||||
|
||||
return d.D, err
|
||||
}
|
||||
|
||||
// GetAuthCode returns the Auth code for a domainName
|
||||
func GetAuthCode(c gotransip.Client, domainName string) (string, error) {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "getAuthCode",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
|
||||
var v string
|
||||
err := c.Call(sr, &v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// GetIsLocked returns the lock status for a domainName
|
||||
func GetIsLocked(c gotransip.Client, domainName string) (bool, error) {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "getIsLocked",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
|
||||
var v bool
|
||||
err := c.Call(sr, &v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Register registers a domain name and will automatically create and sign a proposition for it
|
||||
func Register(c gotransip.Client, domain string) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "register",
|
||||
}
|
||||
sr.AddArgument("domain", domain)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// Cancel cancels a domain name, will automatically create and sign a cancellation document
|
||||
func Cancel(c gotransip.Client, domainName string, endTime gotransip.CancellationTime) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "cancel",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
sr.AddArgument("endTime", string(endTime))
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// TransferWithOwnerChange transfers a domain with changing the owner
|
||||
func TransferWithOwnerChange(c gotransip.Client, domain, authCode string) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "transferWithOwnerChange",
|
||||
}
|
||||
sr.AddArgument("domain", domain)
|
||||
sr.AddArgument("authCode", authCode)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// TransferWithoutOwnerChange transfers a domain without changing the owner
|
||||
func TransferWithoutOwnerChange(c gotransip.Client, domain, authCode string) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "transferWithoutOwnerChange",
|
||||
}
|
||||
sr.AddArgument("domain", domain)
|
||||
sr.AddArgument("authCode", authCode)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// SetNameservers starts a nameserver change for this domain, will replace all
|
||||
// existing nameservers with the new nameservers
|
||||
func SetNameservers(c gotransip.Client, domainName string, nameservers Nameservers) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "setNameservers",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
sr.AddArgument("nameservers", nameservers)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// SetLock locks this domain
|
||||
func SetLock(c gotransip.Client, domainName string) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "setLock",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// UnsetLock unlocks this domain
|
||||
func UnsetLock(c gotransip.Client, domainName string) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "unsetLock",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// SetDNSEntries sets the DnsEntries for this Domain, will replace all existing
|
||||
// dns entries with the new entries
|
||||
func SetDNSEntries(c gotransip.Client, domainName string, dnsEntries DNSEntries) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "setDnsEntries",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
sr.AddArgument("dnsEntries", dnsEntries)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// SetOwner starts an owner change of a domain
|
||||
func SetOwner(c gotransip.Client, domainName, registrantWhoisContact WhoisContact) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "setOwner",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
// make sure contact is of type registrant
|
||||
registrantWhoisContact.Type = "registrant"
|
||||
sr.AddArgument("registrantWhoisContact", registrantWhoisContact)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// SetContacts starts a contact change of a domain, this will replace all existing contacts
|
||||
func SetContacts(c gotransip.Client, domainName, contacts WhoisContacts) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "setContacts",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
sr.AddArgument("contacts", contacts)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// GetAllTLDInfos returns slice with TLD objects or error when this failed
|
||||
func GetAllTLDInfos(c gotransip.Client) ([]TLD, error) {
|
||||
var d = struct {
|
||||
TLD []TLD `xml:"item"`
|
||||
}{}
|
||||
err := c.Call(gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "getAllTldInfos",
|
||||
}, &d)
|
||||
|
||||
return d.TLD, err
|
||||
}
|
||||
|
||||
// GetTldInfo returns info about a specific TLD
|
||||
func GetTldInfo(c gotransip.Client, tldName string) (TLD, error) {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "getTldInfo",
|
||||
}
|
||||
sr.AddArgument("tldName", tldName)
|
||||
|
||||
var v TLD
|
||||
err := c.Call(sr, &v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// GetCurrentDomainAction returns info about the action this domain is currently running
|
||||
func GetCurrentDomainAction(c gotransip.Client, domainName string) (ActionResult, error) {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "getCurrentDomainAction",
|
||||
}
|
||||
sr.AddArgument("domainName", domainName)
|
||||
|
||||
var v ActionResult
|
||||
err := c.Call(sr, &v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// RetryCurrentDomainActionWithNewData retries a failed domain action with new
|
||||
// domain data. The Domain.Name field must contain the name of the Domain. The
|
||||
// Nameservers, Contacts, DNSEntries fields contain the new data for this domain.
|
||||
func RetryCurrentDomainActionWithNewData(c gotransip.Client, domain Domain) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "retryCurrentDomainActionWithNewData",
|
||||
}
|
||||
sr.AddArgument("domain", domain)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// RetryTransferWithDifferentAuthCode retries a transfer action with a new authcode
|
||||
func RetryTransferWithDifferentAuthCode(c gotransip.Client, domain Domain, newAuthCode string) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "retryTransferWithDifferentAuthCode",
|
||||
}
|
||||
sr.AddArgument("domain", domain)
|
||||
sr.AddArgument("newAuthCode", newAuthCode)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
||||
|
||||
// CancelDomainAction cancels a failed domain action
|
||||
func CancelDomainAction(c gotransip.Client, domain string) error {
|
||||
sr := gotransip.SoapRequest{
|
||||
Service: serviceName,
|
||||
Method: "cancelDomainAction",
|
||||
}
|
||||
sr.AddArgument("domain", domain)
|
||||
|
||||
return c.Call(sr, nil)
|
||||
}
|
405
vendor/github.com/transip/gotransip/domain/domain.go
generated
vendored
Normal file
405
vendor/github.com/transip/gotransip/domain/domain.go
generated
vendored
Normal file
@ -0,0 +1,405 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/transip/gotransip"
|
||||
"github.com/transip/gotransip/util"
|
||||
)
|
||||
|
||||
const (
|
||||
serviceName string = "DomainService"
|
||||
)
|
||||
|
||||
// Domain represents a Transip_Domain object
|
||||
// as described at https://api.transip.nl/docs/transip.nl/class-Transip_Domain.html
|
||||
type Domain struct {
|
||||
Name string `xml:"name"`
|
||||
Nameservers []Nameserver `xml:"nameservers>item"`
|
||||
Contacts []WhoisContact `xml:"contacts>item"`
|
||||
DNSEntries []DNSEntry `xml:"dnsEntries>item"`
|
||||
Branding Branding `xml:"branding"`
|
||||
AuthorizationCode string `xml:"authCode"`
|
||||
IsLocked bool `xml:"isLocked"`
|
||||
RegistrationDate util.XMLTime `xml:"registrationDate"`
|
||||
RenewalDate util.XMLTime `xml:"renewalDate"`
|
||||
}
|
||||
|
||||
// EncodeParams returns Domain parameters ready to be used for constructing a signature
|
||||
func (d Domain) EncodeParams(prm gotransip.ParamsContainer) {
|
||||
idx := prm.Len()
|
||||
prm.Add(fmt.Sprintf("%d[name]", idx), d.Name)
|
||||
prm.Add(fmt.Sprintf("%d[authCode]", idx), d.AuthorizationCode)
|
||||
prm.Add(fmt.Sprintf("%d[isLocked]", idx), fmt.Sprintf("%t", d.IsLocked))
|
||||
prm.Add(fmt.Sprintf("%d[registrationDate]", idx), d.RegistrationDate.Format("2006-01-02"))
|
||||
prm.Add(fmt.Sprintf("%d[renewalDate]", idx), d.RenewalDate.Format("2006-01-02"))
|
||||
// nameservers
|
||||
for i, e := range d.Nameservers {
|
||||
var ipv4, ipv6 string
|
||||
if e.IPv4Address != nil {
|
||||
ipv4 = e.IPv4Address.String()
|
||||
}
|
||||
if e.IPv6Address != nil {
|
||||
ipv6 = e.IPv6Address.String()
|
||||
}
|
||||
prm.Add(fmt.Sprintf("%d[nameservers][%d][hostname]", idx, i), e.Hostname)
|
||||
prm.Add(fmt.Sprintf("%d[nameservers][%d][ipv4]", idx, i), ipv4)
|
||||
prm.Add(fmt.Sprintf("%d[nameservers][%d][ipv6]", idx, i), ipv6)
|
||||
}
|
||||
// contacts
|
||||
for i, e := range d.Contacts {
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][type]", idx, i), e.Type)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][firstName]", idx, i), e.FirstName)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][middleName]", idx, i), e.MiddleName)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][lastName]", idx, i), e.LastName)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][companyName]", idx, i), e.CompanyName)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][companyKvk]", idx, i), e.CompanyKvk)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][companyType]", idx, i), e.CompanyType)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][street]", idx, i), e.Street)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][number]", idx, i), e.Number)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][postalCode]", idx, i), e.PostalCode)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][city]", idx, i), e.City)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][phoneNumber]", idx, i), e.PhoneNumber)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][faxNumber]", idx, i), e.FaxNumber)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][email]", idx, i), e.Email)
|
||||
prm.Add(fmt.Sprintf("%d[contacts][%d][country]", idx, i), e.Country)
|
||||
}
|
||||
// dnsEntries
|
||||
for i, e := range d.DNSEntries {
|
||||
prm.Add(fmt.Sprintf("%d[dnsEntries][%d][name]", idx, i), e.Name)
|
||||
prm.Add(fmt.Sprintf("%d[dnsEntries][%d][expire]", idx, i), fmt.Sprintf("%d", e.TTL))
|
||||
prm.Add(fmt.Sprintf("%d[dnsEntries][%d][type]", idx, i), string(e.Type))
|
||||
prm.Add(fmt.Sprintf("%d[dnsEntries][%d][content]", idx, i), e.Content)
|
||||
}
|
||||
// branding
|
||||
prm.Add(fmt.Sprintf("%d[branding][companyName]", idx), d.Branding.CompanyName)
|
||||
prm.Add(fmt.Sprintf("%d[branding][supportEmail]", idx), d.Branding.SupportEmail)
|
||||
prm.Add(fmt.Sprintf("%d[branding][companyUrl]", idx), d.Branding.CompanyURL)
|
||||
prm.Add(fmt.Sprintf("%d[branding][termsOfUsageUrl]", idx), d.Branding.TermsOfUsageURL)
|
||||
prm.Add(fmt.Sprintf("%d[branding][bannerLine1]", idx), d.Branding.BannerLine1)
|
||||
prm.Add(fmt.Sprintf("%d[branding][bannerLine2]", idx), d.Branding.BannerLine2)
|
||||
prm.Add(fmt.Sprintf("%d[branding][bannerLine3]", idx), d.Branding.BannerLine3)
|
||||
}
|
||||
|
||||
// EncodeArgs returns Domain XML body ready to be passed in the SOAP call
|
||||
func (d Domain) EncodeArgs(key string) string {
|
||||
output := fmt.Sprintf(`<%s xsi:type="ns1:Domain">
|
||||
<name xsi:type="xsd:string">%s</name>
|
||||
<authCode xsi:type="xsd:string">%s</authCode>
|
||||
<isLocked xsi:type="xsd:boolean">%t</isLocked>
|
||||
<registrationDate xsi:type="xsd:string">%s</registrationDate>
|
||||
<renewalDate xsi:type="xsd:string">%s</renewalDate>`,
|
||||
key, d.Name, d.AuthorizationCode, d.IsLocked,
|
||||
d.RegistrationDate.Format("2006-01-02"), d.RenewalDate.Format("2006-01-02"),
|
||||
) + "\n"
|
||||
|
||||
output += Nameservers(d.Nameservers).EncodeArgs("nameservers") + "\n"
|
||||
output += WhoisContacts(d.Contacts).EncodeArgs("contacts") + "\n"
|
||||
output += DNSEntries(d.DNSEntries).EncodeArgs("dnsEntries") + "\n"
|
||||
output += d.Branding.EncodeArgs("branding") + "\n"
|
||||
|
||||
return fmt.Sprintf("%s</%s>", output, key)
|
||||
}
|
||||
|
||||
// Capability represents the possible capabilities a TLD can have
|
||||
type Capability string
|
||||
|
||||
var (
|
||||
// CapabilityRequiresAuthCode defines this TLD requires an auth code
|
||||
// to be transferred
|
||||
CapabilityRequiresAuthCode Capability = "requiresAuthCode"
|
||||
// CapabilityCanRegister defines this TLD can be registered
|
||||
CapabilityCanRegister Capability = "canRegister"
|
||||
// CapabilityCanTransferWithOwnerChange defines this TLD can be transferred
|
||||
// with change of ownership
|
||||
CapabilityCanTransferWithOwnerChange Capability = "canTransferWithOwnerChange"
|
||||
// CapabilityCanTransferWithoutOwnerChange defines this TLD can be
|
||||
// transferred without change of ownership
|
||||
CapabilityCanTransferWithoutOwnerChange Capability = "canTransferWithoutOwnerChange"
|
||||
// CapabilityCanSetLock defines this TLD allows to be locked
|
||||
CapabilityCanSetLock Capability = "canSetLock"
|
||||
// CapabilityCanSetOwner defines this TLD supports setting an owner
|
||||
CapabilityCanSetOwner Capability = "canSetOwner"
|
||||
// CapabilityCanSetContacts defines this TLD supports setting contacts
|
||||
CapabilityCanSetContacts Capability = "canSetContacts"
|
||||
// CapabilityCanSetNameservers defines this TLD supports setting nameservers
|
||||
CapabilityCanSetNameservers Capability = "canSetNameservers"
|
||||
)
|
||||
|
||||
// TLD represents a Transip_Tld object as described at
|
||||
// https://api.transip.nl/docs/transip.nl/class-Transip_Tld.html
|
||||
type TLD struct {
|
||||
Name string `xml:"name"`
|
||||
Price float64 `xml:"price"`
|
||||
RenewalPrice float64 `xml:"renewalPrice"`
|
||||
Capabilities []Capability `xml:"capabilities>item"`
|
||||
RegistrationPeriodLength int64 `xml:"registrationPeriodLength"`
|
||||
CancelTimeFrame int64 `xml:"cancelTimeFrame"`
|
||||
}
|
||||
|
||||
// Nameserver represents a Transip_Nameserver object as described at
|
||||
// https://api.transip.nl/docs/transip.nl/class-Transip_Nameserver.html
|
||||
type Nameserver struct {
|
||||
Hostname string `xml:"hostname"`
|
||||
IPv4Address net.IP `xml:"ipv4"`
|
||||
IPv6Address net.IP `xml:"ipv6"`
|
||||
}
|
||||
|
||||
// Nameservers is just an array of Nameserver
|
||||
// basically only here so it can implement paramsEncoder
|
||||
type Nameservers []Nameserver
|
||||
|
||||
// EncodeParams returns Nameservers parameters ready to be used for constructing a signature
|
||||
func (n Nameservers) EncodeParams(prm gotransip.ParamsContainer) {
|
||||
idx := prm.Len()
|
||||
for i, e := range n {
|
||||
var ipv4, ipv6 string
|
||||
if e.IPv4Address != nil {
|
||||
ipv4 = e.IPv4Address.String()
|
||||
}
|
||||
if e.IPv6Address != nil {
|
||||
ipv6 = e.IPv6Address.String()
|
||||
}
|
||||
prm.Add(fmt.Sprintf("%d[%d][hostname]", idx, i), e.Hostname)
|
||||
prm.Add(fmt.Sprintf("%d[%d][ipv4]", idx, i), ipv4)
|
||||
prm.Add(fmt.Sprintf("%d[%d][ipv6]", idx, i), ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeArgs returns Nameservers XML body ready to be passed in the SOAP call
|
||||
func (n Nameservers) EncodeArgs(key string) string {
|
||||
output := fmt.Sprintf(`<%s SOAP-ENC:arrayType="ns1:Nameserver[%d]" xsi:type="ns1:ArrayOfNameserver">`, key, len(n)) + "\n"
|
||||
for _, e := range n {
|
||||
var ipv4, ipv6 string
|
||||
if e.IPv4Address != nil {
|
||||
ipv4 = e.IPv4Address.String()
|
||||
}
|
||||
if e.IPv6Address != nil {
|
||||
ipv6 = e.IPv6Address.String()
|
||||
}
|
||||
output += fmt.Sprintf(` <item xsi:type="ns1:Nameserver">
|
||||
<hostname xsi:type="xsd:string">%s</hostname>
|
||||
<ipv4 xsi:type="xsd:string">%s</ipv4>
|
||||
<ipv6 xsi:type="xsd:string">%s</ipv6>
|
||||
</item>`, e.Hostname, ipv4, ipv6) + "\n"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s</%s>", output, key)
|
||||
}
|
||||
|
||||
// DNSEntryType represents the possible types of DNS entries
|
||||
type DNSEntryType string
|
||||
|
||||
var (
|
||||
// DNSEntryTypeA represents an A-record
|
||||
DNSEntryTypeA DNSEntryType = "A"
|
||||
// DNSEntryTypeAAAA represents an AAAA-record
|
||||
DNSEntryTypeAAAA DNSEntryType = "AAAA"
|
||||
// DNSEntryTypeCNAME represents a CNAME-record
|
||||
DNSEntryTypeCNAME DNSEntryType = "CNAME"
|
||||
// DNSEntryTypeMX represents an MX-record
|
||||
DNSEntryTypeMX DNSEntryType = "MX"
|
||||
// DNSEntryTypeNS represents an NS-record
|
||||
DNSEntryTypeNS DNSEntryType = "NS"
|
||||
// DNSEntryTypeTXT represents a TXT-record
|
||||
DNSEntryTypeTXT DNSEntryType = "TXT"
|
||||
// DNSEntryTypeSRV represents an SRV-record
|
||||
DNSEntryTypeSRV DNSEntryType = "SRV"
|
||||
)
|
||||
|
||||
// DNSEntry represents a Transip_DnsEntry object as described at
|
||||
// https://api.transip.nl/docs/transip.nl/class-Transip_DnsEntry.html
|
||||
type DNSEntry struct {
|
||||
Name string `xml:"name"`
|
||||
TTL int64 `xml:"expire"`
|
||||
Type DNSEntryType `xml:"type"`
|
||||
Content string `xml:"content"`
|
||||
}
|
||||
|
||||
// DNSEntries is just an array of DNSEntry
|
||||
// basically only here so it can implement paramsEncoder
|
||||
type DNSEntries []DNSEntry
|
||||
|
||||
// EncodeParams returns DNSEntries parameters ready to be used for constructing a signature
|
||||
func (d DNSEntries) EncodeParams(prm gotransip.ParamsContainer) {
|
||||
idx := prm.Len()
|
||||
for i, e := range d {
|
||||
prm.Add(fmt.Sprintf("%d[%d][name]", idx, i), e.Name)
|
||||
prm.Add(fmt.Sprintf("%d[%d][expire]", idx, i), fmt.Sprintf("%d", e.TTL))
|
||||
prm.Add(fmt.Sprintf("%d[%d][type]", idx, i), string(e.Type))
|
||||
prm.Add(fmt.Sprintf("%d[%d][content]", idx, i), e.Content)
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeArgs returns DNSEntries XML body ready to be passed in the SOAP call
|
||||
func (d DNSEntries) EncodeArgs(key string) string {
|
||||
output := fmt.Sprintf(`<%s SOAP-ENC:arrayType="ns1:DnsEntry[%d]" xsi:type="ns1:ArrayOfDnsEntry">`, key, len(d)) + "\n"
|
||||
for _, e := range d {
|
||||
output += fmt.Sprintf(` <item xsi:type="ns1:DnsEntry">
|
||||
<name xsi:type="xsd:string">%s</name>
|
||||
<expire xsi:type="xsd:int">%d</expire>
|
||||
<type xsi:type="xsd:string">%s</type>
|
||||
<content xsi:type="xsd:string">%s</content>
|
||||
</item>`, e.Name, e.TTL, e.Type, e.Content) + "\n"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s</%s>", output, key)
|
||||
}
|
||||
|
||||
// Status reflects the current status of a domain in a check result
|
||||
type Status string
|
||||
|
||||
var (
|
||||
// StatusInYourAccount means he domain name is already in your account
|
||||
StatusInYourAccount Status = "inyouraccount"
|
||||
// StatusUnavailable means the domain name is currently unavailable and can not be registered due to unknown reasons.
|
||||
StatusUnavailable Status = "unavailable"
|
||||
// StatusNotFree means the domain name has already been registered
|
||||
StatusNotFree Status = "notfree"
|
||||
// StatusFree means the domain name is currently free, is available and can be registered
|
||||
StatusFree Status = "free"
|
||||
// StatusInternalPull means the domain name is currently registered at TransIP and is available to be pulled from another account to yours.
|
||||
StatusInternalPull Status = "internalpull"
|
||||
// StatusInternalPush means the domain name is currently registered at TransIP in your accounta and is available to be pushed to another account.
|
||||
StatusInternalPush Status = "internalpush"
|
||||
)
|
||||
|
||||
// CheckResult represents a Transip_DomainCheckResult object as described at
|
||||
// https://api.transip.nl/docs/transip.nl/class-Transip_DomainCheckResult.html
|
||||
type CheckResult struct {
|
||||
DomainName string `xml:"domainName"`
|
||||
Status Status `xml:"status"`
|
||||
Actions []Action `xml:"actions>item"`
|
||||
}
|
||||
|
||||
// Branding represents a Transip_DomainBranding object as described at
|
||||
// https://api.transip.nl/docs/transip.nl/class-Transip_DomainBranding.html
|
||||
type Branding struct {
|
||||
CompanyName string `xml:"companyName"`
|
||||
SupportEmail string `xml:"supportEmail"`
|
||||
CompanyURL string `xml:"companyUrl"`
|
||||
TermsOfUsageURL string `xml:"termsOfUsageUrl"`
|
||||
BannerLine1 string `xml:"bannerLine1"`
|
||||
BannerLine2 string `xml:"bannerLine2"`
|
||||
BannerLine3 string `xml:"bannerLine3"`
|
||||
}
|
||||
|
||||
// EncodeParams returns WhoisContacts parameters ready to be used for constructing a signature
|
||||
func (b Branding) EncodeParams(prm gotransip.ParamsContainer) {
|
||||
idx := prm.Len()
|
||||
prm.Add(fmt.Sprintf("%d[companyName]", idx), b.CompanyName)
|
||||
prm.Add(fmt.Sprintf("%d[supportEmail]", idx), b.SupportEmail)
|
||||
prm.Add(fmt.Sprintf("%d[companyUrl]", idx), b.CompanyURL)
|
||||
prm.Add(fmt.Sprintf("%d[termsOfUsageUrl]", idx), b.TermsOfUsageURL)
|
||||
prm.Add(fmt.Sprintf("%d[bannerLine1]", idx), b.BannerLine1)
|
||||
prm.Add(fmt.Sprintf("%d[bannerLine2]", idx), b.BannerLine2)
|
||||
prm.Add(fmt.Sprintf("%d[bannerLine3]", idx), b.BannerLine3)
|
||||
}
|
||||
|
||||
// EncodeArgs returns Branding XML body ready to be passed in the SOAP call
|
||||
func (b Branding) EncodeArgs(key string) string {
|
||||
return fmt.Sprintf(`<branding xsi:type="ns1:DomainBranding">
|
||||
<companyName xsi:type="xsd:string">%s</companyName>
|
||||
<supportEmail xsi:type="xsd:string">%s</supportEmail>
|
||||
<companyUrl xsi:type="xsd:string">%s</companyUrl>
|
||||
<termsOfUsageUrl xsi:type="xsd:string">%s</termsOfUsageUrl>
|
||||
<bannerLine1 xsi:type="xsd:string">%s</bannerLine1>
|
||||
<bannerLine2 xsi:type="xsd:string">%s</bannerLine2>
|
||||
<bannerLine3 xsi:type="xsd:string">%s</bannerLine3>
|
||||
</branding>`, b.CompanyName, b.SupportEmail, b.CompanyURL, b.TermsOfUsageURL, b.BannerLine1, b.BannerLine2, b.BannerLine3)
|
||||
}
|
||||
|
||||
// Action reflects the available actions to perform on a domain
|
||||
type Action string
|
||||
|
||||
var (
|
||||
// ActionRegister registers a domain
|
||||
ActionRegister Action = "register"
|
||||
// ActionTransfer transfers a domain to another provider
|
||||
ActionTransfer Action = "transfer"
|
||||
// ActionInternalPull transfers a domain to another account at TransIP
|
||||
ActionInternalPull Action = "internalpull"
|
||||
)
|
||||
|
||||
// ActionResult represents a Transip_DomainAction object as described at
|
||||
// https://api.transip.nl/docs/transip.nl/class-Transip_DomainAction.html
|
||||
type ActionResult struct {
|
||||
Name string `xml:"name"`
|
||||
HasFailed bool `xml:"hasFailed"`
|
||||
Message string `xml:"message"`
|
||||
}
|
||||
|
||||
// WhoisContact represents a TransIP_WhoisContact object
|
||||
// as described at https://api.transip.nl/docs/transip.nl/class-Transip_WhoisContact.html
|
||||
type WhoisContact struct {
|
||||
Type string `xml:"type"`
|
||||
FirstName string `xml:"firstName"`
|
||||
MiddleName string `xml:"middleName"`
|
||||
LastName string `xml:"lastName"`
|
||||
CompanyName string `xml:"companyName"`
|
||||
CompanyKvk string `xml:"companyKvk"`
|
||||
CompanyType string `xml:"companyType"`
|
||||
Street string `xml:"street"`
|
||||
Number string `xml:"number"`
|
||||
PostalCode string `xml:"postalCode"`
|
||||
City string `xml:"city"`
|
||||
PhoneNumber string `xml:"phoneNumber"`
|
||||
FaxNumber string `xml:"faxNumber"`
|
||||
Email string `xml:"email"`
|
||||
Country string `xml:"country"`
|
||||
}
|
||||
|
||||
// WhoisContacts is just an array of WhoisContact
|
||||
// basically only here so it can implement paramsEncoder
|
||||
type WhoisContacts []WhoisContact
|
||||
|
||||
// EncodeParams returns WhoisContacts parameters ready to be used for constructing a signature
|
||||
func (w WhoisContacts) EncodeParams(prm gotransip.ParamsContainer) {
|
||||
idx := prm.Len()
|
||||
for i, e := range w {
|
||||
prm.Add(fmt.Sprintf("%d[%d][type]", idx, i), e.Type)
|
||||
prm.Add(fmt.Sprintf("%d[%d][firstName]", idx, i), e.FirstName)
|
||||
prm.Add(fmt.Sprintf("%d[%d][middleName]", idx, i), e.MiddleName)
|
||||
prm.Add(fmt.Sprintf("%d[%d][lastName]", idx, i), e.LastName)
|
||||
prm.Add(fmt.Sprintf("%d[%d][companyName]", idx, i), e.CompanyName)
|
||||
prm.Add(fmt.Sprintf("%d[%d][companyKvk]", idx, i), e.CompanyKvk)
|
||||
prm.Add(fmt.Sprintf("%d[%d][companyType]", idx, i), e.CompanyType)
|
||||
prm.Add(fmt.Sprintf("%d[%d][street]", idx, i), e.Street)
|
||||
prm.Add(fmt.Sprintf("%d[%d][number]", idx, i), e.Number)
|
||||
prm.Add(fmt.Sprintf("%d[%d][postalCode]", idx, i), e.PostalCode)
|
||||
prm.Add(fmt.Sprintf("%d[%d][city]", idx, i), e.City)
|
||||
prm.Add(fmt.Sprintf("%d[%d][phoneNumber]", idx, i), e.PhoneNumber)
|
||||
prm.Add(fmt.Sprintf("%d[%d][faxNumber]", idx, i), e.FaxNumber)
|
||||
prm.Add(fmt.Sprintf("%d[%d][email]", idx, i), e.Email)
|
||||
prm.Add(fmt.Sprintf("%d[%d][country]", idx, i), e.Country)
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeArgs returns WhoisContacts XML body ready to be passed in the SOAP call
|
||||
func (w WhoisContacts) EncodeArgs(key string) string {
|
||||
output := fmt.Sprintf(`<%s SOAP-ENC:arrayType="ns1:WhoisContact[%d]" xsi:type="ns1:ArrayOfWhoisContact">`, key, len(w)) + "\n"
|
||||
for _, e := range w {
|
||||
output += fmt.Sprintf(` <item xsi:type="ns1:WhoisContact">
|
||||
<type xsi:type="xsd:string">%s</type>
|
||||
<firstName xsi:type="xsd:string">%s</firstName>
|
||||
<middleName xsi:type="xsd:string">%s</middleName>
|
||||
<lastName xsi:type="xsd:string">%s</lastName>
|
||||
<companyName xsi:type="xsd:string">%s</companyName>
|
||||
<companyKvk xsi:type="xsd:string">%s</companyKvk>
|
||||
<companyType xsi:type="xsd:string">%s</companyType>
|
||||
<street xsi:type="xsd:string">%s</street>
|
||||
<number xsi:type="xsd:string">%s</number>
|
||||
<postalCode xsi:type="xsd:string">%s</postalCode>
|
||||
<city xsi:type="xsd:string">%s</city>
|
||||
<phoneNumber xsi:type="xsd:string">%s</phoneNumber>
|
||||
<faxNumber xsi:type="xsd:string">%s</faxNumber>
|
||||
<email xsi:type="xsd:string">%s</email>
|
||||
<country xsi:type="xsd:string">%s</country>
|
||||
</item>`, e.Type, e.FirstName, e.MiddleName, e.LastName, e.CompanyName,
|
||||
e.CompanyKvk, e.CompanyType, e.Street, e.Number, e.PostalCode, e.City,
|
||||
e.PhoneNumber, e.FaxNumber, e.Email, e.Country) + "\n"
|
||||
}
|
||||
|
||||
return output + fmt.Sprintf("</%s>", key)
|
||||
}
|
49
vendor/github.com/transip/gotransip/sign.go
generated
vendored
Normal file
49
vendor/github.com/transip/gotransip/sign.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package gotransip
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var (
|
||||
asn1Header = []byte{
|
||||
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
|
||||
0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40,
|
||||
}
|
||||
)
|
||||
|
||||
func signWithKey(params *soapParams, key []byte) (string, error) {
|
||||
// create SHA512 hash of given parameters
|
||||
h := sha512.New()
|
||||
h.Write([]byte(params.Encode()))
|
||||
|
||||
// prefix ASN1 header to SHA512 hash
|
||||
digest := append(asn1Header, h.Sum(nil)...)
|
||||
|
||||
// prepare key struct
|
||||
block, _ := pem.Decode(key)
|
||||
if block == nil {
|
||||
return "", errors.New("could not decode private key")
|
||||
}
|
||||
parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not parse private key: %s", err.Error())
|
||||
}
|
||||
|
||||
pkey := parsed.(*rsa.PrivateKey)
|
||||
|
||||
enc, err := rsa.SignPKCS1v15(rand.Reader, pkey, crypto.Hash(0), digest)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not sign data: %s", err.Error())
|
||||
}
|
||||
|
||||
return url.QueryEscape(base64.StdEncoding.EncodeToString(enc)), nil
|
||||
}
|
419
vendor/github.com/transip/gotransip/soap.go
generated
vendored
Normal file
419
vendor/github.com/transip/gotransip/soap.go
generated
vendored
Normal file
@ -0,0 +1,419 @@
|
||||
package gotransip
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// format for SOAP envelopes
|
||||
soapEnvelopeFixture string = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="%s">
|
||||
<SOAP-ENV:Body>%s</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>`
|
||||
)
|
||||
|
||||
// getSOAPArgs returns XML representing given name and argument as SOAP body
|
||||
func getSOAPArgs(name string, input ...string) []byte {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString(fmt.Sprintf("<ns1:%s>", name))
|
||||
for _, x := range input {
|
||||
buf.WriteString(x)
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("</ns1:%s>", name))
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// getSOAPArg returns XML representing given input argument as SOAP parameters
|
||||
// in combination with getSOAPArgs you can build SOAP body
|
||||
func getSOAPArg(name string, input interface{}) (output string) {
|
||||
switch input.(type) {
|
||||
case []string:
|
||||
i := input.([]string)
|
||||
output = fmt.Sprintf(`<%s SOAP-ENC:arrayType="xsd:string[%d]" xsi:type="ns1:ArrayOfString">`, name, len(i))
|
||||
for _, x := range i {
|
||||
output = output + fmt.Sprintf(`<item xsi:type="xsd:string">%s</item>`, x)
|
||||
}
|
||||
output = output + fmt.Sprintf("</%s>", name)
|
||||
case string:
|
||||
output = fmt.Sprintf(`<%s xsi:type="xsd:string">%s</%s>`, name, input, name)
|
||||
case int, int32, int64:
|
||||
output = fmt.Sprintf(`<%s xsi:type="xsd:integer">%d</%s>`, name, input, name)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type soapFault struct {
|
||||
Code string `xml:"faultcode,omitempty"`
|
||||
Description string `xml:"faultstring,omitempty"`
|
||||
}
|
||||
|
||||
func (s soapFault) String() string {
|
||||
return fmt.Sprintf("SOAP Fault %s: %s", s.Code, s.Description)
|
||||
}
|
||||
|
||||
// paramsEncoder allows SoapParams to hook into encoding theirselves, useful when
|
||||
// fields consist of complex structs
|
||||
type paramsEncoder interface {
|
||||
EncodeParams(ParamsContainer)
|
||||
EncodeArgs(string) string
|
||||
}
|
||||
|
||||
// ParamsContainer is the interface a type should implement to be able to hold
|
||||
// SOAP parameters
|
||||
type ParamsContainer interface {
|
||||
Len() int
|
||||
Add(string, interface{})
|
||||
}
|
||||
|
||||
// soapParams is a utility to make sure parameter data is encoded into a query
|
||||
// in the same order as we set them. The TransIP API requires this order for
|
||||
// verifying the signature
|
||||
type soapParams struct {
|
||||
keys []string
|
||||
values []interface{}
|
||||
}
|
||||
|
||||
// Add adds parameter data to the end of this SoapParams
|
||||
func (s *soapParams) Add(k string, v interface{}) {
|
||||
if s.keys == nil {
|
||||
s.keys = make([]string, 0)
|
||||
}
|
||||
|
||||
if s.values == nil {
|
||||
s.values = make([]interface{}, 0)
|
||||
}
|
||||
|
||||
s.keys = append(s.keys, k)
|
||||
s.values = append(s.values, v)
|
||||
}
|
||||
|
||||
// Len returns amount of parameters set in this SoapParams
|
||||
func (s soapParams) Len() int {
|
||||
return len(s.keys)
|
||||
}
|
||||
|
||||
// Encode returns a URL-like query string that can be used to generate a request's
|
||||
// signature. It's similar to url.Values.Encode() but without sorting of the keys
|
||||
// and based on the value's type it tries to encode accordingly.
|
||||
func (s soapParams) Encode() string {
|
||||
var buf bytes.Buffer
|
||||
var key string
|
||||
|
||||
for i, v := range s.values {
|
||||
// if this is not the first parameter, prefix with &
|
||||
if i > 0 {
|
||||
buf.WriteString("&")
|
||||
}
|
||||
|
||||
// for empty data fields, don't encode anything
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
key = s.keys[i]
|
||||
|
||||
switch v.(type) {
|
||||
case []string:
|
||||
c := v.([]string)
|
||||
for j, cc := range c {
|
||||
if j > 0 {
|
||||
buf.WriteString("&")
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s[%d]=", key, j))
|
||||
buf.WriteString(strings.Replace(url.QueryEscape(cc), "+", "%20", -1))
|
||||
}
|
||||
case string:
|
||||
c := v.(string)
|
||||
buf.WriteString(fmt.Sprintf("%s=", key))
|
||||
buf.WriteString(strings.Replace(url.QueryEscape(c), "+", "%20", -1))
|
||||
case int, int8, int16, int32, int64:
|
||||
buf.WriteString(fmt.Sprintf("%s=", key))
|
||||
buf.WriteString(fmt.Sprintf("%d", v))
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type soapHeader struct {
|
||||
XMLName struct{} `xml:"Header"`
|
||||
Contents []byte `xml:",innerxml"`
|
||||
}
|
||||
|
||||
type soapBody struct {
|
||||
XMLName struct{} `xml:"Body"`
|
||||
Contents []byte `xml:",innerxml"`
|
||||
}
|
||||
|
||||
type soapResponse struct {
|
||||
Response struct {
|
||||
InnerXML []byte `xml:",innerxml"`
|
||||
} `xml:"return"`
|
||||
}
|
||||
|
||||
type soapEnvelope struct {
|
||||
XMLName struct{} `xml:"Envelope"`
|
||||
Header soapHeader
|
||||
Body soapBody
|
||||
}
|
||||
|
||||
// SoapRequest holds all information for perfoming a SOAP request
|
||||
// Arguments to the request can be specified with AddArgument
|
||||
// If padding is defined, the SOAP response will be parsed after it being padded
|
||||
// with items in Padding in reverse order
|
||||
type SoapRequest struct {
|
||||
Service string
|
||||
Method string
|
||||
params *soapParams // params used for creating signature
|
||||
args []string // XML body arguments
|
||||
Padding []string
|
||||
}
|
||||
|
||||
// AddArgument adds an argument to the SoapRequest; the arguments ared used to
|
||||
// fill the XML request body as well as to create a valid signature for the
|
||||
// request
|
||||
func (sr *SoapRequest) AddArgument(key string, value interface{}) {
|
||||
if sr.params == nil {
|
||||
sr.params = &soapParams{}
|
||||
}
|
||||
|
||||
// check if value implements paramsEncoder
|
||||
if pe, ok := value.(paramsEncoder); ok {
|
||||
sr.args = append(sr.args, pe.EncodeArgs(key))
|
||||
pe.EncodeParams(sr.params)
|
||||
return
|
||||
}
|
||||
|
||||
switch value.(type) {
|
||||
case []string:
|
||||
sr.params.Add(fmt.Sprintf("%d", sr.params.Len()), value)
|
||||
sr.args = append(sr.args, getSOAPArg(key, value))
|
||||
case string:
|
||||
sr.params.Add(fmt.Sprintf("%d", sr.params.Len()), value)
|
||||
sr.args = append(sr.args, getSOAPArg(key, value.(string)))
|
||||
case int, int8, int16, int32, int64:
|
||||
sr.params.Add(fmt.Sprintf("%d", sr.params.Len()), value)
|
||||
sr.args = append(sr.args, getSOAPArg(key, fmt.Sprintf("%d", value)))
|
||||
default:
|
||||
// check if value implements the String interface
|
||||
if str, ok := value.(fmt.Stringer); ok {
|
||||
sr.params.Add(fmt.Sprintf("%d", sr.params.Len()), str.String())
|
||||
sr.args = append(sr.args, getSOAPArg(key, str.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sr SoapRequest) getEnvelope() string {
|
||||
return fmt.Sprintf(soapEnvelopeFixture, transipAPIHost, getSOAPArgs(sr.Method, sr.args...))
|
||||
}
|
||||
|
||||
type soapClient struct {
|
||||
Login string
|
||||
Mode APIMode
|
||||
PrivateKey []byte
|
||||
}
|
||||
|
||||
// httpReqForSoapRequest creates the HTTP request for a specific SoapRequest
|
||||
// this includes setting the URL, POST body and cookies
|
||||
func (s soapClient) httpReqForSoapRequest(req SoapRequest) (*http.Request, error) {
|
||||
// format URL
|
||||
url := fmt.Sprintf("https://%s/soap/?service=%s", transipAPIHost, req.Service)
|
||||
|
||||
// create HTTP request
|
||||
// TransIP API SOAP requests are always POST requests
|
||||
httpReq, err := http.NewRequest("POST", url, strings.NewReader(req.getEnvelope()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// generate a number-used-once, a.k.a. nonce
|
||||
// seeding the RNG is important if we want to do prevent using the same nonce
|
||||
// in 2 sequential requests
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
nonce := fmt.Sprintf("%d", rand.Int())
|
||||
// set time of request, used later for signature as well
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
|
||||
// set cookies required for the request
|
||||
// most of these cookies are used for the signature as well so they should
|
||||
// obviously match
|
||||
httpReq.AddCookie(&http.Cookie{
|
||||
Name: "login",
|
||||
Value: s.Login,
|
||||
})
|
||||
httpReq.AddCookie(&http.Cookie{
|
||||
Name: "mode",
|
||||
Value: string(s.Mode),
|
||||
})
|
||||
httpReq.AddCookie(&http.Cookie{
|
||||
Name: "timestamp",
|
||||
Value: timestamp,
|
||||
})
|
||||
httpReq.AddCookie(&http.Cookie{
|
||||
Name: "nonce",
|
||||
Value: nonce,
|
||||
})
|
||||
|
||||
// add params required for signature to the request parameters
|
||||
if req.params == nil {
|
||||
req.params = &soapParams{}
|
||||
}
|
||||
// TransIP API is quite picky on the order of the parameters
|
||||
// so don't change anything in the order below
|
||||
req.params.Add("__method", req.Method)
|
||||
req.params.Add("__service", req.Service)
|
||||
req.params.Add("__hostname", transipAPIHost)
|
||||
req.params.Add("__timestamp", timestamp)
|
||||
req.params.Add("__nonce", nonce)
|
||||
|
||||
signature, err := signWithKey(req.params, s.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add signature of the request to the cookies as well
|
||||
httpReq.AddCookie(&http.Cookie{
|
||||
Name: "signature",
|
||||
Value: signature,
|
||||
})
|
||||
|
||||
return httpReq, nil
|
||||
}
|
||||
|
||||
func parseSoapResponse(data []byte, padding []string, statusCode int, result interface{}) error {
|
||||
// try to decode the resulting XML
|
||||
var env soapEnvelope
|
||||
if err := xml.Unmarshal(data, &env); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// try to decode the body to a soapFault
|
||||
var fault soapFault
|
||||
if err := xml.Unmarshal(env.Body.Contents, &fault); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// by checking fault's Code, we can determine if the response body in fact
|
||||
// was a SOAP fault and if it was: return it as an error
|
||||
if len(fault.Code) > 0 {
|
||||
return errors.New(fault.String())
|
||||
}
|
||||
|
||||
// try to decode into soapResponse
|
||||
sr := soapResponse{}
|
||||
if err := xml.Unmarshal(env.Body.Contents, &sr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if the response was empty and HTTP status was 200, consider it a success
|
||||
if len(sr.Response.InnerXML) == 0 && statusCode == 200 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// it seems like xml.Unmarshal won't work well on the most outer element
|
||||
// so even when no Padding is defined, always pad with "transip" element
|
||||
p := append([]string{"transip"}, padding...)
|
||||
innerXML := padXMLData(sr.Response.InnerXML, p)
|
||||
|
||||
// try to decode to given result interface
|
||||
return xml.Unmarshal([]byte(innerXML), &result)
|
||||
}
|
||||
|
||||
func (s *soapClient) call(req SoapRequest, result interface{}) error {
|
||||
// get http request for soap request
|
||||
httpReq, err := s.httpReqForSoapRequest(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create HTTP client and do the actual request
|
||||
client := &http.Client{Timeout: time.Second * 10}
|
||||
// make sure to verify the validity of remote certificate
|
||||
// this is the default, but adding this flag here makes it easier to toggle
|
||||
// it for testing/debugging
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
}
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("request error:\n%s", err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// read entire response body
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse SOAP response into given result interface
|
||||
return parseSoapResponse(b, req.Padding, resp.StatusCode, result)
|
||||
}
|
||||
|
||||
// apply given padding around the XML data fed into this function
|
||||
// padding is applied in reverse order, so last element of padding is the
|
||||
// innermost element in the resulting XML
|
||||
func padXMLData(data []byte, padding []string) []byte {
|
||||
// get right information from padding elements by matching to regex
|
||||
re, _ := regexp.Compile("^<?(?:([^ ]+) )?([^>]+)>?$")
|
||||
|
||||
var prefix, suffix []byte
|
||||
var tag, attr string
|
||||
// go over each padding element
|
||||
for i := len(padding); i > 0; i-- {
|
||||
res := re.FindStringSubmatch(padding[i-1])
|
||||
// no attribute was given
|
||||
if len(res[1]) == 0 {
|
||||
tag = res[2]
|
||||
attr = ""
|
||||
} else {
|
||||
tag = res[1]
|
||||
attr = " " + res[2]
|
||||
}
|
||||
|
||||
prefix = []byte(fmt.Sprintf("<%s%s>", tag, attr))
|
||||
suffix = []byte(fmt.Sprintf("</%s>", tag))
|
||||
data = append(append(prefix, data...), suffix...)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// TestParamsContainer is only useful for unit testing the ParamsContainer
|
||||
// implementation of other type
|
||||
type TestParamsContainer struct {
|
||||
Prm string
|
||||
}
|
||||
|
||||
// Add just makes sure we use Len(), key and value in the result so it can be
|
||||
// tested
|
||||
func (t *TestParamsContainer) Add(key string, value interface{}) {
|
||||
var prefix string
|
||||
if t.Len() > 0 {
|
||||
prefix = "&"
|
||||
}
|
||||
t.Prm = t.Prm + prefix + fmt.Sprintf("%d%s=%s", t.Len(), key, value)
|
||||
}
|
||||
|
||||
// Len returns current length of test data in TestParamsContainer
|
||||
func (t TestParamsContainer) Len() int {
|
||||
return len(t.Prm)
|
||||
}
|
37
vendor/github.com/transip/gotransip/util/util.go
generated
vendored
Normal file
37
vendor/github.com/transip/gotransip/util/util.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"time"
|
||||
)
|
||||
|
||||
// KeyValueXML resembles the complex struct for getting key/value pairs from XML
|
||||
type KeyValueXML struct {
|
||||
Cont []struct {
|
||||
Item []struct {
|
||||
Key string `xml:"key"`
|
||||
Value string `xml:"value"`
|
||||
} `xml:"item"`
|
||||
} `xml:"item"`
|
||||
}
|
||||
|
||||
// XMLTime is a custom type to decode XML values to time.Time directly
|
||||
type XMLTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// UnmarshalXML is implemented to be able act as custom XML type
|
||||
// it tries to parse time from given elements value
|
||||
func (x *XMLTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
var v string
|
||||
if err := d.DecodeElement(&v, &start); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p, _ := time.Parse("2006-01-02 15:04:05", v); !p.IsZero() {
|
||||
*x = XMLTime{p}
|
||||
} else if p, _ := time.Parse("2006-01-02", v); !p.IsZero() {
|
||||
*x = XMLTime{p}
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user