sjaakp / yii2-pluto

User management extension for Yii2
https://demo.sjaakpriester.nl/
MIT License
8 stars 12 forks source link

Advanced template #23

Open ettolo opened 4 years ago

ettolo commented 4 years ago

Can someone share an advanced template configuration? I can't get it working correctly Thanks in advance!

rossaddison commented 4 years ago
**console/config/main.php**

 'bootstrap' => ['log','libra'],
'params' => $params,
    'modules' => [
            'libra' => [
                    'class' => 'sjaakp\pluto\Module',
                    // several options
            ],
      ],

**frontend/config/main.php**

'bootstrap' => [
        'log',
         // The sjaak/pluto user module facility setup in frontend/config/main.php and originating in 
              composer.json uses this pseudonym. Set it here:
     // This will appear next to your domain name eg. yoursite.co.uk/libra 
        'libra'
    ],

'modules' => [
      'libra' => [
        'class' => 'sjaakp\pluto\Module',
        'passwordFlags' => ['all' => 'reveal'],
        'passwordHint' => Yii::t('app','At least eight characters, one uppercase, one digit'),
        'passwordRegexp' => '/^\S*(?=\S{8,})(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[\d])\S*$/',
        'identityClass' => 'sjaakp\pluto\models\User',
        //prevent the external guest signing up of users until site is stable by setting fenceMode to 
 true
    //if fenceMode is set to true you can still signup users internally as the user with 'admin' rights.
        'fenceMode' => true,
        'viewOptions' => [
           'row' => [ 'class' => 'row justify-content-center' ],
           'col' => [ 'class' => 'col-md-6 col-lg-5' ],
           'button' => [ 'class' => 'btn btn-success' ],
           'link' => [ 'class' => 'btn btn-sm btn-secondary' ],
        ],        
       ],

**frontend/views/layouts/main.php**

if (Yii::$app->user->isGuest) {
             $menuItems[] = ['label' => Html::button(Yii::t('app','Home'),['class'=>'btn btn-success btn-lg','title'=>Yii::t('app','Home'),'data-toggle'=>'tooltip']), 'url' => ['/site/index'],];
             $menuItems[] = ['label' => Html::button(Yii::t('app','Login'),['class'=>'btn btn-success btn-lg','title'=>Yii::t('app','Login'),'data-toggle'=>'tooltip']), 'url' => ['/libra/login']];
             $menuItems[] = ['label' => Html::button(Yii::t('app','Forum'),['class'=>'btn btn-success btn-lg','title'=>Yii::t('app','Forum'),'data-toggle'=>'tooltip']), 'url' => ['/flarum/public']];
    } else {
             $menuItems[] = '<li>'
            . Html::beginForm(['/libra/logout'], 'post')
            . Html::submitButton(
                Yii::t('app','Logout (') . Yii::$app->user->identity->attributes['name'].')',
                ['class' => 'btn btn-secondary logout btn-lg']
            )
            . Html::endForm()
            . '</li>';
    }
ettolo commented 4 years ago

Ok, this works for frontend. I had to modify backend config to share login... is this correct?

//        'user' => [
//            'identityClass' => 'common\models\User',
//            'enableAutoLogin' => true,
//            'identityCookie' => [
//                'name' => '_identity-backend',
//                'path' => '/admin',
//                'httpOnly' => true,
//            ],
//        ],
        'session' => [
            // this is the name of the session cookie used for login on the backend
//            'name' => 'advanced-backend',
//            'cookieParams' => [
//                'path' => '/admin',
//            ],

            //share session with frontend to have pluto work
            'name' => 'advanced',
        ]
rossaddison commented 4 years ago

(https://www.yiiframework.com/wiki/814/guide-how-to-actually-separate-frontend-user-and-backend-admin-on-yii2-advanced.)

Two separate user tables with different names and different models advisable. So make sure your backend is accessing a separate table.

   public static function tableName()
    {
        return '{{%user}}';   //change!!
    }

You are using pluto so you will be administering roles and permissions to users that have registered on the frontend, that you have made active by the GUI, and that at best will be restricted to their own database sharing the frontend code.

You at best will have some utility hard code that lists all your databases under config components and depending on what permission you have given them eg. can('Access db1')....not can('Access db)! that is sitting in the auth_itemchild table under database component db, they will be directed to their database only because every model's getDb function linked to your component utility will retrieve their database based on their permission that you have allocated them under Pluto's RBAC and that is evident in the component utilty.

Pluto's RBAC cannot offer you more than that at this stage since it is not an automated role and permission assignment module. ie. the user is not automatically made active apart from default role assignment and registration which is not adequate if you have more than one database. It is a GUI and admin has to intervene with a few clicks to make the connection between the user table and the assignment table and hence an active connection after registration for all users that are not assigned the default Role. Typically the middle higher support role applies here since this will have to be assigned to managers, one level below Admin. Automation can be extended programmatically from the module though no doubt as a future endeavour.

There is enough complexity connecting and administering the frontend efficiently and I personally, considering that you are trying to simplify the backend and frontend by using the same user table, I would `personally be disabling the backend usage right now until your frontend is fully setup.

Different strokes for different folks...especially if you play golf!!

ettolo commented 4 years ago

Thank you! I see your point. I created back-end because I need some kind of advanced users to control the activities of basic users, points earned and statistics and I like to have a different theme for them (admin theme). Basic users register themselves in the front-end and their role (automatically given by Pluto) doesn't give them access to back-end. Advanced user can register themselves or i can create their accounts and their role doesn't give them access to back-end. You see some security problems this way? Is there a simpler way to achieve this? Thanks again!

rossaddison commented 4 years ago

I take it you are only dealing with one database which is not my scenario. I give each user their own database, apart from database db which belongs to the administrator and where the user and auth tables sit, so that they are fully responsible for the installation of the core tables via migrations using a GUI (and not the console/command prompt) using symfony/processbuilder. This adds an extra layer of security to the administrator and to users since they can be held responsible for backing up their own data and there will be no recourse to me in the event of data protection issues as administator and the final axe falls on the head of the hosting provider. This means that users are also solely responsible for their own backups using ifsnop's "ifsnop/mysqldump-php": "*".

The great thing about RBAC is that you can invent any word eg. Create House as a permission in pluto and provided the controller has something like:

    if (!\Yii::$app->user->can('Create House')) {
        throw new \yii\web\ForbiddenHttpException(Yii::t('app','You do not have permission to create a cost.'));
    }

in the beginning of its actions in your Controllers you can prevent access to this action. This applies to permissions which are grouped under roles. Don't forget that you can also put pluto's roles 'support' and 'admin' under the Controller's behaviors as well here to add an initial layer of security here before permissions are applied.

     'access' => 
                        [
                        'class' => \yii\filters\AccessControl::className(),
                        'only' => ['create', 'update'],
                        'rules' => [
                        [
                          'allow' => true,
                          'verbs' => ['POST']
                        ],
                        [
                          'allow' => true,
                          'roles' => ['support','admin'],    //change here.
                        ],

What you need to do is use the 'support' role for your advanced managers. Put all the permissions that are common to all the managers under this support role. Then create a role specific to the manager eg. in my case 'Mdb1' for the manager that will access config component db1. Then put in permissions that are specific to that role, which is 'Assign db1'. Then finally you let Mdb1 inherit the support role. This is all presetup for each database on the RBAC before any user registers so that when a user registers you can assign them the 'Mdb1' role which then gives him access to the database1 ie. config component db1 and make the user active This connects the auth tables to the user table by assigning the user id in the user table to the role in the auth_item table. This user_id role relationship is seen in the auth_assignment table where there are the user_id and item_name (or role).

If you are considering assigning databases per user you will need something like the following:

common\config\main-local.php

** for each database.

return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=my_db',
            //user name is normally 'yoursitename_co_uk_db'
            'username' => 'root',
            //password is the password that has been assigned under the above username
            'password' => '',
            'charset' => 'utf8',
            'enableSchemaCache' => true,
            'schemaCacheDuration' => 3600,
            'schemaCache' => 'cache',

        ],

frontend/components/utilities.php

public static function userLogin_set_database() (which appears in every getDb function for each 
model)
{
                if ( \Yii::$app->user->can('Access demo')){
                    return \Yii::$app->demo;
                    exit;
                }
            else if (\Yii::$app->user->can('Access db')) {
                    return \Yii::$app->db; 
                    exit;
                }
                else if (\Yii::$app->user->can('Access db1')) {
                    return \Yii::$app->db1; 
                    exit;
                }
                else if (\Yii::$app->user->can('Access db2')) {
                    return \Yii::$app->db2; 
                    exit;
                }
                else if (\Yii::$app->user->can('Access db3')) {
                    return \Yii::$app->db3;
                    exit;
                }
                else if (\Yii::$app->user->can('Access db4')) {
                    return \Yii::$app->db4; 
                    exit;
                }
                else if ( \Yii::$app->user->can('Access db5')){
                    return \Yii::$app->db5; 
                    exit;
                }
               else if ( \Yii::$app->user->can('Access db6')){
                    return \Yii::$app->db6; 
                    exit;
                }
               else if ( \Yii::$app->user->can('Access db7')){
                    return \Yii::$app->db7; 
                    exit;
                }
               else if ( \Yii::$app->user->can('Access db8')){
                    return \Yii::$app->db8; 
                    exit;
                }
               else if ( \Yii::$app->user->can('Access db9')){
                    return \Yii::$app->db9; 
                    exit;
                }
                else if ( \Yii::$app->user->can('Access db10')){
                     return \Yii::$app->db10; 
                     exit;
                 }
} 

public static function userdb(){
       return Yii::$app->session['currentdatabase'];
}

And then in each model:

public static function getDb()
   {
       return \frontend\components\Utilities::userdb();
   }

You will also need to modify your index.php in the root as follows:

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
defined('YII_CONSOLE') or define('YII_CONSOLE', false);

//vendors from composer.json
require (__DIR__ . '/vendor/autoload.php');

//yii framework
require(__DIR__  . '/vendor/yiisoft/yii2/Yii.php');

//aliases
require(__DIR__ . '/common/config/bootstrap.php');

//empty
require(__DIR__ . '/frontend/config/bootstrap.php');

$config = yii\helpers\ArrayHelper::merge(
    //filecache and dbmanager
    require(__DIR__ . '/common/config/main.php'),

    //db component,  swiftmailer
    require(__DIR__ . '/common/config/main-local.php'),

    //etc... UrlManager
    require(__DIR__ . '/frontend/config/main.php'),

    //cookie validation key
    require(__DIR__ . '/frontend/config/main-local.php')
);
(new yii\web\Application($config));

if (!Yii::$app->user->isGuest) {
    Yii::$app->session['currentdatabase'] = 
    \frontend\components\Utilities::userLogin_set_database();
}

Yii::$app->run(); 

I do not see any problem with your setup and security although you will have to signup the Managers with support roles since you will be assigning this role to them.

You can further beef up your security by doing the following:

Cryptography: The sjaakp/pluto/models/User uses the yii\web\IdentityInterface. Also it uses the following function:

public function encryptPassword($attribute, $params) { $this->password_hash = Yii::$app->security->generatePasswordHash($this->$attribute); } This function incorporates the Blowfish hash algorithm by default through Yii2. The $cost parameter can be added to the above GeneratePasswordHash parameters.

For further reading https://www.yiiframework.com/doc/api/2.0/yii-base-security#generatePasswordHash()-detail

Hope this will help

ettolo commented 4 years ago

Great explanation! Your environment is far beyond my necessities but it has been a great interesting reading to be saved for future. Thank you!