Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.78% covered (warning)
77.78%
42 / 54
75.00% covered (warning)
75.00%
6 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Helper
77.78% covered (warning)
77.78%
42 / 54
75.00% covered (warning)
75.00%
6 / 8
30.32
0.00% covered (danger)
0.00%
0 / 1
 isRequestForApiV2
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 isRequestForApiV1
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 getRouteName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getBearerTokenFromRequest
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 getRoutePrefix
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 getRoute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPackageInfo
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 signRequest
85.29% covered (warning)
85.29%
29 / 34
0.00% covered (danger)
0.00%
0 / 1
7.16
1<?php
2
3namespace App\Lib;
4
5use App\Exceptions\Api\V2\CrmException;
6use Illuminate\Contracts\Encryption\DecryptException;
7use Illuminate\Http\Request;
8use Illuminate\Support\Facades\Crypt;
9use Illuminate\Support\Facades\Route;
10use Illuminate\Support\Str;
11
12/**
13 * Helper Class
14 */
15class Helper
16{
17    /**
18     * Package infos
19     *
20     * @var array
21     */
22    private static $packageInfos;
23
24    /**
25     * Tells if the request is within apiV2 routes
26     *
27     * @param \Illuminate\Http\Request $request
28     * @return boolean
29     */
30    public static function isRequestForApiV2(Request $request)
31    {
32        // * Small hack to ensure to know if route is From APIv2.
33        $routeName = static::getRouteName($request) ??
34            (strpos($request->path(), 'api/v2') === 0 ? 'apiV2.' : null) ?? 'none';
35        return strpos($routeName, 'apiV2.') === 0;
36    }
37
38    /**
39     * Tells if the request is within apiV1 routes
40     *
41     * @param \Illuminate\Http\Request $request
42     * @return boolean
43     */
44    public static function isRequestForApiV1(Request $request): bool
45    {
46        // * Small hack to ensure to know if route is From APIv1.
47        $routePrefix = (strpos(static::getRoutePrefix($request), 'api/v1') ? 'v1' : null) ??
48            (strpos($request->path(), 'api/v1') === 0 ? 'v1' : null) ?? 'none';
49        return strpos($routePrefix, 'v1') === 0;
50    }
51
52    /**
53     * Try to get current route name
54     *
55     * @param \Illuminate\Http\Request $request
56     * @return string|null
57     */
58    public static function getRouteName(Request $request)
59    {
60        return Route::currentRouteName() ?? ($request->route() ? $request->route()->getName() : null);
61    }
62
63    /**
64     * Get the bearer token from request
65     *
66     * @param \Illuminate\Http\Request $request
67     * @return string|null
68     */
69    public static function getBearerTokenFromRequest(Request $request): ?string
70    {
71        $header = $request->header('Authorization', '');
72
73        if (Str::contains($header, 'Bearer ')) {
74            $auths = \explode(',', $header);
75            foreach ($auths as $auth) {
76                if (Str::contains($auth, 'Bearer ')) {
77                    return Str::substr($auth, 7);
78                }
79            }
80        }
81        return null;
82    }
83
84    /**
85     * Try to get current route prefix
86     *
87     * @param \Illuminate\Http\Request $request
88     * @return string|null
89     */
90    public static function getRoutePrefix(Request $request)
91    {
92        return (Route::getCurrentRoute() ? Route::getCurrentRoute()->getPrefix() : null) ??
93            ($request->route() ? $request->route()->getPrefix() : null);
94    }
95
96    /**
97     * Try to get route from request
98     *
99     * @param \Illuminate\Http\Request $request
100     * @return \Illuminate\Routing\Route|null
101     */
102    public static function getRoute(Request $request)
103    {
104        return Route::getRoutes()->getByName(static::getRouteName($request));
105    }
106
107    /**
108     * Get composer.json infos
109     *
110     * @return array
111     */
112    public static function getPackageInfo(): array
113    {
114        if (!static::$packageInfos) {
115            static::$packageInfos = \json_decode(\file_get_contents(__DIR__ . '/../../composer.json'), true);
116        }
117        return static::$packageInfos;
118    }
119
120    /**
121     * Calculate the request signature
122     *
123     * @param string $secret
124     * @param string $uri
125     * @param string $method
126     * @param array  $parameters
127     * @return string request signature
128     * @throws \App\Exceptions\Api\V2\CrmException If an error occurs.
129     */
130    public static function signRequest(string $secret, string $uri, string $method, array $parameters): string
131    {
132        try {
133            $uri    = collect(\parse_url($uri));
134            $method = \strtolower($method);
135
136            // * Remove signature from parameters to calculate the hashmac.
137            $signature = collect([
138                'uri' => sprintf(
139                    '%s://%s%s',
140                    $uri->get('scheme'),
141                    $uri->get('host'),
142                    $uri->get('path')
143                ),
144                'method' => $method
145            ]);
146
147            $flattenRequestArray = function (array $array) use (&$flattenRequestArray) {
148                $out = [];
149                foreach ($array as $key => $value) {
150                    if (is_array($value)) {
151                        $subArray = $flattenRequestArray($value);
152                        foreach ($subArray as $subKey => $subValue) {
153                            $out["$key.$subKey"] = $subValue;
154                        }
155                        continue;
156                    }
157                    $out[$key] = $value;
158                }
159                return $out;
160            };
161
162            $parameters = $flattenRequestArray($parameters);
163            $parameters = collect($parameters);
164
165            // * Remove signature from parameters.
166            $parameters->pull('signature');
167
168            // * Natural sort all parameters.
169            $parameters = $parameters->sortKeys(\SORT_NATURAL)
170                ->map(function ($value, $key) {
171                    return sprintf('%s=%s', \rawurlencode($key), \rawurlencode($value));
172                });
173
174            $signature->push($parameters->implode('&'));
175
176            $calculatedHash = base64_encode(hash_hmac(
177                'sha256',
178                $signature->implode('\n'),
179                Crypt::decryptString($secret),
180                true
181            ));
182
183            if (!$calculatedHash) {
184                throw new CrmException('Failed to calculate requests hash');
185            }
186
187            return $calculatedHash;
188        } catch (DecryptException $e) {
189            throw new CrmException('Failed to decrypt service credentials', 0, $e);
190        } catch (\Exception $e) {
191            throw new CrmException('An error occured when authenticating service', 0, $e);
192        }//end try
193    }
194}