/** * @param string|MWRestrictions $value The value the field was submitted with * @param array $alldata The data collected from the form * * @return bool|string True on success, or String error to display, or * false to fail validation without displaying an error. */ public function validate($value, $alldata) { if ($this->isHidden($alldata)) { return true; } if (isset($this->mParams['required']) && $this->mParams['required'] !== false && $value instanceof MWRestrictions && !$value->toArray()['IPAddresses']) { return $this->msg('htmlform-required')->parse(); } if (is_string($value)) { // MWRestrictions::newFromArray failed; one of the IP ranges must be invalid $status = Status::newGood(); foreach (explode(PHP_EOL, $value) as $range) { if (!\IP::isIPAddress($range)) { $status->fatal('restrictionsfield-badip', $range); } } if ($status->isOK()) { $status->fatal('unknown-error'); } return $status->getMessage()->parse(); } if (isset($this->mValidationCallback)) { return call_user_func($this->mValidationCallback, $value, $alldata, $this->mParent); } return true; }
/** * Save the BotPassword to the database * @param string $operation 'update' or 'insert' * @param Password|null $password Password to set. * @return bool Success */ public function save($operation, Password $password = null) { $conds = array('bp_user' => $this->centralId, 'bp_app_id' => $this->appId); $fields = array('bp_token' => MWCryptRand::generateHex(User::TOKEN_LENGTH), 'bp_restrictions' => $this->restrictions->toJson(), 'bp_grants' => FormatJson::encode($this->grants)); if ($password !== null) { $fields['bp_password'] = $password->toString(); } elseif ($operation === 'insert') { $fields['bp_password'] = PasswordFactory::newInvalidPassword()->toString(); } $dbw = self::getDB(DB_MASTER); switch ($operation) { case 'insert': $dbw->insert('bot_passwords', $fields + $conds, __METHOD__, array('IGNORE')); break; case 'update': $dbw->update('bot_passwords', $fields, $conds, __METHOD__); break; default: return false; } $ok = (bool) $dbw->affectedRows(); if ($ok) { $this->token = $dbw->selectField('bot_passwords', 'bp_token', $conds, __METHOD__); $this->isSaved = true; } return $ok; }
public function testConstruct() { $field = new HTMLRestrictionsField(['fieldname' => 'restrictions']); $this->assertNotEmpty($field->getLabel(), 'has a default label'); $this->assertNotEmpty($field->getHelpText(), 'has a default help text'); $this->assertEquals(MWRestrictions::newDefault(), $field->getDefault(), 'defaults to the default MWRestrictions object'); $field = new HTMLRestrictionsField(['fieldname' => 'restrictions', 'label' => 'foo', 'help' => 'bar', 'default' => 'baz']); $this->assertEquals('foo', $field->getLabel(), 'label can be customized'); $this->assertEquals('bar', $field->getHelpText(), 'help text can be customized'); $this->assertEquals('baz', $field->getDefault(), 'default can be customized'); }
/** * @covers MWRestrictions::newFromJson * @covers MWRestrictions::__construct * @covers MWRestrictions::loadFromArray * @covers MWRestrictions::toJson * @covers MWRestrictions::__toString * @dataProvider provideJson * @param string $json * @param array|InvalidArgumentException $expect */ public function testJson($json, $expect) { if (is_array($expect)) { $ret = MWRestrictions::newFromJson($json); $this->assertInstanceOf('MWRestrictions', $ret); $this->assertSame($expect, $ret->toArray()); $this->assertSame($json, $ret->toJson(false)); $this->assertSame($json, (string) $ret); $this->assertSame(FormatJson::encode($expect, true, FormatJson::ALL_OK), $ret->toJson(true)); } else { try { MWRestrictions::newFromJson($json); $this->fail('Expected exception not thrown'); } catch (InvalidArgumentException $ex) { $this->assertTrue(true); } } }
/** * @dataProvider provideSave * @param string|null $password */ public function testSave($password) { $passwordFactory = new \PasswordFactory(); $passwordFactory->init(\RequestContext::getMain()->getConfig()); $bp = BotPassword::newUnsaved(['centralId' => 42, 'appId' => 'TestSave', 'restrictions' => MWRestrictions::newFromJson('{"IPAddresses":["127.0.0.0/8"]}'), 'grants' => ['test']]); $this->assertFalse($bp->isSaved(), 'sanity check'); $this->assertNull(BotPassword::newFromCentralId(42, 'TestSave', BotPassword::READ_LATEST), 'sanity check'); $passwordHash = $password ? $passwordFactory->newFromPlaintext($password) : null; $this->assertFalse($bp->save('update', $passwordHash)); $this->assertTrue($bp->save('insert', $passwordHash)); $bp2 = BotPassword::newFromCentralId(42, 'TestSave', BotPassword::READ_LATEST); $this->assertInstanceOf('BotPassword', $bp2); $this->assertEquals($bp->getUserCentralId(), $bp2->getUserCentralId()); $this->assertEquals($bp->getAppId(), $bp2->getAppId()); $this->assertEquals($bp->getToken(), $bp2->getToken()); $this->assertEquals($bp->getRestrictions(), $bp2->getRestrictions()); $this->assertEquals($bp->getGrants(), $bp2->getGrants()); $pw = TestingAccessWrapper::newFromObject($bp)->getPassword(); if ($password === null) { $this->assertInstanceOf('InvalidPassword', $pw); } else { $this->assertTrue($pw->equals($password)); } $token = $bp->getToken(); $this->assertFalse($bp->save('insert')); $this->assertTrue($bp->save('update')); $this->assertNotEquals($token, $bp->getToken()); $bp2 = BotPassword::newFromCentralId(42, 'TestSave', BotPassword::READ_LATEST); $this->assertInstanceOf('BotPassword', $bp2); $this->assertEquals($bp->getToken(), $bp2->getToken()); $pw = TestingAccessWrapper::newFromObject($bp)->getPassword(); if ($password === null) { $this->assertInstanceOf('InvalidPassword', $pw); } else { $this->assertTrue($pw->equals($password)); } $passwordHash = $passwordFactory->newFromPlaintext('XXX'); $token = $bp->getToken(); $this->assertTrue($bp->save('update', $passwordHash)); $this->assertNotEquals($token, $bp->getToken()); $pw = TestingAccessWrapper::newFromObject($bp)->getPassword(); $this->assertTrue($pw->equals('XXX')); $this->assertTrue($bp->delete()); $this->assertFalse($bp->isSaved()); $this->assertNull(BotPassword::newFromCentralId(42, 'TestSave', BotPassword::READ_LATEST)); $this->assertFalse($bp->save('foobar')); }
public function testBotPassword() { global $wgServer, $wgSessionProviders; if (!isset($wgServer)) { $this->markTestIncomplete('This test needs $wgServer to be set in LocalSettings.php'); } $this->setMwGlobals(array('wgSessionProviders' => array_merge($wgSessionProviders, array(array('class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => array(array('priority' => 40))))), 'wgEnableBotPasswords' => true, 'wgBotPasswordsDatabase' => false, 'wgCentralIdLookupProvider' => 'local', 'wgGrantPermissions' => array('test' => array('read' => true)))); // Make sure our session provider is present $manager = TestingAccessWrapper::newFromObject(MediaWiki\Session\SessionManager::singleton()); if (!isset($manager->sessionProviders['MediaWiki\\Session\\BotPasswordSessionProvider'])) { $tmp = $manager->sessionProviders; $manager->sessionProviders = null; $manager->sessionProviders = $tmp + $manager->getProviders(); } $this->assertNotNull(MediaWiki\Session\SessionManager::singleton()->getProvider('MediaWiki\\Session\\BotPasswordSessionProvider'), 'sanity check'); $user = self::$users['sysop']; $centralId = CentralIdLookup::factory()->centralIdFromLocalUser($user->getUser()); $this->assertNotEquals(0, $centralId, 'sanity check'); $passwordFactory = new PasswordFactory(); $passwordFactory->init(RequestContext::getMain()->getConfig()); // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only $passwordFactory->setDefaultType('A'); $pwhash = $passwordFactory->newFromPlaintext('foobaz'); $dbw = wfGetDB(DB_MASTER); $dbw->insert('bot_passwords', array('bp_user' => $centralId, 'bp_app_id' => 'foo', 'bp_password' => $pwhash->toString(), 'bp_token' => '', 'bp_restrictions' => MWRestrictions::newDefault()->toJson(), 'bp_grants' => '["test"]'), __METHOD__); $lgName = $user->username . BotPassword::getSeparator() . 'foo'; $ret = $this->doApiRequest(array('action' => 'login', 'lgname' => $lgName, 'lgpassword' => 'foobaz')); $result = $ret[0]; $this->assertNotInternalType('bool', $result); $this->assertNotInternalType('null', $result['login']); $a = $result['login']['result']; $this->assertEquals('NeedToken', $a); $token = $result['login']['token']; $ret = $this->doApiRequest(array('action' => 'login', 'lgtoken' => $token, 'lgname' => $lgName, 'lgpassword' => 'foobaz'), $ret[2]); $result = $ret[0]; $this->assertNotInternalType('bool', $result); $a = $result['login']['result']; $this->assertEquals('Success', $a); }
private function save(array $data) { $bp = BotPassword::newUnsaved(['centralId' => $this->userId, 'appId' => $this->par, 'restrictions' => MWRestrictions::newFromJson($data['restrictions']), 'grants' => array_merge(MWGrants::getHiddenGrants(), preg_replace('/^grant-/', '', $data['grants']))]); if ($this->operation === 'insert' || !empty($data['resetPassword'])) { $this->password = PasswordFactory::generateRandomPasswordString(max(32, $this->getConfig()->get('MinimalPasswordLength'))); $passwordFactory = new PasswordFactory(); $passwordFactory->init(RequestContext::getMain()->getConfig()); $password = $passwordFactory->newFromPlaintext($this->password); } else { $password = null; } if ($bp->save($this->operation, $password)) { return Status::newGood(); } else { // Messages: botpasswords-insert-failed, botpasswords-update-failed return Status::newFatal("botpasswords-{$this->operation}-failed", $this->par); } }