I'm trying to make a blogging system but obviously certain users in certain groups should only be able to edit/delete their own posts/comments. How would I go about doing this in CakePHP? I followed the manual's basic Acl guide to setup my current Auth system.
Assuming you have a Post and Comment model and the user_id is present via the $this->Auth->... object, you could define a generic method in your model:
function allowUserPost($user_id, $post_id) {
// Then check if this $user_id owns the $post_id
// if true, return true, if false, return false
}
In your controller:
function deletePost($post_id) {
if($this->Post->allowUserPost($user_id, $post_id) {
// Go ahead, delete
} else {
// Deny
}
Related
I have a database that in this: Admin has True isAdmin property, but other users have false isAdmin property.
I want to check if the user who logged in is an Admin or not by redirecting them to different pages in my app. My code in Controller is:
public function store(User $user)
{
if (auth()->attempt(request(['email', 'password']))) {
if ($user->isAdmin == 1) {
return redirect('/ShowUser');
}
{
return redirect('/lo');
}
}
return back()->withErrors(
[
'message' => 'Error'
]
);
}
But this code doesn't work; it sends the users to '/lo' all the time. How can I fix it?
You're missing an else keyword.
Right here:
if ($user->isAdmin == 1) {
return redirect('/ShowUser');
}
{ // <-- right here
return redirect('/lo');
}
add the else keyword.
if ($user->isAdmin == 1) {
return redirect('/ShowUser');
}
else { // <-- right here
return redirect('/lo');
}
anyway, your code will still run fine even after the edit above. But I have questions for you:
Is the user assumed to be in the database already?
What is the default value of isAdmin in the database?
Are you passing the isAdmin attribute as an input from a form or something?
And why is it a store request when you're just trying to log a user in?
It's a bit confusing. I can tell from your code that you're trying to log a user in, but you're doing it in a store method (nothing wrong with that, just convention), the store method is usually used in storing data (how coincidental!)
I have /signup/select-plan which lets the user select a plan, and /signup/tos which displays the terms of services. I want /signup/tos to be only accessible from /signup/select-plan. So if I try to go directly to /signup/tos without selecting a plan, I want it to not allow it. How do I go about this?
In the constructor, or the route (if you are not using contructors), you can check for the previous URL using the global helper url().
public function tos() {
if ( !request()->is('signup/tos') && url()->previous() != url('signup/select-plan') ) {
return redirect()->to('/'); //Send them somewhere else
}
}
In the controller of /signup/tos which returns the tos view just add the following code:
$referer = Request::referer();
// or
// $referer = Request::server('HTTP_REFERER');
if (strpos($referer,'signup/select-plan') !== false) {
//SHOW THE PAGE
}
else
{
dd("YOU ARE NOT ALLOWED")
}
What we are doing here is checking the HTTP referrer and allowing the page access only if user comes from select-plan
You are need of sessions in laravel. You can see the following docs to get more info: Laravel Sessions
First of all you need to configure till how much time you want to have the session variable so you can go to your directory config/sessions.php and you can edit the fields 'lifetime' => 120, also you can set expire_on_close by default it is being set to false.
Now you can have following routes:
Route::get('signup/select-plan', 'SignupController#selectPlan');
Route::post('signup/select-token', 'SignupController#selectToken');
Route::get('signup/tos', 'SignupController#tos');
Route::get('registered', 'SignupController#registered');
Now in your Signupcontroller you can have something like this:
public function selectPlan()
{
// return your views/form...
}
public function selectToken(Request $request)
{
$request->session()->put('select_plan_token', 'value');
return redirect('/signup/tos');
}
Now in signupController tos function you can always check the session value and manipulate the data accordingly
public function tos()
{
$value = $request->session()->get('select_plan_token');
// to your manipulation or show the view.
}
Now if the user is registered and you don't need the session value you can delete by following:
public function registered()
{
$request->session()->forget('select_plan_token');
// Return welcome screen or dashboard..
}
This method will delete the data from session. You can manipulate this. You won't be able to use in tos function as you are refreshing the page and you want data to persist. So its better to have it removed when the final step or the nextstep is carried out. Hope this helps.
Note: This is just the reference please go through the docs for more information and implement accordingly.
I have created a log in form for my admin pages and it works, but for now everyone who logs in can access those pages.
My users are also belong to usergroups and my users table in the database has a group_id column. The admin group has an id of 1.
What I'd like to do is that if someone who belongs to the admin group logs in can access the admin pages, but if the user belongs to a different group and tries to log in be redirected to main page or anywhere.
What I'm trying to do is add a similar code to the admin pages controllers
class Dashboard extends MY_Controller {
public function __construct() {
parent::__construct();
// Access control
if(!$this->session->userdata('logged_in')) {
redirect('admin/login');
}
}
}
My model
class Authenticate_model extends CI_Model {
public function login_user($username, $password){
//Secure password
$enc_password = md5($password);
//Validate
$this->db->where('username',$username);
$this->db->where('password',$enc_password);
$result = $this->db->get('users');
if($result->num_rows() == 1){
return $result->row();
} else {
return false;
}
}
}
You can do it yourself, provide validation and set $SESSION variable to retrieve if the user is logged in. But this is too much work, and error prone.
I recommend you to use this popular library: https://github.com/benedmunds/CodeIgniter-Ion-Auth. It is really easy to set up, you just need to copy some files and you are ready to go.
If you really want to do it your self(thats the question) then you need to store in SESSION two variables - logged_in and is_admin.
I would recomend two create library with function:
function is_logged_in($admin = FALSE){
$is_logged = $this->session->userdata('logged_in');
if($admin){
$is_logged = $this->session->userdata('is_admin')
}
return $is_logged;
}
This assumes that you store two booleans "logged_in" and "is_admin" in SESSION.(If user has group_id = 1 then you would store TRUE in is_admin)
Then you can protect your site members only pages
if(!$this->your_authenticatation_library->is_logged_in()){redirect('notMembersControler')}
and admin page:
if(!$this->your_authenticatation_library->is_logged_in(TRUE)){redirect('notMembersControler')}
Thats the basic idea, you need to work around depending on what you are up to. Hope this helps!
I am making a system using CakePHP in which the users can be either A, B or C. Like student, teacher and some other role. Is it possible to let them all login via 1 link? so not /students/login and /teachers/login, but something like www.somewebsite/login for all of them?
Read this tutorial, it exactly covers what you're asking for. Also read this section.
It does not make any sense at all to have different controllers for different types of users, you'll just duplicate code. If you need to take different actions based on the role you can do that within your login method by calling another method like afterStudentLogin() from within your login() method and do role specific things there. The reason for that is that a single method should always just do one task, so you decouple the role specific code from it in a separate method.
public function login() {
if ($this->Auth->user()) {
/* ... */
$callback = 'after' . $this->Auth->user('role') . 'Login');
$this->{$callback}($this->Auth->user());
/* ... */
}
}
Even if the user types are very different, they all will share a common thing: The login. In this case have an users table and for example student_profils table and teacher_profiles table. If the difference is just a few fields I would put them all in one table like profiles.
If you want to have /login instead of /users/login you should use routing.
Router::connect(
'/login',
array(
'controller' => 'users',
'action' => 'login'
)
);
You can also take a look at this Users plugin which covers a lot of the usual user related tasks. And here is a simple multi-role authorization adapter.
A simple basic login function depending on user group would look like as given below
<?php
public function login() {
//if user already logged in call routing function...
if($this->Session->read('Auth.User')) {
$this->routing();
}
if ($this->request->is('post')) {
if ($this->Auth->login()) {
//if user status is active...
if ($this->Auth->user('status') == 1){
//redirect users based on his group id...
if($this->Auth->User('group_id')==1){
$this->redirect($this->Auth->redirect('/admins/dashboard'));
}
else if($this->Auth->User('group_id')==2){
$this->redirect($this->Auth->redirect('/teachers/dashboard'));
}
else if($this->Auth->User('group_id')==3){
$this->redirect($this->Auth->redirect('/students/dashboard'));
}
}
else{
$this->Session->delete('User');
$this->Session->destroy();
$this->Session->setFlash('Your account is not yet activated. Please activate your account to login.', 'warning');
}
}
else {
$this->Session->setFlash('Your username or password was incorrect.', 'error');
}
}
}
//just route the loggedin users to his proper channel...
public function routing() {
if($this->Session->read('Auth.User.Group.id') == 1) {
$this->redirect('/admins/dashboard');
}
else if($this->Session->read('Auth.User.Group.id') == 2) {
$this->redirect('/teachers/dashboard');
}
else if($this->Session->read('Auth.User.Group.id') == 3) {
$this->redirect('/students/dashboard');
}
else {
$this->Session->destroy();
$this->redirect('/');
}
}
?>
How can I get a security token for any user, not only the one currently logged in ?
I would like to be able to call isGranted() on a user fetched from the database
isGranted() comes from the Security service, so it would be hard/unnecessary to use that to get Roles without adjusting the state of the session.
Don't get me wrong, it's definitely possible... This would work, for example:
public function strangeAction()
{
// Get your User, however you normally get it
$user = $userRepository->find($id);
// Save the current token so you can put it back later
$previousToken = $this->get("security.context")->getToken();
// Create a new token
$token = new UsernamePasswordToken($user, null, "main", $user->getRoles());
// Update the security context with the new token
$this->get("security.context")->setToken($token);
// Now you have access to isGranted()
if ($this->get("security.context")->isGranted("ROLE_SOMETHING"))
{ /* Do something here */ }
// Don't forget to reset the token!
$this->get("security.context")->setToken($previousToken);
}
...but that really makes no sense.
In reality, you don't need the token. A much better way of doing this would be to add an isGranted() method into your User entity:
// Namespace\YourBundle\Entity\User.php
class User
{
...
public function isGranted($role)
{
return in_array($role, $this->getRoles());
}
...
}
Now you can get those roles in your controllers:
public function notSoStrangeAction()
{
// Get your User, however you normally get it
$user = $userRepository->find($id);
// Find out if that User has a Role associated to it
if ($user->isGranted("ROLE_SOMETHING"))
{ /* Do something here */ }
}
I had the same requirements a while ago. So I implemented it myself. Since you require the hierarchy information from the container it is not possible advised to extend the user entity with this functionality though.
// first check if the role is inside the user roles of the user
// if not then check for each user role if it is a master role of the check role
public function isGranted($user, $checkrole){
$userroles = $user->getRoles();
if (in_array($checkrole, $userroles)){return true;}
foreach ($userroles as $userrole){
if ($this->roleOwnsRole($userrole, $checkrole)){return true;}
}
return false;
}
// recursively loop over the subroles of the master to check if any of them are
// the suggested slave role. If yes then the masterrole is a master and has
// the same grants as the slave.
private function roleOwnsRole($masterRole, $slaveRole, $checkvalidityroles=true, $hierarchy=null)
{
if ($hierarchy===null){$hierarchy = $this->container->getParameter('security.role_hierarchy.roles');}
if ($masterRole === $slaveRole){ return false; }
if($checkvalidityroles && (!array_key_exists($masterRole, $hierarchy) || !array_key_exists($slaveRole, $hierarchy))){ return false; }
$masterroles = $hierarchy[$masterRole];
if(in_array($slaveRole, $masterroles)){
return true;
}else{
foreach($masterroles as $masterrolerec){
if ($this->roleOwnsRole($masterrolerec, $slaveRole, false, $hierarchy)){return true;}
}
return false;
}
}
I think the best way is to call AccessDecisionManager manually - like $securityContext->isGranted() does as well but for the currently logged in user. This is good too if you are using Symfony Voters to determine access.
$token = new UsernamePasswordToken($userObject, 'none', 'main', $userObject->getRoles());
$hasAccess = $this->get('security.access.decision_manager')->decide($token, array('voter'), $optionalObjectToCheckAccessTo);