Skip to content
Permalink
4d2c1229b5
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
160 lines (134 sloc) 4.32 KB
<?php
use Wikimedia\Timestamp\ConvertibleTimestamp;
define('USER_API_LINK', 'https://api.scratch.mit.edu/users/%s/');
function getAuthenticator() {
global $wgScratchLoginAuthenticator;
switch ($wgScratchLoginAuthenticator) {
case 'cloud': {
return ScratchLogin\Authenticator\CloudVariableAuthenticator::class;
}
default: {
return ScratchLogin\Authenticator\ProjectCommentAuthenticator::class;
}
}
}
function getScratchUserRegisteredAt($username) {
$apiText = file_get_contents(sprintf(
USER_API_LINK, $username
));
//fail loudly if the API call fails
if (!isset($http_response_header)) {
throw new Exception('API call failed');
}
//this shouldn't happen, but since this is a security-sensitive component we need to be ultra-defensive
if (!strstr($http_response_header[0], '200 OK')) {
throw new Exception('User does not exist');
}
$info = json_decode($apiText, true);
$registeredAt = $info['history']['joined'];
return new ConvertibleTimestamp($registeredAt);
}
class ScratchSpecialPage extends SpecialPage {
function execute($par) {
$request = $this->getRequest();
$out = $this->getOutput();
$out->disallowUserJs();
$this->setHeaders();
$this->checkReadOnly();
if ($par == 'reset') {
$this->resetCode( $out, $request );
} else if ($request->wasPosted()) {
$this->onPost( $out, $request );
} else {
$this->showForm( $out, $request );
}
}
// show an error followed by the login form again
function showError($error, $out, $request) {
$out->addHTML(Html::rawElement('p', [ 'class' => 'error' ], $error));
$this->showForm($out, $request);
}
// $instructions: message key giving instructions for this page
// $action: message key for button value
function verifForm($out, $request, $instructions, $action) {
$authenticator = getAuthenticator();
// this all takes place in a form
$out->addHTML(Html::openElement(
'form',
[ 'method' => 'POST' ]
));
$session = $request->getSession();
$out->addHTML($authenticator::getInstructions(
$instructions,
$session,
$this
)->inContentLanguage()->parseAsBlock());
// show the submit button
$out->addHTML(Html::rawElement(
'input',
[
'type' => 'submit',
'id' => 'mw-scratchlogin-form-submit',
'value' => wfMessage($action)->inContentLanguage()->plain()
]
));
//close the form
$out->addHTML(Html::closeElement( 'form' ));
}
function verifSucceeded($out, $request) {
$session = $request->getSession();
$authenticator = getAuthenticator();
$username = $authenticator::getAssociatedUsername($session, $this);
if ($username == null) {
$this->showError(
$authenticator::getMissingMsg($this)
->inContentLanguage()->plain(),
$out, $request
);
return null;
}
// now attempt to retrieve the MediaWiki user
// associated with whoever commented the verification code
$user = User::newFromName($username);
// ...if that user does not exist, then show an error
// that this account does not exist on the wiki
if ($user->getId() == 0) {
$this->showError(
wfMessage('scratchlogin-unregistered', $username)
->inContentLanguage()->parse(),
$out, $request
);
return null;
}
try {
$wikiUserTimestamp = new ConvertibleTimestamp($user->getRegistration());
$scratchUserTimestamp = getScratchUserRegisteredAt($username);
$diff = $scratchUserTimestamp->diff($wikiUserTimestamp);
if ($diff->invert) {
// Scratch user registered after wiki user.
// To prevent disaster, make it error.
$this->showError(
wfMessage('scratchlogin-account-age-error', $username)
->inContentLanguage()->parse(),
$out, $request
);
return null;
}
} catch (Exception $e) {
//in the event of any failure, do NOT allow the login attempt to continue
$this->showError(wfMessage('scratchlogin-api-failure')->inContentLanguage()->parse(), $out, $request);
return null;
}
// clear the verification code in the session so that they have to
// use a different code to login as a different user
$authenticator::clearAuthCode($session);
return $user;
}
// reset the code associated with the current user's session
function doCodeReset($out, $request, $returnto) {
$session = $request->getSession();
$authenticator = getAuthenticator();
$authenticator::clearAuthCode($session);
$out->addWikiMsg('scratchlogin-code-reset', $returnto);
}
}