diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -50,6 +50,8 @@ private $alternateCSRFString = self::ATTACHABLE; private $session = self::ATTACHABLE; + private $authorities = array(); + protected function readField($field) { switch ($field) { case 'timezoneIdentifier': @@ -694,6 +696,25 @@ $email->getUserPHID()); } + + /** + * Grant a user a source of authority, to let them bypass policy checks they + * could not otherwise. + */ + public function grantAuthority($authority) { + $this->authorities[] = $authority; + return $this; + } + + + /** + * Get authorities granted to the user. + */ + public function getAuthorities() { + return $this->authorities; + } + + /* -( Multi-Factor Authentication )---------------------------------------- */ diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php --- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php +++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php @@ -85,6 +85,12 @@ 'edit/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantEditController', 'orders/(?P<merchantID>\d+)/(?:query/(?P<queryKey>[^/]+)/)?' => 'PhortuneCartListController', + '(?P<merchantID>\d+)/cart/(?P<id>\d+)/' => array( + '' => 'PhortuneCartViewController', + '(?P<action>cancel|refund)/' => 'PhortuneCartCancelController', + 'update/' => 'PhortuneCartUpdateController', + 'accept/' => 'PhortuneCartAcceptController', + ), '(?P<merchantID>\d+)/subscription/' => array( '(?:query/(?P<queryKey>[^/]+)/)?' => 'PhortuneSubscriptionListController', diff --git a/src/applications/phortune/controller/PhortuneCartListController.php b/src/applications/phortune/controller/PhortuneCartListController.php --- a/src/applications/phortune/controller/PhortuneCartListController.php +++ b/src/applications/phortune/controller/PhortuneCartListController.php @@ -42,6 +42,7 @@ return new Aphront404Response(); } $this->merchant = $merchant; + $viewer->grantAuthority($merchant); $engine->setMerchant($merchant); } else if ($account_id) { $account = id(new PhortuneAccountQuery()) diff --git a/src/applications/phortune/controller/PhortuneCartViewController.php b/src/applications/phortune/controller/PhortuneCartViewController.php --- a/src/applications/phortune/controller/PhortuneCartViewController.php +++ b/src/applications/phortune/controller/PhortuneCartViewController.php @@ -13,6 +13,11 @@ $request = $this->getRequest(); $viewer = $request->getUser(); + $authority = $this->loadMerchantAuthority(); + + // TODO: This (and the rest of the Cart controllers) need to be updated + // to use merchant URIs and merchant authority. + $cart = id(new PhortuneCartQuery()) ->setViewer($viewer) ->withIDs(array($this->id)) @@ -157,7 +162,11 @@ $account = $cart->getAccount(); $crumbs = $this->buildApplicationCrumbs(); - $this->addAccountCrumb($crumbs, $cart->getAccount()); + if ($authority) { + $this->addMerchantCrumb($crumbs, $authority); + } else { + $this->addAccountCrumb($crumbs, $cart->getAccount()); + } $crumbs->addTextCrumb(pht('Cart %d', $cart->getID())); $timeline = $this->buildTransactionTimeline( diff --git a/src/applications/phortune/controller/PhortuneController.php b/src/applications/phortune/controller/PhortuneController.php --- a/src/applications/phortune/controller/PhortuneController.php +++ b/src/applications/phortune/controller/PhortuneController.php @@ -84,4 +84,30 @@ return $providers; } + protected function loadMerchantAuthority() { + $request = $this->getRequest(); + $viewer = $this->getViewer(); + + $is_merchant = (bool)$request->getURIData('merchantID'); + if (!$is_merchant) { + return null; + } + + $merchant = id(new PhortuneMerchantQuery()) + ->setViewer($viewer) + ->withIDs(array($request->getURIData('merchantID'))) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$merchant) { + return null; + } + + $viewer->grantAuthority($merchant); + return $merchant; + } + } diff --git a/src/applications/phortune/controller/PhortuneSubscriptionListController.php b/src/applications/phortune/controller/PhortuneSubscriptionListController.php --- a/src/applications/phortune/controller/PhortuneSubscriptionListController.php +++ b/src/applications/phortune/controller/PhortuneSubscriptionListController.php @@ -36,6 +36,7 @@ return new Aphront404Response(); } $this->merchant = $merchant; + $viewer->grantAuthority($merchant); $engine->setMerchant($merchant); } else if ($this->accountID) { $account = id(new PhortuneAccountQuery()) diff --git a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php --- a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php +++ b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php @@ -5,6 +5,8 @@ public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); + $is_merchant = (bool)$this->loadMerchantAuthority(); + $subscription = id(new PhortuneSubscriptionQuery()) ->setViewer($viewer) ->withIDs(array($request->getURIData('id'))) @@ -19,7 +21,6 @@ $subscription, PhabricatorPolicyCapability::CAN_EDIT); - $is_merchant = (bool)$request->getURIData('merchantID'); $merchant = $subscription->getMerchant(); $account = $subscription->getAccount(); diff --git a/src/applications/phortune/query/PhortuneCartQuery.php b/src/applications/phortune/query/PhortuneCartQuery.php --- a/src/applications/phortune/query/PhortuneCartQuery.php +++ b/src/applications/phortune/query/PhortuneCartQuery.php @@ -91,6 +91,10 @@ $cart->attachAccount($account); } + if (!$carts) { + return array(); + } + $merchants = id(new PhortuneMerchantQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($carts, 'getMerchantPHID')) @@ -106,6 +110,10 @@ $cart->attachMerchant($merchant); } + if (!$carts) { + return array(); + } + $implementations = array(); $cart_map = mgroup($carts, 'getCartClass'); diff --git a/src/applications/phortune/query/PhortuneCartSearchEngine.php b/src/applications/phortune/query/PhortuneCartSearchEngine.php --- a/src/applications/phortune/query/PhortuneCartSearchEngine.php +++ b/src/applications/phortune/query/PhortuneCartSearchEngine.php @@ -166,8 +166,21 @@ foreach ($carts as $cart) { $merchant = $cart->getMerchant(); + if ($this->getMerchant()) { + $href = $this->getApplicationURI( + 'merchant/'.$merchant->getID().'/cart/'.$cart->getID().'/'); + } else { + $href = $cart->getDetailURI(); + } + $rows[] = array( $cart->getID(), + phutil_tag( + 'a', + array( + 'href' => $href, + ), + $cart->getName()), $handles[$cart->getPHID()]->renderLink(), $handles[$merchant->getPHID()]->renderLink(), $handles[$cart->getAuthorPHID()]->renderLink(), diff --git a/src/applications/phortune/query/PhortunePaymentMethodQuery.php b/src/applications/phortune/query/PhortunePaymentMethodQuery.php --- a/src/applications/phortune/query/PhortunePaymentMethodQuery.php +++ b/src/applications/phortune/query/PhortunePaymentMethodQuery.php @@ -65,6 +65,10 @@ $method->attachAccount($account); } + if (!$methods) { + return $methods; + } + $merchants = id(new PhortuneMerchantQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($methods, 'getMerchantPHID')) @@ -80,6 +84,10 @@ $method->attachMerchant($merchant); } + if (!$methods) { + return $methods; + } + $provider_configs = id(new PhortunePaymentProviderConfigQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($methods, 'getProviderPHID')) diff --git a/src/applications/phortune/storage/PhortuneAccount.php b/src/applications/phortune/storage/PhortuneAccount.php --- a/src/applications/phortune/storage/PhortuneAccount.php +++ b/src/applications/phortune/storage/PhortuneAccount.php @@ -133,10 +133,6 @@ public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: - // Accounts are technically visible to all users, because merchant - // controllers need to be able to see accounts in order to process - // orders. We lock things down more tightly at the application level. - return PhabricatorPolicies::POLICY_USER; case PhabricatorPolicyCapability::CAN_EDIT: if ($this->getPHID() === null) { // Allow a user to create an account for themselves. @@ -149,7 +145,21 @@ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { $members = array_fuse($this->getMemberPHIDs()); - return isset($members[$viewer->getPHID()]); + if (isset($members[$viewer->getPHID()])) { + return true; + } + + // If the viewer is acting on behalf of a merchant, they can see + // payment accounts. + if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { + foreach ($viewer->getAuthorities() as $authority) { + if ($authority instanceof PhortuneMerchant) { + return true; + } + } + } + + return false; } public function describeAutomaticCapability($capability) {