Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.48% covered (success)
93.48%
86 / 92
66.67% covered (warning)
66.67%
8 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
SeniorCredentialResolver
93.48% covered (success)
93.48%
86 / 92
66.67% covered (warning)
66.67%
8 / 12
44.54
0.00% covered (danger)
0.00%
0 / 1
 resolveForAuthenticatedUser
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
8
 resolveForUser
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
3
 resolveUsername
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 resolveRequiredValue
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
4.07
 resolveValue
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 resolveDefaultCredentials
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 hasMissingRequesterCredentials
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 attemptDecryptString
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 attemptDecryptLegacy
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
 normalizeDecryptedValue
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
4.59
 looksEncryptedPayload
80.00% covered (warning)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
6.29
 looksSerializedString
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace Domain\Shared\Integrations\Senior;
4
5use App\Models\User;
6use Domain\Shared\Enums\SectorEnum;
7use Domain\Shared\Integrations\Senior\Exceptions\SeniorCredentialsException;
8use Domain\User\Enums\AccessLevel;
9use Illuminate\Support\Facades\Auth;
10use Illuminate\Support\Facades\Cache;
11use Illuminate\Support\Facades\Crypt;
12use Throwable;
13
14class SeniorCredentialResolver
15{
16    public function resolveForAuthenticatedUser(?User $user = null, bool $fallbackToDefaultOnMissing = false): array
17    {
18        $user ??= Auth::user();
19
20        if (
21            !$user ||
22            !(
23                $user->hasAccessLevel(AccessLevel::REQUESTER->value, SectorEnum::WAREHOUSE->value) ||
24                $user->hasAccessLevel(AccessLevel::SEPARATOR->value, SectorEnum::WAREHOUSE->value)
25            )
26        ) {
27            return $this->resolveDefaultCredentials();
28        }
29
30        $tokenId = $user->currentAccessToken()?->id;
31        if (!is_null($tokenId)) {
32            $cachedPassword = Cache::get("erp_password_{$tokenId}");
33
34            if (is_string($cachedPassword) && trim($cachedPassword) !== '') {
35                $username = $this->resolveUsername($user);
36
37                if (!is_null($username)) {
38                    return [
39                        'user' => $username,
40                        'password' => $this->resolveValue(
41                            $cachedPassword,
42                            'Credenciais do ERP Senior invalidas para o requisitante.'
43                        ),
44                    ];
45                }
46            }
47        }
48
49        return $this->resolveForUser($user, $fallbackToDefaultOnMissing);
50    }
51
52    public function resolveForUser(User $user, bool $fallbackToDefaultOnMissing = false): array
53    {
54        $seniorCredentials = $user->senior() ?? [];
55
56        if ($fallbackToDefaultOnMissing && $this->hasMissingRequesterCredentials($seniorCredentials)) {
57            return $this->resolveDefaultCredentials();
58        }
59
60        return [
61            'user' => $this->resolveRequiredValue(
62                $seniorCredentials['user'] ?? null,
63                'Credenciais do ERP Senior não configuradas para o requisitante.',
64                'Credenciais do ERP Senior inválidas para o requisitante.'
65            ),
66            'password' => $this->resolveRequiredValue(
67                $seniorCredentials['password'] ?? null,
68                'Credenciais do ERP Senior não configuradas para o requisitante.',
69                'Credenciais do ERP Senior inválidas para o requisitante.'
70            ),
71        ];
72    }
73
74    public function resolveUsername(User $user): ?string
75    {
76        $username = $user->senior()['user'] ?? null;
77
78        if (!is_string($username) || trim($username) === '') {
79            return null;
80        }
81
82        return $this->resolveValue(
83            $username,
84            'Credenciais do ERP Senior inválidas para o requisitante.'
85        );
86    }
87
88    private function resolveRequiredValue(mixed $value, string $missingMessage, string $invalidMessage): string
89    {
90        if (!is_string($value) || trim($value) === '') {
91            throw new SeniorCredentialsException($missingMessage);
92        }
93
94        $resolvedValue = $this->resolveValue($value, $invalidMessage);
95
96        if (trim($resolvedValue) === '') {
97            throw new SeniorCredentialsException($missingMessage);
98        }
99
100        return $resolvedValue;
101    }
102
103    private function resolveValue(string $value, string $invalidMessage): string
104    {
105        if (!$this->looksEncryptedPayload($value)) {
106            return $value;
107        }
108
109        $decryptedValue = $this->attemptDecryptString($value)
110            ?? $this->attemptDecryptLegacy($value);
111
112        if (!is_string($decryptedValue) || $decryptedValue === '') {
113            throw new SeniorCredentialsException($invalidMessage);
114        }
115
116        return $decryptedValue;
117    }
118
119    private function resolveDefaultCredentials(): array
120    {
121        return [
122            'user' => (string) config('integration.user'),
123            'password' => (string) config('integration.user_password'),
124        ];
125    }
126
127    private function hasMissingRequesterCredentials(array $seniorCredentials): bool
128    {
129        $username = $seniorCredentials['user'] ?? null;
130        $password = $seniorCredentials['password'] ?? null;
131
132        return !is_string($username)
133            || trim($username) === ''
134            || !is_string($password)
135            || trim($password) === '';
136    }
137
138    private function attemptDecryptString(string $value): ?string
139    {
140        try {
141            $decryptedValue = Crypt::decryptString($value);
142
143            return $this->normalizeDecryptedValue($decryptedValue);
144        } catch (Throwable) {
145            return null;
146        }
147    }
148
149    private function attemptDecryptLegacy(string $value): ?string
150    {
151        try {
152            $decryptedValue = Crypt::decrypt($value);
153
154            return is_string($decryptedValue) ? $decryptedValue : null;
155        } catch (Throwable) {
156            return null;
157        }
158    }
159
160    private function normalizeDecryptedValue(string $value): ?string
161    {
162        if ($this->looksSerializedString($value)) {
163            try {
164                $deserializedValue = @unserialize($value);
165
166                return is_string($deserializedValue) ? $deserializedValue : null;
167            } catch (Throwable) {
168                return null;
169            }
170        }
171
172        return $value;
173    }
174
175    private function looksEncryptedPayload(string $value): bool
176    {
177        $decodedValue = base64_decode($value, true);
178
179        if ($decodedValue === false) {
180            return false;
181        }
182
183        try {
184            $payload = json_decode($decodedValue, true, 512, JSON_THROW_ON_ERROR);
185        } catch (Throwable) {
186            return false;
187        }
188
189        return is_array($payload)
190            && array_key_exists('iv', $payload)
191            && array_key_exists('value', $payload)
192            && array_key_exists('mac', $payload);
193    }
194
195    private function looksSerializedString(string $value): bool
196    {
197        return str_starts_with($value, 's:')
198            && str_ends_with($value, ';');
199    }
200}