CakePHP 3 - ownership authorisation for associated tables - php

In the CakePHP 3 Blog Tutorial, users are conditionally authorized to use actions like edit and delete based on ownership with the following code:
public function isAuthorized($user)
{
// All registered users can add articles
if ($this->request->getParam('action') === 'add') {
return true;
}
// The owner of an article can edit and delete it
if (in_array($this->request->getParam('action'), ['edit', 'delete'])) {
$articleId = (int)$this->request->getParam('pass.0');
if ($this->Articles->isOwnedBy($articleId, $user['id'])) {
return true;
}
}
return parent::isAuthorized($user);
}
public function isOwnedBy($articleId, $userId)
{
return $this->exists(['id' => $articleId, 'user_id' => $userId]);
}
I've been attempting to implement something similar for my own tables. For example, I have a Payments table, which is linked to Users through several different tables as follows:
Users->Customers->Bookings->Payments.
Foreign keys for each:
user_id in Customers table = Users->id (User hasOne Customer)
customer_id in Bookings table = Customers->id (Customer hasMany Bookings)
booking_id in Payments table = Bookings->id(Booking hasMany Payments)
My AppController's initialize function:
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Auth',[
'authorize' => 'Controller',
]);
$this->Auth->allow(['display']); //primarily for PagesController, all other actions across the various controllers deny access by default
}
In my PaymentsController, I have the following
public function initialize()
{
parent::initialize();
}
public function isAuthorized($user)
{
if (in_array($this->request->action,['view', 'edit', 'index', 'add']
return (bool)($user['role_id'] === 1); //admin functions
}
if (in_array($this->request->action,['cart'])) {
return (bool)($user['role_id'] === 2) //customer function
}
if (in_array($this->request->action, ['cart'])) {
$bookingId = (int)$this->request->getParam('pass.0');
if ($this->Payments->isOwnedBy($bookingId, $user['id'])) {
return true;
}
}
return parent::isAuthorized($user);
}
public function isOwnedBy($bookingId, $userId)
{
return $this->exists(['id' => $bookingId, 'user_id' => $userId]);
}
I'm unsure as to how to link through the different tables to determine ownership.
Currently if a customer who is paying for Booking #123 could just change the URL so they are paying for Booking #111, provided that Booking exists in the database.
Additionally, the Booking ID is passed to the Cart function (since customers are paying for a specific booking). For example: If customer is paying for Booking #123, then the URL = localhost/project/payments/cart/123. Upon submitting their cart, a new Payment entry is created.
Also, regarding the getParam and isOwnedBy methods, hovering over them in my editor shows this:
Method 'getParam' not found in \Cake\Network\Request
Method 'isOwnedBy' not found in App\Model\Table\PaymentsTable
However, I've gone through the entire BlogTutorial and can't find anywhere else that getParam or isOwnedBy is used or set up in the Model.

In the IsAuthorized function in PaymentsController:
if (in_array($this->request->action, ['cart'])) {
$id = $this->request->getParam('pass'); //use $this->request->param('pass') for CakePHP 3.3.x and below.
$booking = $this->Payments->Bookings->get($id,[
'contain' => ['Artists']
]);
if ($booking->artist->user_id == $user['id']) {
return true;
}
}

Related

laravel auth() is not allowing a user to make a post request

A user should be able to set a rating for more than one book, it allows a user to set a rating for ONLY one book, but not any other.
The problem
the user is not able to make another rating for
another book. However, another user
can make a rating for the same book, but cant make another rating for
other books. Not even if the user logged out.
I'm using rateYo and laravel-ratable
The way i have it set up, a rating type is set to false by default, enabling a user to set stars and make a rating pretty much.
And once again, once a user makes a rating for any book doesn't matter which, the rating type is set to true which disables a user to set a star.
Here is what i have
here is how the html setup is like
HTML
<div id="rateYo" data-rateyo-rating="{{ $book->userSumRating or 0}}" data-rateyo-read-only="{{ $book->rated ? 'true' : 'false' }}"></div`>
BookController.php
public function rate(Request $request, $book_id)
{
$book = Book::find($book_id);
$rating = $book->ratings()->where('user_id', auth()->user()->id)->first();
if(is_null($rating)){
$ratings = new Rating();
$ratings->rating = $request['rating'];
$ratings->user_id = auth()->user()->id;
$ratings->type = Book::RATED;
$book->ratings()->save($ratings);
return json_encode($book);
}
else{
return response()->json(['status' => 'You already left a review']);
}
}
public function show($book_name)
{
$books = Book::GetBook($book_name)->get();
$data = $books->map(function(Book $book){
$book['rated'] = $book->type;
return $book;
});
return view('books.show', compact('data', $data));
}
Book.php(relevant code) On default if a rating has not been set, type is equal to false enabling the user to make a rating, if a rating is set, type is equal to true disabling the star feature/read mode.
use Rateable;
const RATED = "true";
const NOT_RATED = "false";
protected $fillable = [ 'user_id', 'title', 'description'];
protected $appends = ['rated'];
public function getRatedAttribute()
{
return Rate::where('type', $this->owl() )->where('user_id', auth()->user()->id )->first();
}
public function owl(){
foreach($this->rate as $rates){
if ($rates->type != "true"){
return self::NOT_RATED;
}
}
return self::RATED;
}
public function rate()
{
return $this->hasMany('App\Rate', 'type', 'user_id');
}
You need to add the book id in the where clause to check if the user has left a rating for that particular book. As you have it it will find the first rating a user has left for any book.
$constraints = [
'user_id' => auth()->id(),
'book_id' => $book_id
];
// checks if the user has not left a rating for this particular book
if ($book->ratings()->where($constraints)->doesntExist()) {
// create new rating...
} else {
// user has already rated this book...
}
i need to change my Book model to the following, thank you digital for steering me into the right direction
Book.php
public function getRatedAttribute()
{
$this->constraints = [
'user_id' => auth()->id()
];
if(Rating::where($this->constraints)->exists()){
return Rating::where(['type' => self::RATED, 'book_id' => $this->getKey()])->first();
}else{
return Rating::where('type' ,self::NOT_RATED)->first();
}
}

what is the best practice for role based login system in Codeigniter

I am working on one role based login system. Actually, What should I do to the controller, model and the views in this role based login system to allocate different access criteria.
I am little confused about how to set and access for the user according to the role.
Mainly I am not sure about how to allocate different view as a role.
ex. I apply if condition to check role and then view according to the role the menu show the different links. like main admin can only watch account tab. the user can not see the account tab.
I also set the same if condition with the session in the controller for preventing direct access to that page.
Here is my code which I applied to menu and controller.
<?php
$login_role= $this->session->userdata('user_data');
if($login_role['user_role'] === 'super_admin'){
?><li><a href="<?php echo base_url('account/view_account'); ?>">
<div>Account</div></a></li><?php
}
?>
and the same condition in the controller
public function index()
{
$login_role= $this->session->userdata('user_data');
if($login_role['user_role'] === 'super_admin')
{
$this->load->model('location_model');
$city_list = $this->location_model->get_city_list();
$state_list = $this->location_model->get_state_list();
//log_message('info', 'City and State list will sucessfully loded.');
$this->load->view('admin/account_insert',['city_list'=>$city_list,'state_list'=>$state_list]);
} else {
redirect('admin/dashboard','refresh');
}
}
I am not sure about is this safe to use like this way. or I have to do something else as a good practice.
I am using a single Controller Login system for all user roles. I have a table of user roles and I have role id in users table. Then I have controller names matching those roles. When user login, I check for role and redirect the user to that controller after verification. Following is the index function of my Login Controller.
public function index()
{
if(!$this->isLoggedIn())
{
$data['title']='Title You want to set on Page';
if($_POST)
{
$config=array(
array(
'field' => 'email',
'label' => 'Email',
'rules' => 'trim|required|valid_email',
),
array(
'field' => 'password',
'label' => 'Password',
'rules' => 'trim|required',
),
);
$this->form_validation->set_rules($config);
if($this->form_validation->run()==false)
{
$data['errors']=validation_errors();
$this->load->view('static/head', $data);
$this->load->view('admin/login');
}
else
{
$user=$this->admin_model->checkUser($_POST);
if(!empty($user))
{
if($user['role']==1)
{
$user['type']='admin';
}
elseif($user['role']==2)
{
$user['type']='team';
}
elseif($user['role']==3)
{
$user['type']='client';
}
elseif($user['role']==4)
{
$user['type']='manager';
}
$this->session->set_userdata($user);
redirect(base_url().$user['type']);
}
else
{
$data['errors']='The credentials you have provided are incorrect or your account has not been approved yet.';
$this->load->view('static/head', $data);
$this->load->view('admin/login');
}
}
}
else
{
$this->load->view('static/head', $data);
$this->load->view('admin/login');
}
}
else
{
redirect(base_url().$this->session->userdata['type']);
}
}
Its working perfectly for me. Furthermore in each Controller I have functions to check if the user is logged in for this role like this
public function isLoggedIn()
{
if(!empty($this->session->userdata['id'])&& $this->session->userdata['type']=='team')
{
return true;
}
else
{
return false;
}
}
And I render my index function of that controller. E.g Following is the team controller index function
public function index()
{
if($this->isLoggedIn())
{
$data['menu']=$this->team_model->getMenuItems();
$data['task_logs']=$this->admin_model->getAllLogs();
$data['title']='Title';
$this->load->view('static/head',$data);
$this->load->view('static/header');
$this->load->view('static/sidebar');
$this->load->view('team/dashboard');
$this->load->view('static/footer');
}
else
{
redirect(base_url());
}
}

Yii: Getting the role of logged in users and showing content according to role

I want to get the roles of the registered users and show the content to the registered users according to their roles.
I have two users right now.
admin
user(authenticated)
The thing i am trying to do is that when the admin logs in via "webapp/user/login" a sidebarwidget which i have already made should be shown upon login and when the user(authenticated) gets logged in, the user(authenticated) should only be able to see the index.php page.
I am using Yii users and rights. I have looked around and found this piece of code which is for getting the role of the logged in user but I dont know where to place this piece of code to get the output.
Below are two pieces of codes, please do tell me which one will be more useful.
if($user = Users::model()->findAll()) {
foreach($user as $id => $user) {
if(!$user->checkAccess('Authenticated')) {
unset($user[$id]);
}
}
$users = array_values($user); // to reset indices (optional)
}
and this is another piece of code which i have found.
$command = Yii::app()->db->createCommand("SELECT * FROM `authassignment` WHERE userid={$user->id}");
$results = $command->queryAll();
$roles = array();
foreach ($results as $result)
{
$roles[] = $result['itemname'];
}
$this->setState('roles', $roles);
From what I have done following tutorials, here is a proposal.
The authentication can take place in file protected/components/UserIdentity.php :
public function authenticate($native=false){
$record=User::model()->findByAttributes(array('username'=>$this->username));
//can provide function "same" if needed - found it here:
//http://codereview.stackexchange.com/questions/13512
if($record!==null&&$this->same($record->password,crypt($this->password,$record->password)){
$authRoleName=Role::model()->findByAttributes(array('id'=>$record->role_id))->name;
$this->setState('role_name', $authRoleName);
$this->errorCode = self::ERROR_NONE;
}else{
$this->errorCode=self::ERROR_UNKNOWN_IDENTITY;
}
return !$this->errorCode;
}
In this case the several roles (admin, mobile, user, etc) are stored in db (table roles) and each user model has a role_id.
I assume the SiteController does the login (file protected/controllers/SiteController.php):
public function actionLogin()
{
$model=new LoginForm;
// if it is ajax validation request
if(isset($_POST['ajax']) && $_POST['ajax']==='login-form')
{
echo CActiveForm::validate($model);
Yii::app()->end();
}
// collect user input data
if(isset($_POST['LoginForm']))
{
$model->attributes=$_POST['LoginForm'];
// validate user input and redirect to the previous page if valid
if($model->validate() && $model->login()){
$this->redirect(Yii::app()->user->returnUrl);
}
}
// display the login form
$this->render('login',array('model'=>$model));
}
File protected/models/LoginForm.php:
class LoginForm extends CFormModel
public $username;
public $password;
public $rememberMe;
private $_identity;
public function authenticate($attribute,$params)
{
if(!$this->hasErrors())
{
$this->_identity=new UserIdentity($this->username,$this->password);
if(!$this->_identity->authenticate())
$this->addError('password','False username or password.');
}
}
public function login()
{
if($this->_identity===null)
{
$this->_identity=new UserIdentity($this->username,$this->password);
$this->_identity->authenticate();
}
if($this->_identity->errorCode===UserIdentity::ERROR_NONE)
{
$duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days
Yii::app()->user->login($this->_identity, duration);
return true;
}
else
return false;
}
In view you could do a role based decision making, like the example below in file protected/views/site/index.php :
<?php
$userModel =User::model()->findByAttributes(array('id'=>Yii::app()->user->getId()));
if($userModel){
if(Yii::app()->user->getState('role_name') == 'admin'){
$this->renderPartial(
//...
);
}else{
//...
}
}
Moreover, if RBAC is on your mind, and you manage to have a proper protected/data/auth.php (there are ways for this, I use command "./protected/yiic rbac" after creating file protected/commands/RbacCommand.php - I can post this latter file if needed) then in any place in your code you simply:
if(Yii::app()->user->checkAccess('admin')){
//staff for admins
}
Also, in this case, you could set the rights of whole actions in controller's function accessRules() by issuing roles instead of usernames:
public function accessRules()
{
return array{
array('allow',
'actions'=>array('index', 'index2', 'view','create','update','getRecordDetails', 'getTotalCount'),
'roles'=>array('admin'),
),
);
}

CakePHP: sending latest user id to admin's create_employee view

I am trying to send the latest user's id from UsersController to AdminController whose add_employee() action creates a new employee. My users and employees table are separate and what I want to do is when Admin creates a new user its entry go into users table. Then he opens create employee form and the latest user id will be assigned to the new employee the admin is creating. So when admin will open create new employee form the latest user id will be shown in the form.
My UsersController has this code for sending latest user it to AdminsController:
public function get_latest_user_id()
{
$content = $this->User->query("SELECT id FROM users ORDER BY id DESC LIMIT 0,1");
$this->set('latest_user', $content);
}
AdminsController page's add_employee contains this code:
public function add_employee()
{
$this->loadModel('Employee');
$this->set('latest_user', $this->requestAction('/Users/get_latest_user_id'));
if ($this->request->is('post'))
{
$this->Employee->create();
if ($this->Employee->save($this->request->data))
{
$this->Session->setFlash(__('The employee profile has been saved.'));
return $this->redirect(array('action' => 'list_of_employees'));
}
else
{
$this->Session->setFlash(__('The employee profile could not be saved. Please, try again.'));
}
}
}
So UserController's get_latest_user_id function sends latest user id to add_employee function of AdminController. There latest_user is set to latest user id so that when add_employee view is called it is there. But it is not showing. So I want to know that am i doing it right? Please help and thanks.
In add_employee.ctp I am displaying it like this:
echo $latest_user['User']['id'];
Move get_latest_user_id to the User model
public function get_latest_user_id()
{
$user = $this->query("SELECT id FROM users ORDER BY id DESC LIMIT 1");
if (empty($user)) {
return 0;
}
// return only the Id
return $user[0]['users']['id'];
}
In the controller:
public function add_employee()
{
$this->loadModel('Employee');
$this->loadModel('User');
$this->set('latest_user', $this->User->get_latest_user_id());
if ($this->request->is('post'))
{
// ....
}
}
cornelb is right that you should move the method to your User model. Although a more Cake-ish approach would be to use a find('first'), rather than doing a direct query():
// app/Model/User.php
public function getLatest() {
// Get the latest user
$user = $this->find('first', array(
'fields' => array('id'), // Only interested in id? Use this.
'order' => array('User.id' => 'DESC')
));
if (!empty($user)) {
return $user['User']['id'];
} else {
// Nothing was returned, this is very awkward
throw new NotFoundException(__('No users found!'));
}
}
And in your controller:
// app/Controller/AdminsController.php
public function add_employee() {
$this->loadModel('User');
$this->set('latestUser', $this->User->getLatest());
// ...
}

CakePHP check or add user id to posts

I have the following two actions in my controller:
function add()
{
if (!empty($this->data))
{
if ($this->Favour->save($this->data))
{
$this->Session->setFlash('Your favour has been saved.');
$this->redirect(array('controller'=>'favours','action'=>'index'));
}
}
}
function edit($id = null)
{
$this->Favour->id = $id;
if (empty($this->data))
{
$this->data = $this->Favour->read();
}
else
{
if ($this->Favour->save($this->data))
{
$this->Session->setFlash('Your favour has been updated.');
$this->redirect(array('controller'=>'favours','action'=>'index'));
}
}
}
1) I want to be able to add the logged in user id to the add action so that the new post is created with that user as its author id (their is a foreign key in the db table). I'm not sure how to talk to fields within the controller itself.
2) And for the edit action I want to make it so that only the author can edit the post so for example user 200 creates post 20 but user 100 cannot edit this post because his id is not 200! I'm not using ACL for my app but just simple authentication.
I've thought about doing a simple if statement in the action like:
function edit($id = null)
{
$this->Favour->id = $id;
$this->Favour->user_id = $user_id;
if($this->Auth->user('id') != $user_id)
{
$this->Session->setFlash('You do not have permission to edit that favour!');
$this->redirect(array('controller'=>'favours','action'=>'index'));
}
else
{
if (empty($this->data))
{
$this->data = $this->Favour->read();
}
else
{
if ($this->Favour->save($this->data))
{
$this->Session->setFlash('Your favour has been updated.');
$this->redirect(array('controller'=>'favours','action'=>'index'));
}
}
}
Would this be correct? BUT how do I get the user id from the favour?
function add() {
if (!empty($this->data)) {
$this->data['Favour']['user_id'] = $this->Auth->user('id');
if ($this->Favour->save($this->data)) {
//etc
This code assumes:
Your user is logged in
the user can access the add function
You are storing the id value of the logged in user in the field id
You have a foreign key in Favours table called user_id that matches the data type of the user id
As for edit; couple ways of achieving it.
I'd do:
function edit($id) {
$this->Favour->id = $id;
$favour_author = $this->Favour->field('user_id');
// get the user of this post
if($this->Auth->user('id') != $favour_author) {
$this->Session->setFlash('You do not own this post.');
$this->redirect('/someplace');
}
if (empty($this->data)) {
$this->data = $this->Favour->read();
}
// carry on.
If you use Auth Component, you can access the logged-in user record in $this->Auth->user() in controller. So to access the id: $this->Auth->user('id'). If you write your own authentication, it's up to you.
how to talk to fields within the controller itself.
What do you mean?

Categories