- SAML OK

- TOTP OK
- STATUS Checker (to do : code)
- EDL Web edit
This commit is contained in:
Vincent BRIDIER 2020-04-14 15:58:24 +02:00
parent 5e5a03eecf
commit 9d5b54a5ea
40 changed files with 7432 additions and 739 deletions

View file

@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Models\Projects;
use App\Models\ProjectsStatusChecker;
use Illuminate\Http\Request;
class StatusCheckerController extends Controller
{
public function index(Request $request) {
$token = $request->token;
$datas = $request->datas;
if ($token !== 'CE8S52Qq7stf5k8frF4T')
return response('Unauthorized', 401);
$oDatas = json_decode($datas);
$app_uuid = $oDatas->uuid;
$sitemap = $oDatas->sitemap;
$robots = $oDatas->robots;
$code = json_encode($oDatas->code);
$logs = $oDatas->logs;
$project_sc = ProjectsStatusChecker::where('project_uuid', '=', $app_uuid)->first();
if (!$project_sc) {
$project = Projects::where('uuid', '=', $app_uuid)->first();
if (!$project)
return response('Unknown', 404);
$project_sc = new ProjectsStatusChecker();
$project_sc->project_uuid = $app_uuid;
}
$project_sc->code = $code;
$project_sc->sitemap = $sitemap;
$project_sc->robots = $robots;
$project_sc->logs = $logs;
$project_sc->save();
return response('OK');
}
}

View file

@ -1,40 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ConfirmsPasswords;
class ConfirmPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Confirm Password Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password confirmations and
| uses a simple trait to include the behavior. You're free to explore
| this trait and override any functions that require customization.
|
*/
use ConfirmsPasswords;
/**
* Where to redirect users when the intended url fails.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}

View file

@ -1,22 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
}

View file

@ -1,40 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}

View file

@ -1,73 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
}

View file

@ -1,42 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\VerifiesEmails;
class VerificationController extends Controller
{
/*
|--------------------------------------------------------------------------
| Email Verification Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling email verification for any
| user that recently registered with the application. Emails may also
| be re-sent if the user didn't receive the original email message.
|
*/
use VerifiesEmails;
/**
* Where to redirect users after verification.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
}

View file

@ -6,16 +6,6 @@ use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
@ -23,6 +13,7 @@ class HomeController extends Controller
*/
public function index()
{
// otpauth://totp/LDC%20WEB:vincent.bridier@ldc.fr?secret=JWAQXGWVH2UDGEPFIY6IOHDZIY&issuer=LDC%20WEB
return view('home');
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Http\Controllers;
use App\Providers\RouteServiceProvider;
use Carbon\Carbon;
use Illuminate\Http\Request;
use lfkeitel\phptotp\{Base32,Totp};
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
class OTPController extends Controller
{
public function verify(Request $request) {
$user = session()->get('tmpUserOTP');
if (!$user)
return redirect(RouteServiceProvider::HOME);
return view('auth.verify');
}
public function check(Request $request) {
$otp1 = $request->otp1;
$otp2 = $request->otp2;
$otp3 = $request->otp3;
$otp4 = $request->otp4;
$otp5 = $request->otp5;
$otp6 = $request->otp6;
$otp = $otp1 . $otp2 . $otp3 . $otp4 . $otp5 . $otp6;
$user = session()->get('tmpUserOTP');
if (!$user)
return redirect(RouteServiceProvider::HOME);
$lastCode = (new Totp())->GenerateToken(Base32::decode($user->totp_secret), Carbon::now()->subSeconds(30)->timestamp);
$code = (new Totp())->GenerateToken(Base32::decode($user->totp_secret));
if ($otp === $code || $otp === $lastCode) {
Auth::login($user);
return redirect(RouteServiceProvider::HOME);
} else {
Auth::logout();
Session::flush();
return redirect()->route('verification_failed');
}
}
}

View file

@ -0,0 +1,128 @@
<?php
namespace App\Http\Controllers;
use App\Models\Projects;
use App\Models\Servers;
use Illuminate\Http\Request;
class ProjectsController extends Controller
{
public function index(Request $request) {
$projects = Projects::all();
return view('projects.index', ['projects' => $projects]);
}
public function edit(Request $request) {
$uuid = $request->uuid;
$project = Projects::where('uuid', '=', $uuid)->first();
if (!$project)
abort(404);
$dev_db_server = Servers::where([
['env', 'DEV'],
['type', 'DB']
])->get();
$dev_web_server = Servers::where([
['env', 'DEV'],
['type', 'WEB']
])->get();
$prod_db_server = Servers::where([
['env', 'PROD'],
['type', 'DB']
])->get();
$prod_web_server = Servers::where([
['env', 'PROD'],
['type', 'WEB']
])->get();
return view('projects.edit', ['project' => $project, 'dev_db_server' => $dev_db_server, 'dev_web_server' => $dev_web_server, 'prod_db_server' => $prod_db_server, 'prod_web_server' => $prod_web_server]);
}
public function update(Request $request) {
$uuid = $request->uuid;
$project = Projects::where('uuid', '=', $uuid)->first();
if (!$project)
abort(404);
$name = $request->name;
$display_name = $request->display_name;
$php = $request->php;
$type = $request->type;
$https = $request->https;
$git_repo = $request->git_repo;
$git_user = $request->git_user;
$git_pwd = $request->git_pwd;
$prod_url = $request->prod_url;
$dev_url = $request->dev_url;
$prod_certificats = $request->prod_certificats;
$dev_certificats = $request->dev_certificats;
$prod_document_root = $request->prod_document_root;
$dev_document_root = $request->dev_document_root;
$prod_unix_user = $request->prod_unix_user;
$dev_unix_user = $request->dev_unix_user;
$prod_unix_pwd = $request->prod_unix_pwd;
$dev_unix_pwd = $request->dev_unix_pwd;
$prod_db_user = $request->prod_db_user;
$dev_db_user = $request->dev_db_user;
$prod_db_pwd = $request->prod_db_pwd;
$dev_db_pwd = $request->dev_db_pwd;
$prod_db_schema = $request->prod_db_schema;
$dev_db_schema = $request->dev_db_schema;
$prod_db_server_id = $request->prod_db_server_id;
$dev_db_server_id = $request->dev_db_server_id;
$prod_web_server_id = $request->prod_web_server_id;
$dev_web_server_id = $request->dev_web_server_id;
$prod_comment = $request->prod_comment;
$dev_comment = $request->dev_comment;
$prod_eai = $request->prod_eai;
$dev_eai = $request->dev_eai;
$prod_whitelist_fw = $request->prod_whitelist_fw;
$dev_whitelist_fw = $request->dev_whitelist_fw;
$prod_redirect_dns = $request->prod_redirect_dns;
$dev_redirect_dns = $request->dev_redirect_dns;
$dev_htaccess_user = $request->dev_htaccess_user;
$dev_htaccess_pwd = $request->dev_htaccess_pwd;
$project->name = $name;
$project->display_name = $display_name;
$project->php = $php;
$project->type = $type;
$project->https = $https;
$project->git_repo = $git_repo;
$project->git_user = $git_user;
$project->git_pwd = $git_pwd;
$project->prod_url = $prod_url;
$project->dev_url = $dev_url;
$project->prod_certificats = $prod_certificats;
$project->dev_certificats = $dev_certificats;
$project->prod_document_root = $prod_document_root;
$project->dev_document_root = $dev_document_root;
$project->prod_unix_user = $prod_unix_user;
$project->dev_unix_user = $dev_unix_user;
$project->prod_unix_pwd = $prod_unix_pwd;
$project->dev_unix_pwd = $dev_unix_pwd;
$project->prod_db_user = $prod_db_user;
$project->dev_db_user = $dev_db_user;
$project->prod_db_pwd = $prod_db_pwd;
$project->dev_db_pwd = $dev_db_pwd;
$project->prod_db_schema = $prod_db_schema;
$project->dev_db_schema = $dev_db_schema;
$project->prod_db_server_id = $prod_db_server_id;
$project->dev_db_server_id = $dev_db_server_id;
$project->prod_web_server_id = $prod_web_server_id;
$project->dev_web_server_id = $dev_web_server_id;
$project->prod_comment = $prod_comment;
$project->dev_comment = $dev_comment;
$project->prod_eai = $prod_eai;
$project->dev_eai = $dev_eai;
$project->prod_whitelist_fw = $prod_whitelist_fw;
$project->dev_whitelist_fw = $dev_whitelist_fw;
$project->prod_redirect_dns = $prod_redirect_dns;
$project->dev_redirect_dns = $dev_redirect_dns;
$project->dev_htaccess_user = $dev_htaccess_user;
$project->dev_htaccess_pwd = $dev_htaccess_pwd;
$project->save();
return redirect()->route('projects.edit', ['uuid' => $project->uuid])->with('status', 'Sauvegardé !');
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace App\Http\Controllers;
use App\Models\Projects;
use Illuminate\Http\Request;
class StatusCheckerController extends Controller
{
public function index(Request $request) {
$projects = Projects::all();
return view('status-checker.index', ['projects' => $projects]);
}
}

View file

@ -41,6 +41,12 @@ class Kernel extends HttpKernel
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'saml' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
],
];
/**

View file

@ -3,6 +3,7 @@
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Facades\URL;
class Authenticate extends Middleware
{
@ -15,7 +16,8 @@ class Authenticate extends Middleware
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
session(['redirectURL' => URL::full()]);
return route('saml2_login', 'ldc');
}
}
}

View file

@ -2,9 +2,11 @@
namespace App\Http\Middleware;
use Aacotroneo\Saml2\Saml2Auth;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\URL;
class RedirectIfAuthenticated
{
@ -18,8 +20,12 @@ class RedirectIfAuthenticated
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
if ($request->ajax()) {
return response('Unauthorized.', 401); // Or, return a response that causes client side js to redirect to '/routesPrefix/myIdp1/login'
} else {
$saml2Auth = new Saml2Auth(Saml2Auth::loadOneLoginAuthFromIpdConfig('ldc'));
session(['redirectURL' => URL::full()]);
return $saml2Auth->login(RouteServiceProvider::HOME);
}
return $next($request);

View file

@ -2,6 +2,7 @@
namespace App\Providers;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -23,6 +24,8 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot()
{
//
if (!app()->environment('local')) {
URL::forceScheme('https');
}
}
}

View file

@ -2,10 +2,14 @@
namespace App\Providers;
use Aacotroneo\Saml2\Events\Saml2LoginEvent;
use App\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Session;
class EventServiceProvider extends ServiceProvider
{
@ -29,6 +33,25 @@ class EventServiceProvider extends ServiceProvider
{
parent::boot();
//
Event::listen('Aacotroneo\Saml2\Events\Saml2LoginEvent', function (Saml2LoginEvent $event) {
$messageId = $event->getSaml2Auth()->getLastMessageId();
// TODO : Add your own code preventing reuse of a $messageId to stop replay attacks
$user = $event->getSaml2User();
$email = $user->getNameId();
$lUser = User::where('email', '=', $email)->first();
if (!$lUser) {
abort(redirect()->route('unauthorized'));
}
session(['sessionIndex' => $user->getSessionIndex()]);
session(['nameId' => $email]);
session(['tmpUserOTP' => $lUser]);
abort(redirect()->route('auth.verify'));
});
Event::listen('Aacotroneo\Saml2\Events\Saml2LogoutEvent', function ($event) {
Auth::logout();
Session::save();
});
}
}

View file

@ -21,7 +21,7 @@ class RouteServiceProvider extends ServiceProvider
*
* @var string
*/
public const HOME = '/home';
public const HOME = '/';
/**
* Define your route model bindings, pattern filters, etc.

View file

@ -9,10 +9,12 @@
"license": "MIT",
"require": {
"php": "^7.2",
"aacotroneo/laravel-saml2": "^2.1",
"fideloper/proxy": "^4.0",
"laravel/framework": "^6.2",
"laravel/tinker": "^2.0",
"laravel/ui": "1.0"
"laravel/ui": "1.0",
"lfkeitel/phptotp": " 1.0"
},
"require-dev": {
"facade/ignition": "^1.4",

View file

@ -67,7 +67,7 @@ return [
|
*/
'timezone' => 'UTC',
'timezone' => 'Europe/Paris',
/*
|--------------------------------------------------------------------------
@ -80,7 +80,7 @@ return [
|
*/
'locale' => 'en',
'locale' => 'fr',
/*
|--------------------------------------------------------------------------

6097
public/css/app.css vendored

File diff suppressed because it is too large Load diff

396
public/js/app.js vendored
View file

@ -1899,9 +1899,9 @@ module.exports = {
/***/ }),
/***/ "./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/ExampleComponent.vue?vue&type=script&lang=js&":
/***/ "./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/OTPFormComponent.vue?vue&type=script&lang=js&":
/*!***************************************************************************************************************************************************************************!*\
!*** ./node_modules/babel-loader/lib??ref--4-0!./node_modules/vue-loader/lib??vue-loader-options!./resources/js/components/ExampleComponent.vue?vue&type=script&lang=js& ***!
!*** ./node_modules/babel-loader/lib??ref--4-0!./node_modules/vue-loader/lib??vue-loader-options!./resources/js/components/OTPFormComponent.vue?vue&type=script&lang=js& ***!
\***************************************************************************************************************************************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
@ -1924,9 +1924,65 @@ __webpack_require__.r(__webpack_exports__);
//
//
//
//
//
/* harmony default export */ __webpack_exports__["default"] = ({
mounted: function mounted() {
console.log('Component mounted.');
},
props: ['csrf', 'action'],
data: function data() {
return {
otp1: '',
otp2: '',
otp3: '',
otp4: '',
otp5: '',
otp6: '',
showSubmitBtn: false,
blockInputs: false
};
},
watch: {
otp6: function otp6() {
if (this.otp6.length === 1) {
this.blockInputs = true;
this.submit();
}
}
},
methods: {
nextInput: function nextInput(id) {
switch (id) {
case 2:
this.$refs.otp2.focus();
break;
case 3:
this.$refs.otp3.focus();
break;
case 4:
this.$refs.otp4.focus();
break;
case 5:
this.$refs.otp5.focus();
break;
case 6:
this.$refs.otp6.focus();
break;
}
},
submit: function submit() {
if (this.otp1.length === 1 && this.otp2.length === 1 && this.otp3.length === 1 && this.otp4.length === 1 && this.otp5.length === 1 && this.otp6.length === 1) {
this.$refs.otp_form.submit();
} else {
this.showSubmitBtn = true;
this.blockInputs = false;
}
}
}
});
@ -37287,9 +37343,9 @@ exports.clearImmediate = (typeof self !== "undefined" && self.clearImmediate) ||
/***/ }),
/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/ExampleComponent.vue?vue&type=template&id=299e239e&":
/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/OTPFormComponent.vue?vue&type=template&id=3aeada99&":
/*!*******************************************************************************************************************************************************************************************************************!*\
!*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./resources/js/components/ExampleComponent.vue?vue&type=template&id=299e239e& ***!
!*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./resources/js/components/OTPFormComponent.vue?vue&type=template&id=3aeada99& ***!
\*******************************************************************************************************************************************************************************************************************/
/*! exports provided: render, staticRenderFns */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
@ -37302,32 +37358,278 @@ var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _vm._m(0)
return _c(
"form",
{
ref: "otp_form",
staticClass: "d-inline",
attrs: { method: "POST", action: _vm.action }
},
[
_c(
"div",
{
directives: [
{
name: "show",
rawName: "v-show",
value: _vm.showSubmitBtn,
expression: "showSubmitBtn"
}
],
staticClass: "alert alert-danger",
attrs: { role: "alert" }
},
[
_vm._v(
"\n Vous devez remplir tous les champs, 1 caractère par case.\n "
)
]
),
_vm._v(" "),
_c("input", {
attrs: { type: "hidden", name: "_token" },
domProps: { value: _vm.csrf }
}),
_vm._v(" "),
_c("div", { staticClass: "otp_fields" }, [
_c("input", {
directives: [
{
name: "model",
rawName: "v-model",
value: _vm.otp1,
expression: "otp1"
}
],
ref: "otp1",
attrs: {
disabled: _vm.blockInputs,
name: "otp1",
type: "number",
min: "0",
max: "9",
maxlength: "1",
minlength: "1",
autofocus: "",
required: ""
},
domProps: { value: _vm.otp1 },
on: {
keypress: function($event) {
return _vm.nextInput(2)
},
click: function($event) {
_vm.otp1 = ""
},
input: function($event) {
if ($event.target.composing) {
return
}
_vm.otp1 = $event.target.value
}
}
}),
_vm._v(" "),
_c("input", {
directives: [
{
name: "model",
rawName: "v-model",
value: _vm.otp2,
expression: "otp2"
}
],
ref: "otp2",
attrs: {
disabled: _vm.blockInputs,
name: "otp2",
type: "number",
min: "0",
max: "9",
maxlength: "1",
minlength: "1",
required: ""
},
domProps: { value: _vm.otp2 },
on: {
keypress: function($event) {
return _vm.nextInput(3)
},
click: function($event) {
_vm.otp2 = ""
},
input: function($event) {
if ($event.target.composing) {
return
}
_vm.otp2 = $event.target.value
}
}
}),
_vm._v(" "),
_c("input", {
directives: [
{
name: "model",
rawName: "v-model",
value: _vm.otp3,
expression: "otp3"
}
],
ref: "otp3",
attrs: {
disabled: _vm.blockInputs,
name: "otp3",
type: "number",
min: "0",
max: "9",
maxlength: "1",
minlength: "1",
required: ""
},
domProps: { value: _vm.otp3 },
on: {
keypress: function($event) {
return _vm.nextInput(4)
},
click: function($event) {
_vm.otp3 = ""
},
input: function($event) {
if ($event.target.composing) {
return
}
_vm.otp3 = $event.target.value
}
}
}),
_vm._v(" "),
_c("input", {
directives: [
{
name: "model",
rawName: "v-model",
value: _vm.otp4,
expression: "otp4"
}
],
ref: "otp4",
attrs: {
disabled: _vm.blockInputs,
name: "otp4",
type: "number",
min: "0",
max: "9",
maxlength: "1",
minlength: "1",
required: ""
},
domProps: { value: _vm.otp4 },
on: {
keypress: function($event) {
return _vm.nextInput(5)
},
click: function($event) {
_vm.otp4 = ""
},
input: function($event) {
if ($event.target.composing) {
return
}
_vm.otp4 = $event.target.value
}
}
}),
_vm._v(" "),
_c("input", {
directives: [
{
name: "model",
rawName: "v-model",
value: _vm.otp5,
expression: "otp5"
}
],
ref: "otp5",
attrs: {
disabled: _vm.blockInputs,
name: "otp5",
type: "number",
min: "0",
max: "9",
maxlength: "1",
minlength: "1",
required: ""
},
domProps: { value: _vm.otp5 },
on: {
keypress: function($event) {
return _vm.nextInput(6)
},
click: function($event) {
_vm.otp5 = ""
},
input: function($event) {
if ($event.target.composing) {
return
}
_vm.otp5 = $event.target.value
}
}
}),
_vm._v(" "),
_c("input", {
directives: [
{
name: "model",
rawName: "v-model",
value: _vm.otp6,
expression: "otp6"
}
],
ref: "otp6",
attrs: {
disabled: _vm.blockInputs,
name: "otp6",
type: "number",
min: "0",
max: "9",
maxlength: "1",
minlength: "1"
},
domProps: { value: _vm.otp6 },
on: {
click: function($event) {
_vm.otp6 = ""
},
input: function($event) {
if ($event.target.composing) {
return
}
_vm.otp6 = $event.target.value
}
}
})
]),
_vm._v(" "),
_c("input", {
directives: [
{
name: "show",
rawName: "v-show",
value: _vm.showSubmitBtn,
expression: "showSubmitBtn"
}
],
staticClass: "btn btn-primary",
staticStyle: { display: "block", margin: "10px auto 0" },
attrs: { type: "submit", value: "Valider" }
})
]
)
}
var staticRenderFns = [
function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c("div", { staticClass: "container" }, [
_c("div", { staticClass: "row justify-content-center" }, [
_c("div", { staticClass: "col-md-8" }, [
_c("div", { staticClass: "card" }, [
_c("div", { staticClass: "card-header" }, [
_vm._v("Example Component")
]),
_vm._v(" "),
_c("div", { staticClass: "card-body" }, [
_vm._v(
"\n I'm an example component.\n "
)
])
])
])
])
])
}
]
var staticRenderFns = []
render._withStripped = true
@ -49516,7 +49818,7 @@ window.Vue = __webpack_require__(/*! vue */ "./node_modules/vue/dist/vue.common.
// const files = require.context('./', true, /\.vue$/i)
// files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default))
Vue.component('example-component', __webpack_require__(/*! ./components/ExampleComponent.vue */ "./resources/js/components/ExampleComponent.vue")["default"]);
Vue.component('otp-form', __webpack_require__(/*! ./components/OTPFormComponent.vue */ "./resources/js/components/OTPFormComponent.vue")["default"]);
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
@ -49587,17 +49889,17 @@ if (token) {
/***/ }),
/***/ "./resources/js/components/ExampleComponent.vue":
/***/ "./resources/js/components/OTPFormComponent.vue":
/*!******************************************************!*\
!*** ./resources/js/components/ExampleComponent.vue ***!
!*** ./resources/js/components/OTPFormComponent.vue ***!
\******************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _ExampleComponent_vue_vue_type_template_id_299e239e___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ExampleComponent.vue?vue&type=template&id=299e239e& */ "./resources/js/components/ExampleComponent.vue?vue&type=template&id=299e239e&");
/* harmony import */ var _ExampleComponent_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ExampleComponent.vue?vue&type=script&lang=js& */ "./resources/js/components/ExampleComponent.vue?vue&type=script&lang=js&");
/* harmony import */ var _OTPFormComponent_vue_vue_type_template_id_3aeada99___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./OTPFormComponent.vue?vue&type=template&id=3aeada99& */ "./resources/js/components/OTPFormComponent.vue?vue&type=template&id=3aeada99&");
/* harmony import */ var _OTPFormComponent_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OTPFormComponent.vue?vue&type=script&lang=js& */ "./resources/js/components/OTPFormComponent.vue?vue&type=script&lang=js&");
/* empty/unused harmony star reexport *//* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ "./node_modules/vue-loader/lib/runtime/componentNormalizer.js");
@ -49607,9 +49909,9 @@ __webpack_require__.r(__webpack_exports__);
/* normalize component */
var component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__["default"])(
_ExampleComponent_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__["default"],
_ExampleComponent_vue_vue_type_template_id_299e239e___WEBPACK_IMPORTED_MODULE_0__["render"],
_ExampleComponent_vue_vue_type_template_id_299e239e___WEBPACK_IMPORTED_MODULE_0__["staticRenderFns"],
_OTPFormComponent_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__["default"],
_OTPFormComponent_vue_vue_type_template_id_3aeada99___WEBPACK_IMPORTED_MODULE_0__["render"],
_OTPFormComponent_vue_vue_type_template_id_3aeada99___WEBPACK_IMPORTED_MODULE_0__["staticRenderFns"],
false,
null,
null,
@ -49619,38 +49921,38 @@ var component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_
/* hot reload */
if (false) { var api; }
component.options.__file = "resources/js/components/ExampleComponent.vue"
component.options.__file = "resources/js/components/OTPFormComponent.vue"
/* harmony default export */ __webpack_exports__["default"] = (component.exports);
/***/ }),
/***/ "./resources/js/components/ExampleComponent.vue?vue&type=script&lang=js&":
/***/ "./resources/js/components/OTPFormComponent.vue?vue&type=script&lang=js&":
/*!*******************************************************************************!*\
!*** ./resources/js/components/ExampleComponent.vue?vue&type=script&lang=js& ***!
!*** ./resources/js/components/OTPFormComponent.vue?vue&type=script&lang=js& ***!
\*******************************************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _node_modules_babel_loader_lib_index_js_ref_4_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ExampleComponent_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/babel-loader/lib??ref--4-0!../../../node_modules/vue-loader/lib??vue-loader-options!./ExampleComponent.vue?vue&type=script&lang=js& */ "./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/ExampleComponent.vue?vue&type=script&lang=js&");
/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__["default"] = (_node_modules_babel_loader_lib_index_js_ref_4_0_node_modules_vue_loader_lib_index_js_vue_loader_options_ExampleComponent_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__["default"]);
/* harmony import */ var _node_modules_babel_loader_lib_index_js_ref_4_0_node_modules_vue_loader_lib_index_js_vue_loader_options_OTPFormComponent_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/babel-loader/lib??ref--4-0!../../../node_modules/vue-loader/lib??vue-loader-options!./OTPFormComponent.vue?vue&type=script&lang=js& */ "./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/OTPFormComponent.vue?vue&type=script&lang=js&");
/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__["default"] = (_node_modules_babel_loader_lib_index_js_ref_4_0_node_modules_vue_loader_lib_index_js_vue_loader_options_OTPFormComponent_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__["default"]);
/***/ }),
/***/ "./resources/js/components/ExampleComponent.vue?vue&type=template&id=299e239e&":
/***/ "./resources/js/components/OTPFormComponent.vue?vue&type=template&id=3aeada99&":
/*!*************************************************************************************!*\
!*** ./resources/js/components/ExampleComponent.vue?vue&type=template&id=299e239e& ***!
!*** ./resources/js/components/OTPFormComponent.vue?vue&type=template&id=3aeada99& ***!
\*************************************************************************************/
/*! exports provided: render, staticRenderFns */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_ExampleComponent_vue_vue_type_template_id_299e239e___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../node_modules/vue-loader/lib??vue-loader-options!./ExampleComponent.vue?vue&type=template&id=299e239e& */ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/ExampleComponent.vue?vue&type=template&id=299e239e&");
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "render", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_ExampleComponent_vue_vue_type_template_id_299e239e___WEBPACK_IMPORTED_MODULE_0__["render"]; });
/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_OTPFormComponent_vue_vue_type_template_id_3aeada99___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../node_modules/vue-loader/lib??vue-loader-options!./OTPFormComponent.vue?vue&type=template&id=3aeada99& */ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/OTPFormComponent.vue?vue&type=template&id=3aeada99&");
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "render", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_OTPFormComponent_vue_vue_type_template_id_3aeada99___WEBPACK_IMPORTED_MODULE_0__["render"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "staticRenderFns", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_ExampleComponent_vue_vue_type_template_id_299e239e___WEBPACK_IMPORTED_MODULE_0__["staticRenderFns"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "staticRenderFns", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_OTPFormComponent_vue_vue_type_template_id_3aeada99___WEBPACK_IMPORTED_MODULE_0__["staticRenderFns"]; });

2
resources/js/app.js vendored
View file

@ -19,7 +19,7 @@ window.Vue = require('vue');
// const files = require.context('./', true, /\.vue$/i)
// files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default))
Vue.component('example-component', require('./components/ExampleComponent.vue').default);
Vue.component('otp-form', require('./components/OTPFormComponent.vue').default);
/**
* Next, we will create a fresh Vue application instance and attach it to

View file

@ -1,23 +0,0 @@
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Example Component</div>
<div class="card-body">
I'm an example component.
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
}
}
</script>

View file

@ -0,0 +1,78 @@
<template>
<form ref="otp_form" class="d-inline" method="POST" :action="action">
<div v-show="showSubmitBtn" class="alert alert-danger" role="alert">
Vous devez remplir tous les champs, 1 caractère par case.
</div>
<input type="hidden" name="_token" :value="csrf">
<div class="otp_fields">
<input ref="otp1" v-model="otp1" :disabled="blockInputs" name="otp1" type="number" min="0" max="9" maxlength="1" minlength="1" @keypress="nextInput(2)" @click="otp1 = ''" autofocus required>
<input ref="otp2" v-model="otp2" :disabled="blockInputs" name="otp2" type="number" min="0" max="9" maxlength="1" minlength="1" @keypress="nextInput(3)" @click="otp2 = ''" required>
<input ref="otp3" v-model="otp3" :disabled="blockInputs" name="otp3" type="number" min="0" max="9" maxlength="1" minlength="1" @keypress="nextInput(4)" @click="otp3 = ''" required>
<input ref="otp4" v-model="otp4" :disabled="blockInputs" name="otp4" type="number" min="0" max="9" maxlength="1" minlength="1" @keypress="nextInput(5)" @click="otp4 = ''" required>
<input ref="otp5" v-model="otp5" :disabled="blockInputs" name="otp5" type="number" min="0" max="9" maxlength="1" minlength="1" @keypress="nextInput(6)" @click="otp5 = ''" required>
<input ref="otp6" v-model="otp6" :disabled="blockInputs" name="otp6" type="number" min="0" max="9" maxlength="1" minlength="1" @click="otp6 = ''">
</div>
<input v-show="showSubmitBtn" type="submit" value="Valider" class="btn btn-primary" style="display: block;margin: 10px auto 0;">
</form>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
props: [
'csrf',
'action'
],
data() {
return {
otp1: '',
otp2: '',
otp3: '',
otp4: '',
otp5: '',
otp6: '',
showSubmitBtn: false,
blockInputs: false
}
},
watch: {
otp6() {
if (this.otp6.length === 1) {
this.blockInputs = true;
this.submit();
}
}
},
methods: {
nextInput(id) {
switch (id) {
case 2:
this.$refs.otp2.focus();
break;
case 3:
this.$refs.otp3.focus();
break;
case 4:
this.$refs.otp4.focus();
break;
case 5:
this.$refs.otp5.focus();
break;
case 6:
this.$refs.otp6.focus();
break;
}
},
submit() {
if (this.otp1.length === 1 && this.otp2.length === 1 && this.otp3.length === 1 && this.otp4.length === 1 && this.otp5.length === 1 && this.otp6.length === 1){
this.$refs.otp_form.submit();
}else{
this.showSubmitBtn = true;
this.blockInputs = false;
}
}
}
}
</script>

View file

@ -6,3 +6,55 @@
// Bootstrap
@import '~bootstrap/scss/bootstrap';
// FontAwesome
@import "fontawesome/fontawesome";
@import "fontawesome/solid";
@import "fontawesome/regular";
.green {
color: #73ce29;
}
.red {
color: #e44a5a;
}
.status {
border-radius: 5px;
color: #FFF;
text-transform: uppercase;
font-size: 0.8em;
font-weight: bold;
padding: 3px 5px 2px;
&.checked {
background-color: #73ce29;
}
&.unchecked {
background-color: #f7b542;
}
}
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none !important;
margin: 0 !important;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield !important;
}
.otp_fields {
display: flex;
justify-content: center;
& input {
width: 60px;
height: 60px;
text-align: center;
font-size: 35px;
&:not(:last-child) {
margin-right: 10px;
}
}
}

View file

@ -1,73 +0,0 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Login') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<div class="col-md-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
{{ __('Remember Me') }}
</label>
</div>
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Login') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -1,47 +0,0 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<form method="POST" action="{{ route('password.email') }}">
@csrf
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Send Password Reset Link') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -1,65 +0,0 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Reset Password') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -1,77 +0,0 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Register') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Register') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,12 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-9 offset-md-3">
<h3>Vous n'êtes pas autorisé à accéder à l'application.</h3>
<p>Vous pouvez faire une demande à cette adresse : <a href="mailto:informatique.internet@ldc.fr?subject=DEMANDE%20ACCES%20LDC%20WEB">informatique.internet@ldc.fr</a></p>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,11 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-12 text-center">
<h3>Code de vérification invalide.</h3>
</div>
</div>
</div>
@endsection

View file

@ -5,21 +5,10 @@
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Verify Your Email Address') }}</div>
<div class="card-header">Confirmation de connexion</div>
<div class="card-body">
@if (session('resent'))
<div class="alert alert-success" role="alert">
{{ __('A fresh verification link has been sent to your email address.') }}
</div>
@endif
{{ __('Before proceeding, please check your email for a verification link.') }}
{{ __('If you did not receive the email') }},
<form class="d-inline" method="POST" action="{{ route('verification.resend') }}">
@csrf
<button type="submit" class="btn btn-link p-0 m-0 align-baseline">{{ __('click here to request another') }}</button>.
</form>
<otp-form csrf='{{ csrf_token() }}' action="/login/checkOTP"></otp-form>
</div>
</div>
</div>

View file

@ -7,14 +7,22 @@
<div class="card">
<div class="card-header">Dashboard</div>
<div class="card-body">
<div class="card-body" style="display: flex;justify-content: space-around;">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
You are logged in!
<a href="/projects" class="btn btn-lg btn-primary">
<i class="far fa-list-alt"></i><br>
EDL Web
</a>
<a href="/status-checker" class="btn btn-lg btn-primary">
<i class="fas fa-clipboard-check"></i><br>
Status Checker
</a>
</div>
</div>
</div>

View file

@ -39,34 +39,26 @@
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
@guest
@auth
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
<a href="/" class="nav-link">Dashboard</a>
</li>
<li class="nav-item">
<a href="/projects" class="nav-link">EDL Web</a>
</li>
<li class="nav-item">
<a href="/status-checker" class="nav-link">Status Checker</a>
</li>
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }} <span class="caret"></span>
{{ Auth::user()->firstname }} {{ Auth::user()->lastname }}<span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
<a class="dropdown-item" href="{{route('saml2_logout', 'ldc')}}?returnTo={{route('saml2_sls', 'ldc')}}&sessionIndex={{session()->get('sessionIndex')}}&nameId={{session()->get('nameId')}}">Déconnexion</a>
</div>
</li>
@endguest
@endauth
</ul>
</div>
</div>

View file

@ -0,0 +1,383 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12">
<div class="card">
<div class="card-header">{{$project->name}} - {{$project->display_name}}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<form action="/projects/{{$project->uuid}}/update" method="POST">
@csrf
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>Nom court :</b></label>
<input type="text" name="name" class="form-control" placeholder="Nom court" value="{{$project->name}}">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>Nom :</b></label>
<input type="text" name="display_name" class="form-control" placeholder="Nom" value="{{$project->display_name}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Dépôt GIT :</b></label>
<input type="text" name="git_repo" class="form-control" placeholder="Dépôt GIT" value="{{$project->git_repo}}">
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label><b>PHP :</b></label>
<select name="php" class="form-control">
<option @if ($project->php === '5.6') selected @endif value="5.6">5.6</option>
<option @if ($project->php === '7') selected @endif value="7">7</option>
<option @if ($project->php === '7.3') selected @endif value="7.3">7.3</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label><b>Type :</b></label>
<select name="type" class="form-control">
<option @if ($project->type === 'WP') selected @endif value="WP">WP</option>
<option @if ($project->type === 'Laravel') selected @endif value="Laravel">Laravel</option>
<option @if ($project->type === 'Symfony') selected @endif value="Symfony">Symfony</option>
<option @if ($project->type === 'other') selected @endif value="other">Autres</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label><b>HTTPS :</b></label>
<select name="https" class="form-control">
<option @if ($project->type === '1') selected @endif value="1">Oui</option>
<option @if ($project->type === '0') selected @endif value="0">Non</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>GIT User :</b></label>
<input type="text" name="git_user" class="form-control" placeholder="GIT User" value="{{$project->git_user}}">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>GIT Password :</b></label>
<input type="text" name="git_pwd" class="form-control" placeholder="GIT Password" value="{{$project->git_pwd}}">
</div>
</div>
</div>
</div>
</div>
<div class="row text-center" style="background-color: rgba(0, 0, 0, 0.03);display: flex;justify-content: space-around;">
<p style="margin: 10px"><b>DEV</b></p>
<p style="margin: 10px;"><b>PROD</b></p>
</div>
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>URL :</b></label>
<input type="url" name="dev_url" class="form-control" placeholder="URL" value="{{$project->dev_url}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Type Certificats :</b></label>
<input type="text" name="dev_certificats" class="form-control" placeholder="Type Certificats" value="{{$project->dev_certificats}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Document Root :</b></label>
<input type="text" name="dev_document_root" class="form-control" placeholder="Document Root" value="{{$project->dev_document_root}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>IP:Port SSH/SFTP :</b></label>
<input type="text" class="form-control" placeholder="IP:Port SSH/SFTP" value="">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>WEB Server :</b></label>
<select name="dev_web_server_id" class="form-control">
@foreach($dev_web_server as $server)
<option value="{{$server->id}}" @if ($project->dev_web_server_id === $server->id) selected @endif>{{$server->title}}</option>
@endforeach
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>Unix user :</b></label>
<input type="text" name="dev_unix_user" class="form-control" placeholder="Unix user" value="{{$project->dev_unix_user}}">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>Unix password :</b></label>
<input type="text" name="dev_unix_pwd" class="form-control" placeholder="Unix password" value="{{$project->dev_unix_pwd}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>DB IP:Port SSH :</b></label>
<input type="text" class="form-control" placeholder="DB SSH" value="">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>DB Server :</b></label>
<select name="dev_db_server_id" class="form-control">
@foreach($dev_db_server as $server)
<option value="{{$server->id}}" @if ($project->dev_db_server_id === $server->id) selected @endif>{{$server->title}}</option>
@endforeach
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>DB Schema :</b></label>
<input type="text" name="dev_db_schema" class="form-control" placeholder="DB Schema" value="{{$project->dev_db_schema}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>DB user :</b></label>
<input type="text" name="dev_db_user" class="form-control" placeholder="DB user" value="{{$project->dev_db_user}}">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>DB password :</b></label>
<input type="text" name="dev_db_pwd" class="form-control" placeholder="DB password" value="{{$project->dev_db_pwd}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>DNS Redirigés :</b></label>
<textarea name="dev_redirect_dns" cols="30" rows="5" class="form-control">{{$project->dev_redirect_dns}}</textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Flux EAI :</b></label>
<input type="text" name="dev_eai" class="form-control" placeholder="Flux EAI" value="{{$project->dev_eai}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Whitelist FW :</b></label>
<input type="text" name="dev_whitelist_fw" class="form-control" placeholder="Whitelist FW" value="{{$project->dev_whitelist_fw}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Commentaire :</b></label>
<textarea name="dev_comment" cols="30" rows="5" class="form-control">{{$project->dev_comment}}</textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>HTACCESS User :</b></label>
<input type="text" name="dev_htaccess_user" class="form-control" placeholder="HTACCESS User" value="{{$project->dev_htaccess_user}}">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>HTACCESS Password :</b></label>
<input type="text" name="dev_htaccess_pwd" class="form-control" placeholder="HTACCESS Password" value="{{$project->dev_htaccess_pwd}}">
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>URL :</b></label>
<input type="url" name="prod_url" class="form-control" placeholder="URL" value="{{$project->prod_url}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Type Certificats :</b></label>
<input type="text" name="prod_certificats" class="form-control" placeholder="Type Certificats" value="{{$project->prod_certificats}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Document Root :</b></label>
<input type="text" name="prod_document_root" class="form-control" placeholder="Document Root" value="{{$project->prod_document_root}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>IP:Port SSH/SFTP :</b></label>
<input type="text" class="form-control" placeholder="IP:Port SSH/SFTP" value="">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>WEB Server :</b></label>
<select name="prod_web_server_id" class="form-control">
@foreach($prod_web_server as $server)
<option value="{{$server->id}}" @if ($project->prod_web_server_id === $server->id) selected @endif>{{$server->title}}</option>
@endforeach
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>Unix user :</b></label>
<input type="text" name="prod_unix_user" class="form-control" placeholder="Unix user" value="{{$project->prod_unix_user}}">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>Unix password :</b></label>
<input type="text" name="prod_unix_pwd" class="form-control" placeholder="Unix password" value="{{$project->prod_unix_pwd}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>DB IP:Port SSH :</b></label>
<input type="text" class="form-control" placeholder="DB SSH" value="">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>DB Server :</b></label>
<select name="prod_db_server_id" class="form-control">
@foreach($prod_db_server as $server)
<option value="{{$server->id}}" @if ($project->prod_db_server_id === $server->id) selected @endif>{{$server->title}}</option>
@endforeach
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>DB Schema :</b></label>
<input type="text" name="prod_db_schema" class="form-control" placeholder="DB Schema" value="{{$project->prod_db_schema}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label><b>DB user :</b></label>
<input type="text" name="prod_db_user" class="form-control" placeholder="DB user" value="{{$project->prod_db_user}}">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label><b>DB password :</b></label>
<input type="text" name="prod_db_pwd" class="form-control" placeholder="DB password" value="{{$project->prod_db_pwd}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>DNS Redirigés :</b></label>
<textarea name="prod_redirect_dns" cols="30" rows="5" class="form-control">{{$project->prod_redirect_dns}}</textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Flux EAI :</b></label>
<input type="text" name="prod_eai" class="form-control" placeholder="Flux EAI" value="{{$project->prod_eai}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Whitelist FW :</b></label>
<input type="text" name="prod_whitelist_fw" class="form-control" placeholder="Whitelist FW" value="{{$project->prod_whitelist_fw}}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label><b>Commentaire :</b></label>
<textarea name="prod_comment" cols="30" rows="5" class="form-control">{{$project->prod_comment}}</textarea>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 text-center">
<input type="submit" value="Enregistrer" class="btn btn-primary">
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,49 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12">
<div class="card">
<div class="card-header">EDL Web</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<table class="table text-center">
<thead class="thead-dark">
<tr>
<th scope="col">Projet</th>
<th scope="col">Nom</th>
<th scope="col">PHP</th>
<th scope="col">Type</th>
<th scope="col">HTTPS</th>
</tr>
</thead>
<tbody>
@foreach($projects as $project)
<tr>
<td class="text-left"><a href="/projects/{{$project->uuid}}/edit">{{$project->name}}</a></td>
<td class="text-left">{{$project->display_name}}</td>
<td><b>{{$project->php}}</b></td>
<td>{{$project->type}}</td>
<td>
@if($project->https === 0)
<i class="fas fa-unlock red"></i>
@else
<i class="fas fa-lock green"></i>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,78 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12">
<table class="table text-center">
<thead class="thead-dark">
<tr>
<th scope="col">Status</th>
<th scope="col">HTTPS</th>
<th scope="col">Site</th>
<th scope="col">PHP</th>
<th scope="col">Type</th>
<th scope="col">Code</th>
<th scope="col">Sitemap</th>
<th scope="col">Robots.txt</th>
<th scope="col">Log d'erreurs</th>
</tr>
</thead>
<tbody>
@foreach($projects as $project)
<tr>
<td>
@if ($project->checked === 'missing')
<i class="fas fa-ghost"></i>
@elseif($project->checked === 'true')
<div class="status checked">checked</div>
@else
<div class="status unchecked">unchecked</div>
@endif
</td>
<td>
@if($project->https === 0)
<i class="fas fa-unlock red"></i>
@else
<i class="fas fa-lock green"></i>
@endif
</td>
<th scope="row" class="text-left"><a href="/projects/{{$project->uuid}}/edit">{{$project->display_name}}</a></th>
<td><b>{{$project->php}}</b></td>
<td>{{$project->type}}</td>
<td></td>
<td>
@if ($project->sitemap_checked === 'missing')
<i class="fas fa-ghost"></i>
@elseif($project->sitemap_checked === 'true')
<i class="fas fa-check-circle green"></i>
@else
<i class="fas fa-times-circle red"></i>
@endif
</td>
<td>
@if ($project->robots_checked === 'missing')
<i class="fas fa-ghost"></i>
@elseif($project->robots_checked === 'true')
<i class="fas fa-check-circle green"></i>
@else
<i class="fas fa-times-circle red"></i>
@endif
</td>
<td>
@if ($project->logs_checked === 'missing')
<i class="fas fa-ghost"></i>
@elseif($project->logs_checked === 0)
<i class="fas fa-bug green"></i>
@else
<i class="fas fa-bug red"></i> {{$project->logs_checked}} B
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection

View file

@ -1,100 +0,0 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Nunito', sans-serif;
font-weight: 200;
height: 100vh;
margin: 0;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
}
.position-ref {
position: relative;
}
.top-right {
position: absolute;
right: 10px;
top: 18px;
}
.content {
text-align: center;
}
.title {
font-size: 84px;
}
.links > a {
color: #636b6f;
padding: 0 25px;
font-size: 13px;
font-weight: 600;
letter-spacing: .1rem;
text-decoration: none;
text-transform: uppercase;
}
.m-b-md {
margin-bottom: 30px;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
@if (Route::has('login'))
<div class="top-right links">
@auth
<a href="{{ url('/home') }}">Home</a>
@else
<a href="{{ route('login') }}">Login</a>
@if (Route::has('register'))
<a href="{{ route('register') }}">Register</a>
@endif
@endauth
</div>
@endif
<div class="content">
<div class="title m-b-md">
Laravel
</div>
<div class="links">
<a href="https://laravel.com/docs">Docs</a>
<a href="https://laracasts.com">Laracasts</a>
<a href="https://laravel-news.com">News</a>
<a href="https://blog.laravel.com">Blog</a>
<a href="https://nova.laravel.com">Nova</a>
<a href="https://forge.laravel.com">Forge</a>
<a href="https://vapor.laravel.com">Vapor</a>
<a href="https://github.com/laravel/laravel">GitHub</a>
</div>
</div>
</div>
</body>
</html>

View file

@ -16,3 +16,5 @@ use Illuminate\Http\Request;
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::post('/status-checker', 'API\StatusCheckerController@index');

View file

@ -11,10 +11,17 @@
|
*/
Route::get('/', function () {
return view('welcome');
Route::get('/login/verify', 'OTPController@verify')->name('auth.verify');
Route::post('/login/checkOTP', 'OTPController@check')->name('auth.otp');
Route::view('/login/verification_failed', 'auth.verification_failed')->name('verification_failed');
Route::view('/login/unauthorized', 'auth.unauthorized')->name('unauthorized');
Route::group(['middleware' => ['auth']], function () {
Route::get('/', 'HomeController@index')->name('home');
/* STATUS CHECKER */
Route::get('/status-checker', 'StatusCheckerController@index');
/* EDL WEB */
Route::get('/projects', 'ProjectsController@index');
Route::get('/projects/{uuid}/edit', 'ProjectsController@edit')->name('projects.edit');
Route::post('/projects/{uuid}/update', 'ProjectsController@update');
});
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');