I'm using Cartalyst Sentinel for the user authentication within Laravel.
I've created my form to add a new user.
For some strange reason the password does not come through Sentinel::register() unless I put the password field into the $fillable array in the User class model.
This is a potential security issue.
How can I get around this? There must be something I am missing when creating a new user with Sentinel (and the Sentinel documents are very light on useful info).
Just a quick rundown of what I'm doing code wise. I have my array filled with the fields that are required to create a user. This array is passed into Sentinel::register(). It all seems to go through fine, but when I go to look in the database, the password field is blank.
$newUser = array(
'_token' => Input::get('_token'),
'email' => Input::get('email'),
'password' => Input::get('password'),
'first_name' => Input::get('first_name'),
'middle_name' => Input::get('middle_name'),
'last_name' => Input::get('last_name'));
$user = Sentinel::register($newUser);
Just a side note: unfortunately I cannot switch the authentication system. I need to use Sentinel.
Just another way of doing almost same as Jeff's answer.
This should work based on Sentinel code, tho i have not used Sentinel. Test before deploying.
$newUser = array(
'_token' => Input::get('_token'),
'email' => Input::get('email'),
'password' => Input::get('password'),
'first_name' => Input::get('first_name'),
'middle_name' => Input::get('middle_name'),
'last_name' => Input::get('last_name')
);
Sentinel::register($newUser, function($user) use ($newUser) {
try {
return $user->password = \Hash::make($newUser['password']);
} catch(RuntimeException $e) {
return false;
}
});
Callback runs after fill method, so it should bypass $fillable restriction and you can remove password from fillable if your design requires that.
If false returned in callback then user will not be created.
You'd need to set the password option manually on a new user if you don't want to make it a fillable property.
$newUser = array(
'_token' => Input::get('_token'),
'email' => Input::get('email'),
'first_name' => Input::get('first_name'),
'middle_name' => Input::get('middle_name'),
'last_name' => Input::get('last_name'));
$user = Sentinel::register($newUser);
$user->password = Input::get('password');
$user->save();
You probably need to be hashing the password before you set it too, right? Something like $user->password = \Hash::make(Input::get('password'));. Unless Sentinel does that automatically.
Related
In my application, I have tables related to users and some of these tables require a row be inserted as soon as a User is registered.
For example, I have the following models:
UserProfile,
UserAssignedRole,
UserSettings
As soon as a User successfully registers, they will need a corresponding row in the DB.
I've done the following:
protected function create(array $data)
{
$user = User::create([
'username' => $data['username'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
UserProfile::create([
'user_id' => $user->id,
]);
UserAssignedRole::create([
'user_id' => $user->id,
'role_id' => 1
]);
UserSettings::create([
'user_id' => $user->id,
'nsfw' => 1
]);
return $user;
}
I'm looking for a better and more elegant way to achieve this. Is there a "Laravel" way in achieving this?
In the Larvel docs there's a chapter about Recursively Saving Models & Relationships. This is probably what you're looking for. They specify the following example:
$post = App\Post::find(1);
$post->comments[0]->message = 'Message';
$post->comments[0]->author->name = 'Author Name';
$post->push();
You could achieve something similar by creating a user including all its relationships in one push().
On my current project (a school management system) I want to give admins the ability to register users. Admins can create courses and subjects for example, which I've managed to do using resource controllers. However, I thought I could do the same for users, since the process appears the same to me. That is: I can show, edit, create, update and delete users.
However, I've run into several problems so far. Right now I can create users, but not update them.
Here's my code:
web.php
Route::middleware(['auth', 'admin'])->group(function () {
Route::get('/admin', 'HomeController#admin');
Route::post('register', 'UserController#store');
Route::resources([
'admin/cursos' => 'CursoController',
'admin/turmas' => 'TurmaController',
'admin/semestres' => 'SemestreController',
'admin/materias' => 'MateriaController',
'admin/usuarios' => 'UserController',
]);
});
UserController.php
public function update(Request $request, $id)
{
$rules = array(
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'role' => 'required|string',
'password' => 'required|string|min:6|confirmed',
);
$validator = validator::make(Input::all(), $rules);
if ($validator->fails()) {
// return dd();
return Redirect::to('/admin/usuarios/' . $id . '/edit')
->withErrors($validator);
} else {
// store
$user = User::find($id);
$user->name = Input::get('name');
$user->email = Input::get('email');
$user->role = Input::get('role');
$user->password = Input::get('password');
$user->save();
// redirect
Session::flash('message', 'Sucesso!');
return Redirect::to('/admin/usuarios');
}
}
Validation fails every time I try to update user information. What exactly is going on here? I'm relatively new to Laravel, so I'm a bit lost now.
If the request is failing when a user is trying to update their information without changing the email address, you need additional logic to ignore the id for user associated with the email.
Sometimes, you may wish to ignore a given ID during the unique check. For example, consider an "update profile" screen that includes the user's name, e-mail address, and location. Of course, you will want to verify that the e-mail address is unique. However, if the user only changes the name field and not the e-mail field, you do not want a validation error to be thrown because the user is already the owner of the e-mail address.
To instruct the validator to ignore the user's ID, we'll use the Rule class to fluently define the rule. In this example, we'll also specify the validation rules as an array instead of using the | character to delimit the rules:
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore($user->id),
],
]);
Applied to your set of validation rules it would look like:
$rules = array(
'name' => 'required|string|max:255',
'email' => [
'required',
'string',
'email,
'max:255',
Rule::unique('users')->ignore(auth()->id())
],
'role' => 'required|string',
'password' => 'required|string|min:6|confirmed',
);
You have to except the User ID ($id) in email validation, since u use "unique" rule.
you can check the guide in here
https://laravel.com/docs/5.6/validation#rule-unique
I'm using Laravel 5.5 and I have modified the registration process recently. This is the registration:
$user = User::create([
'gender' => $data['gender'],
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'email' => $data['email'],
'status' => 'inactive',
'password' => Hash::make($data['password'])
]);
which works fine so far. However, when I try to log in with the account I recently had created, it'd always tell me that was the wrong password and I don't understand why. I'm not exactly sure whether it was Hash::make() or bcrypt() originally and since they're different, I suppose there must be something wrong with the login.
If I bcrypt a password using Tinker and insert it into the DB, I'm able to login. I thought of modifying the "login code", however, I wouldn't find anything similiar to the registration looking process which would let me alter the used hash algorithm for login, so what's the approach in this case?
Edit: I placed this code above the $user = User::create([]):
dd($data['password'], bcrypt($data['password']), Hash::make($data['password']));
and this is the output:
"testtesttest123456"
"$2y$10$y9bl5muW5AmmMZMMEWL0Qucy7RSfCSzgWXl29PiX2gPRFd3jnNeEC"
"$2y$10$tez1W8fIwpksgpjsZmQqPuYIN4QTtiddhaCnc5zQ2MgeYATiQd9Ym"
The user model (as per request):
class User extends Authenticatable
{
use Notifiable, HasRoles;
const STATE_ACTIVE = 'active';
const STATE_INACTIVE = 'inactive';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'first_name', 'last_name', 'email', 'password', 'status', 'profile_pic', 'api_token', 'activation_code'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function setPasswordAttribute($password)
{
$this->attributes['password'] = bcrypt($password);
}
public function setEmailAttribute($email)
{
$this->attributes['email'] = strtolower($email);
}
}
You are double hashing the password. You have a mutator that is hashing the password. You are also hashing the password before passing it to create.
You have the choice of removing that mutator or not hashing the password before assigning it to the model (as assigning it causes the mutator to run). Either one is fine you just have to know which way you are going.
Most likely there is only going to be 2 places a password is getting hashed anyway, so its really not that big of a concern which way you go.
Registration and perhaps a change password/profile type route are basically the only places you will be hashing the password.
If you haven't done anything to the login/registration hasing in the controllers then this should work
$user = User::create([
'gender' => $data['gender'],
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'email' => $data['email'],
'status' => 'inactive',
'password' => bcrypt($data['password'])
]);
I want to create a user if not matched:
$newUser=User::firstOrCreate(array('name' => $user->name, 'email' => $user->email,'avatar'=>$user->avatar));
But, i also need to store token which will be different every time even if other attributes matches. So, i need something like match with only name, email, avatar but also store or update token.
You can do it in this way:
$newUser = User::firstOrCreate([
'name' => $user->name,
'email' => $user->email,
'avatar' => $user->avatar
]);
$newUser->token = bin2hex(openssl_random_pseudo_bytes(16));
$newUser->save();
Hope this helps!
Pass an array of additional values as second parameter:
$newUser = User::firstOrCreate(
['name' => $user->name, 'email' => $user->email, 'avatar' => $user->avatar],
['token' => $token]
);
To understand how it works look at source code of firstOrCreate() method.
I have a login form with
username, password and remember me
remember me is a checkbox (true or false).
How do I create the validation rule in Laravel?
http://laravel.com/docs/validation#basic-usage
The only relevant one it seems was in and you specify the values but the values in this case are booleans and using this method they would be specified as string?
in:true,false
There's a validator for boolean. Assuming you're using one of the packages that simplifies model validation, e.g. EsensiModel, it's as simple as adding the following to your Model:
protected $rules = [
'email' => 'required|email',
'password' => 'required',
'remember_me' => 'boolean',
];
You may try something like this:
$rules = array('email' => 'required|email', 'password' => 'required');
$inputs = array(
'email' => Input::get('email'),
'password' => Input::get('password')
);
$validator = Validator::make($inputs, $rules);
if($validator->fails()) {
return Redirect::back()->withInput()->withErrorts($validator);
}
else {
$remember = Input::get('remember', FALSE);
if(Auth::attempt($inputs, !!$remember)) {
// Log in successful
return Redirect::to('/'); // redirect to home or wherever you want
}
}
I've used email which is recommended but if you use username other than email then just change the email to username and in the rule for username use something like this:
'username' => 'required|alpha|min:6' // Accepts only a-z and minimum 6 letters