@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Legalpad - allow for legalpad documents to be required to be signed for using Phabricator

Summary: Fixes T7159.

Test Plan:
Created a legalpad document that needed a signature and I was required to sign it no matter what page I hit. Signed it and things worked! Added a new legalpad document and I had to sign again!

Ran unit tests and they passed!

Logged out as a user who was roadblocked into signing a bunch of stuff and it worked!

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T7159

Differential Revision: https://secure.phabricator.com/D11759

+219 -28
+5
resources/sql/autopatches/20150212.legalpad.session.1.sql
··· 1 + ALTER TABLE {$NAMESPACE}_user.phabricator_session 2 + ADD signedLegalpadDocuments BOOL NOT NULL DEFAULT 0; 3 + 4 + ALTER TABLE {$NAMESPACE}_legalpad.legalpad_document 5 + ADD requireSignature BOOL NOT NULL DEFAULT 0;
+2
resources/sql/autopatches/20150212.legalpad.session.2.sql
··· 1 + ALTER TABLE {$NAMESPACE}_legalpad.legalpad_document 2 + ADD KEY `key_required` (requireSignature, dateModified);
+4
src/applications/auth/controller/PhabricatorAuthFinishController.php
··· 11 11 return true; 12 12 } 13 13 14 + public function shouldAllowLegallyNonCompliantUsers() { 15 + return true; 16 + } 17 + 14 18 public function processRequest() { 15 19 $request = $this->getRequest(); 16 20 $viewer = $request->getUser();
+4
src/applications/auth/controller/PhabricatorAuthValidateController.php
··· 11 11 return true; 12 12 } 13 13 14 + public function shouldAllowLegallyNonCompliantUsers() { 15 + return true; 16 + } 17 + 14 18 public function processRequest() { 15 19 $request = $this->getRequest(); 16 20 $viewer = $request->getUser();
+4
src/applications/auth/controller/PhabricatorLogoutController.php
··· 21 21 return true; 22 22 } 23 23 24 + public function shouldAllowLegallyNonCompliantUsers() { 25 + return true; 26 + } 27 + 24 28 public function handleRequest(AphrontRequest $request) { 25 29 $request = $this->getRequest(); 26 30 $user = $request->getUser();
+48
src/applications/auth/engine/PhabricatorAuthSessionEngine.php
··· 134 134 s.sessionStart AS s_sessionStart, 135 135 s.highSecurityUntil AS s_highSecurityUntil, 136 136 s.isPartial AS s_isPartial, 137 + s.signedLegalpadDocuments as s_signedLegalpadDocuments, 137 138 u.* 138 139 FROM %T u JOIN %T s ON u.phid = s.userPHID 139 140 AND s.type = %s AND s.sessionKey = %s', ··· 232 233 ->setSessionStart(time()) 233 234 ->setSessionExpires(time() + $session_ttl) 234 235 ->setIsPartial($partial ? 1 : 0) 236 + ->setSignedLegalpadDocuments(0) 235 237 ->save(); 236 238 237 239 $log = PhabricatorUserLog::initializeNewLog( ··· 549 551 $viewer->getPHID(), 550 552 PhabricatorUserLog::ACTION_LOGIN_FULL); 551 553 $log->save(); 554 + unset($unguarded); 555 + } 556 + 557 + 558 + /* -( Legalpad Documents )-------------------------------------------------- */ 559 + 560 + 561 + /** 562 + * Upgrade a session to have all legalpad documents signed. 563 + * 564 + * @param PhabricatorUser User whose session should upgrade. 565 + * @param array LegalpadDocument objects 566 + * @return void 567 + * @task partial 568 + */ 569 + public function signLegalpadDocuments(PhabricatorUser $viewer, array $docs) { 570 + 571 + if (!$viewer->hasSession()) { 572 + throw new Exception( 573 + pht('Signing session legalpad documents of user with no session!')); 574 + } 575 + 576 + $session = $viewer->getSession(); 577 + 578 + if ($session->getSignedLegalpadDocuments()) { 579 + throw new Exception(pht( 580 + 'Session has already signed required legalpad documents!')); 581 + } 582 + 583 + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 584 + $session->setSignedLegalpadDocuments(1); 585 + 586 + queryfx( 587 + $session->establishConnection('w'), 588 + 'UPDATE %T SET signedLegalpadDocuments = %d WHERE id = %d', 589 + $session->getTableName(), 590 + 1, 591 + $session->getID()); 592 + 593 + if (!empty($docs)) { 594 + $log = PhabricatorUserLog::initializeNewLog( 595 + $viewer, 596 + $viewer->getPHID(), 597 + PhabricatorUserLog::ACTION_LOGIN_LEGALPAD); 598 + $log->save(); 599 + } 552 600 unset($unguarded); 553 601 } 554 602
+2
src/applications/auth/storage/PhabricatorAuthSession.php
··· 13 13 protected $sessionExpires; 14 14 protected $highSecurityUntil; 15 15 protected $isPartial; 16 + protected $signedLegalpadDocuments; 16 17 17 18 private $identityObject = self::ATTACHABLE; 18 19 ··· 26 27 'sessionExpires' => 'epoch', 27 28 'highSecurityUntil' => 'epoch?', 28 29 'isPartial' => 'bool', 30 + 'signedLegalpadDocuments' => 'bool', 29 31 ), 30 32 self::CONFIG_KEY_SCHEMA => array( 31 33 'sessionKey' => array(
+45
src/applications/base/controller/PhabricatorController.php
··· 53 53 return PhabricatorEnv::getEnvConfig('security.require-multi-factor-auth'); 54 54 } 55 55 56 + public function shouldAllowLegallyNonCompliantUsers() { 57 + return false; 58 + } 59 + 56 60 public function willBeginExecution() { 57 61 $request = $this->getRequest(); 58 62 ··· 218 222 ->setViewer($user) 219 223 ->withPHIDs(array($application->getPHID())) 220 224 ->executeOne(); 225 + } 226 + } 227 + 228 + 229 + if (!$this->shouldAllowLegallyNonCompliantUsers()) { 230 + $legalpad_class = 'PhabricatorLegalpadApplication'; 231 + $legalpad = id(new PhabricatorApplicationQuery()) 232 + ->setViewer($user) 233 + ->withClasses(array($legalpad_class)) 234 + ->withInstalled(true) 235 + ->execute(); 236 + $legalpad = head($legalpad); 237 + 238 + $doc_query = id(new LegalpadDocumentQuery()) 239 + ->setViewer($user) 240 + ->withSignatureRequired(1) 241 + ->needViewerSignatures(true); 242 + 243 + if ($user->hasSession() && 244 + !$user->getSession()->getIsPartial() && 245 + !$user->getSession()->getSignedLegalpadDocuments() && 246 + $user->isLoggedIn() && 247 + $legalpad) { 248 + 249 + $sign_docs = $doc_query->execute(); 250 + $must_sign_docs = array(); 251 + foreach ($sign_docs as $sign_doc) { 252 + if (!$sign_doc->getUserSignature($user->getPHID())) { 253 + $must_sign_docs[] = $sign_doc; 254 + } 255 + } 256 + if ($must_sign_docs) { 257 + $controller = new LegalpadDocumentSignController(); 258 + $this->getRequest()->setURIMap(array( 259 + 'id' => head($must_sign_docs)->getID(),)); 260 + $this->setCurrentApplication($legalpad); 261 + return $this->delegateToController($controller); 262 + } else { 263 + $engine = id(new PhabricatorAuthSessionEngine()) 264 + ->signLegalpadDocuments($user, $sign_docs); 265 + } 221 266 } 222 267 } 223 268
+1 -1
src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php
··· 170 170 // Test public access. 171 171 172 172 $this->checkAccess( 173 - 'No Login Required', 173 + 'Public Access', 174 174 id(clone $controller)->setConfig('public', true), 175 175 $request, 176 176 array(
+4
src/applications/celerity/controller/CelerityResourceController.php
··· 18 18 return true; 19 19 } 20 20 21 + public function shouldAllowLegallyNonCompliantUsers() { 22 + return true; 23 + } 24 + 21 25 abstract public function getCelerityResourceMap(); 22 26 23 27 protected function serveResource($path, $package_hash = null) {
+1
src/applications/legalpad/constants/LegalpadTransactionType.php
··· 6 6 const TYPE_TEXT = 'text'; 7 7 const TYPE_SIGNATURE_TYPE = 'legalpad:signature-type'; 8 8 const TYPE_PREAMBLE = 'legalpad:premable'; 9 + const TYPE_REQUIRE_SIGNATURE = 'legalpad:require-signature'; 9 10 10 11 }
+41 -10
src/applications/legalpad/controller/LegalpadDocumentEditController.php
··· 2 2 3 3 final class LegalpadDocumentEditController extends LegalpadController { 4 4 5 - private $id; 6 - 7 - public function willProcessRequest(array $data) { 8 - $this->id = idx($data, 'id'); 9 - } 10 - 11 - public function processRequest() { 12 - $request = $this->getRequest(); 5 + public function handleRequest(AphrontRequest $request) { 13 6 $user = $request->getUser(); 14 7 15 - if (!$this->id) { 8 + $id = $request->getURIData('id'); 9 + if (!$id) { 16 10 $is_create = true; 17 11 18 12 $this->requireApplicationCapability( ··· 34 28 PhabricatorPolicyCapability::CAN_VIEW, 35 29 PhabricatorPolicyCapability::CAN_EDIT, 36 30 )) 37 - ->withIDs(array($this->id)) 31 + ->withIDs(array($id)) 38 32 ->executeOne(); 39 33 if (!$document) { 40 34 return new Aphront404Response(); ··· 48 42 $text = $document->getDocumentBody()->getText(); 49 43 $v_signature_type = $document->getSignatureType(); 50 44 $v_preamble = $document->getPreamble(); 45 + $v_require_signature = $document->getRequireSignature(); 51 46 52 47 $errors = array(); 53 48 $can_view = null; ··· 97 92 ->setTransactionType(LegalpadTransactionType::TYPE_PREAMBLE) 98 93 ->setNewValue($v_preamble); 99 94 95 + $v_require_signature = $request->getBool('requireSignature', 0); 96 + if ($v_require_signature) { 97 + if (!$user->getIsAdmin()) { 98 + $errors[] = pht('Only admins may require signature.'); 99 + } 100 + $corp = LegalpadDocument::SIGNATURE_TYPE_CORPORATION; 101 + if ($v_signature_type == $corp) { 102 + $errors[] = pht( 103 + 'Only documents with signature type "individual" may require '. 104 + 'signing to use Phabricator.'); 105 + } 106 + } 107 + if ($user->getIsAdmin()) { 108 + $xactions[] = id(new LegalpadTransaction()) 109 + ->setTransactionType(LegalpadTransactionType::TYPE_REQUIRE_SIGNATURE) 110 + ->setNewValue($v_require_signature); 111 + } 112 + 100 113 if (!$errors) { 101 114 $editor = id(new LegalpadDocumentEditor()) 102 115 ->setContentSourceFromRequest($request) ··· 133 146 ->setName(pht('signatureType')) 134 147 ->setValue($v_signature_type) 135 148 ->setOptions(LegalpadDocument::getSignatureTypeMap())); 149 + $show_require = true; 136 150 } else { 137 151 $form->appendChild( 138 152 id(new AphrontFormMarkupControl()) 139 153 ->setLabel(pht('Who Should Sign?')) 140 154 ->setValue($document->getSignatureTypeName())); 155 + $individual = LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL; 156 + $show_require = $document->getSignatureType() == $individual; 157 + } 158 + 159 + if ($show_require) { 160 + $form 161 + ->appendChild( 162 + id(new AphrontFormCheckboxControl()) 163 + ->setDisabled(!$user->getIsAdmin()) 164 + ->setLabel(pht('Require Signature')) 165 + ->addCheckbox( 166 + 'requireSignature', 167 + 'requireSignature', 168 + pht( 169 + 'Should signing this document be required to use Phabricator? '. 170 + 'Applies to invidivuals only.'), 171 + $v_require_signature)); 141 172 } 142 173 143 174 $form
+2 -9
src/applications/legalpad/controller/LegalpadDocumentSignController.php
··· 2 2 3 3 final class LegalpadDocumentSignController extends LegalpadController { 4 4 5 - private $id; 6 - 7 5 public function shouldAllowPublic() { 8 6 return true; 9 7 } 10 8 11 - public function willProcessRequest(array $data) { 12 - $this->id = $data['id']; 13 - } 14 - 15 - public function processRequest() { 16 - $request = $this->getRequest(); 9 + public function handleRequest(AphrontRequest $request) { 17 10 $viewer = $request->getUser(); 18 11 19 12 $document = id(new LegalpadDocumentQuery()) 20 13 ->setViewer($viewer) 21 - ->withIDs(array($this->id)) 14 + ->withIDs(array($request->getURIData('id'))) 22 15 ->needDocumentBodies(true) 23 16 ->executeOne(); 24 17 if (!$document) {
+2 -8
src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
··· 2 2 3 3 final class LegalpadDocumentSignatureAddController extends LegalpadController { 4 4 5 - private $id; 6 - 7 - public function willProcessRequest(array $data) { 8 - $this->id = $data['id']; 9 - } 10 - 11 - public function processRequest() { 5 + public function handleRequest(AphrontRequest $request) { 12 6 $request = $this->getRequest(); 13 7 $viewer = $request->getUser(); 14 8 ··· 20 14 PhabricatorPolicyCapability::CAN_VIEW, 21 15 PhabricatorPolicyCapability::CAN_EDIT, 22 16 )) 23 - ->withIDs(array($this->id)) 17 + ->withIDs(array($request->getURIData('id'))) 24 18 ->executeOne(); 25 19 if (!$document) { 26 20 return new Aphront404Response();
+21
src/applications/legalpad/editor/LegalpadDocumentEditor.php
··· 32 32 $types[] = LegalpadTransactionType::TYPE_TEXT; 33 33 $types[] = LegalpadTransactionType::TYPE_SIGNATURE_TYPE; 34 34 $types[] = LegalpadTransactionType::TYPE_PREAMBLE; 35 + $types[] = LegalpadTransactionType::TYPE_REQUIRE_SIGNATURE; 35 36 36 37 return $types; 37 38 } ··· 49 50 return $object->getSignatureType(); 50 51 case LegalpadTransactionType::TYPE_PREAMBLE: 51 52 return $object->getPreamble(); 53 + case LegalpadTransactionType::TYPE_REQUIRE_SIGNATURE: 54 + return $object->getRequireSignature(); 52 55 } 53 56 } 54 57 ··· 61 64 case LegalpadTransactionType::TYPE_TEXT: 62 65 case LegalpadTransactionType::TYPE_SIGNATURE_TYPE: 63 66 case LegalpadTransactionType::TYPE_PREAMBLE: 67 + case LegalpadTransactionType::TYPE_REQUIRE_SIGNATURE: 64 68 return $xaction->getNewValue(); 65 69 } 66 70 } ··· 87 91 case LegalpadTransactionType::TYPE_PREAMBLE: 88 92 $object->setPreamble($xaction->getNewValue()); 89 93 break; 94 + case LegalpadTransactionType::TYPE_REQUIRE_SIGNATURE: 95 + $object->setRequireSignature($xaction->getNewValue()); 96 + break; 90 97 } 91 98 } 92 99 93 100 protected function applyCustomExternalTransaction( 94 101 PhabricatorLiskDAO $object, 95 102 PhabricatorApplicationTransaction $xaction) { 103 + 104 + switch ($xaction->getTransactionType()) { 105 + case LegalpadTransactionType::TYPE_REQUIRE_SIGNATURE: 106 + if ($xaction->getNewValue()) { 107 + $session = new PhabricatorAuthSession(); 108 + queryfx( 109 + $session->establishConnection('w'), 110 + 'UPDATE %T SET signedLegalpadDocuments = 0', 111 + $session->getTableName()); 112 + } 113 + break; 114 + } 96 115 return; 97 116 } 98 117 ··· 138 157 case LegalpadTransactionType::TYPE_TEXT: 139 158 case LegalpadTransactionType::TYPE_SIGNATURE_TYPE: 140 159 case LegalpadTransactionType::TYPE_PREAMBLE: 160 + case LegalpadTransactionType::TYPE_REQUIRE_SIGNATURE: 141 161 return $v; 142 162 } 143 163 ··· 182 202 case LegalpadTransactionType::TYPE_TEXT: 183 203 case LegalpadTransactionType::TYPE_TITLE: 184 204 case LegalpadTransactionType::TYPE_PREAMBLE: 205 + case LegalpadTransactionType::TYPE_REQUIRE_SIGNATURE: 185 206 return true; 186 207 } 187 208
+13
src/applications/legalpad/query/LegalpadDocumentQuery.php
··· 10 10 private $signerPHIDs; 11 11 private $dateCreatedAfter; 12 12 private $dateCreatedBefore; 13 + private $signatureRequired; 13 14 14 15 private $needDocumentBodies; 15 16 private $needContributors; ··· 38 39 39 40 public function withSignerPHIDs(array $phids) { 40 41 $this->signerPHIDs = $phids; 42 + return $this; 43 + } 44 + 45 + public function withSignatureRequired($bool) { 46 + $this->signatureRequired = $bool; 41 47 return $this; 42 48 } 43 49 ··· 202 208 $conn_r, 203 209 'contributor.dst IN (%Ls)', 204 210 $this->contributorPHIDs); 211 + } 212 + 213 + if ($this->signatureRequired !== null) { 214 + $where[] = qsprintf( 215 + $conn_r, 216 + 'd.requireSignature = %d', 217 + $this->signatureRequired); 205 218 } 206 219 207 220 $where[] = $this->buildPagingClause($conn_r);
+6
src/applications/legalpad/storage/LegalpadDocument.php
··· 18 18 protected $mailKey; 19 19 protected $signatureType; 20 20 protected $preamble; 21 + protected $requireSignature; 21 22 22 23 const SIGNATURE_TYPE_INDIVIDUAL = 'user'; 23 24 const SIGNATURE_TYPE_CORPORATION = 'corp'; ··· 44 45 ->attachSignatures(array()) 45 46 ->setSignatureType(self::SIGNATURE_TYPE_INDIVIDUAL) 46 47 ->setPreamble('') 48 + ->setRequireSignature(0) 47 49 ->setViewPolicy($view_policy) 48 50 ->setEditPolicy($edit_policy); 49 51 } ··· 61 63 'mailKey' => 'bytes20', 62 64 'signatureType' => 'text4', 63 65 'preamble' => 'text', 66 + 'requireSignature' => 'bool', 64 67 ), 65 68 self::CONFIG_KEY_SCHEMA => array( 66 69 'key_creator' => array( 67 70 'columns' => array('creatorPHID', 'dateModified'), 71 + ), 72 + 'key_required' => array( 73 + 'columns' => array('requireSignature', 'dateModified'), 68 74 ), 69 75 ), 70 76 ) + parent::getConfiguration();
+11
src/applications/legalpad/storage/LegalpadTransaction.php
··· 54 54 return pht( 55 55 '%s updated the preamble.', 56 56 $this->renderHandleLink($author_phid)); 57 + case LegalpadTransactionType::TYPE_REQUIRE_SIGNATURE: 58 + if ($new) { 59 + $text = pht( 60 + '%s set the document to require signatures.', 61 + $this->renderHandleLink($author_phid)); 62 + } else { 63 + $text = pht( 64 + '%s set the document to not require signatures.', 65 + $this->renderHandleLink($author_phid)); 66 + } 67 + return $text; 57 68 } 58 69 59 70 return parent::getTitle();
+3
src/applications/people/storage/PhabricatorUserLog.php
··· 8 8 const ACTION_LOGIN_FULL = 'login-full'; 9 9 const ACTION_LOGOUT = 'logout'; 10 10 const ACTION_LOGIN_FAILURE = 'login-fail'; 11 + const ACTION_LOGIN_LEGALPAD = 'login-legalpad'; 11 12 const ACTION_RESET_PASSWORD = 'reset-pass'; 12 13 13 14 const ACTION_CREATE = 'create'; ··· 53 54 self::ACTION_LOGIN_PARTIAL => pht('Login: Partial Login'), 54 55 self::ACTION_LOGIN_FULL => pht('Login: Upgrade to Full'), 55 56 self::ACTION_LOGIN_FAILURE => pht('Login: Failure'), 57 + self::ACTION_LOGIN_LEGALPAD => 58 + pht('Login: Signed Required Legalpad Documents'), 56 59 self::ACTION_LOGOUT => pht('Logout'), 57 60 self::ACTION_RESET_PASSWORD => pht('Reset Password'), 58 61 self::ACTION_CREATE => pht('Create Account'),