mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-19 03:50:54 +01:00
Allow users to associate SSH Public Keys with their accounts
Summary: With the sshd-vcs thing I hacked together, this will enable Phabricator to host repositories without requiring users to have SSH accounts. I also fixed "subporjects" and added an explicit ENGINE to it. Test Plan: Created, edited and deleted public keys. Attempted to add the same public key twice. Attempted to add invalid and unnamed public keys. Reviewed By: aran Reviewers: jungejason, tuomaspelkonen, aran, cadamo, codeblock CC: aran, epriestley Differential Revision: 711
This commit is contained in:
parent
ffae7b19e6
commit
8df62d5352
9 changed files with 366 additions and 2 deletions
|
@ -2,10 +2,10 @@ ALTER TABLE phabricator_project.project
|
|||
ADD subprojectPHIDs longblob NOT NULL;
|
||||
UPDATE phabricator_project.project
|
||||
SET subprojectPHIDs = '[]';
|
||||
|
||||
|
||||
CREATE TABLE phabricator_project.project_subproject (
|
||||
projectPHID varchar(64) BINARY NOT NULL,
|
||||
subprojectPHID varchar(64) BINARY NOT NULL,
|
||||
PRIMARY KEY (subprojectPHID, projectPHID),
|
||||
UNIQUE KEY (projectPHID, subprojectPHID)
|
||||
);
|
||||
) ENGINE=InnoDB;
|
12
resources/sql/patches/065.sshkeys.sql
Normal file
12
resources/sql/patches/065.sshkeys.sql
Normal file
|
@ -0,0 +1,12 @@
|
|||
CREATE TABLE phabricator_user.user_sshkey (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
userPHID varchar(64) BINARY NOT NULL,
|
||||
key (userPHID),
|
||||
name varchar(255),
|
||||
keyType varchar(255),
|
||||
keyBody varchar(32768) BINARY,
|
||||
unique key (keyBody(512)),
|
||||
keyComment varchar(255),
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB;
|
|
@ -576,6 +576,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorUserOAuthSettingsPanelController' => 'applications/people/controller/settings/panels/oauth',
|
||||
'PhabricatorUserPreferences' => 'applications/people/storage/preferences',
|
||||
'PhabricatorUserProfile' => 'applications/people/storage/profile',
|
||||
'PhabricatorUserSSHKey' => 'applications/people/storage/usersshkey',
|
||||
'PhabricatorUserSSHKeysSettingsPanelController' => 'applications/people/controller/settings/panels/sshkeys',
|
||||
'PhabricatorUserSettingsController' => 'applications/people/controller/settings',
|
||||
'PhabricatorUserSettingsPanelController' => 'applications/people/controller/settings/panels/base',
|
||||
'PhabricatorWorker' => 'infrastructure/daemon/workers/worker',
|
||||
|
@ -1094,6 +1096,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorUserOAuthSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
|
||||
'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
|
||||
'PhabricatorUserProfile' => 'PhabricatorUserDAO',
|
||||
'PhabricatorUserSSHKey' => 'PhabricatorUserDAO',
|
||||
'PhabricatorUserSSHKeysSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
|
||||
'PhabricatorUserSettingsController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorUserSettingsPanelController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
|
||||
|
|
|
@ -34,6 +34,7 @@ class PhabricatorUserSettingsController extends PhabricatorPeopleController {
|
|||
'email' => 'Email',
|
||||
// 'password' => 'Password',
|
||||
'conduit' => 'Conduit Certificate',
|
||||
'sshkeys' => 'SSH Public Keys',
|
||||
);
|
||||
|
||||
$oauth_providers = PhabricatorOAuthProvider::getAllProviders();
|
||||
|
@ -60,6 +61,9 @@ class PhabricatorUserSettingsController extends PhabricatorPeopleController {
|
|||
case 'conduit':
|
||||
$delegate = new PhabricatorUserConduitSettingsPanelController($request);
|
||||
break;
|
||||
case 'sshkeys':
|
||||
$delegate = new PhabricatorUserSSHKeysSettingsPanelController($request);
|
||||
break;
|
||||
default:
|
||||
if (empty($this->pages[$this->page])) {
|
||||
return new Aphront404Response();
|
||||
|
|
|
@ -13,6 +13,7 @@ phutil_require_module('phabricator', 'applications/people/controller/settings/pa
|
|||
phutil_require_module('phabricator', 'applications/people/controller/settings/panels/conduit');
|
||||
phutil_require_module('phabricator', 'applications/people/controller/settings/panels/email');
|
||||
phutil_require_module('phabricator', 'applications/people/controller/settings/panels/oauth');
|
||||
phutil_require_module('phabricator', 'applications/people/controller/settings/panels/sshkeys');
|
||||
phutil_require_module('phabricator', 'view/layout/sidenav');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
<?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 PhabricatorUserSSHKeysSettingsPanelController
|
||||
extends PhabricatorUserSettingsPanelController {
|
||||
|
||||
const PANEL_BASE_URI = '/settings/page/sshkeys/';
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$edit = $request->getStr('edit');
|
||||
$delete = $request->getStr('delete');
|
||||
if (!$edit && !$delete) {
|
||||
return $this->renderKeyListView();
|
||||
}
|
||||
|
||||
$id = nonempty($edit, $delete);
|
||||
|
||||
if ($id && is_numeric($id)) {
|
||||
// NOTE: Prevent editing/deleting of keys you don't own.
|
||||
$key = id(new PhabricatorUserSSHKey())->loadOneWhere(
|
||||
'userPHID = %s AND id = %d',
|
||||
$user->getPHID(),
|
||||
$id);
|
||||
if (!$key) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
} else {
|
||||
$key = new PhabricatorUserSSHKey();
|
||||
$key->setUserPHID($user->getPHID());
|
||||
}
|
||||
|
||||
if ($delete) {
|
||||
return $this->processDelete($key);
|
||||
}
|
||||
|
||||
$e_name = true;
|
||||
$e_key = true;
|
||||
$errors = array();
|
||||
$entire_key = $key->getEntireKey();
|
||||
if ($request->isFormPost()) {
|
||||
$key->setName($request->getStr('name'));
|
||||
$entire_key = $request->getStr('key');
|
||||
|
||||
if (!strlen($entire_key)) {
|
||||
$errors[] = 'You must provide an SSH Public Key.';
|
||||
$e_key = 'Required';
|
||||
} else {
|
||||
$parts = str_replace("\n", '', trim($entire_key));
|
||||
$parts = preg_split('/\s+/', $parts);
|
||||
if (count($parts) == 2) {
|
||||
$parts[] = ''; // Add an empty comment part.
|
||||
} else if (count($parts) == 3) {
|
||||
// This is the expected case.
|
||||
} else {
|
||||
if (preg_match('/private\s*key/i', $entire_key)) {
|
||||
// Try to give the user a better error message if it looks like
|
||||
// they uploaded a private key.
|
||||
$e_key = 'Invalid';
|
||||
$errors[] = 'Provide your public key, not your private key!';
|
||||
} else {
|
||||
$e_key = 'Invalid';
|
||||
$errors[] = 'Provided public key is not properly formatted.';
|
||||
}
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
list($type, $body, $comment) = $parts;
|
||||
if (!preg_match('/^ssh-dsa|ssh-rsa$/', $type)) {
|
||||
$e_key = 'Invalid';
|
||||
$errors[] = 'Public key should be "ssh-dsa" or "ssh-rsa".';
|
||||
} else {
|
||||
$key->setKeyType($type);
|
||||
$key->setKeyBody($body);
|
||||
$key->setKeyComment($comment);
|
||||
|
||||
$e_key = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strlen($key->getName())) {
|
||||
$errors[] = 'You must name this public key.';
|
||||
$e_name = 'Required';
|
||||
} else {
|
||||
$e_name = null;
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
try {
|
||||
$key->save();
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI(self::PANEL_BASE_URI);
|
||||
} catch (AphrontQueryDuplicateKeyException $ex) {
|
||||
$e_key = 'Duplicate';
|
||||
$errors[] = 'This public key is already associated with a user '.
|
||||
'account.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$error_view = null;
|
||||
if ($errors) {
|
||||
$error_view = new AphrontErrorView();
|
||||
$error_view->setTitle('Form Errors');
|
||||
$error_view->setErrors($errors);
|
||||
}
|
||||
|
||||
$is_new = !$key->getID();
|
||||
|
||||
if ($is_new) {
|
||||
$header = 'Add New SSH Public Key';
|
||||
$save = 'Add Key';
|
||||
} else {
|
||||
$header = 'Edit SSH Public Key';
|
||||
$save = 'Save Changes';
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->addHiddenInput('edit', $is_new ? 'true' : $key->getID())
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel('Name')
|
||||
->setName('name')
|
||||
->setValue($key->getName())
|
||||
->setError($e_name))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setLabel('Public Key')
|
||||
->setName('key')
|
||||
->setValue($entire_key)
|
||||
->setError($e_key))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->addCancelButton(self::PANEL_BASE_URI)
|
||||
->setValue($save));
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader($header);
|
||||
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
||||
$panel->appendChild($form);
|
||||
|
||||
return id(new AphrontNullView())
|
||||
->appendChild(
|
||||
array(
|
||||
$error_view,
|
||||
$panel,
|
||||
));
|
||||
}
|
||||
|
||||
private function renderKeyListView() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$keys = id(new PhabricatorUserSSHKey())->loadAllWhere(
|
||||
'userPHID = %s',
|
||||
$user->getPHID());
|
||||
|
||||
$rows = array();
|
||||
foreach ($keys as $key) {
|
||||
$rows[] = array(
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/settings/page/sshkeys/?edit='.$key->getID(),
|
||||
),
|
||||
phutil_escape_html($key->getName())),
|
||||
phutil_escape_html($key->getKeyComment()),
|
||||
phutil_escape_html($key->getKeyType()),
|
||||
phabricator_date($key->getDateCreated(), $user),
|
||||
phabricator_time($key->getDateCreated(), $user),
|
||||
javelin_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/settings/page/sshkeys/?delete='.$key->getID(),
|
||||
'class' => 'small grey button',
|
||||
'sigil' => 'workflow',
|
||||
),
|
||||
'Delete'),
|
||||
);
|
||||
}
|
||||
|
||||
$table = new AphrontTableView($rows);
|
||||
$table->setNoDataString("You haven't added any SSH Public Keys.");
|
||||
$table->setHeaders(
|
||||
array(
|
||||
'Name',
|
||||
'Comment',
|
||||
'Type',
|
||||
'Created',
|
||||
'Time',
|
||||
'',
|
||||
));
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'wide pri',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'right',
|
||||
'action',
|
||||
));
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->addButton(
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/settings/page/sshkeys/?edit=true',
|
||||
'class' => 'green button',
|
||||
),
|
||||
'Add New Public Key'));
|
||||
$panel->setHeader('SSH Public Keys');
|
||||
$panel->appendChild($table);
|
||||
|
||||
return $panel;
|
||||
}
|
||||
|
||||
private function processDelete(PhabricatorUserSSHKey $key) {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$name = phutil_escape_html($key->getName());
|
||||
|
||||
if ($request->isDialogFormPost()) {
|
||||
$key->delete();
|
||||
return id(new AphrontReloadResponse())
|
||||
->setURI(self::PANEL_BASE_URI);
|
||||
}
|
||||
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($user)
|
||||
->addHiddenInput('delete', $key->getID())
|
||||
->setTitle('Really delete SSH Public Key?')
|
||||
->appendChild(
|
||||
'<p>The key "<strong>'.$name.'</strong>" will be permanently deleted, '.
|
||||
'and you will not longer be able to use the corresponding private key '.
|
||||
'to authenticate.</p>')
|
||||
->addSubmitButton('Delete Public Key')
|
||||
->addCancelButton(self::PANEL_BASE_URI);
|
||||
|
||||
return id(new AphrontDialogResponse())
|
||||
->setDialog($dialog);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/response/404');
|
||||
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||
phutil_require_module('phabricator', 'aphront/response/reload');
|
||||
phutil_require_module('phabricator', 'applications/people/controller/settings/panels/base');
|
||||
phutil_require_module('phabricator', 'applications/people/storage/usersshkey');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
phutil_require_module('phabricator', 'view/control/table');
|
||||
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/text');
|
||||
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/null');
|
||||
phutil_require_module('phabricator', 'view/utils');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorUserSSHKeysSettingsPanelController.php');
|
|
@ -0,0 +1,36 @@
|
|||
<?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 PhabricatorUserSSHKey extends PhabricatorUserDAO {
|
||||
|
||||
protected $userPHID;
|
||||
protected $name;
|
||||
protected $keyType;
|
||||
protected $keyBody;
|
||||
protected $keyComment;
|
||||
|
||||
public function getEntireKey() {
|
||||
$parts = array(
|
||||
$this->getKeyType(),
|
||||
$this->getKeyBody(),
|
||||
$this->getKeyComment(),
|
||||
);
|
||||
return trim(implode(' ', $parts));
|
||||
}
|
||||
|
||||
}
|
12
src/applications/people/storage/usersshkey/__init__.php
Normal file
12
src/applications/people/storage/usersshkey/__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/people/storage/base');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorUserSSHKey.php');
|
Loading…
Reference in a new issue