i need to setup my silex firewall like:
www.mysite.com/* => access to all users
www.mysite.com/admin/* => access to only logged in users
i use this set up but it does not work as expected:
$app->register(new SecurityServiceProvider(), array(
'security.firewalls' => array(
'secure' => [
'pattern' => '^/.*$',
'anonymous' => true,
'form' => array(
'login_path' => '/admin/login',
'check_path' => '/admin/auth'
),
'logout' => array(
'logout_path' => '/admin/logout'
),
'users' => $app->share(function() use ($app) {
return new AuthenticationSuccessHandler($app['db']);
}),
]
),
'security.access_rules' => array(
array('^/admin$', 'ROLE_ADMIN')
)
));
Any help?
Many Thanks!! ;-)
'users' => $app->share(function() use ($app) {
return new AuthenticationSuccessHandler($app['db']);
}),
The above function needs to return an object which implements
Symfony\Component\Security\Core\User\UserProviderInterface
Check here for custom user provider documentation
It may also be appropriate to move login_path outside the secured area. Another way of configuring would be:
$app['security.firewalls'] = array(
'secure' => array(
'pattern' => '^/admin/',
'form' => array('login_path' => '/login', 'check_path' => '/admin/auth'),
'users' => $app->share(function () use ($app) {
return new MyUserProvider($app['db']);
}),
),
),
);
$app['security.access_rules'] = array(
array('^/admin', 'ROLE_ADMIN')
);
Make sure you register doctrine dbal.
I'm working for the first time with Silex's Security Provider and I'm having issues with the process. I currently have the basic HTTP auth working (using the coded example user as shown here in the docs).
When switching HTTP out for the form option however the login form is submitting, and returning to itself. I have created a UserProvider class and the loadUserByUsername method is being successfully called, however the email isn't being passed in (being set to "NONE_PROVIDED" - altered from username). This I found when working through the vendor code is because the token isn't being set ($app['security']->getToken() returning null at all points). I've trawled through all the docs I can but I can't find any mention of this.
The main code is included below, let me know if there is anything else, thanks!
Security Provider Configuration
// Protects all routes within /auth, redirecting to /login successfully
$app->register(new SecurityServiceProvider(), array(
'security.firewalls' => array(
'unauth_area' => array(
'pattern' => '^/(?!auth)'
),
'auth_area' => array(
'pattern' => '^/.*$',
'form' => array(
'login_path' => '/login',
'check_path' => '/auth/login_check',
'default_target_path' => '/auth/overview',
),
'users' => $app->share(function () use ($app) {
return new UserProvider($app['db']);
}),
),
),
'access_control' => array(
array('path' => '^/.*$', 'role' => 'ROLE_USER'),
// Include the following line to also secure the /admin path itself
// array('path' => '^/admin$', 'role' => 'ROLE_ADMIN'),
),
));
(My Custom) method - UserProvider class
public function loadUserByUsername($email) {
// Dying at this point shows it reaches here, but $email is null
$stmt = $this->conn->executeQuery('SELECT * FROM user WHERE email = ?', array(strtolower($email)));
if (!$user = $stmt->fetch()) {
throw new UsernameNotFoundException(sprintf('Email "%s" does not exist.', $email));
}
return new User($user['email'], $user['password'], explode(',', $user['roles']), true, true, true, true);
}
Form Class
class LoginType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('_username', 'text', array(
'required' => true,
'constraints' => array(
new Assert\NotBlank(),
new Assert\Email(),
)
))
->add('_password', 'password', array(
'required' => true,
'constraints' => array(
new Assert\NotBlank(),
),
))
->add('Login', 'submit');
}
public function getName() {
return 'login';
}
}
Silex Security Provider docs
It has nothing to do with the token… I just had the same problem with
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'admin' => array(
'pattern' => '^/admin',
'form' => array(
'login_path' => '/',
'check_path' => '/admin/login_check',
'username_parameter'=> 'mail',
'password_parameter' => 'password',
),
'logout' => array('logout_path' => '/logout'),
//'anonymous' => true,
'users' => function () use ($app) {
return new UserProvider($app['db']);
},
)
),
'security.access_rules' => array(
array('^/$', 'IS_AUTHENTICATED_ANONYMOUSLY'),
array('^/admin', 'ROLE_USER')
)
));
After a couple hours trying and testing, I checked the name attribute in my form's input… Saw form[mail]
So I tried
'username_parameter'=> 'form[mail]',
'password_parameter' => 'form[password]',
And … ALLELUIA!!!!! had my mail in loadUserByUsername($mail)
I'm using this function in Bootstrap.php to cache my controllers , and i need to cache some controllers only I don't need to cache index controller and article controller as an example
and i need to cache question controller but it does not working . this function is cached all my controllers
protected function _initCache()
{
mb_internal_encoding("UTF-8");
$dir = "/var/www/data/cache/all";
$frontendOptions = array(
'lifetime' => 3600,
'content_type_memorization' => true,
'default_options' => array(
'cache' => true,
'cache_with_get_variables' => true,
'cache_with_post_variables' => true,
'cache_with_session_variables' => true,
'cache_with_cookie_variables' => true,
),
'regexps' => array(
'^/$' => array('cache' => false),
"^/question/" => array('cache' => true),
"^/article/" => array('cache' => false),
)
);
$backendOptions = array(
'cache_dir' =>$dir
);
// getting a Zend_Cache_Frontend_Page object
$cache = Zend_Cache::factory('Page',
'File',
$frontendOptions,
$backendOptions);
$cache->start();
}
so what i can do I tried all soluations please help me.
Thanks
Create a controller plugin with that code, and detect the request.
Something like...
if($request->getControllerName() == 'index' || ... == 'article') {
return;
}
mb_internal_encoding("UTF-8");
...
I am working on a website using the Lithium framework for PHP, and I need to have two sub-directories (ie. for admin and blog) with my controllers and views:
-controllers
-admin
HomeController.php
...
-blog
HomeController.php
...
HomeController.php
...
-views
-admin
-home
index.html.php
...
...
-blog
-home
index.html.php
...
...
-layouts
default.html.php
admin.html.php
blog.html.php
So far, I have discovered the way to allow the use of sub-domains in the controller using the Dispach::config() method:
Dispatcher::config(array('rules' => array(
'admin' => array('controller' => 'app\controllers\admin\{:controller}Controller'),
'blog' => array('controller' => 'app\controllers\blog\{:controller}Controller'),
)));
This works when you use the following routing:
$options = array('continue' => true);
Router::connect('/admin', array(
'admin' => true,
'controller' => 'Home'
), $options);
Router::connect('/admin/{:args}', array(
'admin' => true
), $options);
Router::connect('/blog', array(
'blog' => true,
'controller' => 'Home'
), $options);
Router::connect('/blog/{:args}', array(
'blog' => true
), $options);
Now the problem I am having is that I can't figure out how to set it to automatically use the admin/blog template and admin/blog view folders.
You could override default templates paths thanks to Media.
The filter above sets different paths for admin requests (in config/bootstrap/media.php).
Dispatcher::applyFilter('_callable', function($self, $params, $chain) {
$next = $chain->next($self, $params, $chain);
if ($params['request']->admin) {
Media::type('default', null, array(
'view' => 'lithium\template\View',
'paths' => array(
'layout' => '{:library}/views/layouts/{:layout}.{:type}.php',
'template' => '{:library}/views/admin/{:controller}/{:template}.{:type}.php'
)
));
}
return $next;
});
I have a Yii project with a main.php config file and dev.php config file which "inherits" from it. The files are as follows:
main.php:
<?php
// uncomment the following to define a path alias
// Yii::setPathOfAlias('local','path/to/local-folder');
// This is the main Web application configuration. Any writable
// CWebApplication properties can be configured here.
return array(
'basePath' => dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..',
'name' => 'FeedStreem',
// preloading 'log' component
'preload' => array( 'log' ),
// autoloading model and component classes
'import' => array(
'application.models.*',
'application.components.*',
'application.controllers.*',
),
// application components
'components' => array(
'db' => array(
'connectionString' => 'mysql:host=remote.host.com;dbname=dbnamehere',
'emulatePrepare' => true,
'username' => 'username',
'password' => 'password',
'charset' => 'utf8',
/*'enableProfiling' => true*/
),
'user' => array(
// enable cookie-based authentication
'allowAutoLogin' => true,
),
'authManager' => array(
'class' => 'CDbAuthManager',
'connectionID' => 'db'
),
'urlManager' => array(
// omitted
),
'errorHandler' => array(
// use 'site/error' action to display errors
'errorAction' => 'site/error',
),
'log' => array(
'class' => 'CLogRouter',
'routes' => array(
array(
'class' => 'CFileLogRoute',
'levels' => 'trace, info, error, warning',
),
),
),
),
// application-level parameters that can be accessed
// using Yii::app()->params['paramName']
'params' => array(
// this is used in contact page
'adminEmail' => 'webmaster#example.com',
),
);
dev.php:
<?php
return CMap::mergeArray(
require(dirname( __FILE__ ) . '/main.php'),
array(
'modules' => array(
'gii' => array(
'class' => 'system.gii.GiiModule',
'password' => 'SECRET',
// If removed, Gii defaults to localhost only. Edit carefully to taste.
'ipFilters' => array( '127.0.0.1', '::1' ),
),
),
'components' => array(
'db' => array(
'connectionString' => 'mysql:host=localhost;dbname=dbname2',
'emulatePrepare' => true,
'username' => 'username2',
'password' => 'password2',
'charset' => 'utf8',
),
'log' => array(
'class' => 'CLogRouter',
'routes' => array(
array(
'class' => 'CFileLogRoute',
'levels' => 'trace, info, error, warning',
),
// uncomment the following to show log messages on web pages
array(
'class' => 'CWebLogRoute',
),
),
),
),
)
);
However, when I use dev.php locally, I get the following error:
Warning: PDO::__construct() [pdo.--construct]: [2002] No connection could be made because the target machine actively (trying to connect via tcp://remote.host.com:3306) in C:\web_workspace\lib\yii\framework\db\CDbConnection.php on line 405
Which tells me the dev.php did not overwrite that 'db' config option. How can I make a config file that inherits from main.php but can overwrite options when I merge it?
As far as i see from source code it should overwrite your config:
public static function mergeArray($a,$b)
{
foreach($b as $k=>$v)
{
if(is_integer($k))
isset($a[$k]) ? $a[]=$v : $a[$k]=$v;
else if(is_array($v) && isset($a[$k]) && is_array($a[$k]))
$a[$k]=self::mergeArray($a[$k],$v);
else
$a[$k]=$v;
}
return $a;
}
Source: http://code.google.com/p/yii/source/browse/tags/1.1.8/framework/collections/CMap.php
Also official documentation says so:
If each array has an element with the same string key value, the latter will overwrite the former (different from array_merge_recursive).
Source: http://www.yiiframework.com/doc/api/1.1/CMap#mergeArray-detail
Try to determine the problem via print_r the result array and look at it inner structure. I think the problem is here.
If your project involves (or will involve) more than 1 developer, server, or customized deployment/test you have to watch out for VCS problems. For us we found it best to import a separate db.php file in config/main.php:
'db'=>require(dirname(__FILE__).'/db.php'),
The db.php file is a copy of (or symbolic link to) either db-test.php or db-deploy.php and is ignored by our VCS, while the various db-*.php files created for individual users continue to be tracked by the VCS:
bzr add protected/config/db-*.php
bzr ignore protected/config/db.php
This allows individual developers to run the system on their home machine connected to localhost databases while maintaining the deployed system's link to the central db.
I apologize if Yii has evolved much since I tested it out, but as I recall, you need to kind of work around this issue. Here is a link which outlines a few methods of doing so. It may be worth checking around to see if things have improved.
Blog article on environment dependent configuration
If you work with a dev, staging, and production environment which are all separate and maintained via git/svn/something else, I find it's easiest to ignore certain config files with frameworks that ignore environment settings. You have to make changes manually across these files at times, but this isn't a hardship as config files tend to remain similar once an app or website is established. This way you can tailor your settings for your environment for better debugging and testing on dev/staging, and otherwise better performance and no debugging on production.
How are you determining which config file to use? With a switch statement like Steve linked to?
I use an if statement in my index.php to decide which config file to use based on the server environment (as mentioned in the article Steve linked). It seems to work fine for me.
Also remember that if you are running a console application, you need to tell it to use the right conig file in protected/yiic.php also (just like in index.php).
Another thing that may be happening is CMap::mergeArray might not be merging like you want. Again, it works for me, but perhaps when the arrays are merged it is overwriting the wrong DB config string (it's choosing the wrong one)?
One way to fix this would be to NOT have the DB creds in the main.php config file, and just have them in each inheriting file. So you will need an inheriting file for each environment, and no environment will run directly off of the main.php file. This way when the arrays are merged you will always have the right DB connection string.
Good luck!
I have 2 configuration files (development and production). May be you can try my configuration.
main.php
return array(
'name' => 'My Application',
'language' => 'id',
'charset' => 'utf-8',
'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',
// gii module
'modules' => array(
'gii' => array(
'class' => 'system.gii.GiiModule',
'password' => 'admin'
),
),
// autoloading model and component classes
'import'=>array(
'application.models.*',
'application.components.*',
),
// preloading 'log' component
'preload' => array('log'),
'defaultController' => 'site',
// application components
'components' => array(
'user' => array(
// enable cookie-based authentication
'allowAutoLogin' => true,
'loginUrl' => array('user/login'),
),
'errorHandler' => array(
// use 'site/error' action to display errors
'errorAction' => 'site/error',
),
),
// application-level parameters that can be accessed
// using Yii::app()->params['paramName']
'params'=>require('params.php'),
);
you can customize how to show the error log for your development and production server
development.php // you can customize the packages imported for development and production
return CMap::mergeArray(
array(
'components'=>array(
'db'=>array(
'connectionString' => 'mysql:host=localhost;dbname=mydb',
'emulatePrepare' => true,
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'tablePrefix' => '',
),
'log' => array(
'class' => 'CLogRouter',
'routes' => array(
array(
'class' => 'CFileLogRoute',
'levels' => 'error, warning',
),
array(
'class' => 'CWebLogRoute',
),
array(
'class' => 'CDbLogRoute',
'levels' => 'trace, info, warning, error',
'connectionID' => 'db',
'autoCreateLogTable' => true,
),
),
),
),
),
require('main.php')
);
production.php
return CMap::mergeArray(
array(
'components'=>array(
'db'=>array(
'connectionString' => 'mysql:host=myserver.com;dbname=mydb',
'emulatePrepare' => true,
'username' => 'root',
'password' => 'mypassword',
'charset' => 'utf8',
'tablePrefix' => '',
),
'log' => array(
'class' => 'CLogRouter',
'routes' => array(
array(
'class' => 'CFileLogRoute',
'levels' => 'error, warning',
),
array(
'class' => 'CDbLogRoute',
'levels' => 'trace, info, warning, error',
'connectionID' => 'db',
'autoCreateLogTable' => true,
),
),
),
),
),
require('main.php')
);
just run the development or production configuration rather than main config
My problem was I was actually loading "index.php" when I wanted "index-dev.php".
My .htaccess redirect wasn't working for "localhost/" but it was working for "localhost/page".
I've got it working now by typing "localhost/index-dev.php
If you're using Yii you can also use the CMap::mergeArray function which does what the longer version of the accepted answer does already.
The last part of my index file looks like this:
$configMain = require_once(dirname(__FILE__).DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'main.php');
$configServer = require_once( dirname( __FILE__ ).DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'main' . $env . '.php' );
require_once($yii);
$config = CMap::mergeArray( $configMain, $configServer );
// Set Path alias for all javascript scripts
Yii::setPathOfAlias('js', dirname($_SERVER['SCRIPT_NAME']).DIRECTORY_SEPARATOR."js/");
Yii::createWebApplication($config)->run();
The part left out is at the top where I determine if I'm local or on the server.