Yii2 per content visibility depending on role - permissions

We have used RBAC to implement simple role based permissions for CRUD, but now we need to also add a 'visibility' functionality which makes it possible to limit content visibility (R) to only registered users or only the content owners.
So, how can we limit content visibility on different levels, for example
PUBLIC: anybody can see the content, including anonymous
INTERNAL: only registered users can see the content
PRIVATE: only the creator can see the content
What would be the best way to implement this, it looks like RBAC does not have a straightforward way of dealing with this.

I think that the problem can be solved by using defaultScope in models. Thus, before giving the content, we can check the current role of the user data and give the necessary conditions.
public static function find()
{
$userRoleArray = \Yii::$app->authManager->getRolesByUser(Yii::$app->user->getId());
$userRole = current($userRoleArray)->name;
if ($userRole == 'admin') {
return parent::find()->where("Your condition");
} elseif ($userRole == 'moderator') {
return parent::find()->where("Your condition");
}
}

you can make a permission function and run in each function that will take user role as argument and returns true or redirect to not allowed page.
Here is something I tried but you can modify according to your need.
public function allowUser($min_level) {
//-1 no login required 0..3: admin level
$userRole = //get user role;
$current_level = -1;
if (Yii::$app->user->isGuest)
$current_level = 0;
else
$current_level = userRole;
if ($min_level > $current_level) {
$this->redirect(array("/pages/not-allowed"),true);
}
}

Related

How to check if user is logged in, in Shopware 6 Storefront Controller or Subscriber?

I am looking for the right way on how to check, if a user is logged in, in the Shopware 6 storefront. I am writing a plugin (not an app), and want to use this in Controllers and/or Subscribers.
Should I:
Use the Storefront API? (but how? which path?)
Use the default symfony way? (isGranted) - but with which Roles? Isn't the role handling different?
Use some built-in functionality like a special service that I can fetch by Dependeny Injection (but which one?)?
Solution:
Thanks to #Uwe Kleinmann, I found a solution, that works in a subscriber like this:
public static function getSubscribedEvents()
{
return [
ProductPageLoadedEvent::class => 'onProductPageLoaded'
];
}
public function onProductPageLoaded(ProductPageLoadedEvent $event): void
{
$saleschannelContext = $event->getSaleschannelContext();
$customer = $saleschannelContext->getCustomer();
if(NULL === $customer) {
$customer = 'not-logged-in';
}
$event->getPage()->addExtension(
'myextension', new ArrayStruct([
'test' => $customer
])
);
}
The SalesChannelContext has a $customer (accessible with getCustomer()) attribute. This context is usually injected into both Storefront controllers and subscribers for any Storefront events.
It is only set, if the current user is logged-in.
You may also use the _loginRequired and _loginRequiredAllowGuest flags in the #Route annotation of a storefront controller's method. This is handy if you only want to allow access for logged in customers as this will automatically redirect logged out users to the login page and back to the origin after they logged in.
/**
* #Route("/my/custom/page", name="frontend.custom.page", methods={"GET"}, defaults={"_loginRequired"=true, "_loginRequiredAllowGuest"=true})
*/

Laravel Authentication with condition

I am using Laravel 5.1 and Laravel's default authentication system.
In database (MySQL) I add a new column named 'role'. The value will be 1 for admin and 2 for members.
Now I want to give login permission only for admin, means where the value is 1. How can I do that?
Actually I solved it. I just add these code in postLogin() method of AthenticatesUsers.php method.
// If role is equal to 1, user allowed to login
// You can change $admin value anytime according to database Design
// Example: In role column the value for admin is 2 or A. You just need to change the value of $admin.
$userData = User::select('role')->where('email',$request['email'])->first();
$admin = 1;
$role = $userData->role;
if($role == $admin){
$request['role'] = $role;
}
I feel that there are better ways to achieve what you're after, such as middleware, however given what you're after this would be one way to do it.
Upon logging in a user us sent to 'home', unless you specify otherwise in the AuthController.
Inside your routes.php, if you just set up a GET route to point to a HomeController (or whatever you name it) then you could use a function to run the tests you're after.
routes.php
Route::get('home', 'HomeController#index');
HomeController
public function index()
{
//If they are yet to log in then return your normal homepage
if (Auth::guest())
{
return View::make('home');
}
else
{
//Run your tests here to check their role and direct appropriately
//Given you have added the role column to the users table, you can access it like so:
//Auth::user()->role
}
}

Dynamically populate authorize attribute

I'm using windows authentication with no roles setup, I simply have some admin names stored in a table that I want to check against in combination with the authorize attribute. I don't have much experience using this, but the only examples I see are hard coded values like below so I'm not sure if this functionality is available or if I'd need to add it.
[Authorize(Users = #"domain\user1, domain\user2")]
Any suggestions will be appreciated.
I ended up adding this myself, very easy to do.
public class AuthorizeUser : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
string[] admins =
//get user names
if (admins.Contains(httpContext.User.Identity.Name))
return true;
return false;
}
}
Then to use just
[AuthorizeUser]

Yii Behaviors and scenario

i have a behavior for my models, the behavior has beforeFind, beforeSave, in methods i override user_id, something like:
...
public functio beforeSave() {
$this->owner->user_id = Yii::app()->user->id
}
I have model User, how can i disable behavior for registration new user?
Saving code:
$user = new User();
$user->id = 1332;
$user->field1 = 'data';
$user->save();
but on save i have null in $user->id (because work behavior).
i tried
$user->disableBehaviors();
$user->detachBehavior();
Without result.
Maybe its not right way? I create behaviors for identify users in system (find only user something, save only with user id...), but that if i have new user with full previegies, i should again detach behaviors?
If condition can be changed in future I just pass it as callback parameter into behavior from model.
This give you a bit more control over the condition. Hence, behavior becomes more reusable - if it is used by several models this condition can be unique for each.
Example below is a bit simplified, but you should get the idea.
Behavior:
class SomeBehavior extends CActiveRecordBehavior
{
public $trigger;
public function beforeSave($event)
{
if(!call_user_func($this->trigger))
return;
// do what you need
$this->owner->user_id = Yii::app()->user->id;
}
}
Model:
class SomeModel extends CActiveRecord
{
public function behaviors()
{
$me=$this;
return array(
'some'=>array(
'class'=>'SomeBehavior',
'trigger'=>function() use($me){
return $me->scenario=='some-scenario';
}
)
);
}
}
Also I use PHP 5.3. So, I use closure for trigger callback.
If your PHP version is less than 5.3 - anything callable can be used instead. Check here http://www.php.net/manual/en/function.is-callable.php
Because of behavior is a method, you can declare your own logic inside.
The model knows about its scenario, so there is no problem to return different arrays for different conditions:)
Hope it be helpful for somebody.
You can check Yii::app()-user->isGuest to determine if the user is logged in or not. or you can just try looking for the null. Like this:
if (!Yii::app()->user->isGuest)
$this->owner->user_id = Yii::app()->user->id;
or
if (null !== Yii::app()->user->id)
$this->owner->user_id = Yii::app()->user->id;

Yii: shared password for member's area

What would be the best way to approach setting up a shared password protected area in Yii?
I am looking to have a view of a Group model, that can be accessed by a shared password created by the owner of that group - group members shouldn't have to log in, purely enter this passcode.
Should this still be done with Yii's built in auth tools? - or is there a simpler solution, bearing in mind that someone might want to access several groups.
You can do this using standard session mechanism built into PHP. When someone tries to view password-protected area, check the session variable, if the user haven't entered password yet then redirect him to some page with a password form (you can do the check using controller filters for example).
After the form is submitted, check correctness of password and if everything is ok, write it into the session. You can differentiate session keys by group ids.
You can use Yii filter capabilities to fire code before executing a controller action, and prevent actions that you do not want to allow.
I would create a common controller for all your group pages, and inherit other controller from this one if need to.
In the filter I would setup code to check/prompt for the password, and keep that in session.
For example we have a filter setup to detect if the user has accepted our revised Terms and Conditions. The filter will detect and will prevent access to the controller until the user doesn't confirm it.
class TocConfirmFilter extends CFilter {
/**
* Initializes the filter.
* This method is invoked after the filter properties are initialized
* and before {#link preFilter} is called.
* You may override this method to include some initialization logic.
*/
public function init() {
}
/**
* Performs the pre-action filtering.
* #param CFilterChain the filter chain that the filter is on.
* #return boolean whether the filtering process should continue and the action
* should be executed.
*/
protected function preFilter($filterChain) {
// do not perform this filter on this action
if ($filterChain->action->controller->id . '/' . $filterChain->action->id == 'public/onePublicPage') {
return true;
}
if (isset(Yii::app()->user->id)) {
$user = user::model()->findbyPk(Yii::app()->user->id);
if ($user === null)
throw new CHttpException(404, 'The requested user does not exist.');
if ($user->tocconfirmed == 0) {
Yii::app()->getRequest()->redirect(Yii::app()->createAbsoluteUrl('authorize/confirm'));
return false;
}
}
return true;
}
/**
* Performs the post-action filtering.
* #param CFilterChain the filter chain that the filter is on.
*/
protected function postFilter($filterChain) {
}
}