Some acutal conduit authentication.
This commit is contained in:
@@ -22,6 +22,10 @@ return array(
|
|||||||
// Example: "http://phabricator.example.com/"
|
// Example: "http://phabricator.example.com/"
|
||||||
'phabricator.base-uri' => null,
|
'phabricator.base-uri' => null,
|
||||||
|
|
||||||
|
// The Conduit URI for API access to this install. Normally this is just
|
||||||
|
// the 'base-uri' plus "/api/" (e.g. "http://phabricator.example.com/api/"),
|
||||||
|
// but make sure you specify 'https' if you have HTTPS configured.
|
||||||
|
'phabricator.conduit-uri' => null,
|
||||||
|
|
||||||
'phabricator.csrf-key' => '0b7ec0592e0a2829d8b71df2fa269b2c6172eca3',
|
'phabricator.csrf-key' => '0b7ec0592e0a2829d8b71df2fa269b2c6172eca3',
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ phutil_register_library_map(array(
|
|||||||
'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base',
|
'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base',
|
||||||
'PhabricatorUser' => 'applications/people/storage/user',
|
'PhabricatorUser' => 'applications/people/storage/user',
|
||||||
'PhabricatorUserDAO' => 'applications/people/storage/base',
|
'PhabricatorUserDAO' => 'applications/people/storage/base',
|
||||||
|
'PhabricatorUserSettingsController' => 'applications/people/controller/settings',
|
||||||
'PhabricatorXHProfController' => 'applications/xhprof/controller/base',
|
'PhabricatorXHProfController' => 'applications/xhprof/controller/base',
|
||||||
'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile',
|
'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile',
|
||||||
'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol',
|
'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol',
|
||||||
@@ -373,6 +374,7 @@ phutil_register_library_map(array(
|
|||||||
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
|
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
|
||||||
'PhabricatorUser' => 'PhabricatorUserDAO',
|
'PhabricatorUser' => 'PhabricatorUserDAO',
|
||||||
'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
|
'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
|
||||||
|
'PhabricatorUserSettingsController' => 'PhabricatorPeopleController',
|
||||||
'PhabricatorXHProfController' => 'PhabricatorController',
|
'PhabricatorXHProfController' => 'PhabricatorController',
|
||||||
'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController',
|
'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController',
|
||||||
'PhabricatorXHProfProfileSymbolView' => 'AphrontView',
|
'PhabricatorXHProfProfileSymbolView' => 'AphrontView',
|
||||||
|
|||||||
@@ -135,6 +135,10 @@ class AphrontDefaultApplicationConfiguration
|
|||||||
),
|
),
|
||||||
|
|
||||||
'/~/' => 'DarkConsoleController',
|
'/~/' => 'DarkConsoleController',
|
||||||
|
|
||||||
|
'/settings/' => array(
|
||||||
|
'(?:page/(?<page>[^/]+)/)?$' => 'PhabricatorUserSettingsController',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -146,4 +146,8 @@ class AphrontRequest {
|
|||||||
return id(new PhutilURI($this->getPath()))->setQueryParams($get);
|
return id(new PhutilURI($this->getPath()))->setQueryParams($get);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function isDialogFormPost() {
|
||||||
|
return $this->isFormPost() && $this->getStr('__dialog__');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,35 @@ class PhabricatorConduitAPIController
|
|||||||
|
|
||||||
$api_request = new ConduitAPIRequest($params);
|
$api_request = new ConduitAPIRequest($params);
|
||||||
|
|
||||||
|
if ($method_handler->shouldRequireAuthentication()) {
|
||||||
|
$session_key = idx($metadata, 'sessionKey');
|
||||||
|
if (!$session_key) {
|
||||||
|
$auth_okay = false;
|
||||||
|
$error_code = 'ERR-NO-CERTIFICATE';
|
||||||
|
$error_info = "This server requires authentication but your client ".
|
||||||
|
"is not configured with an authentication certificate.";
|
||||||
|
} else {
|
||||||
|
$user = new PhabricatorUser();
|
||||||
|
$session = queryfx_one(
|
||||||
|
$user->establishConnection('r'),
|
||||||
|
'SELECT * FROM %T WHERE sessionKey = %s',
|
||||||
|
PhabricatorUser::SESSION_TABLE,
|
||||||
|
$session_key);
|
||||||
|
if (!$session) {
|
||||||
|
$auth_okay = false;
|
||||||
|
$error_code = 'ERR-INVALID-SESSION';
|
||||||
|
$error_info = 'Session key is invalid.';
|
||||||
|
} else {
|
||||||
|
// TODO: Make sessions timeout.
|
||||||
|
$auth_okay = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: When we session, read connectionID from the session table.
|
||||||
|
} else {
|
||||||
|
$auth_okay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($auth_okay) {
|
||||||
try {
|
try {
|
||||||
$result = $method_handler->executeMethod($api_request);
|
$result = $method_handler->executeMethod($api_request);
|
||||||
$error_code = null;
|
$error_code = null;
|
||||||
@@ -90,6 +119,7 @@ class PhabricatorConduitAPIController
|
|||||||
$error_code = $ex->getMessage();
|
$error_code = $ex->getMessage();
|
||||||
$error_info = $method_handler->getErrorDescription($error_code);
|
$error_info = $method_handler->getErrorDescription($error_code);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$result = null;
|
$result = null;
|
||||||
$error_code = 'ERR-CONDUIT-CORE';
|
$error_code = 'ERR-CONDUIT-CORE';
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ phutil_require_module('phabricator', 'applications/conduit/controller/base');
|
|||||||
phutil_require_module('phabricator', 'applications/conduit/method/base');
|
phutil_require_module('phabricator', 'applications/conduit/method/base');
|
||||||
phutil_require_module('phabricator', 'applications/conduit/protocol/request');
|
phutil_require_module('phabricator', 'applications/conduit/protocol/request');
|
||||||
phutil_require_module('phabricator', 'applications/conduit/storage/methodcalllog');
|
phutil_require_module('phabricator', 'applications/conduit/storage/methodcalllog');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
phutil_require_module('phabricator', 'storage/queryfx');
|
||||||
phutil_require_module('phabricator', 'view/control/table');
|
phutil_require_module('phabricator', 'view/control/table');
|
||||||
phutil_require_module('phabricator', 'view/layout/panel');
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ abstract class ConduitAPIMethod {
|
|||||||
return 'ConduitAPI_'.$method_fragment.'_Method';
|
return 'ConduitAPI_'.$method_fragment.'_Method';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shouldRequireAuthentication() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static function getAPIMethodNameFromClassName($class_name) {
|
public static function getAPIMethodNameFromClassName($class_name) {
|
||||||
$match = null;
|
$match = null;
|
||||||
$is_valid = preg_match(
|
$is_valid = preg_match(
|
||||||
|
|||||||
@@ -18,6 +18,10 @@
|
|||||||
|
|
||||||
class ConduitAPI_conduit_connect_Method extends ConduitAPIMethod {
|
class ConduitAPI_conduit_connect_Method extends ConduitAPIMethod {
|
||||||
|
|
||||||
|
public function shouldRequireAuthentication() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function getMethodDescription() {
|
public function getMethodDescription() {
|
||||||
return "Connect a session-based client.";
|
return "Connect a session-based client.";
|
||||||
}
|
}
|
||||||
@@ -28,6 +32,8 @@ class ConduitAPI_conduit_connect_Method extends ConduitAPIMethod {
|
|||||||
'clientVersion' => 'required int',
|
'clientVersion' => 'required int',
|
||||||
'clientDescription' => 'optional string',
|
'clientDescription' => 'optional string',
|
||||||
'user' => 'optional string',
|
'user' => 'optional string',
|
||||||
|
'authToken' => 'optional int',
|
||||||
|
'authSignature' => 'optional string',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +53,17 @@ class ConduitAPI_conduit_connect_Method extends ConduitAPIMethod {
|
|||||||
"a Facebook host, or see ".
|
"a Facebook host, or see ".
|
||||||
"<http://www.intern.facebook.com/intern/wiki/index.php/Arcanist> for ".
|
"<http://www.intern.facebook.com/intern/wiki/index.php/Arcanist> for ".
|
||||||
"laptop instructions.",
|
"laptop instructions.",
|
||||||
|
"ERR-INVALID-USER" =>
|
||||||
|
"The username you are attempting to authenticate with is not valid.",
|
||||||
|
"ERR-INVALID-CERTIFICATE" =>
|
||||||
|
"Your authentication certificate for this server is invalid.",
|
||||||
|
"ERR-INVALID-TOKEN" =>
|
||||||
|
"The challenge token you are authenticating with is outside of the ".
|
||||||
|
"allowed time range. Either your system clock is out of whack or ".
|
||||||
|
"you're executing a replay attack.",
|
||||||
|
"ERR-NO-CERTIFICATE" =>
|
||||||
|
"This server requires authentication but your client is not ".
|
||||||
|
"configured with an authentication certificate."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,13 +72,14 @@ class ConduitAPI_conduit_connect_Method extends ConduitAPIMethod {
|
|||||||
$client = $request->getValue('client');
|
$client = $request->getValue('client');
|
||||||
$client_version = (int)$request->getValue('clientVersion');
|
$client_version = (int)$request->getValue('clientVersion');
|
||||||
$client_description = (string)$request->getValue('clientDescription');
|
$client_description = (string)$request->getValue('clientDescription');
|
||||||
|
$username = (string)$request->getValue('user');
|
||||||
|
|
||||||
// Log the connection, regardless of the outcome of checks below.
|
// Log the connection, regardless of the outcome of checks below.
|
||||||
$connection = new PhabricatorConduitConnectionLog();
|
$connection = new PhabricatorConduitConnectionLog();
|
||||||
$connection->setClient($client);
|
$connection->setClient($client);
|
||||||
$connection->setClientVersion($client_version);
|
$connection->setClientVersion($client_version);
|
||||||
$connection->setClientDescription($client_description);
|
$connection->setClientDescription($client_description);
|
||||||
$connection->setUsername((string)$request->getValue('user'));
|
$connection->setUsername($username);
|
||||||
$connection->save();
|
$connection->save();
|
||||||
|
|
||||||
switch ($client) {
|
switch ($client) {
|
||||||
@@ -80,8 +98,58 @@ class ConduitAPI_conduit_connect_Method extends ConduitAPIMethod {
|
|||||||
throw new ConduitException('ERR-UNKNOWN-CLIENT');
|
throw new ConduitException('ERR-UNKNOWN-CLIENT');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$token = $request->getValue('authToken');
|
||||||
|
$signature = $request->getValue('authSignature');
|
||||||
|
|
||||||
|
$user = id(new PhabricatorUser())->loadOneWhere(
|
||||||
|
'username = %s',
|
||||||
|
$username);
|
||||||
|
if (!$user) {
|
||||||
|
throw new ConduitException('ERR-INVALID-USER');
|
||||||
|
}
|
||||||
|
|
||||||
|
$session_key = null;
|
||||||
|
if ($token && $signature) {
|
||||||
|
if (abs($token - time()) > 60 * 15) {
|
||||||
|
throw new ConduitException('ERR-INVALID-TOKEN');
|
||||||
|
}
|
||||||
|
$valid = sha1($token.$user->getConduitCertificate());
|
||||||
|
if ($valid != $signature) {
|
||||||
|
throw new ConduitException('ERR-INVALID-CERTIFICATE');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessions = queryfx_all(
|
||||||
|
$user->establishConnection('r'),
|
||||||
|
'SELECT * FROM %T WHERE userPHID = %s AND type LIKE %>',
|
||||||
|
PhabricatorUser::SESSION_TABLE,
|
||||||
|
$user->getPHID(),
|
||||||
|
'conduit-');
|
||||||
|
|
||||||
|
$session_type = null;
|
||||||
|
|
||||||
|
$sessions = ipull($sessions, null, 'type');
|
||||||
|
for ($ii = 1; $ii <= 3; $ii++) {
|
||||||
|
if (empty($sessions['conduit-'.$ii])) {
|
||||||
|
$session_type = 'conduit-'.$ii;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$session_type) {
|
||||||
|
$sessions = isort($sessions, 'sessionStart');
|
||||||
|
$oldest = reset($sessions);
|
||||||
|
$session_type = $oldest['type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$session_key = $user->establishSession($session_type);
|
||||||
|
} else {
|
||||||
|
throw new ConduitException('ERR-NO-CERTIFICATE');
|
||||||
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'connectionID' => $connection->getID(),
|
'connectionID' => $connection->getID(),
|
||||||
|
'sessionKey' => $session_key,
|
||||||
|
'userPHID' => $user->getPHID(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,10 @@
|
|||||||
phutil_require_module('phabricator', 'applications/conduit/method/base');
|
phutil_require_module('phabricator', 'applications/conduit/method/base');
|
||||||
phutil_require_module('phabricator', 'applications/conduit/protocol/exception');
|
phutil_require_module('phabricator', 'applications/conduit/protocol/exception');
|
||||||
phutil_require_module('phabricator', 'applications/conduit/storage/connectionlog');
|
phutil_require_module('phabricator', 'applications/conduit/storage/connectionlog');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
phutil_require_module('phabricator', 'storage/queryfx');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
phutil_require_source('ConduitAPI_conduit_connect_Method.php');
|
phutil_require_source('ConduitAPI_conduit_connect_Method.php');
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
<?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 PhabricatorUserSettingsController extends PhabricatorPeopleController {
|
||||||
|
|
||||||
|
private $page;
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->page = idx($data, 'page');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
|
||||||
|
$pages = array(
|
||||||
|
// 'personal' => 'Profile',
|
||||||
|
// 'password' => 'Password',
|
||||||
|
// 'facebook' => 'Facebook Account',
|
||||||
|
'arcanist' => 'Arcanist Certificate',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (empty($pages[$this->page])) {
|
||||||
|
$this->page = key($pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
switch ($this->page) {
|
||||||
|
case 'arcanist':
|
||||||
|
|
||||||
|
if (!$request->isDialogFormPost()) {
|
||||||
|
$dialog = new AphrontDialogView();
|
||||||
|
$dialog->setUser($user);
|
||||||
|
$dialog->setTitle('Really regenerate session?');
|
||||||
|
$dialog->setSubmitURI('/settings/page/arcanist/');
|
||||||
|
$dialog->addSubmitButton('Regenerate');
|
||||||
|
$dialog->addCancelbutton('/settings/page/arcanist/');
|
||||||
|
$dialog->appendChild(
|
||||||
|
'<p>Really destroy the old certificate? Any established '.
|
||||||
|
'sessions will be terminated.');
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())
|
||||||
|
->setDialog($dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn = $user->establishConnection('w');
|
||||||
|
queryfx(
|
||||||
|
$conn,
|
||||||
|
'DELETE FROM %T WHERE userPHID = %s AND type LIKE %>',
|
||||||
|
PhabricatorUser::SESSION_TABLE,
|
||||||
|
$user->getPHID(),
|
||||||
|
'conduit');
|
||||||
|
// This implicitly regenerates the certificate.
|
||||||
|
$user->setConduitCertificate(null);
|
||||||
|
$user->save();
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI('/settings/page/arcanist/?regenerated=true');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($this->page) {
|
||||||
|
case 'arcanist':
|
||||||
|
$content = $this->renderArcanistCertificateForm();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$content = 'derp derp';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$sidenav = new AphrontSideNavView();
|
||||||
|
foreach ($pages as $page => $name) {
|
||||||
|
$sidenav->addNavItem(
|
||||||
|
phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => '/settings/page/'.$page.'/',
|
||||||
|
'class' => ($page == $this->page)
|
||||||
|
? 'aphront-side-nav-selected'
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
phutil_escape_html($name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$sidenav->appendChild($content);
|
||||||
|
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
$sidenav,
|
||||||
|
array(
|
||||||
|
'title' => 'Account Settings',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderArcanistCertificateForm() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
|
||||||
|
if ($request->getStr('regenerated')) {
|
||||||
|
$notice = new AphrontErrorView();
|
||||||
|
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
|
||||||
|
$notice->setTitle('Certificate Regenerated');
|
||||||
|
$notice->appendChild(
|
||||||
|
'<p>Your old certificate has been destroyed and you have been issued '.
|
||||||
|
'a new certificate. Sessions established under the old certificate '.
|
||||||
|
'are no longer valid.</p>');
|
||||||
|
$notice = $notice->render();
|
||||||
|
} else {
|
||||||
|
$notice = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = PhabricatorEnv::getEnvConfig('phabricator.conduit-uri');
|
||||||
|
|
||||||
|
$cert_form = new AphrontFormView();
|
||||||
|
$cert_form
|
||||||
|
->setUser($user)
|
||||||
|
->appendChild(
|
||||||
|
'<p class="aphront-form-instructions">Copy and paste this certificate '.
|
||||||
|
'into your <tt>~/.arcconfig</tt> in the "hosts" section to enable '.
|
||||||
|
'Arcanist to authenticate against this host.</p>')
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTextAreaControl())
|
||||||
|
->setLabel('Certificate')
|
||||||
|
->setHeight(AphrontFormTextAreaControl::HEIGHT_SHORT)
|
||||||
|
->setValue($user->getConduitCertificate()));
|
||||||
|
|
||||||
|
$cert = new AphrontPanelView();
|
||||||
|
$cert->setHeader('Arcanist Certificate');
|
||||||
|
$cert->appendChild($cert_form);
|
||||||
|
$cert->setWidth(AphrontPanelView::WIDTH_FORM);
|
||||||
|
|
||||||
|
$regen_form = new AphrontFormView();
|
||||||
|
$regen_form
|
||||||
|
->setUser($user)
|
||||||
|
->setWorkflow(true)
|
||||||
|
->setAction('/settings/page/arcanist/')
|
||||||
|
->appendChild(
|
||||||
|
'<p class="aphront-form-instructions">You can regenerate this '.
|
||||||
|
'certificate, which will invalidate the old certificate and create '.
|
||||||
|
'a new one.</p>')
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->setValue('Regenerate Certificate'));
|
||||||
|
|
||||||
|
$regen = new AphrontPanelView();
|
||||||
|
$regen->setHeader('Regenerate Certificate');
|
||||||
|
$regen->appendChild($regen_form);
|
||||||
|
$regen->setWidth(AphrontPanelView::WIDTH_FORM);
|
||||||
|
|
||||||
|
return $notice.$cert->render().$regen->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
27
src/applications/people/controller/settings/__init__.php
Normal file
27
src/applications/people/controller/settings/__init__.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
phutil_require_module('phabricator', 'storage/queryfx');
|
||||||
|
phutil_require_module('phabricator', 'view/dialog');
|
||||||
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/textarea');
|
||||||
|
phutil_require_module('phabricator', 'view/form/error');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/sidenav');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorUserSettingsController.php');
|
||||||
@@ -20,6 +20,8 @@ class PhabricatorUser extends PhabricatorUserDAO {
|
|||||||
|
|
||||||
const PHID_TYPE = 'USER';
|
const PHID_TYPE = 'USER';
|
||||||
|
|
||||||
|
const SESSION_TABLE = 'phabricator_session';
|
||||||
|
|
||||||
protected $phid;
|
protected $phid;
|
||||||
protected $userName;
|
protected $userName;
|
||||||
protected $realName;
|
protected $realName;
|
||||||
@@ -33,6 +35,8 @@ class PhabricatorUser extends PhabricatorUserDAO {
|
|||||||
protected $consoleVisible = 0;
|
protected $consoleVisible = 0;
|
||||||
protected $consoleTab = '';
|
protected $consoleTab = '';
|
||||||
|
|
||||||
|
protected $conduitCertificate;
|
||||||
|
|
||||||
public function getProfileImagePHID() {
|
public function getProfileImagePHID() {
|
||||||
return nonempty(
|
return nonempty(
|
||||||
$this->profileImagePHID,
|
$this->profileImagePHID,
|
||||||
@@ -56,6 +60,34 @@ class PhabricatorUser extends PhabricatorUserDAO {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function save() {
|
||||||
|
if (!$this->conduitCertificate) {
|
||||||
|
$this->conduitCertificate = $this->generateConduitCertificate();
|
||||||
|
}
|
||||||
|
return parent::save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateConduitCertificate() {
|
||||||
|
$entropy = $this->generateEntropy($bytes = 256);
|
||||||
|
$entropy = base64_encode($entropy);
|
||||||
|
$entropy = substr($entropy, 0, 255);
|
||||||
|
return $entropy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateEntropy($bytes) {
|
||||||
|
$urandom = fopen('/dev/urandom', 'r');
|
||||||
|
if (!$urandom) {
|
||||||
|
throw new Exception("Failed to open /dev/urandom!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$entropy = fread($urandom, $bytes);
|
||||||
|
if (strlen($entropy) != $bytes) {
|
||||||
|
throw new Exception("Failed to read /dev/urandom!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entropy;
|
||||||
|
}
|
||||||
|
|
||||||
public function comparePassword($password) {
|
public function comparePassword($password) {
|
||||||
$password = $this->hashPassword($password);
|
$password = $this->hashPassword($password);
|
||||||
return ($password === $this->getPasswordHash());
|
return ($password === $this->getPasswordHash());
|
||||||
@@ -105,26 +137,19 @@ class PhabricatorUser extends PhabricatorUserDAO {
|
|||||||
public function establishSession($session_type) {
|
public function establishSession($session_type) {
|
||||||
$conn_w = $this->establishConnection('w');
|
$conn_w = $this->establishConnection('w');
|
||||||
|
|
||||||
$urandom = fopen('/dev/urandom', 'r');
|
$entropy = $this->generateEntropy($bytes = 20);
|
||||||
if (!$urandom) {
|
|
||||||
throw new Exception("Failed to open /dev/urandom!");
|
|
||||||
}
|
|
||||||
|
|
||||||
$entropy = fread($urandom, 20);
|
|
||||||
if (strlen($entropy) != 20) {
|
|
||||||
throw new Exception("Failed to read /dev/urandom!");
|
|
||||||
}
|
|
||||||
|
|
||||||
$session_key = sha1($entropy);
|
$session_key = sha1($entropy);
|
||||||
queryfx(
|
queryfx(
|
||||||
$conn_w,
|
$conn_w,
|
||||||
'INSERT INTO phabricator_session '.
|
'INSERT INTO %T '.
|
||||||
'(userPHID, type, sessionKey, sessionStart)'.
|
'(userPHID, type, sessionKey, sessionStart)'.
|
||||||
' VALUES '.
|
' VALUES '.
|
||||||
'(%s, %s, %s, UNIX_TIMESTAMP()) '.
|
'(%s, %s, %s, UNIX_TIMESTAMP()) '.
|
||||||
'ON DUPLICATE KEY UPDATE '.
|
'ON DUPLICATE KEY UPDATE '.
|
||||||
'sessionKey = VALUES(sessionKey), '.
|
'sessionKey = VALUES(sessionKey), '.
|
||||||
'sessionStart = VALUES(sessionStart)',
|
'sessionStart = VALUES(sessionStart)',
|
||||||
|
self::SESSION_TABLE,
|
||||||
$this->getPHID(),
|
$this->getPHID(),
|
||||||
$session_type,
|
$session_type,
|
||||||
$session_key);
|
$session_key);
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ class AphrontDialogView extends AphrontView {
|
|||||||
),
|
),
|
||||||
'<input type="hidden" name="__form__" value="1" />'.
|
'<input type="hidden" name="__form__" value="1" />'.
|
||||||
'<input type="hidden" name="__csrf__" value="'.$csrf.'" />'.
|
'<input type="hidden" name="__csrf__" value="'.$csrf.'" />'.
|
||||||
|
'<input type="hidden" name="__dialog__" value="1" />'.
|
||||||
$hidden_inputs.
|
$hidden_inputs.
|
||||||
'<div class="aphront-dialog-head">'.
|
'<div class="aphront-dialog-head">'.
|
||||||
phutil_escape_html($this->title).
|
phutil_escape_html($this->title).
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ final class AphrontFormView extends AphrontView {
|
|||||||
private $data = array();
|
private $data = array();
|
||||||
private $encType;
|
private $encType;
|
||||||
private $user;
|
private $user;
|
||||||
|
private $workflow;
|
||||||
|
|
||||||
public function setUser(PhabricatorUser $user) {
|
public function setUser(PhabricatorUser $user) {
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
@@ -50,15 +51,21 @@ final class AphrontFormView extends AphrontView {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setWorkflow($workflow) {
|
||||||
|
$this->workflow = $workflow;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
require_celerity_resource('aphront-form-view-css');
|
require_celerity_resource('aphront-form-view-css');
|
||||||
return phutil_render_tag(
|
return javelin_render_tag(
|
||||||
'form',
|
'form',
|
||||||
array(
|
array(
|
||||||
'action' => $this->action,
|
'action' => $this->action,
|
||||||
'method' => $this->method,
|
'method' => $this->method,
|
||||||
'class' => 'aphront-form-view',
|
'class' => 'aphront-form-view',
|
||||||
'enctype' => $this->encType,
|
'enctype' => $this->encType,
|
||||||
|
'sigil' => $this->workflow ? 'workflow' : null,
|
||||||
),
|
),
|
||||||
$this->renderDataInputs().
|
$this->renderDataInputs().
|
||||||
$this->renderChildren());
|
$this->renderChildren());
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
|
|
||||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||||
phutil_require_module('phabricator', 'view/base');
|
phutil_require_module('phabricator', 'view/base');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'markup');
|
phutil_require_module('phutil', 'markup');
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ final class AphrontErrorView extends AphrontView {
|
|||||||
|
|
||||||
const SEVERITY_ERROR = 'error';
|
const SEVERITY_ERROR = 'error';
|
||||||
const SEVERITY_WARNING = 'warning';
|
const SEVERITY_WARNING = 'warning';
|
||||||
const SEVERITY_NOTE = 'note';
|
const SEVERITY_NOTICE = 'notice';
|
||||||
|
|
||||||
const WIDTH_DEFAULT = 'default';
|
const WIDTH_DEFAULT = 'default';
|
||||||
const WIDTH_WIDE = 'wide';
|
const WIDTH_WIDE = 'wide';
|
||||||
|
|||||||
@@ -28,3 +28,9 @@
|
|||||||
border: 1px solid #888800;
|
border: 1px solid #888800;
|
||||||
background: #ffffdd;
|
background: #ffffdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aphront-error-severity-notice {
|
||||||
|
border: 1px solid #000088;
|
||||||
|
background: #e3e3ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user