1
0
Fork 0
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:
epriestley 2011-06-14 12:17:14 -07:00
parent 3ec76f5f4a
commit 1cd1e1ed18
10 changed files with 256 additions and 1 deletions

View 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);

View file

@ -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',

View file

@ -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',

View file

@ -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',
));
}
}

View 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');

View file

@ -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();
}
}

View file

@ -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');

View file

@ -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;
}

View 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');

View file

@ -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');