Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
69.09% covered (warning)
69.09%
38 / 55
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
RouteServiceProvider
69.09% covered (warning)
69.09%
38 / 55
33.33% covered (danger)
33.33%
1 / 3
17.99
0.00% covered (danger)
0.00%
0 / 1
 boot
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
5
 configureRateLimiting
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
3.01
 cspReportRoute
20.00% covered (danger)
20.00%
4 / 20
0.00% covered (danger)
0.00%
0 / 1
17.80
1<?php
2
3namespace App\Providers;
4
5use App\Models\Answer;
6use App\Models\Brand;
7use App\Models\Customer;
8use App\Models\CustomerInfo;
9use App\Models\Question;
10use App\Models\Service;
11use App\Models\SingleOptin;
12use Illuminate\Cache\RateLimiting\Limit;
13use Illuminate\Contracts\Database\Eloquent\Builder;
14use Illuminate\Database\Eloquent\ModelNotFoundException;
15use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
16use Illuminate\Http\Request;
17use Illuminate\Support\Facades\RateLimiter;
18use Illuminate\Support\Facades\Route;
19
20class RouteServiceProvider extends ServiceProvider
21{
22    /**
23     * The path to the "home" route for your application.
24     *
25     * This is used by Laravel authentication to redirect users after login.
26     *
27     * @var string
28     */
29    public const HOME = '/';
30
31    /**
32     * Define your route model bindings, pattern filters, etc.
33     *
34     * @return void
35     */
36    public function boot(): void
37    {
38        $this->configureRateLimiting();
39
40        $this->cspReportRoute();
41
42        $this->routes(function () {
43            // @codeCoverageIgnoreStart
44            Route::prefix('api')->middleware('api')
45                ->namespace($this->namespace)
46                ->group(base_path('routes/api.php'));
47
48            Route::middleware('web')
49                ->namespace($this->namespace)
50                ->group(base_path('routes/web-bo.php'));
51
52            Route::middleware('web')
53                ->namespace($this->namespace)
54                ->group(base_path('routes/web-fo.php'));
55            // @codeCoverageIgnoreEnd
56        });
57
58        Route::model('brand', Brand::class);
59        Route::model('service', Service::class);
60        Route::model('single_optin', SingleOptin::class);
61        Route::model('question', Question::class);
62        Route::model('answer', Answer::class);
63        Route::model('customer', Customer::class);
64
65        // * Api parameters Bind.
66        Route::model('serviceQuestion', Question::class);
67        Route::bind('serviceCustomer', function ($value) {
68            // * Load brand
69            /** @var \App\Models\Brand $brand */
70            $brand = app(Service::class)->brand;
71
72            // ? Parameter is email
73            if (strpos($value, '@') !== false) {
74                $customer = Customer::query()->whereHas('brands', function (Builder $q) use ($brand) {
75                    $q->where('brands.id', '=', $brand->id);
76                })->where('email', '=', $value)->firstOrFail();
77            }
78
79            try {
80                // ? Have we found a customer yet
81                if (!isset($customer) or \get_class($customer) !== Customer::class) {
82                    // ? Parameter is hashcode
83                    $customer = CustomerInfo::query()->whereHas('brand', function (Builder $q) use ($brand) {
84                        $q->where('brands.id', '=', $brand->id);
85                    })->where('hashcode', $value)
86                        ->with('customer')->firstOrFail()->customer;
87                }
88
89                // * Load Customer Login and Info for this brand.
90                /** @var \App\Models\Customer $customer */
91
92                return $customer->loadBrandData($brand);
93            } catch (ModelNotFoundException $e) {
94                // * Uniform Not found exception using model Customer.
95                throw (new ModelNotFoundException())->setModel(Customer::class);
96            }
97        });
98    }
99
100    /**
101     * Configure the rate limiters for the application.
102     *
103     * @return void
104     */
105    protected function configureRateLimiting(): void
106    {
107        RateLimiter::for('web', function (Request $request) {
108            // @codeCoverageIgnoreStart
109            return Limit::perMinute(25)
110                ->by(optional($request->user())->id ?: app('clientIp'));
111            // @codeCoverageIgnoreEnd
112        });
113        // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
114        RateLimiter::for('api', function (Request $request) {
115            return Limit::perMinute(config('api.apiRequestRatePerMinute', 100))
116                ->by(app('clientIp'));
117        });
118        RateLimiter::for('credentials', function (Request $request) {
119            return Limit::perMinute(30)->by($request->user()?->id ?: app('clientIp'));
120        });
121    }
122
123    /**
124     * * Register CSP report URI
125     *
126     * Put this function inside RouteServiceProvider,
127     * and call it in RouteServiceProvider::boot
128     *
129     * @return void
130     */
131    private function cspReportRoute()
132    {
133        // * CSP REPORT URI, Please add /csp-report to VerifyCsrfToken $except middleware
134        \Illuminate\Support\Facades\Route::post('/csp-report', function () {
135            $isCspRequest = request()->header('Content-Type') === 'application/csp-report';
136            if (!$isCspRequest or !request()->json('csp-report') or !is_array(request()->json('csp-report'))) {
137                abort(401);
138            }
139            // * Flatten if needed
140            $cspReport = collect(request()->json('csp-report'))
141                ->flatMap(function ($v, $k) {
142                    return [$k => is_array($v) ? collect($v)->flatten()->implode(', ') : $v];
143                })->all();
144            // * Filter array keys
145            $cspReport = collect(array_intersect_key($cspReport, [
146                'blocked-uri' => '', 'document-uri' => '', 'original-policy' => '',
147                'referrer' => '', 'violated-directive' => ''
148            ]));
149            \Illuminate\Support\Facades\Log::warning(sprintf(
150                "CSP REPORT for %s : \n %s",
151                $cspReport->get('document-uri'),
152                $cspReport->implode("\n")
153            ));
154        })->middleware('throttle:5,2')
155            ->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class)
156            ->name('cspReportUri');
157    }
158}