1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 00:42:41 +01:00

OAuth Server -- add controllers to RUD client authorizations and CRUD clients

Summary:
beyond the title, this diff tweaks the test console to have a bit more
functionality.  also makes a small change to CSS for AphrontFormControlMarkup,
which IMO fixes a display issue on
https://secure.phabricator.com/settings/page/profile/ where the Profile URI is
all up in the air and whatnot

I think this is missing pagination.  I am getting tired of the size though and
will add later.  See T905.

Test Plan:
viewed, updated and deleted client authorizations.  viewed, created,
updated and deleted clients

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, epriestley

Maniphest Tasks: T849, T850, T848

Differential Revision: https://secure.phabricator.com/D1683
This commit is contained in:
Bob Trahan 2012-02-22 10:21:39 -08:00
parent 5f46a61e6d
commit 3c4070a168
35 changed files with 1450 additions and 78 deletions

View file

@ -0,0 +1,3 @@
ALTER TABLE `phabricator_oauth_server`.`oauth_server_oauthserverclient`
ADD KEY `creatorPHID` (`creatorPHID`)

View file

@ -609,6 +609,15 @@ phutil_register_library_map(array(
'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view',
'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/mysql',
'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/clientauthorization',
'PhabricatorOAuthClientAuthorizationBaseController' => 'applications/oauthserver/controller/clientauthorization/base',
'PhabricatorOAuthClientAuthorizationDeleteController' => 'applications/oauthserver/controller/clientauthorization/delete',
'PhabricatorOAuthClientAuthorizationEditController' => 'applications/oauthserver/controller/clientauthorization/edit',
'PhabricatorOAuthClientAuthorizationListController' => 'applications/oauthserver/controller/clientauthorization/list',
'PhabricatorOAuthClientBaseController' => 'applications/oauthserver/controller/client/base',
'PhabricatorOAuthClientDeleteController' => 'applications/oauthserver/controller/client/delete',
'PhabricatorOAuthClientEditController' => 'applications/oauthserver/controller/client/edit',
'PhabricatorOAuthClientListController' => 'applications/oauthserver/controller/client/list',
'PhabricatorOAuthClientViewController' => 'applications/oauthserver/controller/client/view',
'PhabricatorOAuthDefaultRegistrationController' => 'applications/auth/controller/oauthregistration/default',
'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics',
'PhabricatorOAuthFailureView' => 'applications/auth/view/oauthfailure',
@ -625,6 +634,7 @@ phutil_register_library_map(array(
'PhabricatorOAuthServerAuthController' => 'applications/oauthserver/controller/auth',
'PhabricatorOAuthServerAuthorizationCode' => 'applications/oauthserver/storage/authorizationcode',
'PhabricatorOAuthServerClient' => 'applications/oauthserver/storage/client',
'PhabricatorOAuthServerController' => 'applications/oauthserver/controller/base',
'PhabricatorOAuthServerDAO' => 'applications/oauthserver/storage/base',
'PhabricatorOAuthServerScope' => 'applications/oauthserver/scope',
'PhabricatorOAuthServerTestController' => 'applications/oauthserver/controller/test',
@ -1360,6 +1370,15 @@ phutil_register_library_map(array(
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
'PhabricatorOAuthClientAuthorization' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthClientAuthorizationBaseController' => 'PhabricatorOAuthServerController',
'PhabricatorOAuthClientAuthorizationDeleteController' => 'PhabricatorOAuthClientAuthorizationBaseController',
'PhabricatorOAuthClientAuthorizationEditController' => 'PhabricatorOAuthClientAuthorizationBaseController',
'PhabricatorOAuthClientAuthorizationListController' => 'PhabricatorOAuthClientAuthorizationBaseController',
'PhabricatorOAuthClientBaseController' => 'PhabricatorOAuthServerController',
'PhabricatorOAuthClientDeleteController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthClientEditController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthClientListController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthClientViewController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController',
'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
'PhabricatorOAuthFailureView' => 'AphrontView',
@ -1374,8 +1393,9 @@ phutil_register_library_map(array(
'PhabricatorOAuthServerAuthController' => 'PhabricatorAuthController',
'PhabricatorOAuthServerAuthorizationCode' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthServerClient' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthServerController' => 'PhabricatorController',
'PhabricatorOAuthServerDAO' => 'PhabricatorLiskDAO',
'PhabricatorOAuthServerTestController' => 'PhabricatorAuthController',
'PhabricatorOAuthServerTestController' => 'PhabricatorOAuthServerController',
'PhabricatorOAuthServerTokenController' => 'PhabricatorAuthController',
'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
'PhabricatorObjectGraph' => 'AbstractDirectedGraph',

View file

@ -159,8 +159,23 @@ class AphrontDefaultApplicationConfiguration
'/oauthserver/' => array(
'auth/' => 'PhabricatorOAuthServerAuthController',
'token/' => 'PhabricatorOAuthServerTokenController',
'test/' => 'PhabricatorOAuthServerTestController',
'token/' => 'PhabricatorOAuthServerTokenController',
'clientauthorization/' => array(
'$' => 'PhabricatorOAuthClientAuthorizationListController',
'delete/(?P<phid>[^/]+)/' =>
'PhabricatorOAuthClientAuthorizationDeleteController',
'edit/(?P<phid>[^/]+)/' =>
'PhabricatorOAuthClientAuthorizationEditController',
),
'client/' => array(
'$' => 'PhabricatorOAuthClientListController',
'create/$' => 'PhabricatorOAuthClientEditController',
'delete/(?P<phid>[^/]+)/$' =>
'PhabricatorOAuthClientDeleteController',
'edit/(?P<phid>[^/]+)/$' => 'PhabricatorOAuthClientEditController',
'view/(?P<phid>[^/]+)/$' => 'PhabricatorOAuthClientViewController',
),
),
'/xhprof/' => array(

View file

@ -21,15 +21,28 @@
*/
class Aphront403Response extends AphrontWebpageResponse {
private $forbiddenText;
public function setForbiddenText($text) {
$this->forbiddenText = $text;
return $this;
}
private function getForbiddenText() {
return $this->forbiddenText;
}
public function getHTTPResponseCode() {
return 403;
}
public function buildResponseString() {
$forbidden_text = $this->getForbiddenText();
if (!$forbidden_text) {
$forbidden_text =
'You do not have privileges to access the requested page.';
}
$failure = new AphrontRequestFailureView();
$failure->setHeader('403 Forbidden');
$failure->appendChild(
'<p>You do not have privileges to access the requested page.</p>');
$failure->appendChild('<p>'.$forbidden_text.'</p>');
$view = new PhabricatorStandardPageView();
$view->setTitle('403 Forbidden');

View file

@ -54,9 +54,7 @@ extends PhabricatorAuthController {
$return_auth_code = true;
$unguarded_write = AphrontWriteGuard::beginScopedUnguardedWrites();
} else if ($request->isFormPost()) {
// TODO -- T848 (add scope to Phabricator OAuth)
// should have some $scope based off of user submission here...!
$scope = array(PhabricatorOAuthServerScope::SCOPE_WHOAMI => 1);
$scope = PhabricatorOAuthServerScope::getScopesFromRequest($request);
$server->authorizeClient($scope);
$return_auth_code = true;
$unguarded_write = null;
@ -107,21 +105,28 @@ extends PhabricatorAuthController {
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->setHeader($title);
// TODO -- T848 (add scope to Phabricator OAuth)
// generally inform user what this means as this fleshes out
$description =
"Do want to authorize {$name} to access your ".
"Phabricator account data?";
$desired_scopes = array(
PhabricatorOAuthServerScope::SCOPE_WHOAMI => 1,
PhabricatorOAuthServerScope::SCOPE_OFFLINE_ACCESS => 1
);
$form = id(new AphrontFormView())
->setUser($current_user)
->appendChild(
id(new AphrontFormStaticControl())
->setValue($description))
->setValue($description)
)
->appendChild(
PhabricatorOAuthServerScope::getCheckboxControl()
)
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Authorize')
->addCancelButton('/'));
->addCancelButton('/')
);
// TODO -- T889 (make "cancel" do something more sensible)
$panel->appendChild($form);

View file

@ -0,0 +1,84 @@
<?php
/*
* Copyright 2012 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.
*/
abstract class PhabricatorOAuthServerController
extends PhabricatorController {
public function buildStandardPageResponse($view, array $data) {
$user = $this->getRequest()->getUser();
$page = $this->buildStandardPageView();
$page->setApplicationName('OAuth Server');
$page->setBaseURI('/oauthserver/');
$page->setTitle(idx($data, 'title'));
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/oauthserver/'));
$nav->addLabel('Client Authorizations');
$nav->addFilter('clientauthorization',
'My Authorizations');
$nav->addSpacer();
$nav->addLabel('Clients');
$nav->addFilter('client/create',
'Create Client');
foreach ($this->getExtraClientFilters() as $filter) {
$nav->addFilter($filter['url'],
$filter['label']);
}
$nav->addFilter('client',
'My Clients');
$nav->selectFilter($this->getFilter(),
'clientauthorization');
$nav->appendChild($view);
$page->appendChild($nav);
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
protected function getFilter() {
return 'clientauthorization';
}
protected function getExtraClientFilters() {
return array();
}
protected function getHighlightPHIDs() {
$phids = array();
$request = $this->getRequest();
$edited = $request->getStr('edited');
$new = $request->getStr('new');
if ($edited) {
$phids[$edited] = $edited;
}
if ($new) {
$phids[$new] = $new;
}
return $phids;
}
protected function buildErrorView($error_message) {
$error = new AphrontErrorView();
$error->setSeverity(AphrontErrorView::SEVERITY_ERROR);
$error->setTitle($error_message);
return $error;
}
}

View file

@ -0,0 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/webpage');
phutil_require_module('phabricator', 'applications/base/controller/base');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/layout/sidenavfilter');
phutil_require_module('phutil', 'parser/uri');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthServerController.php');

View file

@ -0,0 +1,41 @@
<?php
/*
* Copyright 2012 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.
*/
/**
* @group oauthserver
*/
abstract class PhabricatorOAuthClientBaseController
extends PhabricatorOAuthServerController {
private $clientPHID;
protected function getClientPHID() {
return $this->clientPHID;
}
private function setClientPHID($phid) {
$this->clientPHID = $phid;
return $this;
}
public function shouldRequireLogin() {
return true;
}
public function willProcessRequest(array $data) {
$this->setClientPHID(idx($data, 'phid'));
}
}

View file

@ -0,0 +1,14 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/oauthserver/controller/base');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthClientBaseController.php');

View file

@ -0,0 +1,65 @@
<?php
/*
* Copyright 2012 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.
*/
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientDeleteController
extends PhabricatorOAuthClientBaseController {
public function processRequest() {
$phid = $this->getClientPHID();
$title = 'Delete OAuth Client';
$request = $this->getRequest();
$current_user = $request->getUser();
$client = id(new PhabricatorOAuthServerClient())
->loadOneWhere('phid = %s',
$phid);
if (empty($client)) {
return new Aphront404Response();
}
if ($client->getCreatorPHID() != $current_user->getPHID()) {
$message = 'Access denied to client with phid '.$phid.'. '.
'Only the user who created the client has permission to '.
'delete the client.';
return id(new Aphront403Response())
->setForbiddenText($message);
}
if ($request->isFormPost()) {
$client->delete();
return id(new AphrontRedirectResponse())
->setURI('/oauthserver/client/?deleted=1');
}
$client_name = phutil_escape_html($client->getName());
$title .= ' '.$client_name;
$dialog = new AphrontDialogView();
$dialog->setUser($current_user);
$dialog->setTitle($title);
$dialog->appendChild(
'<p>Are you sure you want to delete this client?</p>'
);
$dialog->addSubmitButton();
$dialog->addCancelButton($client->getEditURI());
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/403');
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', 'applications/oauthserver/controller/client/base');
phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
phutil_require_module('phabricator', 'view/dialog');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthClientDeleteController.php');

View file

@ -0,0 +1,197 @@
<?php
/*
* Copyright 2012 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.
*/
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientEditController
extends PhabricatorOAuthClientBaseController {
private $isEdit;
protected function isClientEdit() {
return $this->isEdit;
}
private function setIsClientEdit($is_edit) {
$this->isEdit = (bool) $is_edit;
return $this;
}
protected function getExtraClientFilters() {
if ($this->isClientEdit()) {
$filters = array(
array('url' => $this->getFilter(),
'label' => 'Edit Client')
);
} else {
$filters = array();
}
return $filters;
}
public function getFilter() {
if ($this->isClientEdit()) {
$filter = 'client/edit/'.$this->getClientPHID();
} else {
$filter = 'client/create';
}
return $filter;
}
public function processRequest() {
$request = $this->getRequest();
$current_user = $request->getUser();
$error = null;
$bad_redirect = false;
$phid = $this->getClientPHID();
// if we have a phid, then we're editing
$this->setIsClientEdit($phid);
if ($this->isClientEdit()) {
$client = id(new PhabricatorOAuthServerClient())
->loadOneWhere('phid = %s',
$phid);
$title = 'Edit OAuth Client';
// validate the client
if (empty($client)) {
return new Aphront404Response();
}
if ($client->getCreatorPHID() != $current_user->getPHID()) {
$message = 'Access denied to edit client with id '.$phid.'. '.
'Only the user who created the client has permission to '.
'edit the client.';
return id(new Aphront403Response())
->setForbiddenText($message);
}
$submit_button = 'Save OAuth Client';
// new client - much simpler
} else {
$client = new PhabricatorOAuthServerClient();
$title = 'Create OAuth Client';
$submit_button = 'Create OAuth Client';
}
if ($request->isFormPost()) {
$redirect_uri = $request->getStr('redirect_uri');
$client->setName($request->getStr('name'));
$client->setRedirectURI($redirect_uri);
$client->setSecret(Filesystem::readRandomCharacters(32));
$client->setCreatorPHID($current_user->getPHID());
$uri = new PhutilURI($redirect_uri);
if (!$this->validateRedirectURI($uri)) {
$error = new AphrontErrorView();
$error->setSeverity(AphrontErrorView::SEVERITY_ERROR);
$error->setTitle(
'Redirect URI must be a fully qualified domain name.'
);
$bad_redirect = true;
} else {
$client->save();
if ($this->isClientEdit()) {
return id(new AphrontRedirectResponse())
->setURI('/oauthserver/client/?edited='.$phid);
} else {
return id(new AphrontRedirectResponse())
->setURI('/oauthserver/client/?new='.$phid);
}
}
}
$panel = new AphrontPanelView();
if ($this->isClientEdit()) {
$delete_button = phutil_render_tag(
'a',
array(
'href' => $client->getDeleteURI(),
'class' => 'grey button',
),
'Delete OAuth Client');
$panel->addButton($delete_button);
}
$panel->setHeader($title);
$form = id(new AphrontFormView())
->setUser($current_user)
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setValue($client->getName())
);
if ($this->isClientEdit()) {
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('ID')
->setValue($phid)
)
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Secret')
->setValue($client->getSecret())
);
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Redirect URI')
->setName('redirect_uri')
->setValue($client->getRedirectURI())
->setError($bad_redirect)
);
if ($this->isClientEdit()) {
$created = phabricator_datetime($client->getDateCreated(),
$current_user);
$updated = phabricator_datetime($client->getDateModified(),
$current_user);
$form
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Created')
->setValue($created)
)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Last Updated')
->setValue($updated)
);
}
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($submit_button)
);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
array($error,
$panel
),
array('title' => $title)
);
}
private function validateRedirectURI(PhutilURI $uri) {
if (PhabricatorEnv::isValidRemoteWebResource($uri)) {
if ($uri->getDomain()) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,29 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/403');
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/oauthserver/controller/client/base');
phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/static');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/text');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'parser/uri');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthClientEditController.php');

View file

@ -0,0 +1,140 @@
<?php
/*
* Copyright 2012 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.
*/
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientListController
extends PhabricatorOAuthClientBaseController {
public function getFilter() {
return 'client';
}
public function processRequest() {
$title = 'OAuth Clients';
$request = $this->getRequest();
$current_user = $request->getUser();
$clients = id(new PhabricatorOAuthServerClient())
->loadAllWhere('creatorPHID = %s',
$current_user->getPHID());
$rows = array();
$rowc = array();
$highlight = $this->getHighlightPHIDs();
foreach ($clients as $client) {
$row = array(
phutil_render_tag(
'a',
array(
'href' => $client->getViewURI(),
),
phutil_escape_html($client->getName())
),
$client->getPHID(),
phutil_render_tag(
'a',
array(
'href' => $client->getRedirectURI(),
),
phutil_escape_html($client->getRedirectURI())
),
phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => $client->getEditURI(),
),
'Edit'
),
);
$rows[] = $row;
if (isset($highlight[$client->getPHID()])) {
$rowc[] = 'highlighted';
} else {
$rowc[] = '';
}
}
$panel = $this->buildClientList($rows, $rowc, $title);
return $this->buildStandardPageResponse(
array($this->getNoticeView(),
$panel),
array('title' => $title)
);
}
private function buildClientList($rows, $rowc, $title) {
$table = new AphrontTableView($rows);
$table->setRowClasses($rowc);
$table->setHeaders(
array(
'Client',
'ID',
'Redirect URI',
'',
));
$table->setColumnClasses(
array(
'',
'',
'',
'action',
));
if (empty($rows)) {
$table->setNoDataString(
'You have not created any clients for this OAuthServer.'
);
}
$panel = new AphrontPanelView();
$panel->appendChild($table);
$panel->setHeader($title);
return $panel;
}
private function getNoticeView() {
$edited = $this->getRequest()->getStr('edited');
$new = $this->getRequest()->getStr('new');
$deleted = $this->getRequest()->getBool('deleted');
if ($edited) {
$edited = phutil_escape_html($edited);
$title = 'Successfully edited client with id '.$edited.'.';
} else if ($new) {
$new = phutil_escape_html($new);
$title = 'Successfully created client with id '.$new.'.';
} else if ($deleted) {
$title = 'Successfully deleted client.';
} else {
$title = null;
}
if ($title) {
$view = new AphrontErrorView();
$view->setTitle($title);
$view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
} else {
$view = null;
}
return $view;
}
}

View file

@ -0,0 +1,19 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/oauthserver/controller/client/base');
phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthClientListController.php');

View file

@ -0,0 +1,134 @@
<?php
/*
* Copyright 2012 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.
*/
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientViewController
extends PhabricatorOAuthClientBaseController {
protected function getFilter() {
return 'client/view/'.$this->getClientPHID();
}
protected function getExtraClientFilters() {
return array(
array('url' => $this->getFilter(),
'label' => 'View Client')
);
}
public function processRequest() {
$request = $this->getRequest();
$current_user = $request->getUser();
$error = null;
$phid = $this->getClientPHID();
$client = id(new PhabricatorOAuthServerClient())
->loadOneWhere('phid = %s',
$phid);
$title = 'View OAuth Client';
// validate the client
if (empty($client)) {
$message = 'No client found with id '.$phid.'.';
return $this->buildStandardPageResponse(
$this->buildErrorView($message),
array('title' => $title)
);
}
$panel = new AphrontPanelView();
$panel->setHeader($title);
$form = id(new AphrontFormView())
->setUser($current_user)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Name')
->setValue($client->getName())
)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('ID')
->setValue($phid)
);
if ($current_user->getPHID() == $client->getCreatorPHID()) {
$form
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Secret')
->setValue($client->getSecret())
);
}
$form
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Redirect URI')
->setValue($client->getRedirectURI())
);
$created = phabricator_datetime($client->getDateCreated(),
$current_user);
$updated = phabricator_datetime($client->getDateModified(),
$current_user);
$form
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Created')
->setValue($created)
)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Last Updated')
->setValue($updated)
);
$panel->appendChild($form);
$admin_panel = null;
if ($client->getCreatorPHID() == $current_user->getPHID()) {
$edit_button = phutil_render_tag(
'a',
array(
'href' => $client->getEditURI(),
'class' => 'grey button',
),
'Edit OAuth Client');
$panel->addButton($edit_button);
$create_authorization_form = id(new AphrontFormView())
->setUser($current_user)
->addHiddenInput('action', 'testclientauthorization')
->addHiddenInput('client_phid', $phid)
->setAction('/oauthserver/test/')
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Create Scopeless Test Authorization')
);
$admin_panel = id(new AphrontPanelView())
->setHeader('Admin Tools')
->appendChild($create_authorization_form);
}
return $this->buildStandardPageResponse(
array($error,
$panel,
$admin_panel
),
array('title' => $title)
);
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/oauthserver/controller/client/base');
phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/static');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthClientViewController.php');

View file

@ -0,0 +1,41 @@
<?php
/*
* Copyright 2012 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.
*/
/**
* @group oauthserver
*/
abstract class PhabricatorOAuthClientAuthorizationBaseController
extends PhabricatorOAuthServerController {
private $authorizationPHID;
protected function getAuthorizationPHID() {
return $this->authorizationPHID;
}
private function setAuthorizationPHID($phid) {
$this->authorizationPHID = $phid;
return $this;
}
public function shouldRequireLogin() {
return true;
}
public function willProcessRequest(array $data) {
$this->setAuthorizationPHID(idx($data, 'phid'));
}
}

View file

@ -0,0 +1,14 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/oauthserver/controller/base');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthClientAuthorizationBaseController.php');

View file

@ -0,0 +1,75 @@
<?php
/*
* Copyright 2012 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.
*/
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientAuthorizationDeleteController
extends PhabricatorOAuthClientAuthorizationBaseController {
public function processRequest() {
$phid = $this->getAuthorizationPHID();
$title = 'Delete OAuth Client Authorization';
$request = $this->getRequest();
$current_user = $request->getUser();
$authorization = id(new PhabricatorOAuthClientAuthorization())
->loadOneWhere('phid = %s',
$phid);
if (empty($authorization)) {
return new Aphront404Response();
}
if ($authorization->getUserPHID() != $current_user->getPHID()) {
$message = 'Access denied to client authorization with phid '.$phid.'. '.
'Only the user who authorized the client has permission to '.
'delete the authorization.';
return id(new Aphront403Response())
->setForbiddenText($message);
}
if ($request->isFormPost()) {
$authorization->delete();
return id(new AphrontRedirectResponse())
->setURI('/oauthserver/clientauthorization/?notice=deleted');
}
$client_phid = $authorization->getClientPHID();
$client = id(new PhabricatorOAuthServerClient())
->loadOneWhere('phid = %s',
$client_phid);
if ($client) {
$client_name = phutil_escape_html($client->getName());
$title .= ' for '.$client_name;
} else {
// the client does not exist so token is dead already (but
// let's let the user clean this up anyway in that case)
$client_name = '';
}
$dialog = new AphrontDialogView();
$dialog->setUser($current_user);
$dialog->setTitle($title);
$dialog->appendChild(
'<p>Are you sure you want to delete this client authorization?</p>'
);
$dialog->addSubmitButton();
$dialog->addCancelButton($authorization->getEditURI());
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -0,0 +1,22 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/403');
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', 'applications/oauthserver/controller/clientauthorization/base');
phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
phutil_require_module('phabricator', 'applications/oauthserver/storage/clientauthorization');
phutil_require_module('phabricator', 'view/dialog');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthClientAuthorizationDeleteController.php');

View file

@ -0,0 +1,114 @@
<?php
/*
* Copyright 2012 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.
*/
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientAuthorizationEditController
extends PhabricatorOAuthClientAuthorizationBaseController {
public function processRequest() {
$phid = $this->getAuthorizationPHID();
$title = 'Edit OAuth Client Authorization';
$request = $this->getRequest();
$current_user = $request->getUser();
$authorization = id(new PhabricatorOAuthClientAuthorization())
->loadOneWhere('phid = %s',
$phid);
if (empty($authorization)) {
return new Aphront404Response();
}
if ($authorization->getUserPHID() != $current_user->getPHID()) {
$message = 'Access denied to client authorization with phid '.$phid.'. '.
'Only the user who authorized the client has permission to '.
'edit the authorization.';
return id(new Aphront403Response())
->setForbiddenText($message);
}
if ($request->isFormPost()) {
$scopes = PhabricatorOAuthServerScope::getScopesFromRequest($request);
$authorization->setScope($scopes);
$authorization->save();
return id(new AphrontRedirectResponse())
->setURI('/oauthserver/clientauthorization/?edited='.$phid);
}
$client_phid = $authorization->getClientPHID();
$client = id(new PhabricatorOAuthServerClient())
->loadOneWhere('phid = %s',
$client_phid);
$created = phabricator_datetime($authorization->getDateCreated(),
$current_user);
$updated = phabricator_datetime($authorization->getDateModified(),
$current_user);
$panel = new AphrontPanelView();
$delete_button = phutil_render_tag(
'a',
array(
'href' => $authorization->getDeleteURI(),
'class' => 'grey button',
),
'Delete OAuth Client Authorization');
$panel->addButton($delete_button);
$panel->setHeader($title);
$form = id(new AphrontFormView())
->setUser($current_user)
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel('Client')
->setValue(
phutil_render_tag(
'a',
array(
'href' => $client->getViewURI(),
),
phutil_escape_html($client->getName())))
)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Created')
->setValue($created)
)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Last Updated')
->setValue($updated)
)
->appendChild(
PhabricatorOAuthServerScope::getCheckboxControl(
$authorization->getScope()
)
)
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save OAuth Client Authorization')
->addCancelButton('/oauthserver/clientauthorization/')
);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
$panel,
array('title' => $title)
);
}
}

View file

@ -0,0 +1,27 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/403');
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/oauthserver/controller/clientauthorization/base');
phutil_require_module('phabricator', 'applications/oauthserver/scope');
phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
phutil_require_module('phabricator', 'applications/oauthserver/storage/clientauthorization');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/markup');
phutil_require_module('phabricator', 'view/form/control/static');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthClientAuthorizationEditController.php');

View file

@ -0,0 +1,161 @@
<?php
/*
* Copyright 2012 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.
*/
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientAuthorizationListController
extends PhabricatorOAuthClientAuthorizationBaseController {
protected function getFilter() {
return 'clientauthorization';
}
public function processRequest() {
$title = 'OAuth Client Authorizations';
$request = $this->getRequest();
$current_user = $request->getUser();
$authorizations = id(new PhabricatorOAuthClientAuthorization())
->loadAllWhere('userPHID = %s',
$current_user->getPHID());
$client_authorizations = mpull($authorizations, null, 'getClientPHID');
$client_phids = array_keys($client_authorizations);
if ($client_phids) {
$clients = id(new PhabricatorOAuthServerClient())
->loadAllWhere('phid in (%Ls)',
$client_phids);
} else {
$clients = array();
}
$client_dict = mpull($clients, null, 'getPHID');
$rows = array();
$rowc = array();
$highlight = $this->getHighlightPHIDs();
foreach ($client_authorizations as $client_phid => $authorization) {
$client = $client_dict[$client_phid];
$created = phabricator_datetime($authorization->getDateCreated(),
$current_user);
$updated = phabricator_datetime($authorization->getDateModified(),
$current_user);
$row = array(
phutil_render_tag(
'a',
array(
'href' => $client->getViewURI(),
),
phutil_escape_html($client->getName())
),
phutil_render_tag(
'a',
array(
'href' => 'TODO - link to scope about',
),
$authorization->getScopeString()
),
phabricator_datetime(
$authorization->getDateCreated(),
$current_user
),
phabricator_datetime(
$authorization->getDateModified(),
$current_user
),
phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => $authorization->getEditURI(),
),
'Edit'
),
);
$rows[] = $row;
if (isset($highlight[$authorization->getPHID()])) {
$rowc[] = 'highlighted';
} else {
$rowc[] = '';
}
}
$panel = $this->buildClientAuthorizationList($rows, $rowc, $title);
return $this->buildStandardPageResponse(
array($this->getNoticeView(),
$panel),
array('title' => $title)
);
}
private function buildClientAuthorizationList($rows, $rowc, $title) {
$table = new AphrontTableView($rows);
$table->setRowClasses($rowc);
$table->setHeaders(
array(
'Client',
'Scope',
'Created',
'Updated',
'',
));
$table->setColumnClasses(
array(
'wide pri',
'',
'',
'',
'action',
));
if (empty($rows)) {
$table->setNoDataString(
'You have not authorized any clients for this OAuthServer.'
);
}
$panel = new AphrontPanelView();
$panel->appendChild($table);
$panel->setHeader($title);
return $panel;
}
private function getNoticeView() {
$edited = $this->getRequest()->getStr('edited');
$deleted = $this->getRequest()->getBool('deleted');
if ($edited) {
$edited = phutil_escape_html($edited);
$title = 'Successfully edited client authorization.';
} else if ($deleted) {
$title = 'Successfully deleted client authorization.';
} else {
$title = null;
}
if ($title) {
$view = new AphrontErrorView();
$view->setTitle($title);
$view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
} else {
$view = null;
}
return $view;
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/oauthserver/controller/clientauthorization/base');
phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
phutil_require_module('phabricator', 'applications/oauthserver/storage/clientauthorization');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOAuthClientAuthorizationListController.php');

View file

@ -20,69 +20,50 @@
* @group oauthserver
*/
final class PhabricatorOAuthServerTestController
extends PhabricatorAuthController {
extends PhabricatorOAuthServerController {
public function shouldRequireLogin() {
return true;
}
public function shouldRequireAdmin() {
return true;
}
public function processRequest() {
$request = $this->getRequest();
$current_user = $request->getUser();
$server = new PhabricatorOAuthServer($current_user);
$forms = array();
$form = id(new AphrontFormView())
->setUser($current_user)
->appendChild(
id(new AphrontFormStaticControl())
->setValue('Create Test Client'))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setValue(''))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Redirect URI')
->setName('redirect_uri')
->setValue(''))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Create Client'));
$forms[] = $form;
$result = array();
if ($request->isFormPost()) {
$name = $request->getStr('name');
$redirect_uri = $request->getStr('redirect_uri');
$secret = Filesystem::readRandomCharacters(32);
$client = new PhabricatorOAuthServerClient();
$client->setName($name);
$client->setSecret($secret);
$client->setCreatorPHID($current_user->getPHID());
$client->setRedirectURI($redirect_uri);
$client->save();
$id = $client->getID();
$phid = $client->getPHID();
$name = phutil_escape_html($name);
$panels = array();
$results = array();
$results[] = "New client named {$name} with secret {$secret}.";
$results[] = "Client has id {$id} and phid {$phid}.";
$result = implode('<br />', $results);
}
$title = 'Test OAuthServer Stuff';
$panel = new AphrontPanelView();
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->setHeader($title);
$panel->appendChild($result);
$panel->appendChild($forms);
return $this->buildStandardPageResponse(
$panel,
array('title' => $title));
if ($request->isFormPost()) {
$action = $request->getStr('action');
switch ($action) {
case 'testclientauthorization':
$user_phid = $current_user->getPHID();
$client_phid = $request->getStr('client_phid');
$client = id(new PhabricatorOAuthServerClient)
->loadOneWhere('phid = %s', $client_phid);
if (!$client) {
throw new Exception('Failed to load client!');
}
if ($client->getCreatorPHID() != $user_phid ||
$current_user->getPHID() != $user_phid) {
throw new Exception(
'Only allowed to make test data for yourself '.
'for clients you own!'
);
}
// blankclientauthorizations don't get scope
$scope = array();
$server->setUser($current_user);
$server->setClient($client);
$authorization = $server->authorizeClient($scope);
return id(new AphrontRedirectResponse())
->setURI('/oauthserver/clientauthorization/?edited='.
$authorization->getPHID());
break;
default:
break;
}
}
}
}

View file

@ -6,17 +6,11 @@
phutil_require_module('phabricator', 'applications/auth/controller/base');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/oauthserver/controller/base');
phutil_require_module('phabricator', 'applications/oauthserver/server');
phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/static');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/text');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');

View file

@ -34,4 +34,48 @@ final class PhabricatorOAuthServerScope {
);
}
static public function getCheckboxControl($current_scopes) {
$scopes = self::getScopesDict();
$scope_keys = array_keys($scopes);
sort($scope_keys);
$checkboxes = new AphrontFormCheckboxControl();
foreach ($scope_keys as $scope) {
$checkboxes->addCheckbox(
$name = $scope,
$value = 1,
$label = self::getCheckboxLabel($scope),
$checked = isset($current_scopes[$scope])
);
}
$checkboxes->setLabel('Scope');
return $checkboxes;
}
static private function getCheckboxLabel($scope) {
$label = null;
switch ($scope) {
case self::SCOPE_OFFLINE_ACCESS:
$label = 'Make access tokens granted to this client never expire.';
break;
case self::SCOPE_WHOAMI:
$label = 'Read access to Conduit method user.whoami.';
break;
}
return $label;
}
static public function getScopesFromRequest(AphrontRequest $request) {
$scopes = self::getScopesDict();
$requested_scopes = array();
foreach ($scopes as $scope => $bit) {
if ($request->getBool($scope)) {
$requested_scopes[$scope] = 1;
}
}
return $requested_scopes;
}
}

View file

@ -6,5 +6,7 @@
phutil_require_module('phabricator', 'view/form/control/checkbox');
phutil_require_source('PhabricatorOAuthServerScope.php');

View file

@ -108,6 +108,8 @@ final class PhabricatorOAuthServer {
$authorization->setClientPHID($this->getClient()->getPHID());
$authorization->setScope($scope);
$authorization->save();
return $authorization;
}
/**

View file

@ -29,6 +29,18 @@ extends PhabricatorOAuthServerDAO {
protected $redirectURI;
protected $creatorPHID;
public function getEditURI() {
return '/oauthserver/client/edit/'.$this->getPHID().'/';
}
public function getViewURI() {
return '/oauthserver/client/view/'.$this->getPHID().'/';
}
public function getDeleteURI() {
return '/oauthserver/client/delete/'.$this->getPHID().'/';
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,

View file

@ -28,6 +28,21 @@ extends PhabricatorOAuthServerDAO {
protected $clientPHID;
protected $scope;
public function getEditURI() {
return '/oauthserver/clientauthorization/edit/'.$this->getPHID().'/';
}
public function getDeleteURI() {
return '/oauthserver/clientauthorization/delete/'.$this->getPHID().'/';
}
public function getScopeString() {
$scope = $this->getScope();
$scopes = array_keys($scope);
sort($scopes);
return implode(', ', $scopes);
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,

View file

@ -95,7 +95,10 @@ final class AphrontPanelView extends AphrontView {
implode(" ", $this->buttons).
'</div>';
}
$header_elements =
'<div class="aphront-panel-header">'.
$buttons.$header.$caption.
'</div>';
$table = $this->renderChildren();
require_celerity_resource('aphront-panel-view-css');
@ -112,7 +115,7 @@ final class AphrontPanelView extends AphrontView {
'class' => implode(' ', $classes),
'id' => $this->id,
),
$buttons.$header.$caption.$table);
$header_elements.$table);
}
}

View file

@ -99,7 +99,8 @@
}
.aphront-form-control-static .aphront-form-input {
.aphront-form-control-static .aphront-form-input,
.aphront-form-control-markup .aphront-form-input {
padding-top: 4px;
font-size: 13px;
}

View file

@ -11,16 +11,20 @@
margin: 1em 2em;
}
.aphront-panel-view .aphront-panel-header {
margin: 0 0 1em 0;
}
.aphront-panel-view h1 {
font-size: 14px;
font-weight: bold;
padding: 2px 0 8px;
padding: 4px 0 0 0;
}
.aphront-panel-view-caption {
font-size: 11px;
color: #666666;
margin-top: -0.75em;
margin-top: -0.1em;
margin-bottom: 0.75em;
}