@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.

Apply namespace locking rules in Almanac

Summary:
Ref T10246. Ref T6741.

When you have a namespace like "phacility.net", require users creating services and devices within it to have edit permission on the namespace.

This primarily allows us to lock down future device names in the cluster, so instances can't break themselves once they get access to Almanac.

Test Plan:
- Configured a `phacility.net` namespace, locked myself out of it.
- Could not create new `stuff.phacility.net` services/devices.
- Could still edit existing devices I had permission for.
- Configured a `free.phacility.net` namespace with more liberal policies.
- Could create `me.free.phacility.net`.
- Still could not create `other.phacility.net`.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T6741, T10246

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

+182 -31
+1 -1
src/__phutil_library_map__.php
··· 4079 4079 'AlmanacNamespaceListController' => 'AlmanacNamespaceController', 4080 4080 'AlmanacNamespaceNameNgrams' => 'PhabricatorSearchNgrams', 4081 4081 'AlmanacNamespacePHIDType' => 'PhabricatorPHIDType', 4082 - 'AlmanacNamespaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4082 + 'AlmanacNamespaceQuery' => 'AlmanacQuery', 4083 4083 'AlmanacNamespaceSearchEngine' => 'PhabricatorApplicationSearchEngine', 4084 4084 'AlmanacNamespaceTransaction' => 'PhabricatorApplicationTransaction', 4085 4085 'AlmanacNamespaceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+34 -14
src/applications/almanac/editor/AlmanacDeviceEditor.php
··· 148 148 $message, 149 149 $xaction); 150 150 $errors[] = $error; 151 + continue; 151 152 } 152 - } 153 - } 154 153 155 - if ($xactions) { 156 - $duplicate = id(new AlmanacDeviceQuery()) 157 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 158 - ->withNames(array(last($xactions)->getNewValue())) 159 - ->executeOne(); 160 - if ($duplicate && ($duplicate->getID() != $object->getID())) { 161 - $error = new PhabricatorApplicationTransactionValidationError( 162 - $type, 163 - pht('Not Unique'), 164 - pht('Almanac devices must have unique names.'), 165 - last($xactions)); 166 - $errors[] = $error; 154 + $other = id(new AlmanacDeviceQuery()) 155 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 156 + ->withNames(array($name)) 157 + ->executeOne(); 158 + if ($other && ($other->getID() != $object->getID())) { 159 + $error = new PhabricatorApplicationTransactionValidationError( 160 + $type, 161 + pht('Not Unique'), 162 + pht('Almanac devices must have unique names.'), 163 + $xaction); 164 + $errors[] = $error; 165 + continue; 166 + } 167 + 168 + if ($name === $object->getName()) { 169 + continue; 170 + } 171 + 172 + $namespace = AlmanacNamespace::loadRestrictedNamespace( 173 + $this->getActor(), 174 + $name); 175 + if ($namespace) { 176 + $error = new PhabricatorApplicationTransactionValidationError( 177 + $type, 178 + pht('Restricted'), 179 + pht( 180 + 'You do not have permission to create Almanac devices '. 181 + 'within the "%s" namespace.', 182 + $namespace->getName()), 183 + $xaction); 184 + $errors[] = $error; 185 + continue; 186 + } 167 187 } 168 188 } 169 189
+21 -1
src/applications/almanac/editor/AlmanacNamespaceEditor.php
··· 123 123 if ($other && ($other->getID() != $object->getID())) { 124 124 $error = new PhabricatorApplicationTransactionValidationError( 125 125 $type, 126 - pht('Invalid'), 126 + pht('Not Unique'), 127 127 pht( 128 128 'The namespace name "%s" is already in use by another '. 129 129 'namespace. Each namespace must have a unique name.', 130 130 $name), 131 + $xaction); 132 + $errors[] = $error; 133 + continue; 134 + } 135 + 136 + if ($name === $object->getName()) { 137 + continue; 138 + } 139 + 140 + $namespace = AlmanacNamespace::loadRestrictedNamespace( 141 + $this->getActor(), 142 + $name); 143 + if ($namespace) { 144 + $error = new PhabricatorApplicationTransactionValidationError( 145 + $type, 146 + pht('Restricted'), 147 + pht( 148 + 'You do not have permission to create Almanac namespaces '. 149 + 'within the "%s" namespace.', 150 + $namespace->getName()), 131 151 $xaction); 132 152 $errors[] = $error; 133 153 continue;
+34 -14
src/applications/almanac/editor/AlmanacServiceEditor.php
··· 140 140 $message, 141 141 $xaction); 142 142 $errors[] = $error; 143 + continue; 143 144 } 144 - } 145 - } 146 145 147 - if ($xactions) { 148 - $duplicate = id(new AlmanacServiceQuery()) 149 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 150 - ->withNames(array(last($xactions)->getNewValue())) 151 - ->executeOne(); 152 - if ($duplicate && ($duplicate->getID() != $object->getID())) { 153 - $error = new PhabricatorApplicationTransactionValidationError( 154 - $type, 155 - pht('Not Unique'), 156 - pht('Almanac services must have unique names.'), 157 - last($xactions)); 158 - $errors[] = $error; 146 + $other = id(new AlmanacServiceQuery()) 147 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 148 + ->withNames(array($name)) 149 + ->executeOne(); 150 + if ($other && ($other->getID() != $object->getID())) { 151 + $error = new PhabricatorApplicationTransactionValidationError( 152 + $type, 153 + pht('Not Unique'), 154 + pht('Almanac services must have unique names.'), 155 + last($xactions)); 156 + $errors[] = $error; 157 + continue; 158 + } 159 + 160 + if ($name === $object->getName()) { 161 + continue; 162 + } 163 + 164 + $namespace = AlmanacNamespace::loadRestrictedNamespace( 165 + $this->getActor(), 166 + $name); 167 + if ($namespace) { 168 + $error = new PhabricatorApplicationTransactionValidationError( 169 + $type, 170 + pht('Restricted'), 171 + pht( 172 + 'You do not have permission to create Almanac services '. 173 + 'within the "%s" namespace.', 174 + $namespace->getName()), 175 + $xaction); 176 + $errors[] = $error; 177 + continue; 178 + } 159 179 } 160 180 } 161 181
+1 -1
src/applications/almanac/query/AlmanacNamespaceQuery.php
··· 1 1 <?php 2 2 3 3 final class AlmanacNamespaceQuery 4 - extends PhabricatorCursorPagedPolicyAwareQuery { 4 + extends AlmanacQuery { 5 5 6 6 private $ids; 7 7 private $phids;
+45
src/applications/almanac/storage/AlmanacNamespace.php
··· 68 68 return '/almanac/namespace/view/'.$this->getName().'/'; 69 69 } 70 70 71 + public function getNameLength() { 72 + return strlen($this->getName()); 73 + } 74 + 75 + /** 76 + * Load the namespace which prevents use of an Almanac name, if one exists. 77 + */ 78 + public static function loadRestrictedNamespace( 79 + PhabricatorUser $viewer, 80 + $name) { 81 + 82 + // For a name like "x.y.z", produce a list of controlling namespaces like 83 + // ("z", "y.x", "x.y.z"). 84 + $names = array(); 85 + $parts = explode('.', $name); 86 + for ($ii = 0; $ii < count($parts); $ii++) { 87 + $names[] = implode('.', array_slice($parts, -($ii + 1))); 88 + } 89 + 90 + // Load all the possible controlling namespaces. 91 + $namespaces = id(new AlmanacNamespaceQuery()) 92 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 93 + ->withNames($names) 94 + ->execute(); 95 + if (!$namespaces) { 96 + return null; 97 + } 98 + 99 + // Find the "nearest" (longest) namespace that exists. If both 100 + // "sub.domain.com" and "domain.com" exist, we only care about the policy 101 + // on the former. 102 + $namespaces = msort($namespaces, 'getNameLength'); 103 + $namespace = last($namespaces); 104 + 105 + $can_edit = PhabricatorPolicyFilter::hasCapability( 106 + $viewer, 107 + $namespace, 108 + PhabricatorPolicyCapability::CAN_EDIT); 109 + if ($can_edit) { 110 + return null; 111 + } 112 + 113 + return $namespace; 114 + } 115 + 71 116 72 117 /* -( AlmanacPropertyInterface )------------------------------------------- */ 73 118
+46
src/docs/user/userguide/almanac.diviner
··· 133 133 `b.mycompany.com`. 134 134 135 135 136 + Namespaces 137 + ========== 138 + 139 + Almanac namespaces allow you to control who can create services and devices 140 + with certain names. 141 + 142 + If you keep a list of cattle as devices with names like 143 + `cow001.herd.myranch.com`, `cow002.herd.myranch.moo`, you might have some 144 + applications which query for all devices in `*.herd.myranch.moo`, and thus 145 + want to limit who can create devices there in order to prevent mistakes. 146 + 147 + If a namespace like `herd.myranch.moo` exists, users must have permission to 148 + edit the namespace in order to create new services, devices, or namespaces 149 + within it. For example, a user can not create `cow003.herd.myranch.moo` if 150 + they do not have edit permission on the `herd.myranch.moo` namespace. 151 + 152 + When you try to create a `cow003.herd.myranch.moo` service (or rename an 153 + existing service to have that name), Almanac looks for these namespaces, then 154 + checks the policy of the first one it finds: 155 + 156 + | Namespace | 157 + |----|----- 158 + | `cow003.herd.ranch.moo` | //"Nearest" namespace, considered first.// 159 + | `herd.ranch.moo` | | 160 + | `ranch.moo` | | 161 + | `moo` | //"Farthest" namespace, considered last.// 162 + 163 + Note that namespaces treat names as lists of domain parts, not as strict 164 + substrings, so the namespace `herd.myranch.moo` does not prevent 165 + someone from creating `goatherd.myranch.moo` or `goat001.goatherd.myranch.moo`. 166 + The name `goatherd.myranch.moo` is not part of the `herd.myranch.moo` namespace 167 + because the initial subdomain differs. 168 + 169 + If a name belongs to multiple namespaces, the policy of the nearest namespace 170 + is controlling. For example, if `myranch.moo` has a very restrictive edit 171 + policy but `shed.myranch.moo` has a more open one, users can create devices and 172 + services like `rake.shed.myranch.moo` as long as they can pass the policy check 173 + for `shed.myranch.moo`, even if they do not have permission under the policy 174 + for `myranch.moo`. 175 + 176 + Users can edit services and devices within a namespace if they have edit 177 + permission on the service or device itself, as long as they don't try to rename 178 + the service or device to move it into a namespace they don't have permission 179 + to access. 180 + 181 + 136 182 Locking and Unlocking Services 137 183 ============================== 138 184