So I have Zend_ACL set-up(based on this: Zend Framework: need typical example of ACL ).
And I have a config file for setting permissions, but my problem is how do I give access to multiple roles to a specific controller.action
; roles
acl.roles.guest = null
acl.roles.admin = null
acl.roles.company = null
acl.roles.user = null
acl.roles.super_admin = null
; resources
acl.resources.deny.all.all = guest
acl.resources.allow.index.all = guest
acl.resources.allow.index.all = company
So the problem is this one:
acl.resources.allow.index.all = guest
acl.resources.allow.index.all = company
So my question is how do I set this up in order for multiple roles to have access.
I believe you can tweak the code to make it work for you. Change the ini like
acl.resources.allow.index.all = guest,company
Then change the code from Zend Framework: need typical example of ACL
protected function _addResources($resources) {
foreach ($resources as $permissions => $controllers) {
foreach ($controllers as $controller => $actions) {
if ($controller == 'all') {
$controller = null;
} else {
if (!$this->has($controller)) {
$this->add(new Zend_Acl_Resource($controller));
}
}
foreach ($actions as $action => $role) {
if ($action == 'all') {
$action = null;
}
if ($permissions == 'allow') {
if(strpos($role, ',') !== false) {
$multipleRoles = explode(',',$role);
$this->allow($multipleRoles, $controller, $action);
} else {
$this->allow($role, $controller, $action);
}
}
if ($permissions == 'deny') {
if(strpos($role, ',') !== false) {
$multipleRoles = explode(',',$role);
$this->deny($multipleRoles, $controller, $action);
} else {
$this->deny($role, $controller, $action);
}
}
}
}
}
}
I haven't tested this code, but my understanding is that it should work.
Related
I have created the schema.graphql file in PHP using the following code:
$data = SchemaPrinter::doPrint($schema);
file_put_contents('/var/cache/Graphql/schema.graphql', $data);
Now I want to create new schema using this file content. How to achieve this?
I have created the schema from schema.graphql using the BuildSchema::build().
By default, such schema is created without any resolvers. So we need to define our custom resolvers as follows:
$contents = file_get_contents($this->projectDir.'/config/schema.graphql');
$typeConfigDecorator = function($typeConfig, $typeDefinitionNode) {
$name = $typeConfig['name'];
if ($name === 'Query') {
$typeConfig['resolveField'] =
function ($source, $args, $context, ResolveInfo $info) {
if ($info->fieldDefinition->name == 'login') {
if ($args['userName'] === 'test' && $args['password'] === '1234') {
return "Valid User.";
} else {
return "Invalid User";
}
} elseif ($info->fieldDefinition->name == 'validateUser') {
if ($args['age'] < 18) {
return ['userId' => $args['userId'], 'category' => 'Not eligible for voting'];
}
}
}
}
;
}
return $typeConfig;
};
$schema = BuildSchema::build($contents, $typeConfigDecorator);
We built an API to directly access other social networks APIs using our keys.
I'm trying to build a fuction to access that API.
The default function has been written and is working.
Question
How can I specify a new array to target the json data?
This will override the default setting.
function SocialAPI($handle, $service, $path="") {
$handle = strtolower($handle);
$service = strtolower($service);
$api = file_get_contents("https://api.service.domain.com/v1/Social?handle=$handle&service=$service");
if($api !== false) {
$data = json_decode($api, true);
if($data !== null) {
if($service === "twitter") {
return $data['0']['followers_count'];
}
if($service === "instagram") {
if(!empty($path)) {
while($id = array_shift($path)) {
echo $data[$id];
}
return $data;
} else {
return $data['user']['followed_by']['count'];
}
}
} else {
return false;
}
} else {
return "API call failed.";
}
}
//Test API Function - ** TO BE DELETED **
echo SocialAPI("JohnDoe", "Instagram", "['user']['full_name']");
exit();
function array_deref($data, $keys) {
return empty($keys) ? $data
: array_deref($data[$keys[0]], array_slice($data, 1))
}
function SocialAPI($handle, $service, $path="") {
$handle = strtolower($handle);
$service = strtolower($service);
$api = file_get_contents("https://api.service.domain.com/v1/Social?handle=$handle&service=$service");
if ($api === false) {
return "API call failed.";
}
$data = json_decode($api, true);
if($data !== null) {
return false;
}
if ($service === "twitter") {
if (empty($path)) $path = ['0','followers_count'];
return array_deref($data, $path);
} elseif ($service === "instagram") {
if (empty($path)) $path = ['user','followed_by'];
return array_deref($data, $path);
}
}
//Test API Function - ** TO BE DELETED **
echo SocialAPI("JohnDoe", "Instagram", ['user', 'full_name']);
echo SocialAPI("JohnDoe", "Instagram");
exit();
I added a utility function, array_deref, to walk the arrays recursively (calls itself to handle each level down).
I am trying to create an API using CakePHP that allows searching. For example:
http://localhost:8765/users/index/?username=admin
Which should return users with usernames equal to 'admin':
users: [
{
id: 3,
username: "admin",
image: "",
firstName: "Jeremy",
lastName: "Quick",
userTypeId: 1,
email: "jrquick#test.com",
groupId: 2
}
]
So far, I have been able to accomplish this with a custom get() in the AppController which checks the $_GET and $_POST array for fields on the model. But the function is getting more and more complicated and verging on hackiness as I add more functionality (range search, collection search, and child table filtering). Is there a better, more CakePHP friendly way of accomplishing this? Whether through pure cakephp or a plugin?
I think you want to use the Cakephp Search plugin. It has good documentation and uses a PRG method similar to what you are currently using. It will function just fine through an API. Here's a link to that plugin: github.com/FriendsOfCake/search
If You want to create API, You should create a MiddleWare at first, which will filter tokens, keys etc. to make Your API more protected.
Also, You should use Plugins and RESTful Routes, which will be very helpful.
To create plugin:
bin/cake bake plugin Api
Create Model:
bin/cake bake model Users
For example, You want to have UsersController in Api plugin:
<?php
namespace Api\Controller;
/* This controller will be extending like parent */
use Api\Controller\AppController;
use Api\Model\Table\UsersTable;
/**
* Class UsersController
* #package Api\Controller
* #property UsersTable $Users
*
*/
class UsersController extends AppController{
public function initialize(){
parent::initialize();
$this->loadModel('Api.Users');
}
public function getUser($field ='username', $username = false){
return $this->_jsonResponse(
[
'users' => $this->Users->findBy{ucfirst($field)}($username)
];
)
}
public function _jsonResponse($data, $code = 200){
$this->response->type('json');
$this->response->statusCode($code);
$this->response->body(
json_encode((array)$data)
);
return $this->response;
}
}
Route will be descripbed in plugins/config/routes.php. You need to create Route Map for API in /api path:
function (RouteBuilder $routes) {
$routes->resources('Users', [
'map' => [
'get-user' => [
'action' => 'getUser',
'method' => 'GET' /* Can be also as array ['GET', 'PUT', 'DELETE'] */
]
]
]);
$routes->fallbacks('DashedRoute');
}
If You have frequent calls, You should use Cache that calls and save them for some amount of time. For example - 10 minutes. Cache can be configured in config/app.php. You should create separate Cache prefix and use it in this way:
<?php
use Cake\Cache\Cache;
$data = [];
Cache::write('some_key', $data, 'prefix')
dump(Cache::read('some_key', 'prefix'));
It's just examples. If You will face some problems - just tell in comments :)
Also, use Migrations and Seeds instead dumping sql files
If You want to filter data from Middleware - You should have Event as argument, that will contain request data ($_POST) and request query($_GET) variables that You will be able to easily handle with.
From controllers You need to use $this->request->data to get POST data array or $this->request->query to get GET data array.
I haven't found an answer that seems to work exactly how I am wanting, so here is my current get command. It does allow searching by fields, join tables, greater/less than, in array, and like.
If anyone has recommendations to improve I will update my answer.
public function get() {
$response = new Response();
$model = $this->loadModel();
$fields = $this->getFields();
$joins = $this->getJoins();
$order = $this->getOrder();
$params = $this->getParams();
$limit = $this->getLimit();
$offset = $this->getOffset();
$query = $model->find('all', ['fields' => $fields]);
if (!is_null($joins)) {
$query->contain($joins);
}
if (sizeof($params['equals']) > 0) {
foreach ($params['equals'] as $equalsKey=>$equalsValue) {
$query->andWhere([$equalsKey => $equalsValue]);
}
}
if (sizeof($params['or']) > 0) {
foreach ($params['or'] as $orKey=>$orValue) {
$query->orWhere([$orKey => $orValue]);
}
}
if (!is_null($order)) {
$query->order([$order]);
}
if (!is_null($limit)) {
$query->limit($limit);
if (!is_null($offset)) {
$query->offset($offset);
}
}
$response->addMessage($model->table(), $query->toArray());
$response->respond($this);
}
private function getFields() {
$fields = [];
if (array_key_exists('fields', $_GET)) {
$fields = explode(',', $_GET['fields']);
}
return $fields;
}
private function getLimit() {
$limit = null;
if (array_key_exists('limit', $_GET)) {
$limit = $_GET['limit'];
}
return $limit;
}
private function getJoins() {
$joins = null;
if (array_key_exists('joins', $_GET)) {
$joins = explode(',', $_GET['joins']);
}
return $joins;
}
private function getOffset() {
$offset = null;
if (array_key_exists('offset', $_GET)) {
$offset = $_GET['limit'];
}
return $offset;
}
private function getOrder() {
$results = [];
if (array_key_exists('order', $_GET)) {
$orders = explode(',', $_GET['order']);
foreach ($orders as $order) {
$sign = substr($order, 0, 1);
$direction = 'ASC';
if (in_array($sign, ['+', '-'])) {
if ($sign === '-') {
$direction = 'DESC';
}
$order = substr($order, 1);
}
$result = $order;
if (strpos($result, '.') === false) {
$result = $this->loadModel()->alias() . '.' . $order;
}
$result = $result . ' ' . $direction;
$results[] = $result;
}
}
return (sizeof($results) == 0) ? null : implode(',', $results);
}
private function getParams() {
$params = [
'equals' => [],
'or' => []
];
$parentModel = $this->loadModel();
$array = array_merge($_GET, $_POST);
foreach ($array as $field=>$value) {
$comparisonType = 'equals';
$operator = substr($field, strlen($field) - 1);
if (in_array($operator, ['!', '>', '<'])) {
$field = substr($field, 0, strlen($field) - 1);
$operator .= '=';
} else if (in_array($operator, ['|'])) {
$field = substr($field, 0, strlen($field) - 1);
$comparisonType = 'or';
$operator = '=';
} else if (in_array($operator, ['%'])) {
$field = substr($field, 0, strlen($field) - 1);
$operator = 'LIKE';
$value = '%'.$value.'%';
} else {
$operator = '=';
}
if ($value == 'null') {
$operator = (strpos($operator, '!') === false) ? 'IS' : 'IS NOT';
$value = null;
}
$field = str_replace('_', '.', $field);
if (strpos($field, '.') === false) {
$alias = $parentModel->alias();
} else {
$fieldExplosion = explode('.', $field);
$alias = $fieldExplosion[0];
$field = $fieldExplosion[1];
}
$model = null;
if ($parentModel->alias() !== $alias) {
$association = $parentModel->associations()->get($alias);
if (!is_null($association)) {
$model = $this->loadModel($association->className());
}
} else {
$model = $parentModel;
}
if (!is_null($model)) {
if ($model->hasField(rtrim($field, 's')) && !$model->hasField($field)) {
$field = rtrim($field, 's');
$value = '(' . $value . ')';
$operator = ' IN';
}
if ($model->hasField($field)) {
$params[$comparisonType][$alias.'.'.$field . ' ' . $operator] = $value;
}
}
}
return $params;
}
I am using Codeigniter with cloudflare and getting 520 error while storing user value in session during login.
Here is login function:
function check_login_submit($post_data) {
if ($post_data) {
$mob = trim($post_data['mob']);
$password = trim($post_data['password']);
$sql = "Select * from table where phone='$mob' and password='$password'";
$query = $this->db->query($sql);
$user = $query->row();
if ($query->num_rows() == 1) {
if ($user->status == 1)
{
$this->session->set_userdata('mem_id', $user->id);
$this->session->set_userdata('mem_last_login_date', $user->last_login_date);
$this->session->set_userdata('mem_created_on', $user->created_on);
//-- Update last login of successfull Login
$sql = "update table set last_login_date = NOW() where id=$user->id";
$query = $this->db->query($sql);
return TRUE;
}
}
else {
return FALSE;
}
}
}
If i will stop the storing value into session user data than it will working fine however with session cloudflare give me 502 error page.
Please advise
Thanks in advance for your time and support.
If anyone else runs into this problem, I came up with a solution that involves extending the core Session library that ultimately reduces the number of calls to sess_write() and by extension, _set_cookie().
MY_Session.php:
class MY_Session extends CI_Session {
function set_userdata($newdata = array(), $newval = '', $write_session = true)
{
if (is_string($newdata))
{
$newdata = array($newdata => $newval);
}
if (count($newdata) > 0)
{
foreach ($newdata as $key => $val)
{
$this->userdata[$key] = $val;
}
}
// Do not write the session (set the cookies) unless explicitly specified
if ($write_session) {
$this->sess_write();
}
}
function set_flashdata($newdata = array(), $newval = '')
{
if (is_string($newdata))
{
$newdata = array($newdata => $newval);
}
if (count($newdata) > 0)
{
foreach ($newdata as $key => $val)
{
$flashdata_key = $this->flashdata_key.':new:'.$key;
$this->set_userdata($flashdata_key, $val, false); // Do not update the cookie in the foreach
}
}
// Save the cookie now that all userdata has been set
$this->sess_write();
}
function _flashdata_mark()
{
$userdata = $this->all_userdata();
$newUserData = array();
$userDataToUnset = array();
foreach ($userdata as $name => $value)
{
$parts = explode(':new:', $name);
if (is_array($parts) && count($parts) === 2)
{
$new_name = $this->flashdata_key.':old:'.$parts[1];
$newUserData[$new_name] = $value;
$userDataToUnset[$name] = '';
// Cookies were originally set in this loop. Moved to the end of the function
}
}
// Save all changes outside of the loop
if (count($newUserData) > 0) {
$this->set_userdata($newUserData);
$this->unset_userdata($userDataToUnset);
}
}
}
A 520 error generally indicates that there are large cookies or headers being returned that hit proxy buffer limits on our end. A HAR file send to our support team will help us figure out what the issue is.
I've got some sample code that I'd like to refactor as I need it to work after a record is saved. It currently works after the record is first rendered (using the afterFilter). What it does is render the view that I want with the layout and saves it to a file.
function afterFilter() {
parent::afterFilter();
if($this->params['pass'][0] == 'contact') {
$surrenderOuput = $this->surrender($this->params['pass'][0]);
$path = WWW_ROOT . 'cache' . DS . $this->params['pass'][0] . DS . 'index.html';
$file = new File($path, true);
$file->write($surrenderOuput);
$file->close();
}
}
function surrender($action = null, $layout = null, $file = null) {
$this->beforeRender();
$viewClass = $this->view;
if ($this->view != 'View') {
if (strpos($viewClass, '.') !== false) {
list($plugin, $viewClass) = explode('.', $viewClass);
}
$viewClass = $viewClass . 'View';
App::import('View', $this->view);
}
$this->Component->beforeRender($this);
$this->params['models'] = $this->modelNames;
if (Configure::read() > 2) {
$this->set('cakeDebug', $this);
}
$View =& new $viewClass($this);
if (!empty($this->modelNames)) {
$models = array();
foreach ($this->modelNames as $currentModel) {
if (isset($this->$currentModel) && is_a($this->$currentModel, 'Model')) {
$models[] = Inflector::underscore($currentModel);
}
$isValidModel = (
isset($this->$currentModel) && is_a($this->$currentModel, 'Model') &&
!empty($this->$currentModel->validationErrors)
);
if ($isValidModel) {
$View->validationErrors[Inflector::camelize($currentModel)] =&
$this->$currentModel->validationErrors;
}
}
$models = array_diff(ClassRegistry::keys(), $models);
foreach ($models as $currentModel) {
if (ClassRegistry::isKeySet($currentModel)) {
$currentObject =& ClassRegistry::getObject($currentModel);
if (is_a($currentObject, 'Model') && !empty($currentObject->validationErrors)) {
$View->validationErrors[Inflector::camelize($currentModel)] =&
$currentObject->validationErrors;
}
}
}
}
$this->autoRender = false;
$output = $View->render($action, $layout, $file);
return $output;
}
So I'm basically rendering the view with it's layout, and returning it as output, and saving it to a file. Great. Is there any way to do something similar in a model?
You may consider setting a member variable in your afterSave() in the model and checking that value in your afterFilter() in your controller.
I found this thread while searching for how to render a view from a model. In my case I'm calling a custom method in the model, so this might not work for afterSave(), but if you're calling a custom method you can do it like this:
Controller:
$this->Model->myFunc($this);
Model
public function myFunc($object) {
$object->render();
}
Hopefully that helps someone else who comes across this thread.