Social Login Feature
* Added 3rd party identity providers: * Facebook * Google * Linkedin * Apple * UI changes ( 2 steps login) * ReactJS integration * Webpack Update * Meta Document update (oauth2/.well-known/openid-configuration) * Added provider param on oauth2 flow Depends-On: https://review.opendev.org/c/osf/openstackid/+/772531 Change-Id: I86cef9379fcd6ca5320f080e062fc2abaa36203c
This commit is contained in:
parent
adcd76aca2
commit
91c69eb54b
17
.babelrc
17
.babelrc
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"presets": [
|
|
||||||
[
|
|
||||||
"env",
|
|
||||||
{
|
|
||||||
"targets": {
|
|
||||||
"node": "current"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"flow",
|
|
||||||
"react"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"transform-object-rest-spread"
|
|
||||||
]
|
|
||||||
}
|
|
20
.env.example
20
.env.example
|
@ -79,4 +79,22 @@ RABBITMQ_SSL=true
|
||||||
RABBITMQ_SSL_CAFILE=/certs/rabbit/ca-osf.pem
|
RABBITMQ_SSL_CAFILE=/certs/rabbit/ca-osf.pem
|
||||||
RABBITMQ_SSL_LOCALCERT=/certs/rabbit/client-cert-osf.pem
|
RABBITMQ_SSL_LOCALCERT=/certs/rabbit/client-cert-osf.pem
|
||||||
RABBITMQ_SSL_LOCALKEY=/certs/rabbit/client-key-osf.pem
|
RABBITMQ_SSL_LOCALKEY=/certs/rabbit/client-key-osf.pem
|
||||||
RABBITMQ_SSL_VERIFY_PEER=false
|
RABBITMQ_SSL_VERIFY_PEER=false
|
||||||
|
|
||||||
|
# 3rd party idps
|
||||||
|
|
||||||
|
FACEBOOK_CLIENT_ID=
|
||||||
|
FACEBOOK_CLIENT_SECRET=
|
||||||
|
FACEBOOK_REDIRECT_URI=/auth/login/facebook/callback
|
||||||
|
|
||||||
|
GOOGLE_CLIENT_ID=
|
||||||
|
GOOGLE_CLIENT_SECRET=
|
||||||
|
GOOGLE_REDIRECT_URI=/auth/login/google/callback
|
||||||
|
|
||||||
|
APPLE_CLIENT_ID=
|
||||||
|
APPLE_CLIENT_SECRET=
|
||||||
|
APPLE_REDIRECT_URI=/auth/login/apple/callback
|
||||||
|
|
||||||
|
LINKEDIN_CLIENT_ID=
|
||||||
|
LINKEDIN_CLIENT_SECRET=
|
||||||
|
LINKEDIN_REDIRECT_URI=/auth/login/linkedin/callback
|
|
@ -42,3 +42,6 @@ routes.txt
|
||||||
model.sql
|
model.sql
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
!/public/web.config
|
!/public/web.config
|
||||||
|
/public/assets/dist/
|
||||||
|
/public/assets/login.js
|
||||||
|
/public/assets/css/login.css
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php namespace App\Http\Controllers\Api;
|
<?php namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2015 OpenStack Foundation
|
* Copyright 2015 OpenStack Foundation
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -13,10 +12,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Controllers\Traits\JsonResponses;
|
||||||
use Utils\Services\ILogService;
|
use Utils\Services\ILogService;
|
||||||
use Illuminate\Support\Facades\Response;
|
|
||||||
use Illuminate\Support\Facades\Request;
|
|
||||||
use Exception;
|
|
||||||
/**
|
/**
|
||||||
* Class JsonController
|
* Class JsonController
|
||||||
* @package App\Http\Controllers
|
* @package App\Http\Controllers
|
||||||
|
@ -30,74 +27,5 @@ abstract class JsonController extends Controller {
|
||||||
$this->log_service = $log_service;
|
$this->log_service = $log_service;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function error500(Exception $ex){
|
use JsonResponses;
|
||||||
$this->log_service->error($ex);
|
|
||||||
return Response::json(array( 'error' => 'server error'), 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function created($data='ok'){
|
|
||||||
$res = Response::json($data, 201);
|
|
||||||
//jsonp
|
|
||||||
if(Request::has('callback'))
|
|
||||||
$res->setCallback(Request::input('callback'));
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function updated($data = 'ok', $has_content = true)
|
|
||||||
{
|
|
||||||
$res = Response::json($data, $has_content ? 201 : 204);
|
|
||||||
//jsonp
|
|
||||||
if (Request::has('callback')) {
|
|
||||||
$res->setCallback(Request::input('callback'));
|
|
||||||
}
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function deleted($data='ok'){
|
|
||||||
$res = Response::json($data, 204);
|
|
||||||
//jsonp
|
|
||||||
if(Request::has('callback'))
|
|
||||||
$res->setCallback(Request::input('callback'));
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function ok($data = 'ok'){
|
|
||||||
$res = Response::json($data, 200);
|
|
||||||
//jsonp
|
|
||||||
if(Request::has('callback'))
|
|
||||||
$res->setCallback(Request::input('callback'));
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function error400($data = ['message' => 'Bad Request']){
|
|
||||||
return Response::json($data, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function error404($data = array('message' => 'Entity Not Found')){
|
|
||||||
return Response::json($data, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function error403($data = array('message' => 'Forbidden'))
|
|
||||||
{
|
|
||||||
return Response::json($data, 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {
|
|
||||||
"message": "Validation Failed",
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"resource": "Issue",
|
|
||||||
"field": "title",
|
|
||||||
"code": "missing_field"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
* @param $messages
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function error412($messages){
|
|
||||||
|
|
||||||
return Response::json(array('error'=>'validation' , 'messages' => $messages), 412);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -25,7 +25,6 @@ use OAuth2\Factories\OAuth2AuthorizationRequestFactory;
|
||||||
use OAuth2\OAuth2Message;
|
use OAuth2\OAuth2Message;
|
||||||
use OAuth2\Repositories\IClientRepository;
|
use OAuth2\Repositories\IClientRepository;
|
||||||
use OAuth2\Services\IMementoOAuth2SerializerService;
|
use OAuth2\Services\IMementoOAuth2SerializerService;
|
||||||
use Sokil\IsoCodes\IsoCodesFactory;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
/**
|
/**
|
||||||
* Class RegisterController
|
* Class RegisterController
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
<?php namespace App\Http\Controllers;
|
||||||
|
/**
|
||||||
|
* Copyright 2021 OpenStack Foundation
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
use App\libs\Auth\SocialLoginProviders;
|
||||||
|
use App\Services\Auth\IUserService;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Laravel\Socialite\Facades\Socialite;
|
||||||
|
use models\exceptions\ValidationException;
|
||||||
|
use Strategies\ILoginStrategy;
|
||||||
|
use Strategies\ILoginStrategyFactory;
|
||||||
|
use Utils\Services\IAuthService;
|
||||||
|
/**
|
||||||
|
* Class SocialLoginController
|
||||||
|
* @package App\Http\Controllers
|
||||||
|
*/
|
||||||
|
final class SocialLoginController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var IAuthService
|
||||||
|
*/
|
||||||
|
private $auth_service;
|
||||||
|
/**
|
||||||
|
* @var IUserService
|
||||||
|
*/
|
||||||
|
private $user_service;
|
||||||
|
/**
|
||||||
|
* @var ILoginStrategy
|
||||||
|
*/
|
||||||
|
private $login_strategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SocialLoginController constructor.
|
||||||
|
* @param IAuthService $auth_service
|
||||||
|
* @param IUserService $user_service
|
||||||
|
* @param ILoginStrategyFactory $login_strategy_factory
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
IAuthService $auth_service,
|
||||||
|
IUserService $user_service,
|
||||||
|
ILoginStrategyFactory $login_strategy_factory
|
||||||
|
){
|
||||||
|
$this->auth_service = $auth_service;
|
||||||
|
$this->user_service = $user_service;
|
||||||
|
$this->middleware(function ($request, $next) use($login_strategy_factory){
|
||||||
|
// we do it here just to ensure that user session is loaded
|
||||||
|
Log::debug(sprintf("SocialLoginController::middleware"));
|
||||||
|
$this->login_strategy = $login_strategy_factory->build();
|
||||||
|
return $next($request);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $provider
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Symfony\Component\HttpFoundation\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function redirect($provider)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Log::debug(sprintf("SocialLoginController::redirect provider %s", $provider));
|
||||||
|
if(!SocialLoginProviders::isSupportedProvider($provider))
|
||||||
|
throw new ValidationException(sprintf("Provider %s is not supported.", $provider));
|
||||||
|
return Socialite::driver($provider)->redirect();
|
||||||
|
}
|
||||||
|
catch (\Exception $ex){
|
||||||
|
Log::error($ex);
|
||||||
|
}
|
||||||
|
return view("auth.register_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $provider
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|mixed
|
||||||
|
*/
|
||||||
|
public function callback($provider)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Log::debug(sprintf("SocialLoginController::callback provider %s", $provider));
|
||||||
|
// validate provider
|
||||||
|
if(!SocialLoginProviders::isSupportedProvider($provider))
|
||||||
|
throw new ValidationException(sprintf("Provider %s is not supported.", $provider));
|
||||||
|
|
||||||
|
$social_user = Socialite::driver($provider)->user();
|
||||||
|
// try to get user by primary email from our db
|
||||||
|
Log::debug(sprintf("SocialLoginController::callback provider %s trying to get user using email %s", $provider, $social_user->getEmail()));
|
||||||
|
|
||||||
|
$user = $this->auth_service->getUserByUsername($social_user->getEmail());
|
||||||
|
|
||||||
|
if (is_null($user)) {
|
||||||
|
Log::debug(sprintf("SocialLoginController::callback provider %s user does not exists for email %s, creating ...", $provider, $social_user->getEmail()));
|
||||||
|
// if does not exists , registered it with email verified and active
|
||||||
|
|
||||||
|
|
||||||
|
$user = $this->user_service->registerUser([
|
||||||
|
'email' => $social_user->getEmail(),
|
||||||
|
'full_name' => $social_user->getName(),
|
||||||
|
'external_pic' => $social_user->getAvatar(),
|
||||||
|
'external_id' => $social_user->getId(),
|
||||||
|
'email_verified' => true,
|
||||||
|
'active' => true,
|
||||||
|
'external_provider' => $provider
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do login
|
||||||
|
Auth::login($user, true);
|
||||||
|
// and continue the usual flow
|
||||||
|
return $this->login_strategy->postLogin([ 'provider'=> $provider ]);
|
||||||
|
}
|
||||||
|
catch (\Exception $ex){
|
||||||
|
Log::error($ex);
|
||||||
|
}
|
||||||
|
return view("auth.register_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php namespace App\Http\Controllers\Traits;
|
||||||
|
/**
|
||||||
|
* Copyright 2019 OpenStack Foundation
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Request;
|
||||||
|
use Illuminate\Support\Facades\Response;
|
||||||
|
use Exception;
|
||||||
|
/**
|
||||||
|
* Trait JsonResponses
|
||||||
|
* @package App\Http\Controllers\Traits
|
||||||
|
*/
|
||||||
|
trait JsonResponses
|
||||||
|
{
|
||||||
|
protected function error500(Exception $ex){
|
||||||
|
Log::error($ex);
|
||||||
|
return Response::json(array( 'error' => 'server error'), 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function created($data='ok'){
|
||||||
|
$res = Response::json($data, 201);
|
||||||
|
//jsonp
|
||||||
|
if(Request::has('callback'))
|
||||||
|
$res->setCallback(Request::input('callback'));
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function updated($data = 'ok', $has_content = true)
|
||||||
|
{
|
||||||
|
$res = Response::json($data, $has_content ? 201 : 204);
|
||||||
|
//jsonp
|
||||||
|
if (Request::has('callback')) {
|
||||||
|
$res->setCallback(Request::input('callback'));
|
||||||
|
}
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function deleted($data='ok'){
|
||||||
|
$res = Response::json($data, 204);
|
||||||
|
//jsonp
|
||||||
|
if(Request::has('callback'))
|
||||||
|
$res->setCallback(Request::input('callback'));
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function ok($data = 'ok'){
|
||||||
|
$res = Response::json($data, 200);
|
||||||
|
//jsonp
|
||||||
|
if(Request::has('callback'))
|
||||||
|
$res->setCallback(Request::input('callback'));
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function error400($data = ['message' => 'Bad Request']){
|
||||||
|
return Response::json($data, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function error404($data = array('message' => 'Entity Not Found')){
|
||||||
|
return Response::json($data, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function error403($data = array('message' => 'Forbidden'))
|
||||||
|
{
|
||||||
|
return Response::json($data, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {
|
||||||
|
"message": "Validation Failed",
|
||||||
|
"errors": [
|
||||||
|
{
|
||||||
|
"resource": "Issue",
|
||||||
|
"field": "title",
|
||||||
|
"code": "missing_field"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
* @param $messages
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function error412($messages){
|
||||||
|
|
||||||
|
return Response::json(array('error'=>'validation' , 'messages' => $messages), 412);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
use App\Http\Controllers\OpenId\DiscoveryController;
|
use App\Http\Controllers\OpenId\DiscoveryController;
|
||||||
use App\Http\Controllers\OpenId\OpenIdController;
|
use App\Http\Controllers\OpenId\OpenIdController;
|
||||||
|
use App\Http\Controllers\Traits\JsonResponses;
|
||||||
use App\Http\Utils\CountryList;
|
use App\Http\Utils\CountryList;
|
||||||
use Auth\Exceptions\AuthenticationException;
|
use Auth\Exceptions\AuthenticationException;
|
||||||
use Auth\Exceptions\UnverifiedEmailMemberException;
|
use Auth\Exceptions\UnverifiedEmailMemberException;
|
||||||
|
@ -25,6 +26,8 @@ use Illuminate\Support\Facades\Response;
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
|
use models\exceptions\EntityNotFoundException;
|
||||||
|
use models\exceptions\ValidationException;
|
||||||
use OAuth2\Repositories\IApiScopeRepository;
|
use OAuth2\Repositories\IApiScopeRepository;
|
||||||
use OAuth2\Repositories\IClientRepository;
|
use OAuth2\Repositories\IClientRepository;
|
||||||
use OpenId\Services\IUserService;
|
use OpenId\Services\IUserService;
|
||||||
|
@ -224,28 +227,70 @@ final class UserController extends OpenIdController
|
||||||
return $this->login_strategy->cancelLogin();
|
return $this->login_strategy->cancelLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use JsonResponses;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Http\JsonResponse|mixed
|
||||||
|
*/
|
||||||
|
public function getAccount(){
|
||||||
|
try {
|
||||||
|
$email = Request::input("email", "");
|
||||||
|
if(empty($email)){
|
||||||
|
throw new ValidationException("empty email.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->auth_service->getUserByUsername($email);
|
||||||
|
|
||||||
|
if(is_null($user))
|
||||||
|
throw new EntityNotFoundException();
|
||||||
|
|
||||||
|
return $this->ok(
|
||||||
|
[
|
||||||
|
'pic' => $user->getPic(),
|
||||||
|
'full_name' => $user->getFullName()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (ValidationException $ex){
|
||||||
|
Log::warning($ex);
|
||||||
|
return $this->error412($ex->getMessages());
|
||||||
|
}
|
||||||
|
catch (EntityNotFoundException $ex){
|
||||||
|
Log::warning($ex);
|
||||||
|
return $this->error404();
|
||||||
|
}
|
||||||
|
catch (Exception $ex){
|
||||||
|
Log::error($ex);
|
||||||
|
return $this->error500($ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function postLogin()
|
public function postLogin()
|
||||||
{
|
{
|
||||||
$max_login_attempts_2_show_captcha = $this->server_configuration_service->getConfigValue("MaxFailed.LoginAttempts.2ShowCaptcha");
|
$max_login_attempts_2_show_captcha = $this->server_configuration_service->getConfigValue("MaxFailed.LoginAttempts.2ShowCaptcha");
|
||||||
$login_attempts = 0;
|
$login_attempts = 0;
|
||||||
$username = '';
|
$username = '';
|
||||||
try {
|
$user = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
$data = Request::all();
|
$data = Request::all();
|
||||||
|
|
||||||
if (isset($data['username']))
|
if (isset($data['username']))
|
||||||
$data['username'] = trim($data['username']);
|
$data['username'] = trim($data['username']);
|
||||||
if (isset($data['password']))
|
|
||||||
|
if(isset($data['password']))
|
||||||
$data['password'] = trim($data['password']);
|
$data['password'] = trim($data['password']);
|
||||||
|
|
||||||
$login_attempts = intval(Request::input('login_attempts'));
|
$login_attempts = intval(Request::input('login_attempts'));
|
||||||
// Build the validation constraint set.
|
// Build the validation constraint set.
|
||||||
$rules = array
|
$rules = [
|
||||||
(
|
|
||||||
'username' => 'required|email',
|
'username' => 'required|email',
|
||||||
'password' => 'required',
|
'password' => 'required',
|
||||||
);
|
];
|
||||||
if ($login_attempts >= $max_login_attempts_2_show_captcha) {
|
|
||||||
|
if ($login_attempts >= $max_login_attempts_2_show_captcha)
|
||||||
|
{
|
||||||
$rules['g-recaptcha-response'] = 'required|recaptcha';
|
$rules['g-recaptcha-response'] = 'required|recaptcha';
|
||||||
}
|
}
|
||||||
// Create a new validator instance.
|
// Create a new validator instance.
|
||||||
|
@ -255,9 +300,10 @@ final class UserController extends OpenIdController
|
||||||
$username = $data['username'];
|
$username = $data['username'];
|
||||||
$password = $data['password'];
|
$password = $data['password'];
|
||||||
$remember = Request::input("remember");
|
$remember = Request::input("remember");
|
||||||
|
|
||||||
$remember = !is_null($remember);
|
$remember = !is_null($remember);
|
||||||
if ($this->auth_service->login($username, $password, $remember)) {
|
|
||||||
|
if ($this->auth_service->login($username, $password, $remember))
|
||||||
|
{
|
||||||
return $this->login_strategy->postLogin();
|
return $this->login_strategy->postLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,36 +316,56 @@ final class UserController extends OpenIdController
|
||||||
|
|
||||||
return $this->login_strategy->errorLogin
|
return $this->login_strategy->errorLogin
|
||||||
(
|
(
|
||||||
array
|
[
|
||||||
(
|
|
||||||
'max_login_attempts_2_show_captcha' => $max_login_attempts_2_show_captcha,
|
'max_login_attempts_2_show_captcha' => $max_login_attempts_2_show_captcha,
|
||||||
'login_attempts' => $login_attempts,
|
'login_attempts' => $login_attempts,
|
||||||
'username' => $username,
|
'error_message' => "We are sorry, your username or password does not match an existing record.",
|
||||||
'error_message' => "We are sorry, your username or password does not match an existing record."
|
'user_fullname' => $user->getFullName(),
|
||||||
)
|
'user_pic' => $user->getPic(),
|
||||||
|
'user_verified' => true,
|
||||||
|
'username' => $username,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// validator errors
|
// validator errors
|
||||||
|
$response_data = [
|
||||||
|
'max_login_attempts_2_show_captcha' => $max_login_attempts_2_show_captcha,
|
||||||
|
'login_attempts' => $login_attempts,
|
||||||
|
'validator' => $validator
|
||||||
|
];
|
||||||
|
|
||||||
|
if(!is_null($user)){
|
||||||
|
$response_data['user_fullname'] = $user->getFullName();
|
||||||
|
$response_data['user_pic'] = $user->getPic();
|
||||||
|
$response_data['user_verified'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->login_strategy->errorLogin
|
return $this->login_strategy->errorLogin
|
||||||
(
|
(
|
||||||
array
|
$response_data
|
||||||
(
|
|
||||||
'max_login_attempts_2_show_captcha' => $max_login_attempts_2_show_captcha,
|
|
||||||
'login_attempts' => $login_attempts,
|
|
||||||
'validator' => $validator
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} catch (UnverifiedEmailMemberException $ex1) {
|
} catch (UnverifiedEmailMemberException $ex1) {
|
||||||
Log::warning($ex1);
|
Log::warning($ex1);
|
||||||
|
|
||||||
|
$user = $this->auth_service->getUserByUsername($username);
|
||||||
|
|
||||||
|
$response_data = [
|
||||||
|
'max_login_attempts_2_show_captcha' => $max_login_attempts_2_show_captcha,
|
||||||
|
'login_attempts' => $login_attempts,
|
||||||
|
'username' => $username,
|
||||||
|
'error_message' => $ex1->getMessage()
|
||||||
|
];
|
||||||
|
|
||||||
|
if(!is_null($user)){
|
||||||
|
$response_data['user_fullname'] = $user->getFullName();
|
||||||
|
$response_data['user_pic'] = $user->getPic();
|
||||||
|
$response_data['user_verified'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->login_strategy->errorLogin
|
return $this->login_strategy->errorLogin
|
||||||
(
|
(
|
||||||
array
|
$response_data
|
||||||
(
|
|
||||||
'max_login_attempts_2_show_captcha' => $max_login_attempts_2_show_captcha,
|
|
||||||
'login_attempts' => $login_attempts,
|
|
||||||
'username' => $username,
|
|
||||||
'error_message' => $ex1->getMessage()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} catch (AuthenticationException $ex2) {
|
} catch (AuthenticationException $ex2) {
|
||||||
Log::warning($ex2);
|
Log::warning($ex2);
|
||||||
|
|
|
@ -50,6 +50,13 @@ final class EventServiceProvider extends ServiceProvider
|
||||||
protected $listen = [
|
protected $listen = [
|
||||||
'Illuminate\Database\Events\QueryExecuted' => [
|
'Illuminate\Database\Events\QueryExecuted' => [
|
||||||
],
|
],
|
||||||
|
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
|
||||||
|
// ... other providers
|
||||||
|
'SocialiteProviders\\Facebook\\FacebookExtendSocialite@handle',
|
||||||
|
'SocialiteProviders\\Google\\GoogleExtendSocialite@handle',
|
||||||
|
'SocialiteProviders\\Apple\\AppleExtendSocialite@handle',
|
||||||
|
'SocialiteProviders\\LinkedIn\\LinkedInExtendSocialite@handle',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
use App\libs\Auth\SocialLoginProviders;
|
||||||
use Utils\IPHelper;
|
use Utils\IPHelper;
|
||||||
use Services\IUserActionService;
|
use Services\IUserActionService;
|
||||||
use Utils\Services\IAuthService;
|
use Utils\Services\IAuthService;
|
||||||
|
@ -44,15 +46,26 @@ class DefaultLoginStrategy implements ILoginStrategy
|
||||||
public function getLogin()
|
public function getLogin()
|
||||||
{
|
{
|
||||||
if (Auth::guest())
|
if (Auth::guest())
|
||||||
return View::make("auth.login");
|
return View::make("auth.login", [
|
||||||
|
'supported_providers' => SocialLoginProviders::buildSupportedProviders()
|
||||||
|
]);
|
||||||
return Redirect::action("UserController@getProfile");
|
return Redirect::action("UserController@getProfile");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postLogin()
|
public function postLogin(array $params = [])
|
||||||
{
|
{
|
||||||
$user = $this->auth_service->getCurrentUser();
|
$user = $this->auth_service->getCurrentUser();
|
||||||
$identifier = $user->getIdentifier();
|
$identifier = $user->getIdentifier();
|
||||||
$this->user_action_service->addUserAction($this->auth_service->getCurrentUser()->getId(), IPHelper::getUserIp(), IUserActionService::LoginAction);
|
$realm = "From Site";
|
||||||
|
if(isset($params['provider']))
|
||||||
|
$realm .= " using ".strtoupper($params['provider']);
|
||||||
|
$this->user_action_service->addUserAction
|
||||||
|
(
|
||||||
|
$this->auth_service->getCurrentUser()->getId(),
|
||||||
|
IPHelper::getUserIp(),
|
||||||
|
IUserActionService::LoginAction,
|
||||||
|
$realm
|
||||||
|
);
|
||||||
$default_url = URL::action("UserController@getIdentity", array("identifier" => $identifier));
|
$default_url = URL::action("UserController@getIdentity", array("identifier" => $identifier));
|
||||||
return Redirect::intended($default_url);
|
return Redirect::intended($default_url);
|
||||||
}
|
}
|
||||||
|
@ -68,11 +81,11 @@ class DefaultLoginStrategy implements ILoginStrategy
|
||||||
*/
|
*/
|
||||||
public function errorLogin(array $params)
|
public function errorLogin(array $params)
|
||||||
{
|
{
|
||||||
$response = Redirect::action('UserController@getLogin')
|
$response = Redirect::action('UserController@getLogin');
|
||||||
->with('max_login_attempts_2_show_captcha', $params['max_login_attempts_2_show_captcha'])
|
|
||||||
->with('login_attempts', $params['login_attempts']);
|
foreach ($params as $key => $val)
|
||||||
if(isset($params['username']))
|
$response = $response->with($key, $val);
|
||||||
$response= $response->with('username', $params['username']);
|
|
||||||
if(isset($params['error_message']))
|
if(isset($params['error_message']))
|
||||||
$response = $response->with('flash_notice', $params['error_message']);
|
$response = $response->with('flash_notice', $params['error_message']);
|
||||||
if(isset($params['validator']))
|
if(isset($params['validator']))
|
||||||
|
|
|
@ -11,9 +11,12 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
use App\libs\Auth\SocialLoginProviders;
|
||||||
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
|
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
|
||||||
use Illuminate\Support\Facades\Response;
|
use Illuminate\Support\Facades\Response;
|
||||||
use Illuminate\Support\Facades\Redirect;
|
use Illuminate\Support\Facades\Redirect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class DisplayResponseUserAgentStrategy
|
* Class DisplayResponseUserAgentStrategy
|
||||||
* @package Strategies
|
* @package Strategies
|
||||||
|
@ -36,6 +39,12 @@ class DisplayResponseUserAgentStrategy implements IDisplayResponseStrategy
|
||||||
*/
|
*/
|
||||||
public function getLoginResponse(array $data = [])
|
public function getLoginResponse(array $data = [])
|
||||||
{
|
{
|
||||||
|
$provider = $data["provider"] ?? null;
|
||||||
|
|
||||||
|
if(!empty($provider)) {
|
||||||
|
return redirect()->route('social_login', ['provider' => $provider]);
|
||||||
|
}
|
||||||
|
$data['supported_providers'] = SocialLoginProviders::buildSupportedProviders();
|
||||||
return Response::view("auth.login", $data, 200);
|
return Response::view("auth.login", $data, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,12 +54,11 @@ class DisplayResponseUserAgentStrategy implements IDisplayResponseStrategy
|
||||||
*/
|
*/
|
||||||
public function getLoginErrorResponse(array $data = [])
|
public function getLoginErrorResponse(array $data = [])
|
||||||
{
|
{
|
||||||
$response = Redirect::action('UserController@getLogin')
|
$response = Redirect::action('UserController@getLogin');
|
||||||
->with('max_login_attempts_2_show_captcha', $data['max_login_attempts_2_show_captcha'])
|
|
||||||
->with('login_attempts', $data['login_attempts']);
|
foreach ($data as $key => $val)
|
||||||
|
$response= $response->with($key, $val);
|
||||||
|
|
||||||
if(isset($data['username']))
|
|
||||||
$response= $response->with('username', $data['username']);
|
|
||||||
if(isset($data['error_message']))
|
if(isset($data['error_message']))
|
||||||
$response = $response->with('flash_notice', $data['error_message']);
|
$response = $response->with('flash_notice', $data['error_message']);
|
||||||
if(isset($data['validator']))
|
if(isset($data['validator']))
|
||||||
|
|
|
@ -11,9 +11,10 @@ interface ILoginStrategy
|
||||||
public function getLogin();
|
public function getLogin();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param array $params
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function postLogin();
|
public function postLogin(array $params = []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php namespace Strategies;
|
||||||
|
/**
|
||||||
|
* Copyright 2015 OpenStack Foundation
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
use Strategies\ILoginStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface ILoginStrategyFactory
|
||||||
|
* @package Strategies
|
||||||
|
*/
|
||||||
|
interface ILoginStrategyFactory
|
||||||
|
{
|
||||||
|
public function build():ILoginStrategy;
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
<?php namespace Strategies;
|
||||||
|
/**
|
||||||
|
* Copyright 2015 OpenStack Foundation
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
use App\Services\Auth\IUserService;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use OAuth2\Services\IMementoOAuth2SerializerService;
|
||||||
|
use OAuth2\Services\ISecurityContextService;
|
||||||
|
use OpenId\Services\IMementoOpenIdSerializerService;
|
||||||
|
use Services\IUserActionService;
|
||||||
|
use Utils\Services\IAuthService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LoginStrategyFactory
|
||||||
|
* @package Strategies
|
||||||
|
*/
|
||||||
|
final class LoginStrategyFactory implements ILoginStrategyFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var IMementoOpenIdSerializerService
|
||||||
|
*/
|
||||||
|
private $openid_memento_service;
|
||||||
|
/**
|
||||||
|
* @var IMementoOAuth2SerializerService
|
||||||
|
*/
|
||||||
|
private $oauth2_memento_service;
|
||||||
|
/**
|
||||||
|
* @var IAuthService
|
||||||
|
*/
|
||||||
|
private $auth_service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var IUserService
|
||||||
|
*/
|
||||||
|
private $user_service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var IUserActionService
|
||||||
|
*/
|
||||||
|
private $user_action_service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ISecurityContextService
|
||||||
|
*/
|
||||||
|
private $security_context_service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LoginStrategyFactory constructor.
|
||||||
|
* @param IMementoOpenIdSerializerService $openid_memento_service
|
||||||
|
* @param IMementoOAuth2SerializerService $oauth2_memento_service
|
||||||
|
* @param IAuthService $auth_service
|
||||||
|
* @param IUserService $user_service
|
||||||
|
* @param IUserActionService $user_action_service
|
||||||
|
* @param ISecurityContextService $security_context_service
|
||||||
|
*/
|
||||||
|
public function __construct
|
||||||
|
(
|
||||||
|
IMementoOpenIdSerializerService $openid_memento_service,
|
||||||
|
IMementoOAuth2SerializerService $oauth2_memento_service,
|
||||||
|
IAuthService $auth_service,
|
||||||
|
IUserService $user_service,
|
||||||
|
IUserActionService $user_action_service,
|
||||||
|
ISecurityContextService $security_context_service)
|
||||||
|
{
|
||||||
|
$this->openid_memento_service = $openid_memento_service;
|
||||||
|
$this->oauth2_memento_service = $oauth2_memento_service;
|
||||||
|
$this->auth_service = $auth_service;
|
||||||
|
$this->user_service = $user_service;
|
||||||
|
$this->user_action_service = $user_action_service;
|
||||||
|
$this->security_context_service = $security_context_service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function build():ILoginStrategy{
|
||||||
|
$res = null;
|
||||||
|
Log::debug(sprintf("LoginStrategyFactory::build"));
|
||||||
|
if ($this->openid_memento_service->exists())
|
||||||
|
{
|
||||||
|
//openid stuff
|
||||||
|
Log::debug(sprintf("LoginStrategyFactory::build OIDC"));
|
||||||
|
return new OpenIdLoginStrategy
|
||||||
|
(
|
||||||
|
$this->openid_memento_service,
|
||||||
|
$this->user_action_service,
|
||||||
|
$this->auth_service
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ($this->oauth2_memento_service->exists())
|
||||||
|
{
|
||||||
|
Log::debug(sprintf("LoginStrategyFactory::build OAUTH2"));
|
||||||
|
return new OAuth2LoginStrategy
|
||||||
|
(
|
||||||
|
$this->auth_service,
|
||||||
|
$this->oauth2_memento_service,
|
||||||
|
$this->user_action_service,
|
||||||
|
$this->security_context_service
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//default stuff
|
||||||
|
Log::debug(sprintf("LoginStrategyFactory::build DEFAULT"));
|
||||||
|
return new DefaultLoginStrategy($this->user_action_service, $this->auth_service);
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,10 +76,12 @@ class OAuth2LoginStrategy extends DefaultLoginStrategy
|
||||||
|
|
||||||
$response_strategy = DisplayResponseStrategyFactory::build($auth_request->getDisplay());
|
$response_strategy = DisplayResponseStrategyFactory::build($auth_request->getDisplay());
|
||||||
|
|
||||||
return $response_strategy->getLoginResponse();
|
return $response_strategy->getLoginResponse([
|
||||||
|
'provider' => $auth_request->getProvider()
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postLogin()
|
public function postLogin(array $params = [])
|
||||||
{
|
{
|
||||||
$auth_request = OAuth2AuthorizationRequestFactory::getInstance()->build(
|
$auth_request = OAuth2AuthorizationRequestFactory::getInstance()->build(
|
||||||
OAuth2Message::buildFromMemento(
|
OAuth2Message::buildFromMemento(
|
||||||
|
@ -87,8 +89,12 @@ class OAuth2LoginStrategy extends DefaultLoginStrategy
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$realm = "From ".$auth_request->getRedirectUri();
|
||||||
|
if(isset($params['provider']))
|
||||||
|
$realm .= " using ".strtoupper($params['provider']);
|
||||||
|
|
||||||
$this->user_action_service->addUserAction($this->auth_service->getCurrentUser()->getId(), IPHelper::getUserIp(),
|
$this->user_action_service->addUserAction($this->auth_service->getCurrentUser()->getId(), IPHelper::getUserIp(),
|
||||||
IUserActionService::LoginAction, $auth_request->getRedirectUri());
|
IUserActionService::LoginAction, $realm);
|
||||||
|
|
||||||
return Redirect::action("OAuth2\OAuth2ProviderController@auth");
|
return Redirect::action("OAuth2\OAuth2ProviderController@auth");
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
use App\libs\Auth\SocialLoginProviders;
|
||||||
use OpenId\OpenIdMessage;
|
use OpenId\OpenIdMessage;
|
||||||
use OpenId\OpenIdProtocol;
|
use OpenId\OpenIdProtocol;
|
||||||
use OpenId\Requests\OpenIdAuthenticationRequest;
|
use OpenId\Requests\OpenIdAuthenticationRequest;
|
||||||
|
@ -62,23 +64,27 @@ final class OpenIdLoginStrategy extends DefaultLoginStrategy
|
||||||
} else {
|
} else {
|
||||||
$params['identity_select'] = true;
|
$params['identity_select'] = true;
|
||||||
}
|
}
|
||||||
|
$params['supported_providers'] = SocialLoginProviders::buildSupportedProviders();
|
||||||
return View::make("auth.login", $params);
|
return View::make("auth.login", $params);
|
||||||
}
|
}
|
||||||
return Redirect::action("UserController@getProfile");
|
return Redirect::action("UserController@getProfile");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postLogin()
|
public function postLogin(array $params = [])
|
||||||
{
|
{
|
||||||
//go to authentication flow again
|
//go to authentication flow again
|
||||||
$msg = OpenIdMessage::buildFromMemento($this->memento_service->load());
|
$msg = OpenIdMessage::buildFromMemento($this->memento_service->load());
|
||||||
|
|
||||||
|
$realm = "From ". $msg->getParam(OpenIdProtocol::OpenIDProtocol_Realm);
|
||||||
|
if(isset($params['provider']))
|
||||||
|
$realm .= " using ".strtoupper($params['provider']);
|
||||||
|
|
||||||
$this->user_action_service->addUserAction
|
$this->user_action_service->addUserAction
|
||||||
(
|
(
|
||||||
$this->auth_service->getCurrentUser()->getId(),
|
$this->auth_service->getCurrentUser()->getId(),
|
||||||
IPHelper::getUserIp(),
|
IPHelper::getUserIp(),
|
||||||
IUserActionService::LoginAction,
|
IUserActionService::LoginAction,
|
||||||
$msg->getParam(OpenIdProtocol::OpenIDProtocol_Realm)
|
$realm
|
||||||
);
|
);
|
||||||
|
|
||||||
return Redirect::action("OpenId\OpenIdProviderController@endpoint");
|
return Redirect::action("OpenId\OpenIdProviderController@endpoint");
|
||||||
|
|
|
@ -48,6 +48,10 @@ final class StrategyProvider extends ServiceProvider implements DeferrableProvid
|
||||||
// authentication strategies
|
// authentication strategies
|
||||||
App::singleton(OAuth2ServiceCatalog::AuthenticationStrategy, \Strategies\OAuth2AuthenticationStrategy::class);
|
App::singleton(OAuth2ServiceCatalog::AuthenticationStrategy, \Strategies\OAuth2AuthenticationStrategy::class);
|
||||||
App::singleton(OpenIdServiceCatalog::AuthenticationStrategy, \Strategies\OpenIdAuthenticationStrategy::class);
|
App::singleton(OpenIdServiceCatalog::AuthenticationStrategy, \Strategies\OpenIdAuthenticationStrategy::class);
|
||||||
|
|
||||||
|
// factories
|
||||||
|
|
||||||
|
App::singleton(ILoginStrategyFactory::class, LoginStrategyFactory::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provides()
|
public function provides()
|
||||||
|
@ -61,6 +65,7 @@ final class StrategyProvider extends ServiceProvider implements DeferrableProvid
|
||||||
OAuth2IndirectFragmentResponse::OAuth2IndirectFragmentResponse,
|
OAuth2IndirectFragmentResponse::OAuth2IndirectFragmentResponse,
|
||||||
OAuth2ServiceCatalog::AuthenticationStrategy,
|
OAuth2ServiceCatalog::AuthenticationStrategy,
|
||||||
OpenIdServiceCatalog::AuthenticationStrategy,
|
OpenIdServiceCatalog::AuthenticationStrategy,
|
||||||
|
ILoginStrategyFactory::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -166,6 +166,18 @@ final class UserFactory
|
||||||
if(isset($payload['email_verified']) && boolval($payload['email_verified']) === true && !$user->isEmailVerified())
|
if(isset($payload['email_verified']) && boolval($payload['email_verified']) === true && !$user->isEmailVerified())
|
||||||
$user->verifyEmail();
|
$user->verifyEmail();
|
||||||
|
|
||||||
|
if(isset($payload['full_name']))
|
||||||
|
$user->setFullName(trim($payload['full_name']));
|
||||||
|
|
||||||
|
if(isset($payload['external_id']))
|
||||||
|
$user->setExternalId(trim($payload['external_id']));
|
||||||
|
|
||||||
|
if(isset($payload['external_pic']))
|
||||||
|
$user->setExternalPic(trim($payload['external_pic']));
|
||||||
|
|
||||||
|
if(isset($payload['external_provider']))
|
||||||
|
$user->setExternalProvider(trim($payload['external_provider']));
|
||||||
|
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -306,6 +306,24 @@ class User extends BaseEntity
|
||||||
*/
|
*/
|
||||||
private $pic;
|
private $pic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(name="external_id", type="string")
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $external_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(name="external_provider", type="string")
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $external_provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(name="external_pic", type="string")
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $external_pic;
|
||||||
|
|
||||||
// relations
|
// relations
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -412,6 +430,9 @@ class User extends BaseEntity
|
||||||
$this->spam_type = self::SpamTypeNone;
|
$this->spam_type = self::SpamTypeNone;
|
||||||
$this->company = null;
|
$this->company = null;
|
||||||
$this->phone_number = null;
|
$this->phone_number = null;
|
||||||
|
$this->external_id = null;
|
||||||
|
$this->external_provider = null;
|
||||||
|
$this->external_pic = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -823,6 +844,9 @@ class User extends BaseEntity
|
||||||
if (!empty($this->pic)) {
|
if (!empty($this->pic)) {
|
||||||
return Storage::disk('swift')->url(sprintf("%s/%s", self::getProfilePicFolder(), $this->pic));
|
return Storage::disk('swift')->url(sprintf("%s/%s", self::getProfilePicFolder(), $this->pic));
|
||||||
}
|
}
|
||||||
|
if(!empty($this->external_pic))
|
||||||
|
return $this->external_pic;
|
||||||
|
|
||||||
return $this->getGravatarUrl();
|
return $this->getGravatarUrl();
|
||||||
}
|
}
|
||||||
catch (\Exception $ex) {
|
catch (\Exception $ex) {
|
||||||
|
@ -1780,4 +1804,61 @@ SQL;
|
||||||
$this->job_title = $job_title;
|
$this->job_title = $job_title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExternalProvider(): ?string
|
||||||
|
{
|
||||||
|
return $this->external_provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $external_provider
|
||||||
|
*/
|
||||||
|
public function setExternalProvider(string $external_provider): void
|
||||||
|
{
|
||||||
|
$this->external_provider = $external_provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExternalPic(): ?string
|
||||||
|
{
|
||||||
|
return $this->external_pic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $external_pic
|
||||||
|
*/
|
||||||
|
public function setExternalPic(string $external_pic): void
|
||||||
|
{
|
||||||
|
$this->external_pic = $external_pic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExternalId(): string
|
||||||
|
{
|
||||||
|
return $this->external_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $external_id
|
||||||
|
*/
|
||||||
|
public function setExternalId(string $external_id): void
|
||||||
|
{
|
||||||
|
$this->external_id = $external_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $full_name
|
||||||
|
*/
|
||||||
|
public function setFullName(string $full_name):void{
|
||||||
|
$name_parts = explode(" ", $full_name);
|
||||||
|
$this->first_name = $name_parts[0];
|
||||||
|
$this->last_name = $name_parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php namespace App\libs\Auth;
|
||||||
|
/**
|
||||||
|
* Copyright 2021 OpenStack Foundation
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SocialLoginProviders
|
||||||
|
* @package App\libs\Auth
|
||||||
|
*/
|
||||||
|
final class SocialLoginProviders
|
||||||
|
{
|
||||||
|
const Facebook = "facebook";
|
||||||
|
const Apple = "apple";
|
||||||
|
const LinkedIn = "linkedin";
|
||||||
|
const Google = "google";
|
||||||
|
|
||||||
|
const ValidProviders = [
|
||||||
|
self::Facebook,
|
||||||
|
self::Apple,
|
||||||
|
self::LinkedIn,
|
||||||
|
self::Google
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $provider
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isSupportedProvider(string $provider):bool{
|
||||||
|
return in_array($provider, self::ValidProviders);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public static function buildSupportedProviders():array{
|
||||||
|
return [
|
||||||
|
self::Facebook => "Facebook",
|
||||||
|
self::Apple => "Apple",
|
||||||
|
self::LinkedIn => "LinkedIn",
|
||||||
|
self::Google => "Google",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
<?php namespace OAuth2\Discovery;
|
<?php namespace OAuth2\Discovery;
|
||||||
|
use App\libs\Auth\SocialLoginProviders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2015 OpenStack Foundation
|
* Copyright 2015 OpenStack Foundation
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -254,6 +256,15 @@ final class DiscoveryDocumentBuilder
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addAvailableThirdPartyIdentityProviders(){
|
||||||
|
foreach(SocialLoginProviders::ValidProviders as $provider)
|
||||||
|
$this->addArrayValue("third_party_identity_providers", $provider);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -231,6 +231,11 @@ final class OAuth2Protocol implements IOAuth2Protocol
|
||||||
// http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
// http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
const OAuth2Protocol_Nonce = 'nonce';
|
const OAuth2Protocol_Nonce = 'nonce';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* custom param - social login
|
||||||
|
*/
|
||||||
|
const OAuth2Protocol_Provider = 'provider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the End-User authentication occurred. Its value is a JSON number representing the number of seconds
|
* Time when the End-User authentication occurred. Its value is a JSON number representing the number of seconds
|
||||||
* from 1970-01-01T0:0:0Z as measured in UTC until the date/time. When a max_age request is made or when auth_time
|
* from 1970-01-01T0:0:0Z as measured in UTC until the date/time. When a max_age request is made or when auth_time
|
||||||
|
@ -1403,6 +1408,7 @@ final class OAuth2Protocol implements IOAuth2Protocol
|
||||||
->addDisplayValueSupported(self::OAuth2Protocol_Display_Touch)
|
->addDisplayValueSupported(self::OAuth2Protocol_Display_Touch)
|
||||||
->addDisplayValueSupported(self::OAuth2Protocol_Display_Wap)
|
->addDisplayValueSupported(self::OAuth2Protocol_Display_Wap)
|
||||||
->addDisplayValueSupported(self::OAuth2Protocol_Display_Native)
|
->addDisplayValueSupported(self::OAuth2Protocol_Display_Native)
|
||||||
|
->addAvailableThirdPartyIdentityProviders()
|
||||||
->render();
|
->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ class OAuth2AuthenticationRequest extends OAuth2AuthorizationRequest
|
||||||
OAuth2Protocol::OAuth2Protocol_IDTokenHint,
|
OAuth2Protocol::OAuth2Protocol_IDTokenHint,
|
||||||
OAuth2Protocol::OAuth2Protocol_LoginHint,
|
OAuth2Protocol::OAuth2Protocol_LoginHint,
|
||||||
OAuth2Protocol::OAuth2Protocol_ACRValues,
|
OAuth2Protocol::OAuth2Protocol_ACRValues,
|
||||||
|
OAuth2Protocol::OAuth2Protocol_Provider,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -111,6 +112,10 @@ class OAuth2AuthenticationRequest extends OAuth2AuthorizationRequest
|
||||||
return str_contains($this->getScope(), OAuth2Protocol::OfflineAccess_Scope);
|
return str_contains($this->getScope(), OAuth2Protocol::OfflineAccess_Scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getProvider():?string{
|
||||||
|
return $this->getParam(OAuth2Protocol::OAuth2Protocol_Provider);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param OAuth2AuthorizationRequest $auth_request
|
* @param OAuth2AuthorizationRequest $auth_request
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
// babel.config.js
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
"targets": {
|
||||||
|
"edge": "17",
|
||||||
|
"firefox": "60",
|
||||||
|
"chrome": "67",
|
||||||
|
"safari": "11.1",
|
||||||
|
"node":"current"
|
||||||
|
},
|
||||||
|
"useBuiltIns": "usage",
|
||||||
|
"corejs": "3.9.1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@babel/preset-react",
|
||||||
|
"@babel/preset-flow"
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
"@babel/plugin-proposal-object-rest-spread",
|
||||||
|
"@babel/plugin-proposal-class-properties"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
|
@ -29,31 +29,36 @@
|
||||||
"php": "^7.3|^8.0",
|
"php": "^7.3|^8.0",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
"laravel/framework": "^8.0",
|
|
||||||
"laravel/helpers": "^1.4",
|
|
||||||
"laravel/tinker": "^2.5",
|
|
||||||
"laravelcollective/html": "6.2.*",
|
|
||||||
"fruitcake/laravel-cors": "^2.0",
|
|
||||||
"laravel-doctrine/orm": "1.7.*",
|
|
||||||
"laravel-doctrine/extensions": "1.4.*",
|
|
||||||
"laravel-doctrine/migrations": "2.3.*",
|
|
||||||
"beberlei/doctrineextensions": "1.3.*",
|
"beberlei/doctrineextensions": "1.3.*",
|
||||||
"behat/transliterator": "^1.2",
|
"behat/transliterator": "^1.2",
|
||||||
"vladimir-yuldashev/laravel-queue-rabbitmq": "v11.1.*",
|
|
||||||
"s-ichikawa/laravel-sendgrid-driver": "~3.0",
|
|
||||||
"ezyang/htmlpurifier": "v4.12.0",
|
"ezyang/htmlpurifier": "v4.12.0",
|
||||||
"fideloper/proxy": "^4.4",
|
"fideloper/proxy": "^4.4",
|
||||||
|
"fruitcake/laravel-cors": "^2.0",
|
||||||
"get-stream/stream-chat": "^1.1",
|
"get-stream/stream-chat": "^1.1",
|
||||||
"glenscott/url-normalizer": "1.4.0",
|
"glenscott/url-normalizer": "1.4.0",
|
||||||
"greggilbert/recaptcha": "dev-feature/laravel8.x",
|
"greggilbert/recaptcha": "dev-feature/laravel8.x",
|
||||||
"guzzlehttp/guzzle": "^7.0.1",
|
"guzzlehttp/guzzle": "^7.0.1",
|
||||||
"ircmaxell/random-lib": "1.1.0",
|
"ircmaxell/random-lib": "1.1.0",
|
||||||
"jenssegers/agent": "2.6.3",
|
"jenssegers/agent": "2.6.3",
|
||||||
|
"laravel-doctrine/extensions": "1.4.*",
|
||||||
|
"laravel-doctrine/migrations": "2.3.*",
|
||||||
|
"laravel-doctrine/orm": "1.7.*",
|
||||||
|
"laravel/framework": "^8.0",
|
||||||
|
"laravel/helpers": "^1.4",
|
||||||
|
"laravel/socialite": "^5.2",
|
||||||
|
"laravel/tinker": "^2.5",
|
||||||
|
"laravelcollective/html": "6.2.*",
|
||||||
"php-opencloud/openstack": "dev-feature/guzzle_7_x",
|
"php-opencloud/openstack": "dev-feature/guzzle_7_x",
|
||||||
"phpseclib/phpseclib": "2.0.11",
|
"phpseclib/phpseclib": "2.0.11",
|
||||||
"predis/predis": "v1.1.6",
|
"predis/predis": "v1.1.6",
|
||||||
|
"s-ichikawa/laravel-sendgrid-driver": "~3.0",
|
||||||
"smarcet/jose4php": "1.0.17",
|
"smarcet/jose4php": "1.0.17",
|
||||||
|
"socialiteproviders/apple": "^5.0",
|
||||||
|
"socialiteproviders/facebook": "^4.1",
|
||||||
|
"socialiteproviders/google": "^4.1",
|
||||||
|
"socialiteproviders/linkedin": "^4.1",
|
||||||
"sokil/php-isocodes": "^3.0",
|
"sokil/php-isocodes": "^3.0",
|
||||||
|
"vladimir-yuldashev/laravel-queue-rabbitmq": "v11.1.*",
|
||||||
"zendframework/zend-crypt": "3.3.0",
|
"zendframework/zend-crypt": "3.3.0",
|
||||||
"zendframework/zend-math": "3.1.1"
|
"zendframework/zend-math": "3.1.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "c28f890e5a6afe1c57a1e232f3be5e5c",
|
"content-hash": "eaa4d26a199112e2689bbd94bb8e0715",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "asm89/stack-cors",
|
"name": "asm89/stack-cors",
|
||||||
|
@ -3362,6 +3362,75 @@
|
||||||
},
|
},
|
||||||
"time": "2021-02-16T15:27:11+00:00"
|
"time": "2021-02-16T15:27:11+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "laravel/socialite",
|
||||||
|
"version": "v5.2.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/laravel/socialite.git",
|
||||||
|
"reference": "1960802068f81e44b2ae9793932181cf1cb91b5c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/laravel/socialite/zipball/1960802068f81e44b2ae9793932181cf1cb91b5c",
|
||||||
|
"reference": "1960802068f81e44b2ae9793932181cf1cb91b5c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"guzzlehttp/guzzle": "^6.0|^7.0",
|
||||||
|
"illuminate/http": "^6.0|^7.0|^8.0",
|
||||||
|
"illuminate/support": "^6.0|^7.0|^8.0",
|
||||||
|
"league/oauth1-client": "^1.0",
|
||||||
|
"php": "^7.2|^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"illuminate/contracts": "^6.0|^7.0",
|
||||||
|
"mockery/mockery": "^1.0",
|
||||||
|
"orchestra/testbench": "^4.0|^5.0|^6.0",
|
||||||
|
"phpunit/phpunit": "^8.0|^9.3"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "5.x-dev"
|
||||||
|
},
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Laravel\\Socialite\\SocialiteServiceProvider"
|
||||||
|
],
|
||||||
|
"aliases": {
|
||||||
|
"Socialite": "Laravel\\Socialite\\Facades\\Socialite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Laravel\\Socialite\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Taylor Otwell",
|
||||||
|
"email": "taylor@laravel.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.",
|
||||||
|
"homepage": "https://laravel.com",
|
||||||
|
"keywords": [
|
||||||
|
"laravel",
|
||||||
|
"oauth"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/laravel/socialite/issues",
|
||||||
|
"source": "https://github.com/laravel/socialite"
|
||||||
|
},
|
||||||
|
"time": "2021-04-06T14:38:16+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/tinker",
|
"name": "laravel/tinker",
|
||||||
"version": "v2.6.1",
|
"version": "v2.6.1",
|
||||||
|
@ -3502,6 +3571,141 @@
|
||||||
},
|
},
|
||||||
"time": "2020-12-15T20:20:05+00:00"
|
"time": "2020-12-15T20:20:05+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "lcobucci/clock",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/lcobucci/clock.git",
|
||||||
|
"reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/lcobucci/clock/zipball/353d83fe2e6ae95745b16b3d911813df6a05bfb3",
|
||||||
|
"reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"infection/infection": "^0.17",
|
||||||
|
"lcobucci/coding-standard": "^6.0",
|
||||||
|
"phpstan/extension-installer": "^1.0",
|
||||||
|
"phpstan/phpstan": "^0.12",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^0.12",
|
||||||
|
"phpstan/phpstan-phpunit": "^0.12",
|
||||||
|
"phpstan/phpstan-strict-rules": "^0.12",
|
||||||
|
"phpunit/php-code-coverage": "9.1.4",
|
||||||
|
"phpunit/phpunit": "9.3.7"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Lcobucci\\Clock\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Luís Cobucci",
|
||||||
|
"email": "lcobucci@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Yet another clock abstraction",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/lcobucci/clock/issues",
|
||||||
|
"source": "https://github.com/lcobucci/clock/tree/2.0.x"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/lcobucci",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.patreon.com/lcobucci",
|
||||||
|
"type": "patreon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2020-08-27T18:56:02+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lcobucci/jwt",
|
||||||
|
"version": "4.1.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/lcobucci/jwt.git",
|
||||||
|
"reference": "71cf170102c8371ccd933fa4df6252086d144de6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/71cf170102c8371ccd933fa4df6252086d144de6",
|
||||||
|
"reference": "71cf170102c8371ccd933fa4df6252086d144de6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-hash": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-openssl": "*",
|
||||||
|
"ext-sodium": "*",
|
||||||
|
"lcobucci/clock": "^2.0",
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"infection/infection": "^0.21",
|
||||||
|
"lcobucci/coding-standard": "^6.0",
|
||||||
|
"mikey179/vfsstream": "^1.6.7",
|
||||||
|
"phpbench/phpbench": "^1.0@alpha",
|
||||||
|
"phpstan/extension-installer": "^1.0",
|
||||||
|
"phpstan/phpstan": "^0.12",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^0.12",
|
||||||
|
"phpstan/phpstan-phpunit": "^0.12",
|
||||||
|
"phpstan/phpstan-strict-rules": "^0.12",
|
||||||
|
"phpunit/php-invoker": "^3.1",
|
||||||
|
"phpunit/phpunit": "^9.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Lcobucci\\JWT\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Luís Cobucci",
|
||||||
|
"email": "lcobucci@gmail.com",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
|
||||||
|
"keywords": [
|
||||||
|
"JWS",
|
||||||
|
"jwt"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/lcobucci/jwt/issues",
|
||||||
|
"source": "https://github.com/lcobucci/jwt/tree/4.1.4"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/lcobucci",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.patreon.com/lcobucci",
|
||||||
|
"type": "patreon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2021-03-23T23:53:08+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "league/commonmark",
|
"name": "league/commonmark",
|
||||||
"version": "1.5.7",
|
"version": "1.5.7",
|
||||||
|
@ -3754,6 +3958,81 @@
|
||||||
],
|
],
|
||||||
"time": "2021-01-18T20:58:21+00:00"
|
"time": "2021-01-18T20:58:21+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "league/oauth1-client",
|
||||||
|
"version": "v1.9.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/thephpleague/oauth1-client.git",
|
||||||
|
"reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
|
||||||
|
"reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-openssl": "*",
|
||||||
|
"guzzlehttp/guzzle": "^6.0|^7.0",
|
||||||
|
"php": ">=7.1||>=8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"ext-simplexml": "*",
|
||||||
|
"friendsofphp/php-cs-fixer": "^2.17",
|
||||||
|
"mockery/mockery": "^1.3.3",
|
||||||
|
"phpstan/phpstan": "^0.12.42",
|
||||||
|
"phpunit/phpunit": "^7.5||9.5"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-simplexml": "For decoding XML-based responses."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0-dev",
|
||||||
|
"dev-develop": "2.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"League\\OAuth1\\Client\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ben Corlett",
|
||||||
|
"email": "bencorlett@me.com",
|
||||||
|
"homepage": "http://www.webcomm.com.au",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "OAuth 1.0 Client Library",
|
||||||
|
"keywords": [
|
||||||
|
"Authentication",
|
||||||
|
"SSO",
|
||||||
|
"authorization",
|
||||||
|
"bitbucket",
|
||||||
|
"identity",
|
||||||
|
"idp",
|
||||||
|
"oauth",
|
||||||
|
"oauth1",
|
||||||
|
"single sign on",
|
||||||
|
"trello",
|
||||||
|
"tumblr",
|
||||||
|
"twitter"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/thephpleague/oauth1-client/issues",
|
||||||
|
"source": "https://github.com/thephpleague/oauth1-client/tree/v1.9.0"
|
||||||
|
},
|
||||||
|
"time": "2021-01-20T01:40:53+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mobiledetect/mobiledetectlib",
|
"name": "mobiledetect/mobiledetectlib",
|
||||||
"version": "2.8.37",
|
"version": "2.8.37",
|
||||||
|
@ -5307,6 +5586,263 @@
|
||||||
},
|
},
|
||||||
"time": "2019-07-12T23:35:09+00:00"
|
"time": "2019-07-12T23:35:09+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "socialiteproviders/apple",
|
||||||
|
"version": "5.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SocialiteProviders/Apple.git",
|
||||||
|
"reference": "3a626202628b27fbe8500251cd609a9062a00f9e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/SocialiteProviders/Apple/zipball/3a626202628b27fbe8500251cd609a9062a00f9e",
|
||||||
|
"reference": "3a626202628b27fbe8500251cd609a9062a00f9e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-openssl": "*",
|
||||||
|
"firebase/php-jwt": "^5.2",
|
||||||
|
"lcobucci/jwt": "^4.0",
|
||||||
|
"php": "^7.4 || ^8.0",
|
||||||
|
"socialiteproviders/manager": "~4.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ahilmurugesan/socialite-apple-helper": "Automatic Apple client key generation and management."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"SocialiteProviders\\Apple\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ahilan",
|
||||||
|
"email": "ahilmurugesan@gmail.com",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vamsi Krishna V",
|
||||||
|
"email": "vamsi@vonectech.com",
|
||||||
|
"homepage": "https://vonectech.com/",
|
||||||
|
"role": "Farmer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Apple OAuth2 Provider for Laravel Socialite",
|
||||||
|
"keywords": [
|
||||||
|
"apple",
|
||||||
|
"apple client key",
|
||||||
|
"apple sign in",
|
||||||
|
"client key generator",
|
||||||
|
"client key refresh",
|
||||||
|
"laravel",
|
||||||
|
"laravel apple",
|
||||||
|
"laravel socialite",
|
||||||
|
"sign in with apple",
|
||||||
|
"socialite",
|
||||||
|
"socialite apple"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/SocialiteProviders/Apple/tree/5.0.2"
|
||||||
|
},
|
||||||
|
"time": "2021-03-25T22:34:09+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "socialiteproviders/facebook",
|
||||||
|
"version": "4.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SocialiteProviders/Facebook.git",
|
||||||
|
"reference": "9b94a9334b5d0f61de8f5a20928d63d4d8f4e00d"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/SocialiteProviders/Facebook/zipball/9b94a9334b5d0f61de8f5a20928d63d4d8f4e00d",
|
||||||
|
"reference": "9b94a9334b5d0f61de8f5a20928d63d4d8f4e00d",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"php": "^7.2 || ^8.0",
|
||||||
|
"socialiteproviders/manager": "~4.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"SocialiteProviders\\Facebook\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Oleksandr Prypkhan (Alex Wells)",
|
||||||
|
"email": "autaut03@googlemail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Facebook (facebook.com) OAuth2 Provider for Laravel Socialite",
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/SocialiteProviders/Facebook/tree/4.1.0"
|
||||||
|
},
|
||||||
|
"time": "2020-12-01T23:10:59+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "socialiteproviders/google",
|
||||||
|
"version": "4.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SocialiteProviders/Google-Plus.git",
|
||||||
|
"reference": "1cb8f6fb2c0dd0fc8b34e95f69865663fdf0b401"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/SocialiteProviders/Google-Plus/zipball/1cb8f6fb2c0dd0fc8b34e95f69865663fdf0b401",
|
||||||
|
"reference": "1cb8f6fb2c0dd0fc8b34e95f69865663fdf0b401",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"php": "^7.2 || ^8.0",
|
||||||
|
"socialiteproviders/manager": "~4.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"SocialiteProviders\\Google\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "xstoop",
|
||||||
|
"email": "myenglishnameisx@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Google OAuth2 Provider for Laravel Socialite",
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/SocialiteProviders/Google-Plus/tree/4.1.0"
|
||||||
|
},
|
||||||
|
"time": "2020-12-01T23:10:59+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "socialiteproviders/linkedin",
|
||||||
|
"version": "4.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SocialiteProviders/LinkedIn.git",
|
||||||
|
"reference": "bcf8a732328e868261416a5f04135acb0b94bf9a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/SocialiteProviders/LinkedIn/zipball/bcf8a732328e868261416a5f04135acb0b94bf9a",
|
||||||
|
"reference": "bcf8a732328e868261416a5f04135acb0b94bf9a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"php": "^7.2 || ^8.0",
|
||||||
|
"socialiteproviders/manager": "~4.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"SocialiteProviders\\LinkedIn\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Brian Faust",
|
||||||
|
"email": "hello@brianfaust.de"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "LinkedIn OAuth2 Provider for Laravel Socialite",
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/SocialiteProviders/LinkedIn/tree/4.1.0"
|
||||||
|
},
|
||||||
|
"time": "2020-12-01T23:10:59+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "socialiteproviders/manager",
|
||||||
|
"version": "4.0.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SocialiteProviders/Manager.git",
|
||||||
|
"reference": "0f5e82af0404df0080bdc5c105cef936c1711524"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/0f5e82af0404df0080bdc5c105cef936c1711524",
|
||||||
|
"reference": "0f5e82af0404df0080bdc5c105cef936c1711524",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/support": "^6.0|^7.0|^8.0",
|
||||||
|
"laravel/socialite": "~4.0|~5.0",
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.2",
|
||||||
|
"phpunit/phpunit": "^9.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"SocialiteProviders\\Manager\\ServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"SocialiteProviders\\Manager\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Andy Wendt",
|
||||||
|
"email": "andy@awendt.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Anton Komarev",
|
||||||
|
"email": "a.komarev@cybercog.su"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Miguel Piedrafita",
|
||||||
|
"email": "soy@miguelpiedrafita.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "atymic",
|
||||||
|
"email": "atymicq@gmail.com",
|
||||||
|
"homepage": "https://atymic.dev"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Easily add new or override built-in providers in Laravel Socialite.",
|
||||||
|
"homepage": "https://socialiteproviders.com/",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/SocialiteProviders/Manager/issues",
|
||||||
|
"source": "https://github.com/SocialiteProviders/Manager/tree/4.0.1"
|
||||||
|
},
|
||||||
|
"time": "2020-12-01T23:09:06+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "sokil/php-isocodes",
|
"name": "sokil/php-isocodes",
|
||||||
"version": "3.3.4",
|
"version": "3.3.4",
|
||||||
|
|
|
@ -170,6 +170,8 @@ return [
|
||||||
LaravelDoctrine\Extensions\BeberleiExtensionsServiceProvider::class,
|
LaravelDoctrine\Extensions\BeberleiExtensionsServiceProvider::class,
|
||||||
\App\Models\Utils\MySQLExtensionsServiceProvider::class,
|
\App\Models\Utils\MySQLExtensionsServiceProvider::class,
|
||||||
\App\libs\Utils\FileSystem\SwiftServiceProvider::class,
|
\App\libs\Utils\FileSystem\SwiftServiceProvider::class,
|
||||||
|
// remove 'Laravel\Socialite\SocialiteServiceProvider',
|
||||||
|
\SocialiteProviders\Manager\ServiceProvider::class, // add
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -223,6 +225,7 @@ return [
|
||||||
'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class,
|
'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class,
|
||||||
'Registry' => LaravelDoctrine\ORM\Facades\Registry::class,
|
'Registry' => LaravelDoctrine\ORM\Facades\Registry::class,
|
||||||
'Doctrine' => LaravelDoctrine\ORM\Facades\Doctrine::class,
|
'Doctrine' => LaravelDoctrine\ORM\Facades\Doctrine::class,
|
||||||
|
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
'version' => env('APP_VERSION', 'XX.XX.XX'),
|
'version' => env('APP_VERSION', 'XX.XX.XX'),
|
||||||
|
|
|
@ -39,4 +39,25 @@ return [
|
||||||
'api_key' => env('SENDGRID_API_KEY'),
|
'api_key' => env('SENDGRID_API_KEY'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// 3rd party idps
|
||||||
|
'facebook' => [
|
||||||
|
'client_id' => env('FACEBOOK_CLIENT_ID'),
|
||||||
|
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
|
||||||
|
'redirect' => env('FACEBOOK_REDIRECT_URI')
|
||||||
|
],
|
||||||
|
'google' => [
|
||||||
|
'client_id' => env('GOOGLE_CLIENT_ID'),
|
||||||
|
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
|
||||||
|
'redirect' => env('GOOGLE_REDIRECT_URI')
|
||||||
|
],
|
||||||
|
'apple' => [
|
||||||
|
'client_id' => env('APPLE_CLIENT_ID'),
|
||||||
|
'client_secret' => env('APPLE_CLIENT_SECRET'),
|
||||||
|
'redirect' => env('APPLE_REDIRECT_URI')
|
||||||
|
],
|
||||||
|
'linkedin' => [
|
||||||
|
'client_id' => env('LINKEDIN_CLIENT_ID'),
|
||||||
|
'client_secret' => env('LINKEDIN_CLIENT_SECRET'),
|
||||||
|
'redirect' => env('LINKEDIN_REDIRECT_URI')
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php namespace Database\Migrations;
|
||||||
|
/**
|
||||||
|
* Copyright 2021 OpenStack Foundation
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
use Doctrine\DBAL\Schema\Schema as Schema;
|
||||||
|
use LaravelDoctrine\Migrations\Schema\Builder;
|
||||||
|
use LaravelDoctrine\Migrations\Schema\Table;
|
||||||
|
/**
|
||||||
|
* Class Version20210512181715
|
||||||
|
* @package Database\Migrations
|
||||||
|
*/
|
||||||
|
class Version20210512181715 extends AbstractMigration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Schema $schema
|
||||||
|
*/
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$builder = new Builder($schema);
|
||||||
|
if($schema->hasTable("users") && !$builder->hasColumn("users","external_provider") ) {
|
||||||
|
$builder->table('users', function (Table $table) {
|
||||||
|
$table->string('external_provider')->setNotnull(false)->setLength(254);
|
||||||
|
$table->string('external_id')->setNotnull(false)->setLength(254);
|
||||||
|
$table->string('external_pic')->setNotnull(false)->setLength(254);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Schema $schema
|
||||||
|
*/
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$builder = new Builder($schema);
|
||||||
|
if($schema->hasTable("users") && $builder->hasColumn("users","external_provider") ) {
|
||||||
|
$builder->table('users', function (Table $table) {
|
||||||
|
$table->dropColumn('external_provider');
|
||||||
|
$table->dropColumn('external_id');
|
||||||
|
$table->dropColumn('external_pic');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
83
package.json
83
package.json
|
@ -5,38 +5,77 @@
|
||||||
"main": "resources/assets/js/index.js",
|
"main": "resources/assets/js/index.js",
|
||||||
"author": "smarcet@gmail.com",
|
"author": "smarcet@gmail.com",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build-dev": "./node_modules/.bin/webpack --config webpack.config.js",
|
"build-dev": "./node_modules/.bin/webpack --config webpack.dev.js",
|
||||||
"build": "NODE_ENV=production ./node_modules/.bin/webpack --config webpack.config.js"
|
"build": "./node_modules/.bin/webpack --config webpack.prod.js",
|
||||||
|
"serve": "webpack-dev-server --open --port=8888 --https --config webpack.dev.js",
|
||||||
|
"test": "jest --watch"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"webpack": "^3.11.0",
|
"@babel/cli": "^7.13.10",
|
||||||
"babel-cli": "^6.26.0",
|
"@babel/core": "^7.13.10",
|
||||||
"babel-core": "^6.26.0",
|
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||||
"babel-loader": "^7.1.4",
|
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
||||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
"@babel/preset-env": "^7.13.12",
|
||||||
"babel-preset-env": "^1.6.1",
|
"@babel/preset-flow": "^7.0.0",
|
||||||
"babel-preset-flow": "^6.23.0",
|
"@babel/preset-react": "^7.13.13",
|
||||||
"css-loader": "^0.28.10",
|
"@testing-library/jest-dom": "^5.12.0",
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
"@testing-library/react": "^11.2.6",
|
||||||
"file-loader": "^1.1.11",
|
"babel-jest": "^26.6.3",
|
||||||
"json-loader": "^0.5.7",
|
"babel-loader": "^8.0.6",
|
||||||
"less": "^2.7.3",
|
"babel-plugin-react-remove-properties": "^0.3.0",
|
||||||
"less-loader": "^4.1.0",
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
"postcss-loader": "^2.1.3",
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
|
"css-loader": "^3.1.0",
|
||||||
|
"dotenv-webpack": "^1.7.0",
|
||||||
|
"file-loader": "^4.1.0",
|
||||||
|
"font-awesome": "4.7.0",
|
||||||
|
"history": "^4.7.2",
|
||||||
|
"html-webpack-plugin": "^3.2.0",
|
||||||
|
"i18n-react": "^0.6.4",
|
||||||
|
"identity-obj-proxy": "^3.0.0",
|
||||||
|
"immutability-helper": "^2.7.1",
|
||||||
|
"jest": "^26.6.3",
|
||||||
|
"mini-css-extract-plugin": "^0.8.0",
|
||||||
|
"node-sass": "^4.12.0",
|
||||||
|
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"react": "^16.6.3",
|
||||||
|
"react-bootstrap": "^0.33.1",
|
||||||
|
"react-datetime": "^2.15.0",
|
||||||
|
"react-dom": "^16.4.1",
|
||||||
|
"react-redux": "^5.0.7",
|
||||||
|
"react-rte-ref-fix": "^0.16.2",
|
||||||
|
"react-test-renderer": "^17.0.2",
|
||||||
|
"react-transition-group": "^1.2.1",
|
||||||
|
"redux": "^3.7.2",
|
||||||
|
"redux-mock-store": "^1.5.4",
|
||||||
|
"redux-persist": "^5.10.0",
|
||||||
|
"redux-thunk": "^2.3.0",
|
||||||
|
"regenerator-runtime": "^0.13.7",
|
||||||
"sass-loader": "^6.0.7",
|
"sass-loader": "^6.0.7",
|
||||||
"style-loader": "^0.19.1",
|
"style-loader": "^0.19.1",
|
||||||
"url-loader": "^0.6.2"
|
"superagent": "^6.1.0",
|
||||||
|
"timesync": "^1.0.5",
|
||||||
|
"urijs": "^1.19.1",
|
||||||
|
"url-loader": "^0.6.2",
|
||||||
|
"webpack": "^4.29.0",
|
||||||
|
"webpack-cli": "^4.7.0",
|
||||||
|
"webpack-dev-server": "^3.1.14",
|
||||||
|
"webpack-merge": "^4.2.1",
|
||||||
|
"webpack-node-externals": "^1.7.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@github/clipboard-copy-element": "^1.1.2",
|
"@github/clipboard-copy-element": "^1.1.2",
|
||||||
|
"@material-ui/core": "^4.11.4",
|
||||||
|
"@material-ui/icons": "^4.11.2",
|
||||||
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||||
"bootstrap": "^3.3.7",
|
"bootstrap": "^3.3.7",
|
||||||
"bootstrap-datepicker": "^1.8.0",
|
"bootstrap-datepicker": "^1.8.0",
|
||||||
"bootstrap-sass": "^3.0.0",
|
"bootstrap-sass": "^3.0.0",
|
||||||
"bootstrap-tagsinput": "^0.7.1",
|
"bootstrap-tagsinput": "^0.7.1",
|
||||||
"chosen-js": "^1.8.7",
|
"chosen-js": "^1.8.7",
|
||||||
"copy-webpack-plugin": "^4.5.1",
|
|
||||||
"crypto-js": "^3.1.9-1",
|
"crypto-js": "^3.1.9-1",
|
||||||
"file-loader": "^1.1.11",
|
|
||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"jquery": "~2.1.4",
|
"jquery": "~2.1.4",
|
||||||
"jquery-migrate": "1.2.1",
|
"jquery-migrate": "1.2.1",
|
||||||
|
@ -44,11 +83,13 @@
|
||||||
"jquery-validation": "^1.17.0",
|
"jquery-validation": "^1.17.0",
|
||||||
"jquery.cookie": "^1.4.1",
|
"jquery.cookie": "^1.4.1",
|
||||||
"jqueryui": "^1.11.1",
|
"jqueryui": "^1.11.1",
|
||||||
"laravel-elixir": "^5.0.0",
|
"jsdom": "^16.5.3",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
|
"moment-timezone": "^0.5.21",
|
||||||
"popper.js": "^1.14.3",
|
"popper.js": "^1.14.3",
|
||||||
"pure": "^2.85.0",
|
"pure": "^2.85.0",
|
||||||
"pwstrength-bootstrap": "^2.2.1",
|
"pwstrength-bootstrap": "^3.0.10",
|
||||||
|
"react-google-recaptcha": "^2.1.0",
|
||||||
"simplemde": "^1.11.2",
|
"simplemde": "^1.11.2",
|
||||||
"sweetalert2": "7.3.5",
|
"sweetalert2": "7.3.5",
|
||||||
"typeahead.js": "^0.11.1",
|
"typeahead.js": "^0.11.1",
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 549 B |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 375 B |
|
@ -0,0 +1,70 @@
|
||||||
|
import request from 'superagent';
|
||||||
|
import URI from "urijs";
|
||||||
|
let http = request;
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
|
||||||
|
export const createAction = type => payload => ({
|
||||||
|
type,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
|
export const RESET_LOADING = 'RESET_LOADING';
|
||||||
|
export const START_LOADING = 'START_LOADING';
|
||||||
|
export const STOP_LOADING = 'STOP_LOADING';
|
||||||
|
|
||||||
|
export const resetLoading = createAction(RESET_LOADING);
|
||||||
|
export const startLoading = createAction(START_LOADING);
|
||||||
|
export const stopLoading = createAction(STOP_LOADING);
|
||||||
|
|
||||||
|
const xhrs = {};
|
||||||
|
|
||||||
|
const cancel = (key) => {
|
||||||
|
if(xhrs[key]) {
|
||||||
|
xhrs[key].abort();
|
||||||
|
console.log(`aborted request ${key}`);
|
||||||
|
delete xhrs[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const schedule = (key, req) => {
|
||||||
|
// console.log(`scheduling ${key}`);
|
||||||
|
xhrs[key] = req;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isObjectEmpty = (obj) => {
|
||||||
|
return Object.keys(obj).length === 0 && obj.constructor === Object ;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRawRequest = (endpoint, errorHandler = null) => (params) => {
|
||||||
|
let url = URI(endpoint);
|
||||||
|
|
||||||
|
if(!isObjectEmpty(params))
|
||||||
|
url = url.query(params);
|
||||||
|
|
||||||
|
let key = url.toString();
|
||||||
|
|
||||||
|
cancel(key);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let req = http.get(url.toString())
|
||||||
|
.timeout({
|
||||||
|
response: 60000,
|
||||||
|
deadline: 60000,
|
||||||
|
})
|
||||||
|
.end(
|
||||||
|
(err, res) => {
|
||||||
|
if (err || !res.ok) {
|
||||||
|
if(errorHandler) {
|
||||||
|
errorHandler(err, res);
|
||||||
|
}
|
||||||
|
return reject({ err, res })
|
||||||
|
}
|
||||||
|
let json = res.body;
|
||||||
|
return resolve({response: json});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
schedule(key, req);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import 'font-awesome/css/font-awesome.min.css';
|
||||||
|
import 'jquery-ui-themes/themes/ui-darkness/jquery-ui.css';
|
||||||
|
import 'sweetalert2/dist/sweetalert2.css';
|
||||||
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
import 'bootstrap-datepicker/dist/css/bootstrap-datepicker.css'
|
||||||
|
window._ = require('lodash');
|
||||||
|
|
||||||
|
import $ from 'jquery';
|
||||||
|
window.jQuery = $;
|
||||||
|
window.$ = $;
|
||||||
|
|
||||||
|
import 'bootstrap'
|
||||||
|
import 'jqueryui'
|
||||||
|
import 'jquery-validation';
|
||||||
|
import 'jquery-validation/dist/additional-methods.js';
|
||||||
|
import swal from 'sweetalert2';
|
||||||
|
import 'pure';
|
||||||
|
import 'pwstrength-bootstrap/dist/pwstrength-bootstrap.js';
|
||||||
|
import 'bootstrap-datepicker/dist/js/bootstrap-datepicker.js';
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React from "react";
|
||||||
|
import { makeStyles } from "@material-ui/core";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(theme => ({
|
||||||
|
container: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center"
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
borderBottom: "1px solid #3fa2f7",
|
||||||
|
width: "100%"
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
paddingTop: theme.spacing(0.5),
|
||||||
|
paddingBottom: theme.spacing(0.5),
|
||||||
|
paddingRight: theme.spacing(2),
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: 16,
|
||||||
|
color: "#3fa2f7"
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const DividerWithText = ({ children }) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
return (
|
||||||
|
<div className={classes.container}>
|
||||||
|
<div className={classes.border} />
|
||||||
|
<span className={classes.content}>{children}</span>
|
||||||
|
<div className={classes.border} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default DividerWithText;
|
|
@ -10,20 +10,4 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
import 'font-awesome/css/font-awesome.min.css';
|
import './bootstrap';
|
||||||
import 'jquery-ui-themes/themes/ui-darkness/jquery-ui.css';
|
|
||||||
import 'sweetalert2/dist/sweetalert2.css';
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
import 'bootstrap-datepicker/dist/css/bootstrap-datepicker.css'
|
|
||||||
|
|
||||||
import $ from 'jquery';
|
|
||||||
window.jQuery = $;
|
|
||||||
window.$ = $;
|
|
||||||
import 'bootstrap'
|
|
||||||
import 'jqueryui'
|
|
||||||
import 'jquery-validation';
|
|
||||||
import 'jquery-validation/dist/additional-methods.js';
|
|
||||||
import swal from 'sweetalert2';
|
|
||||||
import 'pure';
|
|
||||||
import 'pwstrength-bootstrap/dist/pwstrength-bootstrap.js';
|
|
||||||
import 'bootstrap-datepicker/dist/js/bootstrap-datepicker.js';
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import {getRawRequest} from '../base_actions'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const verifyAccount = (email) => {
|
||||||
|
const params = {
|
||||||
|
email: email
|
||||||
|
};
|
||||||
|
|
||||||
|
return getRawRequest(window.VERIFY_ACCOUNT_ENDPOINT)(params);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,427 @@
|
||||||
|
import styles from './login.module.scss'
|
||||||
|
import "./third_party_identity_providers.scss";
|
||||||
|
import React from 'react';
|
||||||
|
import ReCAPTCHA from "react-google-recaptcha";
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import Avatar from '@material-ui/core/Avatar';
|
||||||
|
import Button from '@material-ui/core/Button';
|
||||||
|
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||||
|
import TextField from '@material-ui/core/TextField';
|
||||||
|
import Link from '@material-ui/core/Link';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
import Container from '@material-ui/core/Container';
|
||||||
|
import Chip from '@material-ui/core/Chip';
|
||||||
|
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||||
|
import Checkbox from '@material-ui/core/Checkbox';
|
||||||
|
import {verifyAccount} from './actions';
|
||||||
|
import {MuiThemeProvider, createMuiTheme} from '@material-ui/core/styles';
|
||||||
|
import DividerWithText from '../components/divider_with_text';
|
||||||
|
import Visibility from '@material-ui/icons/Visibility';
|
||||||
|
import VisibilityOff from '@material-ui/icons/VisibilityOff';
|
||||||
|
import InputAdornment from '@material-ui/core/InputAdornment';
|
||||||
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
import {emailValidator} from '../validator';
|
||||||
|
import Grid from '@material-ui/core/Grid';
|
||||||
|
|
||||||
|
|
||||||
|
const EmailInputForm = ({onValidateEmail, onHandleUserNameChange, disableInput, emailError}) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper elevation={0} component="form"
|
||||||
|
target="_self"
|
||||||
|
className={styles.paper_root}
|
||||||
|
onSubmit={onValidateEmail}>
|
||||||
|
<TextField
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
autoComplete="email"
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
disabled={disableInput}
|
||||||
|
label="Email Address"
|
||||||
|
autoFocus
|
||||||
|
onChange={onHandleUserNameChange}
|
||||||
|
error={emailError != ""}
|
||||||
|
helperText={emailError}
|
||||||
|
/>
|
||||||
|
{emailError == "" &&
|
||||||
|
<Button variant="contained"
|
||||||
|
color="primary"
|
||||||
|
title="Continue"
|
||||||
|
className={styles.apply_button}
|
||||||
|
disabled={disableInput}
|
||||||
|
onClick={onValidateEmail}>
|
||||||
|
>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PasswordInputForm = ({
|
||||||
|
formAction,
|
||||||
|
onAuthenticate,
|
||||||
|
disableInput,
|
||||||
|
showPassword,
|
||||||
|
passwordValue,
|
||||||
|
passwordError,
|
||||||
|
onUserPasswordChange,
|
||||||
|
handleClickShowPassword,
|
||||||
|
handleMouseDownPassword,
|
||||||
|
userNameValue,
|
||||||
|
csrfToken,
|
||||||
|
shouldShowCaptcha,
|
||||||
|
captchaPublicKey,
|
||||||
|
onChangeRecaptcha
|
||||||
|
}) => {
|
||||||
|
return(
|
||||||
|
<form method="post" action={formAction} onSubmit={onAuthenticate} target="_self">
|
||||||
|
<TextField
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
disabled={disableInput}
|
||||||
|
type={showPassword ? 'text' : 'password'}
|
||||||
|
value={passwordValue}
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
label="Enter Your Password"
|
||||||
|
autoComplete="current-password"
|
||||||
|
error={passwordError != ""}
|
||||||
|
helperText={passwordError}
|
||||||
|
onChange={onUserPasswordChange}
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<IconButton
|
||||||
|
aria-label="toggle password visibility"
|
||||||
|
onClick={handleClickShowPassword}
|
||||||
|
onMouseDown={handleMouseDownPassword}
|
||||||
|
edge="end"
|
||||||
|
>
|
||||||
|
{showPassword ? <Visibility/> : <VisibilityOff/>}
|
||||||
|
</IconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
disabled={disableInput}
|
||||||
|
control={<Checkbox value="remember" name="remember" id="remember" color="primary"/>}
|
||||||
|
label="Remember me"
|
||||||
|
/>
|
||||||
|
<input type="hidden" value={userNameValue} id="username" name="username"/>
|
||||||
|
<input type="hidden" value={csrfToken} id="_token" name="_token"/>
|
||||||
|
{shouldShowCaptcha() &&
|
||||||
|
<ReCAPTCHA
|
||||||
|
className={styles.recaptcha}
|
||||||
|
sitekey={captchaPublicKey}
|
||||||
|
onChange={onChangeRecaptcha}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
<Button variant="contained"
|
||||||
|
disabled={disableInput}
|
||||||
|
className={styles.continue_btn}
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
onClick={onAuthenticate}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const HelpLinks = ({forgotPasswordAction, verifyEmailAction, helpAction, appName}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<hr className={styles.separator}/>
|
||||||
|
<Link href={forgotPasswordAction} target="_self" variant="body2">
|
||||||
|
Forgot password?
|
||||||
|
</Link>
|
||||||
|
<Link href={verifyEmailAction} target="_self" variant="body2">
|
||||||
|
Verify {appName}
|
||||||
|
</Link>
|
||||||
|
<Link href={helpAction} variant="body2" target="_self">
|
||||||
|
Having trouble?
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmailErrorActions = ({createAccountAction, onValidateEmail, disableInput}) => {
|
||||||
|
return(
|
||||||
|
<Grid container style={{alignItems: 'center', marginTop: "20%"}}>
|
||||||
|
<Grid item xs>
|
||||||
|
<Link href={createAccountAction} variant="body2" target="_self">
|
||||||
|
Create Account
|
||||||
|
</Link>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Button variant="contained"
|
||||||
|
onClick={onValidateEmail}
|
||||||
|
disabled={disableInput}
|
||||||
|
color="primary">
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThirdPartyIdentityProviders = ({thirdPartyProviders, formAction, disableInput}) => {
|
||||||
|
return(
|
||||||
|
<>
|
||||||
|
<DividerWithText>Or</DividerWithText>
|
||||||
|
{
|
||||||
|
thirdPartyProviders.map((provider) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
disabled={disableInput}
|
||||||
|
key={provider.name}
|
||||||
|
variant="contained"
|
||||||
|
className={styles.third_party_idp_button+` ${provider.name}`}
|
||||||
|
color="primary"
|
||||||
|
title={`Sign In with ${provider.label}`}
|
||||||
|
href={`${formAction}/${provider.name}`}>
|
||||||
|
{provider.label}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginPage extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
user_name: props.userName,
|
||||||
|
user_password: '',
|
||||||
|
user_pic: props.hasOwnProperty('user_pic') ? props.user_pic : null,
|
||||||
|
user_fullname: props.hasOwnProperty('user_fullname') ? props.user_fullname : null,
|
||||||
|
user_verified: props.hasOwnProperty('user_verified') ? props.user_verified : false,
|
||||||
|
errors: {
|
||||||
|
email: "",
|
||||||
|
password: props.authError != "" ? props.authError : "",
|
||||||
|
},
|
||||||
|
captcha_value: '',
|
||||||
|
showPassword: false,
|
||||||
|
disableInput: false,
|
||||||
|
}
|
||||||
|
this.onHandleUserNameChange = this.onHandleUserNameChange.bind(this);
|
||||||
|
this.onValidateEmail = this.onValidateEmail.bind(this);
|
||||||
|
this.handleDelete = this.handleDelete.bind(this);
|
||||||
|
this.onAuthenticate = this.onAuthenticate.bind(this);
|
||||||
|
this.onChangeRecaptcha = this.onChangeRecaptcha.bind(this);
|
||||||
|
this.onUserPasswordChange = this.onUserPasswordChange.bind(this);
|
||||||
|
this.shouldShowCaptcha = this.shouldShowCaptcha.bind(this);
|
||||||
|
this.handleClickShowPassword = this.handleClickShowPassword.bind(this);
|
||||||
|
this.handleMouseDownPassword = this.handleMouseDownPassword.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldShowCaptcha() {
|
||||||
|
return (
|
||||||
|
this.props.hasOwnProperty('maxLoginAttempts2ShowCaptcha') &&
|
||||||
|
this.props.hasOwnProperty('loginAttempts') &&
|
||||||
|
this.props.loginAttempts >= this.props.maxLoginAttempts2ShowCaptcha
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onAuthenticate(ev) {
|
||||||
|
if (this.state.user_password == '') {
|
||||||
|
this.setState({...this.state, errors: {...this.state.errors, password: 'Password is empty'}});
|
||||||
|
ev.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.state.captcha_value == '' && this.shouldShowCaptcha()) {
|
||||||
|
this.setState({...this.state, errors: {...this.state.errors, password: 'you must check CAPTCHA'}});
|
||||||
|
ev.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeRecaptcha(value) {
|
||||||
|
this.setState({...this.state, captcha_value: value});
|
||||||
|
}
|
||||||
|
|
||||||
|
onHandleUserNameChange(ev) {
|
||||||
|
let {value, id} = ev.target;
|
||||||
|
this.setState({...this.state, user_name: value});
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserPasswordChange(ev) {
|
||||||
|
let {errors} = this.state;
|
||||||
|
let {value, id} = ev.target;
|
||||||
|
if(value == "") // clean error
|
||||||
|
errors[id] = '';
|
||||||
|
this.setState({...this.state, user_password: value, errors: {...errors}});
|
||||||
|
}
|
||||||
|
|
||||||
|
onValidateEmail(ev) {
|
||||||
|
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
if (this.state.user_name == '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emailValidator(this.state.user_name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.setState({...this.state, disableInput: true});
|
||||||
|
verifyAccount(this.state.user_name).then((payload) => {
|
||||||
|
let {response} = payload;
|
||||||
|
this.setState({
|
||||||
|
...this.state,
|
||||||
|
user_pic: response.pic,
|
||||||
|
user_fullname: response.full_name,
|
||||||
|
user_verified: true,
|
||||||
|
errors: {
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
},
|
||||||
|
disableInput: false
|
||||||
|
})
|
||||||
|
}, (error) => {
|
||||||
|
let {body} = error.res;
|
||||||
|
let newErrors = {}
|
||||||
|
newErrors['password'] = '';
|
||||||
|
newErrors['email'] = "We could not find an Account with that email Address";
|
||||||
|
this.setState({
|
||||||
|
...this.state,
|
||||||
|
user_pic: null,
|
||||||
|
user_fullname: null,
|
||||||
|
user_verified: false,
|
||||||
|
errors: newErrors,
|
||||||
|
disableInput: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDelete() {
|
||||||
|
this.setState({...this.state, user_name: null, user_pic: null, user_fullname: null, user_verified: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClickShowPassword(ev) {
|
||||||
|
this.setState({...this.state, showPassword: !this.state.showPassword})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseDownPassword(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Container component="main" maxWidth="xs" className={styles.main_container}>
|
||||||
|
<CssBaseline/>
|
||||||
|
<div className={styles.inner_container}>
|
||||||
|
<Typography component="h1">
|
||||||
|
<a href={window.location.href}><img className={styles.app_logo} alt="appLogo" src={this.props.appLogo}/></a>
|
||||||
|
</Typography>
|
||||||
|
<Typography component="h1" variant="h5">
|
||||||
|
Sign in {this.state.user_fullname && <Chip
|
||||||
|
avatar={<Avatar alt={this.state.user_fullname} src={this.state.user_pic}/>}
|
||||||
|
variant="outlined"
|
||||||
|
className={styles.valid_user_name_chip}
|
||||||
|
label={this.state.user_fullname}
|
||||||
|
onDelete={this.handleDelete}/>}
|
||||||
|
</Typography>
|
||||||
|
{!this.state.user_verified &&
|
||||||
|
<>
|
||||||
|
<EmailInputForm
|
||||||
|
onValidateEmail={this.onValidateEmail}
|
||||||
|
onHandleUserNameChange={this.onHandleUserNameChange}
|
||||||
|
disableInput={this.state.disableInput}
|
||||||
|
emailError={this.state.errors.email}/>
|
||||||
|
|
||||||
|
{ this.state.errors.email == '' &&
|
||||||
|
this.props.thirdPartyProviders.length > 0 &&
|
||||||
|
<ThirdPartyIdentityProviders
|
||||||
|
thirdPartyProviders={this.props.thirdPartyProviders}
|
||||||
|
formAction={this.props.formAction}
|
||||||
|
disableInput={this.state.disableInput}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// we already had an interaction and got an user error...
|
||||||
|
this.state.errors.email != '' &&
|
||||||
|
<>
|
||||||
|
<EmailErrorActions
|
||||||
|
onValidateEmail={this.onValidateEmail}
|
||||||
|
disableInput={this.state.disableInput}
|
||||||
|
createAccountAction={(this.state.user_name) ? `${this.props.createAccountAction}?email=${encodeURIComponent(this.state.user_name)}`: this.props.createAccountAction}
|
||||||
|
/>
|
||||||
|
<HelpLinks
|
||||||
|
appName={this.props.appName}
|
||||||
|
forgotPasswordAction={this.props.forgotPasswordAction}
|
||||||
|
verifyEmailAction={this.props.verifyEmailAction}
|
||||||
|
helpAction={this.props.helpAction}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{this.state.user_verified &&
|
||||||
|
// proceed to ask for password ( 2nd step )
|
||||||
|
<>
|
||||||
|
<PasswordInputForm
|
||||||
|
formAction={this.props.formAction}
|
||||||
|
onAuthenticate={this.onAuthenticate}
|
||||||
|
disableInput={this.state.disableInput}
|
||||||
|
showPassword={this.state.showPassword}
|
||||||
|
passwordValue={this.state.user_password}
|
||||||
|
passwordError={this.state.errors.password}
|
||||||
|
onUserPasswordChange={this.onUserPasswordChange}
|
||||||
|
handleClickShowPassword={this.handleClickShowPassword}
|
||||||
|
handleMouseDownPassword={this.handleMouseDownPassword}
|
||||||
|
userNameValue={this.state.user_name}
|
||||||
|
csrfToken={this.props.token}
|
||||||
|
shouldShowCaptcha={this.shouldShowCaptcha}
|
||||||
|
captchaPublicKey={this.props.captchaPublicKey}
|
||||||
|
onChangeRecaptcha={this.onChangeRecaptcha}
|
||||||
|
/>
|
||||||
|
<HelpLinks
|
||||||
|
appName={this.props.appName}
|
||||||
|
forgotPasswordAction={this.props.forgotPasswordAction}
|
||||||
|
verifyEmailAction={this.props.verifyEmailAction}
|
||||||
|
helpAction={this.props.helpAction}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or Create your Own theme:
|
||||||
|
const theme = createMuiTheme({
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#3fa2f7'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
overrides: {
|
||||||
|
MuiButton: {
|
||||||
|
containedPrimary: {
|
||||||
|
color: 'white'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<MuiThemeProvider theme={theme}>
|
||||||
|
<LoginPage {...config}/>
|
||||||
|
</MuiThemeProvider>,
|
||||||
|
document.querySelector('#root')
|
||||||
|
);
|
|
@ -0,0 +1,74 @@
|
||||||
|
$border-color: #e3e3e3;
|
||||||
|
$base-color:#3fa2f7;
|
||||||
|
|
||||||
|
.main_container{
|
||||||
|
border: solid 1px $border-color;
|
||||||
|
margin-top: 10%;
|
||||||
|
margin-bottom: 10%;
|
||||||
|
color: $base-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner_container {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 64px;
|
||||||
|
padding-bottom: 20%;
|
||||||
|
flex-direction: column;
|
||||||
|
.app_logo{
|
||||||
|
max-width: 40%;
|
||||||
|
float: right;
|
||||||
|
margin-bottom: 15%;
|
||||||
|
}
|
||||||
|
.continue_btn{
|
||||||
|
float: right;
|
||||||
|
margin-top: 30%;
|
||||||
|
}
|
||||||
|
.recaptcha{
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.separator{
|
||||||
|
color:$base-color;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid;
|
||||||
|
margin-bottom: 5%;
|
||||||
|
margin-top: 5%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paper_root {
|
||||||
|
padding: 2px 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.apply_button {
|
||||||
|
height: 55px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-left: 10px;
|
||||||
|
min-width: 20px !important;
|
||||||
|
font-size: 20pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.third_party_idp_button {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 5px !important;
|
||||||
|
margin-bottom: 5px !important;
|
||||||
|
position:relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.third_party_idp_button:before{
|
||||||
|
content: "";
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 34px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.valid_user_name_chip{
|
||||||
|
float: right;
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
$facebook_base_color:#3B5998;
|
||||||
|
$apple_base_color:#000000;
|
||||||
|
$google_base_color:#DD4B39;
|
||||||
|
$linkedin_base_color:#2867B2;
|
||||||
|
|
||||||
|
.facebook{
|
||||||
|
background-color: $facebook_base_color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.facebook:hover,
|
||||||
|
.facebook:focus {
|
||||||
|
background-color: #5B7BD5;
|
||||||
|
background-image: linear-gradient(#5B7BD5, #4864B1);
|
||||||
|
}
|
||||||
|
.facebook:before {
|
||||||
|
border-right: #364e92 1px solid !important;
|
||||||
|
background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/14082/icon_facebook.png') 6px 6px no-repeat !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apple{
|
||||||
|
background-color: $apple_base_color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apple:before {
|
||||||
|
border-right: #000001 1px solid !important;
|
||||||
|
background: url('/assets/img/third_party_identity_providers/icon_apple.png') 6px 6px no-repeat !important;
|
||||||
|
}
|
||||||
|
.google:before {
|
||||||
|
border-right: #BB3F30 1px solid;
|
||||||
|
background: url('/assets/img/third_party_identity_providers/icon_google.png') 6px 6px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.google {
|
||||||
|
background-color: $google_base_color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.google:hover,
|
||||||
|
.google:focus {
|
||||||
|
background: #E74B37;
|
||||||
|
}
|
||||||
|
|
||||||
|
.google:before {
|
||||||
|
border-right: #BB3F30 1px solid;
|
||||||
|
background: url('/assets/img/third_party_identity_providers/icon_google.png') 6px 6px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.linkedin{
|
||||||
|
background-color: $linkedin_base_color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.linkedin:hover,
|
||||||
|
.linkedin:focus {
|
||||||
|
background-color: #2867B3;
|
||||||
|
background-image: linear-gradient(#5B7BD5, #4864B1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.linkedin:before {
|
||||||
|
border-right: #4864B1 1px solid;
|
||||||
|
background: url('/assets/img/third_party_identity_providers/icon_linkedin.png') 6px 6px no-repeat;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const emailValidator = (value) => {
|
||||||
|
return /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value )
|
||||||
|
}
|
|
@ -34,7 +34,9 @@
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div class="signup-form">
|
<div class="signup-form">
|
||||||
<form id="form-verification" method="POST" action="{{ URL::action('Auth\EmailVerificationController@resend') }}">
|
<form id="form-verification"
|
||||||
|
target="_self"
|
||||||
|
method="POST" action="{{ URL::action('Auth\EmailVerificationController@resend') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<h2>{{ __('Email Verification') }}</h2>
|
<h2>{{ __('Email Verification') }}</h2>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -1,114 +1,71 @@
|
||||||
@extends('layout')
|
@extends('auth_layout')
|
||||||
@section('title')
|
@section('title')
|
||||||
<title>Welcome to {{ Config::get("app.app_name") }} - Sign in </title>
|
<title>Welcome to {{ Config::get("app.app_name") }} - Sign in </title>
|
||||||
@append
|
@append
|
||||||
@section('meta')
|
@section('meta')
|
||||||
<meta http-equiv="X-XRDS-Location" content="{!! URL::action("OpenId\DiscoveryController@idp") !!}"/>
|
<meta http-equiv="X-XRDS-Location" content="{!! URL::action("OpenId\DiscoveryController@idp") !!}"/>
|
||||||
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
@append
|
@append
|
||||||
@section('css')
|
@section('css')
|
||||||
{!! HTML::style('assets/css/auth/login.css') !!}
|
{!! HTML::style('assets/css/login.css') !!}
|
||||||
@append
|
@append
|
||||||
@section('content')
|
@section('content')
|
||||||
@if(isset($identity_select))
|
|
||||||
<legend style="margin-left: 15px;">
|
|
||||||
@if(!$identity_select)
|
|
||||||
Sign in to <b>{!! $realm !!}</b> using <b>{!! $identity !!}</b>
|
|
||||||
@else
|
|
||||||
Sign in to <b>{!! $realm !!} </b> using your {{ Config::get("app.app_name") }}
|
|
||||||
@endif
|
|
||||||
</legend>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div id="cookies-disabled-dialog" class="alert alert-warning alert-dismissible" style="display: none;" role="alert">
|
|
||||||
<button type="button" class="close" onclick="$('#cookies-disabled-dialog').hide()" aria-label="Close"><span
|
|
||||||
aria-hidden="true">×</span></button>
|
|
||||||
<strong>Warning!</strong> Cookies are not enabled, please enabled them in order to use {{ Config::get("app.app_name") }}.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-4" id="sidebar">
|
|
||||||
<div class="well">
|
|
||||||
{!! Form::open(array('id'=>'login_form','url' => URL::action('UserController@postLogin'), 'method' => 'post', "autocomplete" => "off")) !!}
|
|
||||||
<legend>
|
|
||||||
Welcome to {{ Config::get("app.app_name") }}! <span aria-hidden="true" style="font-size: 10pt;"
|
|
||||||
class="glyphicon glyphicon-info-sign pointable"
|
|
||||||
title="Please use your {{ Config::get("app.app_name") }} to log in"></span>
|
|
||||||
</legend>
|
|
||||||
@if(Config::get("app.app_info"))
|
|
||||||
<p class="help-block">
|
|
||||||
{{Config::get("app.app_info")}}
|
|
||||||
</p>
|
|
||||||
@endif
|
|
||||||
<div class="form-group">
|
|
||||||
{!! Form::email('username',Session::has('username')? Session::get('username'):null, array
|
|
||||||
(
|
|
||||||
'placeholder' => 'Username',
|
|
||||||
'class' =>'form-control',
|
|
||||||
'required' => 'true',
|
|
||||||
'autocomplete' => 'username'
|
|
||||||
)) !!}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<input placeholder="Password" class="form-control" required="true" autocomplete="current-password" name="password" id="password" type="password" value="">
|
|
||||||
<span toggle="#password" class="fa fa-fw fa-eye fa-eye-slash field-icon toggle-password" title="Show Password"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
@if(Session::has('flash_notice'))
|
|
||||||
<span class="error-message"><i
|
|
||||||
class="fa fa-exclamation-triangle"> {!! Session::get('flash_notice') !!}</i></span>
|
|
||||||
@else
|
|
||||||
@foreach($errors->all() as $message)
|
|
||||||
<span class="error-message"><i
|
|
||||||
class="fa fa-exclamation-triangle"> {!! $message !!}</i></span>
|
|
||||||
@endforeach
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
@if(Session::has('login_attempts') && Session::has('max_login_attempts_2_show_captcha') && Session::get('login_attempts') > Session::get('max_login_attempts_2_show_captcha'))
|
|
||||||
{!! Recaptcha::render(array('id'=>'captcha','class'=>'input-block-level')) !!}
|
|
||||||
{!! Form::hidden('login_attempts', Session::get('login_attempts')) !!}
|
|
||||||
@else
|
|
||||||
{!! Form::hidden('login_attempts', '0') !!}
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div class="checkbox">
|
|
||||||
<label class="checkbox">
|
|
||||||
{!! Form::checkbox('remember', '1', false) !!}Remember me
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="pull-right">
|
|
||||||
{!! Form::submit('Sign In',array('id'=>'login','class'=>'btn btn-primary')) !!}
|
|
||||||
<a class="btn btn-primary" href="{!! URL::action('UserController@cancelLogin') !!} ">Cancel</a>
|
|
||||||
</div>
|
|
||||||
<div style="clear:both;padding-top:15px;" class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<a title="register new account" target="_blank"
|
|
||||||
href="{!! URL::action("Auth\RegisterController@showRegistrationForm") !!}">Create an
|
|
||||||
{!! Config::get("app.app_name") !!} </a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="clear:both;padding-top:15px;" class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<a title="forgot password" target="_blank"
|
|
||||||
href="{!! URL::action("Auth\ForgotPasswordController@showLinkRequestForm") !!}">Forgot password?</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="clear:both;padding-top:15px;" class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<a title="verify account" target="_blank" href="{!! URL::action("Auth\EmailVerificationController@showVerificationForm") !!}">Verify
|
|
||||||
{!! Config::get("app.app_name") !!}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="clear:both;padding-top:15px;" class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<a title="help" target="_blank" href="mailto:{!! Config::get("app.help_email") !!}">Help</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
{!! Form::close() !!}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-8">
|
|
||||||
</div>
|
|
||||||
@append
|
@append
|
||||||
@section('scripts')
|
@section('scripts')
|
||||||
{!! HTML::script('assets/js/login.js') !!}
|
<script>
|
||||||
|
|
||||||
|
let authError = '';
|
||||||
|
@if(Session::has('flash_notice'))
|
||||||
|
authError = '{!! Session::get("flash_notice") !!}';
|
||||||
|
@else
|
||||||
|
@foreach($errors->all() as $message)
|
||||||
|
authError = '{!! $message !!}';
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
token : document.head.querySelector('meta[name="csrf-token"]').content,
|
||||||
|
userName:'{{ Session::has('username') ? Session::get('username') : ""}}',
|
||||||
|
realm: '{{isset($identity_select) ? $realm : ""}}',
|
||||||
|
appName: '{{ Config::get("app.app_name") }}',
|
||||||
|
appLogo: '{{ Config::get("app.logo_url") }}',
|
||||||
|
formAction: '{{ URL::action("UserController@postLogin") }}',
|
||||||
|
accountVerifyAction : '{{URL::action("UserController@getAccount")}}',
|
||||||
|
authError: authError,
|
||||||
|
captchaPublicKey: '{{ Config::get("recaptcha.public_key") }}',
|
||||||
|
thirdPartyProviders: [
|
||||||
|
@foreach($supported_providers as $provider => $label)
|
||||||
|
{label: "{{$label}}", name:"{{$provider}}"},
|
||||||
|
@endforeach
|
||||||
|
],
|
||||||
|
forgotPasswordAction:'{{ URL::action("Auth\ForgotPasswordController@showLinkRequestForm") }}',
|
||||||
|
verifyEmailAction:'{{ URL::action("Auth\EmailVerificationController@showVerificationForm") }}',
|
||||||
|
helpAction:'mailto:{!! Config::get("app.help_email") !!}',
|
||||||
|
createAccountAction:'{{ URL::action("Auth\RegisterController@showRegistrationForm") }}',
|
||||||
|
}
|
||||||
|
|
||||||
|
@if(Session::has('max_login_attempts_2_show_captcha'))
|
||||||
|
config.maxLoginAttempts2ShowCaptcha = {{Session::get("max_login_attempts_2_show_captcha")}};
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(Session::has('login_attempts'))
|
||||||
|
config.loginAttempts = {{Session::get("login_attempts")}};
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(Session::has('user_fullname'))
|
||||||
|
config.user_fullname = '{{Session::get("user_fullname")}}';
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(Session::has('user_pic'))
|
||||||
|
config.user_pic = '{{Session::get("user_pic")}}';
|
||||||
|
@endif
|
||||||
|
@if(Session::has('user_verified'))
|
||||||
|
config.user_verified = {{Session::get('user_verified')}};
|
||||||
|
@endif
|
||||||
|
|
||||||
|
window.VERIFY_ACCOUNT_ENDPOINT = config.accountVerifyAction;
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{!! HTML::script('assets/login.js') !!}
|
||||||
@append
|
@append
|
|
@ -35,7 +35,9 @@
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div class="signup-form">
|
<div class="signup-form">
|
||||||
<form id="form-send-password-reset-link" method="POST" action="{{ URL::action('Auth\ForgotPasswordController@sendResetLinkEmail') }}">
|
<form id="form-send-password-reset-link"
|
||||||
|
target="_self"
|
||||||
|
method="POST" action="{{ URL::action('Auth\ForgotPasswordController@sendResetLinkEmail') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<h2>Forgot Password?</h2>
|
<h2>Forgot Password?</h2>
|
||||||
<p class="hint-text">You can reset your password here.</p>
|
<p class="hint-text">You can reset your password here.</p>
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
@endif
|
@endif
|
||||||
<div class="signup-form">
|
<div class="signup-form">
|
||||||
<form id="form-password-reset" class="form-horizontal" method="POST"
|
<form id="form-password-reset" class="form-horizontal" method="POST"
|
||||||
|
target="_self"
|
||||||
action="{{ URL::action('Auth\ResetPasswordController@reset') }}">
|
action="{{ URL::action('Auth\ResetPasswordController@reset') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="token" value="{{ $token }}">
|
<input type="hidden" name="token" value="{{ $token }}">
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
@endif
|
@endif
|
||||||
<div class="signup-form">
|
<div class="signup-form">
|
||||||
<form id="form-password-set" class="form-horizontal" method="POST"
|
<form id="form-password-set" class="form-horizontal" method="POST"
|
||||||
|
target="_self"
|
||||||
action="{{ URL::action('Auth\PasswordSetController@setPassword') }}">
|
action="{{ URL::action('Auth\PasswordSetController@setPassword') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="token" value="{{ $token }}">
|
<input type="hidden" name="token" value="{{ $token }}">
|
||||||
|
|
|
@ -29,7 +29,9 @@
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div class="col-xs-12 col-md-5 col-md-offset-3 signup-form">
|
<div class="col-xs-12 col-md-5 col-md-offset-3 signup-form">
|
||||||
<form id="form-registration" method="POST" autocomplete="off" action="{{ URL::action('Auth\RegisterController@register') }}">
|
<form id="form-registration"
|
||||||
|
target="_self"
|
||||||
|
method="POST" autocomplete="off" action="{{ URL::action('Auth\RegisterController@register') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<h2>Register</h2>
|
<h2>Register</h2>
|
||||||
<p class="hint-text">Create your account. It's free and only takes a minute.</p>
|
<p class="hint-text">Create your account. It's free and only takes a minute.</p>
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
@yield('title')
|
||||||
|
<base href="{!! Config::get('app.url') !!}" target="_self">
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="minimum-scale=1, initial-scale=1, width=device-width"
|
||||||
|
/>
|
||||||
|
<link rel="shortcut icon" href="{!! Config::get('app.tenant_favicon') !!}" />
|
||||||
|
@yield('meta')
|
||||||
|
@yield('css')
|
||||||
|
<!--https://material-ui.com/getting-started/installation/-->
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
|
||||||
|
<style type="text/css">
|
||||||
|
#logo a {
|
||||||
|
background: url("{!! Config::get('app.logo_url') !!}") no-repeat scroll left center rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root">
|
||||||
|
@yield('content')
|
||||||
|
</div>
|
||||||
|
@yield('scripts')
|
||||||
|
<span style="display: none">{!! Config::get('app.version') !!}</span>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -16,12 +16,12 @@
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="row" style="padding-top: 5px;padding-bottom: 5px;">
|
<div class="row" style="padding-top: 5px;padding-bottom: 5px;">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<a href="{!! URL::action("UserController@getLogin") !!}" class="btn btn-default btn-md active">Sign in to your account</a>
|
<a target="_self" href="{!! URL::action("UserController@getLogin") !!}" class="btn btn-default btn-md active">Sign in to your account</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="padding-top: 5px;padding-bottom: 5px;">
|
<div class="row" style="padding-top: 5px;padding-bottom: 5px;">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<a href="{!! URL::action("Auth\RegisterController@showRegistrationForm") !!}" class="btn btn-default btn-md active">Register for an {{ Config::get('app.app_name') }}</a>
|
<a target="_self" href="{!! URL::action("Auth\RegisterController@showRegistrationForm") !!}" class="btn btn-default btn-md active">Register for an {{ Config::get('app.app_name') }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@yield('title')
|
@yield('title')
|
||||||
|
<base href="{!! Config::get('app.url') !!}" target="_blank">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="shortcut icon" href="{!! Config::get('app.tenant_favicon') !!}" />
|
<link rel="shortcut icon" href="{!! Config::get('app.tenant_favicon') !!}" />
|
||||||
@yield('meta')
|
@yield('meta')
|
||||||
|
@ -33,7 +34,6 @@
|
||||||
</div>
|
</div>
|
||||||
<footer class="row"></footer>
|
<footer class="row"></footer>
|
||||||
</div>
|
</div>
|
||||||
{!! HTML::script('assets/__common__.js')!!}
|
|
||||||
{!! HTML::script('assets/index.js')!!}
|
{!! HTML::script('assets/index.js')!!}
|
||||||
{!! HTML::script('assets/js/ajax.utils.js')!!}
|
{!! HTML::script('assets/js/ajax.utils.js')!!}
|
||||||
{!! HTML::script('assets/js/jquery.cleanform.js')!!}
|
{!! HTML::script('assets/js/jquery.cleanform.js')!!}
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
<li> {!!$scope->getShortDescription()!!} <span class="glyphicon glyphicon-info-sign pointable info" aria-hidden="true" data-content="{!! $scope->getDescription() !!}" title="Scope Info"></span></li>
|
<li> {!!$scope->getShortDescription()!!} <span class="glyphicon glyphicon-info-sign pointable info" aria-hidden="true" data-content="{!! $scope->getDescription() !!}" title="Scope Info"></span></li>
|
||||||
@endforeach
|
@endforeach
|
||||||
</ul>
|
</ul>
|
||||||
{!! Form::open(array('url' => URL::action("UserController@postConsent") ,'id'=>'authorization_form', 'method' => 'post', "autocomplete" => "off")) !!}
|
{!! Form::open(array('url' => URL::action("UserController@postConsent") ,'id'=>'authorization_form', 'method' => 'post', "autocomplete" => "off", "target"=> "_self" )) !!}
|
||||||
|
|
||||||
<input type="hidden" name='trust' id='trust' value=""/>
|
<input type="hidden" name='trust' id='trust' value=""/>
|
||||||
<button class="btn btn-default btn-md btn-consent-action" id="cancel-authorization" type="button">Cancel</button>
|
<button class="btn btn-default btn-md btn-consent-action" id="cancel-authorization" type="button">Cancel</button>
|
||||||
<button class="btn btn-primary btn-md btn-consent-action" id="approve-authorization" type="button">Accept</button>
|
<button class="btn btn-primary btn-md btn-consent-action" id="approve-authorization" type="button">Accept</button>
|
||||||
|
|
|
@ -12,7 +12,7 @@ Welcome, <a href="{!! URL::action("UserController@getProfile") !!}">{!!Auth::use
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h4>{!! Config::get('app.app_name') !!} - Openid verification</h4>
|
<h4>{!! Config::get('app.app_name') !!} - Openid verification</h4>
|
||||||
{!! Form::open(array('url' => URL::action("UserController@postConsent"),'id'=>'authorization_form', 'method' => 'post', "autocomplete" => "off")) !!}
|
{!! Form::open(array('url' => URL::action("UserController@postConsent"),'id'=>'authorization_form', 'method' => 'post', "autocomplete" => "off", "target" => "_self")) !!}
|
||||||
<legend>
|
<legend>
|
||||||
Sign in to <b>{!! $realm !!}</b> using your {{ Config::get('app.app_name') }}
|
Sign in to <b>{!! $realm !!}</b> using your {{ Config::get('app.app_name') }}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
|
@ -41,10 +41,16 @@ Route::group(array('middleware' => ['ssl']), function () {
|
||||||
|
|
||||||
//user interaction
|
//user interaction
|
||||||
Route::group(array('prefix' => 'auth'), function () {
|
Route::group(array('prefix' => 'auth'), function () {
|
||||||
|
|
||||||
Route::group(array('prefix' => 'login'), function () {
|
Route::group(array('prefix' => 'login'), function () {
|
||||||
Route::get('', "UserController@getLogin");
|
Route::get('', "UserController@getLogin");
|
||||||
|
Route::get('account-verify', "UserController@getAccount");
|
||||||
Route::post('', ['middleware' => 'csrf', 'uses' => 'UserController@postLogin']);
|
Route::post('', ['middleware' => 'csrf', 'uses' => 'UserController@postLogin']);
|
||||||
Route::get('cancel', "UserController@cancelLogin");
|
Route::get('cancel', "UserController@cancelLogin");
|
||||||
|
Route::group(array('prefix' => '{provider}'), function () {
|
||||||
|
Route::get('', 'SocialLoginController@redirect')->name("social_login");
|
||||||
|
Route::any('callback','SocialLoginController@callback')->name("social_login_callback");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// registration routes
|
// registration routes
|
||||||
|
|
|
@ -156,7 +156,7 @@ final class OIDCProtocolTest extends OpenStackIDBaseTest
|
||||||
|
|
||||||
$url = $response->getTargetUrl();
|
$url = $response->getTargetUrl();
|
||||||
|
|
||||||
$response = $this->call('GET', $url);
|
$response = $this->call('GET', $url, [], [$response->cookie("openstackid_session")]);
|
||||||
|
|
||||||
$this->assertResponseStatus(200);
|
$this->assertResponseStatus(200);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
const CopyPlugin = require("copy-webpack-plugin");
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/*
|
||||||
|
optimization: {
|
||||||
|
splitChunks: {
|
||||||
|
cacheGroups: {
|
||||||
|
commons: {
|
||||||
|
name: 'commons',
|
||||||
|
chunks: 'initial',
|
||||||
|
minChunks: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
entry: {
|
||||||
|
login: './resources/js/login/login.js',
|
||||||
|
index: './resources/js/index.js',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: '[name].js',
|
||||||
|
path: path.resolve(__dirname, 'public/assets'),
|
||||||
|
publicPath: '/assets/',
|
||||||
|
pathinfo: false
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
fs: 'empty',
|
||||||
|
child_process: 'empty',
|
||||||
|
tls: 'empty',
|
||||||
|
net: 'empty',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
$: 'jquery',
|
||||||
|
jQuery: 'jquery'
|
||||||
|
}),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: './css/[name].css',
|
||||||
|
}),
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
{from: './node_modules/bootstrap-tagsinput/dist', to: 'bootstrap-tagsinput'},
|
||||||
|
{from: './node_modules/typeahead.js/dist', to: 'typeahead'},
|
||||||
|
{from: './node_modules/jquery.cookie/jquery.cookie.js', to: 'jquery-cookie/jquery.cookie.js'},
|
||||||
|
{from: './node_modules/crypto-js/crypto-js.js', to: 'crypto-js/crypto-js.js'},
|
||||||
|
{from: './node_modules/pwstrength-bootstrap/dist', to: 'pwstrength-bootstrap'},
|
||||||
|
{from: './node_modules/sweetalert2/dist', to: 'sweetalert2'},
|
||||||
|
{from: './node_modules/urijs/src', to: 'urijs'},
|
||||||
|
{from: './node_modules/chosen-js', to: 'chosen-js'},
|
||||||
|
{from: './node_modules/moment', to: 'moment'},
|
||||||
|
{from: './node_modules/@github/clipboard-copy-element/dist', to: 'clipboard-copy-element'},
|
||||||
|
{from: './node_modules/simplemde/dist', to: 'simplemde'},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(js|jsx)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{"targets": {"node": "current"}}
|
||||||
|
],
|
||||||
|
'@babel/preset-react',
|
||||||
|
'@babel/preset-flow'
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
'@babel/plugin-proposal-object-rest-spread',
|
||||||
|
'@babel/plugin-proposal-class-properties'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [MiniCssExtractPlugin.loader, "css-loader"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.module\.scss/,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
modules: {
|
||||||
|
localIdentName: "[local]___[hash:base64:5]",
|
||||||
|
hashPrefix: 'schedule-filter-widget',
|
||||||
|
},
|
||||||
|
sourceMap: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'sass-loader',
|
||||||
|
options: {
|
||||||
|
sourceMap: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.scss/,
|
||||||
|
exclude: /\.module\.scss/,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
// Translates CSS into CommonJS
|
||||||
|
"css-loader",
|
||||||
|
// Compiles Sass to CSS
|
||||||
|
"sass-loader",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
|
use: "file-loader?name=fonts/[name].[ext]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
|
use: "url-loader?limit=10000&minetype=application/font-woff&name=fonts/[name].[ext]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.svg/,
|
||||||
|
use: "file-loader?name=svg/[name].[ext]!svgo-loader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.jpg|\.png|\.gif$/,
|
||||||
|
use: "file-loader?name=images/[name].[ext]"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
const merge = require('webpack-merge');
|
||||||
|
const common = require('./webpack.common.js');
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
watch:true,
|
||||||
|
plugins: [
|
||||||
|
//new CleanWebpackPlugin(),
|
||||||
|
],
|
||||||
|
mode: 'development',
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
devServer: {
|
||||||
|
contentBase: './public/assets',
|
||||||
|
historyApiFallback: true
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
const path = require('path');
|
||||||
|
//const TerserJSPlugin = require('terser-webpack-plugin');
|
||||||
|
//const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||||
|
const merge = require('webpack-merge');
|
||||||
|
const common = require('./webpack.common.js');
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: 'production',
|
||||||
|
optimization: {
|
||||||
|
minimize: true
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue