benkonrath / transip-api

Python implementation for the TransIP API
https://transip-api.readthedocs.io/en/latest/
MIT License
23 stars 23 forks source link

Implement DomainService.transferWithoutOwnerChange(Domain domain, xs:string authCode, ) #9

Closed korneel closed 8 years ago

korneel commented 10 years ago

SOAP contract:

transferWithoutOwnerChange(Domain domain, xs:string authCode, )

Python implementation in service/domain.py:

def transfer_without_owner_change(self, domain_name, auth_code):
    cookie = self.build_cookie(mode=MODE_RO, method='transferWithoutOwnerChange', parameters=[domain_name, auth_code])
    self.update_cookie(cookie)

    return self.soap_client.service.transferWithoutOwnerChange(domain_name, auth_code)

It returns this error:

suds.WebFault: Server raised fault: 'Invalid API signature, signature does not match the request. (timestamp: 0.01605300 1392806431)'

I think the algorithm in client.py that calculates the signature currently supports only one parameter. What needs to be done in order to support multiple parameters?

mhogerheijde commented 10 years ago

Hi Korneel,

As you can see in the tests, I do test building the signature with mutliple additional parameters.

So this could mean that this is the wrong way after all, or something else is amiss.

korneel commented 10 years ago

The above code was wrong, because the first parameter should be of the SOAP type Domain. But that results in the same error:

def main():
    """ The main method """
    domain_service = DomainService()

    # names = domain_service.get_domain_names()
    # print names

    domain_info = domain_service.get_info('example.com')
    # print domain_info

    transfer = domain_service.transfer_without_owner_change(domain_info, 'xxx-xxx-xxx-xxx-xxx')
    print transfer

This is the implementation in domain.py:

def transfer_without_owner_change(self, domain, auth_code):
    # domain = self.soap_client.factory.create('Domain')
    # domain.name = 'example.com'

    cookie = self.build_cookie(mode=MODE_RO, method='transferWithoutOwnerChange', parameters=[domain, auth_code])
    self.update_cookie(cookie)

    return self.soap_client.service.transferWithoutOwnerChange(domain, auth_code)

This is what the domain parameter looks like in SOAP:

         <domain xsi:type="ns1:Domain">
            <name xsi:type="ns3:string">example.com</name>
            <nameservers xsi:type="ns1:ArrayOfNameserver"/>
            <contacts xsi:type="ns1:ArrayOfWhoisContact"/>
            <dnsEntries xsi:type="ns1:ArrayOfDnsEntry"/>
            <branding xsi:type="ns1:DomainBranding">
               <companyName xsi:type="ns3:string"/>
               <supportEmail xsi:type="ns3:string"/>
               <companyUrl xsi:type="ns3:string"/>
               <termsOfUsageUrl xsi:type="ns3:string"/>
               <bannerLine1 xsi:type="ns3:string"/>
               <bannerLine2 xsi:type="ns3:string"/>
               <bannerLine3 xsi:type="ns3:string"/>
            </branding>
         </domain>
         <authCode xsi:type="ns3:string">xxx-xxx-xxx-xxx-xxx</authCode>
korneel commented 10 years ago

I think I have found the missing link. I will convert this PHP code to Python, fork the transip-api repository and send you a pull request once I have figured it all out.

/**
 * Encodes the given paramaters into a url encoded string based upon RFC 3986.
 *
 * @param  mixed   $parameters  The parameters to encode.
 * @param  string  $keyPrefix   Key prefix.
 * @return string               The given parameters encoded according to RFC 3986.
 */
protected static function _encodeParameters($parameters, $keyPrefix = null)
{
    if(!is_array($parameters) && !is_object($parameters))
        return self::_urlencode($parameters);

    $encodedData = array();

    foreach($parameters as $key => $value)
    {
        $encodedKey = is_null($keyPrefix)
            ? self::_urlencode($key)
            : $keyPrefix . '[' . self::_urlencode($key) . ']';

        if(is_array($value) || is_object($value))
        {
            $encodedData[] = self::_encodeParameters($value, $encodedKey);
        }
        else
        {
            $encodedData[] = $encodedKey . '=' . self::_urlencode($value);
        }
    }

    return implode('&', $encodedData);
}
mhogerheijde commented 10 years ago

So do you know what the expected string should be other than 0=domain.name&1=auth-code&__method=trans...?

I'll wait for your pull request before changing anything about the signing logic ;)

korneel commented 10 years ago

This is the correct form:

0[name]=example.com&&&&0[branding]=&0[authCode]=&0[isLocked]=&0[registrationDate]=&0[renewalDate]=&1=xxx-xxx-xxx-xxx-xxx&__method=retryTransferWithDifferentAuthCode&__service=DomainService&__hostname=api.transip.be&__timestamp=1392906906&__nonce=5306129ab4be17.17042563

So far I get this with my code:

0[name]=example.com&&&&0[branding][companyName]=&&0[branding][termsOfUsageUrl]=&&0[branding][companyUrl]=&&0[branding][supportEmail]=&&0[branding][bannerLine3]=&&0[branding][bannerLine2]=&&0[branding][bannerLine1]=&&0[renewalDate]=&&0[isLocked]=&&0[authCode]=&&&0[registrationDate]=&1=xxx-xxx-xxx-xxx-xxx&__method=retryTransferWithDifferentAuthCode&__service=DomainService&__hostname=api.transip.be&__timestamp=1392914058&__nonce=2d8ecb7e-290c-4d5b-950e-0ab07360

Maybe the order of the parameters is incorrect. In that case I suspect the order has to be the same as defined in the PHP class Domain.php but I don't think the order matters. So maybe it's because the branding parameters shouldn't be included.

Do you spot something that could be causing the error?

korneel commented 10 years ago

It still doesn't work and it's getting too time-consuming, so I will revert to just using the PHP library.

mhogerheijde commented 10 years ago

Hi Korneel,

I'm sorry to hear that! For me, this project is not top-priority, so if you need something working "today" then you're better of using one of the existing implementations.

Thanks for your feedback and help so far!

mhogerheijde commented 10 years ago

By the way, ordering is important for the signature