So far I have been unable to successfully implement ACLs (permissions) in SabreDAV.
I have implemented SabreDAV in Code Igniter with my own Auth, Principal and CalDAV backend. This the actual code from the controller:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class CalDAV extends CI_Controller {
public function _remap() {
$this->load->library('SabreDAV');
$authBackend = new SabreDAV_DAV_Auth_Backend_Tank_Auth;
$principalBackend = new Sabre_DAVACL_PrincipalBackend_Click4Time;
$calendarBackend = new Sabre_CalDAV_Backend_Click4Time;
// Directory tree
$tree = array(
new Sabre_DAVACL_PrincipalCollection($principalBackend),
new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend)
);
// The object tree needs in turn to be passed to the server class
$server = new Sabre_DAV_Server($tree);
// You are highly encouraged to set your WebDAV server base url. Without it,
// SabreDAV will guess, but the guess is not always correct. Putting the
// server on the root of the domain will improve compatibility.
$server->setBaseUri('/caldav/');
// Authentication plugin
$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend, 'SabreDAV');
$server->addPlugin($authPlugin);
// CalDAV plugin
$caldavPlugin = new Sabre_CalDAV_Plugin();
$server->addPlugin($caldavPlugin);
// ACL plugin
$aclPlugin = new Sabre_DAVACL_Custom;
$server->addPlugin($aclPlugin);
// Support for html frontend
$browser = new Sabre_DAV_Browser_Plugin();
$server->addPlugin($browser);
$server->exec();
}
}
My current attempt at implementing permissions has been through my custom ACL Plugin:
<?php
class Sabre_DAVACL_Custom extends Sabre_DAVACL_Plugin {
public $allowAccessToNodesWithoutACL = false;
private function _getCurrentUserName() {
$authPlugin = $this->server->getPlugin('auth');
if (is_null($authPlugin)) return null;
return $authPlugin->getCurrentUser();
}
public function getACL($node) {
$user = $this->_getCurrentUserName();
$path = $node->getName();
if ($path == 'calendars' || $path == 'principals' || $path == 'root') {
return array(
array(
'privilege' => '{DAV:}read',
'principal' => 'principals/' . $user,
'protected' => true,
)
);
}
else if ($path == 'calendars/' . $user) {
return array(
array(
'privilege' => '{DAV:}read',
'principal' => 'principals/' . $user,
'protected' => true,
)
);
}
return array();
}
}
This code pretty much works except the second check which should authorize the user to see his or her own calendar(s). I am unable to get the full path name for $node.
This may be the wrong way to implement but I have been unable to find any documentation to confirm that this is the way to implement ACLs.
i'm using a different attempt, i extended the plugin, just like you did but then i replaced getSupportedPrivilegeSet($node) instead.
in sabredav 1.8.6 it looks like this:
public function getSupportedPrivilegeSet($node) {
if (is_string($node)) {
$node = $this->server->tree->getNodeForPath($node);
}
if ($node instanceof IACL) {
$result = $node->getSupportedPrivilegeSet();
if ($result)
return $result;
}
return self::getDefaultSupportedPrivilegeSet();
}
now you can use the classes instead of the path which i found more usefull, i.e.:
class DavCalAcl extends \Sabre\DAVACL\Plugin {
public function getSupportedPrivilegeSet($node) {
if (is_string($node)) {
$node = $this->server->tree->getNodeForPath($node);
}
if($node instanceof \Sabre\CalDAV\Calendar || $node instanceof \Sabre\CalDAV\CalendarObject) {
return array(
array(
'privilege' => '{DAV:}read',
'aggregates' => array(
array(
'privilege' => '{DAV:}read-acl',
'abstract' => true,
),
array(
'privilege' => '{DAV:}read-current-user-privilege-set',
'abstract' => true,
),
),
)
);
}
if ($node instanceof \Sabre\DAVACL\IACL) {
$result = $node->getSupportedPrivilegeSet();
if ($result)
return $result;
}
return self::getDefaultSupportedPrivilegeSet();
}
}
this is my current attempt to get iCal to recognize a calendar as read-only... i'm not quite there yet but maybe this will help you in better identifying the objects
if you want the absolute path of a node i guess you could always go to the root search it for your current node and by doing so recording the path which took you there. as far as i checked the nodes in sabredav do not support a parent or a root property.
[UPDATE]
the best way seems to be to override getACL in the plugin. here you can test for the node's class and return what you really want on instead of the stuff which is returned by the default objects (for instance look at UserCalendars->getACL().
here's my working solution for read-only enforcement based on the object types:
class DavCalAcl extends \Sabre\DAVACL\Plugin {
/**
* Returns the full ACL list.
*
* Either a uri or a DAV\INode may be passed.
*
* null will be returned if the node doesn't support ACLs.
*
* #param string|DAV\INode $node
* #return array
*/
public function getACL($node) {
if (is_string($node)) {
$node = $this->server->tree->getNodeForPath($node);
}
if (!$node instanceof \Sabre\DAVACL\IACL) {
return null;
}
if( $node instanceof \Sabre\CalDAV\Calendar ||
$node instanceof \Sabre\CalDAV\CalendarObject ||
$node instanceof \Sabre\CalDAV\UserCalendars
) {
$acl = array(
array(
'privilege' => '{DAV:}read',
'principal' => $node->getOwner(),
'protected' => true,
),
);
} else {
$acl = $node->getACL();
}
foreach($this->adminPrincipals as $adminPrincipal) {
$acl[] = array(
'principal' => $adminPrincipal,
'privilege' => '{DAV:}all',
'protected' => true,
);
}
return $acl;
}
}
Related
Recently, I've been working on a system using the Phalcon PHP framework. And up until now, it's been plain sailing.
I seem to be having an issue when pulling data from a MySQL database using a model. Everything else seems to be working fine, but a few fields are returning empty data despite there being data within those fields in the MySQL table.
After scanning the database itself, I can't seem to find the reason why these particular fields are empty because similar fields are returned with the data intact.
I've inserted the model data into an array to make it easier for me to browse it and find out what's going on, and this is the result:
[applications] =>
[application_id] => 5
[application_user] => 1
[application_servers] => skybuild
[application_approved] =>
[application_denied] =>
[application_reviewed] =>
[application_approvals] => 0
[application_denials] => 0
[application_date] => 1470739996
[application_message] => This is just a test application to see whether the system is working.
[user] =>
[user_id] => 1
[user_fname] => Leo
[user_lname] => **********
[user_birthday] => ****-**-**
[user_email] => **********#**********.**.**
[user_uname] => Leo_V117
[user_upass] => ********************************
[user_account] => ********-****-****-****-************
[user_active] => Y
[user_banned] =>
[user_suspended] =>
[user_registered] =>
The fields in question are:
application
application_approved
application_denied
application_reviewed
user
user_banned
user_suspended
user_registered
The question is:
WHY are these particular fields returning empty data and, ultimately, HOW can I go about fixing it.
PHP Code
Bootstrap
<?php ########################
##############################
use Phalcon\Mvc\Application;
error_reporting(E_ALL);
try {
/**
* Define some useful constants
*/
define('BASE_DIR', dirname(__DIR__));
define('APP_DIR', BASE_DIR . '/app');
\Phalcon\Mvc\Model::setup(array(
'notNullValidations' => false
));
/**
* Read the configuration
*/
$config = include APP_DIR . '/config/config.php';
/**
* Read auto-loader
*/
include APP_DIR . '/config/loader.php';
/**
* Read services
*/
include APP_DIR . '/config/services.php';
/**
* Handle the request
*/
$application = new Application($di);
echo $application->handle()->getContent();
} catch (Exception $e) {
echo '<pre>';
print_r ( $e->getMessage() );
print_r ( nl2br( htmlentities( $e->getTraceAsString() ) ) );
echo '</pre>';
}
##############################
########################### ?>
Accounts
<?php ########################
##############################
namespace ProjectRogue\Models;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Validator\Uniqueness;
class Accounts extends Model {
public $user_id; // User ID
public $user_fname; // Field: First Name
public $user_lname; // Field: Last Name
public $user_birthday; // Field: Date of Birth
public $user_email; // Field: E-Mail
public $user_uname; // Field: Username
public $user_upass; // Field: Password
public $user_account; // Minecraft Account UUID
public $user_active; // Active
public $user_banned; // Banned
public $user_suspended; // Suspended
public $user_registered;
public function beforeValidationOnCreate() {
$this->user_active = 'N';
$this->user_banned = 'N';
$this->user_suspended = 'N';
$this->user_code = $this->generateCode();
$this->user_registered = time();
}
public function afterSave() {
if( $this->user_active == 'N' ) {
// Check E-Mail Status;
$email = new EmailConfirmation();
$email->email_user = $this->user_id;
$email->email_code = $this->generateCode();
$email->email_created = date("Y-m-d H:i:s", time());
if( $email->save() ) {
$this->getDI()->getFlash()->notice('A confirmation E-Mail has been sent to '.$this->user_email);
}
// Send Commands;
$service_servers = new ServiceServers();
$service_commands = array();
$servers = $service_servers::find();
$json_response['data']['servers_size'] = count($servers);
foreach( $servers as $server ) {
$service_commands[ $server->server_id ] = new ServiceCommands();
// Insert Data;
$service_commands[ $server->server_id ]->assign(array(
'command_server' => $server->server_token,
'command_body' => 'broadcast &6&l[player]&r &fhas just registered an account to our website!',
'command_player' => $this->user_account,
'command_player_online' => 'Y'
));
// Save;
$service_commands[ $server->server_id ]->save();
}
}
}
public function validation() {
$this->validate(new Uniqueness(array(
'field' => 'user_uname',
'message' => 'This Username is already in use.'
)));
$this->validate(new Uniqueness(array(
'field' => 'user_email',
'message' => 'This E-Mail is already in use.'
)));
$this->validate(new Uniqueness(array(
'field' => 'user_account',
'message' => 'This Minecraft Account is already in use.'
)));
return $this->validationHasFailed() != true;
}
}
##############################
########################### ?>
ApplicationStaff
<?php ########################
##############################
namespace ProjectRogue\Models;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Validator\Uniqueness;
class ApplicationsStaff extends Model {
public $application_id; // Application ID;
public $application_user; // Application User;
public $application_servers; // Application Servers;
public $application_approved;
public $application_denied;
public $application_reviewed;
public $application_approvals; // Application Approvals;
public $application_denials; // Application Denials;
public $application_date; // Application Date;
public $application_message; // Application Message;
public function beforeValidationOnCreate() {
$this->application_date = time();
$this->application_approvals = 0;
$this->application_denials = 0;
$this->application_approved = "N";
$this->application_denied = "N";
$this->application_reviewed = "N";
}
public function validation() {
return $this->validationHasFailed() != true;
}
}
##############################
########################### ?>
It actually turns out that the data from each model was cached in my service. Deleting said cache forced the models to return the data as expected.
I am writing the code with the following for mat in rest api.
I thought that, validation done in controller and the service layer cares of writing business logic and model takes care of database operations. I hope I am correct.
My clarification here is whether I can send var_id (underscore separated) to the service layer or as varID (camel-case).
I searched that lot of the api calls, most of them are var_id, that's the reason I used myself too.
But how can I use the variable here, because zend framework code works with camel-case, if am assigning the variables varID = var_id for each and every variable, is it right.
$dataSendToService = array(
$varID = var_id,
$varID2 = var_id2;
);
I am calling the api like the below in the create method.
http://128.12.788.88/api/v1/users/72
json get method like this
{
"var_id":"var_value",
"var_id1":"var_value1"
}
In controller:
function create() {
$body = $this->getRequest()->getContent();
$data = json_decode($body);
$id = $this->params('id');
//validation
if( !isset( $data->pat_id ) || empty( $data->pat_id ) ) {
$resp = array(
'status' => 'failure',
'errorCode' => 531,
'errorMessage' => 'Patient ID should not be empty'
);
return new JsonModel($resp);
}
if( !isset( $data->doc_id ) || empty($data->doc_id )) {
$resp = array(
'status' => 'failure',
'errorCode' => 532,
'errorMessage' => 'Doctor ID should not be empty'
);
return new JsonModel($resp);
}
if( !isset( $data->apt_time ) || empty($data->apt_time )) {
$resp = array(
'status' => 'failure',
'errorCode' => 533,
'errorMessage' => 'Appointment time should not be empty');
return new JsonModel($resp);
}
if( !isset( $data->apt_subject ) || empty($data->apt_subject )) {
$resp = array(
'status' => 'failure',
'errorCode' => 534,
'errorMessage' => 'Appointment Subject time should not be empty');
return new JsonModel($resp);
}
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$usersService = new UsersService($dbAdapter);
$resp = $usersService->profile($data,$id);
}
In service:
function create() {
//get the data and pass it to model
}
In model:
function create() {
//get the data and insert in table and return the result
}
It is totally fine to use underscore separated values in ZF2 but indeed camel-casing seems to be more common practice.
You definitely don't have to do all this manually, you can easily use filters for changing your json variables to camel-case:
use Zend\Filter\Word\CamelCaseToUnderscore;
...
$filter = new CamelCaseToUnderscore();
print $filter->filter('ThisIsMyContent');
And back to underscore separated:
use Zend\Filter\Word\CamelCaseToDash;
...
filter = new CamelCaseToDash();
print $filter->filter('ThisIsMyContent');
If you use a hydrator then you can use the ZF2 ClassMethods hydrator which can be set to extract and hydrate between both by passing a boolean to the constructor:
underscore-separated (true) or camel-case (false)
use Zend\Stdlib\Hydrator\ClassMethods;
...
$boolean = true|false;
$hydrator = new ClassMethods($boolean)
i'm trying Zend framework, i've got two folders in E:\Archivos de programa\Zend\ZendServer\share, une is ZendServer and the other one is ZendServer2
I can't recall if i ever install this two version but i dont think this is the problem
I'm using netbeans as ide ando i'm trying to make an ABM of users using BlockCipher
Here is my code
<?php
use Zend\Crypt\BlockCipher;
class Application_Model_DbTable_Usuarios extends Zend_Db_Table_Abstract
{
protected $_name = 'usuario';
public function getUsuario($usuario)
{
$usuario = (string)$usuario;
$row = $this->fetchRow('Usuario = ' . $usuario);
if (!$row) {
throw new Exception("Could not find row $usuario");
}
return $row->toArray();
}
public function addUsuario($usuario, $clave)
{
$blockCipher = Zend\Crypt\BlockCipher::factory('mcrypt',array('algo'=>'aes'));
$blockCipher->setKey('encryption key');
$result = $blockCipher->encrypt($clave);
echo "Encrypted text: $result \n";
exit;
$data = array(
'Usuario' => $usuario,
'Clave' => $blockCipher,
);
$this->insert($data);
}
public function updateUsuario($usuario, $clave)
{
$blockCipher = BlockCipher::factory($clave, array(
'algo' => 'blowfish',
'mode' => 'cfb',
'hash' => 'sha512'
));
$data = array(
'Clave' => $blockCipher,
);
$this->update($data, 'Usuario = ' . (string)$usuario);
}
public function deleteUsuario($usuario)
{
$this->delete('Usuario = ' . (string)$usuario);
}
}
and in my php.ini i've got
include_path=".;E:\Archivos de programa\Zend\ZendServer\share\ZendFramework2\library"
And i get this error
Fatal error: Class 'Zend\Crypt\BlockCipher' not found in E:\Documents and Settings\dvieira\Mis documentos\NetBeansProjects\justforgeeks\application\models\DbTable\Usuarios.php on line 21
I dont understand why.
Can you help me please?
Thanks in advance
You are using namespaces in your application, therefore you need to make sure that your autoloader can handle this. If it's a ZF1 app then not. Can you try using require to include the class file instead? You can ass well amend the autoloader to work with namespaces
Secondly when using namespaces, if you create an alias for a class
use Zend\Crypt\BlockCipher;
you then instantiate it
$blockCipher = BlockCipher::factory('mcrypt',array('algo'=>'aes'));
I am looking for a way to access and change the DATABASE_CONFIG variables, based on user input. Using CakePHP I created a custom datasource, based on the one provided in the docs, to access an external API. The API returns a JSON string containing the 12 most recent objects. I need to be able to change the page number in the API request to get the next 12 results, as well as accept a free text query entered by the user.
app/Config/Database.php
class DATABASE_CONFIG {
public $behance = array(
'datasource' => 'BehanceDatasource',
'api_key' => '123456789',
'page' => '1',
'text_query' => 'foo'
);
}
app/Model/Datasource/BehanceDataSource.php
App::uses('HttpSocket', 'Network/Http');
class BehanceDatasource extends DataSource {
public $description = 'Beehance datasource';
public $config = array(
'api_key' => '',
'page' => '',
'text_query' => ''
);
public function __construct($config) {
parent::__construct($config);
$this->Http = new HttpSocket();
}
public function listSources($data = null) {
return null;
}
public function describe($model) {
return $this->_schema;
}
public function calculate(Model $model, $func, $params = array()) {
return 'COUNT';
}
public function read(Model $model, $queryData = array(), $recursive = null) {
if ($queryData['fields'] === 'COUNT') {
return array(array(array('count' => 1)));
}
$queryData['conditions']['api_key'] = $this->config['api_key'];
$queryData['conditions']['page'] = $this->config['page'];
$queryData['conditions']['page'] = $this->config['text_query'];
$json = $this->Http->get('http://www.behance.net/v2/projects', $queryData['conditions']);
$res = json_decode($json, true);
if (is_null($res)) {
$error = json_last_error();
throw new CakeException($error);
}
return array($model->alias => $res);
}
}
Is there anyway to access and change the $behance array, or is there another way to go about accessing an external API with cakePHP that I am totally missing?
Is there a way when uploading images (JPEG) to check the DPI?
I would like to integrate it into a form, so as a validator.
You have to open the image with Imagick (or Gmagick) and then call getImageResolution.
$image = new Imagick($path_to_image);
var_dump($image->getImageResolution());
result:
Array
(
[x]=>75
[y]=>75
)
Edit:
For an integration into symfony, you can use a custom validator for that. You extends the default one to validate a file and add the DPI restriction.
Create this one into /lib/validator/myCustomValidatorFile .class.php:
<?php
class myCustomValidatorFile extends sfValidatorFile
{
protected function configure($options = array(), $messages = array())
{
parent::configure($options, $messages);
$this->addOption('resolution_dpi');
$this->addMessage('resolution_dpi', 'DPI resolution is wrong, you should use image with %resolution_dpi% DPI.');
}
protected function doClean($value)
{
$validated_file = parent::doClean($value);
$image = new Imagick($validated_file->getTempName());
$resolution = $image->getImageResolution();
if (empty($resolution))
{
throw new sfValidatorError($this, 'invalid');
}
if ((isset($resolution['x']) && $resolution['x'] < $this->getOption('resolution_dpi')) || (isset($resolution['y']) && $resolution['y'] < $this->getOption('resolution_dpi')))
{
throw new sfValidatorError($this, 'resolution_dpi', array('resolution_dpi' => $this->getOption('resolution_dpi')));
}
return $validated_file;
}
}
Then, inside your form, use this validator for your file:
$this->validatorSchema['file'] = new myCustomValidatorFile(array(
'resolution_dpi' => 300,
'mime_types' => 'web_images',
'path' => sfConfig::get('sf_upload_dir'),
'required' => true
));