Page MenuHomePhorge

No OneTemporary

diff --git a/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php b/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
index 17a6de0f75..ff06173f50 100644
--- a/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
@@ -1,100 +1,109 @@
<?php
final class PhabricatorConduitTokenEditController
extends PhabricatorConduitController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
if ($id) {
$token = id(new PhabricatorConduitTokenQuery())
->setViewer($viewer)
->withIDs(array($id))
->withExpired(false)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$token) {
return new Aphront404Response();
}
$object = $token->getObject();
$is_new = false;
$title = pht('View API Token');
} else {
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($request->getStr('objectPHID')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$object) {
return new Aphront404Response();
}
$token = PhabricatorConduitToken::initializeNewToken(
$object->getPHID(),
PhabricatorConduitToken::TYPE_STANDARD);
$is_new = true;
$title = pht('Generate API Token');
$submit_button = pht('Generate Token');
}
if ($viewer->getPHID() == $object->getPHID()) {
$panel_uri = '/settings/panel/apitokens/';
} else {
$panel_uri = '/settings/'.$object->getID().'/panel/apitokens/';
}
id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$panel_uri);
if ($request->isFormPost()) {
$token->save();
if ($is_new) {
$token_uri = '/conduit/token/edit/'.$token->getID().'/';
} else {
$token_uri = $panel_uri;
}
return id(new AphrontRedirectResponse())->setURI($token_uri);
}
$dialog = $this->newDialog()
->setTitle($title)
->addHiddenInput('objectPHID', $object->getPHID());
if ($is_new) {
$dialog
->appendParagraph(pht('Generate a new API token?'))
->addSubmitButton($submit_button)
->addCancelButton($panel_uri);
} else {
$form = id(new AphrontFormView())
- ->setUser($viewer)
- ->appendChild(
+ ->setUser($viewer);
+
+ if ($token->getTokenType() === PhabricatorConduitToken::TYPE_CLUSTER) {
+ $dialog->appendChild(
+ pht(
+ 'This token is automatically generated by Phabricator, and used '.
+ 'to make requests between nodes in a Phabricator cluster. You '.
+ 'can not use this token in external applications.'));
+ } else {
+ $form->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Token'))
->setValue($token->getToken()));
+ }
$dialog
->appendForm($form)
->addCancelButton($panel_uri, pht('Done'));
}
return $dialog;
}
}
diff --git a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php
index e0d0886273..38a258c4ed 100644
--- a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php
+++ b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php
@@ -1,115 +1,128 @@
<?php
final class PhabricatorConduitTokenQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $objectPHIDs;
private $expired;
private $tokens;
+ private $tokenTypes;
public function withExpired($expired) {
$this->expired = $expired;
return $this;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withObjectPHIDs(array $phids) {
$this->objectPHIDs = $phids;
return $this;
}
public function withTokens(array $tokens) {
$this->tokens = $tokens;
return $this;
}
+ public function withTokenTypes(array $types) {
+ $this->tokenTypes = $types;
+ return $this;
+ }
+
public function loadPage() {
$table = new PhabricatorConduitToken();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->objectPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
'objectPHID IN (%Ls)',
$this->objectPHIDs);
}
if ($this->tokens !== null) {
$where[] = qsprintf(
$conn_r,
'token IN (%Ls)',
$this->tokens);
}
+ if ($this->tokenTypes !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'tokenType IN (%Ls)',
+ $this->tokenTypes);
+ }
+
if ($this->expired !== null) {
if ($this->expired) {
$where[] = qsprintf(
$conn_r,
'expires <= %d',
PhabricatorTime::getNow());
} else {
$where[] = qsprintf(
$conn_r,
'expires IS NULL OR expires > %d',
PhabricatorTime::getNow());
}
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
protected function willFilterPage(array $tokens) {
$object_phids = mpull($tokens, 'getObjectPHID');
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withPHIDs($object_phids)
->execute();
$objects = mpull($objects, null, 'getPHID');
foreach ($tokens as $key => $token) {
$object = idx($objects, $token->getObjectPHID(), null);
if (!$object) {
$this->didRejectResult($token);
unset($tokens[$key]);
continue;
}
$token->attachObject($object);
}
return $tokens;
}
public function getQueryApplicationClass() {
return 'PhabricatorConduitApplication';
}
}
diff --git a/src/applications/conduit/settings/PhabricatorConduitSettingsPanel.php b/src/applications/conduit/settings/PhabricatorConduitSettingsPanel.php
index 40c982477b..1e2d1d9d5c 100644
--- a/src/applications/conduit/settings/PhabricatorConduitSettingsPanel.php
+++ b/src/applications/conduit/settings/PhabricatorConduitSettingsPanel.php
@@ -1,116 +1,116 @@
<?php
final class PhabricatorConduitSettingsPanel
extends PhabricatorSettingsPanel {
public function isEditableByAdministrators() {
return true;
}
public function getPanelKey() {
return 'apitokens';
}
public function getPanelName() {
return pht('Conduit API Tokens');
}
public function getPanelGroup() {
return pht('Sessions and Logs');
}
public function isEnabled() {
return true;
}
public function processRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$user = $this->getUser();
$tokens = id(new PhabricatorConduitTokenQuery())
->setViewer($viewer)
->withObjectPHIDs(array($user->getPHID()))
->withExpired(false)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$rows = array();
foreach ($tokens as $token) {
$rows[] = array(
javelin_tag(
'a',
array(
'href' => '/conduit/token/edit/'.$token->getID().'/',
'sigil' => 'workflow',
),
- substr($token->getToken(), 0, 8).'...'),
+ $token->getPublicTokenName()),
PhabricatorConduitToken::getTokenTypeName($token->getTokenType()),
phabricator_datetime($token->getDateCreated(), $viewer),
($token->getExpires()
? phabricator_datetime($token->getExpires(), $viewer)
: pht('Never')),
javelin_tag(
'a',
array(
'class' => 'button small grey',
'href' => '/conduit/token/terminate/'.$token->getID().'/',
'sigil' => 'workflow',
),
pht('Terminate')),
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString(pht("You don't have any active API tokens."));
$table->setHeaders(
array(
pht('Token'),
pht('Type'),
pht('Created'),
pht('Expires'),
null,
));
$table->setColumnClasses(
array(
'wide pri',
'',
'right',
'right',
'action',
));
$generate_icon = id(new PHUIIconView())
->setIconFont('fa-plus');
$generate_button = id(new PHUIButtonView())
->setText(pht('Generate API Token'))
->setHref('/conduit/token/edit/?objectPHID='.$user->getPHID())
->setTag('a')
->setWorkflow(true)
->setIcon($generate_icon);
$terminate_icon = id(new PHUIIconView())
->setIconFont('fa-exclamation-triangle');
$terminate_button = id(new PHUIButtonView())
->setText(pht('Terminate All Tokens'))
->setHref('/conduit/token/terminate/?objectPHID='.$user->getPHID())
->setTag('a')
->setWorkflow(true)
->setIcon($terminate_icon);
$header = id(new PHUIHeaderView())
->setHeader(pht('Active API Tokens'))
->addActionLink($generate_button)
->addActionLink($terminate_button);
$panel = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table);
return $panel;
}
}
diff --git a/src/applications/conduit/storage/PhabricatorConduitToken.php b/src/applications/conduit/storage/PhabricatorConduitToken.php
index 8ebcecb16b..5c39173c04 100644
--- a/src/applications/conduit/storage/PhabricatorConduitToken.php
+++ b/src/applications/conduit/storage/PhabricatorConduitToken.php
@@ -1,118 +1,165 @@
<?php
final class PhabricatorConduitToken
extends PhabricatorConduitDAO
implements PhabricatorPolicyInterface {
protected $objectPHID;
protected $tokenType;
protected $token;
protected $expires;
private $object = self::ATTACHABLE;
const TYPE_STANDARD = 'api';
- const TYPE_TEMPORARY = 'tmp';
const TYPE_COMMANDLINE = 'cli';
+ const TYPE_CLUSTER = 'clr';
public function getConfiguration() {
return array(
self::CONFIG_COLUMN_SCHEMA => array(
'tokenType' => 'text32',
'token' => 'text32',
'expires' => 'epoch?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_object' => array(
'columns' => array('objectPHID', 'tokenType'),
),
'key_token' => array(
'columns' => array('token'),
'unique' => true,
),
'key_expires' => array(
'columns' => array('expires'),
),
),
) + parent::getConfiguration();
}
+ public static function loadClusterTokenForUser(PhabricatorUser $user) {
+ if (!$user->isLoggedIn()) {
+ return null;
+ }
+
+ $tokens = id(new PhabricatorConduitTokenQuery())
+ ->setViewer($user)
+ ->withObjectPHIDs(array($user->getPHID()))
+ ->withTokenTypes(array(self::TYPE_CLUSTER))
+ ->withExpired(false)
+ ->execute();
+
+ // Only return a token if it has at least 5 minutes left before
+ // expiration. Cluster tokens cycle regularly, so we don't want to use
+ // one that's going to expire momentarily.
+ $now = PhabricatorTime::getNow();
+ $must_expire_after = $now + phutil_units('5 minutes in seconds');
+
+ foreach ($tokens as $token) {
+ if ($token->getExpires() > $must_expire_after) {
+ return $token;
+ }
+ }
+
+ // We didn't find any existing tokens (or the existing tokens are all about
+ // to expire) so generate a new token.
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ $token = PhabricatorConduitToken::initializeNewToken(
+ $user->getPHID(),
+ self::TYPE_CLUSTER);
+ $token->save();
+ unset($unguarded);
+
+ return $token;
+ }
+
public static function initializeNewToken($object_phid, $token_type) {
$token = new PhabricatorConduitToken();
$token->objectPHID = $object_phid;
$token->tokenType = $token_type;
$token->expires = $token->getTokenExpires($token_type);
$secret = $token_type.'-'.Filesystem::readRandomCharacters(32);
$secret = substr($secret, 0, 32);
$token->token = $secret;
return $token;
}
public static function getTokenTypeName($type) {
$map = array(
self::TYPE_STANDARD => pht('Standard API Token'),
- self::TYPE_TEMPORARY => pht('Temporary API Token'),
self::TYPE_COMMANDLINE => pht('Command Line API Token'),
+ self::TYPE_CLUSTER => pht('Cluster API Token'),
);
return idx($map, $type, $type);
}
public static function getAllTokenTypes() {
return array(
self::TYPE_STANDARD,
- self::TYPE_TEMPORARY,
self::TYPE_COMMANDLINE,
+ self::TYPE_CLUSTER,
);
}
private function getTokenExpires($token_type) {
+ $now = PhabricatorTime::getNow();
switch ($token_type) {
case self::TYPE_STANDARD:
return null;
- case self::TYPE_TEMPORARY:
- return PhabricatorTime::getNow() + phutil_units('24 hours in seconds');
case self::TYPE_COMMANDLINE:
- return PhabricatorTime::getNow() + phutil_units('1 hour in seconds');
+ return $now + phutil_units('1 hour in seconds');
+ case self::TYPE_CLUSTER:
+ return $now + phutil_units('30 minutes in seconds');
default:
throw new Exception(
pht('Unknown Conduit token type "%s"!', $token_type));
}
}
+ public function getPublicTokenName() {
+ switch ($this->getTokenType()) {
+ case self::TYPE_CLUSTER:
+ return pht('Cluster API Token');
+ default:
+ return substr($this->getToken(), 0, 8).'...';
+ }
+ }
+
public function getObject() {
return $this->assertAttached($this->object);
}
public function attachObject(PhabricatorUser $object) {
$this->object = $object;
return $this;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
return $this->getObject()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getObject()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht(
'Conduit tokens inherit the policies of the user they authenticate.');
}
}
diff --git a/src/applications/diffusion/query/DiffusionQuery.php b/src/applications/diffusion/query/DiffusionQuery.php
index 87714249fe..642927c062 100644
--- a/src/applications/diffusion/query/DiffusionQuery.php
+++ b/src/applications/diffusion/query/DiffusionQuery.php
@@ -1,252 +1,256 @@
<?php
abstract class DiffusionQuery extends PhabricatorQuery {
private $request;
final protected function __construct() {
// <protected>
}
protected static function newQueryObject(
$base_class,
DiffusionRequest $request) {
$repository = $request->getRepository();
$obj = self::initQueryObject($base_class, $repository);
$obj->request = $request;
return $obj;
}
final protected static function initQueryObject(
$base_class,
PhabricatorRepository $repository) {
$map = array(
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => 'Git',
PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => 'Mercurial',
PhabricatorRepositoryType::REPOSITORY_TYPE_SVN => 'Svn',
);
$name = idx($map, $repository->getVersionControlSystem());
if (!$name) {
throw new Exception('Unsupported VCS!');
}
$class = str_replace('Diffusion', 'Diffusion'.$name, $base_class);
$obj = new $class();
return $obj;
}
final protected function getRequest() {
return $this->request;
}
final public static function callConduitWithDiffusionRequest(
PhabricatorUser $user,
DiffusionRequest $drequest,
$method,
array $params = array()) {
$repository = $drequest->getRepository();
$core_params = array(
'callsign' => $repository->getCallsign(),
);
if ($drequest->getBranch() !== null) {
$core_params['branch'] = $drequest->getBranch();
}
$params = $params + $core_params;
$service_phid = $repository->getAlmanacServicePHID();
if ($service_phid === null) {
return id(new ConduitCall($method, $params))
->setUser($user)
->execute();
}
$service = id(new AlmanacServiceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($service_phid))
->needBindings(true)
->executeOne();
if (!$service) {
throw new Exception(
pht(
'The Alamnac service for this repository is invalid or could not '.
'be loaded.'));
}
$bindings = $service->getBindings();
if (!$bindings) {
throw new Exception(
pht(
'The Alamanc service for this repository is not bound to any '.
'interfaces.'));
}
$uris = array();
foreach ($bindings as $binding) {
$iface = $binding->getInterface();
$protocol = $binding->getAlmanacPropertyValue('protocol');
if ($protocol === 'http') {
$uris[] = 'http://'.$iface->renderDisplayAddress().'/';
} else if ($protocol === 'https' || $protocol === null) {
$uris[] = 'https://'.$iface->renderDisplayAddress().'/';
} else {
throw new Exception(
pht(
'The Almanac service for this repository has a binding to an '.
'invalid interface with an unknown protocol ("%s").',
$protocol));
}
}
shuffle($uris);
$uri = head($uris);
$domain = id(new PhutilURI(PhabricatorEnv::getURI('/')))->getDomain();
- // TODO: This call needs authentication, which is blocked by T5955.
+ $client = id(new ConduitClient($uri))
+ ->setHost($domain);
- return id(new ConduitClient($uri))
- ->setHost($domain)
- ->callMethodSynchronous($method, $params);
+ $token = PhabricatorConduitToken::loadClusterTokenForUser($user);
+ if ($token) {
+ $client->setConduitToken($token->getToken());
+ }
+
+ return $client->callMethodSynchronous($method, $params);
}
public function execute() {
return $this->executeQuery();
}
abstract protected function executeQuery();
/* -( Query Utilities )---------------------------------------------------- */
final public static function loadCommitsByIdentifiers(
array $identifiers,
DiffusionRequest $drequest) {
if (!$identifiers) {
return array();
}
$commits = array();
$commit_data = array();
$repository = $drequest->getRepository();
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
'repositoryID = %d AND commitIdentifier IN (%Ls)',
$repository->getID(),
$identifiers);
$commits = mpull($commits, null, 'getCommitIdentifier');
// Build empty commit objects for every commit, so we can show unparsed
// commits in history views (as "Importing") instead of not showing them.
// This makes the process of importing and parsing commits clearer to the
// user.
$commit_list = array();
foreach ($identifiers as $identifier) {
$commit_obj = idx($commits, $identifier);
if (!$commit_obj) {
$commit_obj = new PhabricatorRepositoryCommit();
$commit_obj->setRepositoryID($repository->getID());
$commit_obj->setCommitIdentifier($identifier);
$commit_obj->makeEphemeral();
}
$commit_list[$identifier] = $commit_obj;
}
$commits = $commit_list;
$commit_ids = array_filter(mpull($commits, 'getID'));
if ($commit_ids) {
$commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(
'commitID in (%Ld)',
$commit_ids);
$commit_data = mpull($commit_data, null, 'getCommitID');
}
foreach ($commits as $commit) {
if (!$commit->getID()) {
continue;
}
if (idx($commit_data, $commit->getID())) {
$commit->attachCommitData($commit_data[$commit->getID()]);
}
}
return $commits;
}
final public static function loadHistoryForCommitIdentifiers(
array $identifiers,
DiffusionRequest $drequest) {
if (!$identifiers) {
return array();
}
$repository = $drequest->getRepository();
$commits = self::loadCommitsByIdentifiers($identifiers, $drequest);
if (!$commits) {
return array();
}
$path = $drequest->getPath();
$conn_r = $repository->establishConnection('r');
$path_normal = DiffusionPathIDQuery::normalizePath($path);
$paths = queryfx_all(
$conn_r,
'SELECT id, path FROM %T WHERE pathHash IN (%Ls)',
PhabricatorRepository::TABLE_PATH,
array(md5($path_normal)));
$paths = ipull($paths, 'id', 'path');
$path_id = idx($paths, $path_normal);
$commit_ids = array_filter(mpull($commits, 'getID'));
$path_changes = array();
if ($path_id && $commit_ids) {
$path_changes = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE commitID IN (%Ld) AND pathID = %d',
PhabricatorRepository::TABLE_PATHCHANGE,
$commit_ids,
$path_id);
$path_changes = ipull($path_changes, null, 'commitID');
}
$history = array();
foreach ($identifiers as $identifier) {
$item = new DiffusionPathChange();
$item->setCommitIdentifier($identifier);
$commit = idx($commits, $identifier);
if ($commit) {
$item->setCommit($commit);
try {
$item->setCommitData($commit->getCommitData());
} catch (Exception $ex) {
// Ignore, commit just doesn't have data.
}
$change = idx($path_changes, $commit->getID());
if ($change) {
$item->setChangeType($change['changeType']);
$item->setFileType($change['fileType']);
}
}
$history[] = $item;
}
return $history;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 19:18 (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1127896
Default Alt Text
(23 KB)

Event Timeline