I'm facing a problem where I've configured RBAC in Yii 2.0 but it does not work - meaning it dooes not prevent any of the pages from being loaded - even as guest.
This is in my web.php config (also in my console.php):
'authManager' => [
'class' => 'yii\rbac\DbManager',
],
The migrations have completed successfully.
This is how behaviors() look like at the moment, but I tried many different ways.
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'actions' => ['error'],
'allow' => true,
//'roles' => ["?"],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
];
}
If I implement the behaviors() function in my controller, the framework starts doing some access-handling, but the goal of using a DB as I understand should be that the RBAC system takes over this responsibility - meaning I don't have to enable/disable every single action I write for every single role.
I have added a Role "Admin" and assigned a few of the available routes (actions) to it.
Then I assigned this role to my User name. In theory this should enable my login to access those specific routes but nothing else - instead, I can traverse the site however I please, no 403s whatsoever. (This is why I'm saying RBAC acts like it's non-existing.)
Any hints or tips are appreciated.
Thanks.
where is your authManager configuration located?
According to [yii2 guide]
If you are using yii2-basic-app template, there is a config/console.php configuration file where the authManager needs to be declared additionally to config/web.php. In case of yii2-advanced-app the authManager should be declared only once in common/config/main.php.
Update to this question, I just tried do rbac manually
My result
We must do conditional in every action like
...
public function actionAbout()
{
if (Yii::$app->user->can('ViewAbout')) {
echo "you may see view about";
} else {
echo "view about is prohibited";
}
// return $this->render('about');
}
...
If you want assign it in common way, you better use extension/module that handle authmanager (like yii2-admin, yii2-mimin, etc)
Hope this answer help
Related
My Folder and code strucure is -
api/
modules/
v1/
controllers/
UserController.php
BaseController.php
Module.php
v2/
controllers/
UserController.php
BaseController.php
Module.php
And my application configuration would look like:
'modules' => [
'v1' => [
'basePath' => '#app/modules/v1',
'class' => 'api\modules\v1\Module'
],
'v2' => [
'basePath' => '#app/modules/v2',
'class' => 'api\modules\v2\Module'
],
],
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => false,
'showScriptName' => false,
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user']],
['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user']],
],
]
I am following the same procedue as given on yii2 doc But its versioning not working .
UPDATE :
I have created a costume rule and parse according that . Looking for something else .
class ApiUrlRule implements UrlRuleInterface {
public function parseRequest($manager, $request) {
$pathInfo = $request->getPathInfo();
$paramas=$request->getQueryParams();
$version=Yii::$app->response->acceptParams['version'];
$route = Yii::$app->response->acceptParams['version'].'/'.$pathInfo;
return [$route,$paramas];
}
public function createUrl($manager, $route, $params) {
}
}
From the docs you linked:
Both methods have their pros and cons, and there are a lot of debates about each approach. Below you'll see a practical strategy for API versioning that is a mix of these two methods:
Put each major version of API implementation in a separate module whose ID is the major version number (e.g. v1, v2). Naturally, the API URLs will contain major version numbers.
Within each major version (and thus within the corresponding module), use the Accept HTTP request header to determine the minor version number and write conditional code to respond to the minor versions accordingly.
The major api version number is determined by the url, as you've set in your urlManager. You can access them by calling your-yii2-app.com/v1/controller/action or your-yii2-app.com/v2/controller/action. Any code that parses the header must be written by you:
Accept HTTP request header to determine the minor version number and write conditional code to respond to the minor versions accordingly.
If you want to write a custom api version header functionallity, this is possible. I suggest you try and create a new question if you run into trouble.
I worked with RBAC in my project which worked fine in overall my project wherever I checked if particular user can perform specified action.
But the actions that I didnt went thorugh RBAC in site controller are not being rendered from any users.
My code that I used to access to the function in site controller:
My function that needs to be accessed in site controller.
Add additional access rule to your behavior in SiteController:
[
'allow' => true,
'actions' => [
'hofview'
],
'roles' => ['?'], // it means user not logged in can access this action
],
I have an old website, now I have written it with new version of yii framework and I want change the urls, but because of SEO problems I want to keep my old urls.
Now when the user enters www.mysite.pre/car/details/10908 I want application renders www.mysite.pre/site/show_detail/10908 how could I handle it in yii2 Routing?
Assuming you have got this action in your SiteController class
public function actionShow_detail($id) {}
Add this in your configuration file:
// ...
'components' => [
// ...
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
// ...
'car/details/<id:\d+>' => 'site/show_detail',
],
],
],
More details and information about Yii 2 routing can be found in "Routing and URL Creation" section of The Definitive Guide to Yii 2.0.
Right now I'm trying to implement themming for my Yii2 based project.
How I see the thing now:
User chooses an application theme from the list on the settings
page in backend.
Using yii2-settings I'm saving all the
configuration data in DB (pretty easy).
In the application
bootstrap.php I'm creating new alias called #theme. Basically it
should lead us to a application theme base path (used in search
paths, assets manager, e.t.c.).
According to official
documentation, that's how I configured my view component:
'view' => [
'theme' => [
'basePath' => '#theme',
'baseUrl' => '#theme',
'pathMap' => [
'#app/views' => '#theme',
'#app/widgets' => '#theme/widgets',
'#app/modules' => '#theme/modules',
],
],
],
An issue I have is with p.3. According to yii2-settings documentation that's how I supposed to read the settings:
$theme = Yii::$app->settings->get('name', 'general');
Yii::setAlias('#theme', realpath(dirname(__FILE__)."/../../themes/$theme"));
But obviously, it's not working for me because of yii2-settings component didn't initialized yet when bootstrap.php is called. I've been trying to initialize it later in the init() method of my base controller, then adjust other aliases manually, but I feel that way being somewhat 'unclean', and also it still fails because of #theme alias is also used in asset file which is Yii2 starting to publish before calling the controller's init method.
So does anyone has any thoughts of how to do that 'hacking' the code as less as possible? I know I could just move configuration to some file, then read it manually before the application initialization, but it's still not the way I want to go.
Maybe there's some way to override some system component to set the alias after db component is loaded, but before view component configuration? Or Yii loads this components in a different order? Anyway. Any help would be appreciated!
You could try an Application Event in bootstrap:
\Yii::$app->on(\yii\base\Application::EVENT_BEFORE_REQUEST, function ($event) {
$theme = Yii::$app->settings->get('name', 'general');
Yii::setAlias('#theme', realpath(dirname(__FILE__)."/../../themes/$theme"));
});
OR in configuration file:
[
'on beforeRequest' => function ($event) {
// ...
},
]
From Yii 2 docs:
EVENT_BEFORE_REQUEST This event is triggered before an application
handles a request. The actual event name is beforeRequest.
When this event is triggered, the application instance has been
configured and initialized. So it is a good place to insert your
custom code via the event mechanism to intercept the request handling
process. For example, in the event handler, you may dynamically set
the yii\base\Application::$language property based on some parameters.
Here's the final solution:
config/bootstrap.php:
// Setting a temporary path for components configuration - will be changed later
Yii::setAlias('#theme', realpath(dirname(__FILE__)."/../../themes/"));
config/main.php
'components' => [
'view' => [
'theme' => [
'basePath' => '#theme',
'baseUrl' => '#theme',
'pathMap' => [
'#app/views' => '#theme',
'#app/widgets' => '#theme/widgets',
'#app/modules' => '#theme/modules',
],
],
],
],
'on beforeRequest' => function ($event) {
$theme = Yii::$app->settings->get('theme', 'general');
Yii::setAlias('#theme', realpath(dirname(__FILE__)."/../../themes/$theme"));
},
I've been developing web apps using Yii 1.1.14 so far, but now it's time for an upgrade.
The company where I work has developed its own Access Control system, and I was really OK with it until I saw what it was really like... A combination of 8 tables in the database (not counting the users table), with a bunch of foreign keys.
1 table for controllers
1 table for the actions
1 table for the menu categories
1 table for types of users
And the other tables basically just connect 2 or 3 of those tables at a time.
It works well, but in my point of view it's highly time consuming to maintain all those tables, and at some point, when your application goes online, if it hits a certain amount of users it could get really slow. specially because 2 of those tables have the user's table primary key as foreign key.
So I've decided that, when I start developing on Yii 2, I'm going to start using RBAC, so I started looking for tutorials online... Only finding many different versions of the same code with author's role, and permissions for create or update posts.
I found a combination of 5 videos on Youtube, but they are about Yii 1 RBAC. They were helpful because I managed to understand most of RBAC's functionality, but I still have some doubts that I'll
enumerate below. And keep in mind that for this Access Control system I'm using the DBManager class.
My Doubts
Yii 1's RBAC used to have 3 tables: auth_assignment, auth_item and auth_item_child. Now in Yii 2 RBAC, a new table appears that is called auth_rule and I still don't understand what that specific table is doing there, how to use it or how to populate it.
I see that it's possible to restrict the user's access to some actions by using the controller's behavior method, and assigning access to some actions depending on the user's role, but when it comes to this I have to split my question into 2:
2.1. First: If you can just restrict the access to actions by setting it up in the behaviors method, then what's the use of saving permissions to the auth_item table?
2.2. Second: If you DO decide to control access according to permissions, then how exactly do you do it, because I find myself writing the following type of code inside of every function and I don't think using RBAC is supposed to be this tedious. There has to be another way.
public function actionView($id)
{
if(Yii::$app->user->can('view-users')){
return $this->render('view', [
'model' => $this->findModel($id),
]);
}else{
#Redirect to a custom made action that will show a view
#with a custom error message
$this->redirect(['//site/notauthorized']);
}
}
Because of the Access Control System that we use right now, when a user logs in, a complex query is executed that will end up returning an array that will be saved as a session variable, and will be used to create a menu with as many dropdownlists as menu categories, that the controllers that the user has access to belong to. How can this be done with RBAC?
I can only really answer 2.2 of your question, as 3 doesn't sound at all like something an RBAC should do. You could, however, get the information you needed from the rules table most likely, provided you followed a naming convention that matched your controllers or actions.
On to answering 2.2 though:
You can simply set the behavior like such:
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'actions' => ['view'],
'roles' => ['view-users'], //<-- Note, rule instead of role
],
]
]
}
This doesn't solve a different problem of 'view-own-users' style permissions, as this needs to inspect the ActiveRecord model (well, at least it does in my application). If You want to achieve this, take a look at my post in the Yii forums here:
http://www.yiiframework.com/forum/index.php/topic/60439-yii2-rbac-permissions-in-controller-behaviors/#entry269913
I use it in one of the simplest method,I use them in the behaviours of my controller.
public function behaviors()
{
return [
'access' => [
'class' => \yii\filters\AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['sysadmin'],
'actions' => ['index','view','update'],
],
[
'allow' => true,
'roles' => ['staff'],
'actions' => ['index','create','update','view'],
],
],
],
];
}
Here roles are the one created in the auth-item table in the database and they have been assigned for users in auth-assignment table. In the behaviours we just use it as above. In the above code sysadmin can have access to index, view and update action, whereas staff can have access to index,create, update and view action.
Yii2 needs a little setup when it comes to using RBAC under your controllers AccessControl. I got around it by making my own AccessRule file.
namespace app\components;
use Yii;
class AccessRule extends \yii\filters\AccessRule
{
protected function matchRole($user)
{
if (empty($this->roles)) {
return true;
}
foreach ($this->roles as $role) {
if(Yii::$app->authManager->checkAccess($user->identity->code, $role))
return true;
}
return false;
}
then in your controller u can use something like this:
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'ruleConfig' => [
'class' => 'app\components\AccessRule'
],
'rules' => [
[
'actions' => ['index', 'resource-type'],
'allow'=> true,
'roles' => ['admin'],
],
],
],
];
}
Where admin is defined as a auth_item and the user is in the auth_item_assignments.
As I have created a new Rbac system for yii2. you can direct permission for a action and action will show you are not authorisez for this action.
By this you find that you will only provide access for action that need to identify.
I uploaded my detail here you can find lot of solution here.
This is the best solution i could come up with when facing the need to filter access by permissions, it's bothersome but can be useful if you're trying to create roles in a productive enviroment and want to use rbac.
use yii\web\ForbiddenHttpException;
if(Yii::$app->user->can('view-users')){
return $this->render('view', [
'model' => $this->findModel($id),
]);
}else{
throw new ForbiddenHttpException('You dont have access to this site');
}