Create AccessRules in modules Yii Framework - yii

in modules\administrator\components\AdminController:
class AdminController extends CController
{
public function filters()
{
return array('accessControl');
}
public function accessRules()
{
return array(
array('deny', 'users' => array('*')),
);
}
}
in modules\administrator\controllers\Sitecontroller:
class SiteController extends AdminController
{
public function actionIndex()
{
$this->render('index');
}
public function actionLogin()
{
$this->render('login');
}
}
but when visit mydomain.site/administrator/site/index, it does not redirect to mydomain.site/administrator/site/login which redirects to mydomain.site/site/login (default login), I want it redirects to mydomain.site/administrator/site/login
can someone help me?

Set CWebUser::$loginUrl before filters are invoked:
class AdminModule extends CWebModule
{
public function init()
{
$this->setImport(array(
'admin.models.*',
//'application.models.*',
'admin.components.*',
));
}
public function beforeControllerAction($controller, $action)
{
if(parent::beforeControllerAction($controller, $action))
{
// this method is called before any module controller action is performed
// you may place customized code here
if ( !Yii::app()->user->checkAccess('admin') ) {
if(Yii::app()->user->isGuest){
$url = Yii::app()->createUrl(Yii::app()->user->loginUrl);
Yii::app()->user->returnUrl = Yii::app()->createUrl('/admin/');
Yii::app()->request->redirect($url);
}
else {
throw new CHttpException(403,'Have no permission');
}
}
return true;
}
else
return false;
}
}

Try this:
array('allow', // allow authenticated user to perform below actions
'actions'=>array('index'),
'users'=>array('#'),
),
array('deny', // deny all users
'users'=>array('*'),
),
This way, when the url is requested for site/index and as we have set index to be allowed only for authenticated users, the request will be redirected to site/login (or default login).
I hope it helps.

Related

How To Get data From Request Login Form Laravel

I have a problem, i need get data request from other field on login form to dashboard.
Login Form
enter image description here
My Login Controller, and i use default auth from laravel
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/home';
public function username(){
return 'username';
}
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}
My AuthentictesUser
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Http\Request;
use App\Tahun;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
trait AuthenticatesUsers
{
use RedirectsUsers, ThrottlesLogins;
public function showLoginForm()
{
$tahuns = \DB::table('tahuns')->where('active','<>', '2')
->orderBy('id','desc')->get();
return view('auth.login', compact('tahuns'));
}
public function login(Request $request)
{
$this->validateLogin($request);
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
protected function validateLogin(Request $request)
{
$this->validate($request, [
$this->username() => 'required|string',
'password' => 'required|string',
'tahun' => 'required|string',
]);
}
protected function attemptLogin(Request $request)
{
return $this->guard()->attempt(
$this->credentials($request), $request->filled('remember')
);
}
protected function credentials(Request $request)
{
return $request->only($this->username(), 'password');
}
protected function sendLoginResponse(Request $request)
{
$request->session()->regenerate();
$this->clearLoginAttempts($request);
return $this->authenticated($request, $this->guard()->user())
?: redirect()->intended($this->redirectPath());
}
protected function authenticated(Request $request, $user)
{
//
}
protected function sendFailedLoginResponse(Request $request)
{
throw ValidationException::withMessages([
$this->username() => [trans('auth.failed')],
]);
}
public function username()
{
return 'email';
}
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->invalidate();
return redirect('/');
}
protected function guard()
{
return Auth::guard();
}
}
How if i use data from fill tahun(year) on login form?
And i need send data from login form to my dashboard.
There is no need to replace the trait since you can simply override the trait methods in your LoginController, just like you did with username().
Override public function showLoginForm() and return your custom view.
Override protected function validateLogin(Request $request) to validate your new fields.
Finally, override protected function authenticated(Request $request, $user) and save the tahun data or return a redirect response.

Can't change destination controller/action via IRouter

I have simple MyRouter:
public class MyRouter : IRouter
{
private readonly IRouteBuilder _routeBuilder;
public MyRouter(IRouteBuilder routeBuilder)
{
_routeBuilder = routeBuilder;
}
public async Task RouteAsync(RouteContext context)
{
if (ShouldReroute(...))
{
SetNeededPath(context, reroute);
}
await GetDefaultRouter().RouteAsync(context);
}
private bool ShouldReroute(...)
{
return true;
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
return GetDefaultRouter().GetVirtualPath(context);
}
private IRouter GetDefaultRouter()
{
return _routeBuilder.DefaultHandler;
}
private void SetNeededPath(RouteContext context, Reroute reroute)
{
context.RouteData.Values.Clear();
context.RouteData.Values["action"] = "StoreContacts";
context.RouteData.Values["controller"] = "Information";
}
}
As you can see it should change the destination of the request to:
[Route("")]
public class InformationController : Controller
{
[Route("StoreContacts")]
public IActionResult StoreContacts()
{
return View();
}
}
The routers description in Startup.cs is:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "areas",
template: "{area:exists}/{controller=Home}/{action=Index}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.Routes.Add(new MyRouter(routes));
});
So in my brain, it should redirect all unmapped requests like mysite.com/unexistingRoute should go to InformationController.StoreContacts, but I get only 404.
Also the mysite.com/StoreContacts is available via the direct URL.
Attribute routing will take over conventional routing , so you can remove the attribute routing :
public class InformationController : Controller
{
public IActionResult StoreContacts()
{
return View();
}
}
And move your logic into custom route via IRouter . mysite.com/unexistingRoute won't map to existed route template config in Startup.cs . So remove attribute should work in your scenario . To map other url like mysite.com/OtherAction , you can write custom logic like :
if (context.HttpContext.Request.Path.Value.StartsWith("/StoreContacts"))
{
context.RouteData.Values["controller"] = "Information";
context.RouteData.Values["action"] = "StoreContacts";
}

.netcore override controllerlevel Authorizeattribute on single action method

In the controllercode below only users who are in the "Administrator" role can accesss the GetData() action method,
because of the controllerlevel Authorizeattribute.
But I also want users who only are in "Manager" role to have access to the GetData() action method, how to write
a custom attribute for this?
[Authorize(Roles = "Administrator")]
Pulic class AdminController : Controller
{
[Authorize(Roles = "Administrator, Manager")]
public IActionResult GetData()
{
}
}
The class-level attribute is always checked first, so it denies anyone who is not in the right role. You need to specify the widest access at class level, then narrow it down on method level where needed:
[Authorize(Roles = "Administrator, Manager")]
public class AdminController : Controller
{
// no attribute needed here
public IActionResult GetData()
{
}
[Authorize(Roles = "Administrator")]
public IActionResult RestrictedMethod()
{
}
}
In the startup.cs file, add the Authorization as follows:
services.AddAuthorization(options =>
{
var roles = new List<string>{ Role.Administrator, Role.Manager};
var requirement =
new List<IAuthorizationRequirement> {new AdminManagerAuthorizationOverrideOthers(roles) };
var sharedAuthentication =
new AuthorizationPolicy(requirement,
new List<string>());
options.AddPolicy(name: "AdminManager", policy: sharedAuthentication);
options.AddPolicy(name: "Administrator", configurePolicy: policy => policy.RequireAssertion(e =>
{
if (e.Resource is AuthorizationFilterContext afc)
{
var noPolicy = afc.Filters.OfType<AuthorizeFilter>().Any(p =>
p.Policy.Requirements.Count == 1 &&
p.Policy.Requirements.Single() is AdminManagerAuthorizationOverrideOthers);
if (noPolicy)
return true;
}
return e.User.IsInRole(Role.Administrator);
}));
});
Create a class in any namespace that Inherits "RolesAuthorizationRequirement" from "Microsoft.AspNetCore.Authorization.Infrastructure" namespace as follows:
public class AdminManagerAuthorizationOverrideOthers : RolesAuthorizationRequirement
{
public AdminManagerAuthorizationOverrideOthers(IEnumerable<string> allowedRoles) : base(allowedRoles)
{
}
}
Then, decorate the controller and action method as follows:
[Authorize(Policy = "Administrator")]
Public class AdminController : Controller
{
public IActionResult GetData()
{
}
[Authorize(Policy = "AdminManager")]
public IActionResult AdministratorOnly()
{
}
}

Yii2 Login from DB (Setting unknown property: app\models\User::password_hash)

I want the user authentication in Yii to be based on user table in my database. This is my User model:
<?php
namespace app\models;
use Yii;
use yii\base\NotSupportedException;
use yii\db\ActiveRecord;
use yii\helpers\Security;
use yii\web\IdentityInterface;
/**
* This is the model class for table "user".
*
* #property integer $id
* #property string $username
* #property string $password
* #property string $title
*/
class User extends \yii\db\ActiveRecord implements IdentityInterface
{
public static function tableName()
{
return 'user';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['username', 'password'], 'required'],
[['username', 'password'], 'string', 'max' => 100]
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'UserID',
'username' => 'Username',
'password' => 'Password',
];
}
public static function findIdentity($id) {
return static::findOne($id);
}
public static function findIdentityByAccessToken($token, $type = null) {
return static::findOne(['access_token' => $token]);
}
public static function findByUsername ($username){
return static::findOne(['username' => $username]);
}
public static function findbyPasswordResetToken($token)
{
$expire= \Yii::$app->params['user.passwordResetTokenExpire'];
$parts = explode('_', $token);
$timestamp = (int) end($parts);
if ($timestamp + $expire < time()) {
//token expired
return null;
}
return static::findOne(['password_reset_token' => $token ]);
}
public function getId() {
return $this->getPrimaryKey();
}
public function getAuthKey() {
return $this->auth_key;
}
public function validateAuthKey($authKey) {
return $this->getAuthKey() === $authKey;
}
public function validatePassword($password){
$this->password_hash= Yii::$app->security->generatePasswordHash ($password);
}
public function generateAuthKey()
{
$this->auth_key = Yii::$app->security->generateRandomKey();
}
/**
* Generates new password reset token
*/
public function generatePasswordResetToken()
{
$this->password_reset_token = Yii::$app->security->generateRandomKey() . '_' . time();
}
/**
* Removes password reset token
*/
public function removePasswordResetToken()
{
$this->password_reset_token = null;
}
}
But it is giving me this error when I try to login:
Setting unknown property: app\models\User::password_hash
This is actionLogin in siteController:
public function actionLogin()
{
if (!\Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
} else {
return $this->render('login', [
'model' => $model,
]);
}
}
And this is the code in LoginForm.php:
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
/**
* Logs in a user using the provided username and password.
* #return boolean whether the user is logged in successfully
*/
public function login()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
} else {
return false;
}
}
I don't know what is wrong, could you please help me fix this?
This is because the column "password_hash" assigned in the function "validatePassword()" doesn't exist in database or is not declared in the User model.
If the password hash is stored in the database in "password" field, then change "validatePassword()" to,
public function validatePassword($password)
{
return $this->password === Yii::$app->security->generatePasswordHash ($password);
}
Here is the solution
1: Declare it in the user controller
* #property string $password_hash
class User extends ActiveRecord implements IdentityInterface
{
public $password;
/**
* #inheritdoc
*/
public static function tableName()
{
return '{{%user}}';
}
public function rules()
{
return [
[['username','email','first_name','last_name','address','password','contact_no','role'], 'safe'],
[['email'], 'unique'],
[['email'], 'email'],
];
}
.
.
....
public function validatePassword($password)
{
return Yii::$app->security->validatePassword($password, $this->password_hash);
}
2: In your LoginForm model rule function add this
['password', 'validatePassword'],
everything else looks okay. Hope this helps you

authorization check in the attribute

I want to implement a redirect unauthorized user with a check in the proper attribute. To do this I create a class attribute with a constructor with no parameters.
[AttributeUsage(AttributeTargets.Method)]
public class LoggedAttribute:Attribute
{
public LoggedAttribute()
{
//TODO
}
}
Now assign this attribute to all methods of action that requires authorization.
[Logged]
public ViewResult SendMessage()
{
return View();
}
I have a User model with boolean flag IsLoggedIn. How can I check this flag in the class attribute to redirect the user to the authentication page in case of an emitted flag ?
In the case of using a custom authorization attribute like below:
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
//anything else you'd like to do like log it
return false;
}
}
}
and then you can redirect them by the following override:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//disable the redirect
if(disabled)
{
//do something else
}else{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Account",
action = "Login"
})
);
}
}
UPDATE: and you use it like this:
[AuthorizeUser]
public ActionResult myAction()
{
return View();
}