Closed rollsappletree closed 10 years ago
Hi @rollsappletree,
thanks for getting in touch! I'm sorry it has taken a while to get back to you... Would you mind sharing a bit more about what test you've written for the call right now?
Kind regards, David
I'd also be interested in how to test the WSSE. As this doesn't work unfortunately (with Chrome REST tester it works):
class APIControllerTest extends WebTestCase
{
public function testWSSE()
{
$client = static::createClient();
$client->request(
'GET',
'/de/api/user/testauthorization',
array(),
array(),
array(
'Authorization' => 'Authorization profile="UsernameToken"',
'X-WSSE' => 'UsernameToken Username="test16", PasswordDigest="ZLG9XVPRIGNqu/hee2pw2EL0Lak=", Nonce="YzhhZTFiM2YzMWQwOWNlOA==", Created="2014-06-08T21:09:04Z"'
)
);
$this->assertTrue($client->getResponse()->isSuccessful());
}
Hi @timtailor,
could you try giving 'HTTP_Authorization' and 'HTTP_X-WSSE' instead of respectively 'Authorization' and 'X-WSSE' a go? That should do the trick...
Let me know how that works out for you!
Kind regards, David
P.S. I recommend generating the digest dynamically in your tests, but I guess you just posted the "static" X-WSSE value above as a quick example...
FYI: here's a more detailed example - we make use of something along these lines for testing our APIs:
use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
//...
private function getUsername()
{
return self::$kernel->getContainer()->getParameter('api_user_name'); /* eg. "username" */
}
private function getSecret()
{
return self::$kernel->getContainer()->getParameter('api_user_password'); /* eg. "userpassword" */
}
private function getSalt()
{
return self::$kernel->getContainer()->getParameter('api_user_salt'); /* eg. "usersalt" */
}
private function getWSSEAuthEncoderAlgorithm()
{
return self::$kernel->getContainer()->getParameter('api_wsse_auth_encoder_algorithm'); /* eg. sha512 */
}
private function getWSSEAuthEncoderEncodeHashAsBase64()
{
return self::$kernel->getContainer()->getParameter('api_wsse_auth_encoder_encode_hash_as_base64'); /* eg. true */
}
private function getWSSEAuthEncoderIterations()
{
return self::$kernel->getContainer()->getParameter('api_wsse_auth_encoder_iterations'); /* eg. 1000 */
}
private function getEncoder()
{
return new MessageDigestPasswordEncoder(
$this->getWSSEAuthEncoderAlgorithm(), /* helper method */
$this->getWSSEAuthEncoderEncodeHashAsBase64(), /* helper method */
$this->getWSSEAuthEncoderIterations() /* helper method */
);
}
private function generateDigest($secret, $salt, $nonce, $created)
{
return $this->getEncoder()->encodePassword(
sprintf(
'%s%s%s',
base64_decode($nonce),
$created,
$secret
),
$salt
);
}
protected function setUp()
{
$this->serializer = new Serializer(
array(),
array(
'json' => new JsonEncoder(),
'xml' => new XmlEncoder()
)
);
}
public function test_WSSE_API_Call()
{
$client = static::createClient();
$username = $this->getUsername(); /* helper method */
$created = gmdate(DATE_ISO8601);
$nonce = uniqid();
$digest = $this->generateDigest(
$this->getSecret(), /* helper method */
$this->getSalt(), /* helper method */
$nonce,
$created
); /* helper method */
$client->request(
'GET',
'/api/call.json',
array(),
array(),
array(
'HTTP_Authorization' => 'WSSE profile="UsernameToken"',
'HTTP_X-WSSE' => 'UsernameToken Username="'.$username.'", PasswordDigest="'.$digest.'", Nonce="'.$nonce.'", Created="'.$created.'"',
'HTTP_CONTENT_TYPE' => 'application/json'
),
$this->serializer->encode(array(), 'json')
);
$this->assertEquals(200,$client->getResponse()->getStatusCode());
}
If you make use of actual parameters.yml parameters and use those in your security.yml as well it'll be easier in the long run...
Hope this helps! David
Hi David,
thanks, adding the "HTTP_' really did the trick. Now i will try to replace my static string with your long example above!
I will confirm back later... Tim
Hi Tim,
great!
FYI: I've updated my comment above with some missing bits... Do let me know how it goes, so we can close this issue :-)
Thanks in advance for your feedback! David
Sorry, but unfortunately i get a 401 error. It's probably only because of the configuration and not the test itself.
I hardcoded the parameters in your example (user, password in plain text as it is before encoding). In my security.yml i didn't change any setting regarding encoder, etc.
security:
encoders:
FOS\UserBundle\Model\UserInterface:
algorithm: sha512
iterations: 5
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
wsse_secured:
pattern: ^/api/.*
wsse:
realm: "Secured API"
profile: "UsernameToken"
lifetime: 5000
provider: fos_userbundle
anonymous: true
I currently override the Provider->getSalt to return an empty string.
Then i tried all different combinations of parameters in the test (sha1, sha512, 5 iterations, 5000 iterations, etc.) but it didn't work. The digest ist different than expected and thus i get an 401. Any ideas?
Hi Tim,
without specifying the encoder-settings in the wsse-section of your config, the provider is going to go with sha1 with 1 iteration, encoding the hash as base 64. Please have a look at "Specify a custom digest algorithm" in the README and match the settings with the encoder you're using, eg.
firewalls:
wsse_secured:
#...
wsse:
#...
encoder: #digest algorithm
algorithm: sha512
encodeHashAsBase64: true
iterations: 5
That should do the trick!
Hope this helps, David
I tried that already (sha1 & sha512, iterations 1 & 5) without success. But i think you can close this issue anyways, as it was about a functional WSSE test (which you provided in all detail).
I still have to figure out for myself how to set the parameters. Currently the Android app creates a header like the generator at http://www.teria.com/~koseki/tools/wssegen/ It's developed by someone else, and i just failed to duplicate that in PHP ;-)
Hi @timtailor,
thanks for your feedback!
Yep, you've both (@rollsappletree as well) got a heads up to write functional tests for your API-calls.
As you mentioned it will now be a case for you to figure out what http://www.teria.com/~koseki/tools/wssegen/ uses to generate the digest and set your WSSE authentication config accordingly. I actually thought it was sha1 with 1 iteration over there, but apparently you gave that a go already :-) If you find out, do let me know and I'll make a note as every now and then there's someone who uses that generator to give the bundle a spin :-)
I'll close this ticket now, but don't hesitate to add additional comments...
Have a great day! David
For anyone who is interested: If you developed a WSSE header accordlingly to http://www.teria.com/~koseki/tools/wssegen/ then you have to use SHA1, 1 iteration, base64 for the digest, but with the specialty that you have to use the password as it is in the database (encrypted, not plain text) and no salt (override getSalt to return empty string and empty salt in your test file.
Thanks @timtailor!
Hi, I want to create some tests for my REST Api, that i auth with wsse. But I dont know how to write the part about wsse.
Could anyone please write down an example of a test for a call that must be authenticated with wsse?
I know this is not really a issue, but i thought this is the best place where to ask.
Thanks in advance, C