r49209 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r49208‎ | r49209 | r49210 >
Date:13:56, 5 April 2009
Author:tstarling
Status:deferred
Tags:
Comment:
* Did the session transfer (guest login) system.

This is the last module needed to make the license transition vote work, so we're basically feature-complete at this point. There's still a little more work to do in testing/debugging, i18n cleanup and documentation, before this project can be shelved.

* Renamed User to Voter
* Renamed "authority" to "url"
* Added list-based authentication.
Modified paths:
  • /trunk/extensions/SecurePoll/SecurePoll.i18n.php (modified) (history)
  • /trunk/extensions/SecurePoll/SecurePoll.php (modified) (history)
  • /trunk/extensions/SecurePoll/SecurePoll.sql (modified) (history)
  • /trunk/extensions/SecurePoll/SecurePoll_body.php (modified) (history)
  • /trunk/extensions/SecurePoll/includes/Auth.php (modified) (history)
  • /trunk/extensions/SecurePoll/includes/Ballot.php (modified) (history)
  • /trunk/extensions/SecurePoll/includes/DetailsPage.php (modified) (history)
  • /trunk/extensions/SecurePoll/includes/Election.php (modified) (history)
  • /trunk/extensions/SecurePoll/includes/EntryPage.php (modified) (history)
  • /trunk/extensions/SecurePoll/includes/LoginPage.php (added) (history)
  • /trunk/extensions/SecurePoll/includes/Page.php (modified) (history)
  • /trunk/extensions/SecurePoll/includes/TranslatePage.php (modified) (history)
  • /trunk/extensions/SecurePoll/includes/User.php (deleted) (history)
  • /trunk/extensions/SecurePoll/includes/VotePage.php (modified) (history)
  • /trunk/extensions/SecurePoll/includes/Voter.php (added) (history)

Diff [purge]

Index: trunk/extensions/SecurePoll/SecurePoll_body.php
@@ -9,6 +9,7 @@
1010 'dump' => 'SecurePoll_DumpPage',
1111 'entry' => 'SecurePoll_EntryPage',
1212 'list' => 'SecurePoll_ListPage',
 13+ 'login' => 'SecurePoll_LoginPage',
1314 'translate' => 'SecurePoll_TranslatePage',
1415 'vote' => 'SecurePoll_VotePage',
1516 );
@@ -75,9 +76,9 @@
7677 }
7778
7879 function getEditToken() {
79 - if ( !isset( $_SESSION['bvToken'] ) ) {
80 - $_SESSION['bvToken'] = sha1( mt_rand() . mt_rand() . mt_rand() );
 80+ if ( !isset( $_SESSION['spToken'] ) ) {
 81+ $_SESSION['spToken'] = sha1( mt_rand() . mt_rand() . mt_rand() );
8182 }
82 - return $_SESSION['bvToken'];
 83+ return $_SESSION['spToken'];
8384 }
8485 }
Index: trunk/extensions/SecurePoll/SecurePoll.i18n.php
@@ -20,7 +20,6 @@
2121 # Vote (most important to translate)
2222 'securepoll-too-few-params' => 'Not enough subpage parameters (invalid link).',
2323 'securepoll-invalid-election' => '"$1" is not a valid election ID.',
24 - 'securepoll-not-authorised' => 'You are not authorised to vote in this election.',
2524 'securepoll-welcome' => '<strong>Welcome $1!</strong>',
2625 'securepoll-not-started' => 'This election has not yet started.
2726 It is scheduled to start on $2 at $3.',
@@ -55,7 +54,21 @@
5655 'securepoll-gpg-parse-error' => 'Error interpreting GPG output.',
5756 'securepoll-no-decryption-key' => 'No decryption key is configured.
5857 Cannot decrypt.',
 58+ 'securepoll-jump' => 'Go to the voting server',
 59+
 60+ # Authorisation related
 61+ 'securepoll-remote-auth-error' => 'Error fetching your account information from the server.',
 62+ 'securepoll-remote-parse-error' => 'Error interpreting the authorisation response from the server.',
5963
 64+ 'securepoll-api-invalid-params' => 'Invalid parameters.',
 65+ 'securepoll-api-no-user' => 'No user was found with the given ID.',
 66+ 'securepoll-api-token-mismatch' => 'Security token mismatch, cannot log in.',
 67+ 'securepoll-not-logged-in' => 'You must log in to vote in this election',
 68+ 'securepoll-too-few-edits' => 'Sorry, you cannot vote. You need to have made at least $1 edits to vote in this election, you have made $2.',
 69+ 'securepoll-blocked' => 'Sorry, you cannot vote in this election if you are currently blocked from editing.',
 70+ 'securepoll-not-in-group' => 'Only members of the $1 group can vote in this election.',
 71+ 'securepoll-not-in-list' => 'Sorry, you are not in the predetermined list of users authorised to vote in this election.',
 72+
6073 # List page
6174 # Mostly for admins
6275 'securepoll-list-title' => 'List votes: $1',
@@ -81,8 +94,8 @@
8295 'securepoll-details-title' => 'Vote details: #$1',
8396 'securepoll-invalid-vote' => '"$1" is not a valid vote ID',
8497 'securepoll-header-id' => 'ID',
85 - 'securepoll-header-user-type' => 'User type',
86 - 'securepoll-header-authority' => 'URL',
 98+ 'securepoll-header-user-type' => 'Voter type',
 99+ 'securepoll-header-url' => 'URL',
87100 'securepoll-voter-properties' => 'Voter properties',
88101 'securepoll-strike-log' => 'Strike log',
89102 'securepoll-header-action' => 'Action',
@@ -103,6 +116,15 @@
104117 'securepoll-submit-translate' => 'Update',
105118 'securepoll-language-label' => 'Select language: ',
106119 'securepoll-submit-select-lang' => 'Translate',
 120+
 121+ # Entry page
 122+ 'securepoll-header-title' => 'Name',
 123+ 'securepoll-header-start-date' => 'Start date',
 124+ 'securepoll-header-end-date' => 'End date',
 125+ 'securepoll-subpage-vote' => 'Vote',
 126+ 'securepoll-subpage-translate' => 'Translate',
 127+ 'securepoll-subpage-list' => 'List',
 128+ 'securepoll-subpage-dump' => 'Dump',
107129 );
108130
109131 /** Message documentation (Message documentation)
Index: trunk/extensions/SecurePoll/SecurePoll.php
@@ -73,11 +73,12 @@
7474 'SecurePoll_Entity' => "$dir/includes/Entity.php",
7575 'SecurePoll_EntryPage' => "$dir/includes/EntryPage.php",
7676 'SecurePoll_ListPage' => "$dir/includes/ListPage.php",
 77+ 'SecurePoll_LoginPage' => "$dir/includes/LoginPage.php",
7778 'SecurePoll_Option' => "$dir/includes/Option.php",
7879 'SecurePoll_Page' => "$dir/includes/Page.php",
7980 'SecurePoll_Question' => "$dir/includes/Question.php",
8081 'SecurePoll_TranslatePage' => "$dir/includes/TranslatePage.php",
81 - 'SecurePoll_User' => "$dir/includes/User.php",
 82+ 'SecurePoll_Voter' => "$dir/includes/Voter.php",
8283 'SecurePoll_VotePage' => "$dir/includes/VotePage.php",
8384 );
8485
Index: trunk/extensions/SecurePoll/includes/User.php
@@ -1,86 +0,0 @@
2 -<?php
3 -
4 -class SecurePoll_User {
5 - var $id, $name, $domain, $wiki, $type, $authority;
6 - var $properties = array();
7 -
8 - static $paramNames = array( 'id', 'name', 'domain', 'wiki', 'type', 'authority', 'properties' );
9 -
10 - function __construct( $params ) {
11 - foreach ( self::$paramNames as $name ) {
12 - if ( isset( $params[$name] ) ) {
13 - $this->$name = $params[$name];
14 - }
15 - }
16 - }
17 -
18 - static function newFromId( $id ) {
19 - $db = wfGetDB( DB_MASTER );
20 - $row = $db->selectRow( 'securepoll_voters', '*', array( 'voter_id' => $id ), __METHOD__ );
21 - if ( !$row ) {
22 - return false;
23 - }
24 - return self::newFromRow( $row );
25 - }
26 -
27 - static function newFromRow( $row ) {
28 - return new self( array(
29 - 'id' => $row->voter_id,
30 - 'name' => $row->voter_name,
31 - 'domain' => $row->voter_domain,
32 - 'type' => $row->voter_type,
33 - 'authority' => $row->voter_authority,
34 - 'properties' => self::decodeProperties( $row->voter_properties )
35 - ) );
36 - }
37 -
38 - static function createUser( $params ) {
39 - $db = wfGetDB( DB_MASTER );
40 - $id = $db->nextSequenceValue( 'voters_voter_id' );
41 - $row = array(
42 - 'voter_id' => $id,
43 - 'voter_name' => $params['name'],
44 - 'voter_type' => $params['type'],
45 - 'voter_domain' => $params['domain'],
46 - 'voter_authority' => $params['authority'],
47 - 'voter_properties' => self::encodeProperties( $params['properties'] )
48 - );
49 - $db->insert( 'securepoll_voters', $row, __METHOD__ );
50 - $params['id'] = $db->insertId();
51 - return new self( $params );
52 - }
53 -
54 - function getId() { return $this->id; }
55 - function getName() { return $this->name; }
56 - function getType() { return $this->type; }
57 - function getDomain() { return $this->domain; }
58 - function getAuthority() { return $this->authority; }
59 -
60 - function getLanguage() {
61 - return $this->getProperty( 'language', 'en' );
62 - }
63 -
64 - function getProperty( $name, $default = false ) {
65 - if ( isset( $this->properties[$name] ) ) {
66 - return $this->properties[$name];
67 - } else {
68 - return $default;
69 - }
70 - }
71 -
72 - function isRemote() {
73 - return $this->type !== 'local';
74 - }
75 -
76 - static function decodeProperties( $blob ) {
77 - if ( strval( $blob ) == '' ) {
78 - return array();
79 - } else {
80 - return unserialize( $blob );
81 - }
82 - }
83 -
84 - static function encodeProperties( $props ) {
85 - return serialize( $props );
86 - }
87 -}
Index: trunk/extensions/SecurePoll/includes/TranslatePage.php
@@ -66,18 +66,25 @@
6767 $controlName = 'trans_' . $entity->getId() . '_' . $messageName;
6868 $primaryText = $entity->getRawMessage( $messageName, $primary );
6969 $secondaryText = $entity->getRawMessage( $messageName, $secondary );
 70+ $attribs = array( 'class' => 'securepoll-translate-box' );
 71+ if ( !$this->isAdmin ) {
 72+ $attribs['readonly'] = '1';
 73+ }
7074 $s .= '<tr><td>' . htmlspecialchars( "$entityName/$messageName" ) . "</td>\n" .
7175 '<td>' . nl2br( htmlspecialchars( $primaryText ) ) . '</td>' .
7276 '<td>' .
73 - Xml::textarea( $controlName, $secondaryText, 40, 3,
74 - array( 'class' => 'securepoll-translate-box' ) ) .
 77+ Xml::textarea( $controlName, $secondaryText, 40, 3, $attribs ) .
7578 "</td></tr>\n";
7679 }
7780 }
78 - $s .= '</table>' .
 81+ $s .= '</table>';
 82+ if ( $this->isAdmin ) {
 83+ $s .=
7984 '<p style="text-align: center;">' .
8085 Xml::submitButton( wfMsg( 'securepoll-submit-translate' ) ) .
81 - "</p></form>\n";
 86+ "</p>";
 87+ }
 88+ $s .= "</form>\n";
8289 $wgOut->addHTML( $s );
8390 }
8491
@@ -114,6 +121,12 @@
115122
116123 function doSubmit( $secondary ) {
117124 global $wgRequest, $wgOut;
 125+
 126+ if ( !$this->isAdmin ) {
 127+ $wgOut->addWikiMsg( 'securepoll-need-admin' );
 128+ return;
 129+ }
 130+
118131 $entities = array_merge( array( $this->election ), $this->election->getDescendants() );
119132 $replaceBatch = array();
120133 foreach ( $entities as $entity ) {
Index: trunk/extensions/SecurePoll/includes/EntryPage.php
@@ -3,6 +3,103 @@
44 class SecurePoll_EntryPage extends SecurePoll_Page {
55 function execute() {
66 global $wgOut;
7 - $wgOut->addWikiMsg( 'securepoll_entry' );
 7+ $pager = new SecurePoll_ElectionPager( $this );
 8+ $wgOut->addHTML(
 9+ $pager->getBody() .
 10+ $pager->getNavigationBar()
 11+ );
812 }
 13+
 14+ function getTitle() {
 15+ return $this->parent->getTitle( 'entry' );
 16+ }
917 }
 18+
 19+class SecurePoll_ElectionPager extends TablePager {
 20+ var $subpages = array(
 21+ 'vote',
 22+ 'translate',
 23+ 'list',
 24+ 'dump'
 25+ );
 26+ var $fields = array(
 27+ 'el_title',
 28+ 'el_start_date',
 29+ 'el_end_date',
 30+ 'links'
 31+ );
 32+ var $entryPage;
 33+
 34+ function __construct( $parent ) {
 35+ $this->entryPage = $parent;
 36+ parent::__construct();
 37+ }
 38+
 39+ function getQueryInfo() {
 40+ return array(
 41+ 'tables' => 'securepoll_elections',
 42+ 'fields' => '*',
 43+ 'conds' => false,
 44+ 'options' => array()
 45+ );
 46+ }
 47+
 48+ function isFieldSortable( $field ) {
 49+ return in_array( $field, array(
 50+ 'el_title', 'el_start_date', 'el_end_date'
 51+ ) );
 52+ }
 53+
 54+ function formatValue( $name, $value ) {
 55+ global $wgLang;
 56+ switch ( $name ) {
 57+ case 'el_start_date':
 58+ case 'el_end_date':
 59+ return $wgLang->timeanddate( $value );
 60+ case 'links':
 61+ return $this->getLinks();
 62+ default:
 63+ return htmlspecialchars( $value );
 64+ }
 65+ }
 66+
 67+ function getLinks() {
 68+ global $wgUser;
 69+ $id = $this->mCurrentRow->el_entity;
 70+ $s = '';
 71+ $sep = wfMsg( 'pipe-separator' );
 72+ $skin = $wgUser->getSkin();
 73+ foreach ( $this->subpages as $subpage ) {
 74+ $title = $this->entryPage->parent->getTitle( "$subpage/$id" );
 75+ $linkText = wfMsg( "securepoll-subpage-$subpage" );
 76+ if ( $s !== '' ) {
 77+ $s .= $sep;
 78+ }
 79+ $s .= $skin->makeKnownLinkObj( $title, $linkText );
 80+ }
 81+ return $s;
 82+ }
 83+
 84+ function getDefaultSort() {
 85+ return 'el_start_date';
 86+ }
 87+
 88+ function getFieldNames() {
 89+ $names = array();
 90+ foreach ( $this->fields as $field ) {
 91+ if ( $field == 'links' ) {
 92+ $names[$field] = '';
 93+ } else {
 94+ $msgName = 'securepoll-header-' .
 95+ strtr( $field, array( 'el_' => '', '_' => '-' ) );
 96+ $names[$field] = wfMsg( $msgName );
 97+ }
 98+ }
 99+ return $names;
 100+ }
 101+
 102+ function getTitle() {
 103+ return $this->entryPage->getTitle();
 104+ }
 105+}
 106+
Index: trunk/extensions/SecurePoll/includes/Voter.php
@@ -0,0 +1,89 @@
 2+<?php
 3+
 4+class SecurePoll_Voter {
 5+ var $id, $electionId, $name, $domain, $wiki, $type, $url;
 6+ var $properties = array();
 7+
 8+ static $paramNames = array( 'id', 'electionId', 'name', 'domain', 'wiki', 'type', 'url', 'properties' );
 9+
 10+ function __construct( $params ) {
 11+ foreach ( self::$paramNames as $name ) {
 12+ if ( isset( $params[$name] ) ) {
 13+ $this->$name = $params[$name];
 14+ }
 15+ }
 16+ }
 17+
 18+ static function newFromId( $id ) {
 19+ $db = wfGetDB( DB_MASTER );
 20+ $row = $db->selectRow( 'securepoll_voters', '*', array( 'voter_id' => $id ), __METHOD__ );
 21+ if ( !$row ) {
 22+ return false;
 23+ }
 24+ return self::newFromRow( $row );
 25+ }
 26+
 27+ static function newFromRow( $row ) {
 28+ return new self( array(
 29+ 'id' => $row->voter_id,
 30+ 'electionId' => $row->voter_election,
 31+ 'name' => $row->voter_name,
 32+ 'domain' => $row->voter_domain,
 33+ 'type' => $row->voter_type,
 34+ 'url' => $row->voter_url,
 35+ 'properties' => self::decodeProperties( $row->voter_properties )
 36+ ) );
 37+ }
 38+
 39+ static function createVoter( $params ) {
 40+ $db = wfGetDB( DB_MASTER );
 41+ $id = $db->nextSequenceValue( 'voters_voter_id' );
 42+ $row = array(
 43+ 'voter_id' => $id,
 44+ 'voter_election' => $params['electionId'],
 45+ 'voter_name' => $params['name'],
 46+ 'voter_type' => $params['type'],
 47+ 'voter_domain' => $params['domain'],
 48+ 'voter_url' => $params['url'],
 49+ 'voter_properties' => self::encodeProperties( $params['properties'] )
 50+ );
 51+ $db->insert( 'securepoll_voters', $row, __METHOD__ );
 52+ $params['id'] = $db->insertId();
 53+ return new self( $params );
 54+ }
 55+
 56+ function getId() { return $this->id; }
 57+ function getName() { return $this->name; }
 58+ function getType() { return $this->type; }
 59+ function getDomain() { return $this->domain; }
 60+ function getUrl() { return $this->url; }
 61+ function getElectionId() { return $this->electionId; }
 62+
 63+ function getLanguage() {
 64+ return $this->getProperty( 'language', 'en' );
 65+ }
 66+
 67+ function getProperty( $name, $default = false ) {
 68+ if ( isset( $this->properties[$name] ) ) {
 69+ return $this->properties[$name];
 70+ } else {
 71+ return $default;
 72+ }
 73+ }
 74+
 75+ function isRemote() {
 76+ return $this->type !== 'local';
 77+ }
 78+
 79+ static function decodeProperties( $blob ) {
 80+ if ( strval( $blob ) == '' ) {
 81+ return array();
 82+ } else {
 83+ return unserialize( $blob );
 84+ }
 85+ }
 86+
 87+ static function encodeProperties( $props ) {
 88+ return serialize( $props );
 89+ }
 90+}
Property changes on: trunk/extensions/SecurePoll/includes/Voter.php
___________________________________________________________________
Name: svn:mergeinfo
191 +
Name: svn:eol-style
292 + native
Index: trunk/extensions/SecurePoll/includes/Auth.php
@@ -14,18 +14,36 @@
1515 return new $class;
1616 }
1717
18 - function login( $election ) {
 18+ function autoLogin( $election ) {
 19+ return Status::newFatal( 'securepoll-not-logged-in' );
 20+ }
 21+
 22+ function requestLogin( $election ) {
 23+ return $this->autoLogin();
 24+ }
 25+
 26+ function getVoterFromSession( $election ) {
1927 if ( session_id() == '' ) {
2028 wfSetupSession();
2129 }
22 - if ( isset( $_SESSION['bvUser'] ) ) {
23 - $user = SecurePoll_User::newFromId( $_SESSION['bvUser'] );
 30+ if ( isset( $_SESSION['securepoll_voter'][$election->getId()] ) ) {
 31+ $voter = SecurePoll_Voter::newFromId(
 32+ $_SESSION['securepoll_voter'][$election->getId()] );
 33+
 34+ # Sanity check election ID
 35+ if ( $voter->getElectionId() != $election->getId() ) {
 36+ var_dump( $voter );
 37+ return false;
 38+ } else {
 39+ return $voter;
 40+ }
 41+
2442 } else {
2543 return false;
2644 }
2745 }
2846
29 - function getUser( $params ) {
 47+ function getVoter( $params ) {
3048 $dbw = wfGetDB( DB_MASTER );
3149
3250 # This needs to be protected by FOR UPDATE
@@ -35,9 +53,10 @@
3654 $row = $dbw->selectRow(
3755 'securepoll_voters', '*',
3856 array(
39 - 'voter_name' => $params['name'],
 57+ 'voter_name' => $params['name'],
 58+ 'voter_election' => $params['electionId'],
4059 'voter_domain' => $params['domain'],
41 - 'voter_authority' => $params['authority']
 60+ 'voter_url' => $params['url']
4261 ),
4362 __METHOD__,
4463 array( 'FOR UPDATE' )
@@ -45,51 +64,88 @@
4665 if ( $row ) {
4766 # No need to hold the lock longer
4867 $dbw->commit();
49 - $user = SecurePoll_User::newFromRow( $row );
 68+ $user = SecurePoll_Voter::newFromRow( $row );
5069 } else {
5170 # Lock needs to be held until the row is inserted
52 - $user = SecurePoll_User::createUser( $params );
 71+ $user = SecurePoll_Voter::createVoter( $params );
5372 $dbw->commit();
5473 }
5574 return $user;
5675 }
 76+
 77+ function newAutoSession( $election ) {
 78+ $status = $this->autoLogin( $election );
 79+ if ( $status->isGood() ) {
 80+ $_SESSION['securepoll_voter'][$election->getId()] = $status->value->getId();
 81+ }
 82+ return $status;
 83+ }
 84+
 85+ function newRequestedSession( $election ) {
 86+ $status = $this->requestLogin( $election );
 87+ if ( $status->isGood() ) {
 88+ $_SESSION['securepoll_voter'][$election->getId()] = $status->value->getId();
 89+ }
 90+ return $status;
 91+ }
5792 }
5893
5994 class SecurePoll_LocalAuth extends SecurePoll_Auth {
60 - function login( $election ) {
 95+ function autoLogin( $election ) {
6196 global $wgUser, $wgServer, $wgLang;
62 - $user = parent::login( $election );
63 - if ( !$user && $wgUser->isLoggedIn() ) {
64 - $params = array(
65 - 'name' => $wgUser->getName(),
66 - 'type' => 'local',
67 - 'domain' => preg_replace( '!.*/(.*)$!', '$1', $wgServer ),
68 - 'authority' => $wgUser->getUserPage()->getFullURL(),
69 - 'properties' => array(
70 - 'wiki' => wfWikiID(),
71 - 'blocked' => $wgUser->isBlocked(),
72 - 'edit-count' => $wgUser->getEditCount(),
73 - 'bot' => $wgUser->isBot(),
74 - 'language' => $wgLang->getCode(),
75 - )
76 - );
77 - $user = $this->getUser( $params );
 97+ if ( $wgUser->isAnon() ) {
 98+ return Status::newFatal( 'securepoll-not-logged-in' );
7899 }
79 - return $user;
 100+ $params = self::getUserParams( $wgUser );
 101+ $params['electionId'] = $election->getId();
 102+ $qualStatus = $election->getQualifiedStatus( $params );
 103+ if ( !$qualStatus->isOK() ) {
 104+ return $qualStatus;
 105+ }
 106+ $voter = $this->getVoter( $params );
 107+ return Status::newGood( $voter );
80108 }
 109+
 110+ static function getUserParams( $user ) {
 111+ global $wgServer;
 112+ return array(
 113+ 'name' => $user->getName(),
 114+ 'type' => 'local',
 115+ 'domain' => preg_replace( '!.*/(.*)$!', '$1', $wgServer ),
 116+ 'url' => $user->getUserPage()->getFullURL(),
 117+ 'properties' => array(
 118+ 'wiki' => wfWikiID(),
 119+ 'blocked' => $user->isBlocked(),
 120+ 'edit-count' => $user->getEditCount(),
 121+ 'bot' => $user->isBot(),
 122+ 'language' => $user->getOption( 'language' ),
 123+ 'groups' => $user->getGroups(),
 124+ 'lists' => self::getLists( $user )
 125+ )
 126+ );
 127+ }
 128+
 129+ static function getLists( $user ) {
 130+ $dbr = wfGetDB( DB_SLAVE );
 131+ $res = $dbr->select(
 132+ 'securepoll_lists',
 133+ array( 'li_name' ),
 134+ array( 'li_member' => $user->getId() ),
 135+ __METHOD__
 136+ );
 137+ $lists = array();
 138+ foreach ( $res as $row ) {
 139+ $lists[] = $row->li_name;
 140+ }
 141+ return $lists;
 142+ }
81143 }
82144
83145 class SecurePoll_RemoteMWAuth extends SecurePoll_Auth {
84 - function login( $election ) {
 146+ function requestLogin( $election ) {
85147 global $wgRequest;
86148
87 - $user = parent::login( $election );
88 - if ( $user ) {
89 - return $user;
90 - }
91 -
92 - $urlParamNames = array( 'sid', 'casid', 'wiki', 'site', 'domain' );
93 - $params = array();
 149+ $urlParamNames = array( 'id', 'token', 'wiki', 'site', 'domain' );
94150 $vars = array();
95151 foreach ( $urlParamNames as $name ) {
96152 $value = $wgRequest->getVal( $name );
@@ -97,99 +153,63 @@
98154 wfDebug( __METHOD__ . " Invalid parameter: $name\n" );
99155 return false;
100156 }
 157+ $params[$name] = $value;
101158 $vars["\$$name"] = $value;
102 - $params[$name] = $value;
103159 }
104160
105 - $electionParamNames = array( 'remote-mw-api-url', 'remote-mw-cookie', 'remote-mw-ca-cookie' );
106 - foreach ( $electionParamNames as $name ) {
107 - $value = $election->getProperty( $name );
108 - if ( $value !== false ) {
109 - $value = strtr( $value, $vars );
110 - }
111 - $params[$name] = $value;
 161+ $url = $election->getProperty( 'remote-mw-script-path' );
 162+ $url = strtr( $url, $vars );
 163+ if ( substr( $url, -1 ) != '/' ) {
 164+ $url .= '/';
112165 }
 166+ $url .= 'extensions/SecurePoll/auth-api.php?' .
 167+ wfArrayToCGI( array(
 168+ 'token' => $params['token'],
 169+ 'id' => $params['id']
 170+ ) );
113171
114 - if ( !$params['sid'] ) {
115 - return false;
116 - }
117 - if ( !$params['remote-mw-cookie'] ) {
118 - wfDebug( __METHOD__ . ": No remote cookie configured!\n" );
119 - return false;
120 - }
121 - $cookies = array( $params['remote-mw-cookie'] => $params['sid'] );
122 - if ( $params['casid'] && $params['remote-mw-ca-cookie'] ) {
123 - $cookies[$params['remote-mw-ca-cookie']] = $params['casid'];
124 - }
125 - $cookieHeader = $this->encodeCookies( $cookies );
126 - $url = $params['remote-mw-api-url'] .
127 - '?action=query&format=php' .
128 - '&meta=userinfo&uiprop=blockinfo|rights|editcount|options' .
129 - '&meta=siteinfo';
130 - $curlParams = array(
131 - CURLOPT_COOKIE => $cookieHeader,
 172+ // Use the default SSL certificate file
 173+ // Necessary on some versions of cURL, others do this by default
 174+ $curlParams = array( CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt' );
132175
133 - // Use the default SSL certificate file
134 - // Necessary on some versions of cURL, others do this by default
135 - CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt'
136 - );
137 -
138 - wfDebug( "Fetching URL $url\n" );
139176 $value = Http::get( $url, 20, $curlParams );
140177
141178 if ( !$value ) {
142 - wfDebug( __METHOD__ . ": No response from server\n" );
143 - $_SESSION['bvCurlError'] = curl_error( $c );
144 - return false;
 179+ return Status::newFatal( 'securepoll-remote-auth-error' );
145180 }
146181
147 - $decoded = unserialize( $value );
148 - $userinfo = $decoded['query']['userinfo'];
149 - $siteinfo = $decoded['query']['general'];
150 - if ( isset( $userinfo['anon'] ) ) {
151 - wfDebug( __METHOD__ . ": User is not logged in\n" );
152 - return false;
 182+ $status = unserialize( $value );
 183+ $status->cleanCallback = false;
 184+
 185+ if ( !$status || !( $status instanceof Status ) ) {
 186+ return Status::newFatal( 'securepoll-remote-parse-error' );
153187 }
154 - if ( !isset( $userinfo['name'] ) ) {
155 - wfDebug( __METHOD__ . ": No username in response\n" );
156 - return false;
 188+ if ( !$status->isOK() ) {
 189+ return $status;
157190 }
158 - if ( isset( $userinfo['options']['language'] ) ) {
159 - $language = $userinfo['options']['language'];
160 - } else {
161 - $language = 'en';
 191+ $params = $status->value;
 192+ $params['type'] = 'remote-mw';
 193+ $params['electionId'] = $election->getId();
 194+
 195+ $qualStatus = $election->getQualifiedStatus( $params );
 196+ if ( !$qualStatus->isOK() ) {
 197+ return $qualStatus;
162198 }
163 - $urlInfo = wfParseUrl( $decoded['query']['general']['base'] );
164 - $domain = $urlInfo === false ? false : $urlInfo['host'];
165 - $userPage = $siteinfo['server'] .
166 - str_replace( $siteinfo['articlepath'], '$1', '' ) .
167 - 'User:' .
168 - urlencode( str_replace( $userinfo['name'], ' ', '_' ) );
169199
170 - wfDebug( __METHOD__ . " got response for user {$userinfo['name']}@{$params['wiki']}\n" );
171 - return $this->getUser( array(
172 - 'name' => $userinfo['name'],
173 - 'type' => 'remote-mw',
174 - 'domain' => $domain,
175 - 'authority' => $userPage,
176 - 'properties' => array(
177 - 'wiki' => $siteinfo['wikiid'],
178 - 'blocked' => isset( $userinfo['blockedby'] ),
179 - 'edit-count' => $userinfo['edit-count'],
180 - 'bot' => in_array( 'bot', $userinfo['rights'] ),
181 - 'language' => $language,
182 - )
183 - ) );
 200+ return Status::newGood( $this->getVoter( $params ) );
184201 }
185202
186 - function encodeCookies( $cookies ) {
187 - $s = '';
188 - foreach ( $cookies as $name => $value ) {
189 - if ( $s !== '' ) {
190 - $s .= ';';
191 - }
192 - $s .= urlencode( $name ) . '=' . urlencode( $value );
193 - }
194 - return $s;
 203+ /**
 204+ * Apply a one-way hash function to a string.
 205+ *
 206+ * The aim is to encode a user's login token so that it can be transmitted to the
 207+ * voting server without giving the voting server any special rights on the wiki
 208+ * (apart from the ability to verify the user). We truncate the hash at 26
 209+ * hexadecimal digits, to provide 24 bits less information than original token.
 210+ * This makes discovery of the token difficult even if the hash function is
 211+ * completely broken.
 212+ */
 213+ static function encodeToken( $token ) {
 214+ return substr( sha1( __CLASS__ . '-' . $token ), 0, 26 );
195215 }
196216 }
Index: trunk/extensions/SecurePoll/includes/Election.php
@@ -21,7 +21,7 @@
2222 }
2323
2424 function getMessageNames() {
25 - return array( 'title', 'intro' );
 25+ return array( 'title', 'intro', 'jump-text' );
2626 }
2727
2828 function getChildren() {
@@ -52,8 +52,38 @@
5353 return $this->ballot;
5454 }
5555
56 - function getQualifiedStatus( $user ) {
57 - return Status::newGood();
 56+ function getQualifiedStatus( $params ) {
 57+ $props = $params['properties'];
 58+ $status = Status::newGood();
 59+
 60+ # Edits
 61+ $minEdits = $this->getProperty( 'min-edits' );
 62+ $edits = isset( $props['edit-count'] ) ? $props['edit-count'] : 0;
 63+ if ( $minEdits && $edits < $minEdits ) {
 64+ $status->fatal( 'securepoll-too-few-edits', $minEdits, $edits );
 65+ }
 66+
 67+ # Blocked
 68+ $notBlocked = $this->getProperty( 'not-blocked' );
 69+ $isBlocked = !empty( $props['blocked'] );
 70+ if ( $notBlocked && $isBlocked ) {
 71+ $status->fatal( 'securepoll-blocked' );
 72+ }
 73+
 74+ # Groups
 75+ $needGroup = $this->getProperty( 'need-group' );
 76+ $groups = isset( $props['groups'] ) ? $props['groups'] : array();
 77+ if ( $needGroup && !in_array( $needGroup, $groups ) ) {
 78+ $status->fatal( 'securepoll-not-in-group', $needGroup );
 79+ }
 80+
 81+ # Lists
 82+ $needList = $this->getProperty( 'need-list' );
 83+ $lists = isset( $props['lists'] ) ? $props['lists'] : array();
 84+ if ( $needList && !in_array( $needList, $lists ) ) {
 85+ $status->fatal( 'securepoll-not-in-list' );
 86+ }
 87+ return $status;
5888 }
5989
6090 function isAdmin( User $user ) {
Index: trunk/extensions/SecurePoll/includes/LoginPage.php
@@ -0,0 +1,28 @@
 2+<?php
 3+
 4+class SecurePoll_LoginPage extends SecurePoll_Page {
 5+ function execute( $params ) {
 6+ global $wgOut;
 7+
 8+ if ( !count( $params ) ) {
 9+ $wgOut->addWikiMsg( 'securepoll-too-few-params' );
 10+ return;
 11+ }
 12+
 13+ $electionId = intval( $params[0] );
 14+ $this->election = $this->parent->getElection( $electionId );
 15+ if ( !$this->election ) {
 16+ $wgOut->addWikiMsg( 'securepoll-invalid-election', $electionId );
 17+ return;
 18+ }
 19+
 20+ $auth = $this->election->getAuth();
 21+ $status = $auth->newRequestedSession( $this->election );
 22+ if ( !$status->isOK() ) {
 23+ $wgOut->addWikiText( $status->getWikiText() );
 24+ return;
 25+ }
 26+ $votePage = SpecialPage::getTitleFor( 'SecurePoll', 'vote/' . $this->election->getId() );
 27+ $wgOut->redirect( $votePage->getFullUrl() );
 28+ }
 29+}
Property changes on: trunk/extensions/SecurePoll/includes/LoginPage.php
___________________________________________________________________
Name: svn:eol-style
130 + native
Index: trunk/extensions/SecurePoll/includes/VotePage.php
@@ -19,13 +19,24 @@
2020 }
2121
2222 $this->auth = $this->election->getAuth();
23 - $this->user = $this->auth->login( $this->election );
24 - if ( !$this->user ) {
25 - $wgOut->addWikiMsg( 'securepoll-not-authorised' );
26 - return;
 23+
 24+ # Get voter from session
 25+ $this->voter = $this->auth->getVoterFromSession( $this->election );
 26+
 27+ # If there's no session, try creating one.
 28+ # This will fail if the user is not authorised to vote in the election
 29+ if ( !$this->voter ) {
 30+ $status = $this->auth->newAutoSession( $this->election );
 31+ if ( $status->isOK() ) {
 32+ $this->voter = $status->value;
 33+ } else {
 34+ $wgOut->addWikiText( $status->getWikiText() );
 35+ return;
 36+ }
2737 }
28 - $this->initLanguage( $this->user, $this->election );
2938
 39+ $this->initLanguage( $this->voter, $this->election );
 40+
3041 $wgOut->setPageTitle( $this->election->getMessage( 'title' ) );
3142
3243 if ( !$this->election->isStarted() ) {
@@ -36,20 +47,19 @@
3748 return;
3849 }
3950
40 - // Show welcome
41 - if ( $this->user->isRemote() ) {
42 - $wgOut->addWikiMsg( 'securepoll-welcome', $this->user->getName() );
 51+ // Show jump form if necessary
 52+ if ( $this->election->getProperty( 'jump-url' ) ) {
 53+ $this->showJumpForm();
 54+ return;
4355 }
4456
45 - // Show qualification notice
46 - $status = $this->election->getQualifiedStatus( $this->user );
47 - if ( !$status->isOK() ) {
48 - $wgOut->addWikiText( $status->getWikiText( 'securepoll-not-qualified' ) );
49 - return;
 57+ // Show welcome
 58+ if ( $this->voter->isRemote() ) {
 59+ $wgOut->addWikiMsg( 'securepoll-welcome', $this->voter->getName() );
5060 }
5161
5262 // Show change notice
53 - if ( $this->election->hasVoted( $this->user ) && !$this->election->allowChange() ) {
 63+ if ( $this->election->hasVoted( $this->voter ) && !$this->election->allowChange() ) {
5464 $wgOut->addWikiMsg( 'securepoll-change-disallowed' );
5565 return;
5666 }
@@ -70,7 +80,7 @@
7181 global $wgOut;
7282
7383 // Show introduction
74 - if ( $this->election->hasVoted( $this->user ) && $this->election->allowChange() ) {
 84+ if ( $this->election->hasVoted( $this->voter ) && $this->election->allowChange() ) {
7585 $wgOut->addWikiMsg( 'securepoll-change-allowed' );
7686 }
7787 $wgOut->addWikiText( $this->election->getMessage( 'intro' ) );
@@ -127,7 +137,7 @@
128138 array( 'vote_current' => 0 ), # SET
129139 array( # WHERE
130140 'vote_election' => $this->election->getId(),
131 - 'vote_user' => $this->user->getId(),
 141+ 'vote_user' => $this->voter->getId(),
132142 ),
133143 __METHOD__
134144 );
@@ -145,9 +155,9 @@
146156 array(
147157 'vote_id' => $voteId,
148158 'vote_election' => $this->election->getId(),
149 - 'vote_user' => $this->user->getId(),
150 - 'vote_user_name' => $this->user->getName(),
151 - 'vote_user_domain' => $this->user->getDomain(),
 159+ 'vote_user' => $this->voter->getId(),
 160+ 'vote_user_name' => $this->voter->getName(),
 161+ 'vote_user_domain' => $this->voter->getDomain(),
152162 'vote_record' => $encrypted,
153163 'vote_ip' => IP::toHex( wfGetIP() ),
154164 'vote_xff' => $xff,
@@ -181,4 +191,25 @@
182192 global $wgOut;
183193 $wgOut->addWikiMsg( 'securepoll_invalidentered' );
184194 }
 195+
 196+ function showJumpForm() {
 197+ global $wgOut, $wgUser;
 198+ $url = $this->election->getProperty( 'jump-url' );
 199+ if ( !$url ) {
 200+ throw new MWException( 'Configuration error: no jump-url' );
 201+ }
 202+ $id = $this->election->getProperty( 'jump-id' );
 203+ if ( !$id ) {
 204+ throw new MWException( 'Configuration error: no jump-id' );
 205+ }
 206+ $url .= "/login/$id";
 207+ $wgOut->addWikiText( $this->election->getMessage( 'jump-text' ) );
 208+ $wgOut->addHTML(
 209+ Xml::openElement( 'form', array( 'action' => $url, 'method' => 'post' ) ) .
 210+ Xml::hidden( 'token', SecurePoll_RemoteMWAuth::encodeToken( $wgUser->getToken() ) ) .
 211+ Xml::hidden( 'id', $wgUser->getId() ) .
 212+ Xml::submitButton( wfMsg( 'securepoll-jump' ) ) .
 213+ '</form>'
 214+ );
 215+ }
185216 }
Index: trunk/extensions/SecurePoll/includes/Page.php
@@ -8,7 +8,7 @@
99 }
1010
1111 function initLanguage( $user, $election ) {
12 - if ( $user instanceof SecurePoll_User ) {
 12+ if ( $user instanceof SecurePoll_Voter ) {
1313 $userLang = $user->getLanguage();
1414 } else {
1515 $userLang = $user->getOption( 'language' );
Index: trunk/extensions/SecurePoll/includes/Ballot.php
@@ -78,6 +78,9 @@
7979 }
8080 }
8181
 82+/**
 83+ * TODO: this is code copied directly from BoardVote, it needs to be ported.
 84+ */
8285 class SecurePoll_PreferentialBallot extends SecurePoll_Ballot {
8386 function getTallyTypes() {
8487 return array( 'plurality', 'condorcet' );
@@ -132,3 +135,4 @@
133136 function getForm() { }
134137 function submitForm() { }
135138 }
 139+
Index: trunk/extensions/SecurePoll/includes/DetailsPage.php
@@ -42,7 +42,7 @@
4343 $this->detailEntry( 'securepoll-header-user-name', $row->voter_name ) .
4444 $this->detailEntry( 'securepoll-header-user-type', $row->voter_type ) .
4545 $this->detailEntry( 'securepoll-header-user-domain', $row->voter_domain ) .
46 - $this->detailEntry( 'securepoll-header-authority', $row->voter_authority ) .
 46+ $this->detailEntry( 'securepoll-header-url', $row->voter_url ) .
4747 $this->detailEntry( 'securepoll-header-ip', IP::formatHex( $row->vote_ip ) ) .
4848 $this->detailEntry( 'securepoll-header-xff', $row->vote_xff ) .
4949 $this->detailEntry( 'securepoll-header-ua', $row->vote_ua ) .
@@ -51,7 +51,7 @@
5252 );
5353 $wgOut->addHTML( '<h2>' . wfMsgHTML( 'securepoll-voter-properties' ) . "</h2>\n" );
5454 $wgOut->addHTML( '<table class="TablePager">' );
55 - $props = SecurePoll_User::decodeProperties( $row->voter_properties );
 55+ $props = SecurePoll_Voter::decodeProperties( $row->voter_properties );
5656 foreach ( $props as $name => $value ) {
5757 $wgOut->addHTML(
5858 '<td class="securepoll-detail-header">' .
Index: trunk/extensions/SecurePoll/SecurePoll.sql
@@ -47,13 +47,15 @@
4848
4949 CREATE TABLE /*_*/securepoll_voters (
5050 voter_id int not null primary key auto_increment,
 51+ voter_election int not null,
5152 voter_name varchar(255) binary not null,
5253 voter_type varbinary(32) not null,
5354 voter_domain varbinary(255) not null,
54 - voter_authority blob,
 55+ voter_url blob,
5556 voter_properties blob
5657 );
57 -CREATE INDEX /*i*/spvoter_name_domain ON /*_*/securepoll_voters (voter_name, voter_domain);
 58+CREATE INDEX /*i*/spvoter_elec_name_domain ON /*_*/securepoll_voters
 59+ (voter_election, voter_name, voter_domain);
5860
5961 CREATE TABLE /*_*/securepoll_votes (
6062 vote_id int not null primary key auto_increment,
@@ -96,3 +98,13 @@
9799 CREATE INDEX /*i*/spstrike_vote ON /*_*/securepoll_strike
98100 (st_vote, st_timestamp);
99101
 102+CREATE TABLE /*_*/securepoll_lists (
 103+ li_name varbinary(255),
 104+ li_member int not null
 105+);
 106+CREATE INDEX /*i*/splists_name ON /*_*/securepoll_lists
 107+ (li_name, li_member);
 108+CREATE INDEX /*i*/splists_member ON /*_*/securepoll_lists
 109+ (li_member, li_name);
 110+
 111+

Follow-up revisions

RevisionCommit summaryAuthorDate
r49224Per r49209: change optional message nameraymond20:02, 5 April 2009

Status & tagging log