Sindbad~EG File Manager
<?php namespace Common\Auth;
use Common\Auth\User;
use Exception;
use Common\Settings\Settings;
use View, Auth, Session;
use Laravel\Socialite\Contracts\Factory as SocialiteFactory;
class Oauth {
private $validProviders = ['google', 'facebook', 'twitter', 'envato'];
/**
* @var User
*/
private $user;
/**
* @var SocialiteFactory
*/
private $socialite;
/**
* @var UserRepository
*/
private $userCreator;
/**
* @var Settings
*/
private $settings;
/**
* @param SocialiteFactory $socialite
* @param UserRepository $userCreator
* @param Settings $settings
*/
public function __construct(SocialiteFactory $socialite, UserRepository $userCreator, Settings $settings)
{
$this->user = app(config('common.site.user_model'));
$this->settings = $settings;
$this->socialite = $socialite;
$this->userCreator = $userCreator;
}
/**
* Log user in with provider service or throw 404 if service is not valid.
*
* @param string $provider
* @return mixed
*/
public function loginWith($provider)
{
if (Auth::user()) {
return View::make('common::oauth/popup')->with('status', 'ALREADY_LOGGED_IN');
}
return $this->connect($provider);
}
/**
* Connect currently logged in user to specified social account.
*
* @param string $provider
* @return mixed
*/
public function connect($provider)
{
$this->validateProvider($provider);
return $this->socialite->with($provider)->redirect();
}
/**
* Disconnect specified social social account from currently logged in user.
*
* @param string $provider
* @return mixed
*/
public function disconnect($provider)
{
$this->validateProvider($provider);
return Auth::user()->social_profiles()->where('service_name', $provider)->delete();
}
/**
* Get user profile from specified social provider or throw 404 if it's invalid.
*
* @param string $provider
* @return mixed
*/
public function socializeWith($provider)
{
$this->validateProvider($provider);
$user = $this->socialite->with($provider)->user();
//persist envato purchases in session if user is signing in
//with envato, so we can attach them to user later
if ($provider === 'envato' && ! empty($user->purchases)) {
$this->persistSocialProfileData(['envato_purchases' => $user->purchases]);
}
return $user;
}
/**
* Return existing social profile from database for specified external social profile.
*
* @param $profile
* @return SocialProfile|null
*/
public function getExistingProfile($profile)
{
if ( ! $profile) return null;
return SocialProfile::where('user_service_id', $this->getUsersIdentifierOnService($profile))->with('user')->first();
}
/**
* Return user matching given email from database.
*
* @param string $email
* @return User|null
*/
public function findUserByEmail($email = null)
{
if ( ! $email) return;
return $this->user->where('email', $email)->first();
}
/**
* Create a new user from given social profile and log him in.
*
* @param array $data
* @param bool $returnView
* @return mixed
*/
public function createUserFromOAuthData($data, $returnView = false)
{
$profile = $data['profile'];
$service = $data['service'];
$user = $this->findUserByEmail($profile->email);
//create a new user if one does not exist with specified email
if ( ! $user) {
$img = str_replace('http://', 'https://', $profile->avatar);
$user = $this->userCreator->create(['email' => $profile->email, 'avatar' => $img]);
}
//save this social profile data, so we can login the user easily next time
$user->social_profiles()->create($this->transformSocialProfileData($service, $profile, $user));
//save data about user supplied envato purchase code
if ($purchases = $this->getPersistedData('envato_purchases')) {
$user->updatePurchases($purchases, $profile->nickname);
}
return $this->logUserIn($user, $returnView);
}
/**
* Attach specified social profile to user.
*
* @param User $user
* @param Object $profile
* @param string $service
*
* @return \Illuminate\Contracts\View\View
*/
public function attachProfileToExistingUser($user, $profile, $service)
{
$payload = $this->transformSocialProfileData($service, $profile, $user);
//if this social account is already attached to some user
//we will re-attach it to specified user
if ($existing = $this->getExistingProfile($profile)) {
$existing->forceFill($payload)->save();
//if social account is not attached to any user, we will
//create a model for it and attach it to specified user
} else {
$user->social_profiles()->create($payload);
}
//save data about user supplied envato purchase code
if ($purchases = $this->getPersistedData('envato_purchases')) {
$user->updatePurchases($purchases, $profile->nickname);
}
return $this->getPopupResponse('SUCCESS_CONNECTED', ['user' => $user->load('social_profiles', 'roles')->toArray()]);
}
/**
* Transform social profile into data acceptable by SocialProfile model.
*
* @param string $service
* @param Object $profile
* @param User $user
*
* @return array
*/
private function transformSocialProfileData($service, $profile, $user)
{
return $payload = [
'service_name' => $service,
'user_service_id' => $this->getUsersIdentifierOnService($profile),
'user_id' => $user->id,
'username' => $profile->name,
];
}
/**
* Log given user into the app and return
* a view to close popup in front end.
*
* @param User $user
* @param bool $returnView
*
* @return string|array
*/
public function logUserIn($user, $returnView = true)
{
$user = Auth::loginUsingId($user->id, true)->load('roles')->toArray();
if ($returnView) {
return $this->getPopupResponse('SUCCESS', ['user' => $user]);
} else {
return $user;
}
}
/**
* Get a list of extra credentials that we need to request user
* in order finalize his sign in with social service.
*
* @param object $profile
* @param string $service
* @return array
*/
public function getCredentialsThatNeedToBeRequested($profile, $service)
{
$credentialsToRequest = [];
//if this service didn't return user email, we'll need to request it
if ( ! isset($profile->email)) {
$credentialsToRequest[] = 'email';
}
//if we din't create a social profile for this service yet, but
//user with returned email already exists in database, ask user to
//enter password, and if it matches connect account to service
if (isset($profile->email) && $user = $this->findUserByEmail($profile->email)) {
$credentialsToRequest[] = 'password';
}
return $credentialsToRequest;
}
/**
* Request user specified extra credentials.
*
* @param array $credentials
* @param string $service
* @param Object $profile
*
* @return \Illuminate\Contracts\View\View
*/
public function requestExtraCredentials($credentials, $service, $profile)
{
$this->persistSocialProfileData([
'service' => $service,
'profile' => $profile,
'requested_extra_credentials' => $credentials
]);
return $this->getPopupResponse('REQUEST_EXTRA_CREDENTIALS', $credentials);
}
/**
* Validate extra credentials supplied by user.
*
* @param array $input
* @return array
*/
public function validateExtraCredentials($input)
{
//get a list of credentials that we've requested from user
$credentials = $this->getPersistedData('requested_extra_credentials');
$errors = [];
//validate password supplied by user against existing account or supplied email address
if (in_array('password', $credentials) && ! $this->callbackPasswordIsValid($input)) {
$errors['password'] = 'Incorrect password. Please try again.';
}
//if email user supplied already exists and user did not supply a password, show an error
if ( ! isset($input['password']) && in_array('email', $credentials) && $this->findUserByEmail($input['email'])) {
$errors['email'] = 'Email already exists. Please specify password.';
}
return $errors;
}
/**
* Get error response with option error message.
*
* @param string $message
* @return \Illuminate\Http\Response
*/
public function getErrorResponse($message = null)
{
return response()->view('common::oauth.popup', ['status' => 'ERROR', 'data' => $message], 500);
}
/**
* Get oauth data persisted in current session.
*
* @param string $key
* @return mixed
*/
public function getPersistedData($key = null)
{
//test session when not logged, what if multiple users log in at same time etc
$data = Session::get('social_profile');
if ( ! $key) return $data;
if ($key && isset($data[$key])) {
return $data[$key];
}
}
/**
* Validate that the password we requested from
* user matches the one on account stored in session
*
* @param array $credentials
* @return bool
*/
public function callbackPasswordIsValid($credentials)
{
try {
if ( ! isset($credentials['email'])) {
$credentials['email'] = $this->getPersistedData('profile')->email;
}
return Auth::validate([
'email' => $credentials['email'],
'password' => $credentials['password']
]);
} catch (Exception $e) {
return false;
}
}
/**
* Store specified social profile information in the session
* for use in subsequent social login process steps.
*
* @param array $data
*/
private function persistSocialProfileData($data)
{
foreach($data as $key => $value) {
Session::put("social_profile.$key", $value);
}
}
/**
* Check if provider user want to login with is valid, if not throw 404
* @param string $provider
*/
private function validateProvider($provider)
{
if ( ! in_array($provider, $this->validProviders)) {
abort(404);
}
}
/**
* Get users unique identifier on social service from given profile.
*
* @param Object $profile
* @return string|integer
*/
private function getUsersIdentifierOnService($profile)
{
return isset($profile->id) && $profile->id ? $profile->id : $profile->email;
}
/**
* Return response to frontend social login popup.
*
* @param string $status
* @param mixed $data
*
* @return \Illuminate\Contracts\View\View
*/
private function getPopupResponse($status, $data = null)
{
$view = View::make('common::oauth/popup')->with('status', $status);
if ($data) {
$view->with('data', json_encode($data));
}
return $view;
}
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists