1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-15 01:50:55 +01:00

(stable) Promote 2017 Week 51

This commit is contained in:
epriestley 2017-12-26 09:00:20 -08:00
commit e620f8d930
13 changed files with 297 additions and 113 deletions

View file

@ -2112,6 +2112,7 @@ phutil_register_library_map(array(
'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
'PhabricatorAuthSessionInfo' => 'applications/auth/data/PhabricatorAuthSessionInfo.php',
'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
'PhabricatorAuthSetPasswordController' => 'applications/auth/controller/PhabricatorAuthSetPasswordController.php',
'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php',
'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php',
'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'applications/auth/factor/PhabricatorAuthTOTPKeyTemporaryTokenType.php',
@ -7377,6 +7378,7 @@ phutil_register_library_map(array(
'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
'PhabricatorAuthSessionInfo' => 'Phobject',
'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorAuthSetPasswordController' => 'PhabricatorAuthController',
'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorAuthStartController' => 'PhabricatorAuthController',
'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',

View file

@ -84,6 +84,7 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
=> 'PhabricatorAuthSSHKeyDeactivateController',
'view/(?P<id>\d+)/' => 'PhabricatorAuthSSHKeyViewController',
),
'password/' => 'PhabricatorAuthSetPasswordController',
),
'/oauth/(?P<provider>\w+)/login/'

View file

@ -139,8 +139,7 @@ final class PhabricatorAuthOneTimeLoginController
->save();
unset($unguarded);
$username = $target_user->getUsername();
$panel_uri = "/settings/user/{$username}/page/password/";
$panel_uri = '/auth/password/';
$next = (string)id(new PhutilURI($panel_uri))
->setQueryParams(

View file

@ -0,0 +1,155 @@
<?php
final class PhabricatorAuthSetPasswordController
extends PhabricatorAuthController {
public function shouldAllowPartialSessions() {
return true;
}
public function shouldAllowLegallyNonCompliantUsers() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
if (!PhabricatorPasswordAuthProvider::getPasswordProvider()) {
return new Aphront404Response();
}
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
'/');
$key = $request->getStr('key');
$password_type = PhabricatorAuthPasswordResetTemporaryTokenType::TOKENTYPE;
if (!$key) {
return new Aphront404Response();
}
$auth_token = id(new PhabricatorAuthTemporaryTokenQuery())
->setViewer($viewer)
->withTokenResources(array($viewer->getPHID()))
->withTokenTypes(array($password_type))
->withTokenCodes(array(PhabricatorHash::weakDigest($key)))
->withExpired(false)
->executeOne();
if (!$auth_token) {
return new Aphront404Response();
}
$min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len;
$e_password = true;
$e_confirm = true;
$errors = array();
if ($request->isFormPost()) {
$password = $request->getStr('password');
$confirm = $request->getStr('confirm');
$e_password = null;
$e_confirm = null;
if (!strlen($password)) {
$errors[] = pht('You must choose a password or skip this step.');
$e_password = pht('Required');
} else if (strlen($password) < $min_len) {
$errors[] = pht(
'The selected password is too short. Passwords must be a minimum '.
'of %s characters.',
new PhutilNumber($min_len));
$e_password = pht('Too Short');
} else if (!strlen($confirm)) {
$errors[] = pht('You must confirm the selecetd password.');
$e_confirm = pht('Required');
} else if ($password !== $confirm) {
$errors[] = pht('The password and confirmation do not match.');
$e_password = pht('Invalid');
$e_confirm = pht('Invalid');
} else if (PhabricatorCommonPasswords::isCommonPassword($password)) {
$e_password = pht('Very Weak');
$errors[] = pht(
'The selected password is very weak: it is one of the most common '.
'passwords in use. Choose a stronger password.');
}
if (!$errors) {
$envelope = new PhutilOpaqueEnvelope($password);
// This write is unguarded because the CSRF token has already
// been checked in the call to $request->isFormPost() and
// the CSRF token depends on the password hash, so when it
// is changed here the CSRF token check will fail.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
id(new PhabricatorUserEditor())
->setActor($viewer)
->changePassword($viewer, $envelope);
unset($unguarded);
// Destroy the token.
$auth_token->delete();
return id(new AphrontRedirectResponse())->setURI('/');
}
}
$len_caption = null;
if ($min_len) {
$len_caption = pht('Minimum password length: %d characters.', $min_len);
}
if ($viewer->hasPassword()) {
$title = pht('Reset Password');
$crumb = pht('Reset Password');
$submit = pht('Reset Password');
} else {
$title = pht('Set Password');
$crumb = pht('Set Password');
$submit = pht('Set Account Password');
}
$form = id(new AphrontFormView())
->setViewer($viewer)
->addHiddenInput('key', $key)
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)
->setLabel(pht('New Password'))
->setError($e_password)
->setName('password'))
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)
->setLabel(pht('Confirm Password'))
->setCaption($len_caption)
->setError($e_confirm)
->setName('confirm'))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/', pht('Skip This Step'))
->setValue($submit));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setFormErrors($errors)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setForm($form);
$main_view = id(new PHUITwoColumnView())
->setFooter($form_box);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($crumb)
->setBorder(true);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($main_view);
}
}

View file

@ -100,10 +100,11 @@ final class PhabricatorCalendarNotificationEngine
}
$notifiable_phids[] = $invitee->getInviteePHID();
}
if (!$notifiable_phids) {
if ($notifiable_phids) {
$attendee_map[$key] = array_fuse($notifiable_phids);
} else {
unset($events[$key]);
}
$attendee_map[$key] = array_fuse($notifiable_phids);
}
if (!$attendee_map) {
// None of the events have any notifiable attendees, so there is no

View file

@ -65,6 +65,16 @@ final class HarbormasterQueryBuildsConduitAPIMethod
$fields = idx($build_data, 'fields', array());
unset($build_data['fields']);
unset($build_data['attachments']);
// To retain backward compatibility, remove newer keys from the
// result array.
$fields['buildStatus'] = array_select_keys(
$fields['buildStatus'],
array(
'value',
'name',
));
$data[] = array_mergev(array($build_data, $querybuilds, $fields));
}

View file

@ -55,67 +55,28 @@ final class HarbormasterBuildStatus extends Phobject {
* @return string Human-readable name.
*/
public static function getBuildStatusName($status) {
$map = self::getBuildStatusMap();
return idx($map, $status, pht('Unknown ("%s")', $status));
$spec = self::getBuildStatusSpec($status);
return idx($spec, 'name', pht('Unknown ("%s")', $status));
}
public static function getBuildStatusMap() {
return array(
self::STATUS_INACTIVE => pht('Inactive'),
self::STATUS_PENDING => pht('Pending'),
self::STATUS_BUILDING => pht('Building'),
self::STATUS_PASSED => pht('Passed'),
self::STATUS_FAILED => pht('Failed'),
self::STATUS_ABORTED => pht('Aborted'),
self::STATUS_ERROR => pht('Unexpected Error'),
self::STATUS_PAUSED => pht('Paused'),
self::STATUS_DEADLOCKED => pht('Deadlocked'),
);
$specs = self::getBuildStatusSpecMap();
return ipull($specs, 'name');
}
public static function getBuildStatusIcon($status) {
switch ($status) {
case self::STATUS_INACTIVE:
case self::STATUS_PENDING:
return PHUIStatusItemView::ICON_OPEN;
case self::STATUS_BUILDING:
return PHUIStatusItemView::ICON_RIGHT;
case self::STATUS_PASSED:
return PHUIStatusItemView::ICON_ACCEPT;
case self::STATUS_FAILED:
return PHUIStatusItemView::ICON_REJECT;
case self::STATUS_ABORTED:
return PHUIStatusItemView::ICON_MINUS;
case self::STATUS_ERROR:
return PHUIStatusItemView::ICON_MINUS;
case self::STATUS_PAUSED:
return PHUIStatusItemView::ICON_MINUS;
case self::STATUS_DEADLOCKED:
return PHUIStatusItemView::ICON_WARNING;
default:
return PHUIStatusItemView::ICON_QUESTION;
}
$spec = self::getBuildStatusSpec($status);
return idx($spec, 'icon', 'fa-question-circle');
}
public static function getBuildStatusColor($status) {
switch ($status) {
case self::STATUS_INACTIVE:
return 'dark';
case self::STATUS_PENDING:
case self::STATUS_BUILDING:
return 'blue';
case self::STATUS_PASSED:
return 'green';
case self::STATUS_FAILED:
case self::STATUS_ABORTED:
case self::STATUS_ERROR:
case self::STATUS_DEADLOCKED:
return 'red';
case self::STATUS_PAUSED:
return 'dark';
default:
return 'bluegrey';
$spec = self::getBuildStatusSpec($status);
return idx($spec, 'color', 'bluegrey');
}
public static function getBuildStatusANSIColor($status) {
$spec = self::getBuildStatusSpec($status);
return idx($spec, 'color.ansi', 'magenta');
}
public static function getWaitingStatusConstants() {
@ -142,4 +103,67 @@ final class HarbormasterBuildStatus extends Phobject {
);
}
private static function getBuildStatusSpecMap() {
return array(
self::STATUS_INACTIVE => array(
'name' => pht('Inactive'),
'icon' => 'fa-circle-o',
'color' => 'dark',
'color.ansi' => 'yellow',
),
self::STATUS_PENDING => array(
'name' => pht('Pending'),
'icon' => 'fa-circle-o',
'color' => 'blue',
'color.ansi' => 'yellow',
),
self::STATUS_BUILDING => array(
'name' => pht('Building'),
'icon' => 'fa-chevron-circle-right',
'color' => 'blue',
'color.ansi' => 'yellow',
),
self::STATUS_PASSED => array(
'name' => pht('Passed'),
'icon' => 'fa-check-circle',
'color' => 'green',
'color.ansi' => 'green',
),
self::STATUS_FAILED => array(
'name' => pht('Failed'),
'icon' => 'fa-times-circle',
'color' => 'red',
'color.ansi' => 'red',
),
self::STATUS_ABORTED => array(
'name' => pht('Aborted'),
'icon' => 'fa-minus-circle',
'color' => 'red',
'color.ansi' => 'red',
),
self::STATUS_ERROR => array(
'name' => pht('Unexpected Error'),
'icon' => 'fa-minus-circle',
'color' => 'red',
'color.ansi' => 'red',
),
self::STATUS_PAUSED => array(
'name' => pht('Paused'),
'icon' => 'fa-minus-circle',
'color' => 'dark',
'color.ansi' => 'yellow',
),
self::STATUS_DEADLOCKED => array(
'name' => pht('Deadlocked'),
'icon' => 'fa-exclamation-circle',
'color' => 'red',
'color.ansi' => 'red',
),
);
}
private static function getBuildStatusSpec($status) {
return idx(self::getBuildStatusSpecMap(), $status, array());
}
}

View file

@ -435,6 +435,8 @@ final class HarbormasterBuild extends HarbormasterDAO
'buildStatus' => array(
'value' => $status,
'name' => HarbormasterBuildStatus::getBuildStatusName($status),
'color.ansi' =>
HarbormasterBuildStatus::getBuildStatusANSIColor($status),
),
'initiatorPHID' => nonempty($this->getInitiatorPHID(), null),
'name' => $this->getName(),

View file

@ -262,6 +262,10 @@ final class PhabricatorUser
PhabricatorPeopleUserPHIDType::TYPECONST);
}
public function hasPassword() {
return (bool)strlen($this->passwordHash);
}
public function setPassword(PhutilOpaqueEnvelope $envelope) {
if (!$this->getPHID()) {
throw new Exception(

View file

@ -20,6 +20,14 @@ final class PhabricatorProjectViewController
$engine = $this->getProfileMenuEngine();
$default = $engine->getDefaultItem();
// If defaults are broken somehow, serve the manage page. See T13033 for
// discussion.
if ($default) {
$default_key = $default->getBuiltinKey();
} else {
$default_key = PhabricatorProject::ITEM_MANAGE;
}
switch ($default->getBuiltinKey()) {
case PhabricatorProject::ITEM_WORKBOARD:
$controller_object = new PhabricatorProjectBoardViewController();
@ -27,6 +35,9 @@ final class PhabricatorProjectViewController
case PhabricatorProject::ITEM_PROFILE:
$controller_object = new PhabricatorProjectProfileController();
break;
case PhabricatorProject::ITEM_MANAGE:
$controller_object = new PhabricatorProjectManageController();
break;
default:
return $engine->buildResponse();
}

View file

@ -13,6 +13,11 @@ final class PhabricatorProjectDetailsProfileMenuItem
return pht('Project Details');
}
public function canHideMenuItem(
PhabricatorProfileMenuItemConfiguration $config) {
return false;
}
public function canMakeDefault(
PhabricatorProfileMenuItemConfiguration $config) {
return true;

View file

@ -18,6 +18,11 @@ final class PhabricatorProjectManageProfileMenuItem
return false;
}
public function canMakeDefault(
PhabricatorProfileMenuItemConfiguration $config) {
return true;
}
public function getDisplayName(
PhabricatorProfileMenuItemConfiguration $config) {
$name = $config->getMenuItemProperty('name');

View file

@ -35,23 +35,10 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
$min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len;
// NOTE: To change your password, you need to prove you own the account,
// either by providing the old password or by carrying a token to
// the workflow from a password reset email.
$key = $request->getStr('key');
$password_type = PhabricatorAuthPasswordResetTemporaryTokenType::TOKENTYPE;
$token = null;
if ($key) {
$token = id(new PhabricatorAuthTemporaryTokenQuery())
->setViewer($user)
->withTokenResources(array($user->getPHID()))
->withTokenTypes(array($password_type))
->withTokenCodes(array(PhabricatorHash::weakDigest($key)))
->withExpired(false)
->executeOne();
}
// NOTE: Users can also change passwords through the separate "set/reset"
// interface which is reached by logging in with a one-time token after
// registration or password reset. If this flow changes, that flow may
// also need to change.
$e_old = true;
$e_new = true;
@ -59,13 +46,11 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
$errors = array();
if ($request->isFormPost()) {
if (!$token) {
$envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw'));
if (!$user->comparePassword($envelope)) {
$errors[] = pht('The old password you entered is incorrect.');
$e_old = pht('Invalid');
}
}
$pass = $request->getStr('new_pw');
$conf = $request->getStr('conf_pw');
@ -98,16 +83,7 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
unset($unguarded);
if ($token) {
// Destroy the token.
$token->delete();
// If this is a password set/reset, kick the user to the home page
// after we update their account.
$next = '/';
} else {
$next = $this->getPanelURI('?saved=true');
}
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$user,
@ -125,9 +101,6 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
} catch (PhabricatorPasswordHasherUnavailableException $ex) {
$can_upgrade = false;
// Only show this stuff if we aren't on the reset workflow. We can
// do resets regardless of the old hasher's availability.
if (!$token) {
$errors[] = pht(
'Your password is currently hashed using an algorithm which is '.
'no longer available on this install.');
@ -138,7 +111,6 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
'To set a new password, request a password reset link from the '.
'login screen and then follow the instructions.');
}
}
if ($can_upgrade) {
$errors[] = pht(
@ -153,20 +125,13 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
$len_caption = pht('Minimum password length: %d characters.', $min_len);
}
$form = new AphrontFormView();
$form
->setUser($user)
->addHiddenInput('key', $key);
if (!$token) {
$form->appendChild(
$form = id(new AphrontFormView())
->setViewer($user)
->appendChild(
id(new AphrontFormPasswordControl())
->setLabel(pht('Old Password'))
->setError($e_old)
->setName('old_pw'));
}
$form
->setName('old_pw'))
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)