In my Laravel application, a User can have a Profile which they or a user with privileges can update.
The relation for these two models is defined in this method:
/**
* Get the profile associated with this user
*/
public function profile()
{
return $this->hasOne(Profile::class, 'user_username', 'username');
}
This is the method for updating a user profile:
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Profile $profile
* #return \Illuminate\Http\Response
*/
public function update(UpdateProfile $request, User $user)
{
if ($user) {
// Only proceed if there is a logged in user
$profile = $user->profile;
// If there is no profile, create one for this user as they'll need one.
if (!empty(request()->get('background'))) {
$profile->background = clean($request->get('background'));
}
if (!empty(request()->get('skills'))) {
$profile->skills = clean($request->get('skills'));
}
if (!empty(request()->get('filepath'))) {
$profile->displayPicture = $request->get('filepath');
}
if (!empty(request()->get('linkedInUrl'))) {
$socialProfilesDecoded = json_decode($user->profile->socialProfiles, true);
$socialProfilesDecoded["LinkedIn"] = $request->get('linkedInUrl');
$profile->socialProfiles = json_encode($socialProfilesDecoded);
}
if (!empty(request()->get('twitterUrl'))) {
$socialProfilesDecoded = json_decode($user->profile->socialProfiles, true);
$socialProfilesDecoded["Twitter"] = $request->get('twitterUrl');
$profile->socialProfiles = json_encode($socialProfilesDecoded);
}
$user->profile()->save($profile);
return redirect()->back()->withSuccess('Your profile has been successfully updated');
}
}
The route for updating a profile is:
Route::post('profile/{user}', 'ProfileController#update');
It came to my attention that exposing the username presents a vulnerability as if you're able to grab the request with a web proxy you can just change the username and update another user's profile.
Without changing the URL could I put a Policy in place to check that:
The user has permission to update said profile
The profile being updated is the correct profile (and the request wasn't tampered with.
Or, should I change the URL and have a way to edit profiles in an admin area only?
Also, as a Profile is associated with a User, how could a privileged user access another user's profile?
Maybe a hidden input?
Update:
if ($request->is('admin/*')) {
//
}
Could I check if this matches the POST request?
Update 2
Added a simple check to ensure the logged in user had permissions to update a Profile.
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Profile $profile
* #return \Illuminate\Http\Response
*/
public function update(UpdateProfile $request, User $user)
{
// Check this user
if(auth()->user() == $user || auth()->user()->can('Approve user profile')){
if ($user) {
// Only proceed if there is a logged in user
$profile = $user->profile;
// If there is no profile, create one for this user as they'll need one.
if (!empty(request()->get('background'))) {
$profile->background = clean($request->get('background'));
}
if (!empty(request()->get('skills'))) {
$profile->skills = clean($request->get('skills'));
}
if (!empty(request()->get('filepath'))) {
$profile->displayPicture = $request->get('filepath');
}
if (!empty(request()->get('linkedInUrl'))) {
$socialProfilesDecoded = json_decode($user->profile->socialProfiles, true);
$socialProfilesDecoded["LinkedIn"] = $request->get('linkedInUrl');
$profile->socialProfiles = json_encode($socialProfilesDecoded);
}
if (!empty(request()->get('twitterUrl'))) {
$socialProfilesDecoded = json_decode($user->profile->socialProfiles, true);
$socialProfilesDecoded["Twitter"] = $request->get('twitterUrl');
$profile->socialProfiles = json_encode($socialProfilesDecoded);
}
$user->profile()->save($profile);
return redirect()->back()->withSuccess('Your profile has been successfully updated');
}
}
}
Related
Now i have code data like this:
my const
const CacheUserByUid = 'CacheUserByUid_';
const CacheUserByUsername = 'CacheUserByUsername_';
const CacheUserById = 'CacheUserByUsername_';
Get user data bu uid
/**
* Get user by uid , return user data for user profile
*
* #param $uid
* #return mixed
*/
public function getUserByUid($uid)
{
$result = Yii::$app->cache->getOrSet(self::CacheUserByUid . $uid, function () use ($uid) {
$result = self::find()
->select([
'id',
'username',
'email',
'city',
'country',
'name',
'avatar',
'about',
'uid',
])
->where(['uid' => trim($uid)])
->one();
if (!empty($result)) {
$result->id = (string)$result->id;
}
return $result;
});
return $result;
}
get user data by PK
/**
* #param $userId
* #return mixed
*/
public function getUserById($userId)
{
$user = Yii::$app->cache->getOrSet(self::CacheUserById . $userId, function () use ($userId) {
return self::findOne($userId);
});
return $user;
}
Get user by username
/**
* Get user by username. Return only for user front info
*
* #param $username
* #return array|\yii\db\ActiveRecord|null
*/
public function getUserByUsername($username)
{
$result = Yii::$app->cache->getOrSet(self::CacheUserByUsername . $username, function () use ($username) {
$result = self::find()->select([
'user.id',
'user.city',
'user.country',
'user.name',
'user.avatar',
'user.about',
'user.username'
])
->where(['username' => $username])
->one();
if (!empty($result)) {
$result->id = (string)$result->id;
}
});
return $result;
}
I cached this data. And where user was update i used:
/**
* #param $data
* #param $userId
* #return bool
* #throws \yii\db\Exception
*/
public function updateUser($data, $userId)
{
$user = $this->getUserById($userId);
if (!empty($user)) {
foreach ($data as $key => $name) {
if ($this->hasAttribute($key)) {
$user->$key = $name;
}
}
$user->updatedAt = time();
if ($user->save()) {
//чистимо кеш
FileCache::clearCacheByKey(self::CacheUserByUid . $user->uid);
FileCache::clearCacheByKey(self::CacheUserByUsername . $user->username);
FileCache::clearCacheByKey(self::CacheUserById . $user->id);
return true;
}
}
return false;
}
method clearCacheByKey
/**
* #param $key
*/
public static function clearCacheByKey($key)
{
if (Yii::$app->cache->exists($key)) {
Yii::$app->cache->delete($key);
}
}
Am I good at using a single-user cache that caches these requests in different keys? I don't see any other way out
Is it ok to cache user data in FileCache?
maybe it would be better to use something else for this?
In your case, such simple queries don't need to be cached explicitly. Yii already has a query cache and your requests definitely should be already stored in the cache. The key for data in cache would be a combination of your SQL's md5 with some connection metadata.
Just ensure that everything is configured correctly.
Also if you need to update cached data on some changes, make sure that you're making queries with the best for your case cache dependency. It can purge cached results by some auto condition or you can do it manually from your code(by using TagDependency)
What about FileCache it depends on traffic to your app and current infrastructure. Sometimes there is nothing criminal to store cache in files and you're always can switch to something like Redis/Memcache when your app grow big enough
I want to check a fetched user exists in DB. Suppose I assigned the authenticated user to a variable and the same user is deleted with another variable. But the first variable holds the value of the deleted user. So I want to check whether user data exists in the DB. Something like this.
$user = Auth::user();
$user1 = Auth::user();
$user1->delete();
if($user->existsInDB()){
//
}
If you have a look at the official documentation you will see that there's a fresh method:
The fresh method will re-retrieve the model from the database. The existing model instance will not be affected:
If we have a look at the source code, the fresh method returns null if the model doesn't exists (for example if you create a new Model) or if it can't be found:
/**
* Reload a fresh model instance from the database.
*
* #param array|string $with
* #return static|null
*/
public function fresh($with = [])
{
if (! $this->exists) {
return;
}
return static::newQueryWithoutScopes()
->with(is_string($with) ? func_get_args() : $with)
->where($this->getKeyName(), $this->getKey())
->first();
}
Now in your code you can just add a null check to achive your goal... something like:
$user = Auth::user();
$user1 = Auth::user();
$user1->delete();
if($user->fresh()){
// !== null => it exists
} else {
// === null => it doesn't exists
}
use can do that with method 'find()' like this :
$user=Users:find(Auth::id())
if($user)
{
//user is exist
}
I am trying to create a login panel with laravel and finding it difficult to understand why my page is not redirecting to any page
this is the content of my routes.php
Route::get('/myapp/', 'MyApp#index');
//route to show the login form
Route::get('/myapp/login', 'MyApp#login');
//route to process the login form
Route::post('/myapp/login', 'MyApp#doLogin');
//route to show the registration form
Route::get('/myapp/register', 'MyApp#register');
//route to process the registration form
Route::post('/myapp/register', 'MyApp#saveRegister');
//route to show the reset password page
Route::get('/myapp/resetpassword','MyApp#resetpassword');
//route to process the password reset request
Route::post('/myapp/resetpassword', 'MyApp#doReset');
Here is the full code to controller
<?php
class MyApp extends BaseController
{
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
return View::make('myapp.index');
}
public function register()
{
return View::make('myapp.register');
}
/**
* Insert the data into database.
*
* #return Response
*/
public function saveRegister()
{
$input = Input::all();
//validate the form data provided by the user
$rules = array(
'username' => 'required|alphaNum|min:6',
'password' => 'required|alphaNum|min:6',
'email' => 'required|email',
'phone' => 'required',
);
//now validate the above rules
$validator = Validator::make($input,$rules);
//If validator fails send user back to registration form
if($validator->fails())
{
return Redirect::back()//if validation fails redirect back to registration page
->withErrors($validator)//send back all the errors to the form
->withInput(Input::except('password')//send all data back to form except password
);
}
else
{
$password = $input['password'];
$password = Hash::make($password);
$myapp = new User;
$myapp->username = $input['username'];
$myapp->password = $password;
$myapp->email = $input['email'];
$myapp->phone = $input['phone'];
$myapp->save();
return Redirect::to('myapp')->with('success','Registration successful');
}
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function login()
{
return View::make('myapp.login');
}
public function doLogin()
{
$input = Input::all();
$rules = array(
'username' => 'required',
'password' => 'required'
);
//ow validate the rules
$validator = Validator::make($input,$rules);
//if validator passed
if($validator->fails())
{
return Redirect::back()//if validation fails redirect back to registration page
->withErrors($validator)//send back all the errors to the form
->withInput(Input::except('password')//send all data back to form except password
);
}
else
{
$username = Input::get('username');
$password = Hash::make(Input::get('password'));
$userdata = array(
'username' => $username,
'password' => $password
);
//Attempt to do the login
if($entry=Auth::attempt($userdata))
{
// validation successful!
// redirect them to the secure section or whatever
// return Redirect::to('secure');
// for now we'll just echo success (even though echoing in a controller is bad)
return Redirect::to('myapp')->with('message','Success');
}
else
{
return Redirect::to('myapp/fail');
/*return Redirect::back()
->withErrors('message','Wrong username or password')
->withInput(Input::except('password'));*/
}
}
}
/**
* Display the reset password page.
*
* #return Response
*/
public function resetpassword()
{
return View::make('myapp.resetpassword');
}
public function doReset()
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
//
}
}
Now my page redirects to myapp/fail which means it is not able to authenticate the data with the data table
In the else part // if validations ok
$username = Input::get('username');
$password = Input::get('password');
$userdata = array(
'username' => $username,
'password' => $password
);
if(Auth::attempt($userdata)) ....
remove Hash::make part and get the password as $password = Input::get('password');
I have the issue why my form was not redirecting to the desired page even if the data I was providing was correct. As you can see in MyApp controller I have function named saveRegister which was inserting data into users table after hashing the password I have supplied in the form
$password = Hash::make($password);
Now in my doLogin function what I was doing is Hashing the password to validate even though the Auth function of laravel hashes the password on its own so I need not to hash my password manually to match it with database.
I was wondering how I could add a check during user login with Confide, to check if a user account is flagged as disabled?
In the Confide class there's the following function:
/**
* Attempt to log a user into the application with
* password and identity field(s), usually email or username.
*
* #param array $credentials
* #param bool $confirmed_only
* #param mixed $identity_columns
* #return boolean Success
*/
public function logAttempt( $credentials, $confirmed_only = false, $identity_columns = array() )
{
// If identity columns is not provided, use all columns of credentials
// except password and remember.
if(empty($identity_columns))
{
$identity_columns = array_diff(
array_keys($credentials),
array('password','remember')
);
}
// Check for throttle limit then log-in
if(! $this->reachedThrottleLimit( $credentials ) )
{
$user = $this->repo->getUserByIdentity($credentials, $identity_columns);
if(
$user &&
($user->confirmed || ! $confirmed_only ) &&
$this->app['hash']->check(
$credentials['password'],
$user->password
)
)
{
$remember = isset($credentials['remember']) ? $credentials['remember'] : false;
$this->app['auth']->login( $user, $remember );
return true;
}
}
$this->throttleCount( $credentials );
return false;
}
All I would really like to do is add a check for $user->disabled which is a field I created. Short of extending and registering a new service provider, does anyone know of another way of doing this?
I am new to Magento and PHP. I use the following line to get the email, which works fine except in the case a customer just registered. Any suggestions? Thanks.
$userEmail = Mage::getSingleton('customer/session')->getCustomer()->getEmail();
I assume that this code runs before the customer object data was saved propperly.
There is a line of code callled: in the OnePageCheckout and it does the following:
/**
* Involve new customer to system
*
* #return Mage_Checkout_Model_Type_Onepage
*/
protected function _involveNewCustomer()
{
$customer = $this->getQuote()->getCustomer();
if ($customer->isConfirmationRequired()) {
$customer->sendNewAccountEmail('confirmation', '', $this->getQuote()->getStoreId());
$url = Mage::helper('customer')->getEmailConfirmationUrl($customer->getEmail());
$this->getCustomerSession()->addSuccess(
Mage::helper('customer')->__('Account confirmation is required. Please, check your e-mail for confirmation link. To resend confirmation email please click here.', $url)
);
} else {
$customer->sendNewAccountEmail('registered', '', $this->getQuote()->getStoreId());
$this->getCustomerSession()->loginById($customer->getId());
}
return $this;
}
If the customer is just registering, not using the checkout process, then there is a different function using the request parameters like Anton said:
/app/code/core/Mage/Customer/Block/Form/Register.php
/**
* Restore entity data from session
* Entity and form code must be defined for the form
*
* #param Mage_Customer_Model_Form $form
* #return Mage_Customer_Block_Form_Register
*/
public function restoreSessionData(Mage_Customer_Model_Form $form, $scope = null)
{
if ($this->getFormData()->getCustomerData()) {
$request = $form->prepareRequest($this->getFormData()->getData());
$data = $form->extractData($request, $scope, false);
$form->restoreData($data);
}
return $this;
}
you can get it from request parameters directly?