You are browsing a read-only backup copy of Wikitech. The live site can be found at wikitech.wikimedia.org

Difference between revisions of "SecurePoll"

From Wikitech-static
Jump to navigation Jump to search
imported>Foks
(Undo myself, Reedy fixed it :D)
imported>Foks
 
(10 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{outdated|year=2013}}
[https://phabricator.wikimedia.org/diffusion/ESPO/ SecurePoll] is a voting system with per-election configuration stored in the database. We use it for Board elections.
[https://phabricator.wikimedia.org/diffusion/ESPO/ SecurePoll] is a voting system with per-election configuration stored in the database. We use it for Board elections.
==How to send a message dump from Meta==
 
On Wikimedia run (use tin):
== About XML files ==
mwscript  extensions/SecurePoll/cli/wm-scripts/dumpMetaTranslations.php --wiki=metawiki > some-file.xml
 
== How to update the jump text on all Wikimedia wikis==
Prior to 2014, there was no UI for creating or editing election configuration. However, there were CLI scripts to import and export the configuration as XML. Elections were created by manually writing XML files and then importing them. That's why cli/dump.php and cli/import.php are relatively complex, and why various old tools produce or act on XML.
Using an updated version of jumpWiki config run:
 
foreachwiki extensions/SecurePoll/cli/import.php --update-msgs jumpwikiconfig.xml
== WMF-specific CLI scripts ==
== How to create an Arbcom election on enwiki ==
 
The 2009 Arbcom election had voter qualification requirements of the form "X main-namespace edits before date Y". A script to calculate a list of qualified voters with criteria of this form is at SecurePoll/cli/wm-scripts/makeArbcomList.php on NFS. If this form of qualification is used again, run something like:
A lot of scripts were written under extreme time pressure to do a very specific job per the requirements of a given election. Some were archived under in a year-specific directory under cli/wm-scripts, and then copied forward for each new election with minor modifications. Some scripts were apparently lost.
mwscript extensions/SecurePoll/cli/makeArbcomList.php --wiki=enwiki --before=<date> --edits=<edits> arbcom-2010
 
where "arbcom-2010" is the list name, which should be different for each election.  
; dumpMetaTranslations.php : SecurePoll has its own translation UI. In the 2013 Board vote this was not used, instead messages were edited as pages on meta. This script generated compiled the translations on meta and generated XML for use with <tt>import.php --update-msgs</tt>.  
The next task is to author the XML file. The XML for the 2009 election is at SecurePoll/cli/wm-setup/real-arbcom-2009.xml. Copy it to your local computer and open it in a text editor.
; doSpam.php : Collects voter names and email addresses, and outputs them as a tab-delimited text file. Used in 2013 and 2015.
Due to the nasty unfinished nature of the software, it's necessary to hard-code the entity IDs in the XML file. An entity is an election, question or option. To find out the highest allocated ID:
; buildSpamTranslations.php : Extracts bulk email translations from meta and dumps them as a series of text files.
sql enwiki
; sendEmails.php : Actually sends emails using files generated by doSpam.php and buildSpamTranslations.php.
select max(en_id) from securepoll_entity;
; dumpGlobalVoterList.php: Similar to doSpam.php, probably used in 2009 and 2011.  
Round up to the nearest 10 and make that number the election ID. The election ID goes in the &lt;id> element as a child of the &lt;election> element.  
; populateEditCount.php : This script, copied forward with minor variations from 2013 to 2021, counts local edits selected according to various criteria and stores them in a table on the local wiki: bv2013_edits, bv2015_edits, etc.
SecurePoll elections can have multiple questions. For Arbcom elections there is only one question. It should have an ID which is the election ID plus one. The question element is a child of the election element.
; voterList.php: The 2013 version was run on each wiki to generate a local voter list. The 2015-2017 version is run once on a random wiki, and populates centralauth.securepoll_lists with a global voter list by adding together edit counts across all wikis.
Start with the XML file from the previous year. Update the &lt;title>, &lt;startDate>, &lt;endDate> and &lt;id> elements.  
; makeGlobalVoterList.php: A generic version of voterList.php introduced in 2021.
Update the admin list. Election administrators should generally have CheckUser access, or be otherwise identified to the foundation under the privacy policy, since a SecurePoll administrator has access to user IP addresses for the purposes of fraud detection.
; makeArbcomList.php: Supposedly a script of this name was used for the 2009 ArbCom election.
Update the need-list property to be the same as the list name you used in makeArbcomList.php.
 
Messages (in message elements) are editable after import by election administrators, so it's only necessary to put a sensible illustrative default in each message. Update the election title message.  
Delete the previous year's list of &lt;option> elements as children of the &lt;question> element, and create a new list. Each option is a candidate. They should have a message called "text" which is their name, and an &lt;id> element with sequential IDs.
Once the XML file is written, send it to the election administrators for review. You can test it locally by importing it into a local MediaWiki instance. After it is reviewed, copy it into the live wm-setup directory, and import it into the database with:
mwscript extensions/SecurePoll/cli/import.php --wiki=enwiki wm-setup/arbcom-[year].xml
The election will start automatically at the specified start date. After the end of the election, a web-based tallying interface will be enabled, with access for administrators. This web-based interface may time out if there were a lot of votes. In this case, do the tally from the command line, using:
mwscript extensions/SecurePoll/cli/tally.php --wiki=enwiki --name &lt;election-name>
where the election name is the contents of the &lt;title> element in the XML file.
== Session transfers ==
SecurePoll supports having an election stubbed out on the wiki that the voters actually come from, but hosted somewhere else (loginwiki in this case).
On the stubbed out wiki, you use these configuration lines to activate the jump:
<source lang="xml">
<auth>local</auth>
<property name="jump-url">https://vote.wikimedia.org/wiki/Special:SecurePoll</property>
<property name="jump-id">200</property>
</source>
And on the remote wiki which is actually hosting the election, you use these lines:
<source lang="xml">
<auth>remote-mw</auth>
<property name="remote-mw-script-path">https://vote.wikimedia.org/w</property>
</source>
You need to ensure that 'site' and 'lang' are passed as parameters by the jump form. The following code is already in place in CommonSettings.php:
<source lang="php">
$wgHooks['SecurePoll_JumpUrl'][] = 'wmfSecurePollJumpUrl';
function wmfSecurePollJumpUrl( $page, &$url ) {
    global $site, $lang;
   
    $url = wfAppendQuery( $url, array( 'site' => $site, 'lang' => $lang ) );
    return true;
}
</source>
Note that the users do not need an account on the remote wiki!
The source wiki (with the jump election) sends the user to the remote wiki with some POST parameters — the site and language of the source wiki, the user's user ID, and an opaque token based on the user's login token. The remote wiki does a callback to <tt>auth-api.php</tt> to get some user parameters, and then compares them with the eligibility requirements in the XML file.
These parameters are the same as for local elections (generated by the same method in Auth.php).
== How to run a board election ==
== How to run a board election ==
This is a collation of Andrew's experiences in running the election with two days' notice in 2011.
 
=== Voter eligibility ===
=== Voter eligibility ===
Since voter eligibility requirements are generally more complicated than is supported in vote XML dumps, we use "voter lists", stored in the <tt>securepoll_list</tt> table.
 
Use the scripts in <tt>/a/common/php/extensions/SecurePoll/cli/wm-scripts</tt> to generate a voter list. Like any long-running batch job, you should do it in a screen on <tt>hume</tt>. For the "X edits over a short period, Y edits before date Z, across wikis" type of eligibility requirement, you can do the following:
It is necessary to run scripts to pre-compute eligibility lists. The scripts take about 2 days to run.
* Run <tt>populateBv20XXEditCount.php</tt> on all wikis to populate the edit count table in each database. You can expect it to take about three days. Copy one from previous years — don't forget to add the tables to all databases with a copy-modify of bv_20XX.sql.
 
* Make sure the SecurePoll tables exist on all wikis with CentralAuth. I used this shell one-liner: <tt>$ for wiki in `</a/common/all.dblist`; do sql $wiki -e "show tables like 'securepoll_%'" | grep                securepoll_lists | wc -l | xargs echo $wiki | egrep '\b0' >>~/wikis-missing-securepoll; done</tt>
The 2021 Board election voter eligibility rule for editors is:
* When <tt>populateBv20XXEditCount.php</tt> has finished, run <tt>bv20XXVoterList.php</tt> across all wikis. This script will take about one day, and will create the voter list. Don't forget to change the name of the list that you're populating!
 
These scripts only handle the edit-count criteria. The session transfer parameters and the eligibility criteria in the XML file deal with blocked users, users blocked on multiple projects and bots. If somebody wants to make you add a new criterion, you have two options:
<blockquote>
* Add it as a condition to the voter list generation scripts. This is suitable for criteria that won't change between when you run the script and when the election is under way, such as edit counts as of certain dates.
You may vote from any one registered account you own on a Wikimedia wiki. You may only vote once, regardless of how many accounts you own. To qualify, this one account must:
* Add it as an eligibility requirement to the election XML file. You may need to add parameters to the user information in <tt>Auth.php</tt>. This is suitable for criteria that need to be checked when a user tries to vote, such as blocks and group membership.
 
==== Exceptions ====
* blocked in no more than one project;
Exceptions that come from fixed lists, such as staff exceptions, should be put into the list manually on the appropriate wiki.
* and not be a bot;
For example, to allow Tim Starling to vote on mediawiki.org:
* and have made at least 300 edits before 5 July 2021 across Wikimedia wikis;
* and have made at least 20 edits between 5 January 2021 and 5 July 2021.
</blockquote>
 
To implement the global block count requirement:
 
* Go to the voter eligibility subpage, check "Must not be blocked on too many attached wikis", enter 2 for "Central block threshold". This sets the <tt>central-block-threshold</tt> property to 2.
* Uncheck "must not be sitewide blocked" -- this has been checked in previous years but seems to conflict with the block count requirement
 
The "not a bot" criterion is the <tt>not-bot</tt> property, which can be changed in the UI by checking "Must not be flagged as a bot". But note that this only filters by bot permissions on the jump wiki.
 
The edit count criteria don't have a UI. There is [[phab:T288183]] to track that. For now:
 
* Copy and adapt [[gerrit:plugins/gitiles/mediawiki/extensions/SecurePoll/+/refs/heads/master/cli/wm-scripts/bv2021/populateEditCount.php|bv2021/populateEditCount.php]]
* Create the bvYYYY_edits table (where YYYY is the year) on all wikis. Use [[gerrit:plugins/gitiles/mediawiki/extensions/SecurePoll/+/refs/heads/master/cli/wm-scripts/bv2021/bv2021_tables.sql|bv2021_tables.sql]] as a template. For example:
 
<pre>
for db in $(expanddblist all); do
    echo 'CREATE TABLE `bv2021_edits` (
  `bv_user` int(11) NOT NULL,
  `bv_long_edits` int(11) NOT NULL,
  `bv_short_edits` int(11) NOT NULL,
  PRIMARY KEY (`bv_user`)
)' | mwscript sql.php --wiki=$db
done
</pre>
 
or
 
<pre>foreachwiki sql.php /srv/mediawiki-staging/php-1.38.0-wmf.1/extensions/SecurePoll/cli/wm-scripts/bv2021/bv2021_tables.sql</pre>
 
* Run the new populateEditCount.php on all wikis. This takes about 2 days if you use one thread per section, i.e. <tt>foreachwikiindblist $section .../populateEditCount.php</tt>. It is recommended to do this in a [[screen]].
<pre>
section=s1
foreachwikiindblist $section extensions/SecurePoll/cli/wm-scripts/bv2021/populateEditCount.php
</pre>
* Run makeGlobalVoterList.php on a random wiki. (This will take around 10 hours for an election this size, so also use a screen for this.) E.g.:
<pre>
<pre>
$ sql enwiki
mwscript extensions/SecurePoll/cli/wm-scripts/makeGlobalVoterList.php \
mysql> INSERT INTO securepoll_lists (li_name,li_member) SELECT 'board-vote-2011', user_id FROM user WHERE user_name='Tim Starling';
    --wiki=mediawikiwiki \
    --edit-count-table=bv2021_edits \
    --list-name=board-vote-2021 \
    --short-min-edits=20 \
    --long-min-edits=300
</pre>
</pre>
Developer exceptions ARE DONE HOW (not code review anymore)?
 
* After election creation, manually insert into the database a property with name <tt>need-central-list</tt> and the value being the name of the list given to makeGlobalVoterList.php.
 
  $ sql votewiki --write
  wikiadmin@10.192.0.120(votewiki)> insert into securepoll_properties (pr_entity,pr_key,pr_value) values (1079,'need-central-list', 'board-vote-2021');
 
The 2021 rules require developers to automatically be granted access if they:
 
<blockquote>
* are Wikimedia server administrators with shell access
* or have made at least one merged commit to any Wikimedia repos on Gerrit, between 5 January 2021 and 5 July 2021.
</blockquote>
 
It is unclear how this can be implemented. The wording was introduced in June 2021 and was discussed at [[phab:T281977]].
 
==== Exceptions ====
 
Exceptions that come from fixed lists, such as staff exceptions, should be put into a flat file containing usernames only, with one name per line. Then on the maintenance server:
 
  mwscript extensions/SecurePoll/cli/wm-scripts/importGlobalVoterList.php --wiki=metawiki --list-name board-vote-YYYY special-users.txt | tee special-users.log
 
where YYYY is the year.
 
This script can be run multiple times to add different kinds of users.
 
=== Creating the election ===
 
TO DO: how do you create a Board election with the SecurePoll UI?
 
=== Translations ===
=== Translations ===
A few gotchas, pitfalls and comments:
 
A few gotchas, pitfalls and comments (from 2011-2013):
 
* Don't let your translators translate the section titles! It breaks the script that generates the XML file.
* Don't let your translators translate the section titles! It breaks the script that generates the XML file.
* When all the candidates have been submitted, get the translators to translate the "Candidate text" section. If the candidate names don't need to be transliterated (and can be displayed as in English), get them to remove that section entirely. If they do need to be transliterated (for languages such as Arabic), make sure that the candidates are in the right order.
* When all the candidates have been submitted, get the translators to translate the "Candidate text" section. If the candidate names don't need to be transliterated (and can be displayed as in English), get them to remove that section entirely. If they do need to be transliterated (for languages such as Arabic), make sure that the candidates are in the right order.
* Magic words do not (currently) work. They need removing
* Magic words do not (currently) work. They need removing
=== Generating the XML files ===
 
Generally you will be creating two XML files. One is to be put on the vote.wikimedia.org wiki, and one is to be imported on all Wikimedia wikis to provide the stubbed interface to the session transfer (see [[#Session transfers|the section about session transfers]]).
The way I (Andrew) created these files is:
* Run <tt>mwscript extensions/SecurePoll/cli/wm-scripts/bv2013/dumpMetaTranslations.php --wiki=metawiki</tt>, and saved the output to a file. Don't forget to modify <tt>dumpMetaTranslations.php</tt> to match this year's election! Of particular note:
** You need to change the base ID. Figure out which base ID to use with this one-liner: <tt>for wiki in `</a/common/all.dblist`; do sql $wiki -e"select max(en_id) from securepoll_entity;" >>/tmp/max-entity-number; done; sort /tmp/max-entity-number | uniq</tt>. Get the highest number from the output, and round it up to the nearest ten or so.
** You need to change the name of the election.
** You need to adjust the list of election administrators.
** You need to adjust the list of languages depending on which ones you want to offer the election in.
** You need to adjust the start and end dates
** You need to change the need-list parameter to accept the list you created (see [[#Voter eligibility]]).
** You may need to tweak the other eligibility criteria.
* I created one variant, labelled remote, which had the <tt>auth</tt> parameter set to <tt>remote-mw</tt>. This one went to SPI. Currently, <tt>dumpMetaTranslations.php</tt> generates this file.
* I created another variant, labelled jump, which had the <tt>auth</tt> parameter set to <tt>local</tt>, and the <tt>jump-url</tt> and <tt>jump-id</tt> parameters set. The <tt>jump-url</tt> parameter is the URL of <tt>Special:SecurePoll</tt> on the remote wiki, the <tt>jump-id</tt> parameter will generally be the same as the election's base ID, and refers to the election ID in the remote variant of the XML configuration file.
=== Do a test run ===
=== Do a test run ===
A few days before the election, you should run a test election for a few days, just to make sure that everything (particularly eligibility and the session transfer) is working properly. To make sure that there is absolutely no risk of confusion, change every message in this election to "THIS IS A TEST ELECTION, DO NOT VOTE", or similar.
A few days before the election, you should run a test election for a few days, just to make sure that everything (particularly eligibility and the session transfer) is working properly. To make sure that there is absolutely no risk of confusion, change every message in this election to "THIS IS A TEST ELECTION, DO NOT VOTE", or similar.
Make sure that this election's base ID is different from the real election's base ID.
 
=== Email spam ===
=== Email spam ===
There are scripts that can handle the mass-mailing are in the SecurePoll git repository.  It is probably easiest to do a copy-and-modify for the newest elections. <tt>buildSpamTranslations.php</tt> pulls translations out of the database on meta, <tt>doSpam.php</tt> generates a list of emails to send, and <tt>sendMails.php</tt> actually sends the email. Don't forget to change:
 
* The email subject in <tt>sendMails.php</tt>.
Create a translatable base page for translations following the format at [[m:Wikimedia Foundation elections/2021/2021-08-27/Second board voter e-mail]], specifically:
* The base page for translations (and the list of suitable languages) in <tt>buildSpamTranslations.php</tt>.
 
** For 2013, it is [[MetaWikipedia:Wikimedia Foundation elections 2013/Voter e-mail]]. Note, there is some email-unfriendly wiki syntax.
* Translatable sender name on a line starting with the literal text "From:"
* The voter list to draw from in <tt>doSpam.php</tt>.
* Subject in level 2 heading
Run a trial run by manually inputting your own details into <tt>sendMails.php</tt> and see what gets spat out. I'd recommend redirecting output for all of the scripts so you have a paper trail of exactly what happened in each step.
* Wikitext for HTML email can contain external and interwiki links, but internal links won't work without a modification to the script. Anything depending on separate CSS stylesheets will not work — use style attributes.
Translations are pulled from a base page plus a language code. You might need to update <tt>buildSpamTranslations.php</tt> to strip more boilerplate from the pages.
* Plain text version in &lt;pre>
 
Create the page well in advance. The top 10 user preference languages by number of qualified users in 2021 were: en, de, fr, ru, es, ja, it, pl, nl.
 
Find all global users who have the bot right on at least one wiki, so that they can be excluded from the list:
 
year=2021
foreachwikiindblist securepollglobal extensions/SecurePoll/cli/findUsersWithRight.php --central-list=bots-$year --right=bot | tee findUsersWithRight.log
 
Create a mailing list file for each wiki:
 
year=2021
section=s1
for wiki in $(expanddblist $section); do
    echo $wiki
    mwscript extensions/SecurePoll/cli/wm-scripts/makeMailingList.php --wiki=$wiki --election='Wikimedia Foundation Board Elections 2021' --election-wiki=votewiki --exclude-central-list=bots-$year > ml-$wiki
done
 
And similarly for each section s2-s8.
 
Then deduplicate the mailing list files:
 
  mwscript extensions/SecurePoll/cli/wm-scripts/deduplicateMailingList.php --wiki=metawiki ml-* > dedup-all
 
Test the mailout for all translated languages, e.g.
 
for lang in en de fr ru es ja it pl nl; do
    printf "enwiki\tWikipedia\tTim Starling\ttstarling@wikimedia.org\t$lang\t0\n" >> lang-test
done
mwscript extensions/SecurePoll/cli/wm-scripts/sendMail.php --wiki=metawiki --page='Wikimedia Foundation elections/2021/2021-08-20/Board voter e-mail' --sender='board-elections@lists.wikimedia.org' lang-test
 
Do the actual mailout:
 
mwscript extensions/SecurePoll/cli/wm-scripts/sendMail.php --wiki=metawiki --page='Wikimedia Foundation elections/2021/2021-08-20/Board voter e-mail' --sender='board-elections@lists.wikimedia.org' dedup-all | tee -a sendMail.log
 
The script reports the line number from the input file after sending each email, so if you need to restart the script, you can make a new mailing list removing the number of lines corresponding to the last reported line number.
 
Expect autoreply backscatter of about 100 emails.
 
You should be prepared to deal with the following types of complaints:
You should be prepared to deal with the following types of complaints:
* Unflagged bots being invited to vote. Often the bot will not be flagged on all wikis that it is active — we need better handling for this, but you can find out which wiki a bot is being emailed from by grepping the output of <tt>doSpam.php</tt>.
 
* Unflagged bots being invited to vote.
* Multiple accounts — usually people are understanding of the fact that the list is automatically generated, but some extra deduplication by email address would be helpful.
* Multiple accounts — usually people are understanding of the fact that the list is automatically generated, but some extra deduplication by email address would be helpful.
* Gaffes in translation and execution (in the case of 2011, the subject line wasn't updated). The test run will fix most of these, but you should also check that the translations aren't trying to use magic words or anything fancy (unless you add support to the mailout script). Also check on the formatting of the translations themselves, translators have a habit of changing things to make them clearer and breaking the scripts.
* Gaffes in translation and execution (in the case of 2011, the subject line wasn't updated). The test run will fix most of these, but you should also check that the translations aren't trying to use magic words or anything fancy (unless you add support to the mailout script). Also check on the formatting of the translations themselves, translators have a habit of changing things to make them clearer and breaking the scripts.
A few lessons learned from 2011 that would be good to apply in future
A few lessons learned from 2011 that would be good to apply in future
* We tended to get a lot of unflagged global bots receiving messages. Perhaps accounts bot-flagged on more than one wiki should be excluded altogether.
* Some users with multiple accounts got multiple emails — deduplication by email address is an option worth considering.
* Some users with multiple accounts got multiple emails — deduplication by email address is an option worth considering.
* We should send out the email reasonably early in the process — at most halfway through.
* We should send out the email reasonably early in the process — at most halfway through.
* Bidirectional text tended to appear garbled in text-only emails. This was exacerbated by nonstandard formatting of the translation pages.
* Translators need to be briefed about format — in particular, magic words don't work in the plaintext version, and <nowiki>{{GENDER:}}</nowiki> doesn't work in either version.
* Translators need to be briefed about format — in particular, magic words don't work, and we need to try to keep boilerplate to a minimum so that we can extract the text reasonably seamlessly.
 
* If we can get a machine readable list of accounts that have already voted, that might be useful for eliminating them from the mailout.
See also [[MetaWikipedia:Image filter referendum/Email/False positives]].
See also [[MetaWikipedia:Image filter referendum/Email/False positives]].
=== Duplicate removal ===
=== Duplicate removal ===
SecurePoll takes the approach of trying to make ballot stuffing detectable, and allowing it to be rectified if it is detected, rather than discouraging it with error messages. This reduces the risk that a malicious user will refine their methods until they are able to evade all technical restrictions. The tradeoff is that it creates a lot of work for election administrators.
SecurePoll takes the approach of trying to make ballot stuffing detectable, and allowing it to be rectified if it is detected, rather than discouraging it with error messages. This reduces the risk that a malicious user will refine their methods until they are able to evade all technical restrictions. The tradeoff is that it creates a lot of work for election administrators.
(However, allowing the same CentralAuth user to vote twice from different wikis is technical debt arising from the introduction of CentralAuth, it is not a deliberate design decision.)
Duplicate removal is a large task, and as many trusted election administrators as possible should be encouraged to help with it.
Duplicate removal is a large task, and as many trusted election administrators as possible should be encouraged to help with it.
SecurePoll allows people to alter their votes by voting more than once. But if a person votes more than once with different accounts, or with the same username on different wikis, then those votes are counted twice. Such votes need to be manually removed.  
SecurePoll allows people to alter their votes by voting more than once. But if a person votes more than once with different accounts, or with the same username on different wikis, then those votes are counted twice. Such votes need to be manually removed.  
Election documentation should make it clear that each person is only entitled to vote once, and that having multiple qualified accounts is not a license to vote more than once. Policies should be put in place to strongly discourage deliberate ballot-stuffing.
Election documentation should make it clear that each person is only entitled to vote once, and that having multiple qualified accounts is not a license to vote more than once. Policies should be put in place to strongly discourage deliberate ballot-stuffing.
For people who are named as election administrators on vote.wikimedia.org, SecurePoll extends the "list" page to provide a duplicate vote removal interface. As a first pass, sort it by username, and then scroll through the results looking for multiple votes with the same username, ignoring greyed-out entries. Then do the same sorting by IP address.  
For people who are named as election administrators on vote.wikimedia.org, SecurePoll extends the "list" page to provide a duplicate vote removal interface. As a first pass, sort it by username, and then scroll through the results looking for multiple votes with the same username, ignoring greyed-out entries. Then do the same sorting by IP address.  
Votes with a "dup" marker should be investigated. The details page gives the username of the suspected sockpuppet, based on cookie evidence.
Votes with a "dup" marker should be investigated. The details page gives the username of the suspected sockpuppet, based on cookie evidence.
Any votes deemed to be duplicates should be struck out, by clicking the "strike" link. Voters whose votes are struck should be contacted by email, if they have set an email address in their preferences.  
Any votes deemed to be duplicates should be struck out, by clicking the "strike" link. Voters whose votes are struck should be contacted by email, if they have set an email address in their preferences.
 
=== Encryption ===
=== Encryption ===
'''This section has not been updated for the new situation after 2013'''
Two separate keys need to be generated for each election: an encryption key and a signing key.
Two separate keys need to be generated for each election: an encryption key and a signing key.
In the past, election committee members have been put under pressure to release running tallies, while voting is still in progress. On one occasion, such a tally was privately given to the interested party, and the results were used to influence voting in a last-ditch campaign. Having an outside party hold the encryption key exclusively until after the election is complete avoids this potential quandary.  
In the past, election committee members have been put under pressure to release running tallies, while voting is still in progress. On one occasion, such a tally was privately given to the interested party, and the results were used to influence voting in a last-ditch campaign. Having an outside party hold the encryption key exclusively until after the election is complete avoids this potential quandary.  
After voting is complete, the encryption key should be given to several trusted election committee members. To prevent vote-buying, the encryption key should never be publicly released. If the encryption key is released, then a voter can use their encrypted record to prove that they voted in a particular way.
After voting is complete, the encryption key should be given to several trusted election committee members. To prevent vote-buying, the encryption key should never be publicly released. If the encryption key is released, then a voter can use their encrypted record to prove that they voted in a particular way.
Instead, voters wishing to establish the integrity of their encrypted election records should do so by private communication with a private key holder. The election record can be decrypted and compared with who the voter claims they voted for.
Instead, voters wishing to establish the integrity of their encrypted election records should do so by private communication with a private key holder. The election record can be decrypted and compared with who the voter claims they voted for.
The signing key allows voters to prove that they have a valid encrypted record. If a voter has a signed voting record which is not present in the database, then this is clear evidence that the server has been compromised.
The signing key allows voters to prove that they have a valid encrypted record. If a voter has a signed voting record which is not present in the database, then this is clear evidence that the server has been compromised.
To generate each key:
To generate each key:
  gpg --gen-key
  gpg --gen-key
Then follow the prompts. Use usernames like "Wikimedia Board Election 2011 signing key" and "Wikimedia Board Election 2011 encryption key" to make it easier to know which is which. Then export each key:
Then follow the prompts. Use usernames like "Wikimedia Board Election 2011 signing key" and "Wikimedia Board Election 2011 encryption key" to make it easier to know which is which. Then export each key:
  gpg --armor --export-secret-keys 'Wikimedia Board Election 2011 signing key'
  gpg --armor --export-secret-keys 'Wikimedia Board Election 2011 signing key'
  gpg --armor --export 'Wikimedia Board Election 2011 encryption key'
  gpg --armor --export 'Wikimedia Board Election 2011 encryption key'
Key generation should be done by the 3rd party keeping the decryption key.
Key generation should be done by the 3rd party keeping the decryption key.
<source lang="xml">
 
<syntaxhighlight lang="xml">
<property name="gpg-sign-key">...exported secret key...</property>
<property name="gpg-sign-key">...exported secret key...</property>
<property name="gpg-encrypt-key">...exported public key...</property>
<property name="gpg-encrypt-key">...exported public key...</property>
</source>
</syntaxhighlight>
 
=== Tallying ===
=== Tallying ===
Board elections generally have too many records to allow tallying via the web interface. Instead, use the command-line interface.  
 
On the vote wiki side, export the election XML including votes:
Tallying can be done with the UI, however this puts the encryption key in the database. Tallying with cli/tally.php does not have this problem, and is more reliable.
mwscript extensions/SecurePoll/cli/dump.php --wiki=WIKI --votes <election name>
 
This XML file can then be distributed to the relevant election committee members, along with the private key for decryption. The private key can be obtained by the 3rd party holder using the key name from earlier:
== Session transfers ==
gpg --armor --export-secret-keys 'Wikimedia Board Election 2011 encryption key'
 
The dump XML should be edited to contain the private key for decryption:
SecurePoll supports having an election stubbed out on the wiki that the voters actually come from, but hosted somewhere else (votewiki in this case).
<source lang="xml">
 
<property name="gpg-decrypt-key">...exported secret key...</property>
Since 2014 these stub elections are created automatically by the election creation UI when you select the "all wikis" option.
</source>
 
Then the file can be tallied using tally.php. The HTML output format is more suitable for pasting into a wiki page.
Prior to 2014, the stub elections were set up by generating an XML configuration file and importing it into all wikis.
mwscript extensions/SecurePoll/cli/tally.php --wiki=WIKI --html board-election-2011.xml
 
The Schulze method tallier in SecurePoll has an incomplete implementation of Markus Schulze's tallying method, it does not include tie-breaking. This was due to constraints on software development time. In the event of the Schulze tallier declaring a tie, the votes should be further processed according to the method in Markus Schulze's paper in order to produce a result.
Users do not actually need an account on the wiki where the vote is actually hosted. This was used prior to 2013 to host the vote on servers not controlled by WMF. Even though we now self-host our elections, votewiki does not have CentralAuth enabled and so users will typically not have a user account when they vote.
== Creating vote dump ==
 
The election XML dump (from Tallying) can also be used to create an anonymized dump of votes. The script 'convertVotes.php' starts off the process by dumping the responses (along with the questions so that you know how to interpret) to a new file:
Session transfer works by having the jump wiki send a token to votewiki. Votewiki authenticates the token by doing an HTTP request to auth-api.php. The auth-api.php request returns user parameters which are saved by votewiki. This mechanism predates the action API and there is [https://phabricator.wikimedia.org/T204193 a task] to convert this mechanism to finally use the action API which may soon be implemented.
mwscript extensions/SecurePoll/cli/tally.php --wiki= electionvotedump.xml > convertedvotedump.txt
 
A config hack adds "lang" and "site" parameters to the jump URL, which the remote site uses to reconstruct a URL back to the jump wiki.
Becaues the votes are initially dumped in chonological order each question's votes should be shuffled. This can be done using shuf/gshuf or some other shuffling mechanism, makes sure to do so in a different file or in such a way as to not shuffle the part of the dump showing the question options.
 
== Creating other kinds of elections ==
== Timeline ==
For documentation of the election properties which are set in the XML file, see the comment at the top of includes/entities/Election.php, and the comments on the ballot-specific module (e.g. includes/ballots/RadioRangeBallot.php).
 
; 2004: The first Board election. Tim, then a volunteer, quickly wrote the BoardVote extension to run this election. It was one of the first MediaWiki extensions. Many of SecurePoll's quirks were already present, such as GPG-based encrypted vote receipts. The voting/tallying method was approval voting.
; 2005-2008: The BoardVote extension was edited for each election, changing table names etc. to make it continue to work for another year.
; 2007: The first election hosted by Software in the Public Interest (SPI). SPI would host the 2007, 2008, 2009 and 2011 elections.
; 2008: The tallying method was changed to the Schulze method (a Condorcet preferential method).
; 2009: Tim wrote the first version of SecurePoll, as a multi-election adaption of the BoardVote code. It was used for the 2009 Board election.
; 2013: Elections returned to being self-hosted. Encryption of voting records at rest effectively ended, with private (tallying) keys being stored on the server instead of held by an election administrator and tallied offline.
; 2013: The voting method was changed to a modified range vote formula: support/(support+oppose).  
; 2014: Brad wrote an election creation UI. But the UI continues to be incomplete and was not used for voter qualification in 2015-2021.
; 2021: The voting method was changed to Single Transferable Vote.
 
[[Category:How-To]]
[[Category:How-To]]
[[Category:MediaWiki production]]
[[Category:MediaWiki production]]

Latest revision as of 21:40, 27 September 2021

SecurePoll is a voting system with per-election configuration stored in the database. We use it for Board elections.

About XML files

Prior to 2014, there was no UI for creating or editing election configuration. However, there were CLI scripts to import and export the configuration as XML. Elections were created by manually writing XML files and then importing them. That's why cli/dump.php and cli/import.php are relatively complex, and why various old tools produce or act on XML.

WMF-specific CLI scripts

A lot of scripts were written under extreme time pressure to do a very specific job per the requirements of a given election. Some were archived under in a year-specific directory under cli/wm-scripts, and then copied forward for each new election with minor modifications. Some scripts were apparently lost.

dumpMetaTranslations.php
SecurePoll has its own translation UI. In the 2013 Board vote this was not used, instead messages were edited as pages on meta. This script generated compiled the translations on meta and generated XML for use with import.php --update-msgs.
doSpam.php
Collects voter names and email addresses, and outputs them as a tab-delimited text file. Used in 2013 and 2015.
buildSpamTranslations.php
Extracts bulk email translations from meta and dumps them as a series of text files.
sendEmails.php
Actually sends emails using files generated by doSpam.php and buildSpamTranslations.php.
dumpGlobalVoterList.php
Similar to doSpam.php, probably used in 2009 and 2011.
populateEditCount.php
This script, copied forward with minor variations from 2013 to 2021, counts local edits selected according to various criteria and stores them in a table on the local wiki: bv2013_edits, bv2015_edits, etc.
voterList.php
The 2013 version was run on each wiki to generate a local voter list. The 2015-2017 version is run once on a random wiki, and populates centralauth.securepoll_lists with a global voter list by adding together edit counts across all wikis.
makeGlobalVoterList.php
A generic version of voterList.php introduced in 2021.
makeArbcomList.php
Supposedly a script of this name was used for the 2009 ArbCom election.

How to run a board election

Voter eligibility

It is necessary to run scripts to pre-compute eligibility lists. The scripts take about 2 days to run.

The 2021 Board election voter eligibility rule for editors is:

You may vote from any one registered account you own on a Wikimedia wiki. You may only vote once, regardless of how many accounts you own. To qualify, this one account must:

  • blocked in no more than one project;
  • and not be a bot;
  • and have made at least 300 edits before 5 July 2021 across Wikimedia wikis;
  • and have made at least 20 edits between 5 January 2021 and 5 July 2021.

To implement the global block count requirement:

  • Go to the voter eligibility subpage, check "Must not be blocked on too many attached wikis", enter 2 for "Central block threshold". This sets the central-block-threshold property to 2.
  • Uncheck "must not be sitewide blocked" -- this has been checked in previous years but seems to conflict with the block count requirement

The "not a bot" criterion is the not-bot property, which can be changed in the UI by checking "Must not be flagged as a bot". But note that this only filters by bot permissions on the jump wiki.

The edit count criteria don't have a UI. There is phab:T288183 to track that. For now:

for db in $(expanddblist all); do
    echo 'CREATE TABLE `bv2021_edits` (
  `bv_user` int(11) NOT NULL,
  `bv_long_edits` int(11) NOT NULL,
  `bv_short_edits` int(11) NOT NULL,
  PRIMARY KEY (`bv_user`)
)' | mwscript sql.php --wiki=$db
done

or

foreachwiki sql.php /srv/mediawiki-staging/php-1.38.0-wmf.1/extensions/SecurePoll/cli/wm-scripts/bv2021/bv2021_tables.sql
  • Run the new populateEditCount.php on all wikis. This takes about 2 days if you use one thread per section, i.e. foreachwikiindblist $section .../populateEditCount.php. It is recommended to do this in a screen.
 section=s1
 foreachwikiindblist $section extensions/SecurePoll/cli/wm-scripts/bv2021/populateEditCount.php
  • Run makeGlobalVoterList.php on a random wiki. (This will take around 10 hours for an election this size, so also use a screen for this.) E.g.:
 mwscript extensions/SecurePoll/cli/wm-scripts/makeGlobalVoterList.php \
    --wiki=mediawikiwiki \
    --edit-count-table=bv2021_edits \
    --list-name=board-vote-2021 \
    --short-min-edits=20 \
    --long-min-edits=300
  • After election creation, manually insert into the database a property with name need-central-list and the value being the name of the list given to makeGlobalVoterList.php.
 $ sql votewiki --write
 wikiadmin@10.192.0.120(votewiki)> insert into securepoll_properties (pr_entity,pr_key,pr_value) values (1079,'need-central-list', 'board-vote-2021');

The 2021 rules require developers to automatically be granted access if they:

  • are Wikimedia server administrators with shell access
  • or have made at least one merged commit to any Wikimedia repos on Gerrit, between 5 January 2021 and 5 July 2021.

It is unclear how this can be implemented. The wording was introduced in June 2021 and was discussed at phab:T281977.

Exceptions

Exceptions that come from fixed lists, such as staff exceptions, should be put into a flat file containing usernames only, with one name per line. Then on the maintenance server:

 mwscript extensions/SecurePoll/cli/wm-scripts/importGlobalVoterList.php --wiki=metawiki --list-name board-vote-YYYY special-users.txt | tee special-users.log

where YYYY is the year.

This script can be run multiple times to add different kinds of users.

Creating the election

TO DO: how do you create a Board election with the SecurePoll UI?

Translations

A few gotchas, pitfalls and comments (from 2011-2013):

  • Don't let your translators translate the section titles! It breaks the script that generates the XML file.
  • When all the candidates have been submitted, get the translators to translate the "Candidate text" section. If the candidate names don't need to be transliterated (and can be displayed as in English), get them to remove that section entirely. If they do need to be transliterated (for languages such as Arabic), make sure that the candidates are in the right order.
  • Magic words do not (currently) work. They need removing

Do a test run

A few days before the election, you should run a test election for a few days, just to make sure that everything (particularly eligibility and the session transfer) is working properly. To make sure that there is absolutely no risk of confusion, change every message in this election to "THIS IS A TEST ELECTION, DO NOT VOTE", or similar.

Email spam

Create a translatable base page for translations following the format at m:Wikimedia Foundation elections/2021/2021-08-27/Second board voter e-mail, specifically:

  • Translatable sender name on a line starting with the literal text "From:"
  • Subject in level 2 heading
  • Wikitext for HTML email can contain external and interwiki links, but internal links won't work without a modification to the script. Anything depending on separate CSS stylesheets will not work — use style attributes.
  • Plain text version in <pre>

Create the page well in advance. The top 10 user preference languages by number of qualified users in 2021 were: en, de, fr, ru, es, ja, it, pl, nl.

Find all global users who have the bot right on at least one wiki, so that they can be excluded from the list:

year=2021
foreachwikiindblist securepollglobal extensions/SecurePoll/cli/findUsersWithRight.php --central-list=bots-$year --right=bot | tee findUsersWithRight.log

Create a mailing list file for each wiki:

year=2021
section=s1
for wiki in $(expanddblist $section); do
   echo $wiki
   mwscript extensions/SecurePoll/cli/wm-scripts/makeMailingList.php --wiki=$wiki --election='Wikimedia Foundation Board Elections 2021' --election-wiki=votewiki --exclude-central-list=bots-$year > ml-$wiki
done

And similarly for each section s2-s8.

Then deduplicate the mailing list files:

 mwscript extensions/SecurePoll/cli/wm-scripts/deduplicateMailingList.php --wiki=metawiki ml-* > dedup-all

Test the mailout for all translated languages, e.g.

for lang in en de fr ru es ja it pl nl; do
   printf "enwiki\tWikipedia\tTim Starling\ttstarling@wikimedia.org\t$lang\t0\n" >> lang-test
done
mwscript extensions/SecurePoll/cli/wm-scripts/sendMail.php --wiki=metawiki --page='Wikimedia Foundation elections/2021/2021-08-20/Board voter e-mail' --sender='board-elections@lists.wikimedia.org' lang-test

Do the actual mailout:

mwscript extensions/SecurePoll/cli/wm-scripts/sendMail.php --wiki=metawiki --page='Wikimedia Foundation elections/2021/2021-08-20/Board voter e-mail' --sender='board-elections@lists.wikimedia.org' dedup-all | tee -a sendMail.log

The script reports the line number from the input file after sending each email, so if you need to restart the script, you can make a new mailing list removing the number of lines corresponding to the last reported line number.

Expect autoreply backscatter of about 100 emails.

You should be prepared to deal with the following types of complaints:

  • Unflagged bots being invited to vote.
  • Multiple accounts — usually people are understanding of the fact that the list is automatically generated, but some extra deduplication by email address would be helpful.
  • Gaffes in translation and execution (in the case of 2011, the subject line wasn't updated). The test run will fix most of these, but you should also check that the translations aren't trying to use magic words or anything fancy (unless you add support to the mailout script). Also check on the formatting of the translations themselves, translators have a habit of changing things to make them clearer and breaking the scripts.

A few lessons learned from 2011 that would be good to apply in future

  • Some users with multiple accounts got multiple emails — deduplication by email address is an option worth considering.
  • We should send out the email reasonably early in the process — at most halfway through.
  • Translators need to be briefed about format — in particular, magic words don't work in the plaintext version, and {{GENDER:}} doesn't work in either version.

See also MetaWikipedia:Image filter referendum/Email/False positives.

Duplicate removal

SecurePoll takes the approach of trying to make ballot stuffing detectable, and allowing it to be rectified if it is detected, rather than discouraging it with error messages. This reduces the risk that a malicious user will refine their methods until they are able to evade all technical restrictions. The tradeoff is that it creates a lot of work for election administrators.

(However, allowing the same CentralAuth user to vote twice from different wikis is technical debt arising from the introduction of CentralAuth, it is not a deliberate design decision.)

Duplicate removal is a large task, and as many trusted election administrators as possible should be encouraged to help with it. SecurePoll allows people to alter their votes by voting more than once. But if a person votes more than once with different accounts, or with the same username on different wikis, then those votes are counted twice. Such votes need to be manually removed.

Election documentation should make it clear that each person is only entitled to vote once, and that having multiple qualified accounts is not a license to vote more than once. Policies should be put in place to strongly discourage deliberate ballot-stuffing.

For people who are named as election administrators on vote.wikimedia.org, SecurePoll extends the "list" page to provide a duplicate vote removal interface. As a first pass, sort it by username, and then scroll through the results looking for multiple votes with the same username, ignoring greyed-out entries. Then do the same sorting by IP address.

Votes with a "dup" marker should be investigated. The details page gives the username of the suspected sockpuppet, based on cookie evidence. Any votes deemed to be duplicates should be struck out, by clicking the "strike" link. Voters whose votes are struck should be contacted by email, if they have set an email address in their preferences.

Encryption

This section has not been updated for the new situation after 2013

Two separate keys need to be generated for each election: an encryption key and a signing key.

In the past, election committee members have been put under pressure to release running tallies, while voting is still in progress. On one occasion, such a tally was privately given to the interested party, and the results were used to influence voting in a last-ditch campaign. Having an outside party hold the encryption key exclusively until after the election is complete avoids this potential quandary.

After voting is complete, the encryption key should be given to several trusted election committee members. To prevent vote-buying, the encryption key should never be publicly released. If the encryption key is released, then a voter can use their encrypted record to prove that they voted in a particular way.

Instead, voters wishing to establish the integrity of their encrypted election records should do so by private communication with a private key holder. The election record can be decrypted and compared with who the voter claims they voted for.

The signing key allows voters to prove that they have a valid encrypted record. If a voter has a signed voting record which is not present in the database, then this is clear evidence that the server has been compromised.

To generate each key:

gpg --gen-key

Then follow the prompts. Use usernames like "Wikimedia Board Election 2011 signing key" and "Wikimedia Board Election 2011 encryption key" to make it easier to know which is which. Then export each key:

gpg --armor --export-secret-keys 'Wikimedia Board Election 2011 signing key'
gpg --armor --export 'Wikimedia Board Election 2011 encryption key'

Key generation should be done by the 3rd party keeping the decryption key.

<property name="gpg-sign-key">...exported secret key...</property>
<property name="gpg-encrypt-key">...exported public key...</property>

Tallying

Tallying can be done with the UI, however this puts the encryption key in the database. Tallying with cli/tally.php does not have this problem, and is more reliable.

Session transfers

SecurePoll supports having an election stubbed out on the wiki that the voters actually come from, but hosted somewhere else (votewiki in this case).

Since 2014 these stub elections are created automatically by the election creation UI when you select the "all wikis" option.

Prior to 2014, the stub elections were set up by generating an XML configuration file and importing it into all wikis.

Users do not actually need an account on the wiki where the vote is actually hosted. This was used prior to 2013 to host the vote on servers not controlled by WMF. Even though we now self-host our elections, votewiki does not have CentralAuth enabled and so users will typically not have a user account when they vote.

Session transfer works by having the jump wiki send a token to votewiki. Votewiki authenticates the token by doing an HTTP request to auth-api.php. The auth-api.php request returns user parameters which are saved by votewiki. This mechanism predates the action API and there is a task to convert this mechanism to finally use the action API which may soon be implemented.

A config hack adds "lang" and "site" parameters to the jump URL, which the remote site uses to reconstruct a URL back to the jump wiki.

Timeline

2004
The first Board election. Tim, then a volunteer, quickly wrote the BoardVote extension to run this election. It was one of the first MediaWiki extensions. Many of SecurePoll's quirks were already present, such as GPG-based encrypted vote receipts. The voting/tallying method was approval voting.
2005-2008
The BoardVote extension was edited for each election, changing table names etc. to make it continue to work for another year.
2007
The first election hosted by Software in the Public Interest (SPI). SPI would host the 2007, 2008, 2009 and 2011 elections.
2008
The tallying method was changed to the Schulze method (a Condorcet preferential method).
2009
Tim wrote the first version of SecurePoll, as a multi-election adaption of the BoardVote code. It was used for the 2009 Board election.
2013
Elections returned to being self-hosted. Encryption of voting records at rest effectively ended, with private (tallying) keys being stored on the server instead of held by an election administrator and tallied offline.
2013
The voting method was changed to a modified range vote formula: support/(support+oppose).
2014
Brad wrote an election creation UI. But the UI continues to be incomplete and was not used for voter qualification in 2015-2021.
2021
The voting method was changed to Single Transferable Vote.