Acl/Grant account_type should support roles too
authorPaul Mehrer <p.mehrer@metaways.de>
Thu, 15 Jun 2017 07:53:27 +0000 (09:53 +0200)
committerPaul Mehrer <p.mehrer@metaways.de>
Fri, 16 Jun 2017 11:55:43 +0000 (13:55 +0200)
Change-Id: I233ed734975fac1c2fa6828f3562a3d0e438eb63
Reviewed-on: http://gerrit.tine20.com/customers/4879
Reviewed-by: Paul Mehrer <p.mehrer@metaways.de>
Tested-by: Paul Mehrer <p.mehrer@metaways.de>
19 files changed:
tests/tine20/Calendar/Controller/EventGrantsTests.php
tests/tine20/Calendar/Frontend/WebDAV/ContainerTest.php
tests/tine20/Tinebase/WebDav/PrincipalBackendTest.php
tine20/Admin/Frontend/Json.php
tine20/Calendar/Backend/Sql.php
tine20/Calendar/Frontend/WebDAV/Container.php
tine20/Timetracker/Frontend/Json.php
tine20/Tinebase/Acl/Rights.php
tine20/Tinebase/Acl/Roles.php
tine20/Tinebase/Container.php
tine20/Tinebase/Controller/Record/Grants.php
tine20/Tinebase/Frontend/Json.php
tine20/Tinebase/Frontend/Json/Container.php
tine20/Tinebase/Model/Filter/FilterGroup.php
tine20/Tinebase/Model/Grants.php
tine20/Tinebase/Model/Role.php
tine20/Tinebase/WebDav/Container/Abstract.php
tine20/Tinebase/WebDav/Plugin/Inverse.php
tine20/Tinebase/WebDav/PrincipalBackend.php

index 7bc3bfa..f06ae7f 100644 (file)
@@ -119,7 +119,39 @@ class Calendar_Controller_EventGrantsTests extends Calendar_TestCase
         $persistentEvent = $this->_createEventInPersonasCalendar('rwright', 'rwright', 'rwright');
         
         $this->setExpectedException('Tinebase_Exception_AccessDenied');
-        $loadedEvent = $this->_uit->get($persistentEvent->getId());
+        $this->_uit->get($persistentEvent->getId());
+    }
+
+    /**
+     * set role grant for "user" to rwright calendar
+     * try to read an event of the personal calendar of rwright
+     *  -> access because of the role
+     */
+    public function testReadGrantByContainerForRoleRight()
+    {
+        $persistentEvent = $this->_createEventInPersonasCalendar('rwright', 'rwright', 'rwright');
+
+        $calendarId  = $this->_getPersonasDefaultCals('rwright')->getId();
+
+        $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($calendarId, true);
+        $grants->addRecord(new Tinebase_Model_Grants(array(
+                'account_id'    => Tinebase_Acl_Roles::getInstance()->getRoleByName('user role')->getId(),
+                'account_type'  => 'role',
+                Tinebase_Model_Grants::GRANT_READ     => true,
+                Tinebase_Model_Grants::GRANT_ADD      => true,
+                Tinebase_Model_Grants::GRANT_EDIT     => true,
+                Tinebase_Model_Grants::GRANT_DELETE   => true,
+                Tinebase_Model_Grants::GRANT_PRIVATE  => true,
+                Tinebase_Model_Grants::GRANT_ADMIN    => true,
+                Tinebase_Model_Grants::GRANT_FREEBUSY => true,
+        )));
+        Tinebase_Container::getInstance()->setGrants($calendarId, $grants, true);
+
+        $event = $this->_uit->get($persistentEvent->getId());
+        $event->summary = 'role update';
+        $updatedEvent = $this->_uit->update($event);
+
+        static::assertEquals($event->summary, $updatedEvent->summary);
     }
     
     /**
index 472c53d..1cd8dce 100644 (file)
@@ -105,9 +105,25 @@ class Calendar_Frontend_WebDAV_ContainerTest extends PHPUnit_Framework_TestCase
         
         $result = $container->getACL();
         
-        //var_dump($result);
-        
-        $this->assertEquals(6, count($result));
+        static::assertEquals(6, count($result));
+
+        $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($this->objects['initialContainer'], true);
+        $grants->addRecord(new Tinebase_Model_Grants(array(
+            'account_id'    => Tinebase_Acl_Roles::getInstance()->getRoleByName('user role')->getId(),
+            'account_type'  => 'role',
+            Tinebase_Model_Grants::GRANT_READ     => true,
+            Tinebase_Model_Grants::GRANT_ADD      => true,
+            Tinebase_Model_Grants::GRANT_EDIT     => true,
+            Tinebase_Model_Grants::GRANT_DELETE   => true,
+            Tinebase_Model_Grants::GRANT_PRIVATE  => true,
+            Tinebase_Model_Grants::GRANT_ADMIN    => true,
+            Tinebase_Model_Grants::GRANT_FREEBUSY => true,
+        )));
+        Tinebase_Container::getInstance()->setGrants($this->objects['initialContainer'], $grants, true);
+
+        $result = $container->getACL();
+
+        static::assertEquals(11, count($result));
     }
     
     public function testGetIdAsName()
index e86dbaf..6e7ac39 100644 (file)
@@ -50,7 +50,15 @@ class Tinebase_WebDav_PrincipalBackendTest extends TestCase
     {
         $principals = $this->_backend->getPrincipalsByPrefix(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS);
 
-        $this->assertGreaterThanOrEqual(1, count($principals));
+        static::assertGreaterThanOrEqual(1, count($principals));
+        $roleFound = false;
+        foreach($principals as $principal) {
+            if (strpos($principal['uri'], '/role-') !== false) {
+                $roleFound = true;
+                break;
+            }
+        }
+        static::assertTrue($roleFound, 'no role principal found');
     }
     
     /**
@@ -72,18 +80,18 @@ class Tinebase_WebDav_PrincipalBackendTest extends TestCase
         
         $principal = $this->_backend->getPrincipalByPath(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/' . $list->list_id);
         
-        //var_dump($principal);
-        
         $this->assertEquals(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/' . $list->list_id, $principal['uri']);
         $this->assertEquals($list->name . ' ('. Tinebase_Translation::getTranslation('Calendar')->_('Group') . ')', $principal['{DAV:}displayname']);
+
+        $role = Tinebase_Acl_Roles::getInstance()->getRoleByName('user role');
+        $principal = $this->_backend->getPrincipalByPath(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/role-' . $role->getId());
+        $this->assertEquals(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/role-' . $role->getId(), $principal['uri']);
     }
     
     public function testGetPrincipalByUserPath()
     {
         $principal = $this->_backend->getPrincipalByPath(Tinebase_WebDav_PrincipalBackend::PREFIX_USERS . '/' . Tinebase_Core::getUser()->contact_id);
         
-        //var_dump($principal);
-        
         $this->assertEquals(Tinebase_WebDav_PrincipalBackend::PREFIX_USERS . '/' . Tinebase_Core::getUser()->contact_id, $principal['uri']);
         $this->assertEquals(Tinebase_Core::getUser()->accountDisplayName, $principal['{DAV:}displayname']);
     }
@@ -92,11 +100,32 @@ class Tinebase_WebDav_PrincipalBackendTest extends TestCase
     {
         $groupMemberships = $this->_backend->getGroupMembership(Tinebase_WebDav_PrincipalBackend::PREFIX_USERS . '/' . Tinebase_Core::getUser()->contact_id);
         
-        //var_dump($groupMemberships);
-        
         $this->assertGreaterThanOrEqual(1, count($groupMemberships));
+        $roleFound = false;
+        foreach($groupMemberships as $membership) {
+            if (strpos($membership, '/role-') !== false) {
+                $roleFound = true;
+                break;
+            }
+        }
+        static::assertTrue($roleFound, 'no role membership found');
     }
-    
+
+    public function testSearchPrincipalsQueryForGroups()
+    {
+        if (Tinebase_User::getConfiguredBackend() === Tinebase_User::ACTIVEDIRECTORY) {
+            // account email addresses are empty with AD backend
+            $this->markTestSkipped('skipped for ad backend');
+        }
+
+        $uris = $this->_backend->searchPrincipals(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS, array(
+                '{http://calendarserver.org/ns/}search-token' => 'user')
+        );
+
+        $role = Tinebase_Acl_Roles::getInstance()->getRoleByName('user role');
+        $this->assertContains(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/role-' . $role->getId(), $uris);
+    }
+
     public function testSearchPrincipalsByEMail()
     {
         if (Tinebase_User::getConfiguredBackend() === Tinebase_User::ACTIVEDIRECTORY) {
@@ -163,7 +192,11 @@ class Tinebase_WebDav_PrincipalBackendTest extends TestCase
         
         //var_dump($groupMemberships);
         
-        $this->assertGreaterThanOrEqual(1, count($groupMemberships));
+        static::assertGreaterThanOrEqual(1, count($groupMemberships));
+
+        $role = Tinebase_Acl_Roles::getInstance()->getRoleByName('user role');
+        $groupMemberships = $this->_backend->getGroupMemberSet(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/role-' . $role->getId());
+        static::assertGreaterThanOrEqual(1, count($groupMemberships));
     }
 
     public function testGetMemberSetProxyRead()
index df84a69..3f90e22 100644 (file)
@@ -505,6 +505,13 @@ class Admin_Frontend_Json extends Tinebase_Frontend_Json_Abstract
                         $item[$prefix . 'name'] = 'Unknown group';
                     }
                     break;
+                case Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE:
+                    try {
+                        $item[$prefix . 'name'] = Tinebase_Acl_Roles::getInstance()->getRoleById($item[$prefix . 'id'])->name;
+                    } catch(Tinebase_Exception_NotFound $tenf) {
+                        $item[$prefix . 'name'] = 'Unknown role';
+                    }
+                    break;
                 case Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE:
                     $item[$prefix . 'name'] = 'Anyone';
                     break;
index a288211..90db9ca 100644 (file)
@@ -6,7 +6,7 @@
  * @subpackage  Backend
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Cornelius Weiss <c.weiss@metaways.de>
- * @copyright   Copyright (c) 2010-2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2010-2017 Metaways Infosystems GmbH (http://www.metaways.de)
  */
 
 /**
@@ -441,6 +441,12 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
             /* table  */ array('groupmemberships' => $this->_tablePrefix . 'group_members'), 
             /* on     */ $this->_db->quoteInto($this->_db->quoteIdentifier('groupmemberships.account_id') . ' = ?' , Tinebase_Core::getUser()->getId()),
             /* select */ array());
+
+        $_select->joinLeft(
+            /* table  */ array('rolememberships' => $this->_tablePrefix . 'role_accounts'),
+            /* on     */ $this->_db->quoteInto($this->_db->quoteIdentifier('rolememberships.account_id') . ' = ?' , Tinebase_Core::getUser()->getId())
+                        . ' AND ' . $this->_db->quoteInto($this->_db->quoteIdentifier('rolememberships.account_type') . ' = ?', Tinebase_Acl_Rights::ACCOUNT_TYPE_USER),
+            /* select */ array());
         
         // attendee joins the attendee we need to compute the curr users effective grants
         // NOTE: 2010-04 the behaviour changed. Now, only the attendee the client filters for are 
@@ -480,13 +486,18 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
             /* table  */ array('attendeegroupmemberships' => $this->_tablePrefix . 'group_members'), 
             /* on     */ $this->_db->quoteIdentifier('attendeegroupmemberships.account_id') . ' = ' . $this->_db->quoteIdentifier('attendeeaccounts.contact_id'),
             /* select */ array());
-        
+
+        $_select->joinLeft(
+        /* table  */ array('attendeerolememberships' => $this->_tablePrefix . 'role_accounts'),
+            /* on     */ $this->_db->quoteIdentifier('attendeerolememberships.account_id') . ' = ' . $this->_db->quoteIdentifier('attendeeaccounts.id')
+                         . ' AND ' . $this->_db->quoteInto($this->_db->quoteIdentifier('attendeerolememberships.account_type') . ' = ?', Tinebase_Acl_Rights::ACCOUNT_TYPE_USER),
+            /* select */ array());
 
         
         $_select->joinLeft(
             /* table  */ array('dispgrants' => $this->_tablePrefix . 'container_acl'), 
             /* on     */ $this->_db->quoteIdentifier('dispgrants.container_id') . ' = ' . $this->_db->quoteIdentifier('attendee.displaycontainer_id') . 
-                           ' AND ' . $this->_getContainGrantCondition('dispgrants', 'groupmemberships'),
+                           ' AND ' . $this->_getContainGrantCondition('dispgrants', 'groupmemberships', 'rolememberships'),
             /* select */ array());
         
         $_select->joinLeft(
@@ -499,13 +510,13 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
         foreach ($allGrants as $grant) {
             if (in_array($grant, $this->_recordBasedGrants)) {
                 $_select->columns(array($grant => "\n MAX( CASE WHEN ( \n" .
-                    '  /* physgrant */' . $this->_getContainGrantCondition('physgrants', 'groupmemberships', $grant) . " OR \n" . 
+                    '  /* physgrant */' . $this->_getContainGrantCondition('physgrants', 'groupmemberships', 'rolememberships', $grant) . " OR \n" .
                     '  /* implicit  */' . $this->_getImplicitGrantCondition($grant) . " OR \n" .
                     '  /* inherited */' . $this->_getInheritedGrantCondition($grant) . " \n" .
                  ") THEN 1 ELSE 0 END ) "));
             } else {
                 $_select->columns(array($grant => "\n MAX( CASE WHEN ( \n" .
-                    '  /* physgrant */' . $this->_getContainGrantCondition('physgrants', 'groupmemberships', $grant) . "\n" .
+                    '  /* physgrant */' . $this->_getContainGrantCondition('physgrants', 'groupmemberships', 'rolememberships', $grant) . "\n" .
                 ") THEN 1 ELSE 0 END ) "));
             }
         }
@@ -516,11 +527,12 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
      *
      * @param  string                               $_aclTableName
      * @param  string                               $_groupMembersTableName
+     * @param  string                               $_roleMembersTableName
      * @param  string|array                         $_requiredGrant (defaults none)
      * @param  Zend_Db_Expr|int|Tinebase_Model_User $_user (defaults current user)
      * @return string
      */
-    protected function _getContainGrantCondition($_aclTableName, $_groupMembersTableName, $_requiredGrant=NULL, $_user=NULL )
+    protected function _getContainGrantCondition($_aclTableName, $_groupMembersTableName, $_roleMembersTableName, $_requiredGrant=NULL, $_user=NULL )
     {
         $quoteTypeIdentifier = $this->_db->quoteIdentifier($_aclTableName . '.account_type');
         $quoteIdIdentifier = $this->_db->quoteIdentifier($_aclTableName . '.account_id');
@@ -533,7 +545,8 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
         }
         
         $sql = $this->_db->quoteInto(    "($quoteTypeIdentifier = ?", Tinebase_Acl_Rights::ACCOUNT_TYPE_USER)  . " AND $quoteIdIdentifier = $userExpression)" .
-               $this->_db->quoteInto(" OR ($quoteTypeIdentifier = ?", Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP) . ' AND ' . $this->_db->quoteIdentifier("$_groupMembersTableName.group_id") . " = $quoteIdIdentifier" . ')' . 
+               $this->_db->quoteInto(" OR ($quoteTypeIdentifier = ?", Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP) . ' AND ' . $this->_db->quoteIdentifier("$_groupMembersTableName.group_id") . " = $quoteIdIdentifier" . ')' .
+               $this->_db->quoteInto(" OR ($quoteTypeIdentifier = ?", Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE) . ' AND ' . $this->_db->quoteIdentifier("$_roleMembersTableName.role_id") . " = $quoteIdIdentifier" . ')' .
                $this->_db->quoteInto(" OR ($quoteTypeIdentifier = ?)", Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE);
         
         if ($_requiredGrant) {
@@ -586,14 +599,14 @@ class Calendar_Backend_Sql extends Tinebase_Backend_Sql_Abstract
     protected function _getInheritedGrantCondition($_requiredGrant)
     {
         // current user needs to have grant on display calendar
-        $sql = $this->_getContainGrantCondition('dispgrants', 'groupmemberships', $_requiredGrant);
+        $sql = $this->_getContainGrantCondition('dispgrants', 'groupmemberships', 'rolememberships', $_requiredGrant);
         
         // _AND_ attender(admin) of display calendar needs to have grant on phys calendar
         // @todo include implicit inherited grants
         if (! in_array($_requiredGrant, array(Tinebase_Model_Grants::GRANT_READ, Tinebase_Model_Grants::GRANT_FREEBUSY))) {
             $userExpr = new Zend_Db_Expr($this->_db->quoteIdentifier('attendeeaccounts.id'));
             
-            $attenderPhysGrantCond = $this->_getContainGrantCondition('physgrants', 'attendeegroupmemberships', $_requiredGrant, $userExpr);
+            $attenderPhysGrantCond = $this->_getContainGrantCondition('physgrants', 'attendeegroupmemberships', 'attendeerolememberships', $_requiredGrant, $userExpr);
             // NOTE: this condition is weak! Not some attendee must have implicit grant.
             //       -> an attender we have reqired grants for his diplay cal must have implicit grants
             //$attenderImplicitGrantCond = $this->_getImplicitGrantCondition($_requiredGrant, $userExpr);
index 1d0a745..0c65959 100644 (file)
@@ -1,9 +1,4 @@
 <?php
-
-use Sabre\VObject;
-use Sabre\DAVACL;
-use Sabre\CalDAV;
-
 /**
  * Tine 2.0
  *
@@ -11,9 +6,12 @@ use Sabre\CalDAV;
  * @subpackage  Frontend
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Lars Kneschke <l.kneschke@metaways.de>
- * @copyright   Copyright (c) 2011-2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2011-2017 Metaways Infosystems GmbH (http://www.metaways.de)
  */
 
+use Sabre\DAVACL;
+use Sabre\CalDAV;
+
 /**
  * class to handle containers in CalDAV tree
  *
@@ -357,13 +355,13 @@ class Calendar_Frontend_WebDAV_Container extends Tinebase_WebDav_Container_Abstr
         foreach ($grants as $grant) {
             
             switch ($grant->account_type) {
-                case 'anyone':
+                case Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE:
                     // was: '/principals/groups/anyone'
                     $href       = 'urn:uuid:anyone';
                     $commonName = 'Anyone';
                     break;
                 
-                case 'group':
+                case Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP:
                     try {
                         $list       = Tinebase_Group::getInstance()->getGroupById($grant->account_id);
                     } catch (Tinebase_Exception_NotFound $tenf) {
@@ -375,8 +373,21 @@ class Calendar_Frontend_WebDAV_Container extends Tinebase_WebDav_Container_Abstr
                     $commonName = $list->name;
                     
                     break;
+
+                case Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE:
+                    try {
+                        $role       = Tinebase_Acl_Roles::getInstance()->getRoleById($grant->account_id);
+                    } catch (Tinebase_Exception_NotFound $tenf) {
+                        continue 2;
+                    }
+
+                    // was: '/principals/groups/'
+                    $href       = 'urn:uuid:role-' . $role->id;
+                    $commonName = $role->name;
+
+                    break;
                     
-                case 'user':
+                case Tinebase_Acl_Rights::ACCOUNT_TYPE_USER:
                     // apple clients don't want own shares ...
                     if ((string)$this->_container->owner_id === (string)$grant->account_id) {
                         continue 2;
index 57aca6c..3f29af7 100644 (file)
@@ -105,6 +105,9 @@ class Timetracker_Frontend_Json extends Tinebase_Frontend_Json_Abstract
                         case Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE:
                             $value['account_name'] = array('accountDisplayName' => 'Anyone');
                             break;
+                        case Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE:
+                            $value['account_name'] = Tinebase_Acl_Roles::getInstance()->getRoleById($value['account_id'])->toArray();
+                            break;
                         default:
                             throw new Tinebase_Exception_InvalidArgument('Unsupported accountType.');
                             break;
index fc499cc..8229607 100644 (file)
@@ -79,6 +79,12 @@ class Tinebase_Acl_Rights extends Tinebase_Acl_Rights_Abstract
      * @staticvar string
      */
     const ACCOUNT_TYPE_GROUP    = 'group';
+
+    /**
+     * account type role
+     * @staticvar string
+     */
+    const ACCOUNT_TYPE_ROLE     = 'role';
     
     /**
      * holds the instance of the singleton
index 60402ab..d863ad2 100644 (file)
@@ -383,7 +383,7 @@ class Tinebase_Acl_Roles extends Tinebase_Controller_Record_Abstract
     /**
      * get list of role memberships
      *
-     * @param int $accountId
+     * @param int|Tinebase_Model_User $accountId
      * @param string $type
      * @return array of array with role ids
      * @throws Tinebase_Exception_InvalidArgument
@@ -959,4 +959,22 @@ class Tinebase_Acl_Roles extends Tinebase_Controller_Record_Abstract
         Tinebase_ModelConfiguration::resolveRecordsPropertiesForRecordSet($rs, $modelConf);
         return $rs->getFirstRecord();
     }
+
+    /**
+     * get dummy role record
+     *
+     * @param integer $_id [optional]
+     * @return Tinebase_Model_Role
+     */
+    public function getNonExistentRole($_id = NULL)
+    {
+        $translate = Tinebase_Translation::getTranslation('Tinebase');
+
+        $result = new Tinebase_Model_Role(array(
+            'id'        => $_id,
+            'name'      => $translate->_('unknown'),
+        ), TRUE);
+
+        return $result;
+    }
 }
index 7d51573..0df1775 100644 (file)
@@ -281,6 +281,9 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract implements Tineba
             case Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE:
                 $accountId = '0';
                 break;
+            case Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE:
+                $accountId = Tinebase_Model_Role::convertRoleIdToInt($_accountId);
+                break;
             default:
                 throw new Tinebase_Exception_InvalidArgument('invalid $_accountType');
                 break;
@@ -706,6 +709,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract implements Tineba
         $grants = is_array($_grant) ? $_grant : array($_grant);
 
         $groupMemberships   = Tinebase_Group::getInstance()->getGroupMemberships($accountId);
+        $roleMemberships    = Tinebase_Acl_Roles::getInstance()->getRoleMemberships($accountId);
         
         $quotedActId   = $db->quoteIdentifier("{$_aclTableName}.account_id");
         $quotedActType = $db->quoteIdentifier("{$_aclTableName}.account_type");
@@ -713,7 +717,8 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract implements Tineba
         $accountSelect = new Tinebase_Backend_Sql_Filter_GroupSelect($_select);
         $accountSelect
             ->orWhere("{$quotedActId} = ? AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_USER), $accountId)
-            ->orWhere("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP), empty($groupMemberships) ? ' ' : $groupMemberships);
+            ->orWhere("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP), empty($groupMemberships) ? ' ' : $groupMemberships)
+            ->orWhere("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE), empty($roleMemberships) ? ' ' : $roleMemberships);
         
         if (! Tinebase_Config::getInstance()->get(Tinebase_Config::ANYONE_ACCOUNT_DISABLED)) {
             $accountSelect->orWhere("{$quotedActType} = ?", Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE);
index 49da163..51efa4a 100644 (file)
@@ -6,7 +6,7 @@
  * @subpackage  Controller
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schüle <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014-2017 Metaways Infosystems GmbH (http://www.metaways.de)
  * 
  * @todo add caching? we could use the record seq for this.
  */
@@ -109,7 +109,7 @@ abstract class Tinebase_Controller_Record_Grants extends Tinebase_Controller_Rec
      * 
      * @param Tinebase_Record_Interface $record
      * @param string $grant
-     * @param
+     * @param Tinebase_Model_User $account
      * @return boolean
      */
     public function hasGrant($record, $grant, Tinebase_Model_User $account = null)
@@ -320,7 +320,8 @@ abstract class Tinebase_Controller_Record_Grants extends Tinebase_Controller_Rec
         if (empty($record->grants)) {
             $this->_getGrants($record);
         }
-        
+
+        $roleMemberships = Tinebase_Acl_Roles::getInstance()->getRoleMemberships($user);
         $groupMemberships = Tinebase_Group::getInstance()->getGroupMemberships($user);
         $accountGrants = new $this->_grantsModel(array(
             'account_type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER,
@@ -337,7 +338,8 @@ abstract class Tinebase_Controller_Record_Grants extends Tinebase_Controller_Rec
                     (
                         $grantRecord->account_type === Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE ||
                         $grantRecord->account_type === Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP && in_array($grantRecord->account_id, $groupMemberships) ||
-                        $grantRecord->account_type === Tinebase_Acl_Rights::ACCOUNT_TYPE_USER && $user->getId() === $grantRecord->account_id 
+                        $grantRecord->account_type === Tinebase_Acl_Rights::ACCOUNT_TYPE_USER && $user->getId() === $grantRecord->account_id ||
+                        $grantRecord->account_type === Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE && in_array($grantRecord->account_id, $roleMemberships)
                     )
                 ) {
                     $accountGrants->{$grant} = true;
index 0479671..4b61bb4 100644 (file)
@@ -157,10 +157,7 @@ class Tinebase_Frontend_Json extends Tinebase_Frontend_Json_Abstract
             'totalcount'  => 0
         );
         
-        $filter = new Tinebase_Model_RoleFilter(array(
-            'name'        => '%' . $filter[0]['value'] . '%',
-            'description' => '%' . $filter[0]['value'] . '%'
-        ));
+        $filter = new Tinebase_Model_RoleFilter($filter);
         
         $paging['sort'] = isset($paging['sort']) ? $paging['sort'] : 'name';
         $paging['dir'] = isset($paging['dir']) ? $paging['dir'] : 'ASC';
index ee5808b..a132f44 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Container
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2017 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Cornelius Weiss <c.weiss@metaways.de>
  */
 
@@ -211,6 +211,14 @@ class Tinebase_Frontend_Json_Container extends  Tinebase_Frontend_Json_Abstract
                     }
                     $value['account_name'] = $group->toArray();
                     break;
+                case Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE:
+                    try {
+                        $role = Tinebase_Acl_Roles::getInstance()->getRoleById($value['account_id'])->toArray();
+                    } catch(Tinebase_Exception_NotFound $tenf) {
+                        $role = Tinebase_Acl_Roles::getInstance()->getNonExistentRole();
+                    }
+                    $value['account_name'] = $role->toArray();
+                    break;
                 case Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE:
                     $value['account_name'] = array('accountDisplayName' => Tinebase_Translation::getTranslation('Tinebase')->_('Anyone'));
                     break;
index 76ea52e..72babc0 100644 (file)
@@ -482,7 +482,7 @@ class Tinebase_Model_Filter_FilterGroup implements Iterator
      * @param  string|array $_fieldOrData
      * @param  string $_operator
      * @param  mixed  $_value
-     * @return Tinebase_Model_Filter_Abstract
+     * @return Tinebase_Model_Filter_Abstract|Tinebase_Model_Filter_FilterGroup
      * 
      * @todo remove legacy code + obsolete params sometimes
      */
index 75dc7e9..6649f7e 100644 (file)
@@ -107,7 +107,8 @@ class Tinebase_Model_Grants extends Tinebase_Record_Abstract
             'account_type'  => array('presence' => 'required', array('InArray', array(
                 Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE,
                 Tinebase_Acl_Rights::ACCOUNT_TYPE_USER,
-                Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP
+                Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP,
+                Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE
             ))),
         );
         
@@ -192,6 +193,22 @@ class Tinebase_Model_Grants extends Tinebase_Record_Abstract
                         . ' Grant not available for current user (account_id of grant: ' . $this->account_id . ')');
                     return false;
                 }
+                break;
+            case Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE:
+                $userId = $user->getId();
+                foreach (Tinebase_Acl_Roles::getInstance()->getRoleMembers($this->account_id) as $roleMember) {
+                    if (Tinebase_Acl_Rights::ACCOUNT_TYPE_USER === $roleMember['account_type'] &&
+                            $userId === $roleMember['account_id']) {
+                        return true;
+                    }
+                    if (Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP === $roleMember['account_type'] &&
+                        in_array($user->getId(), Tinebase_Group::getInstance()->getGroupMembers($this->account_id))) {
+                        return true;
+                    }
+                }
+                if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
+                    . ' Current user not member of role ' . $this->account_id);
+                return false;
         }
         
         return true;
index b8d29e8..048394b 100644 (file)
  * @package     Tinebase
  * @subpackage  Acl
  *
+ * @property string                     $id
  * @property Tinebase_Record_RecordSet  $members
- *  */
+ * @property string                     $name
+ *
+ */
 class Tinebase_Model_Role extends Tinebase_Record_Abstract
 {
     /**
@@ -106,4 +109,18 @@ class Tinebase_Model_Role extends Tinebase_Record_Abstract
     {
         return true;
     }
+
+    /**
+     * converts a int, string or Tinebase_Model_Role to a roleid
+     *
+     * @param   int|string|Tinebase_Model_Role $_roleId the roleid to convert
+     * @return  string
+     * @throws  Tinebase_Exception_InvalidArgument
+     *
+     * @todo rename this function because we now have string ids
+     */
+    static public function convertRoleIdToInt($_roleId)
+    {
+        return self::convertId($_roleId, 'Tinebase_Model_Role');
+    }
 }
index 939b87c..bd109d4 100644 (file)
@@ -227,6 +227,18 @@ abstract class Tinebase_WebDav_Container_Abstract extends \Sabre\DAV\Collection
                     $principal = 'principals/groups/' . $group->list_id;
                     
                     break;
+
+                case Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE:
+                    try {
+                        $role = Tinebase_Acl_Roles::getInstance()->getRoleById($grant->account_id);
+                    } catch (Tinebase_Exception_NotFound $tenf) {
+                        // skip role
+                        continue 2;
+                    }
+
+                    $principal = 'principals/groups/role-' . $role->id;
+
+                    break;
                     
                 case Tinebase_Acl_Rights::ACCOUNT_TYPE_USER:
                     try {
index 3b83218..a45c1a7 100644 (file)
@@ -7,7 +7,8 @@
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  * @author      Gabriel Malheiros <gabriel.malheiros@serpro.gov.br>
- * @copyright   Copyright (c) 2013-2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2013-2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ *
  *
  */
 
@@ -18,6 +19,8 @@
  *
  * @package     Tinebase
  * @subpackage  WebDAV
+ *
+ * @deprecated remove this
  */
 
 class Tinebase_WebDav_Plugin_Inverse extends Sabre\DAV\ServerPlugin {
index 992474e..94d2df1 100644 (file)
@@ -46,6 +46,10 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
                 foreach ($lists as $list) {
                     $principals[] = $this->_listToPrincipal($list, $prefixPath);
                 }
+
+                foreach (Tinebase_Acl_Roles::getInstance()->getAll() as $role) {
+                    $principals[] = $this->_roleToPrincipal($role, $prefixPath);
+                }
                 
                 break;
                 
@@ -122,29 +126,46 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
                 
             case self::PREFIX_GROUPS:
             case self::PREFIX_INTELLIGROUPS:
-                $filter = new Addressbook_Model_ListFilter(array(
-                    array(
-                        'field'     => 'type',
-                        'operator'  => 'equals',
-                        'value'     => Addressbook_Model_List::LISTTYPE_GROUP
-                    ),
-                    array(
-                        'field'     => 'id',
-                        'operator'  => 'equals',
-                        'value'     => $id
-                    ),
-                ));
-                
-                $list = Addressbook_Controller_List::getInstance()->search($filter)->getFirstRecord();
-                
-                if (! $list) {
-                    if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(
-                            __METHOD__ . '::' . __LINE__ . ' Group/list principal does not exist: ' . $id);
-                    return null;
+                if (0 === strpos($id, 'role-')) {
+                    try {
+                        $role = Tinebase_Acl_Roles::getInstance()->getRoleById(substr($id, 5));
+                    } catch(Tinebase_Exception_NotFound $tenf) {
+                        if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
+                            Tinebase_Core::getLogger()->notice(
+                                __METHOD__ . '::' . __LINE__ . ' Role(group) principal does not exist: ' . $id);
+                        }
+                        return null;
+                    }
+
+                    $principal = $this->_roleToPrincipal($role, $prefix);
+
+                } else {
+                    $filter = new Addressbook_Model_ListFilter(array(
+                        array(
+                            'field' => 'type',
+                            'operator' => 'equals',
+                            'value' => Addressbook_Model_List::LISTTYPE_GROUP
+                        ),
+                        array(
+                            'field' => 'id',
+                            'operator' => 'equals',
+                            'value' => $id
+                        ),
+                    ));
+
+                    $list = Addressbook_Controller_List::getInstance()->search($filter)->getFirstRecord();
+
+                    if (!$list) {
+                        if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
+                            Tinebase_Core::getLogger()->notice(
+                                __METHOD__ . '::' . __LINE__ . ' Group/list principal does not exist: ' . $id);
+                        }
+                        return null;
+                    }
+
+                    $principal = $this->_listToPrincipal($list, $prefix);
                 }
                 
-                $principal = $this->_listToPrincipal($list, $prefix);
-                
                 break;
                 
             case self::PREFIX_USERS:
@@ -223,11 +244,7 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
             case 'calendar-proxy-read':
                 return array();
                 
-                break;
-                
             case 'calendar-proxy-write':
-                $result = array();
-                
                 $applications = array(
                     'Calendar' => 'Calendar_Model_Event',
                     'Tasks'    => 'Tasks_Model_Task'
@@ -236,7 +253,7 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
                 foreach ($applications as $application => $model) {
                     if ($id === self::SHARED) {
                         // check if account has the right to run the calendar at all
-                        if (!Tinebase_Acl_Roles::getInstance()->hasRight($application, Tinebase_Core::getUser(), Tinebase_Acl_Rights::RUN)) {
+                        if (!Tinebase_Acl_Roles::getInstance()->hasRight($application, Tinebase_Core::getUser()->getId(), Tinebase_Acl_Rights::RUN)) {
                             continue;
                         }
                         
@@ -286,27 +303,49 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
                 
             case self::PREFIX_GROUPS:
             case self::PREFIX_INTELLIGROUPS:
-                $filter = new Addressbook_Model_ListFilter(array(
-                    array(
-                        'field'     => 'type',
-                        'operator'  => 'equals',
-                        'value'     => Addressbook_Model_List::LISTTYPE_GROUP
-                    ),
-                    array(
-                        'field'     => 'id',
-                        'operator'  => 'equals',
-                        'value'     => $id
-                    ),
-                ));
-                
-                $list = Addressbook_Controller_List::getInstance()->search($filter)->getFirstRecord();
-                
-                if (!$list) {
-                    return array();
-                }
-                
-                foreach ($list->members as $member) {
-                    $result[] = self::PREFIX_USERS . '/' . $member;
+                if (0 === strpos($id, 'role-')) {
+                    $accounts = array();
+                    $groups = array();
+                    foreach (Tinebase_Acl_Roles::getInstance()->getRoleMembers(substr($id, 5)) as $roleMember) {
+                        if (Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP === $roleMember['account_type']) {
+                            $groups[] = $roleMember['account_id'];
+                        } elseif (Tinebase_Acl_Rights::ACCOUNT_TYPE_USER === $roleMember['account_type']) {
+                            $accounts[] = $roleMember['account_id'];
+                        } else {
+                            Tinebase_Core::getLogger()->err($id .
+                                ' has a member of unknown type: ' . print_r($roleMember));
+                            continue;
+                        }
+                    }
+
+                    if (count($groups) > 0) {
+                        /** @var Tinebase_Model_Group $group */
+                        foreach (Tinebase_Group::getInstance()->getMultiple($groups) as $group) {
+                            if (empty($group->list_id) ||
+                                    $group->visibility !== Tinebase_Model_Group::VISIBILITY_DISPLAYED) {
+                                continue;
+                            }
+                            $result =
+                                array_merge($result, $this->_resolveListToUserPrincipals($group->list_id));
+                        }
+                    }
+
+
+                    if (count($accounts) > 0) {
+                        /** @var Tinebase_Model_FullUser $user */
+                        foreach (Tinebase_User::getInstance()->getMultiple($accounts, 'Tinebase_Model_FullUser') as $user) {
+                            if (Tinebase_Model_User::VISIBILITY_DISPLAYED !== $user->visibility ||
+                                    Tinebase_Model_User::ACCOUNT_STATUS_DISABLED === $user->accountStatus) {
+                                continue;
+                            }
+                            $result[] = self::PREFIX_USERS . '/' . $user->contact_id;
+                        }
+                    }
+
+                    $result = array_unique($result);
+
+                } else {
+                    $result = $this->_resolveListToUserPrincipals($id);
                 }
                 
                 break;
@@ -314,7 +353,39 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
         
         return $result;
     }
-    
+
+    /**
+     * @param string $id
+     * @return array
+     */
+    protected function _resolveListToUserPrincipals($id)
+    {
+        $filter = new Addressbook_Model_ListFilter(array(
+            array(
+                'field' => 'type',
+                'operator' => 'equals',
+                'value' => Addressbook_Model_List::LISTTYPE_GROUP
+            ),
+            array(
+                'field' => 'id',
+                'operator' => 'equals',
+                'value' => $id
+            ),
+        ));
+
+        $list = Addressbook_Controller_List::getInstance()->search($filter)->getFirstRecord();
+
+        if (!$list) {
+            return array();
+        }
+
+        $result = array();
+        foreach ($list->members as $member) {
+            $result[] = self::PREFIX_USERS . '/' . $member;
+        }
+        return $result;
+    }
+
     /**
      * (non-PHPdoc)
      * @see Sabre\DAVACL\IPrincipalBackend::getGroupMembership()
@@ -357,12 +428,20 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
                     
                     $groupIds = Tinebase_Group::getInstance()->getGroupMemberships($user);
                     $groups   = Tinebase_Group::getInstance()->getMultiple($groupIds);
-                    
+
+                    /** @var Tinebase_Model_Group $group */
                     foreach ($groups as $group) {
                         if ($group->list_id && $group->visibility == Tinebase_Model_Group::VISIBILITY_DISPLAYED) {
                             $result[] = self::PREFIX_GROUPS . '/' . $group->list_id;
                         }
                     }
+
+                    $roleIds = Tinebase_Acl_Roles::getInstance()->getRoleMemberships($user->getId());
+                    //$roles   = Tinebase_Acl_Roles::getInstance()->getMultiple($roleIds);
+
+                    foreach ($roleIds as $role) {
+                        $result[] = self::PREFIX_GROUPS . '/role-' . $role;
+                    }
                     
                     if (Tinebase_Core::getUser()->hasRight('Calendar', Tinebase_Acl_Rights::RUN)) {
                         // return users only, if they have the sync AND read grant set
@@ -468,7 +547,33 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
                 foreach ($result as $listId) {
                     $principalUris[] = $prefixPath . '/' . $listId;
                 }
-                
+
+                $filter = null;
+                if (!empty($searchProperties['{http://calendarserver.org/ns/}search-token'])) {
+                    $filter = new Tinebase_Model_RoleFilter(array(array(
+                        'field'     => 'query',
+                        'operator'  => 'contains',
+                        'value'     => $searchProperties['{http://calendarserver.org/ns/}search-token']
+                    )));
+                }
+
+                if (!empty($searchProperties['{DAV:}displayname'])) {
+                    if (null === $filter) {
+                        $filter = new Tinebase_Model_RoleFilter(array());
+                    }
+                    $filter->addFilter($filter->createFilter(array(
+                        'field'     => 'name',
+                        'operator'  => 'contains',
+                        'value'     => $searchProperties['{DAV:}displayname']
+                    )));
+                }
+
+                if (null !== $filter) {
+                    foreach (Tinebase_Acl_Roles::getInstance()->search($filter, null, false, true) as $roleId) {
+                        $principalUris[] = $prefixPath . '/role-' . $roleId;
+                    }
+                }
+
                 break;
                 
             case self::PREFIX_USERS:
@@ -606,24 +711,50 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
                 
                 foreach ($grants as $grant) {
                     switch ($grant->account_type) {
-                        case 'group':
-                            $group = Tinebase_Group::getInstance()->getGroupById($grant->account_id);
-                            if ($group->list_id) {
-                                $containerPrincipals[] = self::PREFIX_GROUPS . '/' . $group->list_id;
+                        case Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP:
+                            try {
+                                $group = Tinebase_Group::getInstance()->getGroupById($grant->account_id);
+                                if ($group->list_id) {
+                                    $containerPrincipals[] = self::PREFIX_GROUPS . '/' . $group->list_id;
+                                }
+                            } catch (Tinebase_Exception_Record_NotDefined $ternd) {
+                                // skip group
+                                continue 2;
+                            } catch (Tinebase_Exception_NotFound $tenf) {
+                                // skip group
+                                continue 2;
                             }
                             break;
-                            
-                        case 'user':
+
+                        case Tinebase_Acl_Rights::ACCOUNT_TYPE_USER:
                             // skip if grant belongs to the owner of the calendar
                             if ($container->owner_id == $grant->account_id) {
                                 continue 2;
                             }
-                            $user = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $grant->account_id);
-                            if ($user->contact_id) {
-                                $containerPrincipals[] = self::PREFIX_USERS . '/' . $user->contact_id;
+                            try {
+                                $user = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $grant->account_id);
+                                if ($user->contact_id) {
+                                    $containerPrincipals[] = self::PREFIX_USERS . '/' . $user->contact_id;
+                                }
+                            } catch (Tinebase_Exception_Record_NotDefined $ternd) {
+                                // skip group
+                                continue 2;
+                            } catch (Tinebase_Exception_NotFound $tenf) {
+                                // skip user
+                                continue 2;
                             }
                             
                             break;
+
+                        case Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE:
+                            try {
+                                $role = Tinebase_Acl_Roles::getInstance()->getRoleById($grant->account_id);
+                                $containerPrincipals[] = self::PREFIX_GROUPS . '/role-' . $role->id;
+                            } catch (Tinebase_Exception_NotFound $tenf) {
+                                // skip role
+                                continue 2;
+                            }
+                            break;
                     }
                 }
                 
@@ -669,4 +800,35 @@ class Tinebase_WebDav_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend
 
         return $principal;
     }
+
+    /**
+     * convert role model to principal array
+     *
+     * @param Tinebase_Model_Role $role
+     * @param string $prefix
+     * @return array
+     */
+    protected function _roleToPrincipal(Tinebase_Model_Role $role, $prefix)
+    {
+        $calUserType = $prefix == self::PREFIX_INTELLIGROUPS ? 'INTELLIGROUP' : 'GROUP';
+
+        $principal = array(
+            'uri'                     => $prefix . '/role-' . $role->getId(),
+            '{DAV:}displayname'       => $role->name . ' (' . $translation = Tinebase_Translation::getTranslation('Calendar')->_('Group') . ')',
+            '{DAV:}alternate-URI-set' => array('urn:uuid:' . $prefix . '/role-' . $role->getId()),
+
+            '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-user-type'  => $calUserType,
+
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}record-type' => 'groups',
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}first-name'  => Tinebase_Translation::getTranslation('Calendar')->_('Group'),
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}last-name'   => $role->name,
+        );
+
+        if ($calUserType == 'INTELLIGROUP') {
+            // OSX needs an email adress to send the attendee
+            $principal['{http://sabredav.org/ns}email-address'] = 'urn:uuid:' . $prefix . '/role-' . $role->getId();
+        }
+
+        return $principal;
+    }
 }