Encryptors 2.68__tar.gz → 2.70__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {encryptors-2.68 → encryptors-2.70}/PKG-INFO +1 -1
- {encryptors-2.68 → encryptors-2.70}/setup.py +1 -1
- {encryptors-2.68 → encryptors-2.70}/src/Encryptors.egg-info/PKG-INFO +1 -1
- {encryptors-2.68 → encryptors-2.70}/src/Encryptors.egg-info/SOURCES.txt +1 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Decorators/SecureResolver.py +28 -19
- encryptors-2.70/src/Osdental/Enums/GrpcStatusCode.py +25 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Helpers/AuditHelper.py +9 -6
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Helpers/AuthTokenProcessor.py +19 -3
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Models/AuditContext.py +5 -4
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Models/Graphql.py +4 -3
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Models/Token.py +0 -2
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Services/ServiceBusAuditEmitter.py +1 -0
- {encryptors-2.68 → encryptors-2.70}/README.md +0 -0
- {encryptors-2.68 → encryptors-2.70}/setup.cfg +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Encryptors.egg-info/dependency_links.txt +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Encryptors.egg-info/entry_points.txt +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Encryptors.egg-info/requires.txt +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Encryptors.egg-info/top_level.txt +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Cache/Redis.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Cache/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Cli/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Constants/Constant.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Constants/Message.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Constants/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Context/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Database/BaseRepository.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Database/Connection.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Database/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Decorators/Grpc.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Decorators/Retry.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Decorators/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Encryptor/Aes.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Encryptor/Argon2.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Encryptor/Bcrypt.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Encryptor/Jwt.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Encryptor/Rsa.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Encryptor/Sha512.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Encryptor/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Enums/AuditType.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Enums/CipherType.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Enums/FileType.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Enums/GrahpqlOperation.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Enums/Profile.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Enums/ResultType.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Enums/StatusCode.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Enums/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Exception/ControlledException.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Exception/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Helpers/AuditQueue.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Helpers/AzureClassifier.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Helpers/CipherHelper.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Helpers/GrpcConnection.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Helpers/JwtTokenHelper.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Helpers/Resilience.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Helpers/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Http/APIClient.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Http/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Messaging/AzureServiceBus.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Messaging/Kafka.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Messaging/RabbitMQ.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Messaging/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Models/ApiResponse.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Models/Notification.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Models/Response.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Models/TokenClaims.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Models/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Secrets/AzureKeyVaultProvider.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Secrets/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Services/JwtAuthTokenService.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Services/WebsocketClient.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Services/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Storage/AzureBlobStorage.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Storage/S3Storage.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Storage/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/CaseConverter.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/CodeGenerator.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/DataNormalizer.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/DataUtils.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/DateUtils.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/FileMetaData.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/HashValidator.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/Mapper.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/PasswordGenerator.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/QueryGenerator.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/RsaUtils.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/TextProcessor.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/Utils/__init__.py +0 -0
- {encryptors-2.68 → encryptors-2.70}/src/Osdental/__init__.py +0 -0
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
# ANDERSON REVISAR EL CACHE LOCAL DEL KEYVAULT PARA VALIDAR SI FUNCIONA
|
|
3
3
|
setup(
|
|
4
4
|
name="Encryptors",
|
|
5
|
-
version="2.
|
|
5
|
+
version="2.70",
|
|
6
6
|
author="OSDental LLC",
|
|
7
7
|
author_email="support@osdental.ai",
|
|
8
8
|
description="End-to-end algorithm library",
|
|
@@ -32,6 +32,7 @@ src/Osdental/Enums/AuditType.py
|
|
|
32
32
|
src/Osdental/Enums/CipherType.py
|
|
33
33
|
src/Osdental/Enums/FileType.py
|
|
34
34
|
src/Osdental/Enums/GrahpqlOperation.py
|
|
35
|
+
src/Osdental/Enums/GrpcStatusCode.py
|
|
35
36
|
src/Osdental/Enums/Profile.py
|
|
36
37
|
src/Osdental/Enums/ResultType.py
|
|
37
38
|
src/Osdental/Enums/StatusCode.py
|
|
@@ -14,7 +14,8 @@ from Osdental.Exception.ControlledException import (
|
|
|
14
14
|
)
|
|
15
15
|
from Osdental.Services import IAuthTokenService
|
|
16
16
|
from Osdental.Helpers.AuthTokenProcessor import (
|
|
17
|
-
extract_bearer_token, build_auth_token, decrypt_and_parse_payload
|
|
17
|
+
extract_bearer_token, build_auth_token, decrypt_and_parse_payload,
|
|
18
|
+
get_current_user, get_current_aes_key
|
|
18
19
|
)
|
|
19
20
|
from Osdental.Enums.StatusCode import StatusCode
|
|
20
21
|
from Osdental.Models.AuditContext import AuditContext
|
|
@@ -40,10 +41,12 @@ def resolver(public: bool = False, action=None):
|
|
|
40
41
|
headers = request.headers
|
|
41
42
|
settings = container.settings
|
|
42
43
|
|
|
43
|
-
access_token =
|
|
44
|
-
token: type[AuthToken] |
|
|
44
|
+
access_token = headers.get("Accesstoken")
|
|
45
|
+
token: type[AuthToken] | None = None
|
|
45
46
|
aes_key = None
|
|
46
|
-
claims: UserTokenClaims | AccessTokenClaims
|
|
47
|
+
claims: UserTokenClaims | AccessTokenClaims | None = None
|
|
48
|
+
audit_ctx: AuditContext | None = None
|
|
49
|
+
operation_type = info.operation.operation.value
|
|
47
50
|
|
|
48
51
|
# ── 1. AUTENTICACIÓN ──────────────────────────────
|
|
49
52
|
token_service: IAuthTokenService = container.auth_token_service
|
|
@@ -60,12 +63,14 @@ def resolver(public: bool = False, action=None):
|
|
|
60
63
|
)
|
|
61
64
|
aes_key = claims.aes_key
|
|
62
65
|
context.access_token = access_token
|
|
63
|
-
context.
|
|
66
|
+
context.access_token_claims = claims
|
|
64
67
|
|
|
65
68
|
# ── 2. DECRYPT DEL PAYLOAD ────────────────────────
|
|
66
69
|
body = await request.json()
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
encrypted_payload = (
|
|
71
|
+
kwargs.get("data")
|
|
72
|
+
or body.get("variables", {}).get("data")
|
|
73
|
+
)
|
|
69
74
|
decrypted_payload = None
|
|
70
75
|
|
|
71
76
|
if encrypted_payload is not None:
|
|
@@ -83,7 +88,7 @@ def resolver(public: bool = False, action=None):
|
|
|
83
88
|
claims=claims,
|
|
84
89
|
headers=request.headers,
|
|
85
90
|
decrypted_payload=decrypted_payload,
|
|
86
|
-
operation_type=
|
|
91
|
+
operation_type=operation_type,
|
|
87
92
|
)
|
|
88
93
|
|
|
89
94
|
context.token = token
|
|
@@ -98,13 +103,15 @@ def resolver(public: bool = False, action=None):
|
|
|
98
103
|
|
|
99
104
|
# ── 5. CONTEXTO DE AUDITORÍA ──────────────
|
|
100
105
|
# Lo construimos antes del resolver para que siempre exista
|
|
101
|
-
|
|
106
|
+
user = get_current_user(context)
|
|
107
|
+
key = get_current_aes_key(context)
|
|
102
108
|
audit_ctx = AuditContext.from_request(
|
|
103
109
|
request=request,
|
|
104
110
|
settings=settings,
|
|
105
111
|
body=body,
|
|
106
|
-
operation_type=
|
|
107
|
-
|
|
112
|
+
operation_type=operation_type,
|
|
113
|
+
id_user=user,
|
|
114
|
+
decrypted_key=key,
|
|
108
115
|
decrypted_payload=decrypted_payload,
|
|
109
116
|
)
|
|
110
117
|
|
|
@@ -124,20 +131,22 @@ def resolver(public: bool = False, action=None):
|
|
|
124
131
|
return result
|
|
125
132
|
|
|
126
133
|
except OSDException as e:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
134
|
+
logger.warning(f"Business error: {str(e)}")
|
|
135
|
+
if audit_ctx:
|
|
136
|
+
await container.audit_emitter.emit(
|
|
137
|
+
_build_error_payload(audit_ctx, e)
|
|
138
|
+
)
|
|
131
139
|
return Response(
|
|
132
140
|
status=e.status_code,
|
|
133
141
|
message=e.message,
|
|
134
142
|
error=e.error
|
|
135
143
|
)
|
|
136
144
|
except Exception as e:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
145
|
+
logger.exception(f"Unexpected error: {str(e)}")
|
|
146
|
+
if audit_ctx:
|
|
147
|
+
await container.audit_emitter.emit(
|
|
148
|
+
_build_unexpected_payload(audit_ctx, e)
|
|
149
|
+
)
|
|
141
150
|
return Response(
|
|
142
151
|
status=StatusCode.INTERNAL_SERVER_ERROR,
|
|
143
152
|
message="Could not process request.",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
|
|
3
|
+
class GrpcStatusCode(StrEnum):
|
|
4
|
+
# --- Éxito ---
|
|
5
|
+
OK = "OK" # Código 0: Operación exitosa.
|
|
6
|
+
|
|
7
|
+
# --- Errores del Cliente (4xx equivalentes) ---
|
|
8
|
+
CANCELLED = "CANCELLED" # Código 1: La operación fue cancelada por el cliente.
|
|
9
|
+
UNKNOWN = "UNKNOWN" # Código 2: Error desconocido (ej. una excepción no controlada).
|
|
10
|
+
INVALID_ARGUMENT = "INVALID_ARGUMENT" # Código 3: El cliente envió datos inválidos (parámetros mutados/vacíos).
|
|
11
|
+
DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED = " # Código 4: El tiempo de espera (Timeout) expiró antes de terminar.
|
|
12
|
+
NOT_FOUND = "NOT_FOUND" # Código 5: El recurso solicitado no fue encontrado.
|
|
13
|
+
ALREADY_EXISTS = "ALREADY_EXISTS" # Código 6: El recurso que se intentó crear ya existe.
|
|
14
|
+
PERMISSION_DENIED = "PERMISSION_DENIED" # Código 7: Elige este si el usuario está autenticado pero NO tiene permisos para este recurso.
|
|
15
|
+
RESOURCE_EXHAUSTED = "RESOURCE_EXHAUSTED" # Código 8: Se agotaron los recursos (Rate limit, cuotas de disco, etc.).
|
|
16
|
+
FAILED_PRECONDITION = "FAILED_PRECONDITION" # Código 9: El sistema no está en el estado requerido (ej. borrar un directorio que no está vacío).
|
|
17
|
+
ABORTED = "ABORTED" # Código 10: Operación abortada por un conflicto de concurrencia (ej. transacciones de BD).
|
|
18
|
+
OUT_OF_RANGE = "OUT_OF_RANGE" # Código 11: Operación pasada del rango válido (ej. un offset de paginación inválido).
|
|
19
|
+
UNAUTHENTICATED = "UNAUTHENTICATED" # Código 16: El usuario no está autenticado (Falta token, token expirado).
|
|
20
|
+
|
|
21
|
+
# --- Errores del Servidor (5xx equivalentes) ---
|
|
22
|
+
UNIMPLEMENTED = "UNIMPLEMENTED" # Código 12: El método o servicio no está implementado en el server.
|
|
23
|
+
INTERNAL = "INTERNAL" # Código 13: Errores graves internos del servidor (Bug en código, caída de BD principal).
|
|
24
|
+
UNAVAILABLE = "UNAVAILABLE" # Código 14: El microservicio o dependencia no está disponible (Servicio caído).
|
|
25
|
+
DATA_LOSS = "DATA_LOSS" # Código 15: Pérdida o corrupción de datos irrecuperable.
|
|
@@ -79,13 +79,16 @@ def _build_final_payload(
|
|
|
79
79
|
def _build_success_payload(ctx: Optional[AuditContext], result: Response) -> Dict[str, Any]:
|
|
80
80
|
base = _base_payload(ctx)
|
|
81
81
|
|
|
82
|
-
key = result.key
|
|
82
|
+
key = result.key or ctx.decrypted_key
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
if key is None:
|
|
85
|
+
data = result.data
|
|
86
|
+
else:
|
|
87
|
+
data = decryptor_data(
|
|
88
|
+
key=key,
|
|
89
|
+
data=result.data,
|
|
90
|
+
encryption_type=result.encryption_type,
|
|
91
|
+
)
|
|
89
92
|
|
|
90
93
|
return base | _build_final_payload(
|
|
91
94
|
type_=ResultType.RESPONSE,
|
|
@@ -7,6 +7,7 @@ from Osdental.Encryptor.Aes import AES
|
|
|
7
7
|
from Osdental.Models.Token import AuthToken
|
|
8
8
|
from Osdental.Enums.Profile import Profile
|
|
9
9
|
from Osdental.Models.TokenClaims import UserTokenClaims
|
|
10
|
+
from Osdental.Models.Graphql import BaseGraphQLContext
|
|
10
11
|
|
|
11
12
|
def extract_bearer_token(headers: Headers) -> str:
|
|
12
13
|
|
|
@@ -56,8 +57,6 @@ def build_auth_token(
|
|
|
56
57
|
id_external_enterprise=final_id,
|
|
57
58
|
id_profile=claims.id_profile,
|
|
58
59
|
id_legacy=claims.id_legacy,
|
|
59
|
-
id_authorization="000",
|
|
60
|
-
id_item_report="000",
|
|
61
60
|
id_enterprise=claims.id_enterprise,
|
|
62
61
|
user_full_name=claims.user_full_name,
|
|
63
62
|
abbreviation=claims.abbreviation,
|
|
@@ -95,4 +94,21 @@ def decrypt_and_parse_payload(aes_key_auth: str, encrypted_payload: str) -> Dict
|
|
|
95
94
|
else:
|
|
96
95
|
raise ValueError("Unsupported decrypted payload type")
|
|
97
96
|
|
|
98
|
-
return decrypted_payload
|
|
97
|
+
return decrypted_payload
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_current_user(context: BaseGraphQLContext):
|
|
101
|
+
if context.token:
|
|
102
|
+
return context.token.id_user
|
|
103
|
+
|
|
104
|
+
return "00000000-0000-0000-0000-000000000000"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_current_aes_key(context: BaseGraphQLContext):
|
|
108
|
+
if context.token:
|
|
109
|
+
return context.token.aes_key_auth
|
|
110
|
+
|
|
111
|
+
if context.access_token_claims:
|
|
112
|
+
return context.access_token_claims.aes_key
|
|
113
|
+
|
|
114
|
+
return None
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Optional, Dict, Any
|
|
3
3
|
from fastapi import Request
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
|
|
6
6
|
@dataclass
|
|
7
7
|
class AuditContext:
|
|
@@ -30,7 +30,8 @@ class AuditContext:
|
|
|
30
30
|
settings,
|
|
31
31
|
body: Dict,
|
|
32
32
|
operation_type: str,
|
|
33
|
-
|
|
33
|
+
id_user: str | None,
|
|
34
|
+
decrypted_key: str | None,
|
|
34
35
|
decrypted_payload = None,
|
|
35
36
|
) -> "AuditContext":
|
|
36
37
|
|
|
@@ -51,9 +52,9 @@ class AuditContext:
|
|
|
51
52
|
operation_name=body.get("operationName", "UnknownOperation"),
|
|
52
53
|
query=body.get("query"),
|
|
53
54
|
variables=decrypted_payload,
|
|
54
|
-
user=
|
|
55
|
+
user=id_user if id_user else "00000000-0000-0000-0000-000000000000",
|
|
55
56
|
message_log_id=request.headers.get("Idmessagelog"),
|
|
56
|
-
decrypted_key=
|
|
57
|
+
decrypted_key=decrypted_key,
|
|
57
58
|
user_ip=user_ip,
|
|
58
59
|
headers=safe_headers,
|
|
59
60
|
url=str(request.url),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Any
|
|
3
3
|
from fastapi import Request
|
|
4
4
|
from Osdental.Models.Token import AuthToken
|
|
5
5
|
from Osdental.Models.TokenClaims import AccessTokenClaims
|
|
@@ -8,5 +8,6 @@ from Osdental.Models.TokenClaims import AccessTokenClaims
|
|
|
8
8
|
class BaseGraphQLContext:
|
|
9
9
|
request: Request
|
|
10
10
|
container: Any
|
|
11
|
-
access_token:
|
|
12
|
-
|
|
11
|
+
access_token: str | None = None
|
|
12
|
+
access_token_claims: AccessTokenClaims | None = None
|
|
13
|
+
token: AuthToken | None = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|