I have two project in yii framework and I want to use both project using SimpleSAMLphp with SSO. The condition, I need is if I login from the first project, i want access to the second project.
Thank you in advance.
First you load the SAML library by temporarily disabling the Yii autoloader. This is just to let you use the SAML classes and methods:
<?php
class YiiSAML extends CComponent {
private $_yiiSAML = null;
static private function pre() {
require_once (Yii::app()->params['simpleSAML'] . '/lib/_autoload.php');
// temporary disable Yii autoloader
spl_autoload_unregister(array(
'YiiBase',
'autoload'
));
}
static private function post() {
// enable Yii autoloader
spl_autoload_register(array(
'YiiBase',
'autoload'
));
}
public function __construct() {
self::pre();
//We select our authentication source:
$this->_yiiSAML = new SimpleSAML_Auth_Simple(Yii::app()->params['authSource']);
self::post();
}
static public function loggedOut($param, $stage) {
self::pre();
$state = SimpleSAML_Auth_State::loadState($param, $stage);
self::post();
if (isset($state['saml:sp:LogoutStatus'])) {
$ls = $state['saml:sp:LogoutStatus']; /* Only for SAML SP */
} else return true;
return $ls['Code'] === 'urn:oasis:names:tc:SAML:2.0:status:Success' && !isset($ls['SubCode']);
}
public function __call($method, $args) {
$params = (is_array($args) and !empty($args)) ? $args[0] : $args;
if (method_exists($this->_yiiSAML, $method)) return $this->_yiiSAML->$method($params);
else throw new YiiSAMLException(Yii::t('app', 'The method {method} does not exist in the SAML class', array(
'{method}' => $method
)));
}
}
class YiiSAMLException extends CException {
}
Then you define a filter extending the CFilter Yii class:
<?php
Yii::import('lib.YiiSAML');
class SAMLControl extends CFilter {
protected function preFilter($filterChain) {
$msg = Yii::t('yii', 'You are not authorized to perform this action.');
$saml = new YiiSAML();
if (Yii::app()->user->isGuest) {
Yii::app()->user->loginRequired();
return false;
} else {
$saml_attributes = $saml->getAttributes();
if (!$saml->isAuthenticated() or Yii::app()->user->id != $saml_attributes['User.id'][0]) {
Yii::app()->user->logout();
Yii::app()->user->loginRequired();
return false;
}
return true;
}
}
}
And finally, in the controllers you are interested to restrict, you override the filters() method:
public function filters() {
return array(
array(
'lib.SAMLControl'
) , // perform access control for CRUD operations
...
);
}
Hope it helps.
It can be done simply using "vendors" directory.
Download PHP Library from https://simplesamlphp.org/
Implement it in Yii Framework as a vendor library. (http://www.yiiframework.com/doc/guide/1.1/en/extension.integration)
Good Luck :)
I came across an Yii Extension for SimpleSAMLphp in github
https://github.com/asasmoyo/yii-simplesamlphp
You can load the simplesamlphp as a vendor library and then specify the autoload file in the extension.
Apart from the extension you can copy all the necessary configs and metadatas into the application and configure SimpleSAML Configuration to load the configurations from your directory, so you can keep the vendor package untouched for future updates.
Related
I would like to disable the contact form in my prestashop installation but there is no plugin to do so. Any suggestions how to do that?
Depends what you mean by disabling contact form but here are few possibilities.
Modifying core contact controller (not recommended since you will lose custom code when updating Prestashop)
Open file controllers/front/ContactController.php and add this code inside the ContactControllerCode class.
public function init()
{
Tools::redirect('pagenotfound'); // redirect contact page to 404 page
}
Overriding contact controller
Create a new file ContactController.php and place it in folder overrides/controllers/front/ and add the following code
class ContactController extends ContactControllerCore {
public function init()
{
Tools::redirect('pagenotfound'); // redirect contact page to 404 page
}
}
Create a small module
Create a new directory contactpagedisabler in folder modules and inside create a file contactpagedisabler.php and put this code in
class ContactPageDisabler extends Module
{
public function __construct()
{
$this->name = 'contactpagedisabler';
$this->tab = 'front_office_features';
$this->version = '1.0';
$this->author = 'whatever';
parent::__construct();
$this->displayName = $this->l('Contact page disabler');
$this->description = $this->l('Disables contact page.');
}
public function install()
{
return parent::install() && $this->registerHook('actionDispatcher');
}
// hook runs just after controller has been instantiated
public function hookActionDispatcher($params)
{
if ($params['controller_type'] === 1 && $params['controller_class'] === 'ContactController') {
Tools::redirect('pagenotfound'); // redirect contact page to 404 page
}
}
}
And then install this module from backoffice.
2nd option is simplest and it doesn't interfere with core files.
3rd option is probably overkill for such a small thing however it doesn't require overriding and if you or store manager ever needs the contact page back he can just disable the module from backoffice.
The module could also be expanded/modified with configuration page where you could for example get a list of all pages in store and let user decide which ones to enable/disable etc.
Update April 2018
Forget first two options and use third. Always use a module (if possible) when modifying your shop.
If You want to block just contact form but You want to display contact page You can put in override\controllers\front\ContactController.php:
<?php
class ContactController extends ContactControllerCore
{
public function postProcess()
{
if (Tools::isSubmit('submitMessage'))
{die('Form disabled');}
else
parent::postProcess();
//return null;
}
}
This will disable ability to send mails.
Then You can cut contact form from theme: /themes/YOUR-THEME/contact-form.tpl
to not display contact form at all
After this You have to delete file /cache/class_index.php to refresh classes in prestashop.
Barto's solution can also be achieved without an override.
Create another module contactformdisabler
class ContactFormDisabler extends Module
{
public function __construct()
{
$this->name = 'contactformdisabler';
$this->tab = 'front_office_features';
$this->version = '1.0';
$this->author = 'whatever';
parent::__construct();
$this->displayName = $this->l('Contact form disabler');
$this->description = $this->l('Disables contact form submission.');
}
public function install()
{
return parent::install() && $this->registerHook('actionDispatcher');
}
public function hookActionDispatcher($params)
{
if ($params['controller_type'] === 1
&& $params['controller_class'] === 'ContactController'
&& Tools::isSubmit('submitMessage')) {
die('Contact form submission disabled');
}
}
}
How to load a template file from my admin controller in custom module in prestashop 1.6
if (!defined('_PS_VERSION_')) exit;
class QueryAllTrxController extends ModuleAdminController
{
public $module;
public function __construct()
{
parent::__construct();
}
public function initContent()
{
parent::initContent();
$this->setTemplate('display.tpl');
//$this->setTemplate(_PS_THEME_DIR_.'mypage.tpl');
}
}
I had the same problem and it took forever to figure out.
I ended up spotting the solution in this video : https://www.youtube.com/watch?v=CdnJpLqqvcM
Any this is how I got it to work :
1 - Create the controller in ModuleName/controllers/AdminMyControllerNameController.php
class AdminMyControllerNameController extends ModuleAdminController
{
public function __construct()
{
$this->display = 'view';
$this->meta_title = $this->l('metatitle');
$this->toolbar_title = $this->l('tollbartitle');
parent::__construct();
}
public function initContent()
{
$this->show_toolbar = true;
$this->display = 'view';
$this->meta_title = $this->l('META TITLE');
parent::initContent();
$this->setTemplate('templatename.tpl');
}
public function initToolBarTitle()
{
$this->toolbar_title = $this->l('TOOLBAR TITLE??');
}
public function initToolBar()
{
return true;
}
}
2 - Create the template file in ModuleName/views/admin/my_controller_name/template.tpl
You have to create a directory in the views/admin folder using the name of your controller written in snake case.
Anyway I hope this will help.
WORKING CODE HERE
Background:
You want to add a custom admin page with a custom module controller. But you cannot customize template because you're stuck with this error message:
Fatal error: Uncaught --> Smarty: Unable to load template file '/var/www/html/admin-dev/themes/default/template/catalog/index.tpl' <-- thrown in /var/www/html/tools/smarty/sysplugins/smarty_internal_templatebase.php on line 129
Your current source code is:
class AdminYourModuleNameProductsController extends ModuleAdminController {
public function initContent() {
parent::initContent();
// enable these lines if you're stuck with damn stupid blank page with just 500 error
// ini_set('display_errors', '1');
// ini_set('display_startup_errors', '1');
// error_reporting(E_ALL);
$this->setTemplate('products/index.tpl');
}
}
And you don't know what to do because PrestaShop dev doc is the worst document in the history of ecommerce platform developer document and moreover its forum is full of chitchats and junks.
Solution
Place index.tpl at
{%PRESTA_ROOT%}/modules/{%YOUR MODULE DIR%}/views/templates/admin/{% snake case version of controller %}/products/index.tpl
For example, if your module name is yourmodulename and the controller name is AdminYourModuleNameProductsController (as in the example), the correct path is:
{%PRESTA_ROOT%}/modules/yourmodulename/views/templates/admin/your_module_name_products/products/index.tpl
If the error still persists:
Check this line:
{%PRESTA_ROOT%}/classes/controller/ModuleAdminController.php
public function createTemplate($tpl_name)
{
if (file_exists(_PS_THEME_DIR_.'modules/'.$this->module->name.'/views/templates/admin/'.$tpl_name) && $this->viewAccess()) {
// echo the following line and exit
return $this->context->smarty->createTemplate(_PS_THEME_DIR_.'modules/'.$this->module->name.'/views/templates/admin/'.$tpl_name, $this->context->smarty);
} elseif (file_exists($this->getTemplatePath().$this->override_folder.$tpl_name) && $this->viewAccess()) {
// echo the following line and exit
return $this->context->smarty->createTemplate($this->getTemplatePath().$this->override_folder.$tpl_name, $this->context->smarty);
}
// the error occurs because php get reach to the following line:
return parent::createTemplate($tpl_name);
}
Do as I commented and you can get the correct file path. Make sure the file exists in the path.
My PrestaShop version is 1.6.1.24
The code $this->setTemplate('display.tpl'); is loading a template file modules/your-custom-module/views/templates/admin/display.tpl or modules/your-custom-module/display.tpl.
The classname must be named that way: AdminQueryAllTrxController
you can put display.tpl in :
modules\module_name\views\templates\admin\classe_name(QueryAllTrx)
and use :$this->setTemplate('display.tpl'); in your AdminQueryAllTrxController
First of all add controller to your module:
modules\module_name\controllers\admin\SomeNameController.php
and extend it by ModuleAdminController, you need at least two methods for it to work properly __construct and initContent
put the following code to the later method:
$this->content .= $this->context->smarty->fetch($this->pathToTpl);
$this->context->smarty->assign(array(
'content' => $this->content,
));
You could replace $this->pathToTpl with any path which is pointed to your tpl file, I'm prefer to create the path dynamically. You can see a simple example here:
class SomeNameController extends ModuleAdminController{
var $pathToTpl;
public function __construct()
{
$this->bootstrap = true;
$this->context = Context::getContext();
$this->pathToTpl = _PS_MODULE_DIR_ .
$this->module->name . // put the name of module
'/views/templates/admin' .
'/' .
'templateName.tpl';
parent::__construct();
}
public function initContent()
{
parent::initContent();
$this->content .= $this->context->smarty->fetch($this->pathToTpl);
$this->context->smarty->assign(array(
'content' => $this->content,
));
}
}
finally you need to place templateName.tpl in the path you wanted to be:
modules\module_name\views\templates\admin\templateName.tpl
From what I saw in the source code RequiresAuthentication() does an Authentication check for the whole module. Is there any way to do this per Route?
I had the same problem. However it turns out the RequiresAuthentication works at both the module level and the route level. To demonstrate, here is some code ripped out my current project (not all routes shown for brevity).
public class RegisterModule : _BaseModule
{
public RegisterModule() : base("/register")
{
Get["/basic-details"] = _ => View["RegisterBasicDetailsView", Model];
Get["/select"] = _ =>
{
this.RequiresAuthentication();
return View["RegisterSelectView", Model];
};
}
}
Of course the only problem with doing it this way is that all the protected routes in the module need to call RequiresAuthentication. In the case of my module above, I have another 5 routes (not shown) all of which need protecting, so that makes six calls to RequiresAuthentication instead of one at the module level. The alternative would be to pull the unprotected route into another module, but my judgement was that a proliferation of modules is worse than the additional RequiresAuthentication calls.
namespace Kallist.Modules {
#region Namespaces
using System;
using Nancy;
#endregion
public static class ModuleExtensions {
#region Methods
public static Response WithAuthentication(this NancyModule module, Func<Response> executeAuthenticated) {
if ((module.Context.CurrentUser != null) && !string.IsNullOrWhiteSpace(module.Context.CurrentUser.UserName)) {
return executeAuthenticated();
}
return new Response { StatusCode = HttpStatusCode.Unauthorized };
}
#endregion
}
}
I ran into the same issue, here's how I solved it.
var module = new MyModule();
module.AddBeforeHookOrExecute(context => null, "Requires Authentication");
_browser = new Browser(with =>
{
with.Module(module);
with.RequestStartup((container, pipelines, ctx) =>
{
ctx.CurrentUser = new User { UserId = "1234", UserName = "test"};
});
});
I can now use this.RequiresAuthentication() at the module level and run my unit tests.
I am working on a project which includes a REST API component. I have a controller dedicated to handling all of the REST API calls.
Is there any way to catch all exceptions for that specific controller so that I can take a different action for those exceptions than the rest of the application's controllers?
IE: I'd like to respond with either an XML/JSON formatted API response that contains the exception message, rather than the default system view/stack trace (which isn't really useful in an API context). Would prefer not having to wrap every method call in the controller in its own try/catch.
Thanks for any advice in advance.
You can completely bypass Yii's default error displaying mechanism by registering onError and onException event listeners.
Example:
class ApiController extends CController
{
public function init()
{
parent::init();
Yii::app()->attachEventHandler('onError',array($this,'handleError'));
Yii::app()->attachEventHandler('onException',array($this,'handleError'));
}
public function handleError(CEvent $event)
{
if ($event instanceof CExceptionEvent)
{
// handle exception
// ...
}
elseif($event instanceof CErrorEvent)
{
// handle error
// ...
}
$event->handled = TRUE;
}
// ...
}
I wasn't able to attach events in controller, and I did it by redefinition CWebApplication class:
class WebApplication extends CWebApplication
{
protected function init()
{
parent::init();
Yii::app()->attachEventHandler('onError',array($this, 'handleApiError'));
Yii::app()->attachEventHandler('onException',array($this, 'handleApiError'));
}
/**
* Error handler
* #param CEvent $event
*/
public function handleApiError(CEvent $event)
{
$statusCode = 500;
if($event instanceof CExceptionEvent)
{
$statusCode = $event->exception->statusCode;
$body = array(
'code' => $event->exception->getCode(),
'message' => $event->exception->getMessage(),
'file' => YII_DEBUG ? $event->exception->getFile() : '*',
'line' => YII_DEBUG ? $event->exception->getLine() : '*'
);
}
else
{
$body = array(
'code' => $event->code,
'message' => $event->message,
'file' => YII_DEBUG ? $event->file : '*',
'line' => YII_DEBUG ? $event->line : '*'
);
}
$event->handled = true;
ApiHelper::instance()->sendResponse($statusCode, $body);
}
}
In index.php:
require_once(dirname(__FILE__) . '/protected/components/WebApplication.php');
Yii::createApplication('WebApplication', $config)->run();
You can write your own actionError() function per controller. There are several ways of doing that described here
I'm using the following Base controller for an API, it's not stateless API, mind you, but it can serve just aswell.
class BaseJSONController extends CController{
public $data = array();
public $layout;
public function filters()
{
return array('mainLoop');
}
/**
* it all starts here
* #param unknown_type $filterChain
*/
public function filterMainLoop($filterChain){
$this->data['Success'] = true;
$this->data['ReturnMessage'] = "";
$this->data['ReturnCode'] = 0;
try{
$filterChain->run();
}catch (Exception $e){
$this->data['Success'] = false;
$this->data['ReturnMessage'] = $e->getMessage();
$this->data['ReturnCode'] = $e->getCode();
}
echo json_encode($this->data);
}
}
You could also catch dbException and email those, as they're somewhat critical and can show underlying problem in the code/db design.
Add this to your controller:
Yii::app()->setComponents(array(
'errorHandler'=>array(
'errorAction'=>'error/error'
)
));
I am using PHP Yii Framework with MongoDB(yiimongodbsuite). I have created a Model which extends from EMongoDocument.
<?php
class MyModel extends EMongoDocument
{
public $attr1;
public $attr2;
// rules, custom validations and other functions....
public function setAttributes($values, $safeOnly=true)
{
if(!is_array($values))
return;
if($this->hasEmbeddedDocuments())
{
$attributes=array_flip($safeOnly ? $this->getSafeAttributeNames() : $this->attributeNames());
foreach($this->embeddedDocuments() as $fieldName => $className)
if(isset($values[$fieldName]) && isset($attributes[$fieldName]))
{
$this->$fieldName->setAttributes($values[$fieldName], $safeOnly);
unset($values[$fieldName]);
}
}
parent::setAttributes($values, $safeOnly);
}
}
In Controller,
$dataModel = new MyModel();
$dataModel->setAttributes($_POST['MyModel']);
if($dataModel->validate()){
$dataModel->save();
}
the above code is not setting the attribute value.
Please let me know if there is any mistake.
You need to make sure that the 'safe' validation rules is used on each level.
To understand more read this http://www.yiiframework.com/wiki/161/understanding-safe-validation-rules/
Try to determine which valdation errors you have:
if(!$model->validate()) {
die( print_r($model->getErrors()) );
}