{{ _("Application '%(client_name)s' by '%(client_user)s' wants permission to access your '%(current_user)s' account.",
- client_name=client.name, client_user=client.user.nickname or client.user.email, current_user=current_user.nickname or current_user.email) }}
{{ _("Application '%(client_name)s' by '%(client_user)s' wants permission to access your
+ '%(current_user)s' account.",
+ client_name=client.name, client_user=client.user.nickname or client.user.email,
+ current_user=current_user.nickname or current_user.email) }}
+
+
+
+
-
{{ _('Review permissions') }}
- {%- for group in scopes|groupby('group') %}
- {%- if loop.first %}
{% endblock %}
diff --git a/modules/invenio-oauth2server/invenio_oauth2server/templates/invenio_oauth2server/errors.html b/modules/invenio-oauth2server/invenio_oauth2server/templates/invenio_oauth2server/errors.html
index 13cea2a2d6..7452474103 100644
--- a/modules/invenio-oauth2server/invenio_oauth2server/templates/invenio_oauth2server/errors.html
+++ b/modules/invenio-oauth2server/invenio_oauth2server/templates/invenio_oauth2server/errors.html
@@ -1,10 +1,10 @@
{# -*- coding: utf-8 -*-
- This file is part of Invenio.
- Copyright (C) 2015-2018 CERN.
+This file is part of Invenio.
+Copyright (C) 2015-2018 CERN.
- Invenio is free software; you can redistribute it and/or modify it
- under the terms of the MIT License; see LICENSE file for more details.
+Invenio is free software; you can redistribute it and/or modify it
+under the terms of the MIT License; see LICENSE file for more details.
#}
{%- extends config.OAUTH2SERVER_COVER_TEMPLATE %}
@@ -14,24 +14,36 @@
{%- block page_body %}
- {{ helpers.panel_start(
+ {{ helpers.panel_start(
_('Invalid authorization request'),
icon='fa fa-warning fa-fw'
- ) }}
-
- {{ _('Invalid authorization request') }}
-
- {{ _('The service that redirected your here made an invalid authorization request (error code: %(x_error)s).',
- x_error=error.error) }}
-
-
-{%- endblock %}
+ {%- endblock %}
diff --git a/modules/invenio-oauth2server/invenio_oauth2server/translations/de/LC_MESSAGES/messages.mo b/modules/invenio-oauth2server/invenio_oauth2server/translations/de/LC_MESSAGES/messages.mo
index a8a88c920e..105341f3fc 100644
Binary files a/modules/invenio-oauth2server/invenio_oauth2server/translations/de/LC_MESSAGES/messages.mo and b/modules/invenio-oauth2server/invenio_oauth2server/translations/de/LC_MESSAGES/messages.mo differ
diff --git a/modules/invenio-oauth2server/invenio_oauth2server/translations/en/LC_MESSAGES/messages.mo b/modules/invenio-oauth2server/invenio_oauth2server/translations/en/LC_MESSAGES/messages.mo
index 7993bcacba..1b177fba6f 100644
Binary files a/modules/invenio-oauth2server/invenio_oauth2server/translations/en/LC_MESSAGES/messages.mo and b/modules/invenio-oauth2server/invenio_oauth2server/translations/en/LC_MESSAGES/messages.mo differ
diff --git a/modules/invenio-oauth2server/invenio_oauth2server/translations/en/LC_MESSAGES/messages.po b/modules/invenio-oauth2server/invenio_oauth2server/translations/en/LC_MESSAGES/messages.po
index 89915abdb9..8f1f0ef602 100644
--- a/modules/invenio-oauth2server/invenio_oauth2server/translations/en/LC_MESSAGES/messages.po
+++ b/modules/invenio-oauth2server/invenio_oauth2server/translations/en/LC_MESSAGES/messages.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: invenio-oauth2server 1.0.0\n"
"Report-Msgid-Bugs-To: info@inveniosoftware.org\n"
-"POT-Creation-Date: 2025-05-07 17:40+0900\n"
+"POT-Creation-Date: 2025-06-26 13:44+0900\n"
"PO-Revision-Date: 2025-05-07 18:18+0900\n"
"Last-Translator: FULL NAME \n"
"Language: en\n"
@@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.5.1\n"
+"Generated-By: Babel 2.9.1\n"
#: invenio_oauth2server/admin.py:60
msgid "OAuth Applications"
@@ -112,8 +112,8 @@ msgstr ""
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:18
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:19
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:58
-#: invenio_oauth2server/views/server.py:69
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:67
+#: invenio_oauth2server/views/server.py:70
msgid "Authorize application"
msgstr ""
@@ -121,41 +121,64 @@ msgstr ""
#, python-format
msgid ""
"Application '%(client_name)s' by '%(client_user)s' wants permission to "
-"access your '%(current_user)s' account."
+"access your\n"
+" '%(current_user)s' account."
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:27
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:29
#: invenio_oauth2server/templates/invenio_oauth2server/settings/token_permission_view.html:29
msgid "Review permissions"
msgstr ""
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:43
+msgid "No permissions granted."
+msgstr ""
+
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:50
#: invenio_oauth2server/templates/invenio_oauth2server/settings/token_permission_view.html:43
msgid "Application"
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:46
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:53
#: invenio_oauth2server/templates/invenio_oauth2server/settings/token_permission_view.html:46
msgid "Visit application website"
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:59
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:69
msgid "Reject"
msgstr ""
#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:18
-#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:22
msgid "Invalid authorization request"
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:24
-#, python-format
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:23
msgid ""
-"The service that redirected your here made an invalid authorization "
-"request (error code: %(x_error)s)."
+"You cannot access the service because there is an error in the "
+"authentication request."
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:31
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:25
+msgid "unsupported_response_type"
+msgstr "error: This response type is not supported."
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:28
+msgid "invalid_client_id"
+msgstr "error: The client ID is incorrect."
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:30
+msgid "invalid_request"
+msgstr "error: The request is invalid."
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:33
+msgid "invalid_scope"
+msgstr "error: The scope is incorrect."
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:35
+msgid "access_denied"
+msgstr "error: Access has been denied."
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:43
msgid "Get me out of here!"
msgstr ""
diff --git a/modules/invenio-oauth2server/invenio_oauth2server/translations/ja/LC_MESSAGES/messages.mo b/modules/invenio-oauth2server/invenio_oauth2server/translations/ja/LC_MESSAGES/messages.mo
index d60d4802e7..a213779f40 100644
Binary files a/modules/invenio-oauth2server/invenio_oauth2server/translations/ja/LC_MESSAGES/messages.mo and b/modules/invenio-oauth2server/invenio_oauth2server/translations/ja/LC_MESSAGES/messages.mo differ
diff --git a/modules/invenio-oauth2server/invenio_oauth2server/translations/ja/LC_MESSAGES/messages.po b/modules/invenio-oauth2server/invenio_oauth2server/translations/ja/LC_MESSAGES/messages.po
index e199cd0dcc..6193754711 100644
--- a/modules/invenio-oauth2server/invenio_oauth2server/translations/ja/LC_MESSAGES/messages.po
+++ b/modules/invenio-oauth2server/invenio_oauth2server/translations/ja/LC_MESSAGES/messages.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: invenio-oauth2server 1.0.0\n"
"Report-Msgid-Bugs-To: info@inveniosoftware.org\n"
-"POT-Creation-Date: 2025-05-07 17:40+0900\n"
+"POT-Creation-Date: 2025-06-26 13:44+0900\n"
"PO-Revision-Date: 2025-05-07 18:18+0900\n"
"Last-Translator: FULL NAME \n"
"Language: ja\n"
@@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.5.1\n"
+"Generated-By: Babel 2.9.1\n"
#: invenio_oauth2server/admin.py:60
msgid "OAuth Applications"
@@ -112,50 +112,72 @@ msgstr ""
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:18
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:19
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:58
-#: invenio_oauth2server/views/server.py:69
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:67
+#: invenio_oauth2server/views/server.py:70
msgid "Authorize application"
-msgstr ""
+msgstr "アプリの使用を承諾する"
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:20
-#, python-format
msgid ""
"Application '%(client_name)s' by '%(client_user)s' wants permission to "
-"access your '%(current_user)s' account."
-msgstr ""
+"access your\n"
+" '%(current_user)s' account."
+msgstr "「%(client_user)s」による「%(client_name)s」アプリケーションがあなたのアカウントへのアクセス許可を求めています。"
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:27
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:29
#: invenio_oauth2server/templates/invenio_oauth2server/settings/token_permission_view.html:29
msgid "Review permissions"
-msgstr ""
+msgstr "権限を確認"
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:43
+msgid "No permissions granted."
+msgstr "権限が付与されていません。"
+
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:50
#: invenio_oauth2server/templates/invenio_oauth2server/settings/token_permission_view.html:43
msgid "Application"
-msgstr ""
+msgstr "アプリケーション"
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:46
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:53
#: invenio_oauth2server/templates/invenio_oauth2server/settings/token_permission_view.html:46
msgid "Visit application website"
-msgstr ""
+msgstr "アプリケーションのウェブサイト閲覧"
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:59
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:69
msgid "Reject"
-msgstr ""
+msgstr "拒否"
#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:18
-#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:22
msgid "Invalid authorization request"
-msgstr ""
+msgstr "無効な認証リクエスト"
-#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:24
-#, python-format
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:23
msgid ""
-"The service that redirected your here made an invalid authorization "
-"request (error code: %(x_error)s)."
-msgstr ""
+"You cannot access the service because there is an error in the "
+"authentication request."
+msgstr "認証リクエストに誤りがあるため、アクセスできません。"
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:25
+msgid "unsupported_response_type"
+msgstr "エラー: このレスポンスタイプはサポートされていません。"
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:28
+msgid "invalid_client_id"
+msgstr "エラー: クライアントIDに誤りがあります。"
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:30
+msgid "invalid_request"
+msgstr "エラー: 無効なリクエストです。"
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:33
+msgid "invalid_scope"
+msgstr "エラー: スコープに誤りがあります。"
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:35
+msgid "access_denied"
+msgstr "エラー: アクセスが拒否されました。"
-#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:31
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:43
msgid "Get me out of here!"
msgstr ""
diff --git a/modules/invenio-oauth2server/invenio_oauth2server/translations/messages.pot b/modules/invenio-oauth2server/invenio_oauth2server/translations/messages.pot
index f59dfdd923..73cb210b50 100644
--- a/modules/invenio-oauth2server/invenio_oauth2server/translations/messages.pot
+++ b/modules/invenio-oauth2server/invenio_oauth2server/translations/messages.pot
@@ -9,14 +9,14 @@ msgid ""
msgstr ""
"Project-Id-Version: invenio-oauth2server 1.0.0\n"
"Report-Msgid-Bugs-To: info@inveniosoftware.org\n"
-"POT-Creation-Date: 2025-05-07 17:40+0900\n"
+"POT-Creation-Date: 2025-06-26 13:44+0900\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.5.1\n"
+"Generated-By: Babel 2.9.1\n"
#: invenio_oauth2server/admin.py:60
msgid "OAuth Applications"
@@ -111,8 +111,8 @@ msgstr ""
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:18
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:19
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:58
-#: invenio_oauth2server/views/server.py:69
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:67
+#: invenio_oauth2server/views/server.py:70
msgid "Authorize application"
msgstr ""
@@ -120,41 +120,64 @@ msgstr ""
#, python-format
msgid ""
"Application '%(client_name)s' by '%(client_user)s' wants permission to "
-"access your '%(current_user)s' account."
+"access your\n"
+" '%(current_user)s' account."
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:27
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:29
#: invenio_oauth2server/templates/invenio_oauth2server/settings/token_permission_view.html:29
msgid "Review permissions"
msgstr ""
#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:43
+msgid "No permissions granted."
+msgstr ""
+
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:50
#: invenio_oauth2server/templates/invenio_oauth2server/settings/token_permission_view.html:43
msgid "Application"
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:46
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:53
#: invenio_oauth2server/templates/invenio_oauth2server/settings/token_permission_view.html:46
msgid "Visit application website"
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:59
+#: invenio_oauth2server/templates/invenio_oauth2server/authorize.html:69
msgid "Reject"
msgstr ""
#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:18
-#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:22
msgid "Invalid authorization request"
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:24
-#, python-format
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:23
msgid ""
-"The service that redirected your here made an invalid authorization "
-"request (error code: %(x_error)s)."
+"You cannot access the service because there is an error in the "
+"authentication request."
+msgstr ""
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:25
+msgid "unsupported_response_type"
+msgstr ""
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:28
+msgid "invalid_client_id"
+msgstr ""
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:30
+msgid "invalid_request"
+msgstr ""
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:33
+msgid "invalid_scope"
+msgstr ""
+
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:35
+msgid "access_denied"
msgstr ""
-#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:31
+#: invenio_oauth2server/templates/invenio_oauth2server/errors.html:43
msgid "Get me out of here!"
msgstr ""
diff --git a/modules/invenio-oauth2server/invenio_oauth2server/views/server.py b/modules/invenio-oauth2server/invenio_oauth2server/views/server.py
index 5e59143bb1..9d40ec41b1 100644
--- a/modules/invenio-oauth2server/invenio_oauth2server/views/server.py
+++ b/modules/invenio-oauth2server/invenio_oauth2server/views/server.py
@@ -13,12 +13,14 @@
from functools import wraps
from flask import Blueprint, _request_ctx_stack, abort, current_app, jsonify, \
- redirect, render_template, request, session
+ redirect, render_template, request, make_response
from flask_babelex import lazy_gettext as _
from flask_breadcrumbs import register_breadcrumb
from flask_login import login_required
from flask_principal import Identity, identity_changed
-from oauthlib.oauth2.rfc6749.errors import InvalidClientError, OAuth2Error
+from oauthlib.oauth2.rfc6749.errors import InvalidClientError, OAuth2Error, \
+ AccessDeniedError, raise_from_error
+
from invenio_db import db
from ..models import Client
@@ -41,7 +43,7 @@ def login_oauth2_user(valid, oauth):
oauth.user.login_via_oauth2 = True
_request_ctx_stack.top.user = oauth.user
identity_changed.send(current_app._get_current_object(),
- identity=Identity(oauth.user.id))
+ identity=Identity(oauth.user.id))
return valid, oauth
@@ -81,17 +83,20 @@ def authorize(*args, **kwargs):
abort(404)
scopes = current_oauth2server.scopes
+ scopes_list = [scopes[x] for x in kwargs.get('scopes', [])]
+ if not scopes_list:
+ return redirect('/oauth/errors?error=invalid_scope')
+
ctx = dict(
client=client,
oauth_request=kwargs.get('request'),
- scopes=[scopes[x] for x in kwargs.get('scopes', [])],
+ scopes=scopes_list
)
return render_template('invenio_oauth2server/authorize.html', **ctx)
confirm = request.form.get('confirm', 'no')
return confirm == 'yes'
-
@blueprint.route('/token', methods=['POST', ])
@oauth2.token_handler
def access_token():
@@ -114,20 +119,29 @@ def access_token():
# Return None or a dictionary. Dictionary will be merged with token
# returned to the client requesting the access token.
# Response is in application/json
+
return None
@blueprint.route('/errors')
def errors():
"""Error view in case of invalid oauth requests."""
- from oauthlib.oauth2.rfc6749.errors import raise_from_error
+ status_code = 200
try:
error = None
- raise_from_error(request.values.get('error'), params=dict())
+ error_code = request.values.get('error')
+ description = request.values.get('error_description')
+ params = {}
+ if description:
+ params['error_description'] = description
+
+ raise_from_error(error_code, params=params)
except OAuth2Error as raised:
error = raised
- return render_template('invenio_oauth2server/errors.html', error=error)
-
+ if not isinstance(error, AccessDeniedError):
+ status_code = 400
+ response = make_response(render_template('invenio_oauth2server/errors.html', error=error), status_code)
+ return response
@blueprint.route('/ping', methods=['GET', 'POST'])
@oauth2.require_oauth()
@@ -163,10 +177,11 @@ def invalid():
@blueprint.teardown_request
def dbsession_clean(exception):
+ """Clean up the database session after each request."""
current_app.logger.debug("invenio_oauth2server dbsession_clean: {}".format(exception))
if exception is None:
try:
db.session.commit()
except:
db.session.rollback()
- db.session.remove()
\ No newline at end of file
+ db.session.remove()
diff --git a/modules/invenio-oauth2server/tests/conftest.py b/modules/invenio-oauth2server/tests/conftest.py
index 05e8fd0860..b7f35001ae 100644
--- a/modules/invenio-oauth2server/tests/conftest.py
+++ b/modules/invenio-oauth2server/tests/conftest.py
@@ -314,10 +314,20 @@ def provider_fixture(app):
'oauth2test.authorized', _external=True
),
_default_scopes='email')
+ c5 = Client(client_id='no-scopes',
+ client_secret='no-scopes',
+ name='no-scopes',
+ description='',
+ is_confidential=False,
+ user=user1,
+ _redirect_uris=url_for(
+ 'oauth2test.authorized', _external=True
+ ))
db.session.add(c1)
db.session.add(c2)
db.session.add(c3)
db.session.add(c4)
+ db.session.add(c5)
personal_token = Token.create_personal('test-personal',
user1.id,
scopes=[],
diff --git a/modules/invenio-oauth2server/tests/test_provider.py b/modules/invenio-oauth2server/tests/test_provider.py
index ab4963ffab..117d524668 100644
--- a/modules/invenio-oauth2server/tests/test_provider.py
+++ b/modules/invenio-oauth2server/tests/test_provider.py
@@ -136,7 +136,7 @@ def test_invalid_authorize_requests(provider_fixture):
assert error_url in next_url
r = client.get(next_url, query_string=data)
- assert 'invalid_request' in str(r.data)
+ assert 'an error in the authentication request' in str(r.data)
# Invalid redirect uri
r = client.get(url_for(
@@ -151,6 +151,27 @@ def test_invalid_authorize_requests(provider_fixture):
assert data['error'] == 'invalid_request'
assert error_url in next_url
+ for client_id in ['no-scopes']:
+ response_type = 'code'
+ error_url = url_for('invenio_oauth2server.errors',
+ _external=True)
+ # Missing scope
+ r = client.get(
+ url_for('invenio_oauth2server.authorize'),
+ data={
+ 'redirect_uri': redirect_uri,
+ 'response_type': response_type,
+ 'client_id': client_id,
+ })
+ next_url, data = parse_redirect(r.location)
+ assert r.status_code == 302
+ assert data['error'] == 'invalid_scope'
+ assert url_for('invenio_oauth2server.errors') in next_url
+
+ r = client.get(
+ url_for('invenio_oauth2server.errors'))
+ assert r.status_code == 400
+
def test_refresh_flow(provider_fixture):
app = provider_fixture
diff --git a/modules/invenio-records-rest/invenio_records_rest/serializers/json.py b/modules/invenio-records-rest/invenio_records_rest/serializers/json.py
index 272c23d197..6984f34dbe 100644
--- a/modules/invenio-records-rest/invenio_records_rest/serializers/json.py
+++ b/modules/invenio-records-rest/invenio_records_rest/serializers/json.py
@@ -10,7 +10,7 @@
from __future__ import absolute_import, print_function
-from flask import json, request
+from flask import json, request,current_app
from weko_records.api import ItemTypes
from .base import PreprocessorMixin, SerializerMixinInterface
@@ -109,20 +109,26 @@ def del_hide_sub_metadata(keys, metadata):
if '_source' in hit and '_item_metadata' in hit['_source'] and hit['_source']['_item_metadata']:
if 'control_number' in hit['_source']['_item_metadata']:
control_number=hit['_source']['_item_metadata']['control_number']
- record = WekoRecord.get_record_by_pid(control_number)
- is_admin = False
- is_owner = False
- roles = get_user_roles()
- if roles[0]:
- is_admin = True
- if check_created_id(record):
- is_owner = True
- is_public = check_publish_status(record)
- if check_created_id(record):
- is_owner = True
- is_public = check_publish_status(record)
- if not is_public and not is_admin and not is_owner:
+ try:
+ record = WekoRecord.get_record_by_pid(control_number)
+ is_admin = False
+ is_owner = False
+ roles = get_user_roles()
+ if roles[0]:
+ is_admin = True
+ if check_created_id(record):
+ is_owner = True
+ is_public = check_publish_status(record)
+ if check_created_id(record):
+ is_owner = True
+ is_public = check_publish_status(record)
+ if not is_public and not is_admin and not is_owner:
+ hit['_source']['_item_metadata']={}
+ except Exception as e:
+ current_app.logger.error(f"Error in serialize_search: {e}")
hit['_source']['_item_metadata']={}
+
+
return json.dumps(dict(
hits=dict(
diff --git a/modules/weko-accounts/tests/test_api.py b/modules/weko-accounts/tests/test_api.py
index 1566c47ab6..83e75166fe 100644
--- a/modules/weko-accounts/tests/test_api.py
+++ b/modules/weko-accounts/tests/test_api.py
@@ -33,7 +33,7 @@ def test_init(self):
"shib_eppn":"test_eppn"
}
shibuser = ShibUser(attr)
- assert shibuser.shib_attr == attr
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
assert shibuser.user == None
assert shibuser.shib_user == None
assert shibuser.is_member_of == []
@@ -46,7 +46,7 @@ def test_init(self):
"https://example.com/gr/yyy"]
}
shibuser = ShibUser(attr)
- assert shibuser.shib_attr == attr
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
assert shibuser.user == None
assert shibuser.shib_user == None
assert shibuser.is_member_of == ["https://example.com/gr/xxx", "https://example.com/gr/yyy"]
@@ -58,7 +58,7 @@ def test_init(self):
"shib_is_member_of": "https://example.com/gr/xxx"
}
shibuser = ShibUser(attr)
- assert shibuser.shib_attr == attr
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
assert shibuser.user == None
assert shibuser.shib_user == None
assert shibuser.is_member_of == ["https://example.com/gr/xxx"]
@@ -70,19 +70,55 @@ def test_init(self):
"shib_is_member_of": "https://example.com/gr/xxx;https://example.com/gr/yyy"
}
shibuser = ShibUser(attr)
- assert shibuser.shib_attr == attr
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
assert shibuser.user == None
assert shibuser.shib_user == None
assert shibuser.is_member_of == ["https://example.com/gr/xxx", "https://example.com/gr/yyy"]
assert shibuser.organizations == []
+ # get is_member_of, type is str and is empty
+ attr = {
+ "shib_eppn": "test_eppn",
+ "shib_is_member_of": ""
+ }
+ shibuser = ShibUser(attr)
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
+ assert shibuser.user == None
+ assert shibuser.shib_user == None
+ assert shibuser.is_member_of == []
+ assert shibuser.organizations == []
+
+ # get is_member_of, type is neither list nor str
+ attr = {
+ "shib_eppn": "test_eppn",
+ "shib_is_member_of": 123
+ }
+ shibuser = ShibUser(attr)
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
+ assert shibuser.user == None
+ assert shibuser.shib_user == None
+ assert shibuser.is_member_of == []
+ assert shibuser.organizations == []
+
+ # get is_member_of, type is none
+ attr = {
+ "shib_eppn": "test_eppn",
+ "shib_is_member_of": None
+ }
+ shibuser = ShibUser(attr)
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
+ assert shibuser.user == None
+ assert shibuser.shib_user == None
+ assert shibuser.is_member_of == []
+ assert shibuser.organizations == []
+
# get organizations and type is list
attr = {
"shib_eppn":"test_eppn",
"shib_organization": ["Abcdef University", "Test Organization"]
}
shibuser = ShibUser(attr)
- assert shibuser.shib_attr == attr
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
assert shibuser.user == None
assert shibuser.shib_user == None
assert shibuser.is_member_of == []
@@ -94,7 +130,7 @@ def test_init(self):
"shib_organization": "Abcdef University"
}
shibuser = ShibUser(attr)
- assert shibuser.shib_attr == attr
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
assert shibuser.user == None
assert shibuser.shib_user == None
assert shibuser.is_member_of == []
@@ -106,12 +142,47 @@ def test_init(self):
"shib_organization": "Abcdef University;Test Organization"
}
shibuser = ShibUser(attr)
- assert shibuser.shib_attr == attr
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
assert shibuser.user == None
assert shibuser.shib_user == None
assert shibuser.is_member_of == []
assert shibuser.organizations == ["Abcdef University", "Test Organization"]
+ # get organizations, type is str and is empty
+ attr = {
+ "shib_eppn": "test_eppn",
+ "shib_organization": ""
+ }
+ shibuser = ShibUser(attr)
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
+ assert shibuser.user == None
+ assert shibuser.shib_user == None
+ assert shibuser.is_member_of == []
+ assert shibuser.organizations == []
+
+ # get organizations, type is neither list nor str
+ attr = {
+ "shib_eppn": "test_eppn",
+ "shib_organization": 123
+ }
+ shibuser = ShibUser(attr)
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
+ assert shibuser.user == None
+ assert shibuser.shib_user == None
+ assert shibuser.is_member_of == []
+ assert shibuser.organizations == []
+
+ # get organizations, type is none
+ attr = {
+ "shib_eppn": "test_eppn",
+ "shib_organization": None
+ }
+ shibuser = ShibUser(attr)
+ assert shibuser.shib_attr == {"shib_eppn": "test_eppn"}
+ assert shibuser.user == None
+ assert shibuser.shib_user == None
+ assert shibuser.is_member_of == []
+ assert shibuser.organizations == []
# def _set_weko_user_role(self, roles):
# .tox/c1/bin/pytest --cov=weko_accounts tests/test_api.py::TestShibUser::test_set_weko_user_role -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-accounts/.tox/c1/tmp
diff --git a/modules/weko-accounts/tests/test_views.py b/modules/weko-accounts/tests/test_views.py
index 3ba78bc5a7..3c20381c2e 100644
--- a/modules/weko-accounts/tests/test_views.py
+++ b/modules/weko-accounts/tests/test_views.py
@@ -1,15 +1,13 @@
-
from unittest.mock import MagicMock
import pytest
import json
import redis
-from invenio_accounts.models import Role
+from invenio_accounts.models import Role, User
from flask import url_for,request,make_response,current_app,Flask
from flask_login.utils import login_user,logout_user
from flask_menu import current_menu
from flask_security import url_for_security
from mock import patch
-from invenio_accounts.models import User
from weko_accounts.api import ShibUser
from weko_accounts.models import ShibbolethUser
from weko_accounts.views import (
@@ -18,7 +16,8 @@
_redirect_method,
find_user_by_email,
shib_sp_login,
- _adjust_shib_admin_DB
+ _adjust_shib_admin_DB,
+ generate_ams_login_url
)
from weko_admin.models import AdminSettings
@@ -26,6 +25,11 @@ def set_session(client,data):
with client.session_transaction() as session:
for k, v in data.items():
session[k] = v
+
+def del_session(client,key):
+ with client.session_transaction() as session:
+ del session[key]
+
#def _has_admin_access():
# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_has_admin_access -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp
#def test_has_admin_access(request_context,users):
@@ -35,12 +39,12 @@ def set_session(client,data):
# logout_user()
# login_user(users[4]["obj"])
# result = _has_admin_access()
-# assert result == False
+# assert result is False
#def init_menu():
# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_init_menu -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp
def test_init_menu(request_context):
init_menu()
- assert current_menu.submenu("setting.admin").active == True
+ assert current_menu.submenu("setting.admin").active is True
assert current_menu.submenu("settings.admin").url == "/admin/"
assert current_menu.submenu("settings.admin").text == ' Administration'
@@ -96,6 +100,59 @@ def test_redirect_method(app,mocker):
_redirect_method(True)
mock_render.assert_called_with("http://TEST_SERVER.localdomain/secure/login.py?next="+url)
+ url = 'ams'
+ with app.test_request_context(url):
+ current_app.config['WEKO_ACCOUNTS_SHIB_AMS_LOGIN_URL'] = '{}ams/login'
+ # Login is blocked.
+ mock_render = mocker.patch('weko_accounts.views.redirect',return_value=make_response())
+ ams_error = 'Login is blocked.'
+ _redirect_method(True, ams_error)
+ mock_render.assert_called_with(\
+ 'http://TEST_SERVER.localdomain/ams/login?error=Login+is+blocked.')
+ # There is no user information.
+ mock_render = mocker.patch('weko_accounts.views.redirect',return_value=make_response())
+ ams_error = 'There is no user information.'
+ _redirect_method(True, ams_error)
+ mock_render.assert_called_with(
+ 'http://TEST_SERVER.localdomain/ams/login?error=There+is+no+user+information.')
+ # server error
+ mock_render = mocker.patch('weko_accounts.views.redirect',return_value=make_response())
+ ams_error = 'Server error has occurred. Please contact server administrator.'
+ _redirect_method(True, ams_error)
+ mock_render.assert_called_with(\
+ 'http://TEST_SERVER.localdomain/ams/login?'\
+ 'error=Server+error+has+occurred.+Please+contact+server+administrator.')
+ # other error
+ mock_render = mocker.patch('weko_accounts.views.redirect',return_value=make_response())
+ ams_error = 'Error Message'
+ _redirect_method(True, ams_error)
+ mock_render.assert_called_with(\
+ 'http://TEST_SERVER.localdomain/ams/login?error=Error+Message')
+
+# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_generate_ams_login_url -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp
+def test_generate_ams_login_url(app):
+ with app.test_request_context():
+ current_app.config['WEKO_ACCOUNTS_SHIB_AMS_LOGIN_URL'] = '{}ams/login'
+ # Missing Shib-Session-ID!
+ ams_error = 'Missing Shib-Session-ID!'
+ url = generate_ams_login_url(ams_error)
+ assert url == '/ams/login?error=Missing+Shib-Session-ID%21'
+
+ # Missing SHIB_ATTRs!
+ ams_error = 'Missing SHIB_ATTRs!'
+ url = generate_ams_login_url(ams_error)
+ assert url == '/ams/login?error=Missing+SHIB_ATTRs%21'
+
+ # Login is blocked.
+ ams_error = 'Login is blocked.'
+ url = generate_ams_login_url(ams_error)
+ assert url == '/ams/login?error=Login+is+blocked.'
+
+ # Server error has occurred. Please contact server administrator.
+ ams_error = 'Server error has occurred. Please contact server administrator.'
+ url = generate_ams_login_url(ams_error)
+ assert url == '/ams/login?error=Server+error+has+occurred.+Please+contact+server+administrator.'
+
#def index():
# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_index -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp
def test_index(client,mocker):
@@ -109,53 +166,101 @@ def test_shib_auto_login(client,redis_connect,mocker):
url = url_for("weko_accounts.shib_auto_login")
set_session(client,{"shib_session_id":None})
# not exist shib_session_id
- mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",return_value=make_response())
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
client.get(url)
mock_redirect_.assert_called_once()
+ # not exist shib_session_id(AMS)
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url+"?next=ams")
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing Shib-Session-ID!" in called_kwargs.get("ams_error", "")
- mocker.patch("weko_accounts.views.RedisConnection.connection",return_value=redis_connect)
# not exist cache
- mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",return_value=make_response())
+ mocker.patch("weko_accounts.views.RedisConnection.connection",
+ return_value=redis_connect)
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
client.get(url+"?Shib-Session-ID=2222")
mock_redirect_.assert_called_once()
+ # not exist cache(AMS)
+ mocker.patch("weko_accounts.views.RedisConnection.connection",
+ return_value=redis_connect)
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url+"?next=ams&Shib-Session-ID=2222")
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing SHIB_CACHE_PREFIX!" in called_kwargs.get("ams_error", "")
-
- redis_connect.put("Shib-Session-1111",bytes("","utf-8"))
# not cache_val
- mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",return_value=make_response())
+ redis_connect.put("Shib-Session-1111",bytes("","utf-8"))
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
client.get(url+"?Shib-Session-ID=1111")
mock_redirect_.assert_called_once()
- assert redis_connect.redis.exists("Shib-Session-1111") == False
+ assert redis_connect.redis.exists("Shib-Session-1111") is False
+
+ # not cache_val(AMS)
+ redis_connect.put("Shib-Session-1111",bytes("","utf-8"))
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url+"?next=ams&Shib-Session-ID=1111")
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing SHIB_ATTR!" in called_kwargs.get("ams_error", "")
+ # is_auto_bind is false, check_in is error
mock_get_relation = mocker.patch("weko_accounts.views.ShibUser.get_relation_info")
mock_new_relation = mocker.patch("weko_accounts.views.ShibUser.new_relation_info")
mock_shib_login = mocker.patch("weko_accounts.views.ShibUser.shib_user_login")
redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
- # is_auto_bind is false, check_in is error
- mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",return_value=make_response())
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
with patch("weko_accounts.views.ShibUser.check_in",return_value="test_error"):
client.get(url+"?Shib-Session-ID=1111")
mock_get_relation.assert_called_once()
mock_redirect_.assert_called_once()
- assert redis_connect.redis.exists("Shib-Session-1111") == False
+ assert redis_connect.redis.exists("Shib-Session-1111") is False
+
+ # is_auto_bind is false, check_in is error(AMS)
+ mock_get_relation = mocker.patch("weko_accounts.views.ShibUser.get_relation_info")
+ mock_new_relation = mocker.patch("weko_accounts.views.ShibUser.new_relation_info")
+ mock_shib_login = mocker.patch("weko_accounts.views.ShibUser.shib_user_login")
redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ with patch("weko_accounts.views.ShibUser.check_in",return_value="test_error"):
+ client.get(url+"?next=ams&Shib-Session-ID=1111")
+ mock_get_relation.assert_called_once()
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "test_error" in called_kwargs.get("ams_error", "")
+ redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
set_session(client,{"shib_session_id":"1111"})
with patch("weko_accounts.views.ShibUser.check_in",return_value=None):
# is_auto_bind is true,shib_user is None
shibuser = ShibUser({})
shibuser.user = User(id=1)
with patch("weko_accounts.views.ShibUser",return_value=shibuser):
- mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response())
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
client.get(url)
mock_new_relation.assert_called_once()
mock_shib_login.assert_not_called()
mock_redirect.assert_called_with("/")
- assert redis_connect.redis.exists("Shib-Session-1111") == False
+ assert redis_connect.redis.exists("Shib-Session-1111") is False
# is_auto_bind is true,shib_user exis
redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
@@ -165,19 +270,50 @@ def test_shib_auto_login(client,redis_connect,mocker):
shibuser.shib_user = "test_user"
shibuser.user = User(id=1)
with patch("weko_accounts.views.ShibUser",return_value=shibuser):
- mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response())
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
client.get(url+'?next=/next_page')
mock_redirect.assert_called_with("/next_page")
mock_shib_login.assert_called_once()
- assert redis_connect.redis.exists("Shib-Session-1111") == False
+ assert redis_connect.redis.exists("Shib-Session-1111") is False
+
+ # is_auto_bind is true,shib_user exis(AMS)
+ mock_shib_login.reset_mock()
+ redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
+ set_session(client,{"shib_session_id":"1111","next":"/next_page"})
+
+ shibuser = ShibUser({})
+ shibuser.shib_user = "test_user"
+ shibuser.user = User(id=1)
+ with patch("weko_accounts.views.ShibUser",return_value=shibuser):
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
+ client.get(url+'?next=ams')
+ called_args, _ = mock_redirect.call_args
+ mock_redirect.assert_called_with("/?next=ams")
+ mock_shib_login.assert_called_once()
+ assert redis_connect.redis.exists("Shib-Session-1111") is False
+
# raise BaseException
with patch("weko_accounts.views.RedisConnection",side_effect=BaseException("test_error")):
res = client.get(url+"?Shib-Session-ID=1111")
assert res.status_code == 400
+
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ with patch("weko_accounts.views.RedisConnection",side_effect=BaseException("test_error")):
+ res = client.get(url+"?next=ams&Shib-Session-ID=1111")
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Server error has occurred. Please contact server " \
+ "administrator." in called_kwargs.get("ams_error", "")
+
#def confirm_user():
# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_confirm_user -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp
def test_confirm_user(client,redis_connect,mocker):
- mocker.patch("weko_accounts.views.RedisConnection.connection",return_value=redis_connect)
+ 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")
@@ -188,19 +324,53 @@ def test_confirm_user(client,redis_connect,mocker):
client.post(url,data=form)
mock_flash.assert_called_with("csrf_random",category="error")
+ # not correct csrf_random(AMS)
+ form = {"csrf_random":"test_csrf"}
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ set_session(client,{"next": "ams"})
+ client.post(url, data=form)
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "csrf_random" in called_kwargs.get("ams_error", "")
+
# not exist shib_session_id
set_session(client,{"csrf_random":"test_csrf","shib_session_id":None})
+ del_session(client, "next")
mock_flash = mocker.patch("weko_accounts.views.flash")
client.post(url,data=form)
mock_flash.assert_called_with("shib_session_id",category="error")
+ # not exist shib_session_id(AMS)
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ set_session(client,{"next": "ams"})
+ client.post(url, data=form)
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing Shib-Session-ID!" in called_kwargs.get("ams_error", "")
+
# not exist cache_key
set_session(client,{"csrf_random":"test_csrf","shib_session_id":"2222"})
+ del_session(client, "next")
mock_flash = mocker.patch("weko_accounts.views.flash")
client.post(url,data=form)
mock_flash.assert_called_with("cache_key",category="error")
+ # not exist cache_key(AMS)
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ set_session(client,{"next": "ams"})
+ client.post(url, data=form)
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing SHIB_CACHE_PREFIX!" in called_kwargs.get("ams_error", "")
+
set_session(client,{"csrf_random":"test_csrf","shib_session_id":"1111"})
+ del_session(client, "next")
# not exist cache_value
redis_connect.put("Shib-Session-1111",bytes("","utf-8"))
mock_flash = mocker.patch("weko_accounts.views.flash")
@@ -208,20 +378,45 @@ def test_confirm_user(client,redis_connect,mocker):
mock_flash.assert_called_with("cache_val",category="error")
assert redis_connect.redis.exists("Shib-Session-1111") is False
+ # not exist cache_value(AMS)
+ redis_connect.put("Shib-Session-1111",bytes("","utf-8"))
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ set_session(client,{"next": "ams"})
+ client.post(url, data=form)
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing SHIB_ATTR!" in called_kwargs.get("ams_error", "")
+
# shib_user.check_weko_user 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=False):
mock_flash = mocker.patch("weko_accounts.views.flash")
+ del_session(client, "next")
client.post(url,data=form)
mock_flash.assert_called_with("check_weko_user",category="error")
assert redis_connect.redis.exists("Shib-Session-1111") is False
+ # shib_user.check_weko_user is false(AMS)
+ redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
+ with patch("weko_accounts.views.ShibUser.check_weko_user",return_value=False):
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ set_session(client, {"next": "ams"})
+ client.post(url,data=form)
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "There is no user information." in called_kwargs.get("ams_error", "")
+
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")
+ del_session(client, "next")
client.post(url,data=form)
mock_flash.assert_called_with("FAILED bind_relation_info!",category="error")
with patch("weko_accounts.views.ShibUser.bind_relation_info",return_value=True):
@@ -236,35 +431,87 @@ def test_confirm_user(client,redis_connect,mocker):
redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
shibuser = ShibUser({})
shibuser.user = User(id=1)
- with patch("weko_accounts.views.ShibUser",return_value=shibuser):
- mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response())
+ with patch("weko_accounts.views.ShibUser",
+ return_value=shibuser):
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
client.post(url,data=form)
mock_redirect.assert_called_with("/")
assert redis_connect.redis.exists("Shib-Session-1111") is False
- # exist ShibUser.shib_user
- set_session(client,{"csrf_random":"test_csrf","shib_session_id":"1111","next":"/next_page"})
- redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
-
+ # exist ShibUser.shib_user
+ set_session(client,{"csrf_random":"test_csrf",
+ "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"
shibuser.user = User(id=1)
with patch("weko_accounts.views.ShibUser",return_value=shibuser):
- mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response())
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
mock_flash = mocker.patch("weko_accounts.views.flash")
client.post(url,data=form)
mock_redirect.assert_called_with("/next_page")
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_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ set_session(client, {"next": "ams"})
+ client.post(url,data=form)
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "FAILED bind_relation_info!" in called_kwargs.get("ams_error", "")
+ 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"):
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.post(url,data=form)
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "test_error" in called_kwargs.get("ams_error", "")
+ 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"))
+ shibuser = ShibUser({})
+ shibuser.user = User(id=1)
+ with patch("weko_accounts.views.ShibUser",return_value=shibuser):
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
+ client.post(url,data=form)
+ called_args, _ = mock_redirect.call_args
+ mock_redirect.assert_called_with("/?next=ams")
+ assert redis_connect.redis.exists("Shib-Session-1111") is False
+
# raise BaseException
with patch("weko_accounts.views._redirect_method",side_effect=BaseException("test_error")):
+ del_session(client, "next")
res = client.post(url,data=form)
assert res.status_code == 400
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ with patch("weko_accounts.views.RedisConnection",side_effect=BaseException("test_error")):
+ set_session(client, {"next": "ams"})
+ res = client.post(url,data=form)
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Server error has occurred. Please contact server " \
+ "administrator." in called_kwargs.get("ams_error", "")
+
#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.RedisConnection.connection",
+ return_value=redis_connect)
mocker.patch("weko_accounts.views.ShibUser.shib_user_login")
url = url_for("weko_accounts.confirm_user_without_page")
@@ -273,11 +520,29 @@ def test_confirm_user_without_page(client,redis_connect,mocker):
client.get(url, query_string={"Shib-Session-ID":None})
mock_flash.assert_called_with("shib_session_id",category="error")
+ # not exist shib_session_id(AMS)
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url+"?next=ams")
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing Shib-Session-ID!" in called_kwargs.get("ams_error", "")
+
# not exist cache_key
mock_flash = mocker.patch("weko_accounts.views.flash")
client.get(url, query_string={"Shib-Session-ID":"2222"})
mock_flash.assert_called_with("cache_key",category="error")
+ # not exist cache_key(AMS)
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url, query_string={"next":"ams","Shib-Session-ID":"2222"})
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing SHIB_CACHE_PREFIX!" in called_kwargs.get("ams_error", "")
+
# not exist cache_value
redis_connect.put("Shib-Session-1111",bytes("","utf-8"))
mock_flash = mocker.patch("weko_accounts.views.flash")
@@ -285,6 +550,16 @@ def test_confirm_user_without_page(client,redis_connect,mocker):
mock_flash.assert_called_with("cache_val",category="error")
assert redis_connect.redis.exists("Shib-Session-1111") is False
+ # not exist cache_value(AMS)
+ redis_connect.put("Shib-Session-1111",bytes("","utf-8"))
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url, query_string={"next":"ams","Shib-Session-ID":"1111"})
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing SHIB_ATTR!" in called_kwargs.get("ams_error", "")
+
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
@@ -305,7 +580,8 @@ def test_confirm_user_without_page(client,redis_connect,mocker):
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())
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
client.get(url, query_string={"Shib-Session-ID":"1111"})
mock_redirect.assert_called_with("/")
assert redis_connect.redis.exists("Shib-Session-1111") is False
@@ -316,7 +592,8 @@ def test_confirm_user_without_page(client,redis_connect,mocker):
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_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
mock_flash = mocker.patch("weko_accounts.views.flash")
client.get(url, query_string={"Shib-Session-ID":"1111"})
mock_redirect.assert_called_with("/")
@@ -328,22 +605,96 @@ def test_confirm_user_without_page(client,redis_connect,mocker):
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_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
mock_flash = mocker.patch("weko_accounts.views.flash")
client.get(url, query_string={"Shib-Session-ID":"1111","next":"/next_page"})
mock_redirect.assert_called_with("/next_page")
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_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url, query_string={"next":"ams","Shib-Session-ID":"1111"})
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "FAILED bind_relation_info!" in called_kwargs.get("ams_error", "")
+ 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_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url, query_string={"next":"ams","Shib-Session-ID":"1111"})
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "test_error" in called_kwargs.get("ams_error", "")
+ 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"))
+ shibuser = ShibUser({})
+ shibuser.user = User(id=1)
+ with patch("weko_accounts.views.ShibUser",return_value=shibuser):
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
+ client.get(url, query_string={"next":"ams","Shib-Session-ID":"1111"})
+ called_args, _ = mock_redirect.call_args
+ mock_redirect.assert_called_with("/?next=ams")
+ assert redis_connect.redis.exists("Shib-Session-1111") is False
+
+ # exist ShibUser.shib_user
+ redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
+
+ shibuser = ShibUser({})
+ shibuser.shib_user = "test_user"
+ shibuser.user = User(id=1)
+ with patch("weko_accounts.views.ShibUser",return_value=shibuser):
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
+ client.get(url, query_string={"next":"ams","Shib-Session-ID":"1111"})
+ called_args, _ = mock_redirect.call_args
+ mock_redirect.assert_called_with("/?next=ams")
+ assert redis_connect.redis.exists("Shib-Session-1111") is False
+
+ # exist ShibUser.shib_user
+ redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8"))
+ shibuser = ShibUser({})
+ shibuser.shib_user = "test_user"
+ shibuser.user = User(id=1)
+ with patch("weko_accounts.views.ShibUser",return_value=shibuser):
+ mock_redirect = mocker.patch("weko_accounts.views.redirect",
+ return_value=make_response())
+ client.get(url, query_string={"next":"ams","Shib-Session-ID":"1111"})
+ called_args, _ = mock_redirect.call_args
+ mock_redirect.assert_called_with("/?next=ams")
+ 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
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ with patch("weko_accounts.views.RedisConnection",side_effect=BaseException("test_error")):
+ client.get(url, query_string={"next":"ams","Shib-Session-ID":"1111"})
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Server error has occurred. Please contact server " \
+ "administrator." in called_kwargs.get("ams_error", "")
#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):
- mocker.patch("weko_accounts.views.RedisConnection.connection",return_value=redis_connect)
+ mocker.patch("weko_accounts.views.RedisConnection.connection",
+ return_value=redis_connect)
mocker.patch("weko_accounts.views.generate_random_str",return_value="asdfghjkl")
url_base = url_for("weko_accounts.shib_login")
@@ -352,13 +703,32 @@ def test_shib_login(client,redis_connect,users,mocker):
client.get(url_base)
mock_flash.assert_called_with("Missing Shib-Session-ID!",category="error")
- url = url_base+"?Shib-Session-ID=2222"
+ # not shib_session_id(AMS)
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url_base+"?next=ams")
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing Shib-Session-ID!" in called_kwargs.get("ams_error", "")
+ url = url_base+"?Shib-Session-ID=2222"
# not exist cache_key
mock_flash = mocker.patch("weko_accounts.views.flash")
client.get(url)
mock_flash.assert_called_with("Missing SHIB_CACHE_PREFIX!",category="error")
+ # not exist cache_key(AMS)
+ mocker.patch("weko_accounts.views.RedisConnection.connection",
+ return_value=redis_connect)
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url+"&next=ams")
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing SHIB_CACHE_PREFIX!" in called_kwargs.get("ams_error", "")
+
url = url_base+"?Shib-Session-ID=1111"
# not cache_val
redis_connect.put("Shib-Session-1111",bytes('',"utf-8"))
@@ -367,23 +737,50 @@ def test_shib_login(client,redis_connect,users,mocker):
mock_flash.assert_called_with("Missing SHIB_ATTR!",category="error")
assert redis_connect.redis.exists("Shib-Session-1111") is False
+ # not cache_val(AMS)
+ redis_connect.put("Shib-Session-1111",bytes('',"utf-8"))
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ client.get(url+"&next=ams")
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Missing SHIB_ATTR!" in called_kwargs.get("ams_error", "")
+
# exist user
- redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn","shib_mail":"user@test.org"}',"utf-8"))
- mock_render = mocker.patch("weko_accounts.views.render_template",return_value=make_response())
+ redis_connect.put("Shib-Session-1111",
+ bytes('{"shib_eppn":"test_eppn","shib_mail":"user@test.org"}',"utf-8"))
+ mock_render = mocker.patch("weko_accounts.views.render_template",
+ return_value=make_response())
client.get(url)
- mock_render.assert_called_with('weko_accounts/confirm_user.html',csrf_random="asdfghjkl",email="user@test.org")
+ mock_render.assert_called_with('weko_accounts/confirm_user.html',
+ csrf_random="asdfghjkl",email="user@test.org")
# not exist user
- redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn","shib_mail":"not_exist_user@test.org"}',"utf-8"))
- mock_render = mocker.patch("weko_accounts.views.render_template",return_value=make_response())
+ redis_connect.put("Shib-Session-1111",
+ bytes('{"shib_eppn":"test_eppn",' \
+ '"shib_mail":"not_exist_user@test.org"}',"utf-8"))
+ mock_render = mocker.patch("weko_accounts.views.render_template",
+ return_value=make_response())
client.get(url)
- mock_render.assert_called_with('weko_accounts/confirm_user.html',csrf_random="asdfghjkl",email="")
+ mock_render.assert_called_with('weko_accounts/confirm_user.html',
+ csrf_random="asdfghjkl",email="")
# raise BaseException
with patch("weko_accounts.views.flash",side_effect=BaseException("test_error")):
res = client.get(url_base)
assert res.status_code == 400
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+ with patch("weko_accounts.views.RedisConnection",side_effect=BaseException("test_error")):
+ res = client.get(url_base+"?Shib-Session-ID=1111&next=ams")
+ mock_redirect_.assert_called_once()
+ called_args, called_kwargs = mock_redirect_.call_args
+ assert called_args[0] is True
+ assert "Server error has occurred. Please contact server " \
+ "administrator." in called_kwargs.get("ams_error", "")
+
#def shib_sp_login():
# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_shib_sp_login -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp
def test_shib_sp_login(client, redis_connect,mocker, db, users):
@@ -397,6 +794,14 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users):
mock_flash.assert_called_with("Missing Shib-Session-ID!",category="error")
mock_redirect.assert_called_with(url_for("security.login"))
+ # not shib_session_id(AMS)
+ mock_generate_ams_login_url = mocker.patch("weko_accounts.views.generate_ams_login_url",
+ return_value=make_response())
+ client.post(url+"?next=ams")
+ mock_generate_ams_login_url.assert_called_once()
+ called_args, _ = mock_generate_ams_login_url.call_args
+ assert "Missing Shib-Session-ID!" in called_args[0]
+
current_app.config.update(
WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True
)
@@ -410,6 +815,15 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users):
client.post(url,data=form)
mock_flash.assert_called_with("Missing SHIB_ATTRs!",category="error")
+ # parse_attribute is error(AMS)
+ with patch("weko_accounts.views.parse_attributes",return_value=("attr",True)):
+ mock_generate_ams_login_url = mocker.patch("weko_accounts.views.generate_ams_login_url",
+ return_value=make_response())
+ client.post(url+"?next=ams",data=form)
+ mock_generate_ams_login_url.assert_called_once()
+ called_args, _ = mock_generate_ams_login_url.call_args
+ assert "Missing SHIB_ATTRs!" in called_args[0]
+
# Check if shib_eppn is not included in the blocked user list
try:
db.session.add(AdminSettings(
@@ -418,7 +832,7 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users):
settings='{"blocked_ePPNs": ["ePPN1", "ePPN2", "ePPN3", "ePPN5", "ePPP*"]}'
))
db.session.commit()
- except Exception as e:
+ except Exception:
db.session.rollback()
raise
finally:
@@ -432,7 +846,16 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users):
}
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())
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
+
+ # Match with blocked user(AMS)
+ mock_generate_ams_login_url = mocker.patch("weko_accounts.views.generate_ams_login_url",
+ return_value=make_response())
+ client.post(url+"?next=ams",data=form)
+ mock_generate_ams_login_url.assert_called_once()
+ called_args, _ = mock_generate_ams_login_url.call_args
+ assert "Login is blocked." in called_args[0]
# Match found with a blocked user from the wildcard
mock_flash = mocker.patch("weko_accounts.views.flash")
@@ -442,7 +865,14 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users):
}
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(AMS)
+ mock_generate_ams_login_url = mocker.patch("weko_accounts.views.generate_ams_login_url",
+ return_value=make_response())
+ client.post(url+"?next=ams",data=form)
+ mock_generate_ams_login_url.assert_called_once()
+ called_args, _ = mock_generate_ams_login_url.call_args
+ assert "Login is blocked." in called_args[0]
# Not a blocked user
form = {
@@ -450,17 +880,24 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users):
"eppn":"test_eppn"
}
+ mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
# WEKO_ACCOUNTS_SHIB_BIND_GAKUNIN_MAP_GROUPSがTrueの場合のテスト
current_app.config.update(
WEKO_ACCOUNTS_SHIB_BIND_GAKUNIN_MAP_GROUPS=True
)
- mock_sync_shib_gakunin_map_groups = mocker.patch("weko_accounts.views.sync_shib_gakunin_map_groups", return_value=None)
+ mock_sync_shib_gakunin_map_groups = \
+ mocker.patch("weko_accounts.views.sync_shib_gakunin_map_groups",
+ return_value=None)
client.post(url, data=form)
mock_sync_shib_gakunin_map_groups.assert_called_once()
# sync_shib_gakunin_map_groupsが例外をスローする場合のテスト
- mock_sync_shib_gakunin_map_groups = mocker.patch("weko_accounts.views.sync_shib_gakunin_map_groups", side_effect=Exception("test_exception"))
- mock_redirect_method = mocker.patch("weko_accounts.views._redirect_method", return_value=make_response())
+ mock_sync_shib_gakunin_map_groups = mocker.patch(
+ "weko_accounts.views.sync_shib_gakunin_map_groups",
+ side_effect=Exception("test_exception"))
+ mock_redirect_method = mocker.patch("weko_accounts.views._redirect_method",
+ return_value=make_response())
res = client.post(url, data=form)
mock_redirect_method.assert_called_once()
assert res.status_code == 200 # _redirect_methodが呼び出されることを確認
@@ -474,22 +911,61 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users):
mock_sync_shib_gakunin_map_groups.assert_not_called()
# shib_user.get_relation_info is None
- with patch("weko_accounts.views.ShibUser.get_relation_info",return_value=None)\
- and patch("weko_accounts.views.redirect",return_value=make_response()):
+ with patch("weko_accounts.views.ShibUser.get_relation_info",
+ return_value=None)\
+ and patch("weko_accounts.views.redirect",
+ return_value=make_response()):
res = client.post(url,data=form)
assert res.status_code == 200
assert res.data.decode() == "/weko/shib/login?Shib-Session-ID=1111&next=%2F"
with client.session_transaction() as session:
assert 'shib_session_id' not in session
# 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()):
+ 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.data.decode() == "/weko/shib/login?Shib-Session-ID=1111&next=%2F"
with client.session_transaction() as session:
assert 'shib_session_id' not in session
+ # test without blocked_user_settings
+ AdminSettings.query.filter_by(id=11).delete()
+ db.session.commit()
+ with patch("weko_accounts.views.ShibUser.get_relation_info",
+ return_value=None)\
+ and patch("weko_accounts.views.redirect",
+ return_value=make_response()):
+ res = client.post(url,data=form)
+ assert res.status_code == 200
+ assert res.data.decode() == "/weko/shib/login?Shib-Session-ID=1111&next=%2F"
+ with client.session_transaction() as session:
+ assert 'shib_session_id' not in session
+
+ # test with blocked_user_setting dict
+ db.session.add(AdminSettings(
+ id=11,
+ name="blocked_user_settings",
+ settings={"blocked_ePPNs": ["ePPN1", "ePPN2", "ePPN3", "ePPN5", "ePPP*"]}
+ ))
+ db.session.commit()
+ mock_flash = mocker.patch("weko_accounts.views.flash")
+ form_blocked = {
+ "Shib-Session-ID":"1111",
+ "eppn":"ePPN3"
+ }
+ client.post(url,data=form_blocked)
+ mock_flash.assert_called_with("Failed to login.",category="error")
+ AdminSettings.query.filter_by(id=11).delete()
+ db.session.add(AdminSettings(
+ id=11,
+ name="blocked_user_settings",
+ settings='{"blocked_ePPNs": ["ePPN1", "ePPN2", "ePPN3", "ePPN5", "ePPP*"]}'
+ ))
+ db.session.commit()
+
current_app.config.update(
WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True,
WEKO_ACCOUNTS_SKIP_CONFIRMATION_PAGE=True
@@ -522,10 +998,25 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users):
# 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_:
+ and patch("weko_accounts.views._redirect_method",
+ return_value=make_response()) as mock_redirect_:
res = client.post(url,data={})
mock_redirect_.assert_called_once()
+ # raise BaseException(AMS)
+ mock_generate_ams_login_url = mocker.patch("weko_accounts.views.generate_ams_login_url",
+ return_value=make_response())
+ with patch("weko_accounts.views.RedisConnection",side_effect=BaseException("test_error")):
+ form = {
+ "Shib-Session-ID":"1111",
+ "eppn":"test_eppn"
+ }
+ res = client.post(url+"?next=ams",data=form)
+ mock_generate_ams_login_url.assert_called_once()
+ called_args, _ = mock_generate_ams_login_url.call_args
+ assert "Server error has occurred. Please contact server " \
+ "administrator." in called_args[0]
+
# all attributes have value and some shibboleth_user records don't have target eppn
current_app.config.update(
WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True,
@@ -715,7 +1206,6 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users):
assert res.status_code == 302
assert res.headers['Location'] == url_for("security.login", _external=True)
-
#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):
@@ -737,9 +1227,11 @@ def test_shib_stub_login(client,mocker):
WEKO_ACCOUNTS_SHIB_IDP_LOGIN_ENABLED=False
)
# WEKO_ACCOUNTS_SHIB_IDP_LOGIN_ENABLED is true
- mock_render_template = mocker.patch("weko_accounts.views.render_template",return_value=make_response())
+ mock_render_template = mocker.patch("weko_accounts.views.render_template",
+ return_value=make_response())
res = client.get(url)
- mock_render_template.assert_called_with('weko_accounts/login_shibuser_pattern_1.html',module_name="WEKO-Accounts")
+ mock_render_template.assert_called_with('weko_accounts/login_shibuser_pattern_1.html',
+ module_name="WEKO-Accounts")
#def shib_logout():
# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_shib_logout -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp
diff --git a/modules/weko-accounts/weko_accounts/api.py b/modules/weko-accounts/weko_accounts/api.py
index 3590f7275a..74173fca78 100644
--- a/modules/weko-accounts/weko_accounts/api.py
+++ b/modules/weko-accounts/weko_accounts/api.py
@@ -51,24 +51,32 @@ def __init__(self, shib_attr=None):
"""The :class:`.models.ShibbolethUser` instance."""
self.is_member_of = []
- if shib_attr.get('shib_is_member_of'):
+ if 'shib_is_member_of' in shib_attr:
is_member_of = shib_attr.get('shib_is_member_of', [])
if isinstance(is_member_of, str):
if is_member_of.find(';') != -1:
is_member_of = is_member_of.split(';')
- else:
+ elif is_member_of:
is_member_of = [is_member_of]
+ else:
+ is_member_of = []
+ elif not isinstance(is_member_of, list):
+ is_member_of = []
self.is_member_of = is_member_of
del shib_attr['shib_is_member_of']
self.organizations = []
- if shib_attr.get('shib_organization'):
+ if 'shib_organization' in shib_attr:
organizations = shib_attr.get('shib_organization', [])
if isinstance(organizations, str):
if organizations.find(';') != -1:
organizations = organizations.split(';')
- else:
+ elif organizations:
organizations = [organizations]
+ else:
+ organizations = []
+ elif not isinstance(organizations, list):
+ organizations = []
self.organizations = organizations
del shib_attr['shib_organization']
diff --git a/modules/weko-accounts/weko_accounts/config.py b/modules/weko-accounts/weko_accounts/config.py
index 6ee4afaf20..d39fec87da 100644
--- a/modules/weko-accounts/weko_accounts/config.py
+++ b/modules/weko-accounts/weko_accounts/config.py
@@ -61,6 +61,8 @@
WEKO_ACCOUNTS_SHIB_IDP_LOGIN_URL = '{}secure/login.py'
"""Login proxy URL."""
+WEKO_ACCOUNTS_SHIB_AMS_LOGIN_URL = '{}ams/login'
+
WEKO_ACCOUNTS_SSO_ATTRIBUTE_MAP = {
'SHIB_ATTR_EPPN': (False, 'shib_eppn'),
# 'SHIB_ATTR_LOGIN_ID': (False, 'shib_uid'),
@@ -101,15 +103,17 @@
'givenName',
'eduPersonAffiliation',
'eduPersonScopedAffiliation',
- 'eduPersonTargetedID'
+ 'eduPersonTargetedID',
+ 'HTTP_WEKOID',
+ 'HTTP_WEKOSOCIETYAFFILIATION'
]
"""Attribute List."""
WEKO_ACCOUNTS_ROLE_LIST = [
- 'System Administrator',
- 'Repository Administrator',
- 'Community Administrator',
- 'Contributor',
+ 'System Administrator',
+ 'Repository Administrator',
+ 'Community Administrator',
+ 'Contributor',
'None'
]
"""Role List."""
@@ -119,26 +123,26 @@
WEKO_ACCOUNTS_GAKUNIN_ROLE = {
'defaultRole': 'Contributor',
- 'organizationName': []
-}
+ 'organizationName': []
+}
"""Gakunin Default role."""
WEKO_ACCOUNTS_ORTHROS_INSIDE_ROLE = {
'defaultRole': 'Repository Administrator',
- 'organizationName': []
-}
+ 'organizationName': []
+}
"""Orthros (Inside) Default role."""
WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE = {
'defaultRole': 'Community Administrator',
- 'organizationName': []
-}
+ 'organizationName': []
+}
"""Orthros (Outside) Default role."""
WEKO_ACCOUNTS_EXTRA_ROLE = {
'defaultRole': 'None', # ロール無
- 'organizationName': []
-}
+ 'organizationName': []
+}
"""Extra Default role."""
WEKO_ACCOUNTS_SHIB_ROLE_RELATION = {
diff --git a/modules/weko-accounts/weko_accounts/rest.py b/modules/weko-accounts/weko_accounts/rest.py
index 0fb97c1436..01911db352 100644
--- a/modules/weko-accounts/weko_accounts/rest.py
+++ b/modules/weko-accounts/weko_accounts/rest.py
@@ -116,6 +116,12 @@ def post_v1(self, **kwargs):
data = request.get_json()
email = data['email']
password = data['password']
+ if email:
+ email = email.strip()
+ email = email.strip('\u200b') # remove zero-width spaces
+ if password:
+ password = password.strip()
+ password = password.strip('\u200b') # remove zero-width spaces
# Check if user is already logged in
if current_user.is_authenticated:
diff --git a/modules/weko-accounts/weko_accounts/templates/weko_accounts/login_shibuser_pattern_1.html b/modules/weko-accounts/weko_accounts/templates/weko_accounts/login_shibuser_pattern_1.html
index 2ff28636f5..521d2d9b48 100644
--- a/modules/weko-accounts/weko_accounts/templates/weko_accounts/login_shibuser_pattern_1.html
+++ b/modules/weko-accounts/weko_accounts/templates/weko_accounts/login_shibuser_pattern_1.html
@@ -26,7 +26,7 @@
diff --git a/modules/weko-accounts/weko_accounts/templates/weko_accounts/login_user.html b/modules/weko-accounts/weko_accounts/templates/weko_accounts/login_user.html
index 5ae2666a90..1d3c4f3e38 100644
--- a/modules/weko-accounts/weko_accounts/templates/weko_accounts/login_user.html
+++ b/modules/weko-accounts/weko_accounts/templates/weko_accounts/login_user.html
@@ -256,6 +256,9 @@
{{_('Log in to account') }}
// var wayf_additional_idps = [ ];
// Example of how to add Identity Provider from other federations
+ var wayf_additional_idps = [
+ {name:"Orthros-Test",entityID:"https://core-stg.orthros.gakunin.nii.ac.jp/idp"}
+ ];
// var wayf_additional_idps = [
//
// {name:"International University X",
@@ -273,6 +276,7 @@
{{_('Log in to account') }}
// [Optional, commented out by default]
// var wayf_discofeed_url = "";
+
// The path of Cookie created by SP is set. As for default configuration,
// the path of an access place is set.
// var wayf_sp_cookie_path = "/";
@@ -310,7 +314,7 @@
{{_('Log in to account') }}
diff --git a/modules/weko-accounts/weko_accounts/utils.py b/modules/weko-accounts/weko_accounts/utils.py
index eae9eab191..7dd2ede02a 100644
--- a/modules/weko-accounts/weko_accounts/utils.py
+++ b/modules/weko-accounts/weko_accounts/utils.py
@@ -30,7 +30,6 @@
import hashlib
from .config import WEKO_API_LIMIT_RATE_DEFAULT
-from weko_admin.models import AdminSettings
limiter = Limiter(
app=None,
@@ -111,6 +110,7 @@ def parse_attributes():
error = False
# Get attribute mapping from admin settings
+ from weko_admin.models import AdminSettings
admin_settings = AdminSettings.get('attribute_mapping', dict_to_object=False)
for header, attr in current_app.config[
@@ -221,7 +221,7 @@ def decorated_view(*args, **kwargs):
def get_sp_info():
"""Get Service Provider (SP) information for Shibboleth login.
-
+
Returns:
dict: A dictionary containing SP entityID, handlerURL, and return URL.
"""
@@ -233,7 +233,7 @@ def get_sp_info():
sp_entityID = 'https://' + current_app.config['WEB_HOST_NAME'] + '/shibboleth-sp'
if 'SP_ENTITYID' in current_app.config:
sp_entityID = current_app.config['SP_ENTITYID']
-
+
sp_handlerURL = 'https://' + current_app.config['WEB_HOST_NAME'] + '/Shibboleth.sso'
if 'SP_HANDLERURL' in current_app.config:
sp_handlerURL = current_app.config['SP_HANDLERURL']
diff --git a/modules/weko-accounts/weko_accounts/views.py b/modules/weko-accounts/weko_accounts/views.py
index a26e313928..ccd9bcb554 100644
--- a/modules/weko-accounts/weko_accounts/views.py
+++ b/modules/weko-accounts/weko_accounts/views.py
@@ -17,6 +17,7 @@
# along with WEKO3; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307, USA.
+#
"""Views for weko-accounts.
@@ -143,7 +144,7 @@ def _adjust_shib_admin_DB():
db.session.commit()
-def _redirect_method(has_next=False):
+def _redirect_method(has_next=False, ams_error=None):
"""Redirect method for instance login to IdP."""
shib_login = current_app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED']
shib_login_url = current_app.config['WEKO_ACCOUNTS_SHIB_IDP_LOGIN_URL']
@@ -151,7 +152,13 @@ def _redirect_method(has_next=False):
idp_login_inst = current_app.config[
'WEKO_ACCOUNTS_SHIB_INST_LOGIN_DIRECTLY_ENABLED']
- if shib_login and idp_login and idp_login_inst:
+ if ams_error:
+ shib_ams_login_url = current_app.config['WEKO_ACCOUNTS_SHIB_AMS_LOGIN_URL']
+ ams_url = shib_ams_login_url.format(request.url_root)
+ encoded_error = quote_plus(str(ams_error), encoding='utf-8', errors='replace')
+ url = f'{ams_url}?error={encoded_error}'
+ return redirect(url)
+ elif shib_login and idp_login and idp_login_inst:
url = shib_login_url.format(request.url_root)
if has_next:
url += '?next=' + request.full_path
@@ -162,6 +169,28 @@ def _redirect_method(has_next=False):
next=request.full_path if has_next else None))
+def generate_ams_login_url(ams_error):
+ """
+ Generate a redirect URL for the AMS login page with error details.
+
+ The returned URL starts with a '/'.
+
+ Args:
+ ams_error (str): Error message for the AMS login page to include
+ in the redirect URL.
+
+ Returns:
+ str: URL for the AMS login page including the error
+ information as a query parameter.
+ """
+ shib_ams_login_url = current_app.config['WEKO_ACCOUNTS_SHIB_AMS_LOGIN_URL']
+ ams_url = shib_ams_login_url.format('/')
+ encoded_error = quote_plus(
+ str(ams_error), encoding='utf-8', errors='replace')
+ url = f'{ams_url}?error={encoded_error}'
+ return url
+
+
@blueprint.route('/')
def index():
"""Render a basic view."""
@@ -176,29 +205,42 @@ def shib_auto_login():
:return: next url
"""
+ ams_login = False
try:
is_auto_bind = False
shib_session_id = request.args.get('Shib-Session-ID', None)
- session['next'] = request.args.get('next', '/')
-
+ next_value = request.args.get('next')
+ if next_value != 'ams':
+ next_value = session.get('next', '/')
+ session['next'] = next_value
+ ams_login = next_value == 'ams'
if not shib_session_id:
shib_session_id = session['shib_session_id']
is_auto_bind = True
if not shib_session_id:
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing Shib-Session-ID!'))
+ else:
+ return _redirect_method()
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
if not datastore.redis.exists(cache_key):
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing SHIB_CACHE_PREFIX!'))
+ else:
+ return _redirect_method()
cache_val = datastore.get(cache_key)
if not cache_val:
datastore.delete(cache_key)
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing SHIB_ATTR!'))
+ else:
+ return _redirect_method()
cache_val = json.loads(str(cache_val, encoding='utf-8'))
shib_user = ShibUser(cache_val)
@@ -212,8 +254,11 @@ def shib_auto_login():
if error:
datastore.delete(cache_key)
current_app.logger.error(error)
- flash(error, category='error')
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=error)
+ else:
+ flash(error, category='error')
+ return _redirect_method()
if shib_user.shib_user:
shib_user.shib_user_login()
@@ -225,7 +270,10 @@ def shib_auto_login():
target_key=shib_user.user.id,
remarks="Shibboleth login"
)
- return redirect(session['next'] if 'next' in session else '/')
+ if ams_login:
+ return redirect('/?next=ams')
+ else:
+ return redirect(session['next'] if 'next' in session else '/')
except BaseException:
db.session.rollback()
current_app.logger.error("Unexpected error: {}".format(sys.exc_info()))
@@ -235,6 +283,9 @@ def shib_auto_login():
operation="LOGIN",
remarks=tb_info[0]
)
+ if ams_login:
+ return _redirect_method(True, ams_error=_(
+ 'Server error has occurred. Please contact server administrator.'))
return abort(400)
@@ -244,15 +295,25 @@ def confirm_user():
:return:
"""
+ ams_login = False
try:
+ next_value = session['next'] if 'next' in session else ''
+ if next_value == 'ams':
+ ams_login = True
if request.form.get('csrf_random', '') != session['csrf_random']:
- flash('csrf_random', category='error')
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('csrf_random'))
+ else:
+ flash('csrf_random', category='error')
+ return _redirect_method()
shib_session_id = session['shib_session_id']
if not shib_session_id:
- flash('shib_session_id', category='error')
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing Shib-Session-ID!'))
+ else:
+ flash('shib_session_id', category='error')
+ return _redirect_method()
redis_connection = RedisConnection()
datastore = redis_connection.connection(db=current_app.config['CACHE_REDIS_DB'], kv = True)
@@ -260,34 +321,55 @@ def confirm_user():
'WEKO_ACCOUNTS_SHIB_CACHE_PREFIX'] + shib_session_id
if not datastore.redis.exists(cache_key):
- flash('cache_key', category='error')
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing SHIB_CACHE_PREFIX!'))
+ else:
+ 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()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing SHIB_ATTR!'))
+ else:
+ 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)
account = request.form.get('WEKO_ATTR_ACCOUNT', None)
password = request.form.get('WEKO_ATTR_PWD', None)
+ if account:
+ account = account.strip()
+ account = account.strip('\u200b') # remove zero-width spaces
+ if password:
+ password = password.strip()
+ password = password.strip('\u200b') # remove zero-width spaces
if not shib_user.check_weko_user(account, password):
- flash('check_weko_user', category='error')
- datastore.delete(cache_key)
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('There is no user information.'))
+ else:
+ flash('check_weko_user', category='error')
+ datastore.delete(cache_key)
+ return _redirect_method()
if not shib_user.bind_relation_info(account):
- flash('FAILED bind_relation_info!', category='error')
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('FAILED bind_relation_info!'))
+ else:
+ flash('FAILED bind_relation_info!', category='error')
+ return _redirect_method()
error = shib_user.check_in()
if error:
datastore.delete(cache_key)
- flash(error, category='error')
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=error)
+ else:
+ flash(error, category='error')
+ return _redirect_method()
if shib_user.shib_user:
shib_user.shib_user_login()
@@ -297,7 +379,10 @@ def confirm_user():
target_key=shib_user.user.id,
remarks="Shibboleth login"
)
- return redirect(session['next'] if 'next' in session else '/')
+ if ams_login:
+ return redirect('/?next=ams')
+ else:
+ return redirect(session['next'] if 'next' in session else '/')
except BaseException:
current_app.logger.error("Unexpected error: {}".format(sys.exc_info()))
exec_info = sys.exc_info()
@@ -306,6 +391,9 @@ def confirm_user():
operation="LOGIN",
remarks=tb_info[0]
)
+ if ams_login:
+ return _redirect_method(True, ams_error=_(
+ 'Server error has occurred. Please contact server administrator.'))
return abort(400)
@@ -315,12 +403,20 @@ def confirm_user_without_page():
:return:
"""
+ ams_login = False
try:
+ next_value = request.args.get('next', '')
+ if next_value == 'ams':
+ ams_login = True
+
# get shib_session_id from session
shib_session_id = request.args.get('Shib-Session-ID', None)
if not shib_session_id:
- flash('shib_session_id', category='error')
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing Shib-Session-ID!'))
+ else:
+ flash('shib_session_id', category='error')
+ return _redirect_method()
# get cache from redis
redis_connection = RedisConnection()
@@ -330,38 +426,56 @@ def confirm_user_without_page():
# check cache
if not datastore.redis.exists(cache_key):
- flash('cache_key', category='error')
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing SHIB_CACHE_PREFIX!'))
+ else:
+ 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()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing SHIB_ATTR!'))
+ else:
+ 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()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('FAILED bind_relation_info!'))
+ else:
+ flash('FAILED bind_relation_info!', category='error')
+ return _redirect_method()
# check in
error = shib_user.check_in()
if error:
datastore.delete(cache_key)
- flash(error, category='error')
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=error)
+ else:
+ flash(error, category='error')
+ return _redirect_method()
if shib_user.shib_user:
shib_user.shib_user_login()
datastore.delete(cache_key)
- return redirect(request.args.get('next', '/'))
+ if ams_login:
+ return redirect('/?next=ams')
+ else:
+ return redirect(request.args.get('next', '/'))
except BaseException:
current_app.logger.error("Unexpected error: {}".format(sys.exc_info()))
+ if ams_login:
+ return _redirect_method(True, ams_error=_(
+ 'Server error has occurred. Please contact server administrator.'))
return abort(400)
@@ -371,14 +485,19 @@ def shib_login():
:return: confirm user page when relation is empty
"""
+ ams_login = False
try:
shib_session_id = request.args.get('Shib-Session-ID', None)
session['next'] = request.args.get('next', '/')
-
+ if session['next'] == 'ams':
+ ams_login = True
if not shib_session_id:
- current_app.logger.error(_("Missing Shib-Session-ID!"))
- flash(_("Missing Shib-Session-ID!"), category='error')
- return _redirect_method()
+ current_app.logger.error(_('Missing Shib-Session-ID!'))
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing Shib-Session-ID!'))
+ else:
+ flash(_('Missing Shib-Session-ID!'), category='error')
+ return _redirect_method()
redis_connection = RedisConnection()
datastore = redis_connection.connection(db=current_app.config['CACHE_REDIS_DB'], kv = True)
@@ -386,17 +505,23 @@ def shib_login():
'WEKO_ACCOUNTS_SHIB_CACHE_PREFIX'] + shib_session_id
if not datastore.redis.exists(cache_key):
- current_app.logger.error(_("Missing SHIB_CACHE_PREFIX!"))
- flash(_("Missing SHIB_CACHE_PREFIX!"), category='error')
- return _redirect_method()
+ current_app.logger.error(_('Missing SHIB_CACHE_PREFIX!'))
+ flash(_('Missing SHIB_CACHE_PREFIX!'), category='error')
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing SHIB_CACHE_PREFIX!'))
+ else:
+ return _redirect_method()
cache_val = datastore.get(cache_key)
if not cache_val:
- current_app.logger.error(_("Missing SHIB_ATTR!"))
- flash(_("Missing SHIB_ATTR!"), category='error')
+ current_app.logger.error(_('Missing SHIB_ATTR!'))
datastore.delete(cache_key)
- return _redirect_method()
+ if ams_login:
+ return _redirect_method(True, ams_error=_('Missing SHIB_ATTR!'))
+ else:
+ flash(_('Missing SHIB_ATTR!'), category='error')
+ return _redirect_method()
cache_val = json.loads(str(cache_val, encoding='utf-8'))
session['shib_session_id'] = shib_session_id
@@ -412,6 +537,9 @@ def shib_login():
)
except BaseException:
current_app.logger.error("Unexpected error: {}".format(sys.exc_info()))
+ if ams_login:
+ return _redirect_method(True, ams_error=_(
+ 'Server error has occurred. Please contact server administrator.'))
return abort(400)
def find_user_by_email(shib_attributes):
@@ -432,7 +560,9 @@ def shib_sp_login():
_shib_username_config = current_app.config[
'WEKO_ACCOUNTS_SHIB_ALLOW_USERNAME_INST_EPPN']
next = request.args.get('next', '/')
-
+ ams_login = False
+ if next == 'ams':
+ ams_login = True
try:
# WEKO_ACCOUNTS_SHIB_BIND_GAKUNIN_MAP_GROUPSがTrueのときの処理
if current_app.config['WEKO_ACCOUNTS_SHIB_BIND_GAKUNIN_MAP_GROUPS']:
@@ -440,8 +570,11 @@ def shib_sp_login():
shib_session_id = request.form.get('Shib-Session-ID', None)
if not shib_session_id and not _shib_enable:
- flash(_("Missing Shib-Session-ID!"), category='error')
- return redirect(url_for_security('login'))
+ if ams_login:
+ return generate_ams_login_url(_('Missing Shib-Session-ID!'))
+ else:
+ flash(_('Missing Shib-Session-ID!'), category='error')
+ return redirect(url_for_security('login'))
shib_attr, error = parse_attributes()
@@ -449,8 +582,11 @@ def shib_sp_login():
if error or not (
shib_attr.get('shib_eppn', None)
or _shib_username_config and shib_attr.get('shib_user_name')):
- flash(_("Missing SHIB_ATTRs!"), category='error')
- return _redirect_method()
+ if ams_login:
+ return generate_ams_login_url(_('Missing SHIB_ATTRs!'))
+ else:
+ 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():
@@ -468,8 +604,11 @@ def _wildcard_to_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()
+ if ams_login:
+ return generate_ams_login_url(_('Login is blocked.'))
+ else:
+ flash(_('Failed to login.'), category='error')
+ return _redirect_method()
# Redis connection
redis_connection = RedisConnection()
@@ -509,7 +648,11 @@ def _wildcard_to_regex(pattern):
return url_for(next_url, **query_string)
except BaseException:
current_app.logger.error("Unexpected error: {}".format(sys.exc_info()))
- return _redirect_method()
+ if ams_login:
+ return generate_ams_login_url(
+ _('Server error has occurred. Please contact server administrator.'))
+ else:
+ return _redirect_method()
@blueprint.route('/shib/sp/login', methods=['GET'])
diff --git a/modules/weko-admin/weko_admin/static/js/weko_admin/sword_api_jsonld_setting.js b/modules/weko-admin/weko_admin/static/js/weko_admin/sword_api_jsonld_setting.js
index eaf74888f6..800acf6875 100644
--- a/modules/weko-admin/weko_admin/static/js/weko_admin/sword_api_jsonld_setting.js
+++ b/modules/weko-admin/weko_admin/static/js/weko_admin/sword_api_jsonld_setting.js
@@ -387,7 +387,7 @@ function saveDataFormat(type) {
if (type === 'add') {
url = '/admin/swordapi/jsonld/add/';
} else {
- url = '/admin/swordapi/jsonld/edit/' + current_model_json['id'];
+ url = '/admin/swordapi/jsonld/edit/' + current_model_json['id'] + '/';
}
fetch(url, {
method: 'POST',
diff --git a/modules/weko-deposit/requirements2.txt b/modules/weko-deposit/requirements2.txt
index 3c30c7d8a7..f203b6675a 100644
--- a/modules/weko-deposit/requirements2.txt
+++ b/modules/weko-deposit/requirements2.txt
@@ -287,3 +287,4 @@ xmlschema==0.9.30
xmltodict==0.12.0
zipp==3.6.0
zope.interface==5.5.2
+pypdfium2==4.30.0
diff --git a/modules/weko-deposit/tests/data/test_files/sample_word.docx b/modules/weko-deposit/tests/data/test_files/sample_word.docx
new file mode 100644
index 0000000000..6de626ae06
Binary files /dev/null and b/modules/weko-deposit/tests/data/test_files/sample_word.docx differ
diff --git a/modules/weko-deposit/tests/test_utils.py b/modules/weko-deposit/tests/test_utils.py
index a48feffb22..2b017a018e 100644
--- a/modules/weko-deposit/tests/test_utils.py
+++ b/modules/weko-deposit/tests/test_utils.py
@@ -19,12 +19,14 @@
# MA 02111-1307, USA.
from weko_deposit.api import WekoDeposit
-from weko_deposit.utils import update_pdf_contents_es
+from weko_deposit.utils import update_pdf_contents_es, extract_text_from_pdf, extract_text_with_tika
from mock import patch
+from mock import MagicMock
import uuid
from tests.helpers import create_record_with_pdf
-
+import os
+import pytest
# .tox/c1/bin/pytest --cov=weko_deposit tests/test_utils.py::test_update_pdf_contents_es -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-deposit/.tox/c1/tmp
def test_update_pdf_contents_es(app, db, location, mocker):
@@ -46,4 +48,57 @@ def test_update_pdf_contents_es(app, db, location, mocker):
for args, _ in args_list:
test = pdf_file_infos[i]
assert args[0] == (test,str(record_ids[i]))
- i+=1
\ No newline at end of file
+ i+=1
+
+
+# .tox/c1/bin/pytest --cov=weko_deposit tests/test_utils.py::test_extract_text_from_pdf -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-deposit/.tox/c1/tmp
+def test_extract_text_from_pdf():
+ filepath = os.path.join(os.path.dirname(__file__),"data","test_files","test_file_1.2M.pdf")
+
+ # file size > max_size
+ data = extract_text_from_pdf(filepath, 10000)
+ assert len(data.encode('utf-8')) <= 10000
+ assert data.count("test file page") < 1240
+
+ # file size <= max_size
+ data = extract_text_from_pdf(filepath, 100000000)
+ assert len(data.encode('utf-8')) == 19561
+ assert data.count("test file page") == 1240
+
+ # not exist file
+ filepath = "not_exist_file.pdf"
+ with pytest.raises(FileNotFoundError) as e:
+ data = extract_text_from_pdf(filepath, 10000)
+ assert str(e.value) == "/code/modules/weko-deposit/not_exist_file.pdf"
+
+
+# .tox/c1/bin/pytest --cov=weko_deposit tests/test_utils.py::test_extract_text_with_tika -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-deposit/.tox/c1/tmp
+def test_extract_text_with_tika():
+ filepath = os.path.join(os.path.dirname(__file__),"data","test_files","sample_word.docx")
+ # not exist tika jar file.
+ mock_env_not_exist_tika = {"TIKA_JAR_FILE_PATH": "/not/exist/path/tika-server.jar"}
+ with patch.dict(os.environ, mock_env_not_exist_tika, clear=False):
+ with pytest.raises(Exception) as e:
+ data = extract_text_with_tika(filepath, 100)
+ assert str(e.value) == "not exist tika jar file."
+
+ mock_env_not_exist_tika = {"TIKA_JAR_FILE_PATH": "/code/tika/tika-app-2.6.0.jar"}
+ with patch.dict(os.environ, mock_env_not_exist_tika, clear=False):
+ # error with subprocess
+ mock_run = MagicMock()
+ mock_run.returncode.return_value=1
+ mock_run.stderr.decode.return_value="test_error"
+ with patch("weko_deposit.utils.subprocess.run", return_value=mock_run):
+ with pytest.raises(Exception) as e:
+ data = extract_text_with_tika(filepath, 100)
+ assert str(e.value) == "raise in tika: test_error"
+
+ # file size > max_size
+ data = extract_text_with_tika(filepath, 50)
+ assert len(data.encode('utf-8')) < 50
+ assert data == "これはテスト用のサンプルwordファイ"
+
+ # file size <= max_size
+ data = extract_text_with_tika(filepath, 5000)
+ assert len(data.encode('utf-8')) > 50
+ assert data == "これはテスト用のサンプルwordファイルです中身は特に意味がありません"
diff --git a/modules/weko-deposit/weko_deposit/config.py b/modules/weko-deposit/weko_deposit/config.py
index 52b9d7608c..dc211fdff5 100644
--- a/modules/weko-deposit/weko_deposit/config.py
+++ b/modules/weko-deposit/weko_deposit/config.py
@@ -37,7 +37,14 @@
'text/tab-separated-values',
'text/xml',
'application/x-tex',
- 'application/x-latex'
+ 'application/x-latex',
+ 'text/markdown',
+ 'application/json',
+ 'application/ld+json',
+ 'text/vnd.yaml',
+ 'text/yaml',
+ 'text/x-yaml',
+ 'application/x-yaml'
]
WEKO_MIMETYPE_WHITELIST_FOR_ES = [
@@ -51,6 +58,7 @@
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.oasis.opendocument.presentation',
'application/pdf',
+ 'application/rtf',
] + WEKO_DEPOSIT_TEXTMIMETYPE_WHITELIST_FOR_ES
diff --git a/modules/weko-deposit/weko_deposit/utils.py b/modules/weko-deposit/weko_deposit/utils.py
index a47761dd9e..0c7cd5a1d1 100644
--- a/modules/weko-deposit/weko_deposit/utils.py
+++ b/modules/weko-deposit/weko_deposit/utils.py
@@ -20,6 +20,10 @@
from .tasks import extract_pdf_and_update_file_contents
from .api import WekoDeposit
+import pypdfium2
+import os
+import subprocess
+
def update_pdf_contents_es(record_ids):
"""register the contents of the record PDF file in elasticsearch
@@ -29,4 +33,73 @@ def update_pdf_contents_es(record_ids):
deposits = WekoDeposit.get_records(record_ids)
for dep in deposits:
file_infos = dep.get_pdf_info()
- extract_pdf_and_update_file_contents.apply_async((file_infos, str(dep.id)))
\ No newline at end of file
+ extract_pdf_and_update_file_contents.apply_async((file_infos, str(dep.id)))
+
+
+def extract_text_from_pdf(filepath, max_size):
+ """Read PDF file and extract text.
+
+ Args:
+ filepath (str): Path to the PDF file.
+ max_size (int): Maximum size of the extracted text in bytes.
+
+ Returns:
+ str: Extracted text from the PDF file.
+
+ """
+ reader = None
+ data = ""
+ try:
+ reader = pypdfium2.PdfDocument(filepath)
+ texts = []
+ total_bytes = 0
+ for page in reader:
+ text = page.get_textpage().get_text_range()
+ encoded = text.encode('utf-8', errors='replace')
+ if total_bytes + len(encoded) > max_size:
+ remain = max_size - total_bytes
+ texts.append(encoded[:remain].decode('utf-8', errors='ignore'))
+ break
+ else:
+ texts.append(text)
+ total_bytes += len(encoded)
+ data = "".join(texts)
+ data = "".join(data.splitlines())
+ finally:
+ if reader is not None:
+ reader.close()
+
+ return data
+
+
+def extract_text_with_tika(filepath, max_size):
+ """Read non-PDF file and extract text.
+
+ Args:
+ filepath (str): Path to the PDF file.
+ max_size (int): Maximum size of the extracted text in bytes.
+
+ Raises:
+ Exception: If Tika jar file does not exist.
+ Exception: If Tika processing fails.
+
+ Returns:
+ str: Extracted text from the non-PDF file.
+ """
+ tika_jar_path = os.environ.get("TIKA_JAR_FILE_PATH")
+ if not tika_jar_path or os.path.isfile(tika_jar_path) is False:
+ raise Exception("not exist tika jar file.")
+ args = ["java", "-jar", tika_jar_path, "-t", filepath]
+ result = subprocess.run(
+ args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ if result.returncode != 0:
+ raise Exception("raise in tika: {}".format(result.stderr.decode("utf-8")))
+ data = "".join(result.stdout.decode("utf-8").splitlines())
+ if len(data.encode('utf-8')) > max_size:
+ encoded = data.encode('utf-8')
+ data = encoded[:max_size].decode('utf-8', errors='ignore')
+
+ return data
diff --git a/modules/weko-index-tree/tests/test_rest.py b/modules/weko-index-tree/tests/test_rest.py
index 562d2d8c5c..17c374867c 100644
--- a/modules/weko-index-tree/tests/test_rest.py
+++ b/modules/weko-index-tree/tests/test_rest.py
@@ -1347,3 +1347,14 @@ def run_delete_index_server_error(self, app, client_rest, auth_headers):
with patch.object(Indexes, "delete", side_effect=SQLAlchemyError):
response = client_rest.delete(url, headers=auth_headers)
assert response.status_code == 500, f"{response.json}"
+
+# class GetIndex:
+# .tox/c1/bin/pytest --cov=weko_index_tree tests/test_rest.py::TestGetIndex -vv -s --cov-branch --cov-report=term --cov-report=html --basetemp=/code/modules/weko_index_tree/.tox/c1/tmp --full-trace
+class TestGetIndex:
+ # .tox/c1/bin/pytest --cov=weko_index_tree tests/test_rest.py::TestGetIndex::test_get_v1 -vv -s --cov-branch --cov-report=term --cov-report=html --basetemp=/code/modules/weko_index_tree/.tox/c1/tmp --full-trace
+ def test_get_v1(self, client_rest, users, test_indices, auth_headers_sysadmin):
+ res = client_rest.get('/v1/tree/index/1', headers=auth_headers_sysadmin)
+ assert res.status_code == 200
+ data = json.loads(res.get_data())
+ assert data['index']['name'] == 'Test index 1'
+ assert data['index']['public_date'] == '20220101'
diff --git a/modules/weko-index-tree/weko_index_tree/rest.py b/modules/weko-index-tree/weko_index_tree/rest.py
index bfadf5c0c0..d881fe6231 100644
--- a/modules/weko-index-tree/weko_index_tree/rest.py
+++ b/modules/weko-index-tree/weko_index_tree/rest.py
@@ -651,6 +651,21 @@ def get(self, **kwargs):
raise VersionNotFoundRESTError()
def get_v1(self, **kwargs):
+
+ def json_serialize(obj):
+ """Serialize object to JSON.
+
+ Args:
+ obj: The object to serialize.
+
+ Returns:
+ str: The serialized JSON string.
+ """
+ if isinstance(obj, (datetime, date)):
+ return obj.strftime("%Y%m%d")
+ else:
+ return str(obj)
+
try:
pid = kwargs.get('index_id')
@@ -707,7 +722,8 @@ def get_v1(self, **kwargs):
# Create Response
res = Response(
- response=json.dumps(result_tree, indent=indent),
+ response=json.dumps(
+ result_tree, indent=indent, default=json_serialize),
status=200,
content_type='application/json')
res.set_etag(etag)
diff --git a/modules/weko-records-ui/weko_records_ui/config.py b/modules/weko-records-ui/weko_records_ui/config.py
index 52083eea6e..f426fdafbb 100644
--- a/modules/weko-records-ui/weko_records_ui/config.py
+++ b/modules/weko-records-ui/weko_records_ui/config.py
@@ -68,7 +68,7 @@
OPEN_DATE_HIDE_VALUE = '0'
# setting the release date if display
-DISPLAY_REQUEST_FORM = False
+DISPLAY_REQUEST_FORM = True
# Default setting whether to display the request form
# CSL Citation Formatter
diff --git a/modules/weko-records-ui/weko_records_ui/rest.py b/modules/weko-records-ui/weko_records_ui/rest.py
index 921ba83497..5bcdc7513e 100644
--- a/modules/weko-records-ui/weko_records_ui/rest.py
+++ b/modules/weko-records-ui/weko_records_ui/rest.py
@@ -26,6 +26,7 @@
from flask import Blueprint, current_app, jsonify, make_response, request, Response
from flask_babelex import get_locale
from flask_babelex import gettext as _
+from flask_login import current_user
from werkzeug.http import generate_etag
from redis import RedisError
from invenio_oauth2server import require_api_auth, require_oauth_scopes
@@ -53,8 +54,11 @@
from .views import escape_str
from .permissions import page_permission_factory, file_permission_factory
-from .errors import AvailableFilesNotFoundRESTError, ContentsNotFoundError, InvalidRequestError, VersionNotFoundRESTError, InternalServerError, \
- RecordsNotFoundRESTError, PermissionError, DateFormatRESTError, FilesNotFoundRESTError, ModeNotFoundRESTError, RequiredItemNotExistError
+from .errors import AvailableFilesNotFoundRESTError, ContentsNotFoundError, \
+ InvalidRequestError, VersionNotFoundRESTError, InternalServerError, \
+ RecordsNotFoundRESTError, PermissionError, DateFormatRESTError, \
+ FilesNotFoundRESTError, ModeNotFoundRESTError, RequiredItemNotExistError, \
+ AuthenticationRequiredError
from .scopes import file_read_scope
@@ -343,7 +347,10 @@ def get_v1(self, **kwargs):
# Check Permission
if not page_permission_factory(record).can():
- raise PermissionError()
+ if current_user.is_authenticated:
+ raise PermissionError()
+ else:
+ raise AuthenticationRequiredError()
# Convert RO-Crate format
from .utils import RoCrateConverter
@@ -388,8 +395,10 @@ def get_v1(self, **kwargs):
return res
- except (PermissionError, SameContentException) as e:
- raise e
+ except (PermissionError,
+ SameContentException,
+ AuthenticationRequiredError) as e:
+ raise e
except PIDDoesNotExistError:
raise RecordsNotFoundRESTError()
diff --git a/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/output_detail_data.html b/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/output_detail_data.html
index 0c5c412a5c..b1c7ed794b 100644
--- a/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/output_detail_data.html
+++ b/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/output_detail_data.html
@@ -72,7 +72,7 @@
{%- endfor -%}
{%- else -%}
{{ output_attribute_value_mlt_Init( record_detail_data ) }}
- {%- endif -%}
+ {%- endif -%}
{%- endif -%}
{% endmacro %}
@@ -83,7 +83,7 @@
{% if parrent_name %}
{%- set labels = parrent_name.split('.') -%}
{%- if labels|length == 1 -%}
- {{ child_data(parrent_name, '', level) }}
+ {{ child_data(parrent_name, ' ', level) }}
{%- else -%}
{%- set displayflag = False -%}
{%- endif -%}
@@ -118,7 +118,7 @@