forked from GNUsocial/gnu-social
Merge branch 'testing-sig' of gitorious.org:~brion/statusnet/brion-fixes into testing
This commit is contained in:
commit
4b29d0ebb6
@ -95,7 +95,11 @@ function _have_config()
|
||||
return StatusNet::haveConfig();
|
||||
}
|
||||
|
||||
function __autoload($cls)
|
||||
/**
|
||||
* Wrapper for class autoloaders.
|
||||
* This used to be the special function name __autoload(), but that causes bugs with PHPUnit 3.5+
|
||||
*/
|
||||
function autoload_sn($cls)
|
||||
{
|
||||
if (file_exists(INSTALLDIR.'/classes/' . $cls . '.php')) {
|
||||
require_once(INSTALLDIR.'/classes/' . $cls . '.php');
|
||||
@ -111,6 +115,8 @@ function __autoload($cls)
|
||||
}
|
||||
}
|
||||
|
||||
spl_autoload_register('autoload_sn');
|
||||
|
||||
// XXX: how many of these could be auto-loaded on use?
|
||||
// XXX: note that these files should not use config options
|
||||
// at compile time since DB config options are not yet loaded.
|
||||
|
@ -39,11 +39,41 @@ class Magicsig extends Memcached_DataObject
|
||||
|
||||
public $__table = 'magicsig';
|
||||
|
||||
/**
|
||||
* Key to user.id/profile.id for the local user whose key we're storing.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $user_id;
|
||||
|
||||
/**
|
||||
* Flattened string representation of the key pair; callers should
|
||||
* usually use $this->publicKey and $this->privateKey directly,
|
||||
* which hold live Crypt_RSA key objects.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $keypair;
|
||||
|
||||
/**
|
||||
* Crypto algorithm used for this key; currently only RSA-SHA256 is supported.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $alg;
|
||||
|
||||
/**
|
||||
* Public RSA key; gets serialized in/out via $this->keypair string.
|
||||
*
|
||||
* @var Crypt_RSA
|
||||
*/
|
||||
public $publicKey;
|
||||
|
||||
/**
|
||||
* PrivateRSA key; gets serialized in/out via $this->keypair string.
|
||||
*
|
||||
* @var Crypt_RSA
|
||||
*/
|
||||
public $privateKey;
|
||||
|
||||
public function __construct($alg = 'RSA-SHA256')
|
||||
@ -51,6 +81,13 @@ class Magicsig extends Memcached_DataObject
|
||||
$this->alg = $alg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a Magicsig object from the cache or database on a field match.
|
||||
*
|
||||
* @param string $k
|
||||
* @param mixed $v
|
||||
* @return Magicsig
|
||||
*/
|
||||
public /*static*/ function staticGet($k, $v=null)
|
||||
{
|
||||
$obj = parent::staticGet(__CLASS__, $k, $v);
|
||||
@ -103,6 +140,14 @@ class Magicsig extends Memcached_DataObject
|
||||
return array(false, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save this keypair into the database.
|
||||
*
|
||||
* Overloads default insert behavior to encode the live key objects
|
||||
* as a flat string for storage.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function insert()
|
||||
{
|
||||
$this->keypair = $this->toString();
|
||||
@ -110,6 +155,14 @@ class Magicsig extends Memcached_DataObject
|
||||
return parent::insert();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new keypair for a local user and store in the database.
|
||||
*
|
||||
* Warning: this can be very slow on systems without the GMP module.
|
||||
* Runtimes of 20-30 seconds are not unheard-of.
|
||||
*
|
||||
* @param int $user_id id of local user we're creating a key for
|
||||
*/
|
||||
public function generate($user_id)
|
||||
{
|
||||
$rsa = new Crypt_RSA();
|
||||
@ -128,6 +181,12 @@ class Magicsig extends Memcached_DataObject
|
||||
$this->insert();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the keypair or public key as a string.
|
||||
*
|
||||
* @param boolean $full_pair set to false to leave out the private key.
|
||||
* @return string
|
||||
*/
|
||||
public function toString($full_pair = true)
|
||||
{
|
||||
$mod = Magicsig::base64_url_encode($this->publicKey->modulus->toBytes());
|
||||
@ -140,6 +199,13 @@ class Magicsig extends Memcached_DataObject
|
||||
return 'RSA.' . $mod . '.' . $exp . $private_exp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a string representation of an RSA public key or keypair
|
||||
* as a Magicsig object which can be used to sign or verify.
|
||||
*
|
||||
* @param string $text
|
||||
* @return Magicsig
|
||||
*/
|
||||
public static function fromString($text)
|
||||
{
|
||||
$magic_sig = new Magicsig();
|
||||
@ -168,6 +234,14 @@ class Magicsig extends Memcached_DataObject
|
||||
return $magic_sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill out $this->privateKey or $this->publicKey with a Crypt_RSA object
|
||||
* representing the give key (as mod/exponent pair).
|
||||
*
|
||||
* @param string $mod base64-encoded
|
||||
* @param string $exp base64-encoded exponent
|
||||
* @param string $type one of 'public' or 'private'
|
||||
*/
|
||||
public function loadKey($mod, $exp, $type = 'public')
|
||||
{
|
||||
common_log(LOG_DEBUG, "Adding ".$type." key: (".$mod .', '. $exp .")");
|
||||
@ -186,11 +260,22 @@ class Magicsig extends Memcached_DataObject
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the crypto algorithm used for this key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->alg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of a hash function to use for signing with this key.
|
||||
*
|
||||
* @return string
|
||||
* @fixme is this used? doesn't seem to be called by name.
|
||||
*/
|
||||
public function getHash()
|
||||
{
|
||||
switch ($this->alg) {
|
||||
@ -200,24 +285,48 @@ class Magicsig extends Memcached_DataObject
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate base64-encoded signature for the given byte string
|
||||
* using our private key.
|
||||
*
|
||||
* @param string $bytes as raw byte string
|
||||
* @return string base64-encoded signature
|
||||
*/
|
||||
public function sign($bytes)
|
||||
{
|
||||
$sig = $this->privateKey->sign($bytes);
|
||||
return Magicsig::base64_url_encode($sig);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $signed_bytes as raw byte string
|
||||
* @param string $signature as base64
|
||||
* @return boolean
|
||||
*/
|
||||
public function verify($signed_bytes, $signature)
|
||||
{
|
||||
$signature = Magicsig::base64_url_decode($signature);
|
||||
return $this->publicKey->verify($signed_bytes, $signature);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* URL-encoding-friendly base64 variant encoding.
|
||||
*
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
public static function base64_url_encode($input)
|
||||
{
|
||||
return strtr(base64_encode($input), '+/', '-_');
|
||||
}
|
||||
|
||||
/**
|
||||
* URL-encoding-friendly base64 variant decoding.
|
||||
*
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
public static function base64_url_decode($input)
|
||||
{
|
||||
return base64_decode(strtr($input, '-_', '+/'));
|
||||
|
@ -331,6 +331,7 @@ class Ostatus_profile extends Memcached_DataObject
|
||||
* an acceptable response from the remote site.
|
||||
*
|
||||
* @param mixed $entry XML string, Notice, or Activity
|
||||
* @param Profile $actor
|
||||
* @return boolean success
|
||||
*/
|
||||
public function notifyActivity($entry, $actor)
|
||||
|
@ -80,21 +80,53 @@ class MagicEnvelope
|
||||
throw new Exception(_m('Unable to locate signer public key.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* The current MagicEnvelope spec as used in StatusNet 0.9.7 and later
|
||||
* includes both the original data and some signing metadata fields as
|
||||
* the input plaintext for the signature hash.
|
||||
*
|
||||
* @param array $env
|
||||
* @return string
|
||||
*/
|
||||
public function signingText($env) {
|
||||
return implode('.', array($env['data'], // this field is pre-base64'd
|
||||
Magicsig::base64_url_encode($env['data_type']),
|
||||
Magicsig::base64_url_encode($env['encoding']),
|
||||
Magicsig::base64_url_encode($env['alg'])));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <type> $text
|
||||
* @param <type> $mimetype
|
||||
* @param <type> $keypair
|
||||
* @return array: associative array of envelope properties
|
||||
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
|
||||
*/
|
||||
public function signMessage($text, $mimetype, $keypair)
|
||||
{
|
||||
$signature_alg = Magicsig::fromString($keypair);
|
||||
$armored_text = Magicsig::base64_url_encode($text);
|
||||
|
||||
return array(
|
||||
$env = array(
|
||||
'data' => $armored_text,
|
||||
'encoding' => MagicEnvelope::ENCODING,
|
||||
'data_type' => $mimetype,
|
||||
'sig' => $signature_alg->sign($armored_text),
|
||||
'sig' => '',
|
||||
'alg' => $signature_alg->getName()
|
||||
);
|
||||
|
||||
$env['sig'] = $signature_alg->sign($this->signingText($env));
|
||||
|
||||
return $env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an <me:env> XML representation of the envelope.
|
||||
*
|
||||
* @param array $env associative array with envelope data
|
||||
* @return string representation of XML document
|
||||
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
|
||||
*/
|
||||
public function toXML($env) {
|
||||
$xs = new XMLStringer();
|
||||
$xs->startXML();
|
||||
@ -110,6 +142,16 @@ class MagicEnvelope
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the contained XML payload, and insert a copy of the envelope
|
||||
* signature data as an <me:provenance> section.
|
||||
*
|
||||
* @param array $env associative array with envelope data
|
||||
* @return string representation of modified XML document
|
||||
*
|
||||
* @fixme in case of XML parsing errors, this will spew to the error log or output
|
||||
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
|
||||
*/
|
||||
public function unfold($env)
|
||||
{
|
||||
$dom = new DOMDocument();
|
||||
@ -136,6 +178,14 @@ class MagicEnvelope
|
||||
return $dom->saveXML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the author URI referenced in the given Atom entry.
|
||||
*
|
||||
* @param string $text string containing Atom entry XML
|
||||
* @return mixed URI string or false if XML parsing fails, or null if no author URI can be found
|
||||
*
|
||||
* @fixme XML parsing failures will spew to error logs/output
|
||||
*/
|
||||
public function getAuthor($text) {
|
||||
$doc = new DOMDocument();
|
||||
if (!$doc->loadXML($text)) {
|
||||
@ -153,11 +203,30 @@ class MagicEnvelope
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the author in the Atom entry fragment claims to match
|
||||
* the given identifier URI.
|
||||
*
|
||||
* @param string $text string containing Atom entry XML
|
||||
* @param string $signer_uri
|
||||
* @return boolean
|
||||
*/
|
||||
public function checkAuthor($text, $signer_uri)
|
||||
{
|
||||
return ($this->getAuthor($text) == $signer_uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to verify cryptographic signing for parsed envelope data.
|
||||
* Requires network access to retrieve public key referenced by the envelope signer.
|
||||
*
|
||||
* Details of failure conditions are dumped to output log and not exposed to caller.
|
||||
*
|
||||
* @param array $env array representation of magic envelope data, as returned from MagicEnvelope::parse()
|
||||
* @return boolean
|
||||
*
|
||||
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
|
||||
*/
|
||||
public function verify($env)
|
||||
{
|
||||
if ($env['alg'] != 'RSA-SHA256') {
|
||||
@ -187,15 +256,35 @@ class MagicEnvelope
|
||||
return false;
|
||||
}
|
||||
|
||||
return $verifier->verify($env['data'], $env['sig']);
|
||||
return $verifier->verify($this->signingText($env), $env['sig']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract envelope data from an XML document containing an <me:env> or <me:provenance> element.
|
||||
*
|
||||
* @param string XML source
|
||||
* @return mixed associative array of envelope data, or false on unrecognized input
|
||||
*
|
||||
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
|
||||
* @fixme will spew errors to logs or output in case of XML parse errors
|
||||
* @fixme may give fatal errors if some elements are missing or invalid XML
|
||||
* @fixme calling DOMDocument::loadXML statically triggers warnings in strict mode
|
||||
*/
|
||||
public function parse($text)
|
||||
{
|
||||
$dom = DOMDocument::loadXML($text);
|
||||
return $this->fromDom($dom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract envelope data from an XML document containing an <me:env> or <me:provenance> element.
|
||||
*
|
||||
* @param DOMDocument $dom
|
||||
* @return mixed associative array of envelope data, or false on unrecognized input
|
||||
*
|
||||
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
|
||||
* @fixme may give fatal errors if some elements are missing
|
||||
*/
|
||||
public function fromDom($dom)
|
||||
{
|
||||
$env_element = $dom->getElementsByTagNameNS(MagicEnvelope::NS, 'env')->item(0);
|
||||
@ -218,3 +307,24 @@ class MagicEnvelope
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of MagicEnvelope using the earlier signature form listed in the MagicEnvelope
|
||||
* spec in early 2010; this was used in StatusNet up through 0.9.6, so for backwards compatiblity
|
||||
* we still need to accept and sometimes send this format.
|
||||
*/
|
||||
class MagicEnvelopeCompat extends MagicEnvelope {
|
||||
|
||||
/**
|
||||
* StatusNet through 0.9.6 used an earlier version of the MagicEnvelope spec
|
||||
* which used only the input data, without the additional fields, as the plaintext
|
||||
* for signing.
|
||||
*
|
||||
* @param array $env
|
||||
* @return string
|
||||
*/
|
||||
public function signingText($env) {
|
||||
return $env['data'];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,10 +38,12 @@ class Salmon
|
||||
/**
|
||||
* Sign and post the given Atom entry as a Salmon message.
|
||||
*
|
||||
* @fixme pass through the actor for signing?
|
||||
* Side effects: may generate a keypair on-demand for the given user,
|
||||
* which can be very slow on some systems.
|
||||
*
|
||||
* @param string $endpoint_uri
|
||||
* @param string $xml
|
||||
* @param string $xml string representation of payload
|
||||
* @param Profile $actor local user profile whose keys to sign with
|
||||
* @return boolean success
|
||||
*/
|
||||
public function post($endpoint_uri, $xml, $actor)
|
||||
@ -50,34 +52,65 @@ class Salmon
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$xml = $this->createMagicEnv($xml, $actor);
|
||||
} catch (Exception $e) {
|
||||
common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
foreach ($this->formatClasses() as $class) {
|
||||
try {
|
||||
$envelope = $this->createMagicEnv($xml, $actor, $class);
|
||||
} catch (Exception $e) {
|
||||
common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
$headers = array('Content-Type: application/magic-envelope+xml');
|
||||
|
||||
try {
|
||||
$client = new HTTPClient();
|
||||
$client->setBody($envelope);
|
||||
$response = $client->post($endpoint_uri, $headers);
|
||||
} catch (HTTP_Request2_Exception $e) {
|
||||
common_log(LOG_ERR, "Salmon ($class) post to $endpoint_uri failed: " . $e->getMessage());
|
||||
continue;
|
||||
}
|
||||
if ($response->getStatus() != 200) {
|
||||
common_log(LOG_ERR, "Salmon ($class) at $endpoint_uri returned status " .
|
||||
$response->getStatus() . ': ' . $response->getBody());
|
||||
continue;
|
||||
}
|
||||
|
||||
$headers = array('Content-Type: application/magic-envelope+xml');
|
||||
|
||||
try {
|
||||
$client = new HTTPClient();
|
||||
$client->setBody($xml);
|
||||
$response = $client->post($endpoint_uri, $headers);
|
||||
} catch (HTTP_Request2_Exception $e) {
|
||||
common_log(LOG_ERR, "Salmon post to $endpoint_uri failed: " . $e->getMessage());
|
||||
return false;
|
||||
// Success!
|
||||
return true;
|
||||
}
|
||||
if ($response->getStatus() != 200) {
|
||||
common_log(LOG_ERR, "Salmon at $endpoint_uri returned status " .
|
||||
$response->getStatus() . ': ' . $response->getBody());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function createMagicEnv($text, $actor)
|
||||
/**
|
||||
* List the magic envelope signature class variants in the order we try them.
|
||||
* Multiples are needed for backwards-compat with StatusNet prior to 0.9.7,
|
||||
* which used a draft version of the magic envelope spec.
|
||||
*/
|
||||
protected function formatClasses() {
|
||||
return array('MagicEnvelope', 'MagicEnvelopeCompat');
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the given string as a signed MagicEnvelope XML document,
|
||||
* using the keypair for the given local user profile.
|
||||
*
|
||||
* Side effects: will create and store a keypair on-demand if one
|
||||
* hasn't already been generated for this user. This can be very slow
|
||||
* on some systems.
|
||||
*
|
||||
* @param string $text XML fragment to sign, assumed to be Atom
|
||||
* @param Profile $actor Profile of a local user to use as signer
|
||||
* @param string $class to override the magic envelope signature version, pass a MagicEnvelope subclass here
|
||||
*
|
||||
* @return string XML string representation of magic envelope
|
||||
*
|
||||
* @throws Exception on bad profile input or key generation problems
|
||||
* @fixme if signing fails, this seems to return the original text without warning. Is there a reason for this?
|
||||
*/
|
||||
public function createMagicEnv($text, $actor, $class='MagicEnvelope')
|
||||
{
|
||||
$magic_env = new MagicEnvelope();
|
||||
$magic_env = new $class();
|
||||
|
||||
$user = User::staticGet('id', $actor->id);
|
||||
if ($user->id) {
|
||||
@ -101,12 +134,32 @@ class Salmon
|
||||
return $magic_env->toXML($env);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given magic envelope is well-formed and correctly signed.
|
||||
* Needs to have network access to fetch public keys over the web.
|
||||
* Both current and back-compat signature formats will be checked.
|
||||
*
|
||||
* Side effects: exceptions and caching updates may occur during network
|
||||
* fetches.
|
||||
*
|
||||
* @param string $text XML fragment of magic envelope
|
||||
* @return boolean
|
||||
*
|
||||
* @throws Exception on bad profile input or key generation problems
|
||||
* @fixme could hit fatal errors or spew output on invalid XML
|
||||
*/
|
||||
public function verifyMagicEnv($text)
|
||||
{
|
||||
$magic_env = new MagicEnvelope();
|
||||
foreach ($this->formatClasses() as $class) {
|
||||
$magic_env = new $class();
|
||||
|
||||
$env = $magic_env->parse($text);
|
||||
$env = $magic_env->parse($text);
|
||||
|
||||
return $magic_env->verify($env);
|
||||
if ($magic_env->verify($env)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
60
plugins/OStatus/tests/MagicEnvelopeTest.php
Normal file
60
plugins/OStatus/tests/MagicEnvelopeTest.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
|
||||
print "This script must be run from the command line\n";
|
||||
exit();
|
||||
}
|
||||
|
||||
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
|
||||
define('STATUSNET', true);
|
||||
|
||||
require_once INSTALLDIR . '/lib/common.php';
|
||||
|
||||
class MagicEnvelopeTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Test that MagicEnvelope builds the correct plaintext for signing.
|
||||
* @dataProvider provider
|
||||
*/
|
||||
public function testSignatureText($env, $expected)
|
||||
{
|
||||
$magic = new MagicEnvelope;
|
||||
$text = $magic->signingText($env);
|
||||
|
||||
$this->assertEquals($expected, $text, "'$text' should be '$expected'");
|
||||
}
|
||||
|
||||
static public function provider()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
// Sample case given in spec:
|
||||
// http://salmon-protocol.googlecode.com/svn/trunk/draft-panzer-magicsig-00.html#signing
|
||||
array(
|
||||
'data' => 'Tm90IHJlYWxseSBBdG9t',
|
||||
'data_type' => 'application/atom+xml',
|
||||
'encoding' => 'base64url',
|
||||
'alg' => 'RSA-SHA256'
|
||||
),
|
||||
'Tm90IHJlYWxseSBBdG9t.YXBwbGljYXRpb24vYXRvbSt4bWw=.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that MagicEnvelope builds the correct plaintext for signing.
|
||||
* @dataProvider provider
|
||||
*/
|
||||
public function testSignatureTextCompat($env, $expected)
|
||||
{
|
||||
// Our old code didn't add the extra fields, just used the armored text.
|
||||
$alt = $env['data'];
|
||||
|
||||
$magic = new MagicEnvelopeCompat;
|
||||
$text = $magic->signingText($env);
|
||||
|
||||
$this->assertEquals($alt, $text, "'$text' should be '$alt'");
|
||||
}
|
||||
|
||||
}
|
92
plugins/OStatus/tests/slap.php
Normal file
92
plugins/OStatus/tests/slap.php
Normal file
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
* StatusNet - a distributed open-source microblogging tool
|
||||
* Copyright (C) 2010, StatusNet, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
|
||||
|
||||
$longoptions = array('verify', 'slap=', 'notice=');
|
||||
|
||||
$helptext = <<<END_OF_HELP
|
||||
slap.php [options]
|
||||
|
||||
Test generation and sending of magic envelopes for Salmon slaps.
|
||||
|
||||
--notice=N generate entry for this notice number
|
||||
--verify send signed magic envelope to Tuomas Koski's test service
|
||||
--slap=<url> send signed Salmon slap to the destination endpoint
|
||||
|
||||
|
||||
END_OF_HELP;
|
||||
|
||||
require_once INSTALLDIR.'/scripts/commandline.inc';
|
||||
|
||||
if (!have_option('--notice')) {
|
||||
print "$helptext";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$notice_id = get_option_value('--notice');
|
||||
|
||||
$notice = Notice::staticGet('id', $notice_id);
|
||||
$profile = $notice->getProfile();
|
||||
$entry = $notice->asAtomEntry(true);
|
||||
|
||||
echo "== Original entry ==\n\n";
|
||||
print $entry;
|
||||
print "\n\n";
|
||||
|
||||
$salmon = new Salmon();
|
||||
$envelope = $salmon->createMagicEnv($entry, $profile);
|
||||
|
||||
echo "== Signed envelope ==\n\n";
|
||||
print $envelope;
|
||||
print "\n\n";
|
||||
|
||||
echo "== Testing local verification ==\n\n";
|
||||
$ok = $salmon->verifyMagicEnv($envelope);
|
||||
if ($ok) {
|
||||
print "OK\n\n";
|
||||
} else {
|
||||
print "FAIL\n\n";
|
||||
}
|
||||
|
||||
if (have_option('--verify')) {
|
||||
$url = 'http://www.madebymonsieur.com/ostatus_discovery/magic_env/validate/';
|
||||
echo "== Testing remote verification ==\n\n";
|
||||
print "Sending for verification to $url ...\n";
|
||||
|
||||
$client = new HTTPClient();
|
||||
$response = $client->post($url, array(), array('magic_env' => $envelope));
|
||||
|
||||
print $response->getStatus() . "\n\n";
|
||||
print $response->getBody() . "\n\n";
|
||||
}
|
||||
|
||||
if (have_option('--slap')) {
|
||||
$url = get_option_value('--slap');
|
||||
echo "== Remote salmon slap ==\n\n";
|
||||
print "Sending signed Salmon slap to $url ...\n";
|
||||
|
||||
$ok = $salmon->post($url, $entry, $profile);
|
||||
if ($ok) {
|
||||
print "OK\n\n";
|
||||
} else {
|
||||
print "FAIL\n\n";
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user