Skip to content

Commit

Permalink
Merge pull request #257 from Tinkoff/send_to_support_unreg_users
Browse files Browse the repository at this point in the history
Add setting for redirect unregistred users to support
  • Loading branch information
Resinchen authored Jul 20, 2023
2 parents a210685 + fecfcce commit b5735ff
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 7 deletions.
5 changes: 4 additions & 1 deletion docs/includes/context_example.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
task_tracker_url="https://jira.company.com/browse",
tasks_keyword="Tasks",
),
admin_settings=OverhaveAdminSettings(index_template_path=path_settings.index_template_path),
admin_settings=OverhaveAdminSettings(
index_template_path=path_settings.index_template_path,
support_chat_url="https://messenger.company.com/chat_id",
),
auth_settings=OverhaveAuthorizationSettings(auth_strategy=AuthorizationStrategy.LDAP),
ldap_client_settings=OverhaveLdapClientSettings(
ldap_url="ldap://company.com", ldap_domain="company\\", ldap_dn="dc=company,dc=com"
Expand Down
2 changes: 1 addition & 1 deletion makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CODE = overhave
VENV ?= .venv
WORK_DIR ?= .
MIN_COVERAGE ?= 88.1
MIN_COVERAGE ?= 88.2
PACKAGE_BUILD_DIR ?= dist
PYTHON_VERSION ?= 3.11

Expand Down
1 change: 1 addition & 0 deletions overhave/admin/flask/flask_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def get_flask_admin(factory: IAdminFactory) -> Admin:
url="/",
auth_manager=factory.auth_manager,
index_template_path=factory.context.admin_settings.index_template_path,
support_chat_url=factory.context.admin_settings.support_chat_url,
)
admin = Admin(
name="Overhave", template_mode="bootstrap4", index_view=index_view, base_template="overhave_master.html"
Expand Down
16 changes: 14 additions & 2 deletions overhave/admin/views/index/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from typing import Any

import flask
import httpx
import ldap
from flask_admin import AdminIndexView, expose
from flask_login import login_required, login_user, logout_user
from markupsafe import Markup
from sqlalchemy.exc import OperationalError, ProgrammingError
from werkzeug.wrappers import Response
from wtforms.validators import ValidationError
Expand All @@ -21,14 +23,20 @@ class OverhaveIndexView(AdminIndexView):
"""View for index."""

def __init__(
self, name: str, url: str, auth_manager: IAdminAuthorizationManager, index_template_path: Path | None
self,
name: str,
url: str,
auth_manager: IAdminAuthorizationManager,
index_template_path: Path | None,
support_chat_url: httpx.URL | None,
) -> None:
super().__init__(
name=name,
url=url,
)
self._auth_manager = auth_manager
self._index_template_path = index_template_path
self._support_chat_url = support_chat_url

@expose("/login", methods=["GET", "POST"])
def login(self) -> Any: # noqa: C901
Expand All @@ -39,7 +47,11 @@ def login(self) -> Any: # noqa: C901
user = form.get_user()
login_user(user)
except ValidationError:
return form.flash_and_redirect("Incorrect username/password pair!")
flash_msg = f"Username '{form.username.data}' is not registered!"
if self._support_chat_url:
flash_msg += f" Please contact the <a href='{self._support_chat_url}'>support channel</a>!"

return form.flash_and_redirect(Markup(flash_msg))
except ldap.SERVER_DOWN:
return form.flash_and_redirect("LDAP auth_managers service is unreachable!")
except OperationalError:
Expand Down
3 changes: 3 additions & 0 deletions overhave/entities/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class OverhaveAdminSettings(BaseOverhavePrefix):
# Force filling the tasks field in feature creation
strict_feature_tasks: bool = False

# Link to support chat
support_chat_url: httpx.URL | None


class OverhaveLanguageSettings(BaseOverhavePrefix):
"""Settings for language definitions."""
Expand Down
10 changes: 9 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Any, Callable, Iterator, Sequence, cast
from unittest import mock

import httpx
import py
import pytest
import sqlalchemy as sa
Expand All @@ -19,6 +20,7 @@
from sqlalchemy import event

from overhave import (
OverhaveAdminSettings,
OverhaveAuthorizationStrategy,
OverhaveDBSettings,
OverhaveLoggingSettings,
Expand Down Expand Up @@ -139,6 +141,12 @@ def mocked_context(session_mocker: MockerFixture, tmpdir: py.path.local) -> Base
context_mock.compilation_settings = OverhaveScenarioCompilerSettings()
context_mock.parser_settings = OverhaveScenarioParserSettings()

if os.environ.get("TEST_SUPPORT_CHAT_URL"):
test_support_chat_url = httpx.URL(os.environ["TEST_SUPPORT_CHAT_URL"])
else:
test_support_chat_url = None
context_mock.admin_settings = OverhaveAdminSettings(support_chat_url=test_support_chat_url)

root_dir = Path(tmpdir)
features_dir = root_dir / "features"
fixtures_dir = root_dir / "fixtures"
Expand All @@ -149,7 +157,7 @@ def mocked_context(session_mocker: MockerFixture, tmpdir: py.path.local) -> Base
context_mock.file_settings.tmp_fixtures_dir = fixtures_dir
context_mock.file_settings.tmp_reports_dir = reports_dir

return cast(BaseFactoryContext, context_mock)
return cast("BaseFactoryContext", context_mock)


@pytest.fixture(scope="session")
Expand Down
36 changes: 34 additions & 2 deletions tests/integration/admin/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,45 @@
import pytest
from faker import Faker
from flask.testing import FlaskClient
from pytest_mock import MockFixture

from overhave import OverhaveAdminApp, overhave_app
from overhave import OverhaveAdminApp, OverhaveLdapManagerSettings, overhave_app
from overhave.admin.flask.login_manager import AdminPanelUser
from overhave.admin.views.index.login_form import LoginForm
from overhave.base_settings import DataBaseSettings
from overhave.entities import LDAPAdminAuthorizationManager
from overhave.factory import IAdminFactory
from overhave.factory.context.base_context import BaseFactoryContext
from overhave.pytest_plugin import IProxyManager
from overhave.storage import SystemUserModel
from overhave.storage import SystemUserGroupStorage, SystemUserModel, SystemUserStorage
from overhave.transport import LDAPAuthenticator


@pytest.fixture()
def mocked_ldap_authenticator(mocker: MockFixture) -> LDAPAuthenticator:
mocked = mocker.create_autospec(LDAPAuthenticator)
mocked.get_user_groups.return_value = None
return mocked


@pytest.fixture()
def test_ldap_manager_settings(faker) -> OverhaveLdapManagerSettings:
return OverhaveLdapManagerSettings(ldap_admin_group=faker.word())


@pytest.fixture()
def test_ldap_auth_manager(
test_system_user_storage: SystemUserStorage,
test_system_user_group_storage: SystemUserGroupStorage,
mocked_ldap_authenticator: LDAPAuthenticator,
test_ldap_manager_settings: OverhaveLdapManagerSettings,
) -> LDAPAdminAuthorizationManager:
return LDAPAdminAuthorizationManager(
settings=test_ldap_manager_settings,
system_user_storage=test_system_user_storage,
system_user_group_storage=test_system_user_group_storage,
ldap_authenticator=mocked_ldap_authenticator,
)


@pytest.fixture()
Expand All @@ -24,10 +54,12 @@ def patched_app_admin_factory(
database: None,
mocked_context: BaseFactoryContext,
clean_admin_factory: Callable[[], IAdminFactory],
test_ldap_auth_manager,
) -> IAdminFactory:
db_settings.setup_engine()
factory = clean_admin_factory()
factory.set_context(mocked_context)
factory.auth_manager = test_ldap_auth_manager
return factory


Expand Down
45 changes: 45 additions & 0 deletions tests/integration/admin/views/test_login_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import os

import pytest
from flask.testing import FlaskClient

from overhave import OverhaveAdminApp


@pytest.fixture(scope="module")
def mock_support_chat_url() -> None:
os.environ["TEST_SUPPORT_CHAT_URL"] = "https://localhost"
return


@pytest.mark.usefixtures("database")
class TestLoginView:
"""Tests for login view."""

def test_show_flash_with_chat_for_unregistered_user(
self,
test_app: OverhaveAdminApp,
test_client: FlaskClient,
mock_support_chat_url: None,
) -> None:
test_app.config["WTF_CSRF_ENABLED"] = False

response = test_client.post("/login", data={"username": "kek", "password": "12345"}, follow_redirects=True)

assert (
"Username 'kek' is not registered! Please contact the <a href='https://localhost'>support channel</a>!"
in response.data.decode("utf-8")
), '"Unauthorized" flash not be showed'

def test_show_flash_without_chat_for_unregistered_user(
self,
test_app: OverhaveAdminApp,
test_client: FlaskClient,
) -> None:
test_app.config["WTF_CSRF_ENABLED"] = False

response = test_client.post("/login", data={"username": "kek", "password": "12345"}, follow_redirects=True)

assert "Username 'kek' is not registered!" in response.data.decode(
"utf-8"
), '"Unauthorized" flash not be showed'
37 changes: 37 additions & 0 deletions tests/unit/api/test_auth_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import pytest

from overhave.api.auth import AuthTokenData
from overhave.api.auth.token import get_token_data
from overhave.api.settings import OverhaveApiAuthSettings


@pytest.fixture()
def auth_settings() -> OverhaveApiAuthSettings:
return OverhaveApiAuthSettings(secret_key="secret")


class TestAuthToken:
"""Tests for get jwt-token payload."""

@pytest.mark.parametrize(
("token", "expected_value"),
[
(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdHViIjoicXdlcnR5In0."
"jsJEgGO9F0h2A-DuJqFxT2XhfZ_I-pK1bZPWP76ZczQ",
None,
),
(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJxd2VydHkifQ."
"6G7k5vyAcQPCMGmIhHH-dH1eHltTuhRg6o-cy6QdCnk",
AuthTokenData(username="qwerty"),
),
],
)
def test_get_token_data(
self,
auth_settings: OverhaveApiAuthSettings,
token: str,
expected_value: AuthTokenData | None,
) -> None:
assert get_token_data(auth_settings, token) == expected_value

0 comments on commit b5735ff

Please sign in to comment.