mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-28 16:30:59 +01:00
"arc install-certificate", server-side components
Summary: Provides a new workflow for making it non-horrible to install certificates. Basically you run "arc install-certificate" and then copy/paste a short token off a webpage and it does the ~/.arcrc edits for you. Test Plan: Installed certificates, used bad tokens, hit rate limiting. Reviewed By: aran Reviewers: aran, jungejason, tuomaspelkonen CC: aran Differential Revision: 460
This commit is contained in:
parent
3ec76f5f4a
commit
1cd1e1ed18
10 changed files with 256 additions and 1 deletions
12
resources/sql/patches/045.conduittoken.sql
Normal file
12
resources/sql/patches/045.conduittoken.sql
Normal file
|
@ -0,0 +1,12 @@
|
|||
CREATE TABLE phabricator_conduit.conduit_certificatetoken (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
userPHID VARCHAR(64) BINARY NOT NULL,
|
||||
token VARCHAR(64),
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
);
|
||||
|
||||
ALTER TABLE phabricator_conduit.conduit_certificatetoken
|
||||
ADD UNIQUE KEY (userPHID);
|
||||
ALTER TABLE phabricator_conduit.conduit_certificatetoken
|
||||
ADD UNIQUE KEY (token);
|
|
@ -82,6 +82,7 @@ phutil_register_library_map(array(
|
|||
'ConduitAPIMethod' => 'applications/conduit/method/base',
|
||||
'ConduitAPIRequest' => 'applications/conduit/protocol/request',
|
||||
'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect',
|
||||
'ConduitAPI_conduit_getcertificate_Method' => 'applications/conduit/method/conduit/getcertificate',
|
||||
'ConduitAPI_conduit_ping_Method' => 'applications/conduit/method/conduit/ping',
|
||||
'ConduitAPI_daemon_launched_Method' => 'applications/conduit/method/daemon/launched',
|
||||
'ConduitAPI_daemon_log_Method' => 'applications/conduit/method/daemon/log',
|
||||
|
@ -279,12 +280,14 @@ phutil_register_library_map(array(
|
|||
'Phabricator404Controller' => 'applications/base/controller/404',
|
||||
'PhabricatorAuthController' => 'applications/auth/controller/base',
|
||||
'PhabricatorConduitAPIController' => 'applications/conduit/controller/api',
|
||||
'PhabricatorConduitCertificateToken' => 'applications/conduit/storage/token',
|
||||
'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/connectionlog',
|
||||
'PhabricatorConduitConsoleController' => 'applications/conduit/controller/console',
|
||||
'PhabricatorConduitController' => 'applications/conduit/controller/base',
|
||||
'PhabricatorConduitDAO' => 'applications/conduit/storage/base',
|
||||
'PhabricatorConduitLogController' => 'applications/conduit/controller/log',
|
||||
'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/methodcalllog',
|
||||
'PhabricatorConduitTokenController' => 'applications/conduit/controller/token',
|
||||
'PhabricatorController' => 'applications/base/controller/base',
|
||||
'PhabricatorCountdownController' => 'applications/countdown/controller/base',
|
||||
'PhabricatorCountdownDAO' => 'applications/countdown/storage/base',
|
||||
|
@ -619,6 +622,7 @@ phutil_register_library_map(array(
|
|||
'AphrontWebpageResponse' => 'AphrontResponse',
|
||||
'CelerityResourceController' => 'AphrontController',
|
||||
'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_conduit_getcertificate_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_conduit_ping_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_daemon_launched_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_daemon_log_Method' => 'ConduitAPIMethod',
|
||||
|
@ -756,12 +760,14 @@ phutil_register_library_map(array(
|
|||
'Phabricator404Controller' => 'PhabricatorController',
|
||||
'PhabricatorAuthController' => 'PhabricatorController',
|
||||
'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
|
||||
'PhabricatorConduitCertificateToken' => 'PhabricatorConduitDAO',
|
||||
'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
|
||||
'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
|
||||
'PhabricatorConduitController' => 'PhabricatorController',
|
||||
'PhabricatorConduitDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorConduitLogController' => 'PhabricatorConduitController',
|
||||
'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO',
|
||||
'PhabricatorConduitTokenController' => 'PhabricatorConduitController',
|
||||
'PhabricatorController' => 'AphrontController',
|
||||
'PhabricatorCountdownController' => 'PhabricatorController',
|
||||
'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO',
|
||||
|
|
|
@ -84,10 +84,10 @@ class AphrontDefaultApplicationConfiguration
|
|||
'$' => 'PhabricatorConduitConsoleController',
|
||||
'method/(?P<method>[^/]+)$' => 'PhabricatorConduitConsoleController',
|
||||
'log/$' => 'PhabricatorConduitLogController',
|
||||
'token/$' => 'PhabricatorConduitTokenController',
|
||||
),
|
||||
'/api/(?P<method>[^/]+)$' => 'PhabricatorConduitAPIController',
|
||||
|
||||
|
||||
'/D(?P<id>\d+)' => 'DifferentialRevisionViewController',
|
||||
'/differential/' => array(
|
||||
'$' => 'DifferentialRevisionListController',
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PhabricatorConduitTokenController extends PhabricatorConduitController {
|
||||
|
||||
public function processRequest() {
|
||||
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$old_token = id(new PhabricatorConduitCertificateToken())
|
||||
->loadOneWhere(
|
||||
'userPHID = %s',
|
||||
$user->getPHID());
|
||||
if ($old_token) {
|
||||
$old_token->delete();
|
||||
}
|
||||
|
||||
$token = id(new PhabricatorConduitCertificateToken())
|
||||
->setUserPHID($user->getPHID())
|
||||
->setToken(sha1(Filesystem::readRandomBytes(128)))
|
||||
->save();
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader('Certificate Install Token');
|
||||
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
||||
|
||||
$panel->appendChild(
|
||||
'<p class="aphront-form-instructions">Copy and paste this token into '.
|
||||
'the prompt given to you by "arc install-certificate":</p>'.
|
||||
'<p style="padding: 0 0 1em 4em;">'.
|
||||
'<strong>'.phutil_escape_html($token->getToken()).'</strong>'.
|
||||
'</p>'.
|
||||
'<p class="aphront-form-instructions">arc will then complete the '.
|
||||
'install process for you.</p>');
|
||||
|
||||
|
||||
return $this->buildStandardPageResponse(
|
||||
$panel,
|
||||
array(
|
||||
'title' => 'Certificate Install Token',
|
||||
));
|
||||
}
|
||||
}
|
18
src/applications/conduit/controller/token/__init__.php
Normal file
18
src/applications/conduit/controller/token/__init__.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/conduit/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/conduit/storage/token');
|
||||
phutil_require_module('phabricator', 'view/layout/panel');
|
||||
|
||||
phutil_require_module('phutil', 'filesystem');
|
||||
phutil_require_module('phutil', 'markup');
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorConduitTokenController.php');
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class ConduitAPI_conduit_getcertificate_Method extends ConduitAPIMethod {
|
||||
|
||||
public function shouldRequireAuthentication() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getMethodDescription() {
|
||||
return "Retrieve certificate information for a user.";
|
||||
}
|
||||
|
||||
public function defineParamTypes() {
|
||||
return array(
|
||||
'token' => 'required string',
|
||||
);
|
||||
}
|
||||
|
||||
public function defineReturnType() {
|
||||
return 'dict<string, any>';
|
||||
}
|
||||
|
||||
public function defineErrorTypes() {
|
||||
return array(
|
||||
"ERR-BAD-TOKEN" => "Token does not exist or has expired.",
|
||||
"ERR-RATE-LIMIT" =>
|
||||
"You have made too many invalid token requests recently. Wait before ".
|
||||
"making more.",
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(ConduitAPIRequest $request) {
|
||||
|
||||
$failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(
|
||||
PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE_FAILURE,
|
||||
60 * 5);
|
||||
|
||||
if (count($failed_attempts) > 5) {
|
||||
$this->logFailure();
|
||||
throw new ConduitException('ERR-RATE-LIMIT');
|
||||
}
|
||||
|
||||
$token = $request->getValue('token');
|
||||
$info = id(new PhabricatorConduitCertificateToken())->loadOneWhere(
|
||||
'token = %s',
|
||||
trim($token));
|
||||
|
||||
if (!$info || $info->getDateCreated() < time() - (60 * 15)) {
|
||||
$this->logFailure();
|
||||
throw new ConduitException('ERR-BAD-TOKEN');
|
||||
} else {
|
||||
$log = id(new PhabricatorUserLog())
|
||||
->setActorPHID($info->getUserPHID())
|
||||
->setUserPHID($info->getUserPHID())
|
||||
->setAction(PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE)
|
||||
->save();
|
||||
}
|
||||
|
||||
$user = id(new PhabricatorUser())->loadOneWhere(
|
||||
'phid = %s',
|
||||
$info->getUserPHID());
|
||||
if (!$user) {
|
||||
throw new Exception("Certificate token points to an invalid user!");
|
||||
}
|
||||
|
||||
return array(
|
||||
'username' => $user->getUserName(),
|
||||
'certificate' => $user->getConduitCertificate(),
|
||||
);
|
||||
}
|
||||
|
||||
private function logFailure() {
|
||||
$log = id(new PhabricatorUserLog())
|
||||
->setUserPHID('-')
|
||||
->setAction(PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE_FAILURE)
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/conduit/method/base');
|
||||
phutil_require_module('phabricator', 'applications/conduit/protocol/exception');
|
||||
phutil_require_module('phabricator', 'applications/conduit/storage/token');
|
||||
phutil_require_module('phabricator', 'applications/people/storage/log');
|
||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('ConduitAPI_conduit_getcertificate_Method.php');
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PhabricatorConduitCertificateToken extends PhabricatorConduitDAO {
|
||||
|
||||
protected $userPHID;
|
||||
protected $token;
|
||||
|
||||
}
|
12
src/applications/conduit/storage/token/__init__.php
Normal file
12
src/applications/conduit/storage/token/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/conduit/storage/base');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorConduitCertificateToken.php');
|
|
@ -28,6 +28,9 @@ class PhabricatorUserLog extends PhabricatorUserDAO {
|
|||
const ACTION_ADMIN = 'admin';
|
||||
const ACTION_DISABLE = 'disable';
|
||||
|
||||
const ACTION_CONDUIT_CERTIFICATE = 'conduit-cert';
|
||||
const ACTION_CONDUIT_CERTIFICATE_FAILURE = 'conduit-cert-fail';
|
||||
|
||||
protected $actorPHID;
|
||||
protected $userPHID;
|
||||
protected $action;
|
||||
|
@ -59,6 +62,15 @@ class PhabricatorUserLog extends PhabricatorUserDAO {
|
|||
return $log;
|
||||
}
|
||||
|
||||
public static function loadRecentEventsFromThisIP($action, $timespan) {
|
||||
return id(new PhabricatorUserLog())->loadAllWhere(
|
||||
'action = %s AND remoteAddr = %s AND dateCreated > %d
|
||||
ORDER BY dateCreated DESC',
|
||||
$action,
|
||||
idx($_SERVER, 'REMOTE_ADDR'),
|
||||
time() - $timespan);
|
||||
}
|
||||
|
||||
public function save() {
|
||||
if (!$this->remoteAddr) {
|
||||
$this->remoteAddr = idx($_SERVER, 'REMOTE_ADDR');
|
||||
|
|
Loading…
Reference in a new issue