Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

管理画面ログイン履歴とIPアドレスによるアクセス拒否を実装 #4978

Merged
merged 8 commits into from
Mar 30, 2021
1 change: 1 addition & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ MAILER_URL=null://localhost
#ECCUBE_ADMIN_ROUTE=admin
#ECCUBE_USER_DATA_ROUTE=user_data
#ECCUBE_ADMIN_ALLOW_HOSTS=[]
#ECCUBE_ADMIN_DENY_HOSTS=[]
#ECCUBE_FORCE_SSL=false
#ECCUBE_TEMPLATE_CODE=default
#ECCUBE_AUTH_MAGIC=<change.me>
Expand Down
59 changes: 59 additions & 0 deletions app/DoctrineMigrations/Version20210319122142.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

/*
* This file is part of EC-CUBE
*
* Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
*
* http://www.ec-cube.co.jp/
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
use Eccube\Entity\Master\LoginHistoryStatus;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210319122142 extends AbstractMigration
{
public function up(Schema $schema): void
{
$lang = env('ECCUBE_LOCALE');
$statuses = [
LoginHistoryStatus::FAILURE => $lang === 'en' ? 'Failure' : '失敗',
LoginHistoryStatus::SUCCESS => $lang === 'en' ? 'Success' : '成功',
];

$sortNo = $this->connection->fetchColumn('SELECT MAX(sort_no) + 1 FROM mtb_login_history_status');
if (is_null($sortNo)) {
$sortNo = 0;
}

foreach ($statuses as $id => $name) {
$statusExists = $this->connection->fetchColumn(
'SELECT COUNT(*) FROM mtb_login_history_status WHERE id = :id',
[':id' => $id]
);

if ($statusExists == 0) {
$this->addSql(
"INSERT INTO mtb_login_history_status (id, name, sort_no, discriminator_type) VALUES (?, ?, ?, 'loginhistorystatus')",
[$id, $name, $sortNo++]
);
}
}
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
}
}
2 changes: 2 additions & 0 deletions app/config/eccube/packages/eccube.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ parameters:
env(ECCUBE_ADMIN_ROUTE): 'admin'
env(ECCUBE_USER_DATA_ROUTE): 'user_data'
env(ECCUBE_ADMIN_ALLOW_HOSTS): '[]'
env(ECCUBE_ADMIN_DENY_HOSTS): '[]'
env(ECCUBE_FORCE_SSL): false
env(ECCUBE_TEMPLATE_CODE): 'default'
env(ECCUBE_AUTH_MAGIC): '<change.me>'
Expand All @@ -20,6 +21,7 @@ parameters:
eccube_admin_route: '%env(ECCUBE_ADMIN_ROUTE)%'
eccube_user_data_route: '%env(ECCUBE_USER_DATA_ROUTE)%'
eccube_admin_allow_hosts: '%env(json:ECCUBE_ADMIN_ALLOW_HOSTS)%'
eccube_admin_deny_hosts: '%env(json:ECCUBE_ADMIN_DENY_HOSTS)%'
eccube_force_ssl: '%env(bool:ECCUBE_FORCE_SSL)%'
eccube.theme: '%env(ECCUBE_TEMPLATE_CODE)%'
eccube_theme_code: '%eccube.theme%'
Expand Down
3 changes: 3 additions & 0 deletions app/config/eccube/packages/eccube_nav.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ parameters:
security:
name: admin.setting.system.security_management
url: admin_setting_system_security
login_history:
name: admin.setting.system.login_history
url: admin_setting_system_login_history
log:
name: admin.setting.system.log_display
url: admin_setting_system_log
Expand Down
2 changes: 1 addition & 1 deletion codeception/_support/AcceptanceTester.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function logoutAsAdmin()
$isLogin = $I->grabTextFrom('header.c-headerBar div.c-headerBar__container a.c-headerBar__userMenu span');
if ($isLogin == '管理者 様') {
$I->click('header.c-headerBar div.c-headerBar__container a.c-headerBar__userMenu');
$I->click('#page_admin_homepage div.popover .popover-body a:last-child');
$I->click('body div.popover .popover-body a:last-child');
$config = Fixtures::get('config');
$I->amOnPage('/'.$config['eccube_admin_route'].'/logout');
$I->see('ログイン', '#form1 > button');
Expand Down
63 changes: 63 additions & 0 deletions codeception/_support/Page/Admin/LoginHistoryPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/*
* This file is part of EC-CUBE
*
* Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
*
* http://www.ec-cube.co.jp/
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Page\Admin;

class LoginHistoryPage extends AbstractAdminPageStyleGuide
{
public static $URL = '/setting/system/login_history';

public static $検索条件 = ['id' => 'admin_search_login_history_multi'];
public static $検索ボタン = '#search_form .c-outsideBlock__contents button';
public static $詳細検索ボタン = '//*[@id="search_form"]/div[1]/div[1]/div/div/div[2]/a/span';
public static $検索結果_メッセージ = '//*[@id="search_form"]/div[2]/span';

public function __construct(\AcceptanceTester $I)
{
parent::__construct($I);
}

public static function go(\AcceptanceTester $I)
{
$page = new self($I);

return $page->goPage(self::$URL, 'ログイン履歴システム設定');
}

/**
* 指定したログインID/IPアドレスで検索する。
*
* @param string $multi ログインID/IPアドレス
*
* @return $this
*/
public function 検索($multi = '')
{
$this->tester->fillField(self::$検索条件, $multi);
$this->tester->click(self::$検索ボタン);
$this->tester->see('ログイン履歴システム設定', '.c-pageTitle');

return $this;
}

public function 詳細検索_ステータス($value)
{
$this->tester->click(self::$詳細検索ボタン);
$this->tester->wait(1);
$this->tester->checkOption(['id' => 'admin_search_login_history_Status_'.$value]);
$this->tester->click(self::$検索ボタン);
$this->tester->see('ログイン履歴システム設定', '.c-pageTitle');

return $this;
}
}
67 changes: 65 additions & 2 deletions codeception/acceptance/EA08SysteminfoCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Codeception\Util\Fixtures;
use Page\Admin\AuthorityManagePage;
use Page\Admin\LoginHistoryPage;

/**
* @group admin
Expand Down Expand Up @@ -308,6 +309,24 @@ public function systeminfo_セキュリティ管理SSL(\AcceptanceTester $I)
$I->click('#page_admin_setting_system_security form div.c-contentsArea__cols > div.c-conversionArea > div > div > div:nth-child(2) > div > div > button');
}

/**
* GitHub Actions は IPv6で実行されており、アクセス拒否のテストはできない
*/
public function systeminfo_セキュリティ管理IP制限_拒否リスト(\AcceptanceTester $I)
{
$I->wantTo('EA0804-UC01-T05 セキュリティ管理 - IP制限(拒否リスト)');

// 表示
$config = Fixtures::get('config');
$I->amOnPage('/'.$config['eccube_admin_route'].'/setting/system/security');
$I->see('セキュリティ管理システム設定', '#page_admin_setting_system_security .c-pageTitle__titles');

$I->fillField(['id' => 'admin_security_admin_deny_hosts'], '1.1.1.1');
$I->click('#page_admin_setting_system_security form div.c-contentsArea__cols > div.c-conversionArea > div > div > div:nth-child(2) > div > div > button');

$I->see('保存しました', AuthorityManagePage::$完了メッセージ);
}

public function systeminfo_権限管理追加(\AcceptanceTester $I)
{
$I->wantTo('EA0805-UC01-T01 権限管理 - 追加');
Expand Down Expand Up @@ -377,12 +396,56 @@ public function systeminfo_マスターデータ管理(\AcceptanceTester $I)
$I->see('無回答', '#customer_form #admin_customer_sex');
}

public function systeminfo_ログイン履歴検索(\AcceptanceTester $I)
{
$I->wantTo('EA0808-UC01-T01 ログイン履歴 - 検索');

LoginHistoryPage::go($I)->検索('admin');

// 1項目目をチェック
$I->see('admin', '//*[@id="search_form"]/div[4]/div/div/div[2]/div/table/tbody/tr[1]/td[2]');
$I->see('成功', '//*[@id="search_form"]/div[4]/div/div/div[2]/div/table/tbody/tr[1]/td[5]/span');

LoginHistoryPage::go($I)->検索('admin-failure');

$I->see('検索結果:0件が該当しました', LoginHistoryPage::$検索結果_メッセージ);

$I->logoutAsAdmin();

// ログインに失敗する
$I->submitForm('#form1', [
'login_id' => 'admin-failure',
'password' => 'password',
]);

$I->loginAsAdmin();

LoginHistoryPage::go($I)->検索('admin-failure');

// 1項目目をチェック
$I->see('admin-failure', '//*[@id="search_form"]/div[4]/div/div/div[2]/div/table/tbody/tr[1]/td[2]');
$I->see('失敗', '//*[@id="search_form"]/div[4]/div/div/div[2]/div/table/tbody/tr[1]/td[5]/span');


// ステータスで詳細検索

LoginHistoryPage::go($I)->検索();

$I->see('失敗', '//*[@id="search_form"]/div[4]/div/div/div[2]/div/table/tbody');
$I->see('成功', '//*[@id="search_form"]/div[4]/div/div/div[2]/div/table/tbody');

LoginHistoryPage::go($I)->詳細検索_ステータス('0');

$I->see('失敗', '//*[@id="search_form"]/div[4]/div/div/div[2]/div/table/tbody');
$I->dontSee('成功', '//*[@id="search_form"]/div[4]/div/div/div[2]/div/table/tbody');
}

/**
* ATTENTION 後続のテストが失敗するため、最後に実行する必要がある
*/
public function systeminfo_セキュリティ管理IP制限(\AcceptanceTester $I)
public function systeminfo_セキュリティ管理IP制限_許可リスト(\AcceptanceTester $I)
{
$I->wantTo('EA0804-UC01-T03 セキュリティ管理 - IP制限');
$I->wantTo('EA0804-UC01-T03 セキュリティ管理 - IP制限(許可リスト)');

$findPlugins = Fixtures::get('findPlugins');
$Plugins = $findPlugins();
Expand Down
135 changes: 135 additions & 0 deletions src/Eccube/Controller/Admin/Setting/System/LoginHistoryController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php

/*
* This file is part of EC-CUBE
*
* Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
*
* http://www.ec-cube.co.jp/
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Eccube\Controller\Admin\Setting\System;

use Eccube\Controller\AbstractController;
use Eccube\Form\Type\Admin\SearchLoginHistoryType;
use Eccube\Repository\LoginHistoryRepository;
use Eccube\Repository\Master\PageMaxRepository;
use Eccube\Util\FormUtil;
use Knp\Component\Pager\PaginatorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

/**
* Class LoginHistoryController
*/
class LoginHistoryController extends AbstractController
{
/**
* @var PageMaxRepository
*/
protected $pageMaxRepository;

/**
* @var LoginHistoryRepository
*/
protected $loginHistoryRepository;

/**
* LoginHistoryController constructor.
*/
public function __construct(
PageMaxRepository $pageMaxRepository,
LoginHistoryRepository $loginHistoryRepository
) {
$this->pageMaxRepository = $pageMaxRepository;
$this->loginHistoryRepository = $loginHistoryRepository;
}

/**
* ログイン履歴検索画面を表示する.
* 左ナビゲーションの選択はGETで遷移する.
*
* @Route("/%eccube_admin_route%/setting/system/login_history", name="admin_setting_system_login_history")
* @Route("/%eccube_admin_route%/setting/system/login_history/{page_no}", requirements={"page_no" = "\d+"}, name="admin_setting_system_login_history_page")
* @Template("@admin/Setting/System/login_history.twig")
*
* @param integer $page_no
*
* @return \Symfony\Component\HttpFoundation\Response|array
*/
public function index(Request $request, PaginatorInterface $paginator, $page_no = null)
{
$session = $request->getSession();
$pageNo = $page_no;
$pageMaxis = $this->pageMaxRepository->findAll();
$pageCount = $session->get('eccube.admin.login_history.search.page_count', $this->eccubeConfig['eccube_default_page_count']);
$pageCountParam = $request->get('page_count');
if ($pageCountParam && is_numeric($pageCountParam)) {
foreach ($pageMaxis as $pageMax) {
if ($pageCountParam == $pageMax->getName()) {
$pageCount = $pageMax->getName();
$session->set('eccube.admin.login_history.search.page_count', $pageCount);
break;
}
}
}

$pagination = null;
$searchForm = $this->formFactory
->createBuilder(SearchLoginHistoryType::class)
->getForm();

if ('POST' === $request->getMethod()) {
$searchForm->handleRequest($request);
if ($searchForm->isSubmitted() && $searchForm->isValid()) {
$searchData = $searchForm->getData();
$pageNo = 1;
$session->set('eccube.admin.login_history.search', FormUtil::getViewData($searchForm));
$session->set('eccube.admin.login_history.search.page_no', $pageNo);
} else {
return [
'searchForm' => $searchForm->createView(),
'pagination' => [],
'pageMaxis' => $pageMaxis,
'page_no' => $pageNo ? $pageNo : 1,
'page_count' => $pageCount,
'has_errors' => true,
];
}
} else {
if (null !== $pageNo || $request->get('resume')) {
if ($pageNo) {
$session->set('eccube.admin.login_history.search.page_no', (int) $pageNo);
} else {
$pageNo = $session->get('eccube.admin.login_history.search.page_no', 1);
}
$viewData = $session->get('eccube.admin.login_history.search', []);
} else {
$pageNo = 1;
$viewData = FormUtil::getViewData($searchForm);
$session->set('eccube.admin.login_history.search', $viewData);
$session->set('eccube.admin.login_history.search.page_no', $pageNo);
}
$searchData = FormUtil::submitAndGetData($searchForm, $viewData);
}

$qb = $this->loginHistoryRepository->getQueryBuilderBySearchDataForAdmin($searchData);
$pagination = $paginator->paginate(
$qb,
$pageNo,
$pageCount
);

return [
'searchForm' => $searchForm->createView(),
'pagination' => $pagination,
'pageMaxis' => $pageMaxis,
'page_count' => $pageCount,
'has_errors' => false,
];
}
}
Loading