Index: trunk/extensions/SecurePoll/SecurePoll_body.php |
— | — | @@ -9,6 +9,7 @@ |
10 | 10 | 'dump' => 'SecurePoll_DumpPage', |
11 | 11 | 'entry' => 'SecurePoll_EntryPage', |
12 | 12 | 'list' => 'SecurePoll_ListPage', |
| 13 | + 'login' => 'SecurePoll_LoginPage', |
13 | 14 | 'translate' => 'SecurePoll_TranslatePage', |
14 | 15 | 'vote' => 'SecurePoll_VotePage', |
15 | 16 | ); |
— | — | @@ -75,9 +76,9 @@ |
76 | 77 | } |
77 | 78 | |
78 | 79 | 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() ); |
81 | 82 | } |
82 | | - return $_SESSION['bvToken']; |
| 83 | + return $_SESSION['spToken']; |
83 | 84 | } |
84 | 85 | } |
Index: trunk/extensions/SecurePoll/SecurePoll.i18n.php |
— | — | @@ -20,7 +20,6 @@ |
21 | 21 | # Vote (most important to translate) |
22 | 22 | 'securepoll-too-few-params' => 'Not enough subpage parameters (invalid link).', |
23 | 23 | 'securepoll-invalid-election' => '"$1" is not a valid election ID.', |
24 | | - 'securepoll-not-authorised' => 'You are not authorised to vote in this election.', |
25 | 24 | 'securepoll-welcome' => '<strong>Welcome $1!</strong>', |
26 | 25 | 'securepoll-not-started' => 'This election has not yet started. |
27 | 26 | It is scheduled to start on $2 at $3.', |
— | — | @@ -55,7 +54,21 @@ |
56 | 55 | 'securepoll-gpg-parse-error' => 'Error interpreting GPG output.', |
57 | 56 | 'securepoll-no-decryption-key' => 'No decryption key is configured. |
58 | 57 | 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.', |
59 | 63 | |
| 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 | + |
60 | 73 | # List page |
61 | 74 | # Mostly for admins |
62 | 75 | 'securepoll-list-title' => 'List votes: $1', |
— | — | @@ -81,8 +94,8 @@ |
82 | 95 | 'securepoll-details-title' => 'Vote details: #$1', |
83 | 96 | 'securepoll-invalid-vote' => '"$1" is not a valid vote ID', |
84 | 97 | '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', |
87 | 100 | 'securepoll-voter-properties' => 'Voter properties', |
88 | 101 | 'securepoll-strike-log' => 'Strike log', |
89 | 102 | 'securepoll-header-action' => 'Action', |
— | — | @@ -103,6 +116,15 @@ |
104 | 117 | 'securepoll-submit-translate' => 'Update', |
105 | 118 | 'securepoll-language-label' => 'Select language: ', |
106 | 119 | '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', |
107 | 129 | ); |
108 | 130 | |
109 | 131 | /** Message documentation (Message documentation) |
Index: trunk/extensions/SecurePoll/SecurePoll.php |
— | — | @@ -73,11 +73,12 @@ |
74 | 74 | 'SecurePoll_Entity' => "$dir/includes/Entity.php", |
75 | 75 | 'SecurePoll_EntryPage' => "$dir/includes/EntryPage.php", |
76 | 76 | 'SecurePoll_ListPage' => "$dir/includes/ListPage.php", |
| 77 | + 'SecurePoll_LoginPage' => "$dir/includes/LoginPage.php", |
77 | 78 | 'SecurePoll_Option' => "$dir/includes/Option.php", |
78 | 79 | 'SecurePoll_Page' => "$dir/includes/Page.php", |
79 | 80 | 'SecurePoll_Question' => "$dir/includes/Question.php", |
80 | 81 | 'SecurePoll_TranslatePage' => "$dir/includes/TranslatePage.php", |
81 | | - 'SecurePoll_User' => "$dir/includes/User.php", |
| 82 | + 'SecurePoll_Voter' => "$dir/includes/Voter.php", |
82 | 83 | 'SecurePoll_VotePage' => "$dir/includes/VotePage.php", |
83 | 84 | ); |
84 | 85 | |
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 @@ |
67 | 67 | $controlName = 'trans_' . $entity->getId() . '_' . $messageName; |
68 | 68 | $primaryText = $entity->getRawMessage( $messageName, $primary ); |
69 | 69 | $secondaryText = $entity->getRawMessage( $messageName, $secondary ); |
| 70 | + $attribs = array( 'class' => 'securepoll-translate-box' ); |
| 71 | + if ( !$this->isAdmin ) { |
| 72 | + $attribs['readonly'] = '1'; |
| 73 | + } |
70 | 74 | $s .= '<tr><td>' . htmlspecialchars( "$entityName/$messageName" ) . "</td>\n" . |
71 | 75 | '<td>' . nl2br( htmlspecialchars( $primaryText ) ) . '</td>' . |
72 | 76 | '<td>' . |
73 | | - Xml::textarea( $controlName, $secondaryText, 40, 3, |
74 | | - array( 'class' => 'securepoll-translate-box' ) ) . |
| 77 | + Xml::textarea( $controlName, $secondaryText, 40, 3, $attribs ) . |
75 | 78 | "</td></tr>\n"; |
76 | 79 | } |
77 | 80 | } |
78 | | - $s .= '</table>' . |
| 81 | + $s .= '</table>'; |
| 82 | + if ( $this->isAdmin ) { |
| 83 | + $s .= |
79 | 84 | '<p style="text-align: center;">' . |
80 | 85 | Xml::submitButton( wfMsg( 'securepoll-submit-translate' ) ) . |
81 | | - "</p></form>\n"; |
| 86 | + "</p>"; |
| 87 | + } |
| 88 | + $s .= "</form>\n"; |
82 | 89 | $wgOut->addHTML( $s ); |
83 | 90 | } |
84 | 91 | |
— | — | @@ -114,6 +121,12 @@ |
115 | 122 | |
116 | 123 | function doSubmit( $secondary ) { |
117 | 124 | global $wgRequest, $wgOut; |
| 125 | + |
| 126 | + if ( !$this->isAdmin ) { |
| 127 | + $wgOut->addWikiMsg( 'securepoll-need-admin' ); |
| 128 | + return; |
| 129 | + } |
| 130 | + |
118 | 131 | $entities = array_merge( array( $this->election ), $this->election->getDescendants() ); |
119 | 132 | $replaceBatch = array(); |
120 | 133 | foreach ( $entities as $entity ) { |
Index: trunk/extensions/SecurePoll/includes/EntryPage.php |
— | — | @@ -3,6 +3,103 @@ |
4 | 4 | class SecurePoll_EntryPage extends SecurePoll_Page { |
5 | 5 | function execute() { |
6 | 6 | global $wgOut; |
7 | | - $wgOut->addWikiMsg( 'securepoll_entry' ); |
| 7 | + $pager = new SecurePoll_ElectionPager( $this ); |
| 8 | + $wgOut->addHTML( |
| 9 | + $pager->getBody() . |
| 10 | + $pager->getNavigationBar() |
| 11 | + ); |
8 | 12 | } |
| 13 | + |
| 14 | + function getTitle() { |
| 15 | + return $this->parent->getTitle( 'entry' ); |
| 16 | + } |
9 | 17 | } |
| 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 |
1 | 91 | + |
Name: svn:eol-style |
2 | 92 | + native |
Index: trunk/extensions/SecurePoll/includes/Auth.php |
— | — | @@ -14,18 +14,36 @@ |
15 | 15 | return new $class; |
16 | 16 | } |
17 | 17 | |
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 ) { |
19 | 27 | if ( session_id() == '' ) { |
20 | 28 | wfSetupSession(); |
21 | 29 | } |
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 | + |
24 | 42 | } else { |
25 | 43 | return false; |
26 | 44 | } |
27 | 45 | } |
28 | 46 | |
29 | | - function getUser( $params ) { |
| 47 | + function getVoter( $params ) { |
30 | 48 | $dbw = wfGetDB( DB_MASTER ); |
31 | 49 | |
32 | 50 | # This needs to be protected by FOR UPDATE |
— | — | @@ -35,9 +53,10 @@ |
36 | 54 | $row = $dbw->selectRow( |
37 | 55 | 'securepoll_voters', '*', |
38 | 56 | array( |
39 | | - 'voter_name' => $params['name'], |
| 57 | + 'voter_name' => $params['name'], |
| 58 | + 'voter_election' => $params['electionId'], |
40 | 59 | 'voter_domain' => $params['domain'], |
41 | | - 'voter_authority' => $params['authority'] |
| 60 | + 'voter_url' => $params['url'] |
42 | 61 | ), |
43 | 62 | __METHOD__, |
44 | 63 | array( 'FOR UPDATE' ) |
— | — | @@ -45,51 +64,88 @@ |
46 | 65 | if ( $row ) { |
47 | 66 | # No need to hold the lock longer |
48 | 67 | $dbw->commit(); |
49 | | - $user = SecurePoll_User::newFromRow( $row ); |
| 68 | + $user = SecurePoll_Voter::newFromRow( $row ); |
50 | 69 | } else { |
51 | 70 | # Lock needs to be held until the row is inserted |
52 | | - $user = SecurePoll_User::createUser( $params ); |
| 71 | + $user = SecurePoll_Voter::createVoter( $params ); |
53 | 72 | $dbw->commit(); |
54 | 73 | } |
55 | 74 | return $user; |
56 | 75 | } |
| 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 | + } |
57 | 92 | } |
58 | 93 | |
59 | 94 | class SecurePoll_LocalAuth extends SecurePoll_Auth { |
60 | | - function login( $election ) { |
| 95 | + function autoLogin( $election ) { |
61 | 96 | 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' ); |
78 | 99 | } |
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 ); |
80 | 108 | } |
| 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 | + } |
81 | 143 | } |
82 | 144 | |
83 | 145 | class SecurePoll_RemoteMWAuth extends SecurePoll_Auth { |
84 | | - function login( $election ) { |
| 146 | + function requestLogin( $election ) { |
85 | 147 | global $wgRequest; |
86 | 148 | |
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' ); |
94 | 150 | $vars = array(); |
95 | 151 | foreach ( $urlParamNames as $name ) { |
96 | 152 | $value = $wgRequest->getVal( $name ); |
— | — | @@ -97,99 +153,63 @@ |
98 | 154 | wfDebug( __METHOD__ . " Invalid parameter: $name\n" ); |
99 | 155 | return false; |
100 | 156 | } |
| 157 | + $params[$name] = $value; |
101 | 158 | $vars["\$$name"] = $value; |
102 | | - $params[$name] = $value; |
103 | 159 | } |
104 | 160 | |
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 .= '/'; |
112 | 165 | } |
| 166 | + $url .= 'extensions/SecurePoll/auth-api.php?' . |
| 167 | + wfArrayToCGI( array( |
| 168 | + 'token' => $params['token'], |
| 169 | + 'id' => $params['id'] |
| 170 | + ) ); |
113 | 171 | |
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' ); |
132 | 175 | |
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" ); |
139 | 176 | $value = Http::get( $url, 20, $curlParams ); |
140 | 177 | |
141 | 178 | 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' ); |
145 | 180 | } |
146 | 181 | |
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' ); |
153 | 187 | } |
154 | | - if ( !isset( $userinfo['name'] ) ) { |
155 | | - wfDebug( __METHOD__ . ": No username in response\n" ); |
156 | | - return false; |
| 188 | + if ( !$status->isOK() ) { |
| 189 | + return $status; |
157 | 190 | } |
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; |
162 | 198 | } |
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'], ' ', '_' ) ); |
169 | 199 | |
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 ) ); |
184 | 201 | } |
185 | 202 | |
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 ); |
195 | 215 | } |
196 | 216 | } |
Index: trunk/extensions/SecurePoll/includes/Election.php |
— | — | @@ -21,7 +21,7 @@ |
22 | 22 | } |
23 | 23 | |
24 | 24 | function getMessageNames() { |
25 | | - return array( 'title', 'intro' ); |
| 25 | + return array( 'title', 'intro', 'jump-text' ); |
26 | 26 | } |
27 | 27 | |
28 | 28 | function getChildren() { |
— | — | @@ -52,8 +52,38 @@ |
53 | 53 | return $this->ballot; |
54 | 54 | } |
55 | 55 | |
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; |
58 | 88 | } |
59 | 89 | |
60 | 90 | 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 |
1 | 30 | + native |
Index: trunk/extensions/SecurePoll/includes/VotePage.php |
— | — | @@ -19,13 +19,24 @@ |
20 | 20 | } |
21 | 21 | |
22 | 22 | $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 | + } |
27 | 37 | } |
28 | | - $this->initLanguage( $this->user, $this->election ); |
29 | 38 | |
| 39 | + $this->initLanguage( $this->voter, $this->election ); |
| 40 | + |
30 | 41 | $wgOut->setPageTitle( $this->election->getMessage( 'title' ) ); |
31 | 42 | |
32 | 43 | if ( !$this->election->isStarted() ) { |
— | — | @@ -36,20 +47,19 @@ |
37 | 48 | return; |
38 | 49 | } |
39 | 50 | |
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; |
43 | 55 | } |
44 | 56 | |
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() ); |
50 | 60 | } |
51 | 61 | |
52 | 62 | // Show change notice |
53 | | - if ( $this->election->hasVoted( $this->user ) && !$this->election->allowChange() ) { |
| 63 | + if ( $this->election->hasVoted( $this->voter ) && !$this->election->allowChange() ) { |
54 | 64 | $wgOut->addWikiMsg( 'securepoll-change-disallowed' ); |
55 | 65 | return; |
56 | 66 | } |
— | — | @@ -70,7 +80,7 @@ |
71 | 81 | global $wgOut; |
72 | 82 | |
73 | 83 | // Show introduction |
74 | | - if ( $this->election->hasVoted( $this->user ) && $this->election->allowChange() ) { |
| 84 | + if ( $this->election->hasVoted( $this->voter ) && $this->election->allowChange() ) { |
75 | 85 | $wgOut->addWikiMsg( 'securepoll-change-allowed' ); |
76 | 86 | } |
77 | 87 | $wgOut->addWikiText( $this->election->getMessage( 'intro' ) ); |
— | — | @@ -127,7 +137,7 @@ |
128 | 138 | array( 'vote_current' => 0 ), # SET |
129 | 139 | array( # WHERE |
130 | 140 | 'vote_election' => $this->election->getId(), |
131 | | - 'vote_user' => $this->user->getId(), |
| 141 | + 'vote_user' => $this->voter->getId(), |
132 | 142 | ), |
133 | 143 | __METHOD__ |
134 | 144 | ); |
— | — | @@ -145,9 +155,9 @@ |
146 | 156 | array( |
147 | 157 | 'vote_id' => $voteId, |
148 | 158 | '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(), |
152 | 162 | 'vote_record' => $encrypted, |
153 | 163 | 'vote_ip' => IP::toHex( wfGetIP() ), |
154 | 164 | 'vote_xff' => $xff, |
— | — | @@ -181,4 +191,25 @@ |
182 | 192 | global $wgOut; |
183 | 193 | $wgOut->addWikiMsg( 'securepoll_invalidentered' ); |
184 | 194 | } |
| 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 | + } |
185 | 216 | } |
Index: trunk/extensions/SecurePoll/includes/Page.php |
— | — | @@ -8,7 +8,7 @@ |
9 | 9 | } |
10 | 10 | |
11 | 11 | function initLanguage( $user, $election ) { |
12 | | - if ( $user instanceof SecurePoll_User ) { |
| 12 | + if ( $user instanceof SecurePoll_Voter ) { |
13 | 13 | $userLang = $user->getLanguage(); |
14 | 14 | } else { |
15 | 15 | $userLang = $user->getOption( 'language' ); |
Index: trunk/extensions/SecurePoll/includes/Ballot.php |
— | — | @@ -78,6 +78,9 @@ |
79 | 79 | } |
80 | 80 | } |
81 | 81 | |
| 82 | +/** |
| 83 | + * TODO: this is code copied directly from BoardVote, it needs to be ported. |
| 84 | + */ |
82 | 85 | class SecurePoll_PreferentialBallot extends SecurePoll_Ballot { |
83 | 86 | function getTallyTypes() { |
84 | 87 | return array( 'plurality', 'condorcet' ); |
— | — | @@ -132,3 +135,4 @@ |
133 | 136 | function getForm() { } |
134 | 137 | function submitForm() { } |
135 | 138 | } |
| 139 | + |
Index: trunk/extensions/SecurePoll/includes/DetailsPage.php |
— | — | @@ -42,7 +42,7 @@ |
43 | 43 | $this->detailEntry( 'securepoll-header-user-name', $row->voter_name ) . |
44 | 44 | $this->detailEntry( 'securepoll-header-user-type', $row->voter_type ) . |
45 | 45 | $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 ) . |
47 | 47 | $this->detailEntry( 'securepoll-header-ip', IP::formatHex( $row->vote_ip ) ) . |
48 | 48 | $this->detailEntry( 'securepoll-header-xff', $row->vote_xff ) . |
49 | 49 | $this->detailEntry( 'securepoll-header-ua', $row->vote_ua ) . |
— | — | @@ -51,7 +51,7 @@ |
52 | 52 | ); |
53 | 53 | $wgOut->addHTML( '<h2>' . wfMsgHTML( 'securepoll-voter-properties' ) . "</h2>\n" ); |
54 | 54 | $wgOut->addHTML( '<table class="TablePager">' ); |
55 | | - $props = SecurePoll_User::decodeProperties( $row->voter_properties ); |
| 55 | + $props = SecurePoll_Voter::decodeProperties( $row->voter_properties ); |
56 | 56 | foreach ( $props as $name => $value ) { |
57 | 57 | $wgOut->addHTML( |
58 | 58 | '<td class="securepoll-detail-header">' . |
Index: trunk/extensions/SecurePoll/SecurePoll.sql |
— | — | @@ -47,13 +47,15 @@ |
48 | 48 | |
49 | 49 | CREATE TABLE /*_*/securepoll_voters ( |
50 | 50 | voter_id int not null primary key auto_increment, |
| 51 | + voter_election int not null, |
51 | 52 | voter_name varchar(255) binary not null, |
52 | 53 | voter_type varbinary(32) not null, |
53 | 54 | voter_domain varbinary(255) not null, |
54 | | - voter_authority blob, |
| 55 | + voter_url blob, |
55 | 56 | voter_properties blob |
56 | 57 | ); |
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); |
58 | 60 | |
59 | 61 | CREATE TABLE /*_*/securepoll_votes ( |
60 | 62 | vote_id int not null primary key auto_increment, |
— | — | @@ -96,3 +98,13 @@ |
97 | 99 | CREATE INDEX /*i*/spstrike_vote ON /*_*/securepoll_strike |
98 | 100 | (st_vote, st_timestamp); |
99 | 101 | |
| 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 | + |