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