1<?php 2 3namespace Sabre\DAVACL\Xml\Property; 4 5use Sabre\DAV; 6use Sabre\DAV\Browser\HtmlOutput; 7use Sabre\DAV\Browser\HtmlOutputHelper; 8use Sabre\Xml\Element; 9use Sabre\Xml\Reader; 10use Sabre\Xml\Writer; 11 12/** 13 * This class represents the {DAV:}acl property. 14 * 15 * The {DAV:}acl property is a full list of access control entries for a 16 * resource. 17 * 18 * {DAV:}acl is used as a WebDAV property, but it is also used within the body 19 * of the ACL request. 20 * 21 * See: 22 * http://tools.ietf.org/html/rfc3744#section-5.5 23 * 24 * @copyright Copyright (C) fruux GmbH (https://fruux.com/) 25 * @author Evert Pot (http://evertpot.com/) 26 * @license http://sabre.io/license/ Modified BSD License 27 */ 28class Acl implements Element, HtmlOutput { 29 30 /** 31 * List of privileges 32 * 33 * @var array 34 */ 35 protected $privileges; 36 37 /** 38 * Whether or not the server base url is required to be prefixed when 39 * serializing the property. 40 * 41 * @var bool 42 */ 43 protected $prefixBaseUrl; 44 45 /** 46 * Constructor 47 * 48 * This object requires a structure similar to the return value from 49 * Sabre\DAVACL\Plugin::getACL(). 50 * 51 * Each privilege is a an array with at least a 'privilege' property, and a 52 * 'principal' property. A privilege may have a 'protected' property as 53 * well. 54 * 55 * The prefixBaseUrl should be set to false, if the supplied principal urls 56 * are already full urls. If this is kept to true, the servers base url 57 * will automatically be prefixed. 58 * 59 * @param array $privileges 60 * @param bool $prefixBaseUrl 61 */ 62 function __construct(array $privileges, $prefixBaseUrl = true) { 63 64 $this->privileges = $privileges; 65 $this->prefixBaseUrl = $prefixBaseUrl; 66 67 } 68 69 /** 70 * Returns the list of privileges for this property 71 * 72 * @return array 73 */ 74 function getPrivileges() { 75 76 return $this->privileges; 77 78 } 79 80 /** 81 * The xmlSerialize method is called during xml writing. 82 * 83 * Use the $writer argument to write its own xml serialization. 84 * 85 * An important note: do _not_ create a parent element. Any element 86 * implementing XmlSerializable should only ever write what's considered 87 * its 'inner xml'. 88 * 89 * The parent of the current element is responsible for writing a 90 * containing element. 91 * 92 * This allows serializers to be re-used for different element names. 93 * 94 * If you are opening new elements, you must also close them again. 95 * 96 * @param Writer $writer 97 * @return void 98 */ 99 function xmlSerialize(Writer $writer) { 100 101 foreach ($this->privileges as $ace) { 102 103 $this->serializeAce($writer, $ace); 104 105 } 106 107 } 108 109 /** 110 * Generate html representation for this value. 111 * 112 * The html output is 100% trusted, and no effort is being made to sanitize 113 * it. It's up to the implementor to sanitize user provided values. 114 * 115 * The output must be in UTF-8. 116 * 117 * The baseUri parameter is a url to the root of the application, and can 118 * be used to construct local links. 119 * 120 * @param HtmlOutputHelper $html 121 * @return string 122 */ 123 function toHtml(HtmlOutputHelper $html) { 124 125 ob_start(); 126 echo "<table>"; 127 echo "<tr><th>Principal</th><th>Privilege</th><th></th></tr>"; 128 foreach ($this->privileges as $privilege) { 129 130 echo '<tr>'; 131 // if it starts with a {, it's a special principal 132 if ($privilege['principal'][0] === '{') { 133 echo '<td>', $html->xmlName($privilege['principal']), '</td>'; 134 } else { 135 echo '<td>', $html->link($privilege['principal']), '</td>'; 136 } 137 echo '<td>', $html->xmlName($privilege['privilege']), '</td>'; 138 echo '<td>'; 139 if (!empty($privilege['protected'])) echo '(protected)'; 140 echo '</td>'; 141 echo '</tr>'; 142 143 } 144 echo "</table>"; 145 return ob_get_clean(); 146 147 } 148 149 /** 150 * The deserialize method is called during xml parsing. 151 * 152 * This method is called statically, this is because in theory this method 153 * may be used as a type of constructor, or factory method. 154 * 155 * Often you want to return an instance of the current class, but you are 156 * free to return other data as well. 157 * 158 * Important note 2: You are responsible for advancing the reader to the 159 * next element. Not doing anything will result in a never-ending loop. 160 * 161 * If you just want to skip parsing for this element altogether, you can 162 * just call $reader->next(); 163 * 164 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to 165 * the next element. 166 * 167 * @param Reader $reader 168 * @return mixed 169 */ 170 static function xmlDeserialize(Reader $reader) { 171 172 $elementMap = [ 173 '{DAV:}ace' => 'Sabre\Xml\Element\KeyValue', 174 '{DAV:}privilege' => 'Sabre\Xml\Element\Elements', 175 '{DAV:}principal' => 'Sabre\DAVACL\Xml\Property\Principal', 176 ]; 177 178 $privileges = []; 179 180 foreach ((array)$reader->parseInnerTree($elementMap) as $element) { 181 182 if ($element['name'] !== '{DAV:}ace') { 183 continue; 184 } 185 $ace = $element['value']; 186 187 if (empty($ace['{DAV:}principal'])) { 188 throw new DAV\Exception\BadRequest('Each {DAV:}ace element must have one {DAV:}principal element'); 189 } 190 $principal = $ace['{DAV:}principal']; 191 192 switch ($principal->getType()) { 193 case Principal::HREF : 194 $principal = $principal->getHref(); 195 break; 196 case Principal::AUTHENTICATED : 197 $principal = '{DAV:}authenticated'; 198 break; 199 case Principal::UNAUTHENTICATED : 200 $principal = '{DAV:}unauthenticated'; 201 break; 202 case Principal::ALL : 203 $principal = '{DAV:}all'; 204 break; 205 206 } 207 208 $protected = array_key_exists('{DAV:}protected', $ace); 209 210 if (!isset($ace['{DAV:}grant'])) { 211 throw new DAV\Exception\NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported'); 212 } 213 foreach ($ace['{DAV:}grant'] as $elem) { 214 if ($elem['name'] !== '{DAV:}privilege') { 215 continue; 216 } 217 218 foreach ($elem['value'] as $priv) { 219 $privileges[] = [ 220 'principal' => $principal, 221 'protected' => $protected, 222 'privilege' => $priv, 223 ]; 224 } 225 226 } 227 228 } 229 230 return new self($privileges); 231 232 } 233 234 /** 235 * Serializes a single access control entry. 236 * 237 * @param Writer $writer 238 * @param array $ace 239 * @return void 240 */ 241 private function serializeAce(Writer $writer, array $ace) { 242 243 $writer->startElement('{DAV:}ace'); 244 245 switch ($ace['principal']) { 246 case '{DAV:}authenticated' : 247 $principal = new Principal(Principal::AUTHENTICATED); 248 break; 249 case '{DAV:}unauthenticated' : 250 $principal = new Principal(Principal::UNAUTHENTICATED); 251 break; 252 case '{DAV:}all' : 253 $principal = new Principal(Principal::ALL); 254 break; 255 default: 256 $principal = new Principal(Principal::HREF, $ace['principal']); 257 break; 258 } 259 260 $writer->writeElement('{DAV:}principal', $principal); 261 $writer->startElement('{DAV:}grant'); 262 $writer->startElement('{DAV:}privilege'); 263 264 $writer->writeElement($ace['privilege']); 265 266 $writer->endElement(); // privilege 267 $writer->endElement(); // grant 268 269 if (!empty($ace['protected'])) { 270 $writer->writeElement('{DAV:}protected'); 271 } 272 273 $writer->endElement(); // ace 274 275 } 276 277} 278