Can not config Header security middleware CakePHP - cakephp

I have a site built with CakePHP, default it doesn't allow subdomain embed on iframe.
I configured Frame Option on nginx/conf.d and now my homepage can be embedded in an iframe of a subdomain.
However, another post cannot be embedded on a subdomain's iframe. (exp: http://example.net/postabc will not display). I tried to change options on Header security middleware(please read below).
Is there anywhere I need to change the configuration to so all my posts can be displayed?
<?php
namespace Cake\Http\Middleware;
use InvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class SecurityHeadersMiddleware
{
protected $headers = [];
public function noSniff()
{
$this->headers['x-content-type-options'] = 'nosniff';
return $this;
}
public function noOpen()
{
$this->headers['x-download-options'] = 'noopen';
return $this;
}
public function setReferrerPolicy($policy = 'same-origin')
{
$available = [
'no-referrer', 'no-referrer-when-downgrade', 'origin',
'origin-when-cross-origin',
'same-origin', 'strict-origin', 'strict-origin-when-cross-origin',
'unsafe-url'
];
$this->checkValues($policy, $available);
$this->headers['referrer-policy'] = $policy;
return $this;
}
public function setXFrameOptions($option = 'allow-from', $url = 'http://subdomain.example.net')
{
$this->checkValues($option, ['deny', 'sameorigin', 'allow-from']);
if ($option === 'allow-from') {
if (empty($url)) {
throw new InvalidArgumentException('The 2nd arg $url can not be empty when `allow-from` is used');
}
$option .= ' ' . $url;
}
$this->headers['x-frame-options'] = $option;
return $this;
}
public function setXssProtection($mode = 'block')
{
$mode = (string)$mode;
if ($mode === 'block') {
$mode = '1; mode=block';
}
$this->checkValues($mode, ['1', '0', '1; mode=block']);
$this->headers['x-xss-protection'] = $mode;
return $this;
}
public function setCrossDomainPolicy($policy = 'all')
{
$this->checkValues($policy, ['all', 'none', 'master-only', 'by-content-type', 'by-ftp-filename']);
$this->headers['x-permitted-cross-domain-policies'] = $policy;
return $this;
}
protected function checkValues($value, array $allowed)
{
if (!in_array($value, $allowed)) {
throw new InvalidArgumentException(sprintf(
'Invalid arg `%s`, use one of these: %s',
$value,
implode(', ', $allowed)
));
}
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
$response = $next($request, $response);
foreach ($this->headers as $header => $value) {
$response = $response->withHeader($header, $value);
}
return $response;
}
}

Related

Cannot see validation errors when form is invalid (submitted as json from react)

How do I see validation errors when posting json data and self submitting to a symfony form? isValid() is false but I can't access error messages to return. But the Symfony Profiler DOES show the error messages in the Ajax request history. E.g. when duplicate username the profiler shows:
Validator calls in ValidationListener.php
data.username There is already an account with this username
Forms "registration_form" "App\Form\RegistrationFormType"
There is already an account with this username
Caused by: Symfony\Component\Validator\ConstraintViolation
When all the fields are valid the new User is created in the database successfully as expected.
Here is my controller:
namespace App\Controller;
use App\Entity\User;
use App\Form\RegistrationFormType;
use App\Security\LoginFormAuthenticator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
class RegistrationController extends AbstractController
{
/**
* #Route("/api/register", name="app_register")
*/
public function register(
Request $request,
UserPasswordEncoderInterface $passwordEncoder,
GuardAuthenticatorHandler $guardHandler,
LoginFormAuthenticator $authenticator
): Response {
if ($request->isMethod('POST')) {
$user = new User();
$form = $this->createForm(RegistrationFormType::class, $user);
$data = json_decode($request->getContent(), true);
$form->submit($data);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$user->setPassword(
$passwordEncoder->encodePassword(
$user,
$form->get('plainPassword')->getData()
)
);
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
// login the newly registered user
$login = $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
if ($login !== null) {
return $login;
}
return $this->json([
'username' => $user->getUsername(),
'roles' => $user->getRoles(),
]);
} else {
$formErrors = $form->getErrors(true); // returns {}
return $this->json($formErrors, Response::HTTP_BAD_REQUEST);
}
}
}
}
Here is my RegistrationFormType:
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\IsTrue;
class RegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstName', TextType::class, [
'label' => 'First Name',
'required' => false
])
->add('lastName', TextType::class, [
'label' => 'Last Name',
'required' => false
])
->add('username')
->add('emailAddress', EmailType::class, [
'label' => 'Email Address'
])
->add('plainPassword', PasswordType::class, [
'mapped' => false
])
->add('agreeTerms', CheckboxType::class, [
'mapped' => false,
'constraints' => [
new IsTrue([
'message' => 'You must comply.',
]),
],
])
->add('Register', SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
'csrf_protection' => false
]);
}
public function getName()
{
return 'registration_form';
}
}
Here is my entity:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity(fields={"username"}, message="There is already an account with this username")
* #UniqueEntity(fields={"emailAddress"}, message="There is already an account with this email address")
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $username;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $emailAddress;
/**
* #ORM\Column(type="string", length=80, nullable=true)
*/
private $firstName;
/**
* #ORM\Column(type="string", length=80, nullable=true)
*/
private $lastName;
public function getId(): ?int
{
return $this->id;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return (string) $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getEmailAddress(): ?string
{
return $this->emailAddress;
}
public function setEmailAddress(string $emailAddress): self
{
$this->emailAddress = $emailAddress;
return $this;
}
public function getFirstName(): ?string
{
return $this->firstName;
}
public function setFirstName(?string $firstName): self
{
$this->firstName = $firstName;
return $this;
}
public function getLastName(): ?string
{
return $this->lastName;
}
public function setLastName(?string $lastName): self
{
$this->lastName = $lastName;
return $this;
}
}
Got it working.
$formErrors = [];
foreach ($form->all() as $childForm) {
if ($childErrors = $childForm->getErrors()) {
foreach ($childErrors as $error) {
$formErrors[$error->getOrigin()->getName()] = $error->getMessage();
}
}
}
return $this->json(
['errors' => $formErrors],
Response::HTTP_BAD_REQUEST
);
Returns:
{
"errors": {
"username": "There is already an account with this username"
}
}
Basically you get form errors at the top level like if there are extra fields etc but you get the form field errors from the actual child elements. You are only returning the top level form errors.
I have a helper function I use to return all errors from a form if I am returning a JsonResponse.
The following is my extended abstract controller. All my controllers extend this and then I can keep a few helper methods here.
I use the createNamedForm() method and keep the name blank as it's easier when sending from Ajax as I don't need to nest the data in the array with the form name as the key.
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as SymfonyAbstractController;
use Symfony\Component\Form\FormInterface;
class AbstractController extends SymfonyAbstractController
{
protected function createNamedForm(string $name, string $type, $data = null, array $options = []): FormInterface
{
return $this->container->get('form.factory')->createNamed($name, $type, $data, $options);
}
protected function getFormErrors($form)
{
$errors = [];
foreach ($form->getErrors() as $error) {
$errors[] = $error->getMessage();
}
foreach ($form->all() as $childForm) {
if ($childForm instanceof FormInterface) {
if ($childErrors = $this->getFormErrors($childForm)) {
$errors[$childForm->getName()] = $childErrors;
}
}
}
return $errors;
}
}
I would create the form in the following way:
$form = $this->createNamedForm('', RegistrationFormType::class);
and return my form errors as follows:
return $this->json($this->getFormErrors($form), 400)
If you want to use the normal createForm() function then you will need to send the data in the following format:
['registration_form' => ['firstName' => 'Tom', 'lastName' => 'Thumb']]
This works in both Symfony4 and Symfony5

Codeigniter 3.1.9 - CI_Session is filling up my database on every refresh

I have been getting back into Codeigniter as support was picked up by BCIT. I have a problem with ci_sessions and the database driver which is regenerating the encrypted session ID and storing new data in my database on every page refresh. I'm so frustrated right now! I have both secure file storage and database for both common drivers. I want to use both or either but the effect on my application is the same whether I am using a database or files. The ci_session keeps refreshing and it is not ideal for logins, registration or any account type. Please help me see what I am doing wrong? Much appreciation granted in advance.
Config:
$config['sess_driver'] = 'database';
$config['sess_cookie_name'] = 'ci_session';
$config['sess_expiration'] = 7200;
$config['sess_save_path'] = 'users';
$config['sess_match_ip'] = FALSE;
$config['sess_time_to_update'] = 300;
$config['sess_regenerate_destroy'] = FALSE;
Controllers:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* User Management class created by CodexWorld
*/
class Limousers extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->library('form_validation');
$this->load->model('user');
}
/*
* User account information
*/
public function account(){
print_r($_SESSION);
$data = array();
print_r($this->session->userdata());
if($this->session->userdata('isUserLoggedIn')){
$data['user'] = $this->user->getRows(array('id'=>$this->session->userdata('userId')));
//load the view
$this->load->view('limousers/account', $data);
}else{
redirect('limousers/login');
exit;
}
}
/*
* User login
*/
public function login(){
print_r($_SESSION);
if($this->session->userdata('isUserLoggedIn'))
{
print_r($this->session->userdata);
redirect('limousers/account');
exit;
}
$data = array();
if($this->session->userdata('success_msg')){
$data['success_msg'] = $this->session->userdata('success_msg');
$this->session->unset_userdata('success_msg');
}
if($this->session->userdata('error_msg')){
$data['error_msg'] = $this->session->userdata('error_msg');
$this->session->unset_userdata('error_msg');
}
if($this->input->post('loginSubmit')){
$this->form_validation->set_rules('email', 'Email', 'required|valid_email');
$this->form_validation->set_rules('password', 'password', 'required');
if ($this->form_validation->run() == true) {
$con['returnType'] = 'single';
$con['conditions'] = array(
'email'=>$this->input->post('email'),
'password' => md5($this->input->post('password')),
'status' => '1'
);
$checkLogin = $this->user->getRows($con);
if($checkLogin){
$this->session->set_userdata('name',$con['conditions']['email']);
$this->session->set_userdata('isUserLoggedIn',TRUE);
$this->session->set_userdata('userId',$checkLogin['id']);
redirect('limousers/account');
exit;
}else{
$data['error_msg'] = 'Wrong email or password, please try again.';
}
}
}
//load the view
$this->load->view('limousers/login', $data);
}
/*
* User registration
*/
public function registration(){
print_r($_SESSION);
$data = array();
$userData = array();
if($this->input->post('regisSubmit')){
$this->form_validation->set_rules('name', 'Name', 'required');
$this->form_validation->set_rules('email', 'Email', 'required|valid_email|callback_email_check');
$this->form_validation->set_rules('password', 'password', 'required');
$this->form_validation->set_rules('conf_password', 'confirm password', 'required|matches[password]');
$userData = array(
'name' => strip_tags($this->input->post('name')),
'email' => strip_tags($this->input->post('email')),
'password' => md5($this->input->post('password')),
'gender' => $this->input->post('gender'),
'phone' => strip_tags($this->input->post('phone'))
);
if($this->form_validation->run() == true){
$insert = $this->user->insert($userData);
if($insert){
$this->session->set_userdata('success_msg', 'Your registration was successfully. Please login to your account.');
redirect('limousers/login');
exit;
}else{
$data['error_msg'] = 'Some problems occured, please try again.';
}
}
}
$data['user'] = $userData;
//load the view
$this->load->view('limousers/registration', $data);
}
/*
* User logout
*/
public function logout(){
$this->session->unset_userdata('isUserLoggedIn');
$this->session->unset_userdata('userId');
$this->session->sess_destroy();
redirect('limousers/login');
exit;
}
/*
* Existing email check during validation
*/
public function email_check($str){
$con['returnType'] = 'count';
$con['conditions'] = array('email'=>$str);
$checkEmail = $this->user->getRows($con);
if($checkEmail > 0){
$this->form_validation->set_message('email_check', 'The given email already exists.');
return FALSE;
} else {
return TRUE;
}
}
}
Models:
<?php if ( ! defined('BASEPATH')) exit('No direct script access
allowed');
class User extends CI_Model{
function __construct() {
$this->userTbl = 'users';
}
/*
* get rows from the users table
*/
function getRows($params = array()){
$this->db->select('*');
$this->db->from($this->userTbl);
//fetch data by conditions
if(array_key_exists("conditions",$params)){
foreach ($params['conditions'] as $key => $value) {
$this->db->where($key,$value);
}
}
if(array_key_exists("id",$params)){
$this->db->where('id',$params['id']);
$query = $this->db->get();
$result = $query->row_array();
}else{
//set start and limit
if(array_key_exists("start",$params) &&
array_key_exists("limit",$params)){
$this->db->limit($params['limit'],$params['start']);
}elseif(!array_key_exists("start",$params) &&
array_key_exists("limit",$params)){
$this->db->limit($params['limit']);
}
$query = $this->db->get();
if(array_key_exists("returnType",$params) &&
$params['returnType'] == 'count'){
$result = $query->num_rows();
}elseif(array_key_exists("returnType",$params) &&
$params['returnType'] == 'single'){
$result = ($query->num_rows() > 0)?$query- >row_array():FALSE;
}else{
$result = ($query->num_rows() > 0)?$query->result_array():FALSE;
}
}
//return fetched data
return $result;
}
/*
* Insert user information
*/
public function insert($data = array()) {
//add created and modified data if not included
if(!array_key_exists("created", $data)){
$data['created'] = date("Y-m-d H:i:s");
}
if(!array_key_exists("modified", $data)){
$data['modified'] = date("Y-m-d H:i:s");
}
//insert user data to users table
$insert = $this->db->insert($this->userTbl, $data);
//return the status
if($insert){
return $this->db->insert_id();
}else{
return false;
}
}
}

Cakephp 3 PHPUnit ingration test fails after the test before

I have the UsersFixture with three records.
The test methods first() and second(), which both are before guest_can_login(), pr's show "Joe", "Joe", as expected. But with test method third(), which comes after guest_can_login(), I get notice error: trying to get property of non-object.
So, for a reason, something in the guest_can_login() breaks the rest of the test methods. I have tried by making a duplicate of guest_can_login() as well.
I think it is strange, as tearDown should "reset" everything after each test. I'm out of ideas. And after reading the Cakephp Testing docs, I haven't been able to solve it.
Any suggestions to help me solve this is much appreciated.
Code below (gist if you prefer: https://gist.github.com/chris-andre/2eb3ad053073caf4f1c81722428a900b):
public $fixtures = [
'app.users',
'app.tenants',
'app.roles',
'app.roles_users',
];
public $Users;
public function setUp()
{
parent::setUp();
$config = TableRegistry::getTableLocator()->exists('Users') ? [] : ['className' => UsersTable::class];
$this->Users = TableRegistry::getTableLocator()->get('Users', $config);
}
/**
* tearDown method
*
* #return void
*/
public function tearDown()
{
unset($this->Users);
TableRegistry::clear();
parent::tearDown();
}
/** #test */
public function guest_can_register()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->configRequest([
'headers' => [
'host' => 'timbas.test'
]
]);
$data = [
'email' => 'chris#andre.com',
'first_name' => 'Christian',
'last_name' => 'Andreassen',
'password' => '123456',
'tenant' => ['name' => 'Test Company AS', 'domain' => 'testcomp', 'active' => true],
'active' => true
];
$this->post('/register', $data);
$this->assertResponseSuccess();
$this->assertRedirect(['controller' => 'Users', 'action' => 'login', '_host' => 'testcomp.timbas.test']);
$user = $this->Users->find()
->contain(['Tenants', 'Roles'])
->where(['Users.email' => 'chris#andre.com'])
->first();
}
/** #test */
public function first()
{
$users = $this->Users->find()->first();
pr($users->first_name);
}
/** #test */
public function second()
{
$users = $this->Users->find()->first();
pr($users->first_name);
}
/** #test */
public function guest_can_login()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->configRequest([
'headers' => [
'host' => 'testcomp.timbas.test'
]
]);
$data = [
'email' => 'chris#andre.com',
'first_name' => 'Christian',
'last_name' => 'Andreassen',
'password' => '123456',
'tenant' => ['name' => 'Test Company AS', 'domain' => 'testcomp', 'active' => true],
'active' => true,
'roles' => ['_ids' => [ADMINISTRATOR_ROLE_ID]]
];
$user = $this->Users->newEntity($data, [
'associated' => ['Tenants', 'Roles']
]);
$this->Users->save($user);
$getNewUser = $this->Users->find()
->contain(['Roles'])
->where(['Users.email' => 'chris#andre.com'])
->first()
->toArray();
// pr($getNewUser->id);
$this->post('/users/login', [
'email' => 'chris#andre.com',
'password' => '123456'
]);
$this->assertSession($getNewUser, 'Auth.User');
}
/** #test */
public function third()
{
$users = $this->Users->find()->first();
pr($users->first_name);
}
EDIT 2018-08-06:
Users::register() is a global context, I cannot be accessed from url with subdomain. E.g. tenant1.domain.com/register will throw a badRequest, while domain.com/register is a valid url. On registration success, user is forwarded to login from right url. Login-url = Tenants.domain + domain + suffix, e.g. tenant1.domain.com. When user is on the tenant scope (url with subdomain), the Tenants.id where Tenants.domain = tenant1, will be added to the where clause in all queries for the models having the behavior attached.
Now, what happens in third() is that the tenant_id from the newly created Tenant in guest_can_login() is added to the query, which means the test "is still on the tenant scope" when third() is run. That is the problem.
The other problem is that setUp() is called on all test methods but third(). testDown() is called on every test methods.
App\Middleware\TenantMiddleware.php:
use InstanceConfigTrait;
/**
* Default config.
* Options:
* - globalScope: tells the middleware what controller and action tenant scope is not being used
* Example
* 'globalScope' => [
* 'Pages' => ['*'], // All actions in PagesController is global
* 'Users' => ['register'] // Register action in UsersController is global
* ]
* #var array
*/
protected $_defaultConfig = [
'globalScope' => [
'Users' => ['register'],
'Landing' => ['*'],
'Pages' => ['*']
],
];
public function __construct($config = [])
{
if (!isset($config['primaryDomain'])) {
$config['primaryDomain'] = Configure::read('Site.domain');
}
$this->setConfig($config);
}
/**
* Invoke method.
*
* #param \Cake\Http\ServerRequest $request The request.
* #param \Psr\Http\Message\ResponseInterface $response The response.
* #param callable $next Callback to invoke the next middleware.
* #return \Psr\Http\Message\ResponseInterface A response
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
// Get subdomains
$subdomains = $request->subdomains();
// If subdomains not empty, the first is always the tenants domain
$subdomain = !empty($subdomains) ? $subdomains[0] : '';
Tenant::setDomain($subdomain);
// Get params of current request
$params = $request->getAttribute('params');
$controller = $params['controller'];
$action = $params['action'];
// Set tenantScope as default
Tenant::setScope('tenant');
$globalScope = $this->getConfig('globalScope');
// If Controller and action is a global scope
if (array_key_exists($controller, $globalScope)) {
if (in_array($action, $globalScope[$controller]) || in_array('*', $globalScope[$controller])) {
Tenant::setScope('global');
}
}
if (
(Tenant::getScope() === 'tenant' && Tenant::tenant() === null)
|| (Tenant::getDomain() === '' && Tenant::getScope() === 'tenant')
|| (Tenant::getDomain() !== '' && Tenant::getScope() === 'global')
) {
throw new NotFoundException('The page you are looking for does not exists.');
}
$primaryDomain = $this->getConfig('primaryDomain');
if (array_key_exists($controller, $globalScope)) {
if (in_array($action, $globalScope[$controller]) && Tenant::getScope() === 'global') {
}
}
return $next($request, $response);
}
App\Model\Behavior\TenantScopeBehavior.php:
protected $_table;
/**
* Default configuration.
*
* #var array
*/
protected $_defaultConfig = [];
public function __construct(Table $table, array $config = [])
{
parent::__construct($table, $config);
}
public function beforeFind(Event $event, Query $query, ArrayObject $options)
{
$model = $this->_table->getAlias();
$foreig_key = 'tenant_id';
if (!isset($options['skipTenantCheck']) || $options['skipTenantCheck'] !== true) {
if (Tenant::getScope() === 'tenant') {
if ($model === 'Tenants') {
$query->where(['Tenants.id' => Tenant::tenant()->id]);
} else {
$query->where([$model . '.' . $foreig_key => Tenant::tenant()->id]);
}
}
}
return $query;
}
public function beforeSave(Event $event, Entity $entity, $options)
{
if (Tenant::getScope() === 'tenant') {
if ($entity->isNew()) {
$entity->tenant_id = Tenant::tenant()->id;
} else {
// Check if current tenant is owner
if ($this->_table->getAlias() === 'Tenants') {
if ($entity->id != Tenant::tenant()->id) {
throw new BadRequestException();
}
} else {
if ($entity->tenant_id != Tenant::tenant()->id) {
throw new BadRequestException();
}
}
}
}
return true;
}
public function beforeDelete(Event $event, Entity $entity, $options)
{
if (Tenant::getScope() === 'tenant') {
if ($entity->tenant_id != Tenant::tenant()->id) { //current tenant is NOT owner
throw new BadRequestException();
}
}
return true;
}
App\Tenant\Tenant.php:
/**
* $_domain will be empty or comtain the domain (subdomain from url)
* #var string can be empty
*/
protected static $_domain;
/**
* $_scope shall be 'global' or 'tenant'.
* #var string
*/
protected static $_scope;
/**
* #var null|object \App\Model\Entity\Tenant
*/
protected static $_tenant;
/**
* Gets domain from $_domain and returns the string
* #return string
*/
public static function getDomain()
{
return self::$_domain;
}
/**
* Set the tenant scope domain. Will be set in the TenantMiddleware, and shall not be set anywhere else
* #param string $domain
* #return string empty or with domain
*/
public static function setDomain($domain)
{
self::$_domain = $domain;
}
/**
* Tenant method
* Return the object \App\Model\Table\Tenants ro null
* #return type
*/
public static function tenant()
{
$tenant = static::_getTenant();
return $tenant;
}
protected static function _getTenant()
{
if (self::$_tenant === null) {
$cachedTenants = Cache::read('tenants');
if($cachedTenants !== false) {
// do something
}
$tenantsTable = TableRegistry::get('Tenants');
$tenant = $tenantsTable->find('all', ['skipTenantCheck' => true])
->where(['Tenants.domain' => self::getDomain()])
->where(['Tenants.active' => true])
->first();
self::$_tenant = $tenant;
}
return self::$_tenant;
}
public static function getScope()
{
return self::$_scope;
}
/**
* Description
* #param type $scope
* #return type
*/
public static function setScope($scope)
{
self::$_scope = $scope;
}

Using session component in custom component

I'm trying to use Session component in custom component (CakePHP 2.3) but when I call Session component functions I get: Fatal error: Call to a member function read() on a non-object in ...\app\Controller\Component\CartComponent.php on line 7
My CartComponent looks like that:
<?php
App::uses('Component', 'Controller');
class CartComponent extends Component {
public $components = array('Session');
function hasItems() {
$cart = $this->Session->read('Cart');
return $cart != null && count($cart) > 0;
}
}
?>
And I use it in controller:
<?php
class OrdersController extends AppController {
public $name = 'Orders';
public $components = array('Cart', 'Email');
function beforeFilter() {
parent::beforeFilter();
if ($this->Cart->hasItems()) {
$this->Auth->allow('add_item', 'remove_item', 'cart');
} else {
$this->Auth->allow('add_item', 'remove_item', 'cart', 'make');
}
}
}
?>
For using session inside the custom component I tried with
public $components = array('Session');
and then called it by using
$this->Session->read('Cart');
but I cant able to use it and I start to use
CakeSession::read('Cart')
Now it works Hope it will used for you note I used in cake php version > 2
If you want to use Session in your Component Use-
$test = CakeSession::read('user');
print_r($test);
You should use as bellow
class YourComponent extends Component {
public function initialize(Controller $controller){
$this->controller = $controller;
if (!isset($this->controller->presetVars)) {
$this->controller->presetVars = true;
}
$model = $this->controller->modelClass;
if (!empty($settings['model'])) {
$model = $settings['model'];
}
if ($this->controller->presetVars === true) {
// auto-set the presetVars based on search definitions in model
$this->controller->presetVars = array();
$filterArgs = array();
if (!empty($this->controller->$model->filterArgs)) {
$filterArgs = $this->controller->$model->filterArgs;
}
foreach ($filterArgs as $key => $arg) {
if ($args = $this->_parseFromModel($arg, $key)) {
$this->controller->presetVars[] = $args;
}
}
}
foreach ($this->controller->presetVars as $key => $field) {
if ($field === true) {
if (isset($this->controller->$model->filterArgs[$key])) {
$field = $this->_parseFromModel($this->controller->$model->filterArgs[$key], $key);
} else {
$field = array('type' => 'value');
}
}
if (!isset($field['field'])) {
$field['field'] = $key;
}
$this->controller->presetVars[$key] = $field;
}
/* now you can use Component existing in your Component :) */
public function sayHello(){
$this->controller->Session->setFlash(__('Hello you'));
}
}

CakePHP REST Authorization issue on POST and PUT (I get 404)

I'm developing a REST api for a application, and everething went fine up until now...
I'm building a header with login data, GET and DELETE work fine but when I try to send a PUT or POST request I get 404...
When authorization is off (i.e., I do not check it in cake) everything works fine.
Here's the controller code:
class SitesController extends AppController {
var $uses = array("Site");
var $name = 'Sites';
var $scaffold;
var $components = array('RequestHandler','Security');
function beforeFilter() {
$this->Security->loginOptions = array(
'type'=>'basic'
);
$this->Security->loginUsers = array(
'lukasz'=>'blabla',
'test'=>'test'
);
$this->Security->requireLogin();
}
function index() {
$sites = $this->Site->find('all');
$this->set(compact('sites'));
}
function view($id) {
$site = $this->Site->findById($id);
$this->set(compact('site'));
}
function add() {
if($this->data != null) {
$this->Site->create();
if($this->Site->save($this->data)) {
$message = array('Deleted');
} else {
$message = $this->data;
}
$this->set(compact("message"));
}
}
function edit($id) {
$this->Site->id = $id;
if ($this->Site->save($this->data)) {
$message = array('Saved');
} else {
$message = array('Error');
}
$this->set(compact("message"));
}
function delete($id) {
if($this->Site->delete($id)) {
$message = array('Deleted');
} else {
$message = array('Error');
}
$this->set(compact("message"));
}
}
And here's how I send requests:
http://bin.cakephp.org/view/165115685
http://bin.cakephp.org/view/1477117088
I suspect you're running into the CSRF protection (form spoofing protection) the SecurityComponent applies to all POST and PUT requests. Try turning it off using the $validatePost option.

Resources