buntu 0.1.0__tar.gz → 0.2.0__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.
- {buntu-0.1.0/src/buntu.egg-info → buntu-0.2.0}/PKG-INFO +1 -1
- {buntu-0.1.0 → buntu-0.2.0}/pyproject.toml +1 -1
- {buntu-0.1.0 → buntu-0.2.0/src/buntu.egg-info}/PKG-INFO +1 -1
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_account_attacks.py +4 -4
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_mfa.py +10 -6
- {buntu-0.1.0 → buntu-0.2.0}/LICENSE +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/README.md +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/setup.cfg +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/buntu.egg-info/SOURCES.txt +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/buntu.egg-info/dependency_links.txt +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/buntu.egg-info/requires.txt +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/buntu.egg-info/top_level.txt +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/__init__.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/app.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/cli.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/config.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/db/__init__.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/extensions.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/models/__init__.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/models/account.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/models/clients.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/models/grants.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/models/platform.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/models/users.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/oauth/__init__.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/oauth/claims.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/oauth/grants.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/oauth/jwt.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/oauth/server.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/plugins/__init__.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/plugins/sync.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/security/__init__.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/security/crypto.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/security/csrf.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/security/data/breached.txt.gz +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/security/keys.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/security/passwords.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/security/tokens.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/security/totp.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/__init__.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/accounts.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/activity.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/audit.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/backchannel.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/closing.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/connected.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/consent.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/credentials.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/devices.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/mail.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/mfa.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/own_apps.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/passkeys.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/platform.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/profile.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/retention.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/security_prompt.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/session_check.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/sessions.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/services/setup.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/account-D-MHfC1z.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/account-DN_Addf0.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/app-CzfYFveQ.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/di-D0iZ5SU4.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/di-dx_XE3Av.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/i18n-BZyjOlbu.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/i18n-Dog5o8z6.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/roboto-latin-variable-ccAYIvAh.woff2 +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/schemas-Bl_iabpv.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/secure-account-Dl1pS_Wo.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/service-BhG4-X2O.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/service-vCuiuvPs.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/theme-toggle-C07m3YIv.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/two-step-section-DMQmuG2q.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/webauthn-BQas4tAd.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/idp.css +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/idp.js +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/templates/shell.html +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/web/__init__.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/web/admin.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/web/api.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/web/developer.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/web/oidc.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/web/openapi.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/web/pages.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/src/idp/web/validation.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_account_area.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_account_closing.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_account_connected.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_account_profile.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_account_security.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_app_overview.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_authorization_code.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_authorization_code_attacks.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_backchannel_logout.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_boundary_attacks.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_brand.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_cli.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_console_api.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_console_users.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_credentials.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_data_retention.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_deployment.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_developer_api.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_flows.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_grant_attacks.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_invitations.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_members.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_offline_access.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_openapi.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_own_apps.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_passkeys.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_platform_gates.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_platform_organization.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_plugin_attacks.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_plugins.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_review_rules.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_reviews.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_role_matrix.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_scope_rules.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_security_prompt.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_session_attacks.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_session_check.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_setup.py +0 -0
- {buntu-0.1.0 → buntu-0.2.0}/tests/test_token_attacks.py +0 -0
|
@@ -7,7 +7,7 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
from flask import Flask
|
|
9
9
|
from flask.testing import FlaskClient
|
|
10
|
-
from test_mfa import enable, password_step
|
|
10
|
+
from test_mfa import enable, password_step, wrong_code
|
|
11
11
|
|
|
12
12
|
from idp.security import totp
|
|
13
13
|
from idp.services import mail
|
|
@@ -34,7 +34,7 @@ def test_replaying_an_old_cookie_does_not_reset_the_two_step_limit(
|
|
|
34
34
|
cookie = other.get_cookie(COOKIE)
|
|
35
35
|
assert cookie is not None
|
|
36
36
|
for _ in range(8):
|
|
37
|
-
api(_with_cookie(app, cookie.value), "POST", "/api/mfa/verify", {"code":
|
|
37
|
+
api(_with_cookie(app, cookie.value), "POST", "/api/mfa/verify", {"code": wrong_code(secret)}, csrf)
|
|
38
38
|
good = totp.code_at(secret, totp.current_step() + 1) # the setup used the current step
|
|
39
39
|
last = api(_with_cookie(app, cookie.value), "POST", "/api/mfa/verify", {"code": good}, csrf)
|
|
40
40
|
assert last.status_code == 400 and last.json["error"] == "too_many_attempts", last.json
|
|
@@ -47,7 +47,7 @@ def test_the_two_step_limit_also_holds_across_fresh_password_steps(
|
|
|
47
47
|
for _ in range(2):
|
|
48
48
|
other, csrf, _ = password_step(app)
|
|
49
49
|
for _ in range(5):
|
|
50
|
-
api(other, "POST", "/api/mfa/verify", {"code":
|
|
50
|
+
api(other, "POST", "/api/mfa/verify", {"code": wrong_code(secret)}, csrf)
|
|
51
51
|
other, csrf, _ = password_step(app)
|
|
52
52
|
good = totp.code_at(secret, totp.current_step() + 1)
|
|
53
53
|
late = api(other, "POST", "/api/mfa/verify", {"code": good}, csrf)
|
|
@@ -67,7 +67,7 @@ def test_the_account_owner_gets_in_again_after_the_pause(
|
|
|
67
67
|
secret, _ = enable(browser, sign_up(browser, outbox))
|
|
68
68
|
other, csrf, _ = password_step(app)
|
|
69
69
|
for _ in range(5):
|
|
70
|
-
api(other, "POST", "/api/mfa/verify", {"code":
|
|
70
|
+
api(other, "POST", "/api/mfa/verify", {"code": wrong_code(secret)}, csrf)
|
|
71
71
|
with app.app_context():
|
|
72
72
|
db().execute(update(User).values(second_factor_failed_at=utcnow() - timedelta(minutes=16)))
|
|
73
73
|
db().commit()
|
|
@@ -31,14 +31,18 @@ def test_a_code_works_once_and_only_inside_the_window() -> None:
|
|
|
31
31
|
assert totp.matching_step(secret, old, None, now) is None
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def wrong_code(secret: str) -> str:
|
|
35
|
+
"""A code no step around now produces, so a refusal never depends on chance or the clock."""
|
|
36
|
+
here = totp.current_step()
|
|
37
|
+
taken = {totp.code_at(secret, step) for step in range(here - 2, here + 3)}
|
|
38
|
+
return next(c for c in ("000000", "111111", "222222", "333333", "444444", "555555") if c not in taken)
|
|
39
|
+
|
|
40
|
+
|
|
34
41
|
def enable(browser: FlaskClient, csrf: str) -> tuple[str, list[str]]:
|
|
35
42
|
setup = api(browser, "POST", "/api/mfa/totp", {}, csrf).json
|
|
36
43
|
assert setup["qr"].startswith("data:image/svg+xml") and setup["uri"].startswith("otpauth://totp/")
|
|
37
|
-
wrong = api(browser, "POST", "/api/mfa/totp/confirm", {"code": "
|
|
38
|
-
assert
|
|
39
|
-
wrong.json == {"error": "invalid_code"}
|
|
40
|
-
or totp.code_at(setup["secret"], totp.current_step()) == "000000"
|
|
41
|
-
)
|
|
44
|
+
wrong = api(browser, "POST", "/api/mfa/totp/confirm", {"code": wrong_code(setup["secret"])}, csrf)
|
|
45
|
+
assert wrong.json == {"error": "invalid_code"}
|
|
42
46
|
code = totp.code_at(setup["secret"], totp.current_step())
|
|
43
47
|
done = api(browser, "POST", "/api/mfa/totp/confirm", {"code": code}, csrf)
|
|
44
48
|
assert done.status_code == 200, done.json
|
|
@@ -99,7 +103,7 @@ def test_five_wrong_codes_start_over(app: Flask, browser: FlaskClient, outbox: l
|
|
|
99
103
|
def test_disabling_needs_a_code(app: Flask, browser: FlaskClient, outbox: list[mail.Message]) -> None:
|
|
100
104
|
csrf = sign_up(browser, outbox)
|
|
101
105
|
secret, _ = enable(browser, csrf)
|
|
102
|
-
assert api(browser, "POST", "/api/mfa/disable", {"code":
|
|
106
|
+
assert api(browser, "POST", "/api/mfa/disable", {"code": wrong_code(secret)}, csrf).status_code == 400
|
|
103
107
|
code = totp.code_at(secret, totp.current_step() + 1)
|
|
104
108
|
assert api(browser, "POST", "/api/mfa/disable", {"code": code}, csrf).json == {"enabled": False}
|
|
105
109
|
_, _, reply = password_step(app)
|
|
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
|
{buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2
RENAMED
|
File without changes
|
{buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2
RENAMED
|
File without changes
|
{buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2
RENAMED
|
File without changes
|
{buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-latin-wght-normal-BgDaEnEv.woff2
RENAMED
|
File without changes
|
|
File without changes
|
{buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2
RENAMED
|
File without changes
|
|
File without changes
|
{buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2
RENAMED
|
File without changes
|
{buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2
RENAMED
|
File without changes
|
|
File without changes
|
{buntu-0.1.0 → buntu-0.2.0}/src/idp/static/idp/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2
RENAMED
|
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
|