I am using the Laravel 5 built in user stuff with Entrust for user roles and permissions. I have two roles set up which are administrators and users. Basically what I want to do is have two different forgotten password email templates - one for users and one for administrators. So when a user enters their email address to get the reset link emailed to them I need to check what sort of user they are first and then send them the right template. I don't want to have to do any sort of hacky stuff in the standard email template their must be a way to do this in the controller or something surely? Anyone know how I would do it?
You can probably prompt them to enter their email and when they submit you can grab it in the controller:
public function forgot()
{
$email = Input::get('email');
$user = User::where('email', $email)->first();
if($user->type == 'admin') {
// logic to email admin with admin template
} else {
// logic to email user with user template
}
// redirect to success page letting user know the email was sent
return View::make('someview');
}
Or better yet, just pass the user type to an email service that handles the emailing:
public function forgot()
{
$email = Input::get('email');
$user = User::where('email', $email)->first();
$emailService->sendForgotForType($user->type);
// redirect to success page letting user know the email was sent
return View::make('someview');
}
If you are using Laravel 5's built in User Management:
To override the default template used you would need to manually set the $emailView in the PasswordBroker.php by writing a new class that extends PasswordBroker.
For example, comment out 'Illuminate\Auth\Passwords\PasswordResetServiceProvider' in config/app.php
Then create an extension class:
use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Contracts\Auth\CanResetPassword;
class MyPasswordBroker extends PasswordBroker {
// override
public function emailResetLink(CanResetPasswordContract $user, $token, Closure $callback = null)
{
// Override Logic to email reset link function perhaps using the example above?
}
}
Then you would need to bind your new MyPasswordBroker class to AppServiceProvider at app/Providers/AppServiceProvider.php in the register method (below found online):
$this->app->bind('App\Model\PasswordBroker', function($app) {
$key = $app['config']['app.key'];
$userToken = new \App\Model\NewUserToken;
$tokens = new \App\Repository\NewTokenRepository($key,$userToken);
$user = new \App\Model\NewUser;
$view = $app['config']['auth.password.email'];
return new \App\Model\PasswordBroker($tokens, $users, $app['mailer'], $view);
});
Definitely moderately advanced stuff, if you can handle it - great. Otherwise I would possibly look into using an authentication package with built in features you need.
Related
I've searched the forums and have seen many similar issues but none that seem to address my concern. I believe this is different because:
Form validation is not being used at this point
The form method does not seem to be related (just 1 post action)
The routes are not wrapped in web middleware
Here's what the application is supposed to be doing:
A user (with or without Authentication) views a public page with form (display_event)
The user selects a specific ticket for ordering and is directed to a 2nd form (register_step1)
The user then fills out demographic info for as many tickets as are being ordered
The processing step, if the email address used is of a valid user (in DB) should return to the form in step 2 & 3, populate the fields and flash a message. Otherwise it would perform the save() actions required. (register_step2)
The relevant routes from web.php are here:
Route::get('/events/{event}', 'EventController#show')->name('display_event');
Route::post('/register/{event}', 'RegistrationController#showRegForm')->name('register_step1');
Route::post('/register/{event}/create', 'RegistrationController#store')->name('register_step2');
The relevant portions of the RegistrationController.php are here:
public function showRegForm (Request $request, $id) {
// Registering for an event from /event/{id}
$ticket = Ticket::find(request()->input('ticketID'));
$quantity = request()->input('quantity');
$discount_code = request()->input('discount_code');
$event = Event::find($ticket->eventID);
return view('v1.public_pages.register', compact('ticket', 'event', 'quantity', 'discount_code'));
}
And:
public function store (Request $request) {
$event = Event::find(request()->input('eventID'));
if(Auth::check()) {
$this->currentPerson = Person::find(auth()->user()->id);
}
// set up a bunch of easy-reference variables from request()->input()
$email = Email::where('emailADDR', $checkEmail)->first();
if(!Auth::check() && $email === null) {
// Not logged in and email is not in database; must create
$person = new Person;
// add person demographics from form
} elseif(!Auth::check() && $email !== null) {
// Not logged in and email is in the database;
// Should force a login -- return to form with input saved.
flash("You have an account that we've created for you.
Please attempt to login and we'll send you a password to your email address.", 'warning');
return back()->withInput();
} elseif(Auth::check() && ($email->personID == $this->currentPerson->personID)) {
// the email entered belongs to the person logged in; ergo in DB
$person = $this->currentPerson;
// add person demographics from form
} elseif(Auth::check() && ($email->personID != $this->currentPerson->personID)) {
// someone logged in is registering for someone else in the DB
$person = Person::find($email->personID);
// add person demographics from form
} else {
// someone logged in is registering for someone else NOT in the DB
$person = new Person;
// add person demographics from form
}
// do more stuff...
$reg = new Registration; (set up a registration record)
}
I took the advice indicated in #apokryfos's comment and changed the form parsing-then-display script from a POST to a get.
redirect()->back() is, apparently, always a method=get and that was the cause of the MethodNotAllowedHttpException. In my ~2 weeks using Laravel, I hadn't yet come across that fact.
I have created a log in form for my admin pages and it works, but for now everyone who logs in can access those pages.
My users are also belong to usergroups and my users table in the database has a group_id column. The admin group has an id of 1.
What I'd like to do is that if someone who belongs to the admin group logs in can access the admin pages, but if the user belongs to a different group and tries to log in be redirected to main page or anywhere.
What I'm trying to do is add a similar code to the admin pages controllers
class Dashboard extends MY_Controller {
public function __construct() {
parent::__construct();
// Access control
if(!$this->session->userdata('logged_in')) {
redirect('admin/login');
}
}
}
My model
class Authenticate_model extends CI_Model {
public function login_user($username, $password){
//Secure password
$enc_password = md5($password);
//Validate
$this->db->where('username',$username);
$this->db->where('password',$enc_password);
$result = $this->db->get('users');
if($result->num_rows() == 1){
return $result->row();
} else {
return false;
}
}
}
You can do it yourself, provide validation and set $SESSION variable to retrieve if the user is logged in. But this is too much work, and error prone.
I recommend you to use this popular library: https://github.com/benedmunds/CodeIgniter-Ion-Auth. It is really easy to set up, you just need to copy some files and you are ready to go.
If you really want to do it your self(thats the question) then you need to store in SESSION two variables - logged_in and is_admin.
I would recomend two create library with function:
function is_logged_in($admin = FALSE){
$is_logged = $this->session->userdata('logged_in');
if($admin){
$is_logged = $this->session->userdata('is_admin')
}
return $is_logged;
}
This assumes that you store two booleans "logged_in" and "is_admin" in SESSION.(If user has group_id = 1 then you would store TRUE in is_admin)
Then you can protect your site members only pages
if(!$this->your_authenticatation_library->is_logged_in()){redirect('notMembersControler')}
and admin page:
if(!$this->your_authenticatation_library->is_logged_in(TRUE)){redirect('notMembersControler')}
Thats the basic idea, you need to work around depending on what you are up to. Hope this helps!
I want to redirect 2 different page from this controller function along with value.Here is my code. It works but both of time url become same.what shuld I do?
//in routes.php
Route::post('/','mycontroller#check');
// in controller.php
public function check(Request $request)
{
$c_email = $request->email;
$c_pass=$request->pass;
$c_type=$request->select;
$var=DB::select("SELECT * FROM reg where email = '$c_email' and Password = '$c_pass' and type = '$c_type'");
if ($var) {
return view('farmer')->with('user',$var);
// return redirect('farmer')->with('user',$var);
}
else {
$msg="Invalid login";
return view('index')->with('show',$msg);
}
}
If you want to actually redirect u can use the redirect() helper as statet in the official docs https://laravel.com/docs/5.3/redirects
You can also pass data
redirect('/my-route')->with(['user' => $var]);
The passed data can then be accesses through the session helper
$var = session('user')
HOWEVER, it seems like you have major issues in your code. Your password does not seem to be encrypted. Also there's no reason to use plain sql instead of eloquent here.
The route that is shown in the browser is defined in your
Route::post('/','mycontroller#check');
If you just return different views, the route does not change. You need to redirect to other views.
If you redirect to other routes you will ofcourse need to add / define them.
Route::get('/my-route', function() {}); // or post etc.
I have recently dived into OOP & PHP MVC Application Design. At the moment I am learning a lot but I have one thing that is currently bugging me.
I read and now understand why it isn't wise to place http redirects within a service layer. We do not know what the controller will need to do once the service is complete, etc. etc. I also read that the service should not do anything outside of its purpose. Example: User Registration should only create a new user, using input passed by controller, but I am wondering if it is also fine to set flash messages within the service layer.
My application displays a lot of flash messages session based notifications for users. All of them are based on service related input validation checks, and produce alerts similar to the following
The username xxxxxx is already in use
Usernames Should be > 5 Characters
Should/can this be defined/set within the service class or is there something wrong with that? I have a Alert Helper function that handles setting the alerts. I can easily use my dependency injector to make it available I am just wondering if there is an issue with doing that.
I made the mistake of implementing all redirects within the services and I just finished removing all of them and placing them in the controllers, I don't want to make the same time consuming mistake so I am looking for advice here.
Thank you in advance for the help.
EDIT - CODE EXAMPLE
<?php
/**
*-----------------------------------------------------------------
*
* LOGIN CONTROLLER
*
*/
namespace Controller\www;
use \Helper\Controller;
class Login extends Controller {
public $dependencies = ['arena', 'login', 'site'];
/**
* Login
*
* Login Runs Through Various Checks Including If User is Banned, Account is Locked,
* or Forgot Password Request Is Active. Then the Entered Password is Matched & if Valid
* User is Logged In
*/
public function index() {
// Define Default
$username = '';
/**
* User Login
*
* If Successful, Login User, Redirect Home
* Else Set Error Alerts
*/
if ($this->form->post('login')) {
// Define and Sanitize Post Data
$username = $this->input->get('username');
$password = $this->input->get('password');
// Login Service Layer
$login = $this->factory->make('user/login');
// If Successful Redirect Home - Else Set Errors
if ($login->user($username, $password) === true) {
$this->redirect->home();
}
$this->alert->error($login->get('errors'));
}
/**
* Define Site Title & Display Page
*/
$this->view->sitetitle('login');
$this->view->display('www/login', [
'video' => $this->arena->video(),
'username' => $this->input->set($username)
], ['notifications' => 'user/forgotpassword']);
}
}
Service Layer
/**
*-----------------------------------------------------------------
*
* USER LOGIN SERVICE LAYER
*
*/
namespace Service\User;
use \Helper\Service;
class Login extends Service {
public $dependencies = ['login', 'mail', 'time', 'user', 'vars'];
/**
* Handles Entire Login Process For Site Users
*
* #params all User Submitted Form Data
*/
public function user($username = '', $password = '') {
// Validate $_POST Form Data
$this->validateInput($username, $password);
/**
* No Errors Produced - Complete Form Submission
*
* We Are Not Using `elseif` Between Forgot Password & Normal Login
* After a Forgot Password Code is Generated User May Remember Old Passwords
* We Need to Ensure Users Can Still Login Using Account Password As Well
*/
if (!$this->errors()) {
/**
* User Input Password Matches Account Password
*/
if ($this->input->verifyhash($password, $this->user->get('info.password'))) {
$this->login->user();
return true;
}
/**
* If We Have Not Been Redirected Login Was Unsuccessful
*/
$message = $forgotPW ? 'Forgot Password Code Invalid - Login Lost Incorrect' : 'Login Unsuccessful - Incorrect Username or Password';
$this->log->error($message, ['Username' => $username, 'Password' => $password]);
$this->error('Incorrect Username or Password');
}
/**
* If We Have Made It This Far Login Was Unsuccessful - Log Unsuccessful Attempt
*/
$this->login->logAttempt();
return false;
}
/**
* Validate $_POST Data
*
* #params all User Submitted Form Data
*/
private function validateInput($username = '', $password = '') {
// Display Error if Username is Empty
if (!$username) {
$this->error('Please enter a username');
}
// Display Error if Password is Empty
elseif (!$password) {
$this->error('Please enter a password');
}
// Search DB For User With Matching Username - If User Not Found Display/Log Error, Else Set User
else {
$user = $this->user->info($username, 'username', '', '`userid`');
if (!$user) {
$this->error('The username ' . $username . ' does not exist');
$this->log->error('User Not Found When Attempting to Login', ['username' => $username]);
} else {
$this->user->set('user', $user['userid']);
}
}
}
}
In order to answer your question, I think it's best to break down the concept of MVC into a very basic form, and its individual parts. I apologise in advance if this comes across as being somewhat condescending.
View
The view of the application displays anything and everything. If something is going to be displayed, it should be done in this layer
Controller
The controller is a mediator between the view and the model. It takes input from the view, applies logic/rules to it (where required), and interacts with the model to then get data to pass back to the view.
Model
This is where the loading and saving of data are done. The majority of the validation should have been done as part of the rules in the controller, and this should only pass details of any errors during loading or saving back to the controller should the arise. If there are no errors, it should return the relevant data, or a success status back to the controller.
With those points in mind, the model should not set flash messages to the session, that should be done within the controller depending on the result from the model.
Look at redirects and alerts as specific to one particular form of UI, and it should be obvious that there's no place for them in the Model. Simply always try to picture an alternative interface for your application; e.g. a command line interface for administrative tasks or a REST API. Redirects obviously have no place in either of these alternatives. Alerts are debatable... at the very least the form of the alert will be very different. Your Model will need to be able to pass back some status code to your Controller or View, and then it's the job of the Controller to react to "negative" events and the job of the View to visualise any alerts if necessary.
For example, your model may do something like this:
public function registerUser(User $user) {
...
if (!$successful) {
throw new EmailAlreadyRegisteredException;
}
return true;
}
The controller may then look like this:
public function userRegistration(Request $request) {
try {
$user = User::fromRequest($request);
$this->services->get('Users')->registerUser($user);
$this->view->render('registration_successful', $user);
} catch (InvalidUserData $e) {
$this->view->render('registration_form', $request, $e);
} catch (EmailAlreadyRegisteredException $e) {
$this->view->render('registration_failed', $user, $e);
}
}
The "alert" is passed around as an exception. It's just a method for the Model to signal to its callers what happened. It's up to the callers then to react to and visualise those events. You should certainly not expect any particular type of visualisation in the Model. So you don't want to hardcode specific HTML encoded messages or such. You don't even want to touch human languages at all, that's all the job of the View.
I'm trying to create an account register page with CakePHP 2.0 where user needs to activate it's new account by clicking on a link in the email he's received after insert username, email and password.
My question is how can I set an activation code inside the user record.
I thought to create a table field named activation_code and then to store an hashed version of the username to be sure the user can activate itself by clicking the email link with the activation key.
All the procedure is done but I don't know how can I set the activation_code inside the $data['User'] object and It's not clear for me if this is a good usage of the MVC framework or I should make it in a different way.
During the user registration action I've done this but I get an error when I try to create 'activation_code' dynamically:
// from the UserController class
public function register () {
if (!empty($this->data)) {
if ($this->data['User']['password'] == $this->data['User']['confirm_password']) {
// here is where I get the error
$this->data['User']['activation_key'] = AuthComponent::password($this->data['User']['email']);
$this->User->create();
if ($this->User->save($this->data)) {
// private method
$this->registrationEmail ($this->data['User']['email'], $this->data['User']['username']);
$this->redirect(array('controller'=>'users', 'action'=>'registration', 'success'));
}
}
}
}
Obviously the activation_key is an empty field inside my database.
So how can I create a filed dynamically from the controller?
$this->data['User']['activation_key']
should be:
$this->request->data['User']['activation_key']
(You should change all references to $this->data to the new cakephp2.0 $this->request->data)
I've solved the problem with the method Model::set(), so:
public function register () {
if (!empty($this->data)) {
if ($this->data['User']['password'] == $this->data['User']['confirm_password']) {
$this->User->create();
// I've used set method
$this->User->set('activation_key', AuthComponent::password($this->data['User']['email']));
if ($this->User->save($this->data)) {
$this->registrationEmail ($this->data['User']['email'], $this->data['User']['username']);
$this->redirect(array('controller'=>'users', 'action'=>'registration', 'success'));
}
}
}
}