"Fossies" - the Fresh Open Source Software Archive

Member "dav-4.0.3/lib/DAVACL/PrincipalBackend/PDO.php" (10 Jan 2020, 13984 Bytes) of package /linux/www/dav-4.0.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "PDO.php" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.0.2_vs_4.0.3.

    1 <?php
    2 
    3 declare(strict_types=1);
    4 
    5 namespace Sabre\DAVACL\PrincipalBackend;
    6 
    7 use Sabre\DAV;
    8 use Sabre\DAV\MkCol;
    9 use Sabre\Uri;
   10 
   11 /**
   12  * PDO principal backend.
   13  *
   14  *
   15  * This backend assumes all principals are in a single collection. The default collection
   16  * is 'principals/', but this can be overridden.
   17  *
   18  * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
   19  * @author Evert Pot (http://evertpot.com/)
   20  * @license http://sabre.io/license/ Modified BSD License
   21  */
   22 class PDO extends AbstractBackend implements CreatePrincipalSupport
   23 {
   24     /**
   25      * PDO table name for 'principals'.
   26      *
   27      * @var string
   28      */
   29     public $tableName = 'principals';
   30 
   31     /**
   32      * PDO table name for 'group members'.
   33      *
   34      * @var string
   35      */
   36     public $groupMembersTableName = 'groupmembers';
   37 
   38     /**
   39      * pdo.
   40      *
   41      * @var PDO
   42      */
   43     protected $pdo;
   44 
   45     /**
   46      * A list of additional fields to support.
   47      *
   48      * @var array
   49      */
   50     protected $fieldMap = [
   51         /*
   52          * This property can be used to display the users' real name.
   53          */
   54         '{DAV:}displayname' => [
   55             'dbField' => 'displayname',
   56         ],
   57 
   58         /*
   59          * This is the users' primary email-address.
   60          */
   61         '{http://sabredav.org/ns}email-address' => [
   62             'dbField' => 'email',
   63         ],
   64     ];
   65 
   66     /**
   67      * Sets up the backend.
   68      *
   69      * @param \PDO $pdo
   70      */
   71     public function __construct(\PDO $pdo)
   72     {
   73         $this->pdo = $pdo;
   74     }
   75 
   76     /**
   77      * Returns a list of principals based on a prefix.
   78      *
   79      * This prefix will often contain something like 'principals'. You are only
   80      * expected to return principals that are in this base path.
   81      *
   82      * You are expected to return at least a 'uri' for every user, you can
   83      * return any additional properties if you wish so. Common properties are:
   84      *   {DAV:}displayname
   85      *   {http://sabredav.org/ns}email-address - This is a custom SabreDAV
   86      *     field that's actualy injected in a number of other properties. If
   87      *     you have an email address, use this property.
   88      *
   89      * @param string $prefixPath
   90      *
   91      * @return array
   92      */
   93     public function getPrincipalsByPrefix($prefixPath)
   94     {
   95         $fields = [
   96             'uri',
   97         ];
   98 
   99         foreach ($this->fieldMap as $key => $value) {
  100             $fields[] = $value['dbField'];
  101         }
  102         $result = $this->pdo->query('SELECT '.implode(',', $fields).'  FROM '.$this->tableName);
  103 
  104         $principals = [];
  105 
  106         while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
  107             // Checking if the principal is in the prefix
  108             list($rowPrefix) = Uri\split($row['uri']);
  109             if ($rowPrefix !== $prefixPath) {
  110                 continue;
  111             }
  112 
  113             $principal = [
  114                 'uri' => $row['uri'],
  115             ];
  116             foreach ($this->fieldMap as $key => $value) {
  117                 if ($row[$value['dbField']]) {
  118                     $principal[$key] = $row[$value['dbField']];
  119                 }
  120             }
  121             $principals[] = $principal;
  122         }
  123 
  124         return $principals;
  125     }
  126 
  127     /**
  128      * Returns a specific principal, specified by it's path.
  129      * The returned structure should be the exact same as from
  130      * getPrincipalsByPrefix.
  131      *
  132      * @param string $path
  133      *
  134      * @return array
  135      */
  136     public function getPrincipalByPath($path)
  137     {
  138         $fields = [
  139             'id',
  140             'uri',
  141         ];
  142 
  143         foreach ($this->fieldMap as $key => $value) {
  144             $fields[] = $value['dbField'];
  145         }
  146         $stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).'  FROM '.$this->tableName.' WHERE uri = ?');
  147         $stmt->execute([$path]);
  148 
  149         $row = $stmt->fetch(\PDO::FETCH_ASSOC);
  150         if (!$row) {
  151             return;
  152         }
  153 
  154         $principal = [
  155             'id' => $row['id'],
  156             'uri' => $row['uri'],
  157         ];
  158         foreach ($this->fieldMap as $key => $value) {
  159             if ($row[$value['dbField']]) {
  160                 $principal[$key] = $row[$value['dbField']];
  161             }
  162         }
  163 
  164         return $principal;
  165     }
  166 
  167     /**
  168      * Updates one ore more webdav properties on a principal.
  169      *
  170      * The list of mutations is stored in a Sabre\DAV\PropPatch object.
  171      * To do the actual updates, you must tell this object which properties
  172      * you're going to process with the handle() method.
  173      *
  174      * Calling the handle method is like telling the PropPatch object "I
  175      * promise I can handle updating this property".
  176      *
  177      * Read the PropPatch documentation for more info and examples.
  178      *
  179      * @param string        $path
  180      * @param DAV\PropPatch $propPatch
  181      */
  182     public function updatePrincipal($path, DAV\PropPatch $propPatch)
  183     {
  184         $propPatch->handle(array_keys($this->fieldMap), function ($properties) use ($path) {
  185             $query = 'UPDATE '.$this->tableName.' SET ';
  186             $first = true;
  187 
  188             $values = [];
  189 
  190             foreach ($properties as $key => $value) {
  191                 $dbField = $this->fieldMap[$key]['dbField'];
  192 
  193                 if (!$first) {
  194                     $query .= ', ';
  195                 }
  196                 $first = false;
  197                 $query .= $dbField.' = :'.$dbField;
  198                 $values[$dbField] = $value;
  199             }
  200 
  201             $query .= ' WHERE uri = :uri';
  202             $values['uri'] = $path;
  203 
  204             $stmt = $this->pdo->prepare($query);
  205             $stmt->execute($values);
  206 
  207             return true;
  208         });
  209     }
  210 
  211     /**
  212      * This method is used to search for principals matching a set of
  213      * properties.
  214      *
  215      * This search is specifically used by RFC3744's principal-property-search
  216      * REPORT.
  217      *
  218      * The actual search should be a unicode-non-case-sensitive search. The
  219      * keys in searchProperties are the WebDAV property names, while the values
  220      * are the property values to search on.
  221      *
  222      * By default, if multiple properties are submitted to this method, the
  223      * various properties should be combined with 'AND'. If $test is set to
  224      * 'anyof', it should be combined using 'OR'.
  225      *
  226      * This method should simply return an array with full principal uri's.
  227      *
  228      * If somebody attempted to search on a property the backend does not
  229      * support, you should simply return 0 results.
  230      *
  231      * You can also just return 0 results if you choose to not support
  232      * searching at all, but keep in mind that this may stop certain features
  233      * from working.
  234      *
  235      * @param string $prefixPath
  236      * @param array  $searchProperties
  237      * @param string $test
  238      *
  239      * @return array
  240      */
  241     public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof')
  242     {
  243         if (0 == count($searchProperties)) {
  244             return [];
  245         }    //No criteria
  246 
  247         $query = 'SELECT uri FROM '.$this->tableName.' WHERE ';
  248         $values = [];
  249         foreach ($searchProperties as $property => $value) {
  250             switch ($property) {
  251                 case '{DAV:}displayname':
  252                     $column = 'displayname';
  253                     break;
  254                 case '{http://sabredav.org/ns}email-address':
  255                     $column = 'email';
  256                     break;
  257                 default:
  258                     // Unsupported property
  259                     return [];
  260             }
  261             if (count($values) > 0) {
  262                 $query .= (0 == strcmp($test, 'anyof') ? ' OR ' : ' AND ');
  263             }
  264             $query .= 'lower('.$column.') LIKE lower(?)';
  265             $values[] = '%'.$value.'%';
  266         }
  267         $stmt = $this->pdo->prepare($query);
  268         $stmt->execute($values);
  269 
  270         $principals = [];
  271         while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
  272             // Checking if the principal is in the prefix
  273             list($rowPrefix) = Uri\split($row['uri']);
  274             if ($rowPrefix !== $prefixPath) {
  275                 continue;
  276             }
  277 
  278             $principals[] = $row['uri'];
  279         }
  280 
  281         return $principals;
  282     }
  283 
  284     /**
  285      * Finds a principal by its URI.
  286      *
  287      * This method may receive any type of uri, but mailto: addresses will be
  288      * the most common.
  289      *
  290      * Implementation of this API is optional. It is currently used by the
  291      * CalDAV system to find principals based on their email addresses. If this
  292      * API is not implemented, some features may not work correctly.
  293      *
  294      * This method must return a relative principal path, or null, if the
  295      * principal was not found or you refuse to find it.
  296      *
  297      * @param string $uri
  298      * @param string $principalPrefix
  299      *
  300      * @return string
  301      */
  302     public function findByUri($uri, $principalPrefix)
  303     {
  304         $uriParts = Uri\parse($uri);
  305 
  306         // Only two types of uri are supported :
  307         //   - the "mailto:" scheme with some non-empty address
  308         //   - a principals uri, in the form "principals/NAME"
  309         // In both cases, `path` must not be empty.
  310         if (empty($uriParts['path'])) {
  311             return null;
  312         }
  313 
  314         $uri = null;
  315         if ('mailto' === $uriParts['scheme']) {
  316             $query = 'SELECT uri FROM '.$this->tableName.' WHERE lower(email)=lower(?)';
  317             $stmt = $this->pdo->prepare($query);
  318             $stmt->execute([$uriParts['path']]);
  319 
  320             while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
  321                 // Checking if the principal is in the prefix
  322                 list($rowPrefix) = Uri\split($row['uri']);
  323                 if ($rowPrefix !== $principalPrefix) {
  324                     continue;
  325                 }
  326 
  327                 $uri = $row['uri'];
  328                 break; //Stop on first match
  329             }
  330         } else {
  331             $pathParts = Uri\split($uriParts['path']); // We can do this since $uriParts['path'] is not null
  332 
  333             if (2 === count($pathParts) && $pathParts[0] === $principalPrefix) {
  334                 // Checking that this uri exists
  335                 $query = 'SELECT * FROM '.$this->tableName.' WHERE uri = ?';
  336                 $stmt = $this->pdo->prepare($query);
  337                 $stmt->execute([$uriParts['path']]);
  338                 $rows = $stmt->fetchAll();
  339 
  340                 if (count($rows) > 0) {
  341                     $uri = $uriParts['path'];
  342                 }
  343             }
  344         }
  345 
  346         return $uri;
  347     }
  348 
  349     /**
  350      * Returns the list of members for a group-principal.
  351      *
  352      * @param string $principal
  353      *
  354      * @return array
  355      */
  356     public function getGroupMemberSet($principal)
  357     {
  358         $principal = $this->getPrincipalByPath($principal);
  359         if (!$principal) {
  360             throw new DAV\Exception('Principal not found');
  361         }
  362         $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?');
  363         $stmt->execute([$principal['id']]);
  364 
  365         $result = [];
  366         while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
  367             $result[] = $row['uri'];
  368         }
  369 
  370         return $result;
  371     }
  372 
  373     /**
  374      * Returns the list of groups a principal is a member of.
  375      *
  376      * @param string $principal
  377      *
  378      * @return array
  379      */
  380     public function getGroupMembership($principal)
  381     {
  382         $principal = $this->getPrincipalByPath($principal);
  383         if (!$principal) {
  384             throw new DAV\Exception('Principal not found');
  385         }
  386         $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?');
  387         $stmt->execute([$principal['id']]);
  388 
  389         $result = [];
  390         while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
  391             $result[] = $row['uri'];
  392         }
  393 
  394         return $result;
  395     }
  396 
  397     /**
  398      * Updates the list of group members for a group principal.
  399      *
  400      * The principals should be passed as a list of uri's.
  401      *
  402      * @param string $principal
  403      * @param array  $members
  404      */
  405     public function setGroupMemberSet($principal, array $members)
  406     {
  407         // Grabbing the list of principal id's.
  408         $stmt = $this->pdo->prepare('SELECT id, uri FROM '.$this->tableName.' WHERE uri IN (? '.str_repeat(', ? ', count($members)).');');
  409         $stmt->execute(array_merge([$principal], $members));
  410 
  411         $memberIds = [];
  412         $principalId = null;
  413 
  414         while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
  415             if ($row['uri'] == $principal) {
  416                 $principalId = $row['id'];
  417             } else {
  418                 $memberIds[] = $row['id'];
  419             }
  420         }
  421         if (!$principalId) {
  422             throw new DAV\Exception('Principal not found');
  423         }
  424         // Wiping out old members
  425         $stmt = $this->pdo->prepare('DELETE FROM '.$this->groupMembersTableName.' WHERE principal_id = ?;');
  426         $stmt->execute([$principalId]);
  427 
  428         foreach ($memberIds as $memberId) {
  429             $stmt = $this->pdo->prepare('INSERT INTO '.$this->groupMembersTableName.' (principal_id, member_id) VALUES (?, ?);');
  430             $stmt->execute([$principalId, $memberId]);
  431         }
  432     }
  433 
  434     /**
  435      * Creates a new principal.
  436      *
  437      * This method receives a full path for the new principal. The mkCol object
  438      * contains any additional webdav properties specified during the creation
  439      * of the principal.
  440      *
  441      * @param string $path
  442      * @param MkCol  $mkCol
  443      */
  444     public function createPrincipal($path, MkCol $mkCol)
  445     {
  446         $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (uri) VALUES (?)');
  447         $stmt->execute([$path]);
  448         $this->updatePrincipal($path, $mkCol);
  449     }
  450 }