fetch full user from sql to avoid ldap lookups
[tine20] / tine20 / Tinebase / WebDav / Plugin / Inverse.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  WebDAV
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Lars Kneschke <l.kneschke@metaways.de>
9  * @author      Gabriel Malheiros <gabriel.malheiros@serpro.gov.br>
10  * @copyright   Copyright (c) 2013-2013 Metaways Infosystems GmbH (http://www.metaways.de)
11  *
12  */
13
14 /**
15  * SOGO Integrator plugin
16  *
17  * This plugin provides functionality added by SOGO Integrator
18  *
19  * @package     Tinebase
20  * @subpackage  WebDAV
21  */
22
23 class Tinebase_WebDav_Plugin_Inverse extends Sabre\DAV\ServerPlugin {
24
25     const NS_INVERSE = 'urn:inverse:params:xml:ns:inverse-dav';
26  
27     const NS_DAV = 'DAV:';
28
29     /**
30      * Reference to server object 
31      * 
32      * @var Sabre\DAV\Server 
33      */
34     private $server;
35
36     /**
37      * Returns a list of reports this plugin supports.
38      *
39      * This will be used in the {DAV:}supported-report-set property.
40      * Note that you still need to subscribe to the 'report' event to actually 
41      * implement them 
42      * 
43      * @param string $uri
44      * @return array 
45      */
46     public function getSupportedReportSet($uri) 
47     {
48         return array(
49             '{' . self::NS_DAV . '}principal-match',
50             '{' . self::NS_INVERSE . '}acl-query',
51             '{' . self::NS_INVERSE . '}user-query',
52         );
53     }
54
55     /**
56      * Initializes the plugin 
57      * 
58      * @param Sabre\DAV\Server $server 
59      * @return void
60      */
61     public function initialize(Sabre\DAV\Server $server) 
62     {
63         $this->server = $server;
64         
65         $server->subscribeEvent('unknownMethod', array($this, 'unknownMethod'));
66         $server->subscribeEvent('report',        array($this, 'report'));
67
68         $server->xmlNamespaces[Sabre\CalDAV\Plugin::NS_CALDAV]         = 'cal';
69         $server->xmlNamespaces[Sabre\CalDAV\Plugin::NS_CALENDARSERVER] = 'cs';
70
71         $server->resourceTypeMapping['Sabre\CalDAV\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
72
73         /*array_push($server->protectedProperties,
74
75             '{' . self::NS_CALDAV . '}supported-calendar-component-set',
76             '{' . self::NS_CALDAV . '}supported-calendar-data',
77             '{' . self::NS_CALDAV . '}max-resource-size',
78             '{' . self::NS_CALDAV . '}min-date-time',
79             '{' . self::NS_CALDAV . '}max-date-time',
80             '{' . self::NS_CALDAV . '}max-instances',
81             '{' . self::NS_CALDAV . '}max-attendees-per-instance',
82             '{' . self::NS_CALDAV . '}calendar-home-set',
83             '{' . self::NS_CALDAV . '}supported-collation-set',
84             '{' . self::NS_CALDAV . '}calendar-data',
85
86             // scheduling extension
87             '{' . self::NS_CALDAV . '}calendar-user-address-set',
88
89             // CalendarServer extensions
90             '{' . self::NS_CALENDARSERVER . '}getctag',
91             '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
92             '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'
93
94         );*/
95     }
96
97     /**
98      * This functions handles REPORT requests specific to SOGO Integrator 
99      * 
100      * @param  string   $reportName 
101      * @param  DOMNode  $dom
102      * @param  string   $uri
103      * @return bool
104      */
105     public function report($reportName, $dom, $uri) 
106     {
107         switch($reportName) { 
108             case '{' . self::NS_DAV . '}principal-match' :
109                 $this->principalMatch($dom);
110                 
111                 return false;
112                 
113             case '{' . self::NS_INVERSE . '}acl-query' :
114                 $this->aclQueryReport($dom, $uri);
115                 
116                 return false;
117                 
118             case '{' . self::NS_INVERSE . '}user-query' :
119                 $this->userQuery($dom);
120                 
121                 return false;
122         }
123     }
124
125     /**
126      * This function handles support for the POST method
127      *
128      * @param  string  $method
129      * @param  string  $uri
130      * @return bool
131      */
132     public function unknownMethod($method, $uri) 
133     {
134         switch ($method) {
135             case 'POST' :
136                 $body = $this->server->httpRequest->getBody(true);
137                 try {
138                     $dom = \Sabre\DAV\XMLUtil::loadDOMDocument($body);
139                 } catch (\Sabre\DAV\Exception\BadRequest $sdavebr) {
140                     return;
141                 }
142                 
143                 $reportName = \Sabre\DAV\XMLUtil::toClarkNotation($dom->firstChild);
144
145                 switch($reportName) { 
146                     case '{' . self::NS_INVERSE . '}acl-query' :
147                         $this->aclQueryPost($dom, $uri);
148                         
149                         return false;
150                 }
151         }
152     }
153     
154     /**
155      * resolve contactId to Addressbook_Model_Contact model
156      * 
157      * @param  string $contactId
158      * @throws \Sabre\DAV\Exception\NotFound
159      * @return Addressbook_Model_Contact
160      */
161     protected function _resolveContactId($contactId)
162     {
163         $filter = new Addressbook_Model_ContactFilter(array (
164             array (
165                 'field'    => 'id',
166                 'operator' => 'equals',
167                 'value'    => $contactId,
168             ),
169             array (
170                 'field'    => 'type',
171                 'operator' => 'equals',
172                 'value'    => 'user',
173             )
174         ));
175         
176         $contact = Addressbook_Controller_Contact::getInstance()
177             ->search($filter)
178             ->getFirstRecord();
179         
180         if (! $contact instanceof Addressbook_Model_Contact) {
181             throw new \Sabre\DAV\Exception\NotFound("user $contactId not found");
182         } 
183         
184         return $contact;
185     }
186     
187     /**
188      * handle acl-query post requests
189      * 
190      * @param  DOMDocument  $dom
191      * @param  string       $uri
192      */
193     public function aclQueryPost(DOMDocument $dom, $uri)
194     {
195         list($parent, $containerId) = Sabre\DAV\URLUtil::splitPath($uri);
196         
197         // handle add-user
198         $adduser = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'add-user');
199         
200         if ($adduser->length == 1) {
201             $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($containerId);
202
203             $contact = $this->_resolveContactId($adduser->item(0)->getAttribute('user'));
204             
205             $newGrant = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(
206                 array(
207                     'account_id'                           => $contact->account_id,
208                     'account_type'                         => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER,
209                     Tinebase_Model_Grants::GRANT_FREEBUSY  => true
210                 )
211             ));
212             
213             $grants->merge($newGrant);
214             
215             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
216                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' new grants: ' . print_r($grants->toArray(), true));
217
218             try {
219                 Tinebase_Container::getInstance()->setGrants($containerId, $grants);
220             } catch (Tinebase_Exception_AccessDenied $tead) {
221                 throw new \Sabre\DAV\Exception\Forbidden($tead->getMessage());
222             }
223             
224             $this->server->httpResponse->sendStatus(201);
225             $this->server->httpResponse->setHeader('Content-Type','text/xml; charset=utf-8');
226         }
227         
228         // handle remove-user
229         $removeuser = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'remove-user');
230         
231         if ($removeuser->length == 1) {
232             $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($containerId);
233
234             $contact = $this->_resolveContactId($removeuser->item(0)->getAttribute('user'));
235             
236             $newGrant = $grants->filter('account_id', $contact->account_id);
237             $grants->removeRecords($newGrant);
238
239             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
240                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' id container : ' . print_r($newGrant, true));
241
242             try {
243                 Tinebase_Container::getInstance()->setGrants($containerId, $grants);
244             } catch (Tinebase_Exception_AccessDenied $tead) {
245                 throw new \Sabre\DAV\Exception\Forbidden($tead->getMessage());
246             }
247             
248             $this->server->httpResponse->sendStatus(201);
249             $this->server->httpResponse->setHeader('Content-Type', 'text/xml; charset=utf-8');
250         }
251         
252         // handle set-roles
253         $setroles = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'set-roles');
254         
255         if ($setroles->length == 1) {
256             $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($containerId);
257
258             $contact = $this->_resolveContactId($setroles->item(0)->getAttribute('user'));
259             
260             $newGrant = $grants->filter('account_id', $contact->account_id);
261             $grants->removeRecords($newGrant);
262
263             $check = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'ObjectCreator');
264             
265             if($check->length == 1) {
266                 $newGrant->readGrant   = true;
267                 $newGrant->addGrant    = true;
268                 $newGrant->editGrant   = true;
269                 $newGrant->exportGrant = true;
270                 $newGrant->syncGrant   = true;
271             } else {
272                 $newGrant->readGrant   = false;
273                 $newGrant->addGrant    = false;
274                 $newGrant->editGrant   = false;
275                 $newGrant->exportGrant = false;
276                 $newGrant->syncGrant   = false;
277
278             }
279             
280             $check = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'ObjectEditor');
281             if ($check->length == 1) {
282                $newGrant->editGrant = true;
283             } else {
284                $newGrant->editGrant = false;
285             }
286
287             $check = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'ObjectViewer');
288             if ($check->length == 1) {
289                 $newGrant->readGrant = true;
290             } else {
291                 $newGrant->readGrant = false;
292             }
293
294             $check = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'ObjectEraser');
295             if ($check->length == 1) {
296                $newGrant->deleteGrant = true;
297             } else {
298                $newGrant->deleteGrant = false;
299             }
300
301             $grants->merge($newGrant);
302
303             try {
304                 Tinebase_Container::getInstance()->setGrants($containerId, $grants);
305             } catch (Tinebase_Exception_AccessDenied $tead) {
306                 throw new \Sabre\DAV\Exception\Forbidden($tead->getMessage());
307             }
308             
309             $this->server->httpResponse->sendStatus(201);
310             $this->server->httpResponse->setHeader('Content-Type', 'text/xml; charset=utf-8');
311         }
312         
313     }
314     
315     /**
316      * ACE reports for sogo integrator
317      * 
318      * @param DOMDocument $dom
319      * @throws Tinebase_Exception_InvalidArgument
320      */
321     public function aclQueryReport(DOMDocument $dom, $uri)
322     {
323         list($parent, $containerId) = Sabre\DAV\URLUtil::splitPath($uri);
324         
325         // handle user-list
326         $userlists = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'user-list');
327         
328         if ($userlists->length == 1) {
329             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
330                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' id container : ' . $containerId );
331             
332             $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($containerId);
333             
334             $domout = new DOMDocument('1.0','utf-8');
335             $domout->formatOutput = true;
336             
337             $userlist = $domout->createElement('user-list');
338             $domout->appendChild($userlist);
339             
340             foreach($grants as &$value) {
341                 switch($value['account_type']) {
342                     case Tinebase_Acl_Rights::ACCOUNT_TYPE_USER:
343                         try {
344                             $account = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $value['account_id'], 'Tinebase_Model_FullUser');
345                         } catch (Tinebase_Exception_NotFound $e) {
346                             $account = Tinebase_User::getInstance()->getNonExistentUser();
347                         }
348                         
349                         $userElement = $domout->createElement('user');
350                         
351                         $userElement->appendChild(new DOMElement('id',          $account->contact_id));
352                         $userElement->appendChild(new DOMElement('displayName', $account->accountFullName));
353                         $userElement->appendChild(new DOMElement('email',       $account->accountEmailAddress));
354                         
355                         $userlist->appendChild($userElement);
356                         
357                         break;
358                         
359                     case Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP:
360                         try {
361                             $group = Tinebase_Group::getInstance()->getGroupById($value['account_id']);
362                         } catch (Tinebase_Exception_Record_NotDefined $e) {
363                             $group = Tinebase_Group::getInstance()->getNonExistentGroup();
364                         }
365                         
366                         $group = $group->toArray();
367                         
368                         // @todo add xml for group
369                         
370                         break;
371                         
372                     case Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE:
373                         $userElement = $domout->createElement('user');
374                         
375                         // @todo this is not an anonymous user, but all authenticated users
376                         
377                         $userElement->appendChild(new DOMElement('id',          'anonymous'));
378                         $userElement->appendChild(new DOMElement('displayName', 'Public User'));
379                         $userElement->appendChild(new DOMElement('email',       'anonymous'));
380                         
381                         $userlist->appendChild($userElement);
382                         
383                         break;
384                         
385                     default:
386                         throw new Tinebase_Exception_InvalidArgument('Unsupported accountType.');
387                         
388                         break;
389                 }
390             }
391         
392             $xmml = $domout->saveXML();
393
394             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
395                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' id container : ' . str_replace(array("\r\n", "\n", "\r","  "),'',$xmml));
396             
397             $this->server->httpResponse->sendStatus(207);
398             $this->server->httpResponse->setHeader('Content-Type','text/xml; charset=utf-8');
399             $this->server->httpResponse->sendBody(str_replace(array("\r\n", "\n", "\r","  "),'',$xmml));
400         }
401         
402         // handle roles
403         $roles = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'roles');
404         
405         if ($roles->length == 1) {
406
407             $contact = $this->_resolveContactId($roles->item(0)->getAttribute('user'));
408             
409             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
410                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' contact id : ' . $contact->getId());
411             
412             $acls = Tinebase_Container::getInstance()->getGrantsOfAccount($contact->account_id, $containerId);
413
414             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
415                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' id container : ' . print_r($acls, true));
416             
417             $domout = new DOMDocument('1.0', 'utf-8');
418             $domout->formatOutput = true;
419             
420             $role = $domout->createElement('roles');
421             $domout->appendChild($role);
422             
423             foreach ($acls as $acl => $value) {
424                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
425                     Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' fazendo acl : ' . $acl );
426  
427                 switch ($acl) {
428                     case Tinebase_Model_Grants::GRANT_READ:
429                         if ($value) {
430                             $role->appendChild(new DOMElement('ObjectViewer'));
431                         }
432                         
433                         break;
434                         
435                     case Tinebase_Model_Grants::GRANT_PRIVATE:
436                         if ($value) {
437                             $role->appendChild(new DOMElement('PrivateViewer'));
438                         }
439                         
440                         break;
441                         
442                     case Tinebase_Model_Grants::GRANT_EDIT:
443                         if ($value) {
444                             $role->appendChild(new DOMElement('ObjectEditor'));
445                         }
446                         
447                         break;
448                         
449                     case Tinebase_Model_Grants::GRANT_ADD:
450                         if ($value) {
451                             $role->appendChild(new DOMElement('ObjectCreator'));
452                         }
453                         
454                         break;
455                         
456                     case Tinebase_Model_Grants::GRANT_DELETE:
457                         if ($value) {
458                             $role->appendChild(new DOMElement('ObjectEraser'));
459                         }
460                         
461                         break;
462                 }
463             }
464  
465             $xmml = $domout->saveXML();
466             
467             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
468                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' id container : ' . str_replace(array("\r\n", "\n", "\r","  "),'',$xmml));
469             
470             $this->server->httpResponse->sendStatus(207);
471             $this->server->httpResponse->setHeader('Content-Type', 'text/xml; charset=utf-8');
472             $this->server->httpResponse->sendBody(str_replace(array("\r\n", "\n", "\r","  "), '', $xmml));
473         }
474     }
475     
476     /**
477      * Implementing Principal Match
478      * 
479      * @param DOMDocument $dom
480      */
481     public function principalMatch(DOMDocument $dom)
482     {
483         $xml = array(
484             array(
485                 'href' => 'principals/users/' . Tinebase_Core::getUser()->contact_id
486             )
487         );
488         
489         $this->server->httpResponse->sendStatus(207);
490         $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
491         $this->server->httpResponse->sendBody($this->server->generateMultiStatus($xml));
492     }
493
494     /**
495      * 
496      * @param DOMDocument $dom
497      */
498     public function userQuery(DOMDocument $dom) 
499     {
500         $users = $dom->getElementsByTagNameNS(self::NS_INVERSE, 'users');
501         
502         if ($users->length == 1) {
503             $match = $users->item(0)->getAttribute('match-name');
504             
505             $filter = new Addressbook_Model_ContactFilter(array (
506                 array (
507                     'field'    => 'query',
508                     'operator' => 'contains',
509                     'value'    => $match,
510                 ),
511                 array (
512                     'field'    => 'type',
513                     'operator' => 'equals',
514                     'value'    => 'user',
515                 )
516             ));
517             
518             $records = Addressbook_Controller_Contact::getInstance()->search($filter);
519             
520             $domout = new DOMDocument('1.0','utf-8');
521             $domout->formatOutput = true;
522             
523             $users = $domout->createElement('users');
524             $domout->appendChild($users);
525             
526             foreach( $records as $record) {
527                 $user = $domout->createElement('user');
528                 
529                 $user->appendChild(new DOMElement('id',          $record->id));
530                 $user->appendChild(new DOMElement('displayName', $record->n_fileas));
531                 $user->appendChild(new DOMElement('email',       $record->email));
532                 
533                 $users->appendChild($user);
534             }
535
536             $xmml = $domout->saveXML();
537             
538             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
539                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' id container : ' . str_replace(array("\r\n", "\n", "\r","  "),'',$xmml));
540             
541             $this->server->httpResponse->sendStatus(207);
542             $this->server->httpResponse->setHeader('Content-Type','text/xml; charset=utf-8');
543             $this->server->httpResponse->sendBody(str_replace(array("\r\n", "\n", "\r","  "),'',$xmml));
544         }
545     }
546 }