/** * Update the local cache of the remote user details * @param object $usr The user details we read from the remote. */ function UpdateUserFromExternal(&$usr) { global $c; /** * When we're doing the create we will usually need to generate a user number */ if (!isset($usr->user_no) || intval($usr->user_no) == 0) { $qry = new AwlQuery("SELECT nextval('usr_user_no_seq');"); $qry->Exec('Login', __LINE__, __FILE__); $sequence_value = $qry->Fetch(true); // Fetch as an array $usr->user_no = $sequence_value[0]; } $qry = new AwlQuery('SELECT * FROM usr WHERE user_no = :user_no', array(':user_no' => $usr->user_no)); if ($qry->Exec('Login', __LINE__, __FILE__) && $qry->rows() == 1) { $type = "UPDATE"; if ($old = $qry->Fetch()) { $changes = false; foreach ($usr as $k => $v) { if ($old->{$k} != $v) { $changes = true; dbg_error_log("Login", "User '%s' field '%s' changed from '%s' to '%s'", $usr->username, $k, $old->{$k}, $v); break; } } if (!$changes) { dbg_error_log("Login", "No changes to user record for '%s' - leaving as-is.", $usr->username); if (isset($usr->active) && $usr->active == 'f') { return false; } return; // Normal case, if there are no changes } else { dbg_error_log("Login", "Changes to user record for '%s' - updating.", $usr->username); } } } else { $type = "INSERT"; } $params = array(); if ($type != 'INSERT') { $params[':user_no'] = $usr->user_no; } $qry = new AwlQuery(sql_from_object($usr, $type, 'usr', 'WHERE user_no= :user_no'), $params); $qry->Exec('Login', __LINE__, __FILE__); /** * We disallow login by inactive users _after_ we have updated the local copy */ if (isset($usr->active) && ($usr->active === 'f' || $usr->active === false)) { return false; } if ($type == 'INSERT') { $qry = new AwlQuery('INSERT INTO principal( type_id, user_no, displayname, default_privileges) SELECT 1, user_no, fullname, :privs::INT::BIT(24) FROM usr WHERE username=:username', array(':privs' => privilege_to_bits($c->default_privileges), ':username' => $usr->username)); $qry->Exec('Login', __LINE__, __FILE__); CreateHomeCalendar($usr->username); } else { if ($usr->fullname != $old->{'fullname'}) { // Also update the displayname if the fullname has been updated. $qry->QDo('UPDATE principal SET displayname=:new_display WHERE user_no=:user_no', array(':new_display' => $usr->fullname, ':user_no' => $usr->user_no)); } } }
case 'DAV::privilege': case 'http://www.xythos.com/namespaces/StorageServer:privilege': $ticket_privs_array = $v->GetElements(); // Ensure we always get an array back $ticket_privileges = 0; foreach ($ticket_privs_array as $k1 => $v1) { $ticket_privileges |= privilege_to_bits($v1->GetTag()); } if ($ticket_privileges & privilege_to_bits('write')) { $ticket_privileges |= privilege_to_bits('read'); } if ($ticket_privileges & privilege_to_bits('read')) { $ticket_privileges |= privilege_to_bits(array('read-free-busy', 'read-current-user-privilege-set')); } if ($ticket_privileges & privilege_to_bits('read-free-busy')) { $ticket_privileges |= privilege_to_bits('schedule-query-freebusy'); } 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');
function ticket_row_editor() { global $c, $id, $editor, $can_write_principal, $privilege_names; $ticketrow = new Editor("Tickets", "access_ticket"); $ticketrow->SetSubmitName('ticketrow'); if ($can_write_principal && $ticketrow->IsSubmit()) { $username = $editor->Value('username'); $ugly_path = $_POST['target']; if ($ugly_path == '/' . $username || $ugly_path == '/' . $username . '/') { $target_collection = $id; } else { $username_len = strlen($username) + 2; $sql = "SELECT collection_id FROM collection WHERE dav_name = :exact_name"; $sql .= " AND substring(dav_name FROM 1 FOR {$username_len}) = '/{$username}/'"; $params = array(':exact_name' => $ugly_path); if (!preg_match('#/$#', $ugly_path)) { $sql .= " OR dav_name = :truncated_name OR dav_name = :trailing_slash_name"; $params[':truncated_name'] = preg_replace('#[^/]*$#', '', $ugly_path); $params[':trailing_slash_name'] = $ugly_path . "/"; } $sql .= " ORDER BY LENGTH(dav_name) DESC LIMIT 1"; $qry = new AwlQuery($sql, $params); if ($qry->Exec() && $qry->rows() > 0) { $row = $qry->Fetch(); $target_collection = $row->collection_id; } else { $c->messages[] = translate('Can only add tickets for existing collection paths which you own'); return $ticketrow; } } $_POST['dav_owner_id'] = $id; $_POST['target_collection_id'] = $target_collection; $ticket_id = check_by_regex($_POST['ticket_id'], '/[A-Za-z0-9]+/'); $ticketrow->SetWhere('dav_owner_id=' . $id . ' AND ticket_id=' . AwlQuery::quote($ticket_id)); if (isset($_POST['ticket_privileges'])) { $privilege_bitpos = array_flip($privilege_names); $priv_names = array_keys($_POST['ticket_privileges']); $privs_dec = privilege_to_bits($priv_names); $_POST['privileges'] = sprintf('%024s', decbin($privs_dec)); $ticketrow->Assign('privileges', $privs_dec); } $c->messages[] = translate('Creating new ticket granting privileges to this Principal'); $ticketrow->Write(); } return $ticketrow; }
/** * Is the user has the privileges to do what is requested. */ function HavePrivilegeTo($do_what) { $test_bits = privilege_to_bits($do_what); // dbg_error_log( 'caldav', 'request::HavePrivilegeTo("%s") [%s] against allowed "%s" => "%s" (%s)', // (is_array($do_what) ? implode(',',$do_what) : $do_what), decbin($test_bits), // decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) ); return ($this->privileges & $test_bits) > 0; }
$distinct = 'DISTINCT ON (calendar_item.uid) '; $params[':path_match'] = '^' . $target_collection->bound_from(); } else { $where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id(); $distinct = ''; } if (is_array($qry_filters)) { dbg_log_array("calquery", "qry_filters", $qry_filters, true); $components = array(); $filter_fragment = SqlFilterFragment($qry_filters, $components); if ($filter_fragment !== false) { $where .= ' ' . $filter_fragment['sql']; $params = array_merge($params, $filter_fragment['params']); } } if ($target_collection->Privileges() != privilege_to_bits('DAV::all')) { $where .= " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) "; } if (isset($c->hide_TODO) && ($c->hide_TODO === true || is_string($c->hide_TODO) && preg_match($c->hide_TODO, $_SERVER['HTTP_USER_AGENT'])) && !$target_collection->HavePrivilegeTo('all')) { $where .= " AND caldav_data.caldav_type NOT IN ('VTODO') "; } if (isset($c->hide_older_than) && intval($c->hide_older_than > 0)) { $where .= " AND (CASE WHEN caldav_data.caldav_type<>'VEVENT' OR calendar_item.dtstart IS NULL THEN true ELSE calendar_item.dtstart > (now() - interval '" . intval($c->hide_older_than) . " days') END) "; } $sql = 'SELECT ' . $distinct . ' caldav_data.*,calendar_item.* FROM collection INNER JOIN caldav_data USING(collection_id) INNER JOIN calendar_item USING(dav_id) ' . $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= " ORDER BY caldav_data.dav_id"; } $qry = new AwlQuery($sql, $params); if ($qry->Exec("calquery", __LINE__, __FILE__) && $qry->rows() > 0) { while ($dav_object = $qry->Fetch()) {
function get_freebusy($path_match, $range_start, $range_end, $bin_privs = null) { global $request, $c; $debugging = false; // if ( $debugging ) { // printf( "Path: %s\n", $path_match ); // print_r( $range_start ); // print_r( $range_end ); // } if (!isset($bin_privs)) { $bin_privs = $request->Privileges(); } if (!isset($range_start) || !isset($range_end)) { $request->DoResponse(400, 'All valid freebusy requests MUST contain a time-range filter'); } $params = array(':path_match' => $path_match, ':start' => $range_start->UTC(), ':end' => $range_end->UTC()); $where = ' WHERE caldav_data.dav_name ~ :path_match '; $where .= 'AND rrule_event_overlaps( dtstart, dtend, rrule, :start, :end) '; $where .= "AND caldav_data.caldav_type IN ( 'VEVENT', 'VTODO' ) "; $where .= "AND (calendar_item.transp != 'TRANSPARENT' OR calendar_item.transp IS NULL) "; $where .= "AND (calendar_item.status != 'CANCELLED' OR calendar_item.status IS NULL) "; $where .= "AND collection.is_calendar AND collection.schedule_transp = 'opaque' "; if ($bin_privs != privilege_to_bits('all')) { $where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) "; } $fbtimes = array(); $sql = 'SELECT caldav_data.caldav_data, calendar_item.rrule, calendar_item.transp, calendar_item.status, '; $sql .= "to_char(calendar_item.dtstart at time zone 'GMT'," . AWLDatabase::SqlUTCFormat . ') AS start, '; $sql .= "to_char(calendar_item.dtend at time zone 'GMT'," . AWLDatabase::SqlUTCFormat . ') AS finish, '; $sql .= "calendar_item.class, calendar_item.dav_id "; $sql .= 'FROM caldav_data INNER JOIN calendar_item USING(dav_id,user_no,dav_name,collection_id) '; $sql .= 'INNER JOIN collection USING(collection_id)'; $sql .= $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= ' ORDER BY dav_id'; } $qry = new AwlQuery($sql, $params); if ($qry->Exec("REPORT", __LINE__, __FILE__) && $qry->rows() > 0) { while ($calendar_object = $qry->Fetch()) { $extra = ''; if ($calendar_object->status == 'TENTATIVE') { $extra = ';BUSY-TENTATIVE'; } else { if (isset($c->_workaround_client_freebusy_bug) && $c->_workaround_client_freebusy_bug) { $extra = ';BUSY'; } } // if ( $debugging ) { // $extra = ';'.$calendar_object->dav_id; // } // dbg_error_log( "REPORT", " FreeBusy: Not transparent, tentative or cancelled: %s, %s, %s", $calendar_object->start, $calendar_object->finish, $calendar_object->class ); $ics = new vComponent($calendar_object->caldav_data); $expanded = expand_event_instances($ics, $range_start, $range_end); $expansion = $expanded->GetComponents(array('VEVENT' => true, 'VTODO' => true, 'VJOURNAL' => true)); // if ( $debugging ) echo "=================== $calendar_object->dav_id ========================\n"; $dtstart_type = 'DTSTART'; foreach ($expansion as $k => $v) { // if ( $debugging ) print $k."\n".$v->Render(); $start_date = $v->GetProperty($dtstart_type); if (!isset($start_date) && $v->GetType() != 'VTODO') { $dtstart_type = 'DUE'; $start_date = $v->GetProperty($dtstart_type); } $start_date = new RepeatRuleDateTime($start_date); $duration = $v->GetProperty('DURATION'); $duration = !isset($duration) ? 'P1D' : $duration->Value(); $end_date = clone $start_date; $end_date->modify($duration); if ($end_date == $start_date || $end_date < $range_start || $start_date > $range_end) { // if ( $debugging ) // echo "-----------------------------------------------------\n"; continue; } // if ( $debugging ) // echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++\n"; $thisfb = $start_date->UTC() . '/' . $end_date->UTC() . $extra; array_push($fbtimes, $thisfb); } } } $freebusy = new vComponent(); $freebusy->setType('VFREEBUSY'); $freebusy->AddProperty('DTSTAMP', date('Ymd\\THis\\Z')); $freebusy->AddProperty('DTSTART', $range_start->UTC()); $freebusy->AddProperty('DTEND', $range_end->UTC()); sort($fbtimes); foreach ($fbtimes as $k => $v) { $text = explode(';', $v, 2); $freebusy->AddProperty('FREEBUSY', $text[0], isset($text[1]) ? array('FBTYPE' => $text[1]) : null); } return $freebusy; }
function handle_freebusy_request($ic) { global $c, $session, $request, $ical; $request->NeedPrivilege('CALDAV:schedule-send-freebusy'); $reply = new XMLDocument(array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C")); $responses = array(); $fbq_start = $ic->GetPValue('DTSTART'); $fbq_end = $ic->GetPValue('DTEND'); if (!(isset($fbq_start) || isset($fbq_end))) { $request->DoResponse(400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND'); } $range_start = new RepeatRuleDateTime($fbq_start); $range_end = new RepeatRuleDateTime($fbq_end); $attendees = $ic->GetProperties('ATTENDEE'); if (preg_match('# iCal/\\d#', $_SERVER['HTTP_USER_AGENT'])) { dbg_error_log("POST", "Non-compliant iCal request. Using X-WR-ATTENDEE property"); $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE'); foreach ($wr_attendees as $k => $v) { $attendees[] = $v; } } dbg_error_log("POST", "Responding with free/busy for %d attendees", count($attendees)); foreach ($attendees as $k => $attendee) { $attendee_email = preg_replace('/^mailto:/', '', $attendee->Value()); dbg_error_log("POST", "Calculating free/busy for %s", $attendee_email); /** @todo Refactor this so we only do one query here and loop through the results */ $params = array(':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth, ':email' => $attendee_email); $qry = new AwlQuery('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params); if (!$qry->Exec('POST', __LINE__, __FILE__)) { $request->DoResponse(501, 'Database error'); } if ($qry->rows() > 1) { // Unlikely, but if we get more than one result we'll do an exact match instead. if (!$qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE usr.email = :email', $params)) { $request->DoResponse(501, 'Database error'); } if ($qry->rows() == 0) { /** Sigh... Go back to the original case-insensitive match */ $qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params); } } $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav'); $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value())); if ($qry->rows() == 0) { $remote = new iSchedule(); $answer = $remote->sendRequest($attendee->Value(), 'VFREEBUSY/REQUEST', $ical->Render()); if ($answer === false) { $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User"); $reply->CalDAVElement($response, "calendar-data"); $responses[] = $response; continue; } foreach ($answer as $a) { if ($a === false) { $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User"); $reply->CalDAVElement($response, "calendar-data"); } elseif (substr($a, 0, 1) >= 1) { $reply->CalDAVElement($response, "request-status", $a); $reply->CalDAVElement($response, "calendar-data"); } else { $reply->CalDAVElement($response, "request-status", "2.0;Success"); $reply->CalDAVElement($response, "calendar-data", $a); } $responses[] = $response; } continue; } if (!($attendee_usr = $qry->Fetch())) { $request->DoResponse(501, 'Database error'); } if ((privilege_to_bits('schedule-query-freebusy') & bindec($attendee_usr->p)) == 0) { $reply->CalDAVElement($response, "request-status", "3.8;No authority"); $reply->CalDAVElement($response, "calendar-data"); $responses[] = $response; continue; } $attendee_path_match = '^/' . $attendee_usr->username . '/'; $fb = get_freebusy($attendee_path_match, $range_start, $range_end, bindec($attendee_usr->p)); $fb->AddProperty('UID', $ic->GetPValue('UID')); $fb->SetProperties($ic->GetProperties('ORGANIZER'), 'ORGANIZER'); $fb->AddProperty($attendee); $vcal = new vCalendar(array('METHOD' => 'REPLY')); $vcal->AddComponent($fb); $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav'); $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value())); $reply->CalDAVElement($response, "request-status", "2.0;Success"); // Cargo-cult setting $reply->CalDAVElement($response, "calendar-data", $vcal->Render()); $responses[] = $response; } $response = $reply->NewXMLElement("schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:caldav'); $request->XMLResponse(200, $response); }
/** * Return the privileges bits for the current session user to this resource */ function Privileges() { global $session; if (!isset($this->privileges)) { $this->privileges = 0; } if (is_string($this->privileges)) { $this->privileges = bindec($this->privileges); } if ($this->_is_group && in_array(ConstructURL('/' . $session->username . '/'), $this->GroupMemberSet())) { $this->privileges |= privilege_to_bits(array('DAV::read', 'DAV::read-current-user-privilege-set')); } return $this->privileges; }
/** * Does the user have the privileges to do what is requested. * @param $do_what mixed The request privilege name, or array of privilege names, to be checked. * @param $any boolean Whether we accept any of the privileges. The default is true, unless the requested privilege is 'all', when it is false. * @return boolean Whether they do have one of those privileges against this resource. */ function HavePrivilegeTo($do_what, $any = null) { if (!isset($this->privileges)) { $this->FetchPrivileges(); } if (!isset($any)) { $any = $do_what != 'all'; } $test_bits = privilege_to_bits($do_what); dbg_error_log('DAVResource', 'Testing %s privileges of "%s" (%s) against allowed "%s" => "%s" (%s)', $any ? 'any' : 'exactly', $do_what, decbin($test_bits), decbin($this->privileges), $this->privileges & $test_bits, decbin($this->privileges & $test_bits)); if ($any) { return ($this->privileges & $test_bits) > 0; } else { return ($this->privileges & $test_bits) == $test_bits; } }
/** * Return the privileges bits for the current session user to this resource */ function Privileges() { global $session; if (!isset($this->privileges)) { $this->privileges = 0; } if (is_string($this->privileges)) { $this->privileges = bindec($this->privileges); } if ($this->_is_group) { if (isset($session->principal) && in_array($session->principal->url(), $this->GroupMemberSet())) { $this->privileges |= privilege_to_bits(array('DAV::read', 'DAV::read-current-user-privilege-set')); } } return $this->privileges; }
$href = $bound_from . $rawurl; } else { $href = $bound_from . substr($rawurl, $path_pos + strlen($request->path)); } @dbg_error_log("REPORT", 'Reporting on href "%s"', $href); $href_in .= $href_in == '' ? '' : ', '; $href_in .= ':href' . $k; $params[':href' . $k] = $href; } $where = " WHERE caldav_data.collection_id = " . $collection->resource_id(); $where .= " AND caldav_data.dav_name IN ( {$href_in} ) "; if ($mode == 'caldav') { if ($collection->Privileges() != privilege_to_bits('DAV::all')) { $where .= " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) "; } if (isset($c->hide_TODO) && $c->hide_TODO && !$collection->Privileges() == privilege_to_bits('all')) { $where .= " AND caldav_data.caldav_type NOT IN ('VTODO') "; } } $sql = 'SELECT calendar_item.*, addressbook_resource.*, caldav_data.* FROM caldav_data LEFT JOIN calendar_item USING(dav_id, user_no, dav_name, collection_id) LEFT JOIN addressbook_resource USING(dav_id) LEFT JOIN collection USING(collection_id)'; /** * @todo: Add stanzas for missing rows, so we don't just return a blank multistatus but * actually return <response> stanzas with a 404 for each absent href. We could do * this relatively easily with an array_flip($params) and remove each matching dav_name * as we process it. */ if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $where .= " ORDER BY caldav_data.dav_id";
function process_ace($grantor, $by_principal, $by_collection, $ace) { global $cache_delete_list, $request; $elements = $ace->GetContent(); $principal_node = $elements[0]; $grant = $elements[1]; if ($principal_node->GetNSTag() != 'DAV::principal') { $request->MalformedRequest('ACL request must contain a principal, not ' . $principal->GetNSTag()); } $grant_tag = $grant->GetNSTag(); if ($grant_tag == 'DAV::deny') { $request->PreconditionFailed(403, 'grant-only'); } if ($grant_tag == 'DAV::invert') { $request->PreconditionFailed(403, 'no-invert'); } if ($grant->GetNSTag() != 'DAV::grant') { $request->MalformedRequest('ACL request must contain a principal for each ACE'); } $privilege_names = array(); $xml_privs = $grant->GetPath("/DAV::grant/DAV::privilege/*"); foreach ($xml_privs as $k => $priv) { $privilege_names[] = $priv->GetNSTag(); } $privileges = privilege_to_bits($privilege_names); $principal_content = $principal_node->GetContent(); if (count($principal_content) != 1) { $request->MalformedRequest('ACL request must contain exactly one principal per ACE'); } $principal_content = $principal_content[0]; switch ($principal_content->GetNSTag()) { case 'DAV::property': $principal_property = $principal_content->GetContent(); if ($principal_property[0]->GetNSTag() != 'DAV::owner') { $request->PreconditionFailed(403, 'recognized-principal'); } if (privilege_to_bits('all') != $privileges) { $request->PreconditionFailed(403, 'no-protected-ace-conflict', 'Owner must always have all permissions'); } continue; // and then we ignore it, since it's protected break; case 'DAV::unauthenticated': $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users'); break; case 'DAV::href': $principal_type = 'href'; $grantee = new DAVResource(DeconstructURL($principal_content->GetContent())); $grantee_id = $grantee->getProperty('principal_id'); if (!$grantee->Exists() || !$grantee->IsPrincipal()) { $request->PreconditionFailed(403, 'recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.'); } $sqlparms = array(':to_principal' => $grantee_id); $where = 'WHERE to_principal=:to_principal AND '; if (isset($by_principal)) { $sqlparms[':by_principal'] = $by_principal; $where .= 'by_principal = :by_principal'; } else { $sqlparms[':by_collection'] = $by_collection; $where .= 'by_collection = :by_collection'; } $qry = new AwlQuery('SELECT privileges FROM grants ' . $where, $sqlparms); if ($qry->Exec('ACL', __LINE__, __FILE__) && $qry->rows() == 1 && ($current = $qry->Fetch())) { $sql = 'UPDATE grants SET privileges=:privileges::INT::BIT(24) ' . $where; } else { $sqlparms[':by_principal'] = $by_principal; $sqlparms[':by_collection'] = $by_collection; $sql = 'INSERT INTO grants (by_principal, by_collection, to_principal, privileges) VALUES(:by_principal, :by_collection, :to_principal, :privileges::INT::BIT(24))'; } $sqlparms[':privileges'] = $privileges; $qry = new AwlQuery($sql, $sqlparms); if ($qry->Exec('ACL', __LINE__, __FILE__)) { Principal::cacheDelete('dav_name', $grantee->dav_name()); Principal::cacheFlush('principal_id IN (SELECT member_id FROM group_member WHERE group_id = ?)', array($grantee_id)); } break; case 'DAV::authenticated': $principal_type = 'authenticated'; if (bindec($grantor->GetProperty('default_privileges')) == $privileges) { continue; } // There is no change, so skip it $sqlparms = array(':privileges' => $privileges); if (isset($by_collection)) { $sql = 'UPDATE collection SET default_privileges=:privileges::INT::BIT(24) WHERE collection_id=:by_collection'; $sqlparms[':by_collection'] = $by_collection; } else { $sql = 'UPDATE principal SET default_privileges=:privileges::INT::BIT(24) WHERE principal_id=:by_principal'; $sqlparms[':by_principal'] = $by_principal; } $qry = new AwlQuery($sql, $sqlparms); if ($qry->Exec('ACL', __LINE__, __FILE__)) { /** * Basically this has changed everyone's permissions now, so... */ Principal::cacheFlush('TRUE'); } break; case 'DAV::all': // $principal_type = 'all'; $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users'); break; default: $request->PreconditionFailed(403, 'recognized-principal'); break; } }
if ($editor->Available()) { $c->stylesheets[] = 'css/browse.css'; $c->scripts[] = 'js/browse.js'; $grantrow = new Editor("Grants", "grants"); $grantrow->SetSubmitName('savegrantrow'); $grantrow->SetLookup('to_principal', 'SELECT principal_id, displayname FROM dav_principal WHERE principal_id NOT IN (SELECT member_id FROM group_member WHERE group_id = ' . $id . ') ORDER BY displayname'); if ($can_write_collection) { if ($grantrow->IsSubmit()) { $_POST['by_collection'] = $id; $to_principal = intval($_POST['to_principal']); $orig_to_id = intval($_POST['orig_to_id']); $grantrow->SetWhere("by_collection=" . $id . " AND to_principal={$orig_to_id}"); if (isset($_POST['grant_privileges'])) { $privilege_bitpos = array_flip($privilege_names); $priv_names = array_keys($_POST['grant_privileges']); $privs = privilege_to_bits($priv_names); $_POST['privileges'] = sprintf('%024s', decbin($privs)); $grantrow->Assign('privileges', $privs_dec); } $grantrow->Write(); unset($_GET['to_principal']); } elseif (isset($_GET['delete_grant'])) { $qry = new AwlQuery("DELETE FROM grants WHERE by_collection=:grantor_id AND to_principal = :to_principal", array(':grantor_id' => $id, ':to_principal' => intval($_GET['delete_grant']))); $qry->Exec('collection-edit'); } } function edit_grant_row($row_data) { global $grantrow, $id, $privilege_xlate, $privilege_names; global $btn_all, $btn_all_title, $btn_rw, $btn_rw_title, $btn_read, $btn_read_title; global $btn_fb, $btn_fb_title, $btn_sd, $btn_sd_title, $btn_ss, $btn_ss_title;
foreach ($xml_privs as $k => $priv) { $privilege_names[] = $priv->GetTag(); } $privileges = privilege_to_bits($privilege_names); $principal_content = $principal->GetContent(); if (count($principal_content) != 1) { $request->MalformedRequest('ACL request must contain exactly one principal per ACE'); } $principal_content = $principal_content[0]; switch ($principal_content->GetTag()) { case 'DAV::property': $principal_property = $principal_content->GetContent(); if ($principal_property[0]->GetTag() != 'DAV::owner') { $request->PreconditionFailed(403, 'recognized-principal'); } if (privilege_to_bits('all') != $privileges) { $request->PreconditionFailed(403, 'no-protected-ace-conflict', 'Owner must always have all permissions'); } continue; // and then we ignore it, since it's protected break; case 'DAV::unauthenticated': $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users'); break; case 'DAV::href': $principal_type = 'href'; $principal = new DAVResource(DeconstructURL($principal_content->GetContent())); if (!$principal->Exists() || !$principal->IsPrincipal()) { $request->PreconditionFailed(403, 'recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.'); } $sqlparms = array(':to_principal' => $principal->GetProperty('principal_id'));
private function Write($field_values, $inserting = true) { global $c; if (is_array($field_values)) { $field_values = (object) $field_values; } if (!isset($field_values->{'user_active'})) { if (isset($field_values->{'active'})) { $field_values->{'user_active'} = $field_values->{'active'}; } else { if ($inserting) { $field_values->{'user_active'} = true; } } } if (!isset($field_values->{'modified'}) && isset($field_values->{'updated'})) { $field_values->{'modified'} = $field_values->{'updated'}; } if (!isset($field_values->{'type_id'}) && $inserting) { $field_values->{'type_id'} = 1; } // Default to 'person' if (!isset($field_values->{'default_privileges'}) && $inserting) { $field_values->{'default_privileges'} = sprintf('%024s', decbin(privilege_to_bits($c->default_privileges))); } $sql = ''; if ($inserting) { $insert_fields = array(); $param_names = array(); } else { $update_list = array(); } $sql_params = array(); foreach (self::updateableFields() as $k) { if (!isset($field_values->{$k}) && !isset($this->{$k})) { continue; } $param_name = ':' . $k; $sql_params[$param_name] = isset($field_values->{$k}) ? $field_values->{$k} : $this->{$k}; if ($k == 'default_privileges') { $sql_params[$param_name] = sprintf('%024s', $sql_params[$param_name]); $param_name = 'cast(' . $param_name . ' as text)::BIT(24)'; } else { if ($k == 'modified' && isset($field_values->{$k}) && preg_match('{^([23]\\d\\d\\d[01]\\d[0123]\\d)T?([012]\\d[0-5]\\d[0-5]\\d)$}', $field_values->{$k}, $matches)) { $sql_params[$param_name] = $matches[1] . 'T' . $matches[2]; } } if ($inserting) { $param_names[] = $param_name; $insert_fields[] = $k; } else { $update_list[] = $k . '=' . $param_name; } } if ($inserting && isset(self::$db_mandatory_fields)) { foreach (self::$db_mandatory_fields as $k) { if (!isset($sql_params[':' . $k])) { throw new Exception(get_class($this) . '::Create: Mandatory field "' . $k . '" is not set.'); } } if (isset($this->user_no)) { $param_names[] = ':user_no'; $insert_fields[] = 'user_no'; $sql_params[':user_no'] = $this->user_no; } if (isset($this->created)) { $param_names[] = ':created'; $insert_fields[] = 'created'; $sql_params[':created'] = $this->created; } $sql = 'INSERT INTO ' . self::$db_tablename . ' (' . implode(',', $insert_fields) . ') VALUES(' . implode(',', $param_names) . ')'; } else { $sql = 'UPDATE ' . self::$db_tablename . ' SET ' . implode(',', $update_list); $sql .= ' WHERE principal_id=:principal_id'; $sql_params[':principal_id'] = $this->principal_id; } $qry = new AwlQuery($sql, $sql_params); if ($qry->Exec('Principal', __FILE__, __LINE__)) { $this->unCache(); $new_principal = new Principal('username', $sql_params[':username']); foreach ($new_principal as $k => $v) { $this->{$k} = $v; } } }