Commit 26394bec authored by gdd's avatar gdd

Merge remote-tracking branch 'core/8.x' into 8.x

parents 7795cfa3 1b1464ce
......@@ -73,7 +73,7 @@
'\x{A836}-\x{A839}\x{A874}-\x{A877}\x{A8CE}-\x{A8CF}\x{A8F8}-\x{A8FA}' .
'\x{A92E}-\x{A92F}\x{A95F}\x{A9C1}-\x{A9CD}\x{A9DE}-\x{A9DF}' .
'\x{AA5C}-\x{AA5F}\x{AA77}-\x{AA79}\x{AADE}-\x{AADF}\x{ABEB}' .
'\x{D800}-\x{F8FF}\x{FB29}\x{FD3E}-\x{FD3F}\x{FDFC}-\x{FDFD}' .
'\x{E000}-\x{F8FF}\x{FB29}\x{FD3E}-\x{FD3F}\x{FDFC}-\x{FDFD}' .
'\x{FE10}-\x{FE19}\x{FE30}-\x{FE6B}\x{FEFF}-\x{FF0F}\x{FF1A}-\x{FF20}' .
'\x{FF3B}-\x{FF40}\x{FF5B}-\x{FF65}\x{FFE0}-\x{FFFD}');
......
......@@ -37,7 +37,7 @@ function aggregator_view() {
($feed->checked && $feed->refresh ? t('%time left', array('%time' => format_interval($feed->checked + $feed->refresh - REQUEST_TIME))) : t('never')),
l(t('edit'), "admin/config/services/aggregator/edit/feed/$feed->fid"),
l(t('remove items'), "admin/config/services/aggregator/remove/$feed->fid"),
l(t('update items'), "admin/config/services/aggregator/update/$feed->fid"),
l(t('update items'), "admin/config/services/aggregator/update/$feed->fid", array('query' => array('token' => drupal_get_token("aggregator/update/$feed->fid")))),
);
}
$output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No feeds available. <a href="@link">Add feed</a>.', array('@link' => url('admin/config/services/aggregator/add/feed')))));
......@@ -84,7 +84,7 @@ function aggregator_form_feed($form, &$form_state, stdClass $feed = NULL) {
$form['url'] = array('#type' => 'textfield',
'#title' => t('URL'),
'#default_value' => isset($feed->url) ? $feed->url : '',
'#maxlength' => 255,
'#maxlength' => NULL,
'#description' => t('The fully-qualified URL of the feed.'),
'#required' => TRUE,
);
......@@ -421,6 +421,9 @@ function _aggregator_parse_opml($opml) {
* @see aggregator_menu()
*/
function aggregator_admin_refresh_feed($feed) {
if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'aggregator/update/' . $feed->fid)) {
return MENU_ACCESS_DENIED;
}
aggregator_refresh($feed);
drupal_goto('admin/config/services/aggregator');
}
......
......@@ -130,10 +130,8 @@ function aggregator_schema() {
'description' => 'Title of the feed.',
),
'url' => array(
'type' => 'varchar',
'length' => 255,
'type' => 'text',
'not null' => TRUE,
'default' => '',
'description' => 'URL to the feed.',
),
'refresh' => array(
......@@ -155,10 +153,8 @@ function aggregator_schema() {
'description' => 'Time when this feed was queued for refresh, 0 if not queued.',
),
'link' => array(
'type' => 'varchar',
'length' => 255,
'type' => 'text',
'not null' => TRUE,
'default' => '',
'description' => 'The parent website of the feed; comes from the <link> element in the feed.',
),
'description' => array(
......@@ -202,13 +198,13 @@ function aggregator_schema() {
)
),
'primary key' => array('fid'),
'unique keys' => array(
'url' => array('url'),
'title' => array('title'),
),
'indexes' => array(
'url' => array(array('url', 255)),
'queued' => array('queued'),
),
'unique keys' => array(
'title' => array('title'),
),
);
$schema['aggregator_item'] = array(
......@@ -233,10 +229,8 @@ function aggregator_schema() {
'description' => 'Title of the feed item.',
),
'link' => array(
'type' => 'varchar',
'length' => 255,
'type' => 'text',
'not null' => TRUE,
'default' => '',
'description' => 'Link to the feed item.',
),
'author' => array(
......@@ -258,9 +252,8 @@ function aggregator_schema() {
'description' => 'Posted date of the feed item, as a Unix timestamp.',
),
'guid' => array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'type' => 'text',
'not null' => TRUE,
'description' => 'Unique identifier for the feed item.',
)
),
......
......@@ -537,6 +537,7 @@ function aggregator_save_feed($edit) {
'url' => $edit['url'],
'refresh' => $edit['refresh'],
'block' => $edit['block'],
'link' => '',
'description' => '',
'image' => '',
))
......@@ -573,15 +574,13 @@ function aggregator_remove($feed) {
// Call hook_aggregator_remove() on all modules.
module_invoke_all('aggregator_remove', $feed);
// Reset feed.
db_merge('aggregator_feed')
->key(array('fid' => $feed->fid))
db_update('aggregator_feed')
->condition('fid', $feed->fid)
->fields(array(
'checked' => 0,
'hash' => '',
'etag' => '',
'modified' => 0,
'description' => $feed->description,
'image' => $feed->image,
))
->execute();
}
......
......@@ -92,8 +92,13 @@ class AggregatorTestCase extends DrupalWebTestCase {
$this->drupalGet($feed->url);
$this->assertResponse(200, t('!url is reachable.', array('!url' => $feed->url)));
// Refresh the feed (simulated link click).
// Attempt to access the update link directly without an access token.
$this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid);
$this->assertResponse(403);
// Refresh the feed (simulated link click).
$this->drupalGet('admin/config/services/aggregator');
$this->clickLink('update items');
// Ensure we have the right number of items.
$result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid));
......@@ -349,6 +354,35 @@ class AddFeedTestCase extends AggregatorTestCase {
// Delete feed.
$this->deleteFeed($feed);
}
/**
* Tests feeds with very long URLs.
*/
function testAddLongFeed() {
// Create a feed with a URL of > 255 characters.
$long_url = "https://www.google.com/search?ix=heb&sourceid=chrome&ie=UTF-8&q=angie+byron#sclient=psy-ab&hl=en&safe=off&source=hp&q=angie+byron&pbx=1&oq=angie+byron&aq=f&aqi=&aql=&gs_sm=3&gs_upl=0l0l0l10534l0l0l0l0l0l0l0l0ll0l0&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=a70b6b1f0abe28d8&biw=1629&bih=889&ix=heb";
$feed = $this->createFeed($long_url);
// Create a second feed of > 255 characters, where the only difference is
// after the 255th character.
$long_url_2 = "https://www.google.com/search?ix=heb&sourceid=chrome&ie=UTF-8&q=angie+byron#sclient=psy-ab&hl=en&safe=off&source=hp&q=angie+byron&pbx=1&oq=angie+byron&aq=f&aqi=&aql=&gs_sm=3&gs_upl=0l0l0l10534l0l0l0l0l0l0l0l0ll0l0&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=a70b6b1f0abe28d8&biw=1629&bih=889";
$feed_2 = $this->createFeed($long_url_2);
// Check feed data.
$this->assertTrue($this->uniqueFeed($feed->title, $feed->url), 'The first long URL feed is unique.');
$this->assertTrue($this->uniqueFeed($feed_2->title, $feed_2->url), 'The second long URL feed is unique.');
// Check feed source.
$this->drupalGet('aggregator/sources/' . $feed->fid);
$this->assertResponse(200, 'Long URL feed source exists.');
$this->assertText($feed->title, 'Page title');
$this->drupalGet('aggregator/sources/' . $feed->fid . '/categorize');
$this->assertResponse(200, 'Long URL feed categorization page exists.');
// Delete feeds.
$this->deleteFeed($feed);
$this->deleteFeed($feed_2);
}
}
class CategorizeFeedTestCase extends AggregatorTestCase {
......@@ -502,8 +536,8 @@ class UpdateFeedItemTestCase extends AggregatorTestCase {
$this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])), t('The feed !name has been added.', array('!name' => $edit['title'])));
$feed = db_query("SELECT * FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url']))->fetchObject();
$this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid);
aggregator_refresh($feed);
$before = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField();
// Sleep for 3 second.
......@@ -517,10 +551,9 @@ class UpdateFeedItemTestCase extends AggregatorTestCase {
'modified' => 0,
))
->execute();
$this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid);
aggregator_refresh($feed);
$after = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField();
$this->assertTrue($before === $after, t('Publish timestamp of feed item was not updated (!before === !after)', array('!before' => $before, '!after' => $after)));
}
}
......
......@@ -18,8 +18,8 @@
* @ingroup themeable
*/
?>
<div id="node-<?php print $node->nid; ?>" class="section-<?php print $depth; ?>">
<article id="node-<?php print $node->nid; ?>" class="section-<?php print $depth; ?>">
<h1 class="book-heading"><?php print $title; ?></h1>
<?php print $content; ?>
<?php print $children; ?>
</div>
</article>
......@@ -1128,7 +1128,7 @@ function filter_dom_serialize_escape_cdata_element($dom_document, $dom_element,
* @ingroup themeable
*/
function theme_filter_tips_more_info() {
return '<p>' . l(t('More information about text formats'), 'filter/tips') . '</p>';
return '<p>' . l(t('More information about text formats'), 'filter/tips', array('attributes' => array('target' => '_blank'))) . '</p>';
}
/**
......
......@@ -264,13 +264,25 @@ function locale_update_8001() {
// storing data for blocks will need to update for themselves.
$block_tables = array('block', 'block_node_type', 'block_role');
foreach ($block_tables as $table) {
db_update($table)
->fields(array(
'delta' => 'language_interface',
))
->condition('delta', 'language')
->condition('module', 'locale')
->execute();
// Perform the update only if the language switcher block data is available.
$block_data = db_query_range('SELECT 1 FROM {' . $table . '} WHERE delta = :delta AND module = :module', 0, 1, array(':delta' => 'language', ':module' => 'locale'))
->fetchField();
if ($block_data) {
// If block information is rebuilt before performing the update, we might
// already have data for the new delta. In this case we need to remove it
// to avoid integrity constraint violation errors.
db_delete($table)
->condition('delta', 'language_interface')
->condition('module', 'locale')
->execute();
db_update($table)
->fields(array(
'delta' => 'language_interface',
))
->condition('delta', 'language')
->condition('module', 'locale')
->execute();
}
}
}
......
......@@ -610,18 +610,31 @@ function _openid_get_params($str) {
* @param $fallback_prefix
* An optional prefix that will be used in case no prefix is found for the
* target extension namespace.
* @param $only_signed
* Return only keys that are included in the message signature in openid.sig.
* Unsigned fields may have been modified or added by other parties than the
* OpenID Provider.
*
* @return
* An associative array containing all the parameters in the response message
* that belong to the extension. The keys are stripped from their namespace
* prefix.
*
* @see http://openid.net/specs/openid-authentication-2_0.html#extensions
*/
function openid_extract_namespace($response, $extension_namespace, $fallback_prefix = NULL) {
function openid_extract_namespace($response, $extension_namespace, $fallback_prefix = NULL, $only_signed = FALSE) {
$signed_keys = explode(',', $response['openid.signed']);
// Find the namespace prefix.
$prefix = $fallback_prefix;
foreach ($response as $key => $value) {
if ($value == $extension_namespace && preg_match('/^openid\.ns\.([^.]+)$/', $key, $matches)) {
$prefix = $matches[1];
if ($only_signed && !in_array('ns.' . $matches[1], $signed_keys)) {
// The namespace was defined but was not signed as required. In this
// case we do not fall back to $fallback_prefix.
$prefix = NULL;
}
break;
}
}
......@@ -634,7 +647,9 @@ function openid_extract_namespace($response, $extension_namespace, $fallback_pre
foreach ($response as $key => $value) {
if (preg_match('/^openid\.' . $prefix . '\.(.+)$/', $key, $matches)) {
$local_key = $matches[1];
$output[$local_key] = $value;
if (!$only_signed || in_array($prefix . '.' . $local_key, $signed_keys)) {
$output[$local_key] = $value;
}
}
}
......
......@@ -185,10 +185,15 @@ function openid_form_user_register_form_alter(&$form, &$form_state) {
$response = $_SESSION['openid']['response'];
// Extract Simple Registration keys from the response.
$sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg');
// Extract Attribute Exchanges keys from the response.
$ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax');
// Extract Simple Registration keys from the response. We only include
// signed keys as required by OpenID Simple Registration Extension 1.0,
// section 4.
$sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE);
// Extract Attribute Exchanges keys from the response. We only include
// signed keys. This is not required by the specification, but it is
// recommended by Google, see
// http://googlecode.blogspot.com/2011/05/security-advisory-to-websites-using.html
$ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax', TRUE);
if (!empty($sreg_values['nickname'])) {
// Use the nickname returned by Simple Registration if available.
......
......@@ -364,17 +364,49 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase {
// Use a User-supplied Identity that is the URL of an XRDS document.
$identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
// Do not sign all mandatory fields (e.g. assoc_handle).
// Respond with an invalid signature.
variable_set('openid_test_response', array('openid.sig' => 'this-is-an-invalid-signature'));
$this->submitLoginForm($identity);
$this->assertRaw('OpenID login failed.');
// Do not sign the mandatory field openid.assoc_handle.
variable_set('openid_test_response', array('openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce'));
$this->submitLoginForm($identity);
$this->assertRaw('OpenID login failed.');
// Sign all mandatory fields and some custom fields.
variable_set('openid_test_response', array('openid.foo' => 'bar', 'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,foo'));
// Sign all mandatory fields and a custom field.
$keys_to_sign = array('op_endpoint', 'claimed_id', 'identity', 'return_to', 'response_nonce', 'assoc_handle', 'foo');
$association = new stdClass();
$association->mac_key = variable_get('mac_key');
$response = array(
'openid.op_endpoint' => url('openid-test/endpoint', array('absolute' => TRUE)),
'openid.claimed_id' => $identity,
'openid.identity' => $identity,
'openid.return_to' => url('openid/authenticate', array('absolute' => TRUE)),
'openid.response_nonce' => _openid_nonce(),
'openid.assoc_handle' => 'openid-test',
'openid.foo' => 123,
'openid.signed' => implode(',', $keys_to_sign),
);
$response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign);
variable_set('openid_test_response', $response);
$this->submitLoginForm($identity);
$this->assertNoRaw('OpenID login failed.');
}
$this->assertFieldByName('name', '', t('No username was supplied by provider.'));
$this->assertFieldByName('mail', '', t('No e-mail address was supplied by provider.'));
// Check that unsigned SREG fields are ignored.
$response = array(
'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,sreg.nickname',
'openid.sreg.nickname' => 'john',
'openid.sreg.email' => 'john@example.com',
);
variable_set('openid_test_response', $response);
$this->submitLoginForm($identity);
$this->assertNoRaw('OpenID login failed.');
$this->assertFieldByName('name', 'john', t('Username was supplied by provider.'));
$this->assertFieldByName('mail', '', t('E-mail address supplied by provider was ignored.'));
}
}
/**
......@@ -707,4 +739,41 @@ class OpenIDUnitTest extends DrupalWebTestCase {
$this->assertEqual(openid_normalize('http://example.com/path#fragment'), 'http://example.com/path', t('openid_normalize() correctly normalized a URL with a fragment.'));
}
/**
* Test openid_extract_namespace().
*/
function testOpenidExtractNamespace() {
$response = array(
'openid.sreg.nickname' => 'john',
'openid.ns.ext1' => OPENID_NS_SREG,
'openid.ext1.nickname' => 'george',
'openid.ext1.email' => 'george@example.com',
'openid.ns.ext2' => 'http://example.com/ns/ext2',
'openid.ext2.foo' => '123',
'openid.ext2.bar' => '456',
'openid.signed' => 'sreg.nickname,ns.ext1,ext1.email,ext2.foo',
);
$values = openid_extract_namespace($response, 'http://example.com/ns/dummy', NULL, FALSE);
$this->assertEqual($values, array(), t('Nothing found for unused namespace.'));
$values = openid_extract_namespace($response, 'http://example.com/ns/dummy', 'sreg', FALSE);
$this->assertEqual($values, array('nickname' => 'john'), t('Value found for fallback prefix.'));
$values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', FALSE);
$this->assertEqual($values, array('nickname' => 'george', 'email' => 'george@example.com'), t('Namespace takes precedence over fallback prefix.'));
// ext1.email is signed, but ext1.nickname is not.
$values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE);
$this->assertEqual($values, array('email' => 'george@example.com'), t('Unsigned namespaced fields ignored.'));
$values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', FALSE);
$this->assertEqual($values, array('foo' => '123', 'bar' => '456'), t('Unsigned fields found.'));
// ext2.foo and ext2.bar are ignored, because ns.ext2 is not signed. The
// fallback prefix is not used, because the namespace is specified.
$values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', TRUE);
$this->assertEqual($values, array(), t('Unsigned fields ignored.'));
}
}
......@@ -325,9 +325,7 @@ function _openid_test_endpoint_authenticate() {
// Generate unique identifier for this authentication.
$nonce = _openid_nonce();
// Generate response containing the user's identity. The openid.sreg.xxx
// entries contain profile data stored by the OpenID Provider (see OpenID
// Simple Registration Extension 1.0).
// Generate response containing the user's identity.
$response = variable_get('openid_test_response', array()) + array(
'openid.ns' => OPENID_NS_2_0,
'openid.mode' => 'id_res',
......@@ -337,14 +335,27 @@ function _openid_test_endpoint_authenticate() {
'openid.return_to' => $_REQUEST['openid_return_to'],
'openid.response_nonce' => $nonce,
'openid.assoc_handle' => 'openid-test',
'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle',
);
if (isset($response['openid.signed'])) {
$keys_to_sign = explode(',', $response['openid.signed']);
}
else {
// Unless openid.signed is explicitly defined, all keys are signed.
$keys_to_sign = array();
foreach ($response as $key => $value) {
// Strip off the "openid." prefix.
$keys_to_sign[] = substr($key, 7);
}
$response['openid.signed'] = implode(',', $keys_to_sign);
}
// Sign the message using the MAC key that was exchanged during association.
$association = new stdClass();
$association->mac_key = variable_get('mac_key');
$keys_to_sign = explode(',', $response['openid.signed']);
$response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign);
if (!isset($response['openid.sig'])) {
$response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign);
}
// Put the signed message into the query string of a URL supplied by the
// Relying Party, and redirect the user.
......
......@@ -33,8 +33,12 @@
*/
.draggable a.tabledrag-handle {
float: right;
margin: -0.4em -0.5em -0.4em 0;
padding: 0.42em 0.5em 0.42em 1.5em;
margin-right: -1em;
margin-left: 0;
}
a.tabledrag-handle .handle {
margin: -0.4em 0.5em;
padding: 0.42em 0.5em;
}
div.indentation {
float: right;
......
......@@ -93,21 +93,22 @@ body.drag {
cursor: move;
float: left; /* LTR */
height: 1.7em;
margin: -0.4em 0 -0.4em -0.5em; /* LTR */
padding: 0.42em 1.5em 0.42em 0.5em; /* LTR */
margin-left: -1em; /* LTR */
overflow: hidden;
text-decoration: none;
}
a.tabledrag-handle:hover {
text-decoration: none;
}
a.tabledrag-handle .handle {
background: url(../../misc/draggable.png) no-repeat 0 0;
background: url(../../misc/draggable.png) no-repeat 6px 9px;
height: 13px;
margin-top: 4px;
margin: -0.4em 0.5em; /* LTR */
padding: 0.42em 0.5em; /* LTR */
width: 13px;
}
a.tabledrag-handle-hover .handle {
background-position: 0 -20px;
background-position: 6px -11px;
}
div.indentation {
float: left; /* LTR */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment