/** * Constructor * @param string $ticket_id */ function __construct($ticket_id) { global $c; $this->dav_name = null; $this->target_collection_id = null; $this->target_resource_id = null; $this->expiry = null; $this->expired = true; $this->dav_owner_id = null; $this->ticket_id = $ticket_id; $this->privileges = 0; $this->grantor_collection_privileges = 0; $qry = new AwlQuery('SELECT access_ticket.*, collection.dav_name, (access_ticket.expires < current_timestamp) AS expired, path_privs(access_ticket.dav_owner_id,collection.dav_name,:scan_depth) AS grantor_collection_privileges FROM access_ticket JOIN collection ON (target_collection_id = collection_id) WHERE ticket_id = :ticket_id::text', array(':ticket_id' => $ticket_id, ':scan_depth' => $c->permission_scan_depth)); if ($qry->Exec('DAVTicket', __LINE__, __FILE__) && $qry->rows() == 1 && ($t = $qry->Fetch())) { if (!$t->expired) { foreach ($t as $k => $v) { $this->{$k} = $v; } $this->expired = false; $this->privileges = bindec($this->privileges); $this->grantor_collection_privileges = bindec($this->grantor_collection_privileges); dbg_error_log('DAVTicket', 'Found a current ticket for "%s"', implode(', ', bits_to_privilege($this->privileges()))); } else { dbg_error_log('DAVTicket', 'Found an expired ticket: %s - %s', $ticket_id, $t->expires); } } if (isset($this->target_resource_id)) { $qry = new AwlQuery('SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id', array(':dav_id' => $this->target_resource_id)); if ($qry->Exec('DAVTicket', __LINE__, __FILE__) && $qry->rows() == 1 && ($r = $qry->Fetch())) { $this->dav_name = $r->dav_name; } } }
} break; } } if ($ticket_timeout == 'infinity') { $sql_timeout = null; } else { if (preg_match('{^([a-z]+)-(\\d+)$}i', $ticket_timeout, $matches)) { /** It isn't specified, but timeout seems to be 'unit-number' like 'Seconds-3600', so we make it '3600 Seconds' which PostgreSQL understands */ $sql_timeout = $matches[2] . ' ' . $matches[1]; } else { $sql_timeout = $ticket_timeout; } } $collection_id = $target->GetProperty('collection_id'); $resource_id = $target->GetProperty('dav_id'); $i = 0; do { $ticket_id = substr(str_replace('/', '', str_replace('+', '', base64_encode(sha1(date('r') . rand(0, 2100000000) . microtime(true), true)))), 7, 8); $qry = new AwlQuery('INSERT INTO access_ticket ( ticket_id, dav_owner_id, privileges, target_collection_id, target_resource_id, expires ) VALUES( :ticket_id, :owner, :privs::INT::BIT(24), :collection, :resource, (current_timestamp + :expires::interval) )', array(':ticket_id' => $ticket_id, ':owner' => $session->principal_id, ':privs' => $ticket_privileges, ':collection' => $collection_id, ':resource' => $resource_id, ':expires' => $sql_timeout)); $result = $qry->Exec('MKTICKET', __LINE__, __FILE__); } while (!$result && $i++ < 2); $privs = new XMLElement('privilege'); foreach (bits_to_privilege($ticket_privileges) as $k => $v) { $reply->NSElement($privs, $v); } $ticketinfo = new XMLElement('T:ticketinfo', array(new XMLElement('T:id', $ticket_id), new XMLElement('owner', $reply->href(ConstructURL('/' . $session->username . '/'))), $privs, new XMLElement('T:timeout', $ticket_timeout), new XMLElement('T:visits', 'infinity'))); $prop = new XMLElement("prop", new XMLElement('T:ticketdiscovery', $ticketinfo), $reply->GetXmlNsArray()); header('Ticket: ' . $ticket_id); $request->XMLResponse(200, $prop);
/** * Permissions are controlled as follows: * 1. if the path is '/', the request has read privileges * 2. if the requester is an admin, the request has read/write priviliges * 3. if there is a <user name> component which matches the logged on user * then the request has read/write privileges * 4. otherwise we query the defined relationships between users and use * the minimum privileges returned from that analysis. * * @param int $user_no The current user number * */ function setPermissions() { global $c, $session; if ($this->path == '/' || $this->path == '') { $this->privileges = privilege_to_bits(array('read', 'read-free-busy', 'read-acl')); dbg_error_log("caldav", "Full read permissions for user accessing /"); } else { if ($session->AllowedTo("Admin") || $session->principal->user_no() == $this->user_no) { $this->privileges = privilege_to_bits('all'); dbg_error_log("caldav", "Full permissions for %s", $session->principal->user_no() == $this->user_no ? "user accessing their own hierarchy" : "a systems administrator"); } else { $this->privileges = 0; if ($this->IsPublic()) { $this->privileges = privilege_to_bits(array('read', 'read-free-busy')); dbg_error_log("caldav", "Basic read permissions for user accessing a public collection"); } else { if (isset($c->public_freebusy_url) && $c->public_freebusy_url) { $this->privileges = privilege_to_bits('read-free-busy'); dbg_error_log("caldav", "Basic free/busy permissions for user accessing a public free/busy URL"); } } /** * In other cases we need to query the database for permissions */ $params = array(':session_principal_id' => $session->principal->principal_id(), ':scan_depth' => $c->permission_scan_depth); if (isset($this->by_email) && $this->by_email) { $sql = 'SELECT pprivs( :session_principal_id::int8, :request_principal_id::int8, :scan_depth::int ) AS perm'; $params[':request_principal_id'] = $this->principal_id; } else { $sql = 'SELECT path_privs( :session_principal_id::int8, :request_path::text, :scan_depth::int ) AS perm'; $params[':request_path'] = $this->path; } $qry = new AwlQuery($sql, $params); if ($qry->Exec('caldav', __LINE__, __FILE__) && ($permission_result = $qry->Fetch())) { $this->privileges |= bindec($permission_result->perm); } dbg_error_log('caldav', 'Restricted permissions for user accessing someone elses hierarchy: %s', decbin($this->privileges)); if (isset($this->ticket) && $this->ticket->MatchesPath($this->path)) { $this->privileges |= $this->ticket->privileges(); dbg_error_log('caldav', 'Applying permissions for ticket "%s" now: %s', $this->ticket->id(), decbin($this->privileges)); } } } /** convert privileges into older style permissions */ $this->permissions = array(); $privs = bits_to_privilege($this->privileges); foreach ($privs as $k => $v) { switch ($v) { case 'DAV::all': $type = 'abstract'; break; case 'DAV::write': $type = 'aggregate'; break; default: $type = 'real'; } $v = str_replace('DAV::', '', $v); $this->permissions[$v] = $type; } }
/** * privilege_format_function is for formatting the binary privileges from the * database, including localising them. This is a hook function for a browser * column object, so it takes three parameters: * @param mixed $value The value of the column. * @param BrowserColumn $column The BrowserColumn object we are hooked into. * @param dbrow $row The row object we read from the database. * @return string The formatted privileges. */ function privilege_format_function($value, $column, $row) { global $privilege_xlate; $privs = bits_to_privilege($value, '*'); $formatted = ''; foreach ($privs as $k => $v) { $formatted .= $formatted == '' ? '' : ', '; $v = preg_replace('{^.*:}', '', $v); $formatted .= isset($privilege_xlate[$v]) ? $privilege_xlate[$v] : $v; } return $formatted; }
/** * BuildACE - construct an XMLElement subtree for a DAV::ace */ function BuildACE(&$xmldoc, $privs, $principal) { $privilege_names = bits_to_privilege($privs, $this->_is_collection ? $this->collection->type : 'resource'); $privileges = array(); foreach ($privilege_names as $k) { $privilege = new XMLElement('privilege'); if (isset($xmldoc)) { $xmldoc->NSElement($privilege, $k); } else { $privilege->NewElement($k); } $privileges[] = $privilege; } $ace = new XMLElement('ace', array(new XMLElement('principal', $principal), new XMLElement('grant', $privileges))); return $ace; }