How can I implement yii2-rbac in frontend and backend separate? - yii2

I want to implement rbac in frontend and backend separate.
I have a table for admins in backend and admin users work with this table. Also a table for normal users and they work with this table (for login, signup etc).
In the rbac related table (auth_assignment) the 'user_id' field must be valued from another table (user or admin) and this is not possible to get value from both admin and users table.
Is it possible to implement rbac for frontend and backend separate?
if yes how?

It is possible. RBAC DbManager tables can be configured so you can prepare two RBAC components, one for frontend and second for backend, with different tables.
This is the attribute for assignment:
public $assignmentTable = '{{%auth_assignment}}';

Check out this link. Its very useful for RBAC.
https://github.com/yiisoft/yii2/blob/master/docs/guide/security-authorization.md
The RBAC componet are base on common part .. typically if they are base on DB you use common models and shared the related db table ..
You can declare this element in component section of main.php in cofig area and if you do this in common dir this component si correctly shared between both the enviroment (frontend , backend).
eg : common/config/main.php
'components' => [
.....
'authManager' => [
'class' => 'yii\rbac\DbManager',
'cache' => 'cache',
....
],
this mean they could be naturally shared between frontend and backend ..

Related

Yii2 Multi-Tenant Architecture - Choose correct database

I am building an app using Yii2 and I am working with multi-tenant architecture. So... every client has his own database (identical structure).
What I have done so far:
I declare all the different databases in the config/web.php file
I have a master database that corresponds each user to his database. So, when someone logs in, the app knows what database should use.
What I have done but I am not sure about:
I created a file components/ActiveRecord.php with the following code:
<?php
namespace app\components;
use Yii;
class ActiveRecord extends \yii\db\ActiveRecord {
public static function getDb() {
if (isset($_SESSION['userdb'])) {
$currentDB = $_SESSION['userdb'];
return Yii::$app->get($currentDB);
}
}
}?>
So... on login I save the database on the session and in the aforementioned file which extends the default ActiveRecord I override the getDb function and I choose my own. Subsequently, I changed all models so they extend my ActiveRecord.
I am not sure that this strategy is correct but it almost works.
Where is the problem:
Everything works fine except from... RBAC! For RBAC I use the yii2-rbac extension. The problem is that the user is not getting his role from his database but from the first declared database in the config/web.php file. So, whatever the logged in user, the first db is used.
Question(s):
How can I solve this problem? Do you have any idea on where is the file that gives the role to the logged in user?
Bonus Questions: Do you think this strategy is wrong? If so, what would you suggest?
Thanks a lot in advance for your time and your support.
Question # 1: How can I solve this problem? Do you have any idea on where is the file that gives the role to the logged in user?
I would suggest you use yii2 admin extension for this purpose(https://github.com/mdmsoft/yii2-admin) that will solve your issue and it is the best extension to manage user role. use this link for better understanding (https://github.com/mdmsoft/yii2-admin/blob/master/docs/guide/configuration.md)
Install above by following above URL or just add "mdmsoft/yii2-admin": "~2.0" in your composer.json file and run composer update.
After successfully installed this extension run migration to create RBAC tables, if you already have then skipped it.
yii migrate --migrationPath=#yii/rbac/migrations
You have to do some configuration in your main.php to tell your application about public routes and for all other application route system will implement RBAC on them.
This is what you have to add in your main.php file.
'components' => [],
'as access' => [
'class' => 'mdm\admin\components\AccessControl',
'allowActions' => [
'site/login', // add or remove allowed actions to this list
'site/logout',
'site/error',
'site/index',
]
]
Above settings will tell your application to make the login, logout, error and index function are public under site controller and all other applications routes need to have RBAC assignment to perform the action.
main.php is exist in backend/config/main.php in my case, you can use according to your requirements may be in common/config/main.php
Question # 2: Bonus Questions: Do you think this strategy is wrong? If so, what would you suggest?
I think your approach is not extendable, I suggest you create a master database and use that for your application tenants with their databases.
Setup tenant table in master database with tenant name, database required parameters
Make master database connection in your application, that is used to get all tenant and there store DB connections from the master database.
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=127.0.0.1;dbname=master-db',
'username' => 'root',
'password' => 'root',
'charset' => 'utf8',
]
Now at login page show tenants list to choose their info OR you can also play with the domain name as well if you are using the unique domain for each tenant. if you are using unique domain then you have to store domain name with tenant info in the master database to able to get tenant info based on the domain.
// Set Session Values on success login page OR read user domain and get tenant info from the master database and add into session.
``
$tenant = Tenant::findOne(Yii::$app->request->post('tenant_id'));
Yii::$app->session->set('DB_HOST', $tenant->DB_host);
Yii::$app->session->set('DB_USER', $tenant->DB_username);
Yii::$app->session->set('DB_PASS', $tenant->DB_password);
Yii::$app->session->set('DB_NAME', $tenant->DB_name);
``
Once you have tenant info you can get the tenant from the master database and add their database settings into SESSION.
Create a BaseModel.php class and extend it with all of your application models classes.
In you BaseModel.php add below method to make runtime DB connection based on domain or tenant selection from the login page.
public static function getDb(){
if ( Yii::$app->session->get('DB_HOST') ){
$host = Yii::$app->session->get('DB_HOST');
$dbName = Yii::$app->session->get('DB_NAME');
$dsn = "mysql:host=$host;dbname=$dbName";
Yii::$app->db->dsn = $dsn;
Yii::$app->db->username = Yii::$app->session->get('DB_USER');
Yii::$app->db->password = Yii::$app->session->get('DB_PASS');
return Yii::$app->db;
}
}
By doing this you will have the option to add multiple tenants from backend to master database and that will automatically available in your application.
I believe that this information is helpful for you.
Cheers!!!
Here is the documentation on dbmanager.
If the you want to get rbac info from a different database, this should work.
Inside your config folder, create a file called rbacdb.php. Add the following:
<?php
return [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=<db host or ip>;dbname=<dbname>',
'username' => '<dbuser>',
'password' => '<dbPassword>',
'charset' => 'utf8',
];
Then go to your config file and find the authmanger section. It should look like this:
'authManager' => [
'class' => 'yii\rbac\DbManager',
'db'=> require(__DIR__ . '/rbacdb.php'),
'assignmentTable'=>'<tableName>',
'itemChildTable'=>'<tableName>',
'itemTable'=>'<tableName>',
'ruleTable'=>'<tableName>'
]
EDIT: after rereading your post...each user doesn't need their own rbac table/schema. use the system that Yii has in place.

Drupal 8 - multisite with shared tables (user tables / all tables)?

I need to create about 20-30 Drupal8 sites on different domains. There will be similar content (difference only in details like city name, ajax calls, etc.) but also there will be a specific content like news.
I know all weakness of this idea, but anyway I think that shared tables in one database will be the best solution for this project.
My steps:
installing first default site (sites/default) with prefix for tables default_
creating directory for second site (sites/second), and configuring sites.php (seconddomain.com => sites/second)
installing second site (sites/second) with prefix for tables second_
... then I tried to use solution which is described on many sites:
$databases['default']['default'] = array(
'database-configuration-stuff' => '[...database configuration]'
'prefix' => array(
'default' => 'second_', // default prefix for second site
'users' => 'default_', // shared users...
'sessions' => 'default_',
'role' => 'default_',
'authmap' => 'default_',
),
);
but it doesn't work. I see only users from second site. Cache cleaning doesn't change anything. Any ideas?
Maybe there is possibility to create multi-page solution with one shared database (not only for users but for nodes also) and create content directed to different domains from one admin console?
BTW: If there is any possibility to create sth like this using Drupal7 I can change d8 to d7.
if you'd like to make sth I was looking for you've got three options:
you need to write your own module ;) ,
you need to wait for "Domain Access" module for D8: https://www.drupal.org/project/domain ,
you can also use D7 and module from URL which I provide above.
I chose 3rd option.

Design best practice - model vs controller vs UI - CakePHP, MySQL

I have been struggling for a few days with this problem and finally seek the opinion of the experts and crowd at this website.
I have two tables - one is a template of workflow steps and the other is an instance of these workflow steps called events. The templates table contains information like step name, step type etc - very generic information. The event table contains a reference link back to the workflow step table and an additional column called notes - which stores data that the user logged as they logged a particular workflow step. Both Workflow Steps and Events are linked to a POST on the website
Workflow step templates can exist without events having yet occurred - that is the user may be still on Step 3 or Step 5 and not logged an event for Step 1, 2 , 4 - basically the order of steps is only suggestive but not binding. Workflow Steps have a sequence field that dictate the order in which they should appear on screen.
Events can also occur without a workflow step - in other words, a user can log a note outside the context of workflow steps. These are generic events and directly associated with the POST
I am able to successfully retrieve both of these values for a given POST - they are retrieved as two separate arrays. I am using CakePHP and MySQL
The UI needs to render a screen that shows all the workflow steps in order and corresponding events that have occured in correlation to these steps or outside of these steps. The ordering of the screen will be driven primarily by the sequence of workflow steps and secondarily by created_date for those events that are not associated with a particular workflow step
Problem statement -
1. Do I send two separate arrays (as noted in #4) to the UI and let the UI determine the complex logic of how to interweave the steps and events for display?
2. Do I process the interweaving of steps and events in the controller and then send to the UI a simple array that it can loop through and display?
3. I have tried moving this logic to the database but because of variations explained in #2 and #3 it becomes quite complicated
I am seeking advise on which would be a better option from a design practice as well as from a simplification point of view. I understand that I have given a limited picture here but am hoping that someone on this website may have run into a similar issue elsewhere.
Depending on how you are assigning events to users, I would make a hasOne relation in Event to Workflow. You would need another relationship for you users, hasOne or hasMany.
$hasOne = 'Workflow';
Obviously this would mean that your Event table would have a column called wordflow_id and would be associated with a single row in your workflow. In the controller I would call the Event with by the user.
$this->Event->findAllByUserId($user_id);
This should provide you with an array that might look something like this.
array(
[0] => array(
[Event] => array(
'id' => 1,
'name' => 'blah',
...
),
[User] => array(
'id' => 1,
'name' => 'Charles',
...
),
[Workflow] => array(
[0] => array(
'id' => 1,
'name' => 'more blagblagblag',
...
),
[1] => array(
'id' => 9,
'name' => 'sblagblsagblag',
...
),
[2] => array(
'id' => 42,
'name' => 'mordse d',
...
)
)
)
)
Call all your workflow templates
$this->Workflow->find('all');
Then I would user cake's built in SET:: functionality to print the workflow template in your view and use your Event call to fill in the data.
Please post more detail and your code, models, ect and I'm sure we can get you the exact query/logic you'll need to achieve this.
http://book.cakephp.org/2.0/en/core-utility-libraries/set.html
OK - I have solved this. I ended up moving the functionality to the Model.
I created two SQL queries - one that retrieves all the workflow steps along with any event information that maybe associated with each of them.
Then I created a second SQL that retrieves all those events that are stand-alone and not associated with any particular workflow step
I used UNION ALL to stack them on top of each other
I used a SORT on modified date and squence number so that all the steps and events appear in chronological order and sequence
I then passed this from the Model to the View (via the controller) and let the View iterate and display the elements. This approach actually simplified my View and Controller code immensely and even the Model code is quite simple since all it is a query statement with parameters.

ATK4 How OneToMany retion can be used with multiselect?

I have 2 MySQL DB tables called roles (representing user access role) and modules (representing allowed modules per role). Each role can have many modules and each module can have many roles.
I want to add a crud representing roles table with a multiselect field allowing to select all related modules. What is the best way to do that. Thanks in advance.
There can be multiple solutions for this.
First one - use User CRUD with expander with Roles (should work):
$crud = $this->add('CRUD');
$crud->setModel('User');
if (! $crud->isEditing()) {
// add subCRUD
$sub_crud = $crud->addRef('UserRole', array(
'extra_fields' => array('role'),
'view_options' => array('entity_name' => 'Role'),
'label' => 'Roles'
));
}
Second one - use Grid with Roles + grid->addSelectable($fields) (not tested, but just to give you idea):
$grid = $this->add('Grid');
$grid->setModel('UserRole');
$grid->addSelectable('selected');
Third one - use two lists with roles (available roles and associated roles) and some buttons to "move" role from one list to another.
Something like this: (can't find link to appropriate Codepad page now) :(
There definitely can be even more ways to do this.

Kohana ORM store count in many-to-many relationship

I'm building a application using Kohana 3.2 and Kohana ORM.
The application has systems. Systems contain components. A system may contain multiple components, but also multiple components of the same type. E.g. System_A may have 10 Component_Y and 3 Component_Z
So instead of just having two belongs_to fields in my pivot table I also want to store the count.
If I just use a has-many-through I won't be able to access the count. Without ORM I'd just join the count onto the component in SQL, because the count is unique for the System + Component combination and so I can access the count for the component when I visit the object in the context of a certain system.
How best to go about this in Kohana ORM?
I solved it partially this way:
protected $_has_many = array(
'omvormer' => array(
'model' => 'omvormer',
'through' => 'systeemomvormer'
),
'systeemomvormer' => array(
'model' => 'systeemomvormer',
)
);
I've added the pivot tabel systeemomvormer separately to systeem.
I can now do this:
$so = ORM::factory("systeemomvormer");
$so->where('systeem_id', '=', $systeem_id);
$so->where('omvormer_id', '=', $omvormer_id);
$result = $so->find();
$result->aantal = $omvormer_count;
But it really still only is a partial solution, because I'm not able to update() the result. Kohana says that the result is not loaded. However that's outside the scope of this question and I'll open a new question for that.
This was also helpfull:
http://forum.kohanaframework.org/discussion/7247/kohana-3-orm-save-for-update-a-many-to-many/p1
To store more data in a junction table than just the two keys, you need to create a model for it.
So,
system _has_many system_component
component _has_many system_component
system_component _belongs_to component
system_component _belongs_to system
However, you may not need to store the count if you do it this way. Instead, you can do the following:
$system->components->count_all();
Then, to access them:
foreach($system->components->find_all() as $component)
echo $component->component->name;