diff --git a/modules/weko-accounts/setup.py b/modules/weko-accounts/setup.py index b3eccbe33d..3079aa2ff3 100644 --- a/modules/weko-accounts/setup.py +++ b/modules/weko-accounts/setup.py @@ -109,6 +109,7 @@ 'weko_accounts_embedded_ds_multi_lang_js = ' 'weko_accounts.bundles:embedded_ds_multi_language_js', 'weko_accounts_suggest_js = weko_accounts.bundles:suggest_js', + 'weko_accounts_shibuser_css = weko_accounts.bundles:shibuser_css', ], }, extras_require=extras_require, diff --git a/modules/weko-accounts/tests/conftest.py b/modules/weko-accounts/tests/conftest.py index 7cbe5af4a0..55303b3e68 100644 --- a/modules/weko-accounts/tests/conftest.py +++ b/modules/weko-accounts/tests/conftest.py @@ -46,6 +46,7 @@ from weko_index_tree.models import Index from weko_records_ui import WekoRecordsUI from weko_redis.redis import RedisConnection +from weko_search_ui import WekoSearchUI from weko_user_profiles import WekoUserProfiles from weko_accounts import WekoAccounts, WekoAccountsREST @@ -102,6 +103,7 @@ def base_app(instance_path): WekoUserProfiles(app_) app_.register_blueprint(blueprint) WekoAccountsREST(app_) + WekoSearchUI(app_) return app_ @@ -189,7 +191,7 @@ def users(app, db): ds.add_role_to_user(generaluser, general_role) ds.add_role_to_user(originalroleuser, originalrole) ds.add_role_to_user(originalroleuser2, originalrole) - ds.add_role_to_user(originalroleuser2, repoadmin_role) + # ds.add_role_to_user(originalroleuser2, repoadmin_role) ds.add_role_to_user(student,studentrole) # Assign access authorization diff --git a/modules/weko-accounts/tests/test_admin.py b/modules/weko-accounts/tests/test_admin.py index 53786a5e5d..79111c1aee 100644 --- a/modules/weko-accounts/tests/test_admin.py +++ b/modules/weko-accounts/tests/test_admin.py @@ -1,7 +1,12 @@ import pytest +import json from mock import patch from flask import current_app,url_for,make_response from invenio_accounts.testutils import login_user_via_session as login +from weko_admin.models import AdminSettings +from invenio_accounts.models import User +from sqlalchemy.orm.session import object_session + class TestShibSettingView: @@ -11,14 +16,14 @@ def test_index_acl_guest(self,client,session_time): assert res.status_code == 302 @pytest.mark.parametrize('user_index, is_can',[ (0,True), - (1,False), + (1,True), (2,False), (3,False), (4,False), (5,False), (6,False), ]) - def test_index_acl(self,client,users,user_index,is_can,mocker): + def test_index_acl(self,client,users,user_index,is_can,mocker, admin_settings): mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) login(client=client,email=users[user_index]["email"]) url = url_for("shibboleth.index",_external=True) @@ -28,43 +33,191 @@ def test_index_acl(self,client,users,user_index,is_can,mocker): else: assert res.status_code == 403 # .tox/c1/bin/pytest --cov=weko_accounts tests/test_admin.py::TestShibSettingView::test_index -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp - def test_index(self,client,users,mocker): - login(client=client,email=users[0]["email"]) - url = url_for("shibboleth.index") - sibuser_html = 'weko_accounts/setting/shibuser.html' - mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) - current_app.config.update( - WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True - ) - # shib_flg = 1 - res = client.post(url,data=dict( - submit="shib_form", - shibbolethRadios="1" - )) - assert current_app.config["WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED"] == True - assert res.status_code==200 - mock_render.assert_called_with(sibuser_html,shib_flg="1") - - mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) - current_app.config.update( - WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=False - ) - # shib_flg = 0 - res = client.post(url,data=dict( - submit="shib_form", - shibbolethRadios="0" - )) - assert res.status_code == 200 - assert current_app.config["WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED"] == False - mock_render.assert_called_with(sibuser_html,shib_flg="0") - - # raise BaseException - with patch("weko_accounts.admin.ShibSettingView.render",side_effect=BaseException): - res = client.post(url) - assert res.status_code == 400 - - # method is GET - mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) - res = client.get(url) - assert res.status_code == 200 - mock_render.assert_called_with(sibuser_html,shib_flg="0") \ No newline at end of file + def test_index(self,app,client,users,mocker, admin_settings, db): + with app.app_context(): + user = User.query.filter_by(email=users[0]['email']).first() + login(client=client, user=user) + url = url_for("shibboleth.index") + sibuser_html = 'weko_accounts/setting/shibuser.html' + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + current_app.config["WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED"] = True + + # モックに渡す変数を設定 + role_list = current_app.config['WEKO_ACCOUNTS_ROLE_LIST'] + attr_list = current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_LIST'] + block_user_list = admin_settings[0].settings['blocked_ePPNs'] + roles = admin_settings[2].settings + set_language = "en" + shib_flg = "1" + attributes = admin_settings[3].settings + + data = { + "submit": "shib_form", + "shibbolethRadios": "1", + "block-eppn-option-list": json.dumps(block_user_list), + "enable-login-user-list": [], + } + + for i, (_, value) in enumerate(roles.items()): + data[f"role-lists{i}"] = value + + for i, (_, value) in enumerate(attributes.items()): + data[f"attr-lists{i}"] = value + + # new_shib_flg = 1 + res = client.post(url, data=data) + + assert admin_settings[1].settings["shib_flg"] is True + assert res.status_code==200 + + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + current_app.config["WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED"] = False + + data["shibbolethRadios"] = "0" + + # new_shib_flg = 0 + res = client.post(url, data=data) + + assert res.status_code == 200 + assert admin_settings[1].settings["shib_flg"] is False + shib_flg = "0" + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + + # デフォルトロールを変更 + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + roles = { + "gakunin_role": "Repository Administrator", + "orthros_outside_role": "None", + "extra_role": "Contributor"} + + for i, (_, value) in enumerate(roles.items()): + data[f"role-lists{i}"] = value + + res = client.post(url, data=data) + + assert res.status_code == 200 + assert admin_settings[2].settings["gakunin_role"] == "Repository Administrator" + assert admin_settings[2].settings["orthros_outside_role"] == "None" + assert admin_settings[2].settings["extra_role"] == "Contributor" + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + + # 属性を変更 + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + attributes = { + "shib_eppn": "eduPersonAffiliation", + "shib_role_authority_name": "eppn", + "shib_mail": "DisplayName", + "shib_user_name": "sn"} + + for i, (_, value) in enumerate(attributes.items()): + data[f"attr-lists{i}"] = value + + res = client.post(url, data=data) + + assert res.status_code == 200 + assert admin_settings[3].settings["shib_eppn"] == "eduPersonAffiliation" + assert admin_settings[3].settings["shib_role_authority_name"] == "eppn" + assert admin_settings[3].settings["shib_mail"] == "DisplayName" + assert admin_settings[3].settings["shib_user_name"] == "sn" + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + + # ブロックユーザーを変更 + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + block_user_list = ['test1','test2','test3'] + data["block-eppn-option-list"] = json.dumps(block_user_list) + + res = client.post(url, data=data) + + assert res.status_code == 200 + assert "test1" in admin_settings[0].settings["blocked_ePPNs"] + + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=str(block_user_list), + enable_login_user_list=[], + **roles, + **attributes + ) + + # raise BaseException + with patch("weko_accounts.admin.ShibSettingView.render",side_effect=BaseException): + res = client.post(url) + assert res.status_code == 400 + + # method is GET + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + res = client.get(url) + assert res.status_code == 200 + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + + @pytest.fixture + def admin_settings(self, db): + settings = list() + settings.append(AdminSettings(id=6,name="blocked_user_settings",settings={"blocked_ePPNs": []})) + settings.append(AdminSettings(id=7,name="shib_login_enable",settings={"shib_flg": False})) + settings.append(AdminSettings(id=8,name="default_role_settings",settings={ + "gakunin_role": "Contributor", + "orthros_outside_role": "Community Administrator", + "extra_role": "None"})) + settings.append(AdminSettings(id=9,name="attribute_mapping",settings={ + "shib_eppn": "eppn", + "shib_role_authority_name": "eduPersonAffiliation", + "shib_mail": "mail", + "shib_user_name": "DisplayName"})) + db.session.add_all(settings) + db.session.commit() + return settings diff --git a/modules/weko-accounts/tests/test_views.py b/modules/weko-accounts/tests/test_views.py index ab532ab6d4..d52d093468 100644 --- a/modules/weko-accounts/tests/test_views.py +++ b/modules/weko-accounts/tests/test_views.py @@ -14,8 +14,11 @@ _has_admin_access, init_menu, _redirect_method, + find_user_by_email shib_sp_login, ) +from weko_admin.models import AdminSettings + def set_session(client,data): with client.session_transaction() as session: for k, v in data.items(): @@ -219,6 +222,90 @@ def test_confirm_user(client,redis_connect,mocker): res = client.post(url,data=form) assert res.status_code == 400 +#def confirm_user_without_page(): +# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_confirm_user_without_page -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp +def test_confirm_user_without_page(client,redis_connect,mocker): + mocker.patch("weko_accounts.views.RedisConnection.connection",return_value=redis_connect) + mocker.patch("weko_accounts.views.ShibUser.shib_user_login") + url = url_for("weko_accounts.confirm_user_without_page") + + # not exist shib_session_id + set_session(client,{"shib_session_id":None}) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("shib_session_id",category="error") + + # not exist cache_key + set_session(client,{"shib_session_id":"2222"}) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("cache_key",category="error") + + set_session(client,{"shib_session_id":"1111"}) + # not exist cache_value + redis_connect.put("Shib-Session-1111",bytes("","utf-8")) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("cache_val",category="error") + assert redis_connect.redis.exists("Shib-Session-1111") is False + + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + with patch("weko_accounts.views.ShibUser.check_weko_user",return_value=True): + # shib_user.bind_relation_info is false + with patch("weko_accounts.views.ShibUser.bind_relation_info",return_value=False): + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("FAILED bind_relation_info!",category="error") + assert redis_connect.redis.exists("Shib-Session-1111") is False + with patch("weko_accounts.views.ShibUser.bind_relation_info",return_value=True): + # ShibUser.check_in is error + with patch("weko_accounts.views.ShibUser.check_in",return_value="test_error"): + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("test_error",category="error") + assert redis_connect.redis.exists("Shib-Session-1111") is False + with patch("weko_accounts.views.ShibUser.check_in",return_value=None): + # ShibUser.shib_user is None,not exist next in session + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) + client.get(url) + mock_redirect.assert_called_with("/") + assert redis_connect.redis.exists("Shib-Session-1111") is False + + # exist ShibUser.shib_user + set_session(client,{"shib_session_id":"1111"}) + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + + shibuser = ShibUser({}) + shibuser.shib_user = "test_user" + with patch("weko_accounts.views.ShibUser",return_value=shibuser): + mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_redirect.assert_called_with("/") + assert redis_connect.redis.exists("Shib-Session-1111") is False + + # exist ShibUser.shib_user + set_session(client,{"shib_session_id":"1111","next":"/next_page"}) + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + + shibuser = ShibUser({}) + shibuser.shib_user = "test_user" + with patch("weko_accounts.views.ShibUser",return_value=shibuser): + mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_redirect.assert_called_with("/next_page") + assert redis_connect.redis.exists("Shib-Session-1111") is False + + # raise BaseException + with patch("weko_accounts.views._redirect_method",side_effect=BaseException("test_error")): + res = client.get(url) + assert res.status_code == 400 + + #def shib_login(): # .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_shib_login -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp def test_shib_login(client,redis_connect,users,mocker): @@ -288,7 +375,42 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users): mock_flash = mocker.patch("weko_accounts.views.flash") client.post(url,data=form) mock_flash.assert_called_with("Missing SHIB_ATTRs!",category="error") - + + # Check if shib_eppn is not included in the blocked user list + try: + db.session.add(AdminSettings( + id=6, + name="blocked_user_settings", + settings='{"blocked_ePPNs": ["ePPN1", "ePPN2", "ePPN3", "ePPN5", "ePPP*"]}' + )) + db.session.commit() + except Exception as e: + db.session.rollback() + raise + finally: + db.session.remove() + + # Match with blocked user + mock_flash = mocker.patch("weko_accounts.views.flash") + form = { + "SHIB_ATTR_SESSION_ID":"1111", + "SHIB_ATTR_EPPN":"ePPN3" + } + client.post(url,data=form) + mock_flash.assert_called_with("Failed to login.",category="error") + mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",return_value=make_response()) + + # Match found with a blocked user from the wildcard + mock_flash = mocker.patch("weko_accounts.views.flash") + form = { + "SHIB_ATTR_SESSION_ID":"1111", + "SHIB_ATTR_EPPN":"ePPP3" + } + client.post(url,data=form) + mock_flash.assert_called_with("Failed to login.",category="error") + mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",return_value=make_response()) + + # Not a blocked user form = { "SHIB_ATTR_SESSION_ID":"1111", "SHIB_ATTR_EPPN":"test_eppn" @@ -322,20 +444,42 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users): and patch("weko_accounts.views.redirect",return_value=make_response()): res = client.post(url,data=form) assert res.status_code == 200 - #assert res.url == "/weko/shib/login?SHIB_ATTR_SESSION_ID=1111&_method=GET" + assert res.data.decode() == "/weko/shib/login?SHIB_ATTR_SESSION_ID=1111&next=%2F" # shib_user.get_relation_info is not None with patch("weko_accounts.views.ShibUser.get_relation_info",return_value="chib_user")\ and patch("weko_accounts.views.redirect",return_value=make_response()): res = client.post(url,data=form) assert res.status_code == 200 - #assert res == "/weko/auto/login?SHIB_ATTR_SESSION_ID=1111&_method=GET" - + assert res.data.decode() == "/weko/auto/login?SHIB_ATTR_SESSION_ID=1111&next=%2F" + + current_app.config.update( + WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True, + WEKO_ACCOUNTS_SKIP_CONFIRMATION_PAGE=True + ) + # shib_user.get_relation_info is None + with patch("weko_accounts.views.ShibUser.get_relation_info",return_value=None): + with patch("weko_accounts.views.find_user_by_email",return_value="shib_user"): + res = client.post(url,data=form) + assert res.status_code == 200 + assert res.data.decode() == "/weko/confim/user/skip?SHIB_ATTR_SESSION_ID=1111&next=%2F" + + with patch("weko_accounts.views.find_user_by_email",return_value=None): + res = client.post(url,data=form) + assert res.status_code == 200 + assert res.data.decode() == "/weko/auto/login?SHIB_ATTR_SESSION_ID=1111&next=%2F" + + # shib_user.get_relation_info is not None + with patch("weko_accounts.views.ShibUser.get_relation_info",return_value="shib_user"): + res = client.post(url,data=form) + assert res.status_code == 200 + assert res.data.decode() == "/weko/auto/login?SHIB_ATTR_SESSION_ID=1111&next=%2F" + # raise BaseException with patch("weko_accounts.views.flash",side_effect=BaseException("test_error"))\ and patch("weko_accounts.views._redirect_method",return_value=make_response()) as mock_redirect_: res = client.post(url,data={}) mock_redirect_.assert_called_once() - + # all attributes have value and some shibboleth_user records don't have target eppn current_app.config.update( WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True, @@ -513,6 +657,7 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users): assert res.status_code == 302 assert res.headers['Location'] == 'http://{}/login/'.format(current_app.config["SERVER_NAME"]) + #def shib_stub_login(): # .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_shib_stub_login -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp def test_shib_stub_login(client,mocker): @@ -543,4 +688,16 @@ def test_shib_stub_login(client,mocker): def test_shib_logout(client, mocker): mocker.patch("weko_accounts.views.ShibUser.shib_user_logout") res = client.get(url_for("weko_accounts.shib_logout")) - assert res.data == bytes("logout success","utf-8") \ No newline at end of file + assert res.data == bytes("logout success","utf-8") + +# def find_user_by_email(shib_attributes): +# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_find_user_by_email -vv -s --cov-branch --cov-report=term --cov-report=html --basetemp=/code/modules/weko-workflow/.tox/c1/tmp +def test_find_user_by_email(app, users): + + with app.test_request_context(): + user = find_user_by_email({"shib_mail": users[0].get("email")}) + assert user.email == users[0].get("email") + assert user.id == users[0].get("id") + + user = find_user_by_email({"shib_mail": "invalid.email@nii.ac.jp"}) + assert user is None \ No newline at end of file diff --git a/modules/weko-accounts/weko_accounts/admin.py b/modules/weko-accounts/weko_accounts/admin.py index a10c25fa68..8cf816ca04 100644 --- a/modules/weko-accounts/weko_accounts/admin.py +++ b/modules/weko-accounts/weko_accounts/admin.py @@ -21,14 +21,17 @@ """WEKO3 module docstring.""" import sys +import json from flask import abort, current_app, flash, request from flask_admin import BaseView, expose from flask_babelex import gettext as _ from werkzeug.local import LocalProxy -_app = LocalProxy(lambda: current_app.extensions['weko-admin'].app) +from weko_admin.models import AdminSettings, db +from weko_accounts.models import ShibbolethUser, db +_app = LocalProxy(lambda: current_app.extensions['weko-admin'].app) class ShibSettingView(BaseView): """ShibSettingView.""" @@ -37,31 +40,97 @@ class ShibSettingView(BaseView): def index(self): """Index.""" try: - shib_flg = '0' - if current_app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED']: - shib_flg = '1' + # Shibbolethログイン可否 + shib_login_enable = AdminSettings.get('shib_login_enable', dict_to_object=False) + shib_flg = '0' if not shib_login_enable.get('shib_flg', current_app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED']) else '1' + + role_list = current_app.config['WEKO_ACCOUNTS_ROLE_LIST'] + attr_list = current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_LIST'] + set_language = _('language') + + block_user_settings = AdminSettings.get('blocked_user_settings') + block_user_list = block_user_settings.__dict__['blocked_ePPNs'] + + shib_eppns = db.session.query(ShibbolethUser.shib_eppn).all() + enable_login_user_list = [shib_eppn[0] for shib_eppn in shib_eppns] + + # デフォルトロール + default_roles = AdminSettings.get('default_role_settings', dict_to_object=False) + roles = { + 'gakunin_role': default_roles.get('gakunin_role', current_app.config['WEKO_ACCOUNTS_GAKUNIN_ROLE']['defaultRole']), + 'orthros_outside_role': default_roles.get('orthros_outside_role', current_app.config['WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE']['defaultRole']), + 'extra_role': default_roles.get('extra_role', current_app.config['WEKO_ACCOUNTS_EXTRA_ROLE']['defaultRole']) + } + # 属性マッピング + attribute_mappings = AdminSettings.get('attribute_mapping', dict_to_object=False) + attributes = { + 'shib_eppn': attribute_mappings.get('shib_eppn', current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP']['shib_eppn']), + 'shib_role_authority_name': attribute_mappings.get('shib_role_authority_name', current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP']['shib_role_authority_name']), + 'shib_mail': attribute_mappings.get('shib_mail', current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP']['shib_mail']), + 'shib_user_name': attribute_mappings.get('shib_user_name', current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP']['shib_user_name']) + } + + # ブロックユーザー + block_user_settings = AdminSettings.get('blocked_user_settings', dict_to_object=False) + block_user_list = block_user_settings.get('blocked_ePPNs', []) + if request.method == 'POST': # Process forms form = request.form.get('submit', None) + new_shib_flg = request.form.get('shibbolethRadios', '0') + new_roles = {key: request.form.get(f'role-lists{i}', []) for i, key in enumerate(roles)} + new_attributes = {key: request.form.get(f'attr-lists{i}', []) for i, key in enumerate(attributes)} + new_block_user_list = request.form.get('block-eppn-option-list', "[]") + if form == 'shib_form': - shib_flg = request.form.get('shibbolethRadios', '0') - if shib_flg == '1': - _app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED'] = True - else: - _app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED'] = False - flash( - _('Shibboleth flag was updated.'), - category='success') + if shib_flg != new_shib_flg: + shib_flg = new_shib_flg + AdminSettings.update('shib_login_enable', {"shib_flg": (shib_flg == '1')}) + flash(_('Shibboleth flag was updated.'), category='success') + + # デフォルトロールの更新 + for key in roles: + if roles[key] != new_roles[key]: + roles[key] = new_roles[key] + flash(_(f'{key.replace("_", " ").title()} was updated.'), category='success') + AdminSettings.update('default_role_settings', roles) + + # 属性マッピングの更新 + for key in attributes: + if attributes[key] != new_attributes[key]: + attributes[key] = new_attributes[key] + flash(_(f'{key.replace("_", " ").title()} mapping was updated.'), category='success') + AdminSettings.update('attribute_mapping', attributes) + + # ブロックユーザーの更新 + if block_user_list != json.loads(new_block_user_list): + new_eppn_list = json.loads(new_block_user_list) + new_eppn_list.sort() + updateSettings = {'blocked_ePPNs': new_eppn_list} + AdminSettings.update('blocked_user_settings', updateSettings) + flash( + _('Blocked user list was updated.'), + category='success') + block_user_list = str(new_eppn_list).replace('"', '\\"') + + self.get_latest_current_app() return self.render( current_app.config['WEKO_ACCOUNTS_SET_SHIB_TEMPLATE'], - shib_flg=shib_flg) + shib_flg=shib_flg, set_language=set_language, role_list=role_list, attr_list=attr_list, block_user_list=block_user_list, enable_login_user_list=enable_login_user_list, **roles, **attributes ) except BaseException: current_app.logger.error( 'Unexpected error: {}'.format(sys.exc_info())) return abort(400) - + + def get_latest_current_app(self): + _app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED'] = AdminSettings.get('shib_login_enable', dict_to_object=False)['shib_flg'] + default_roles = AdminSettings.get('default_role_settings', dict_to_object=False) + _app.config['WEKO_ACCOUNTS_GAKUNIN_ROLE']['defaultRole'] = default_roles['gakunin_role'] + _app.config['WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE']['defaultRole'] = default_roles['orthros_outside_role'] + _app.config['WEKO_ACCOUNTS_EXTRA_ROLE']['defaultRole'] = default_roles['extra_role'] + _app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP'] = AdminSettings.get('attribute_mapping', dict_to_object=False) shib_adminview = { 'view_class': ShibSettingView, diff --git a/modules/weko-accounts/weko_accounts/bundles.py b/modules/weko-accounts/weko_accounts/bundles.py index 07602ae331..71dac40986 100644 --- a/modules/weko-accounts/weko_accounts/bundles.py +++ b/modules/weko-accounts/weko_accounts/bundles.py @@ -42,3 +42,9 @@ 'js/weko_accounts/suggest.js', output="gen/weko_accounts_suggest.js", ) + +shibuser_css = Bundle( + 'css/weko_accounts/styles.bundle.css', + filters='cleancss', + output='gen/weko_accounts_styles.%(version)s.css', +) \ No newline at end of file diff --git a/modules/weko-accounts/weko_accounts/config.py b/modules/weko-accounts/weko_accounts/config.py index ce7c0b5537..1d79b1cc9b 100644 --- a/modules/weko-accounts/weko_accounts/config.py +++ b/modules/weko-accounts/weko_accounts/config.py @@ -63,28 +63,91 @@ WEKO_ACCOUNTS_SSO_ATTRIBUTE_MAP = { 'SHIB_ATTR_EPPN': (False, 'shib_eppn'), - # "SHIB_ATTR_LOGIN_ID": (False, 'shib_uid'), - # "SHIB_ATTR_HANDLE": (False, 'shib_handle'), + # 'SHIB_ATTR_LOGIN_ID': (False, 'shib_uid'), + # 'SHIB_ATTR_HANDLE': (False, 'shib_handle'), 'SHIB_ATTR_ROLE_AUTHORITY_NAME': (False, 'shib_role_authority_name'), - # "SHIB_ATTR_PAGE_NAME": (False, 'shib_page_name'), - # "SHIB_ATTR_ACTIVE_FLAG": (False, 'shib_active_flag'), + # 'SHIB_ATTR_PAGE_NAME': (False, 'shib_page_name'), + # 'SHIB_ATTR_ACTIVE_FLAG': (False, 'shib_active_flag'), 'SHIB_ATTR_SITE_USER_WITHIN_IP_RANGE_FLAG': (False, 'shib_ip_range_flag'), 'SHIB_ATTR_MAIL': (False, 'shib_mail'), 'SHIB_ATTR_USER_NAME': (False, 'shib_user_name'), } """IdP attribute map.""" -WEKO_ACCOUNTS_SHIB_ROLE_RELATION = { - '管理者': 'System Administrator', - '図書館員': 'Repository Administrator', - '教員': 'Contributor', - '教官': 'Contributor' +WEKO_ACCOUNTS_ATTRIBUTE_MAP = { + 'shib_eppn': 'eppn', + 'shib_role_authority_name': 'eduPersonAffiliation', + 'shib_mail': 'mail', + 'shib_user_name': 'DisplayName' } -"""Role relation.""" +"""IdP attribute map.""" + +WEKO_ACCOUNTS_ATTRIBUTE_LIST = [ + 'eppn', + 'DisplayName', + 'mail', + 'eduPersonOrcid', + 'jasn', + 'jaGivenName', + 'jaDisplayName', + 'jao', + 'jaou', + 'isMemberOf', + 'sn', + 'o', + 'ou', + 'givenName', + 'eduPersonAffiliation', + 'eduPersonScopedAffiliation', + 'eduPersonTargetedID' +] +"""Attribute List.""" + +WEKO_ACCOUNTS_ROLE_LIST = [ + 'System Administrator', + 'Repository Administrator', + 'Community Administrator', + 'Contributor', + 'None' +] +"""Role List.""" WEKO_ACCOUNTS_GENERAL_ROLE = 'Contributor' """Default role.""" +WEKO_ACCOUNTS_GAKUNIN_ROLE = { + 'defaultRole': 'Contributor', + 'organizationName': [] +} +"""Gakunin Default role.""" + +WEKO_ACCOUNTS_ORTHROS_INSIDE_ROLE = { + 'defaultRole': 'Repository Administrator', + 'organizationName': [] +} +"""Orthros (Inside) Default role.""" + +WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE = { + 'defaultRole': 'Community Administrator', + 'organizationName': [] +} +"""Orthros (Outside) Default role.""" + +WEKO_ACCOUNTS_EXTRA_ROLE = { + 'defaultRole': 'None', # ロール無 + 'organizationName': [] +} +"""Extra Default role.""" + +WEKO_ACCOUNTS_SHIB_ROLE_RELATION = { + '管理者': 'System Administrator', + '学認IdP': WEKO_ACCOUNTS_GAKUNIN_ROLE['defaultRole'], + '機関内のOrthros': WEKO_ACCOUNTS_ORTHROS_INSIDE_ROLE['defaultRole'], + '機関外のOrthros': WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE['defaultRole'], + 'その他': WEKO_ACCOUNTS_EXTRA_ROLE['defaultRole'] +} +"""Role relation.""" + WEKO_ACCOUNTS_SHIB_IDP_LOGIN_ENABLED = True """Shibboleth login pattern.""" @@ -96,17 +159,17 @@ WEKO_ACCOUNTS_SHIB_ALLOW_USERNAME_INST_EPPN = True """Allow using SHIB_ATTR_USER_NAME instead of SHIB_ATTR_EPPN.""" -WEKO_ACCOUNTS_LOGIN_LABEL = "Log in to account" +WEKO_ACCOUNTS_LOGIN_LABEL = 'Log in to account' """The login label""" -WEKO_ACCOUNTS_REGISTER_LABEL = "Sign up for a %(sitename)s account!" +WEKO_ACCOUNTS_REGISTER_LABEL = 'Sign up for a %(sitename)s account!' """The register label""" WEKO_ACCOUNTS_REAL_IP = None # X-Real-IP > X-Forwarded-For[0] > remote_addr -# WEKO_ACCOUNTS_REAL_IP = "remote_add" # remote_addr -# WEKO_ACCOUNTS_REAL_IP = "x_real_ip" # X-Real-IP > remote_addr -# WEKO_ACCOUNTS_REAL_IP = "x_forwarded_for" # X-Forwarded-For[first] > remote_addr -# WEKO_ACCOUNTS_REAL_IP = "x_forwarded_for_rev" # X-Forwarded-For[last] > remote_addr +# WEKO_ACCOUNTS_REAL_IP = 'remote_add' # remote_addr +# WEKO_ACCOUNTS_REAL_IP = 'x_real_ip' # X-Real-IP > remote_addr +# WEKO_ACCOUNTS_REAL_IP = 'x_forwarded_for' # X-Forwarded-For[first] > remote_addr +# WEKO_ACCOUNTS_REAL_IP = 'x_forwarded_for_rev' # X-Forwarded-For[last] > remote_addr WEKO_ACCOUNTS_REST_ENDPOINTS = { 'login': { @@ -121,9 +184,13 @@ WEKO_ACCOUNTS_API_LIMIT_RATE_DEFAULT = ['100 per minute'] -WEKO_API_LIMIT_RATE_DEFAULT = ["100 per minute"] +WEKO_API_LIMIT_RATE_DEFAULT = ['100 per minute'] """Default rate limit per endpoint for one user in the WEKO API.""" + +WEKO_ACCOUNTS_SKIP_CONFIRMATION_PAGE = False +"""Skip shibboleth confirmation page.""" + WEKO_ACCOUNTS_IDP_ENTITY_ID = '' """IdP entity ID that institution owned.""" @@ -152,4 +219,4 @@ """閲覧権限のデフォルト権限を設定する""" WEKO_INNDEXTREE_GAKUNIN_GROUP_DEFAULT_CONTRIBUTE_PERMISSION = False -"""投稿権限のデフォルト権限を設定する""" \ No newline at end of file +"""投稿権限のデフォルト権限を設定する""" diff --git a/modules/weko-accounts/weko_accounts/ext.py b/modules/weko-accounts/weko_accounts/ext.py index 088ae11ae6..5327a4f4a0 100644 --- a/modules/weko-accounts/weko_accounts/ext.py +++ b/modules/weko-accounts/weko_accounts/ext.py @@ -22,6 +22,7 @@ from flask_babelex import gettext as _ from flask_login import user_logged_in, user_logged_out +from weko_admin.models import AdminSettings, db from . import config diff --git a/modules/weko-accounts/weko_accounts/static/css/weko_accounts/styles.bundle.css b/modules/weko-accounts/weko_accounts/static/css/weko_accounts/styles.bundle.css index 672e0dcb0a..721309540b 100644 --- a/modules/weko-accounts/weko_accounts/static/css/weko_accounts/styles.bundle.css +++ b/modules/weko-accounts/weko_accounts/static/css/weko_accounts/styles.bundle.css @@ -1,3 +1,282 @@ -.Mymodal{position:fixed;z-index:1000;padding-top:25px;left:25%;top:20%;width:50%; - /* height: 40%; */overflow:auto - /* background-color: rgb(117, 116, 116); */}.node-menu{position:relative;width:150px}.node-menu .node-menu-content{width:100%;padding:5px;position:absolute;border:1px solid #bdbdbd;border-radius:5%;-webkit-box-shadow:0 0 5px #bdbdbd;box-shadow:0 0 5px #bdbdbd;background-color:#eee;color:#212121;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;z-index:999}.node-menu .node-menu-content li.node-menu-item{list-style:none;padding:3px}.node-menu .node-menu-content .node-menu-item:hover{border-radius:5%;opacity:unset;cursor:pointer;background-color:#bdbdbd;-webkit-transition:background-color .2s ease-out;transition:background-color .2s ease-out}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon{display:inline-block;width:16px}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon.new-tag:before{content:"\25CF"}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon.new-folder:before{content:"\25B6"}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon.rename:before{content:"\270E"}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon.remove:before{content:"\2716"}.node-menu .node-menu-content .node-menu-item .node-menu-item-value{margin-left:5px}tree-internal ul{padding:3px 0 3px 25px}tree-internal li{padding:0;margin:0;list-style:none}tree-internal .over-drop-target{border:4px solid #757575;-webkit-transition:padding .2s ease-out;transition:padding .2s ease-out;padding:5px;border-radius:5%}tree-internal .tree{-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica Neue,Helvetica,Arial,sans-serif}tree-internal .tree li{list-style:none;cursor:default}tree-internal .tree li div{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box}tree-internal .tree .node-value{display:inline-block;color:#212121}tree-internal .tree .node-value:after{display:block;padding-top:-3px;width:0;height:2px;background-color:#212121;content:"";-webkit-transition:width .3s;transition:width .3s}tree-internal .tree .node-value:hover:after{width:100%}tree-internal .tree .node-left-menu{display:inline-block;height:100%;width:auto}tree-internal .tree .node-left-menu span:before{content:"\2026";color:#757575}tree-internal .tree .node-selected:after{width:100%}tree-internal .tree .folding{width:25px;display:inline-block;line-height:1px;padding:0 5px;font-weight:700}tree-internal .tree .folding.node-collapsed{cursor:pointer}tree-internal .tree .folding.node-collapsed:before{content:"\25B6";color:#757575}tree-internal .tree .folding.node-expanded{cursor:pointer}tree-internal .tree .folding.node-expanded:before{content:"\25BC";color:#757575}tree-internal .tree .folding.node-empty{color:#212121;text-align:center;font-size:.89em}tree-internal .tree .folding.node-empty:before{content:"\25B6";color:#757575}tree-internal .tree .folding.node-leaf{color:#212121;text-align:center;font-size:.89em}tree-internal .tree .folding.node-leaf:before{content:"\25CF";color:#757575}tree-internal ul.rootless{padding:0}tree-internal div.rootless{display:none!important}tree-internal .loading-children:after{content:" loading ...";color:#6a1b9a;font-style:italic;font-size:.9em;-webkit-animation-name:loading-children;animation-name:loading-children;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}@-webkit-keyframes loading-children{0%{color:#f3e5f5}12.5%{color:#e1bee7}25%{color:#ce93d8}37.5%{color:#ba68c8}50%{color:#ab47bc}62.5%{color:#9c27b0}75%{color:#8e24aa}87.5%{color:#7b1fa2}to{color:#6a1b9a}}@keyframes loading-children{0%{color:#f3e5f5}12.5%{color:#e1bee7}25%{color:#ce93d8}37.5%{color:#ba68c8}50%{color:#ab47bc}62.5%{color:#9c27b0}75%{color:#8e24aa}87.5%{color:#7b1fa2}to{color:#6a1b9a}} \ No newline at end of file +.Mymodal { + position: fixed; + z-index: 1000; + padding-top: 25px; + left: 25%; + top: 20%; + width: 50%; + /* height: 40%; */ + overflow: auto; + /* background-color: rgb(117, 116, 116); */ +} +.node-menu { + position: relative; + width: 150px; +} +.node-menu .node-menu-content { + width: 100%; + padding: 5px; + position: absolute; + border: 1px solid #bdbdbd; + border-radius: 5%; + -webkit-box-shadow: 0 0 5px #bdbdbd; + box-shadow: 0 0 5px #bdbdbd; + background-color: #eee; + color: #212121; + font-family: Helvetica Neue, Helvetica, Arial, sans-serif; + z-index: 999; +} +.node-menu .node-menu-content li.node-menu-item { + list-style: none; + padding: 3px; +} +.node-menu .node-menu-content .node-menu-item:hover { + border-radius: 5%; + opacity: unset; + cursor: pointer; + background-color: #bdbdbd; + -webkit-transition: background-color 0.2s ease-out; + transition: background-color 0.2s ease-out; +} +.node-menu .node-menu-content .node-menu-item .node-menu-item-icon { + display: inline-block; + width: 16px; +} +.node-menu + .node-menu-content + .node-menu-item + .node-menu-item-icon.new-tag:before { + content: "\25CF"; +} +.node-menu + .node-menu-content + .node-menu-item + .node-menu-item-icon.new-folder:before { + content: "\25B6"; +} +.node-menu + .node-menu-content + .node-menu-item + .node-menu-item-icon.rename:before { + content: "\270E"; +} +.node-menu + .node-menu-content + .node-menu-item + .node-menu-item-icon.remove:before { + content: "\2716"; +} +.node-menu .node-menu-content .node-menu-item .node-menu-item-value { + margin-left: 5px; +} +tree-internal ul { + padding: 3px 0 3px 25px; +} +tree-internal li { + padding: 0; + margin: 0; + list-style: none; +} +tree-internal .over-drop-target { + border: 4px solid #757575; + -webkit-transition: padding 0.2s ease-out; + transition: padding 0.2s ease-out; + padding: 5px; + border-radius: 5%; +} +tree-internal .tree { + -webkit-box-sizing: border-box; + box-sizing: border-box; + font-family: Helvetica Neue, Helvetica, Arial, sans-serif; +} +tree-internal .tree li { + list-style: none; + cursor: default; +} +tree-internal .tree li div { + display: inline-block; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +tree-internal .tree .node-value { + display: inline-block; + color: #212121; +} +tree-internal .tree .node-value:after { + display: block; + padding-top: -3px; + width: 0; + height: 2px; + background-color: #212121; + content: ""; + -webkit-transition: width 0.3s; + transition: width 0.3s; +} +tree-internal .tree .node-value:hover:after { + width: 100%; +} +tree-internal .tree .node-left-menu { + display: inline-block; + height: 100%; + width: auto; +} +tree-internal .tree .node-left-menu span:before { + content: "\2026"; + color: #757575; +} +tree-internal .tree .node-selected:after { + width: 100%; +} +tree-internal .tree .folding { + width: 25px; + display: inline-block; + line-height: 1px; + padding: 0 5px; + font-weight: 700; +} +tree-internal .tree .folding.node-collapsed { + cursor: pointer; +} +tree-internal .tree .folding.node-collapsed:before { + content: "\25B6"; + color: #757575; +} +tree-internal .tree .folding.node-expanded { + cursor: pointer; +} +tree-internal .tree .folding.node-expanded:before { + content: "\25BC"; + color: #757575; +} +tree-internal .tree .folding.node-empty { + color: #212121; + text-align: center; + font-size: 0.89em; +} +tree-internal .tree .folding.node-empty:before { + content: "\25B6"; + color: #757575; +} +tree-internal .tree .folding.node-leaf { + color: #212121; + text-align: center; + font-size: 0.89em; +} +tree-internal .tree .folding.node-leaf:before { + content: "\25CF"; + color: #757575; +} +tree-internal ul.rootless { + padding: 0; +} +tree-internal div.rootless { + display: none !important; +} +tree-internal .loading-children:after { + content: " loading ..."; + color: #6a1b9a; + font-style: italic; + font-size: 0.9em; + -webkit-animation-name: loading-children; + animation-name: loading-children; + -webkit-animation-duration: 2s; + animation-duration: 2s; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; +} +@-webkit-keyframes loading-children { + 0% { + color: #f3e5f5; + } + 12.5% { + color: #e1bee7; + } + 25% { + color: #ce93d8; + } + 37.5% { + color: #ba68c8; + } + 50% { + color: #ab47bc; + } + 62.5% { + color: #9c27b0; + } + 75% { + color: #8e24aa; + } + 87.5% { + color: #7b1fa2; + } + to { + color: #6a1b9a; + } +} +@keyframes loading-children { + 0% { + color: #f3e5f5; + } + 12.5% { + color: #e1bee7; + } + 25% { + color: #ce93d8; + } + 37.5% { + color: #ba68c8; + } + 50% { + color: #ab47bc; + } + 62.5% { + color: #9c27b0; + } + 75% { + color: #8e24aa; + } + 87.5% { + color: #7b1fa2; + } + to { + color: #6a1b9a; + } +} + +#default-role-list, +#attr-list, +#block-user-setting { + margin-bottom: 2rem; +} + +.select-list { + display: inline-block; + width: 20%; + margin-bottom: 3px; +} + +#block_eppn { + width: 75%; +} + +.block-user-area { + margin-left: 20%; +} + +#block-user-list { + display: inline-block; + width: 80%; + margin-left: 20%; +} + +#btn_delete_block_eppn { + margin-bottom: 2rem; + margin-left: 2px; +} + +#shib-save-button { + text-align: right; + width: 100%; +} diff --git a/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds.js b/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds.js index b4666e18fd..11385b0575 100644 --- a/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds.js +++ b/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds.js @@ -25,7 +25,13 @@ var wayf_hide_idps; var wayf_unhide_idps; var wayf_show_remember_checkbox; var wayf_force_remember_for_session; -var wayf_additional_idps; +var wayf_additional_idps = [ + { + "entityID": "https://core.orthros.gakunin.nii.ac.jp/idp", + "name": "Orthros", + "search": ["https://core.orthros.gakunin.nii.ac.jp/idp", "Orthros"] + }, +]; var wayf_discofeed_url; var wayf_sp_cookie_path; var wayf_list_height; diff --git a/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds_2.js b/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds_2.js index 21527e2721..93a0dcefdd 100644 --- a/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds_2.js +++ b/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds_2.js @@ -25,7 +25,13 @@ var wayf_hide_idps; var wayf_unhide_idps; var wayf_show_remember_checkbox = false; var wayf_force_remember_for_session; -var wayf_additional_idps; +var wayf_additional_idps = [ + { + "entityID": "https://core.orthros.gakunin.nii.ac.jp/idp", + "name": "Orthros", + "search": ["https://core.orthros.gakunin.nii.ac.jp/idp", "Orthros"] + }, +]; var wayf_discofeed_url; var wayf_sp_cookie_path; var wayf_list_height; diff --git a/modules/weko-accounts/weko_accounts/static/js/weko_accounts/shibuser.js b/modules/weko-accounts/weko_accounts/static/js/weko_accounts/shibuser.js new file mode 100644 index 0000000000..170efa96ed --- /dev/null +++ b/modules/weko-accounts/weko_accounts/static/js/weko_accounts/shibuser.js @@ -0,0 +1,203 @@ +const setLanguage = $("#shib_form").data("value"); + +$(document).ready(function () { + // 各設定欄を生成 + createDefaultRoleSettingArea(); + createAttrMapSettingArea(); + createBlockUserSettingArea(); +}); + +/** + * 既定のロール設定欄を生成 + */ +function createDefaultRoleSettingArea() { + const defaultRoleList = $("#default-role-list").data("value"); + + const roleElements = [ + { id: "#gakunin-role-list", index: 0 }, + { id: "#orthros-role-list", index: 1 }, + { id: "#extra-role-list", index: 2 }, + ]; + + roleElements.forEach((roleElement) => { + const roleList = $(roleElement.id); + const roleValue = roleList.data("value"); + roleList.append( + createSelectList( + roleElement.index, + "role-lists", + roleValue, + defaultRoleList + ) + ); + }); +} + +/** + * 属性マッピング設定欄を生成 + */ +function createAttrMapSettingArea() { + const attrList = $("#attr-list").data("value"); + + const attrElements = [ + { id: "#eppn-attr-list", index: 0 }, + { id: "#role-authority-attr-list", index: 1 }, + { id: "#mail-attr-list", index: 2 }, + { id: "#user-attr-list", index: 3 }, + ]; + + attrElements.forEach((attrElement) => { + const attrListElement = $(attrElement.id); + const attrValue = attrListElement.data("value"); + attrListElement.append( + createSelectList(attrElement.index, "attr-lists", attrValue, attrList) + ); + }); +} + +/** + * ブロックユーザー設定欄を生成 + */ +function createBlockUserSettingArea() { + const userePPNList = $("#block-user-list"); + const blockEPPNList = userePPNList.data("value"); + const ePPNList = createSelectList( + "", + "block-user-lists", + blockEPPNList, + blockEPPNList + ); + const deleteButton = $("#btn_delete_block_eppn"); + userePPNList.append(ePPNList); + userePPNList.append(deleteButton); + updateBlockUserList(); +} + +/** + * select要素を生成 + * @param {number} id インデックス + * @param {string} kinds リストの種類 + * @param {string} value 選択中の値 + * @param {Array} defaultList 選択肢作成に使用するデフォルトリスト + * @returns + */ +function createSelectList(id, kinds, value, defaultList) { + const select = $(" @@ -47,6 +54,7 @@ {%- else %} +
{%- endif %} -
-
- -
-
+
+ +
+ +
+ {{_("Gakunin IdP")}} +
+
+ {{_("Outside Orthros")}} +
+
+ {{_("Extra IdP")}} +
+
+ + +
+ +
+ {{_("shib_eppn")}} +
+
+ {{_("shib_role_authority_name")}} +
+
+ {{_("shib_mail")}} +
+
+ {{_("shib_user_name")}} +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+ +
-{%- endblock body %} + +{%- endblock body %} \ No newline at end of file diff --git a/modules/weko-accounts/weko_accounts/translations/en/LC_MESSAGES/messages.mo b/modules/weko-accounts/weko_accounts/translations/en/LC_MESSAGES/messages.mo index bdb1aca392..22a9413799 100644 Binary files a/modules/weko-accounts/weko_accounts/translations/en/LC_MESSAGES/messages.mo and b/modules/weko-accounts/weko_accounts/translations/en/LC_MESSAGES/messages.mo differ diff --git a/modules/weko-accounts/weko_accounts/translations/en/LC_MESSAGES/messages.po b/modules/weko-accounts/weko_accounts/translations/en/LC_MESSAGES/messages.po index 563d48154f..87e103b58a 100644 --- a/modules/weko-accounts/weko_accounts/translations/en/LC_MESSAGES/messages.po +++ b/modules/weko-accounts/weko_accounts/translations/en/LC_MESSAGES/messages.po @@ -19,10 +19,46 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.5.1\n" -#: weko_accounts/admin.py:56 +#: weko_accounts/admin.py:46 +msgid "language" +msgstr "en" + +#: weko_accounts/admin.py: msgid "Shibboleth flag was updated." msgstr "Updated Shibboleth settings" +#: weko_accounts/admin.py: +msgid "Gakunin Role was updated." +msgstr "" + +#: weko_accounts/admin.py: +msgid "Orthros Outside Role was updated." +msgstr "Orthros Role was updated." + +#: weko_accounts/admin.py: +msgid "Extra Role was updated." +msgstr "" + +#: weko_accounts/admin.py: +msgid "Shib Eppn mapping was updated." +msgstr "Shibboleth Eppn mapping was updated." + +#: weko_accounts/admin.py: +msgid "Shib Role Authority Name mapping was updated." +msgstr "Shibboleth Role Authority Name mapping was updated." + +#: weko_accounts/admin.py: +msgid "Shib Mail mapping was updated." +msgstr "Shibboleth Mail mapping was updated." + +#: weko_accounts/admin.py: +msgid "Shib User Name mapping was updated." +msgstr "Shibboleth User Name mapping was updated." + +#: weko_accounts/admin.py: +msgid "Blocked user list was updated." +msgstr "Updated User Login Block settings" + #: weko_accounts/admin.py:69 msgid "Setting" msgstr "" @@ -101,8 +137,48 @@ msgstr "Enable Shibboleth Authentication" msgid "Deny shibboleth user" msgstr "Disable Shibboleth Authentication" -#: weko_accounts/templates/weko_accounts/setting/shibuser.html:68 -msgid "Update" +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:71 +msgid "Default Role" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:73 +msgid "Gakunin IdP" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:76 +msgid "Outside Orthros" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:79 +msgid "Extra IdP" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:84 +msgid "Attribute Mapping" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:86 +msgid "shib_eppn" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:89 +msgid "shib_role_authority_name" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:92 +msgid "shib_mail" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:95 +msgid "shib_user_name" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:100 +msgid "Block User" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:120 +msgid "Save" msgstr "" #~ msgid "%(module_name)s" @@ -126,7 +202,7 @@ msgstr "" #: weko_accounts/templates/weko_accounts/login_shibuser_pattern_1.html msgid "Users who belongs to institution outside Japan, please choose your institution's location." -msgstr "Users who belongs to institution outside Japan, please choose your institution\'s location." +msgstr "Users who belongs to institution outside Japan, please choose your institution's location." #: weko_accounts/templates/weko_accounts/login_shibuser_pattern_2.html msgid "Select Institution" diff --git a/modules/weko-accounts/weko_accounts/translations/ja/LC_MESSAGES/messages.mo b/modules/weko-accounts/weko_accounts/translations/ja/LC_MESSAGES/messages.mo index f9aa653709..e64e7358c7 100644 Binary files a/modules/weko-accounts/weko_accounts/translations/ja/LC_MESSAGES/messages.mo and b/modules/weko-accounts/weko_accounts/translations/ja/LC_MESSAGES/messages.mo differ diff --git a/modules/weko-accounts/weko_accounts/translations/ja/LC_MESSAGES/messages.po b/modules/weko-accounts/weko_accounts/translations/ja/LC_MESSAGES/messages.po index 379fde11ae..a6173c499f 100644 --- a/modules/weko-accounts/weko_accounts/translations/ja/LC_MESSAGES/messages.po +++ b/modules/weko-accounts/weko_accounts/translations/ja/LC_MESSAGES/messages.po @@ -19,10 +19,46 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.5.1\n" -#: weko_accounts/admin.py:56 +#: weko_accounts/admin.py:46 +msgid "language" +msgstr "ja" + +#: weko_accounts/admin.py: msgid "Shibboleth flag was updated." msgstr "Shibboleth設定を更新しました" +#: weko_accounts/admin.py: +msgid "Shib User Name mapping was updated." +msgstr "属性マッピング設定(shib_user_name)を更新しました" + +#: weko_accounts/admin.py: +msgid "Gakunin Role was updated." +msgstr "学認IdP のロール設定を更新しました" + +#: weko_accounts/admin.py: +msgid "Orthros Outside Role was updated." +msgstr "機関外のOrthros のロール設定を更新しました" + +#: weko_accounts/admin.py: +msgid "Extra Role was updated." +msgstr "その他 のロール設定を更新しました" + +#: weko_accounts/admin.py: +msgid "Shib Eppn mapping was updated." +msgstr "属性マッピング設定(shib_eppn)を更新しました" + +#: weko_accounts/admin.py: +msgid "Shib Role Authority Name mapping was updated." +msgstr "属性マッピング設定(shib_role_authority_name)を更新しました" + +#: weko_accounts/admin.py: +msgid "Shib Mail mapping was updated." +msgstr "属性マッピング設定(shib_mail)を更新しました" + +#: weko_accounts/admin.py: +msgid "Blocked user list was updated." +msgstr "ユーザーログインブロック設定を更新しました" + #: weko_accounts/admin.py:69 msgid "Setting" msgstr "設定" @@ -101,7 +137,47 @@ msgstr "Shibbolethを有効にする" msgid "Deny shibboleth user" msgstr "Shibbolethを無効にする" -#: weko_accounts/templates/weko_accounts/setting/shibuser.html:68 +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:71 +msgid "Default Role" +msgstr "既定のロール" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:73 +msgid "Gakunin IdP" +msgstr "学認IdP" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:76 +msgid "Outside Orthros" +msgstr "機関外のOrthros" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:79 +msgid "Extra IdP" +msgstr "上記以外のIdP" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:84 +msgid "Attribute Mapping" +msgstr "属性マッピング" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:86 +msgid "shib_eppn" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:89 +msgid "shib_role_authority_name" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:92 +msgid "shib_mail" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:95 +msgid "shib_user_name" +msgstr "" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:100 +msgid "Block User" +msgstr "ブロックユーザー" + +#: weko_accounts/templates/weko_accounts/setting/shibuser.html:120 msgid "Save" msgstr "保存" @@ -114,7 +190,7 @@ msgid "Invalid attribute." msgstr "属性値が不正です。" #: weko_accounts/api.py:223 -msgid "Invalid attribute." +msgid "Failed to login." msgstr "ログインに失敗しました。" #: weko_accounts/templates/weko_accounts/login_shibuser_pattern_1.html diff --git a/modules/weko-accounts/weko_accounts/views.py b/modules/weko-accounts/weko_accounts/views.py index 215e2bb5a3..56a4b8efe6 100644 --- a/modules/weko-accounts/weko_accounts/views.py +++ b/modules/weko-accounts/weko_accounts/views.py @@ -26,6 +26,7 @@ import json import sys +import re from urllib.parse import quote_plus from flask import Blueprint, abort, current_app, flash, redirect, \ @@ -38,6 +39,7 @@ from weko_redis.redis import RedisConnection from werkzeug.local import LocalProxy from invenio_db import db +from weko_admin.models import AdminSettings, db from .api import ShibUser, sync_shib_gakunin_map_groups from .utils import generate_random_str, parse_attributes @@ -59,7 +61,6 @@ def _has_admin_access(): return current_user.is_authenticated and current_admin \ .permission_factory(current_admin.admin.index_view).can() - @blueprint.before_app_first_request def init_menu(): """Initialize menu before first request.""" @@ -71,6 +72,73 @@ def init_menu(): _('%(icon)s Administration', icon=''), visible_when=_has_admin_access, order=100) + +@blueprint.before_app_first_request +def _adjust_shib_admin_DB(): + """ + Create or Update Shibboleth Admin database table. + """ + if current_app.config.get('TESTING', False): # テスト環境では何もしない + return + + with _app.app_context(): + if AdminSettings.query.filter_by(name='blocked_user_settings').first() is None: + max_id = db.session.query(db.func.max(AdminSettings.id)).scalar() + new_setting = AdminSettings( + id=max_id + 1, + name="blocked_user_settings", + settings={"blocked_ePPNs": []} + ) + db.session.add(new_setting) + db.session.commit() + + if AdminSettings.query.filter_by(name='shib_login_enable').first() is None: + max_id = db.session.query(db.func.max(AdminSettings.id)).scalar() + new_setting = AdminSettings( + id=max_id + 1, + name="shib_login_enable", + settings={"shib_flg": _app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED']} + ) + db.session.add(new_setting) + db.session.commit() + else: + setting = AdminSettings.query.filter_by(name='shib_login_enable').first() + setting.settings = {"shib_flg": _app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED']} + db.session.commit() + + if AdminSettings.query.filter_by(name='default_role_settings').first() is None: + max_id = db.session.query(db.func.max(AdminSettings.id)).scalar() + new_setting = AdminSettings( + id=max_id + 1, + name="default_role_settings", + settings={ + "gakunin_role": _app.config['WEKO_ACCOUNTS_GAKUNIN_ROLE']['defaultRole'], + "orthros_outside_role": _app.config['WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE']['defaultRole'], + "extra_role": _app.config['WEKO_ACCOUNTS_EXTRA_ROLE']['defaultRole']} + ) + db.session.add(new_setting) + db.session.commit() + else: + setting = AdminSettings.query.filter_by(name='default_role_settings').first() + setting.settings = { + "gakunin_role": _app.config['WEKO_ACCOUNTS_GAKUNIN_ROLE']['defaultRole'], + "orthros_outside_role": _app.config['WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE']['defaultRole'], + "extra_role": _app.config['WEKO_ACCOUNTS_EXTRA_ROLE']['defaultRole']} + db.session.commit() + + if AdminSettings.query.filter_by(name='attribute_mapping').first() is None: + max_id = db.session.query(db.func.max(AdminSettings.id)).scalar() + new_setting = AdminSettings( + id=max_id + 1, + name="attribute_mapping", + settings=_app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP'] + ) + db.session.add(new_setting) + db.session.commit() + else: + setting = AdminSettings.query.filter_by(name='attribute_mapping').first() + setting.settings = _app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP'] + db.session.commit() def _redirect_method(has_next=False): @@ -217,6 +285,62 @@ def confirm_user(): return abort(400) +@blueprint.route('/confim/user/skip', methods=['GET']) +def confirm_user_without_page(): + """Check weko user info without page. + + :return: + """ + try: + # get shib_session_id from session + shib_session_id = session['shib_session_id'] + if not shib_session_id: + flash('shib_session_id', category='error') + return _redirect_method() + + # get cache from redis + redis_connection = RedisConnection() + datastore = redis_connection.connection(db=current_app.config['CACHE_REDIS_DB'], kv = True) + cache_key = current_app.config[ + 'WEKO_ACCOUNTS_SHIB_CACHE_PREFIX'] + shib_session_id + + # check cache + if not datastore.redis.exists(cache_key): + flash('cache_key', category='error') + return _redirect_method() + + cache_val = datastore.get(cache_key) + if not cache_val: + flash('cache_val', category='error') + datastore.delete(cache_key) + return _redirect_method() + + cache_val = json.loads(str(cache_val, encoding='utf-8')) + shib_user = ShibUser(cache_val) + + # bind relation info + if not shib_user.bind_relation_info(cache_val.get('shib_mail')): + flash('FAILED bind_relation_info!', category='error') + datastore.delete(cache_key) + return _redirect_method() + + # check in + error = shib_user.check_in() + + if error: + datastore.delete(cache_key) + flash(error, category='error') + return _redirect_method() + + if shib_user.shib_user: + shib_user.shib_user_login() + datastore.delete(cache_key) + return redirect(session['next'] if 'next' in session else '/') + except BaseException: + current_app.logger.error("Unexpected error: {}".format(sys.exc_info())) + return abort(400) + + @blueprint.route('/shib/login', methods=['GET']) def shib_login(): """Get shibboleth user login page. @@ -255,10 +379,7 @@ def shib_login(): csrf_random = generate_random_str(length=64) session['csrf_random'] = csrf_random - _datastore = LocalProxy( - lambda: current_app.extensions['security'].datastore) - user = _datastore.find_user( - email=cache_val.get('shib_mail')) + user = find_user_by_email(cache_val) return render_template( current_app.config['WEKO_ACCOUNTS_CONFIRM_USER_TEMPLATE'], @@ -269,6 +390,13 @@ def shib_login(): current_app.logger.error("Unexpected error: {}".format(sys.exc_info())) return abort(400) +def find_user_by_email(shib_attributes): + """Find user by email.""" + _datastore = LocalProxy( + lambda: current_app.extensions['security'].datastore) + user = _datastore.find_user(email=shib_attributes.get('shib_mail')) + + return user @blueprint.route('/shib/login', methods=['POST']) def shib_sp_login(): @@ -300,6 +428,26 @@ def shib_sp_login(): flash(_("Missing SHIB_ATTRs!"), category='error') return _redirect_method() + # Check if shib_eppn is not included in the blocked user list + if AdminSettings.query.filter_by(name='blocked_user_settings').first(): + block_user_settings = AdminSettings.get('blocked_user_settings', dict_to_object=False) + if isinstance(block_user_settings, str): + block_user_settings = json.loads(block_user_settings) + block_user_list = block_user_settings.get('blocked_ePPNs', []) + shib_eppn = shib_attr.get('shib_eppn') + + # Convert wildcards to regular expressions + def _wildcard_to_regex(pattern): + regex_pattern = pattern.replace("*", ".*") + return re.compile(f"^{regex_pattern}$") + + blocked = any(_wildcard_to_regex(pattern).match(shib_eppn) or pattern == shib_eppn for pattern in block_user_list) + + if blocked: + flash(_("Failed to login."), category='error') + return _redirect_method() + + # Redis connection redis_connection = RedisConnection() datastore = redis_connection.connection(db=current_app.config['CACHE_REDIS_DB'], kv = True) ttl_sec = int(current_app.config[ @@ -315,9 +463,15 @@ def shib_sp_login(): rst = shib_user.get_relation_info() next_url = 'weko_accounts.shib_auto_login' + if not rst: - # Relation is not existed, cache shibboleth info to redis. - next_url = 'weko_accounts.shib_login' + if current_app.config['WEKO_ACCOUNTS_SKIP_CONFIRMATION_PAGE']: + user = find_user_by_email(shib_attr) + if user: + next_url = 'weko_accounts.confirm_user_without_page' + else: + # Relation is not existed, cache shibboleth info to redis. + next_url = 'weko_accounts.shib_login' query_string = { 'SHIB_ATTR_SESSION_ID': shib_session_id, diff --git a/modules/weko-admin/tests/test_cli.py b/modules/weko-admin/tests/test_cli.py index 14eac9edaf..6121dd0ee8 100644 --- a/modules/weko-admin/tests/test_cli.py +++ b/modules/weko-admin/tests/test_cli.py @@ -17,7 +17,8 @@ create_settings, create_default_settings, create_default_affiliation_settings, - insert_facet_search_to_db + insert_facet_search_to_db, + update_attribute_mapping ) from weko_admin.models import AdminLangSettings,ApiCertificate,StatisticUnit,\ StatisticTarget,BillingPermission,AdminSettings,FacetSearchSetting @@ -226,4 +227,41 @@ def test_insert_facet_search_to_db(db, script_info): with patch("weko_admin.cli.FacetSearchSetting.create",side_effect=Exception("test_error")): result = runner.invoke(insert_facet_search_to_db,["Data Language","データの言語","language","[]","True","SelectBox","1","True","OR"],obj=script_info) assert result.exit_code == 0 + assert result.output == "test_error\n" + +#def admin_settings(): +#def update_attribute_mapping(shib_eppn, shib_role_authority_name, shib_mail, shib_user_name): +# .tox/c1/bin/pytest --cov=weko_admin tests/test_cli.py::test_update_attribute_mapping -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-admin/.tox/c1/tmp +def test_update_attribute_mapping(db, script_info): + runner = CliRunner() + + try: + db.session.add(AdminSettings( + id=9, + name="attribute_mapping", + settings='{"shib_eppn": "eduPersonPrincipalName", "shib_mail": "mail", "shib_user_name": "displayName", "shib_role_authority_name": "eduPersonAffiliation"}' + )) + db.session.commit() + except Exception as e: + db.session.rollback() + raise + finally: + db.session.remove() + + # テスト用引数をオプション形式で定義 + args = [ + '--shib_eppn', 'o', + '--shib_role_authority_name', None, + '--shib_mail', 'o', + '--shib_user_name', None + ] + + result = runner.invoke(update_attribute_mapping, args=args, obj=script_info) + + assert result.exit_code == 0 + assert result.output.strip() == "Mapping and update were successful." + + with patch("weko_admin.cli.AdminSettings.update",side_effect=Exception("test_error")): + result = runner.invoke(update_attribute_mapping, args=args, obj=script_info) + assert result.exit_code == 0 assert result.output == "test_error\n" \ No newline at end of file diff --git a/modules/weko-admin/weko_admin/cli.py b/modules/weko-admin/weko_admin/cli.py index 6e635b9fb0..9b90cc4a89 100644 --- a/modules/weko-admin/weko_admin/cli.py +++ b/modules/weko-admin/weko_admin/cli.py @@ -22,6 +22,7 @@ import ast import click +import json from flask.cli import with_appcontext from weko_authors.models import AuthorsPrefixSettings, AuthorsAffiliationSettings @@ -208,6 +209,40 @@ def create_settings(id, name, settings): except Exception as ex: click.secho(str(ex)) +@admin_settings.command('mapping_update') +@click.option('--shib_eppn', type=str, default=None) +@click.option('--shib_role_authority_name', type=str, default=None) +@click.option('--shib_mail', type=str, default=None) +@click.option('--shib_user_name', type=str, default=None) +@with_appcontext +def update_attribute_mapping(shib_eppn, shib_role_authority_name, shib_mail, shib_user_name): + """Update Attribute Mapping between Shibboleth and WEKO3.""" + attribute_mappings = AdminSettings.get('attribute_mapping', dict_to_object=False) + if isinstance(attribute_mappings, str): + attribute_mappings = json.loads(attribute_mappings) + + attributes = { + 'shib_eppn': attribute_mappings.get('shib_eppn', ''), + 'shib_role_authority_name': attribute_mappings.get('shib_role_authority_name', ''), + 'shib_mail': attribute_mappings.get('shib_mail', ''), + 'shib_user_name': attribute_mappings.get('shib_user_name', '') + } + + try: + if shib_eppn is not None: + attributes['shib_eppn'] = shib_eppn + if shib_role_authority_name is not None: + attributes['shib_role_authority_name'] = shib_role_authority_name + if shib_mail is not None: + attributes['shib_mail'] = shib_mail + if shib_user_name is not None: + attributes['shib_user_name'] = shib_user_name + + AdminSettings.update('attribute_mapping', attributes) + click.secho("Mapping and update were successful.") + + except Exception as e: + click.secho(str(e)) @click.group() def authors_prefix(): diff --git a/modules/weko-admin/weko_admin/config.py b/modules/weko-admin/weko_admin/config.py index 1ca35d53e6..71d9da8a17 100644 --- a/modules/weko-admin/weko_admin/config.py +++ b/modules/weko-admin/weko_admin/config.py @@ -1176,6 +1176,7 @@ 'location', 'facet-search', 'community', + 'shibboleth', # 'restricted_access' ] + WEKO_ADMIN_COMMUNITY_ACCESS_LIST """Classes Repository Administrator can access.""" diff --git a/nginx/Dockerfile b/nginx/Dockerfile index bd4db38a55..99c6c1d377 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -67,6 +67,7 @@ ADD nginx.conf /etc/nginx/nginx.conf ADD ./keys/server.crt /etc/nginx/server.crt ADD ./keys/server.key /etc/nginx/server.key ADD ./idp-metadata.xml /etc/shibboleth/idp-metadata.xml +ADD ./orthros-idp-metadata.xml /etc/shibboleth/orthros-idp-metadata.xml ADD ./htpasswd /etc/nginx/.htpasswd ADD ./htdigest /etc/nginx/.htdigest ADD redirect_list.map /etc/nginx/redirect_list.map diff --git a/nginx/Dockerfile.ams b/nginx/Dockerfile.ams index 43313c1820..7e7e78e016 100644 --- a/nginx/Dockerfile.ams +++ b/nginx/Dockerfile.ams @@ -66,6 +66,7 @@ ADD nginx.conf /etc/nginx/nginx.conf ADD ./keys/server.crt /etc/nginx/server.crt ADD ./keys/server.key /etc/nginx/server.key ADD ./idp-metadata.xml /etc/shibboleth/idp-metadata.xml +ADD ./orthros-idp-metadata.xml /etc/shibboleth/orthros-idp-metadata.xml ADD ./htpasswd /etc/nginx/.htpasswd ADD ./htdigest /etc/nginx/.htdigest ADD redirect_list.map /etc/nginx/redirect_list.map diff --git a/nginx/Dockerfile.arm64 b/nginx/Dockerfile.arm64 index f8962f3b5e..931e274b21 100644 --- a/nginx/Dockerfile.arm64 +++ b/nginx/Dockerfile.arm64 @@ -72,6 +72,7 @@ ADD nginx.conf /etc/nginx/nginx.conf ADD ./keys/server.crt /etc/nginx/server.crt ADD ./keys/server.key /etc/nginx/server.key ADD ./idp-metadata.xml /etc/shibboleth/idp-metadata.xml +ADD ./orthros-idp-metadata.xml /etc/shibboleth/orthros-idp-metadata.xml ADD ./htpasswd /etc/nginx/.htpasswd ADD ./htdigest /etc/nginx/.htdigest ADD redirect_list.map /etc/nginx/redirect_list.map diff --git a/nginx/orthros-idp-metadata.xml b/nginx/orthros-idp-metadata.xml new file mode 100644 index 0000000000..8b1191ad43 --- /dev/null +++ b/nginx/orthros-idp-metadata.xml @@ -0,0 +1,39 @@ + + + + + openidp.nii.ac.jp + + Orthros + Orthros + Orthrosは、学認に参加していない機関に所属するユーザにもアカウントを発行し、連携するサービスを利用できるようにするIdPです。 + https://ui.orthros.gakunin.nii.ac.jp/static/user/img/Orthros_LOGO_02_color.svg + https://meatwiki.nii.ac.jp/confluence/pages/viewpage.action?pageId=118168818 + https://ui.orthros.gakunin.nii.ac.jp/ja/pages/privacy-policy + https://ui.orthros.gakunin.nii.ac.jp/en/pages/privacy-policy + category:location:others category:organizationType:others + + + + + + MIIC9zCCAd8CFExC+m7Rpuo7wwPVKMeVnE8n/fZ4MA0GCSqGSIb3DQEBCwUAMDgxCzAJBgNVBAMMAkpQMQ4wDAYDVQQIDAVUb2t5bzEZMBcGA1UEAwwQT3J0aHJvcy1JZFAtQ2VydDAeFw0yMzAzMjMwNTExMTZaFw0yODAzMjEwNTExMTZaMDgxCzAJBgNVBAMMAkpQMQ4wDAYDVQQIDAVUb2t5bzEZMBcGA1UEAwwQT3J0aHJvcy1JZFAtQ2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALAobQleYbpuJBB31HMkMCO4NOjfY4fDKkEpkBu+genfmJrKlTMda/ekT/eF3K73uYg5OUV1zcDXOnsM+J8RcqcLsRQAo21+3Q/95MF9B4gcFYcwDpDPVdqrwj5YvCUUfp2H45jrSRsICYYffUVsgwcSK7oUQf900imHWpKt7VimUmAgAukioQSyvV4+4RffAOUMZ6QQrhXUHcGD+uvYyiBZdx0Dr4ez7w/C632TKjzKQz6vMXHfnhIwyebcjAD3XwPj+1HfUUvdXBoLdUsMwvsNXbG1HIb6si7M+2hjf9cDhasFf/TOs0+tbWkN40aHzt7swW9z+iOmfix+eDqG04cCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZhbQ3bVz7tUJ7B1POiQNklJw73bB5AWVnHDneO1Cx0vLt/6DDmmAO5HqKKUPfJeQVlABR3AGA5Ge0eEHAG/0oCjfeq2IU/ZD2lhmmZApn3TUPZB125aUQ/0uPpLVFOmAz7VMqiYVOTxkJ5GPGQ12xlkXQ4hDmkxh9Kf9LpuGj9gDiLomQOr79lduurwpZPuNjI6KyLtUhtSbWFiAznwA3KarF69HyAP0OFMUt3uTKyyH9Nu8HlHMgBOqMXaaVb39KZg2lEKltx/QBPbAp35d5QRnaLblRQgyseKmhXw+M/BM+xKAfF5q9W5NLLjm9fU19e3FlbzrLk0M0Cq5eNa8qA== + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + + + National Institute of Informatics + 国立情報学研究所 + Orthros + Orthros + https://www.nii.ac.jp/ + + + Orthros Office + mailto:orthros-office@nii.ac.jp + + diff --git a/nginx/shibboleth2.xml b/nginx/shibboleth2.xml index fb10f05c27..27bf33a9f3 100644 --- a/nginx/shibboleth2.xml +++ b/nginx/shibboleth2.xml @@ -85,7 +85,8 @@ --> - + +