Page MenuHomePhabricator

D15325.diff
No OneTemporary

D15325.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -4079,7 +4079,7 @@
'AlmanacNamespaceListController' => 'AlmanacNamespaceController',
'AlmanacNamespaceNameNgrams' => 'PhabricatorSearchNgrams',
'AlmanacNamespacePHIDType' => 'PhabricatorPHIDType',
- 'AlmanacNamespaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'AlmanacNamespaceQuery' => 'AlmanacQuery',
'AlmanacNamespaceSearchEngine' => 'PhabricatorApplicationSearchEngine',
'AlmanacNamespaceTransaction' => 'PhabricatorApplicationTransaction',
'AlmanacNamespaceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
diff --git a/src/applications/almanac/editor/AlmanacDeviceEditor.php b/src/applications/almanac/editor/AlmanacDeviceEditor.php
--- a/src/applications/almanac/editor/AlmanacDeviceEditor.php
+++ b/src/applications/almanac/editor/AlmanacDeviceEditor.php
@@ -148,22 +148,42 @@
$message,
$xaction);
$errors[] = $error;
+ continue;
}
- }
- }
- if ($xactions) {
- $duplicate = id(new AlmanacDeviceQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withNames(array(last($xactions)->getNewValue()))
- ->executeOne();
- if ($duplicate && ($duplicate->getID() != $object->getID())) {
- $error = new PhabricatorApplicationTransactionValidationError(
- $type,
- pht('Not Unique'),
- pht('Almanac devices must have unique names.'),
- last($xactions));
- $errors[] = $error;
+ $other = id(new AlmanacDeviceQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withNames(array($name))
+ ->executeOne();
+ if ($other && ($other->getID() != $object->getID())) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Not Unique'),
+ pht('Almanac devices must have unique names.'),
+ $xaction);
+ $errors[] = $error;
+ continue;
+ }
+
+ if ($name === $object->getName()) {
+ continue;
+ }
+
+ $namespace = AlmanacNamespace::loadRestrictedNamespace(
+ $this->getActor(),
+ $name);
+ if ($namespace) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Restricted'),
+ pht(
+ 'You do not have permission to create Almanac devices '.
+ 'within the "%s" namespace.',
+ $namespace->getName()),
+ $xaction);
+ $errors[] = $error;
+ continue;
+ }
}
}
diff --git a/src/applications/almanac/editor/AlmanacNamespaceEditor.php b/src/applications/almanac/editor/AlmanacNamespaceEditor.php
--- a/src/applications/almanac/editor/AlmanacNamespaceEditor.php
+++ b/src/applications/almanac/editor/AlmanacNamespaceEditor.php
@@ -123,7 +123,7 @@
if ($other && ($other->getID() != $object->getID())) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
- pht('Invalid'),
+ pht('Not Unique'),
pht(
'The namespace name "%s" is already in use by another '.
'namespace. Each namespace must have a unique name.',
@@ -132,6 +132,26 @@
$errors[] = $error;
continue;
}
+
+ if ($name === $object->getName()) {
+ continue;
+ }
+
+ $namespace = AlmanacNamespace::loadRestrictedNamespace(
+ $this->getActor(),
+ $name);
+ if ($namespace) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Restricted'),
+ pht(
+ 'You do not have permission to create Almanac namespaces '.
+ 'within the "%s" namespace.',
+ $namespace->getName()),
+ $xaction);
+ $errors[] = $error;
+ continue;
+ }
}
}
diff --git a/src/applications/almanac/editor/AlmanacServiceEditor.php b/src/applications/almanac/editor/AlmanacServiceEditor.php
--- a/src/applications/almanac/editor/AlmanacServiceEditor.php
+++ b/src/applications/almanac/editor/AlmanacServiceEditor.php
@@ -140,22 +140,42 @@
$message,
$xaction);
$errors[] = $error;
+ continue;
+ }
+
+ $other = id(new AlmanacServiceQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withNames(array($name))
+ ->executeOne();
+ if ($other && ($other->getID() != $object->getID())) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Not Unique'),
+ pht('Almanac services must have unique names.'),
+ last($xactions));
+ $errors[] = $error;
+ continue;
}
- }
- }
- if ($xactions) {
- $duplicate = id(new AlmanacServiceQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withNames(array(last($xactions)->getNewValue()))
- ->executeOne();
- if ($duplicate && ($duplicate->getID() != $object->getID())) {
- $error = new PhabricatorApplicationTransactionValidationError(
- $type,
- pht('Not Unique'),
- pht('Almanac services must have unique names.'),
- last($xactions));
- $errors[] = $error;
+ if ($name === $object->getName()) {
+ continue;
+ }
+
+ $namespace = AlmanacNamespace::loadRestrictedNamespace(
+ $this->getActor(),
+ $name);
+ if ($namespace) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Restricted'),
+ pht(
+ 'You do not have permission to create Almanac services '.
+ 'within the "%s" namespace.',
+ $namespace->getName()),
+ $xaction);
+ $errors[] = $error;
+ continue;
+ }
}
}
diff --git a/src/applications/almanac/query/AlmanacNamespaceQuery.php b/src/applications/almanac/query/AlmanacNamespaceQuery.php
--- a/src/applications/almanac/query/AlmanacNamespaceQuery.php
+++ b/src/applications/almanac/query/AlmanacNamespaceQuery.php
@@ -1,7 +1,7 @@
<?php
final class AlmanacNamespaceQuery
- extends PhabricatorCursorPagedPolicyAwareQuery {
+ extends AlmanacQuery {
private $ids;
private $phids;
diff --git a/src/applications/almanac/storage/AlmanacNamespace.php b/src/applications/almanac/storage/AlmanacNamespace.php
--- a/src/applications/almanac/storage/AlmanacNamespace.php
+++ b/src/applications/almanac/storage/AlmanacNamespace.php
@@ -68,6 +68,51 @@
return '/almanac/namespace/view/'.$this->getName().'/';
}
+ public function getNameLength() {
+ return strlen($this->getName());
+ }
+
+ /**
+ * Load the namespace which prevents use of an Almanac name, if one exists.
+ */
+ public static function loadRestrictedNamespace(
+ PhabricatorUser $viewer,
+ $name) {
+
+ // For a name like "x.y.z", produce a list of controlling namespaces like
+ // ("z", "y.x", "x.y.z").
+ $names = array();
+ $parts = explode('.', $name);
+ for ($ii = 0; $ii < count($parts); $ii++) {
+ $names[] = implode('.', array_slice($parts, -($ii + 1)));
+ }
+
+ // Load all the possible controlling namespaces.
+ $namespaces = id(new AlmanacNamespaceQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withNames($names)
+ ->execute();
+ if (!$namespaces) {
+ return null;
+ }
+
+ // Find the "nearest" (longest) namespace that exists. If both
+ // "sub.domain.com" and "domain.com" exist, we only care about the policy
+ // on the former.
+ $namespaces = msort($namespaces, 'getNameLength');
+ $namespace = last($namespaces);
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $namespace,
+ PhabricatorPolicyCapability::CAN_EDIT);
+ if ($can_edit) {
+ return null;
+ }
+
+ return $namespace;
+ }
+
/* -( AlmanacPropertyInterface )------------------------------------------- */
diff --git a/src/docs/user/userguide/almanac.diviner b/src/docs/user/userguide/almanac.diviner
--- a/src/docs/user/userguide/almanac.diviner
+++ b/src/docs/user/userguide/almanac.diviner
@@ -133,6 +133,52 @@
`b.mycompany.com`.
+Namespaces
+==========
+
+Almanac namespaces allow you to control who can create services and devices
+with certain names.
+
+If you keep a list of cattle as devices with names like
+`cow001.herd.myranch.com`, `cow002.herd.myranch.moo`, you might have some
+applications which query for all devices in `*.herd.myranch.moo`, and thus
+want to limit who can create devices there in order to prevent mistakes.
+
+If a namespace like `herd.myranch.moo` exists, users must have permission to
+edit the namespace in order to create new services, devices, or namespaces
+within it. For example, a user can not create `cow003.herd.myranch.moo` if
+they do not have edit permission on the `herd.myranch.moo` namespace.
+
+When you try to create a `cow003.herd.myranch.moo` service (or rename an
+existing service to have that name), Almanac looks for these namespaces, then
+checks the policy of the first one it finds:
+
+| Namespace |
+|----|-----
+| `cow003.herd.ranch.moo` | //"Nearest" namespace, considered first.//
+| `herd.ranch.moo` | |
+| `ranch.moo` | |
+| `moo` | //"Farthest" namespace, considered last.//
+
+Note that namespaces treat names as lists of domain parts, not as strict
+substrings, so the namespace `herd.myranch.moo` does not prevent
+someone from creating `goatherd.myranch.moo` or `goat001.goatherd.myranch.moo`.
+The name `goatherd.myranch.moo` is not part of the `herd.myranch.moo` namespace
+because the initial subdomain differs.
+
+If a name belongs to multiple namespaces, the policy of the nearest namespace
+is controlling. For example, if `myranch.moo` has a very restrictive edit
+policy but `shed.myranch.moo` has a more open one, users can create devices and
+services like `rake.shed.myranch.moo` as long as they can pass the policy check
+for `shed.myranch.moo`, even if they do not have permission under the policy
+for `myranch.moo`.
+
+Users can edit services and devices within a namespace if they have edit
+permission on the service or device itself, as long as they don't try to rename
+the service or device to move it into a namespace they don't have permission
+to access.
+
+
Locking and Unlocking Services
==============================

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 22, 3:33 AM (21 h, 40 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6917308
Default Alt Text
D15325.diff (11 KB)

Event Timeline