1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-15 10:00: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', 'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
'PhabricatorAuthSessionInfo' => 'applications/auth/data/PhabricatorAuthSessionInfo.php', 'PhabricatorAuthSessionInfo' => 'applications/auth/data/PhabricatorAuthSessionInfo.php',
'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php', 'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
'PhabricatorAuthSetPasswordController' => 'applications/auth/controller/PhabricatorAuthSetPasswordController.php',
'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php', 'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php',
'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php', 'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php',
'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'applications/auth/factor/PhabricatorAuthTOTPKeyTemporaryTokenType.php', 'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'applications/auth/factor/PhabricatorAuthTOTPKeyTemporaryTokenType.php',
@ -7377,6 +7378,7 @@ phutil_register_library_map(array(
'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector', 'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
'PhabricatorAuthSessionInfo' => 'Phobject', 'PhabricatorAuthSessionInfo' => 'Phobject',
'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorAuthSetPasswordController' => 'PhabricatorAuthController',
'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorAuthStartController' => 'PhabricatorAuthController', 'PhabricatorAuthStartController' => 'PhabricatorAuthController',
'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType', 'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',

View file

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

View file

@ -139,8 +139,7 @@ final class PhabricatorAuthOneTimeLoginController
->save(); ->save();
unset($unguarded); unset($unguarded);
$username = $target_user->getUsername(); $panel_uri = '/auth/password/';
$panel_uri = "/settings/user/{$username}/page/password/";
$next = (string)id(new PhutilURI($panel_uri)) $next = (string)id(new PhutilURI($panel_uri))
->setQueryParams( ->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(); $notifiable_phids[] = $invitee->getInviteePHID();
} }
if (!$notifiable_phids) { if ($notifiable_phids) {
$attendee_map[$key] = array_fuse($notifiable_phids);
} else {
unset($events[$key]); unset($events[$key]);
} }
$attendee_map[$key] = array_fuse($notifiable_phids);
} }
if (!$attendee_map) { if (!$attendee_map) {
// None of the events have any notifiable attendees, so there is no // 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()); $fields = idx($build_data, 'fields', array());
unset($build_data['fields']); unset($build_data['fields']);
unset($build_data['attachments']); 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)); $data[] = array_mergev(array($build_data, $querybuilds, $fields));
} }

View file

@ -55,67 +55,28 @@ final class HarbormasterBuildStatus extends Phobject {
* @return string Human-readable name. * @return string Human-readable name.
*/ */
public static function getBuildStatusName($status) { public static function getBuildStatusName($status) {
$map = self::getBuildStatusMap(); $spec = self::getBuildStatusSpec($status);
return idx($map, $status, pht('Unknown ("%s")', $status)); return idx($spec, 'name', pht('Unknown ("%s")', $status));
} }
public static function getBuildStatusMap() { public static function getBuildStatusMap() {
return array( $specs = self::getBuildStatusSpecMap();
self::STATUS_INACTIVE => pht('Inactive'), return ipull($specs, 'name');
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'),
);
} }
public static function getBuildStatusIcon($status) { public static function getBuildStatusIcon($status) {
switch ($status) { $spec = self::getBuildStatusSpec($status);
case self::STATUS_INACTIVE: return idx($spec, 'icon', 'fa-question-circle');
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;
}
} }
public static function getBuildStatusColor($status) { public static function getBuildStatusColor($status) {
switch ($status) { $spec = self::getBuildStatusSpec($status);
case self::STATUS_INACTIVE: return idx($spec, 'color', 'bluegrey');
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';
} }
public static function getBuildStatusANSIColor($status) {
$spec = self::getBuildStatusSpec($status);
return idx($spec, 'color.ansi', 'magenta');
} }
public static function getWaitingStatusConstants() { 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( 'buildStatus' => array(
'value' => $status, 'value' => $status,
'name' => HarbormasterBuildStatus::getBuildStatusName($status), 'name' => HarbormasterBuildStatus::getBuildStatusName($status),
'color.ansi' =>
HarbormasterBuildStatus::getBuildStatusANSIColor($status),
), ),
'initiatorPHID' => nonempty($this->getInitiatorPHID(), null), 'initiatorPHID' => nonempty($this->getInitiatorPHID(), null),
'name' => $this->getName(), 'name' => $this->getName(),

View file

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

View file

@ -20,6 +20,14 @@ final class PhabricatorProjectViewController
$engine = $this->getProfileMenuEngine(); $engine = $this->getProfileMenuEngine();
$default = $engine->getDefaultItem(); $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()) { switch ($default->getBuiltinKey()) {
case PhabricatorProject::ITEM_WORKBOARD: case PhabricatorProject::ITEM_WORKBOARD:
$controller_object = new PhabricatorProjectBoardViewController(); $controller_object = new PhabricatorProjectBoardViewController();
@ -27,6 +35,9 @@ final class PhabricatorProjectViewController
case PhabricatorProject::ITEM_PROFILE: case PhabricatorProject::ITEM_PROFILE:
$controller_object = new PhabricatorProjectProfileController(); $controller_object = new PhabricatorProjectProfileController();
break; break;
case PhabricatorProject::ITEM_MANAGE:
$controller_object = new PhabricatorProjectManageController();
break;
default: default:
return $engine->buildResponse(); return $engine->buildResponse();
} }

View file

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

View file

@ -18,6 +18,11 @@ final class PhabricatorProjectManageProfileMenuItem
return false; return false;
} }
public function canMakeDefault(
PhabricatorProfileMenuItemConfiguration $config) {
return true;
}
public function getDisplayName( public function getDisplayName(
PhabricatorProfileMenuItemConfiguration $config) { PhabricatorProfileMenuItemConfiguration $config) {
$name = $config->getMenuItemProperty('name'); $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 = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len; $min_len = (int)$min_len;
// NOTE: To change your password, you need to prove you own the account, // NOTE: Users can also change passwords through the separate "set/reset"
// either by providing the old password or by carrying a token to // interface which is reached by logging in with a one-time token after
// the workflow from a password reset email. // registration or password reset. If this flow changes, that flow may
// also need to change.
$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();
}
$e_old = true; $e_old = true;
$e_new = true; $e_new = true;
@ -59,13 +46,11 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
$errors = array(); $errors = array();
if ($request->isFormPost()) { if ($request->isFormPost()) {
if (!$token) {
$envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw')); $envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw'));
if (!$user->comparePassword($envelope)) { if (!$user->comparePassword($envelope)) {
$errors[] = pht('The old password you entered is incorrect.'); $errors[] = pht('The old password you entered is incorrect.');
$e_old = pht('Invalid'); $e_old = pht('Invalid');
} }
}
$pass = $request->getStr('new_pw'); $pass = $request->getStr('new_pw');
$conf = $request->getStr('conf_pw'); $conf = $request->getStr('conf_pw');
@ -98,16 +83,7 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
unset($unguarded); 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'); $next = $this->getPanelURI('?saved=true');
}
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions( id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$user, $user,
@ -125,9 +101,6 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
} catch (PhabricatorPasswordHasherUnavailableException $ex) { } catch (PhabricatorPasswordHasherUnavailableException $ex) {
$can_upgrade = false; $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( $errors[] = pht(
'Your password is currently hashed using an algorithm which is '. 'Your password is currently hashed using an algorithm which is '.
'no longer available on this install.'); '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 '. 'To set a new password, request a password reset link from the '.
'login screen and then follow the instructions.'); 'login screen and then follow the instructions.');
} }
}
if ($can_upgrade) { if ($can_upgrade) {
$errors[] = pht( $errors[] = pht(
@ -153,20 +125,13 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
$len_caption = pht('Minimum password length: %d characters.', $min_len); $len_caption = pht('Minimum password length: %d characters.', $min_len);
} }
$form = new AphrontFormView(); $form = id(new AphrontFormView())
$form ->setViewer($user)
->setUser($user) ->appendChild(
->addHiddenInput('key', $key);
if (!$token) {
$form->appendChild(
id(new AphrontFormPasswordControl()) id(new AphrontFormPasswordControl())
->setLabel(pht('Old Password')) ->setLabel(pht('Old Password'))
->setError($e_old) ->setError($e_old)
->setName('old_pw')); ->setName('old_pw'))
}
$form
->appendChild( ->appendChild(
id(new AphrontFormPasswordControl()) id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true) ->setDisableAutocomplete(true)