diff --git a/.gitignore b/.gitignore index f1e5019509..dc7245b41e 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,6 @@ test/data test/import.zip test/dummyfile/import.zip test/dummyfile/data + +# inbox +inbox/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 7a73a41bfd..2c63c08510 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,2 +1,2 @@ { -} \ No newline at end of file +} diff --git a/docker-compose.yml b/docker-compose.yml index f222a69341..27049567b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,16 +3,16 @@ # This file is part of WEKO3. # Copyright (C) 2017 National Institute of Informatics. # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# +# modification, are permitted provided that the following conditions are met: +# # 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. +# this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# +# and/or other materials provided with the distribution. +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -23,9 +23,9 @@ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# +# # The views and conclusions contained in the software and documentation are those -# of the authors and should not be interpreted as representing official policies, +# of the authors and should not be interpreted as representing official policies, # either expressed or implied, of the FreeBSD Project. @@ -88,6 +88,7 @@ services: - WEKO_RECORDS_UI_SECRET_KEY="secret" - SECRET_KEY="CHANGE ME" - WTF_CSRF_SECRET_KEY="CHANGE ME" + - TIKA_JAR_FILE_PATH=/code/tika/tika-app-2.6.0.jar volumes: - weko3_data:/var/tmp - static_data:/home/invenio/.virtualenvs/invenio/var/instance/static @@ -140,6 +141,8 @@ services: - /code/modules/weko-sitemap/weko_sitemap.egg-info - /code/modules/invenio-iiif/invenio_iiif.egg-info - /code/modules/weko-swordserver/weko_swordserver.egg-info + - /code/modules/weko-signposting/weko_signposting.egg-info + - /code/modules/weko-notifications/weko_notifications.egg-info user: invenio links: - postgresql @@ -148,6 +151,7 @@ services: - elasticsearch - rabbitmq - worker + - inbox logging: driver: "json-file" options: @@ -186,7 +190,7 @@ services: - INVENIO_USER_EMAIL=wekosoftware@nii.ac.jp - INVENIO_USER_PASS=uspass123 #- INVENIO_POSTGRESQL_HOST=postgresql - - INVENIO_POSTGRESQL_HOST=pgpool + - INVENIO_POSTGRESQL_HOST=pgpool - INVENIO_POSTGRESQL_DBNAME=invenio - INVENIO_POSTGRESQL_DBUSER=invenio - INVENIO_POSTGRESQL_DBPASS=dbpass123 @@ -212,12 +216,13 @@ services: - INVENIO_DB_POOL_CLASS=NullPool - GOOGLE_TRACKING_ID_SYSTEM=UA-86504114-1 - GOOGLE_TRACKING_ID_USER= + - TIKA_JAR_FILE_PATH=/code/tika/tika-app-2.6.0.jar - TMPDIR=/var/tmp volumes: - weko3_data:/var/tmp - static_data:/home/invenio/.virtualenvs/invenio/var/instance/static - data_data:/home/invenio/.virtualenvs/invenio/var/instance/data - - conf_data:/home/invenio/.virtualenvs/invenio/var/instance/conf + - conf_data:/home/invenio/.virtualenvs/invenio/var/instance/conf - type: bind source: . target: /code @@ -265,6 +270,8 @@ services: - /code/modules/weko-sitemap/weko_sitemap.egg-info - /code/modules/invenio-iiif/invenio_iiif.egg-info - /code/modules/weko-swordserver/weko_swordserver.egg-info + - /code/modules/weko-signposting/weko_signposting.egg-info + - /code/modules/weko-notifications/weko_notifications.egg-info user: invenio links: - postgresql @@ -286,7 +293,6 @@ services: limits: memory: 8000m - postgresql: restart: "always" image: postgres:12 @@ -302,7 +308,7 @@ services: - "5432" pgpool: - restart: "always" + restart: "always" image: bitnami/pgpool environment: - PGPOOL_BACKEND_NODES=0:postgresql:5432 @@ -318,7 +324,7 @@ services: - PGPOOL_NUM_INIT_CHILDREN=2 - PGPOOL_MAX_POOL=2 ports: - - "25401:5432" + - "25401:5432" redis: restart: "always" @@ -345,7 +351,7 @@ services: rabbitmq: restart: "always" - image: rabbitmq:4.0.2 + image: rabbitmq:4.0.2 ports: - "24301:4369" - "45601:25672" @@ -365,6 +371,7 @@ services: # - letsencrypt_html:/var/www/html links: - web + - inbox deploy: resources: limits: @@ -386,6 +393,38 @@ services: links: - rabbitmq + inbox: + restart: "always" + build: + context: ./inbox + dockerfile: Dockerfile + ports: + - "8080:8080" + environment: + - ENABLE_PUSH_NOTIFICATIONS=False + - ICON=/static/images/weko-logo-256.png + - MONGO_DB_URI=mongodb://inbox:ibpass123@mongo:27017 + - MONGO_DB_NAME=inbox + - ON_RECEIVE_NOTIFICATION_WEBHOOK_URL= + - ALLOWED_ADMIN_ORIGINS=["*"] + - ALLOWED_ORIGINS=["*"] + - SUBSCRIBER=mailto:wekosoftware@nii.ac.jp + - VAPID_PUBLIC_KEY= + - VAPID_PRIVATE_KEY= + links: + - mongo + + mongo: + restart: "always" + image: mongo:7.0.14 + environment: + - MONGO_INITDB_ROOT_USERNAME=inbox + - MONGO_INITDB_ROOT_PASSWORD=ibpass123 + ports: + - "27017:27017" + volumes: + - mongo_data:/data/db + # kibana: # build: # context: . @@ -408,6 +447,7 @@ volumes: conf_data: pgsql-data: es-data: + mongo_data: # letsencrypt_etc: # letsencrypt_html: diff --git a/docker-compose2.yml b/docker-compose2.yml index f222a69341..27049567b1 100644 --- a/docker-compose2.yml +++ b/docker-compose2.yml @@ -3,16 +3,16 @@ # This file is part of WEKO3. # Copyright (C) 2017 National Institute of Informatics. # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# +# modification, are permitted provided that the following conditions are met: +# # 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. +# this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# +# and/or other materials provided with the distribution. +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -23,9 +23,9 @@ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# +# # The views and conclusions contained in the software and documentation are those -# of the authors and should not be interpreted as representing official policies, +# of the authors and should not be interpreted as representing official policies, # either expressed or implied, of the FreeBSD Project. @@ -88,6 +88,7 @@ services: - WEKO_RECORDS_UI_SECRET_KEY="secret" - SECRET_KEY="CHANGE ME" - WTF_CSRF_SECRET_KEY="CHANGE ME" + - TIKA_JAR_FILE_PATH=/code/tika/tika-app-2.6.0.jar volumes: - weko3_data:/var/tmp - static_data:/home/invenio/.virtualenvs/invenio/var/instance/static @@ -140,6 +141,8 @@ services: - /code/modules/weko-sitemap/weko_sitemap.egg-info - /code/modules/invenio-iiif/invenio_iiif.egg-info - /code/modules/weko-swordserver/weko_swordserver.egg-info + - /code/modules/weko-signposting/weko_signposting.egg-info + - /code/modules/weko-notifications/weko_notifications.egg-info user: invenio links: - postgresql @@ -148,6 +151,7 @@ services: - elasticsearch - rabbitmq - worker + - inbox logging: driver: "json-file" options: @@ -186,7 +190,7 @@ services: - INVENIO_USER_EMAIL=wekosoftware@nii.ac.jp - INVENIO_USER_PASS=uspass123 #- INVENIO_POSTGRESQL_HOST=postgresql - - INVENIO_POSTGRESQL_HOST=pgpool + - INVENIO_POSTGRESQL_HOST=pgpool - INVENIO_POSTGRESQL_DBNAME=invenio - INVENIO_POSTGRESQL_DBUSER=invenio - INVENIO_POSTGRESQL_DBPASS=dbpass123 @@ -212,12 +216,13 @@ services: - INVENIO_DB_POOL_CLASS=NullPool - GOOGLE_TRACKING_ID_SYSTEM=UA-86504114-1 - GOOGLE_TRACKING_ID_USER= + - TIKA_JAR_FILE_PATH=/code/tika/tika-app-2.6.0.jar - TMPDIR=/var/tmp volumes: - weko3_data:/var/tmp - static_data:/home/invenio/.virtualenvs/invenio/var/instance/static - data_data:/home/invenio/.virtualenvs/invenio/var/instance/data - - conf_data:/home/invenio/.virtualenvs/invenio/var/instance/conf + - conf_data:/home/invenio/.virtualenvs/invenio/var/instance/conf - type: bind source: . target: /code @@ -265,6 +270,8 @@ services: - /code/modules/weko-sitemap/weko_sitemap.egg-info - /code/modules/invenio-iiif/invenio_iiif.egg-info - /code/modules/weko-swordserver/weko_swordserver.egg-info + - /code/modules/weko-signposting/weko_signposting.egg-info + - /code/modules/weko-notifications/weko_notifications.egg-info user: invenio links: - postgresql @@ -286,7 +293,6 @@ services: limits: memory: 8000m - postgresql: restart: "always" image: postgres:12 @@ -302,7 +308,7 @@ services: - "5432" pgpool: - restart: "always" + restart: "always" image: bitnami/pgpool environment: - PGPOOL_BACKEND_NODES=0:postgresql:5432 @@ -318,7 +324,7 @@ services: - PGPOOL_NUM_INIT_CHILDREN=2 - PGPOOL_MAX_POOL=2 ports: - - "25401:5432" + - "25401:5432" redis: restart: "always" @@ -345,7 +351,7 @@ services: rabbitmq: restart: "always" - image: rabbitmq:4.0.2 + image: rabbitmq:4.0.2 ports: - "24301:4369" - "45601:25672" @@ -365,6 +371,7 @@ services: # - letsencrypt_html:/var/www/html links: - web + - inbox deploy: resources: limits: @@ -386,6 +393,38 @@ services: links: - rabbitmq + inbox: + restart: "always" + build: + context: ./inbox + dockerfile: Dockerfile + ports: + - "8080:8080" + environment: + - ENABLE_PUSH_NOTIFICATIONS=False + - ICON=/static/images/weko-logo-256.png + - MONGO_DB_URI=mongodb://inbox:ibpass123@mongo:27017 + - MONGO_DB_NAME=inbox + - ON_RECEIVE_NOTIFICATION_WEBHOOK_URL= + - ALLOWED_ADMIN_ORIGINS=["*"] + - ALLOWED_ORIGINS=["*"] + - SUBSCRIBER=mailto:wekosoftware@nii.ac.jp + - VAPID_PUBLIC_KEY= + - VAPID_PRIVATE_KEY= + links: + - mongo + + mongo: + restart: "always" + image: mongo:7.0.14 + environment: + - MONGO_INITDB_ROOT_USERNAME=inbox + - MONGO_INITDB_ROOT_PASSWORD=ibpass123 + ports: + - "27017:27017" + volumes: + - mongo_data:/data/db + # kibana: # build: # context: . @@ -408,6 +447,7 @@ volumes: conf_data: pgsql-data: es-data: + mongo_data: # letsencrypt_etc: # letsencrypt_html: diff --git a/inbox/Dockerfile b/inbox/Dockerfile new file mode 100644 index 0000000000..68da0095a1 --- /dev/null +++ b/inbox/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.12 as base + +ENV PYTHONUNBUFFERED True +ENV APP_HOME /app +WORKDIR $APP_HOME + +FROM base as production + +RUN apt-get update && apt-get install -y git +RUN git clone --branch nii_main https://github.com/RCOSDP/coar-notify-inbox.git $APP_HOME + +RUN pip install --upgrade pip +RUN pip install --no-cache-dir -r requirements.txt --prefer-binary + +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"] diff --git a/modules/invenio-accounts/invenio_accounts/admin.py b/modules/invenio-accounts/invenio_accounts/admin.py index 8074ac61b5..4f3ac7eeac 100644 --- a/modules/invenio-accounts/invenio_accounts/admin.py +++ b/modules/invenio-accounts/invenio_accounts/admin.py @@ -98,9 +98,9 @@ def scaffold_form(self): get_label='name', widget=Select2Widget(multiple=True) ) - + return form_class - + def _order_fields(self, form): custom_order = ['email', 'password', 'active', 'role', 'group', 'notification'] ordered_fields = OrderedDict() @@ -108,11 +108,11 @@ def _order_fields(self, form): ordered_fields[field_name] = form._fields[field_name] form._fields = ordered_fields return form - + def create_form(self, obj=None): form = super(UserView, self).create_form(obj) return self._order_fields(form) - + def edit_form(self, obj=None): form = super(UserView, self).edit_form(obj) return self._order_fields(form) @@ -121,7 +121,7 @@ def on_form_prefill(self, form, id): obj = self.get_one(id) form.role.data = [role for role in obj.roles if '_groups_' not in role.name] form.group.data = [role for role in obj.roles if '_groups_' in role.name] - + def on_model_change(self, form, User, is_created): """Hash password when saving.""" if form.password.data is not None: @@ -207,7 +207,7 @@ def action_activate(self, ids): _com_role = os.environ.get('INVENIO_ROLE_COMMUNITY', 'Community Administrator') _admin_roles = [_system_role, _repo_role, _com_role] - + @property def can_create(self): """Check permission for creating.""" diff --git a/modules/invenio-accounts/invenio_accounts/alembic/b5c2d8a5bf90_create_invenio_accounts_branch.py b/modules/invenio-accounts/invenio_accounts/alembic/b5c2d8a5bf90_create_invenio_accounts_branch.py new file mode 100644 index 0000000000..b6ee682824 --- /dev/null +++ b/modules/invenio-accounts/invenio_accounts/alembic/b5c2d8a5bf90_create_invenio_accounts_branch.py @@ -0,0 +1,32 @@ +# +# This file is part of Invenio. +# Copyright (C) 2016-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. + +"""Create invenio_accounts branch""" + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'b5c2d8a5bf90' +down_revision = 'e12419831262' +branch_labels = () +depends_on = None + + +def upgrade(): + """Upgrade database.""" + with op.batch_alter_table('accounts_user_session_activity') as batch_op: + batch_op.add_column( + sa.Column('orgniazation_name', sa.String(255), nullable=True) + ) + + +def downgrade(): + """Downgrade database.""" + with op.batch_alter_table('accounts_user_session_activity') as batch_op: + batch_op.drop_column('orgniazation_name') diff --git a/modules/invenio-accounts/invenio_accounts/models.py b/modules/invenio-accounts/invenio_accounts/models.py index f81255bea7..4bbfe5cbb2 100644 --- a/modules/invenio-accounts/invenio_accounts/models.py +++ b/modules/invenio-accounts/invenio_accounts/models.py @@ -98,6 +98,11 @@ def __str__(self): """Representation.""" return 'User '.format(self) + @classmethod + def get_email_by_id(cls, id): + """Get id, name by user_id. """ + query = db.session.query(cls).with_entities(cls.email).filter(cls.id == id) + return query.first() class SessionActivity(db.Model, Timestamp): """User Session Activity model. @@ -137,6 +142,9 @@ class SessionActivity(db.Model, Timestamp): device = db.Column(db.String(80), nullable=True) """User device.""" + orgniazation_name = db.Column(db.String(255), nullable=True) + """User Organization.""" + @classmethod def query_by_expired(cls): """Query to select all expired sessions.""" diff --git a/modules/invenio-accounts/invenio_accounts/sessions.py b/modules/invenio-accounts/invenio_accounts/sessions.py index 35bcbf36a5..2c8d87f793 100644 --- a/modules/invenio-accounts/invenio_accounts/sessions.py +++ b/modules/invenio-accounts/invenio_accounts/sessions.py @@ -75,6 +75,9 @@ def add_session(session=None): """ user_id, sid_s = session['user_id'], session.sid_s + request_data = request.get_json() + orgniazation_name = request_data.get('jao') if request_data else None + with db.session.begin_nested(): session_activity = SessionActivity( user_id=user_id, @@ -83,7 +86,8 @@ def add_session(session=None): country=_ip2country(get_remote_addr()), **_extract_info_from_useragent( request.headers.get('User-Agent', '') - ) + ), + orgniazation_name=orgniazation_name ) db.session.merge(session_activity) diff --git a/modules/invenio-accounts/invenio_accounts/templates/invenio_accounts/settings/security.html b/modules/invenio-accounts/invenio_accounts/templates/invenio_accounts/settings/security.html index 498a95c556..0b9039c2a0 100644 --- a/modules/invenio-accounts/invenio_accounts/templates/invenio_accounts/settings/security.html +++ b/modules/invenio-accounts/invenio_accounts/templates/invenio_accounts/settings/security.html @@ -31,7 +31,7 @@ {%- endif %}
- {{session.ip}}
+ {{session.ip}} {% if session.orgniazation_name %}{% trans orgniazation_name=session.orgniazation_name %}MSG_INVENIO_ACCOUNT_AUTHENTICATED_IDP{% endtrans %}{%- endif %}
{{_("Signed in")}}: {{session.created | tousertimezone | dateformat}} diff --git a/modules/invenio-accounts/invenio_accounts/translations/en/LC_MESSAGES/messages.mo b/modules/invenio-accounts/invenio_accounts/translations/en/LC_MESSAGES/messages.mo new file mode 100644 index 0000000000..7f4951f5bf Binary files /dev/null and b/modules/invenio-accounts/invenio_accounts/translations/en/LC_MESSAGES/messages.mo differ diff --git a/modules/invenio-accounts/invenio_accounts/translations/en/LC_MESSAGES/messages.po b/modules/invenio-accounts/invenio_accounts/translations/en/LC_MESSAGES/messages.po new file mode 100644 index 0000000000..b5b1b2fd91 --- /dev/null +++ b/modules/invenio-accounts/invenio_accounts/translations/en/LC_MESSAGES/messages.po @@ -0,0 +1,209 @@ +# Translations template for invenio-accounts. +# Copyright (C) 2017 CERN +# This file is distributed under the same license as the invenio-accounts +# project. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: invenio-accounts 1.0.0b3\n" +"Report-Msgid-Bugs-To: info@inveniosoftware.org\n" +"POT-Creation-Date: 2017-03-20 13:08+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.3.4\n" + +#: invenio_accounts/admin.py:74 +msgid "Current Login IP" +msgstr "" + +#: invenio_accounts/admin.py:75 +msgid "Last Login IP" +msgstr "" + +#: invenio_accounts/admin.py:78 +msgid "Inactivate" +msgstr "" + +#: invenio_accounts/admin.py:79 +msgid "Are you sure you want to inactivate selected users?" +msgstr "" + +#: invenio_accounts/admin.py:88 invenio_accounts/admin.py:111 +msgid "Cannot find user." +msgstr "" + +#: invenio_accounts/admin.py:92 invenio_accounts/admin.py:115 +msgid "User(s) were successfully inactivated." +msgstr "" + +#: invenio_accounts/admin.py:98 +msgid "Failed to inactivate users." +msgstr "" + +#: invenio_accounts/admin.py:101 +msgid "Activate" +msgstr "" + +#: invenio_accounts/admin.py:102 +msgid "Are you sure you want to activate selected users?" +msgstr "" + +#: invenio_accounts/admin.py:121 +msgid "Failed to activate users." +msgstr "" + +#: invenio_accounts/admin.py:143 invenio_accounts/admin.py:149 +msgid "User Management" +msgstr "" + +#: invenio_accounts/forms.py:38 +msgid "Please complete the reCAPTCHA." +msgstr "" + +#: invenio_accounts/views.py:78 +msgid "Account" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:29 +#: invenio_accounts/views.py:83 invenio_accounts/views.py:101 +msgid "Change password" +msgstr "" + +#. NOTE: Menu item text (icon replaced by a user icon). +#: invenio_accounts/views.py:91 +#, python-format +msgid "%(icon)s Change password" +msgstr "" + +#. NOTE: Menu item text (icon replaced by a user icon). +#: invenio_accounts/views.py:123 +#, python-format +msgid "%(icon)s Security" +msgstr "" + +#: invenio_accounts/views.py:125 +msgid "Security" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:38 +msgid "Current password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:42 +msgid "New password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:46 +msgid "Confirm new password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:50 +msgid "Change Password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:35 +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:45 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:36 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:46 +msgid "Reset Password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:41 +msgid "" +"Enter your email address below and we will send you a link to reset your " +"password." +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:53 +#: invenio_accounts/templates/invenio_accounts/login_user.html:43 +#: invenio_accounts/templates/invenio_accounts/register_user.html:54 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:54 +msgid "Log In" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:53 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:54 +msgid "or" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:53 +#: invenio_accounts/templates/invenio_accounts/login_user.html:51 +#: invenio_accounts/templates/invenio_accounts/register_user.html:48 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:54 +msgid "Sign Up" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/login_user.html:34 +msgid "Log in to account" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/login_user.html:51 +#, python-format +msgid "New to %(sitename)s?" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/login_user.html:58 +msgid "Forgot password?" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/register_user.html:34 +#, python-format +msgid "Sign up for a %(sitename)s account!" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/register_user.html:53 +msgid "Already have an account?" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/send_confirmation.html:35 +msgid "Resend Confirmation Email" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/send_confirmation.html:36 +msgid "" +"Enter your email address below and we will send you an email confirmation" +" link." +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/send_confirmation.html:41 +msgid "Send Confirmation" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:27 +msgid "Sessions" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:32 +msgid "Revoke" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:36 +msgid "This is a list of devices that have logged into your account." +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:50 +msgid "Session identifier" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:52 +msgid "Signed in" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:54 +msgid "current session" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:62 +msgid "Logout" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:34 +msgid "MSG_INVENIO_ACCOUNT_AUTHENTICATED_IDP" +msgstr "(Authenticated by %(orgniazation_name)s)" diff --git a/modules/invenio-accounts/invenio_accounts/translations/ja/LC_MESSAGES/messages.mo b/modules/invenio-accounts/invenio_accounts/translations/ja/LC_MESSAGES/messages.mo new file mode 100644 index 0000000000..855ce74119 Binary files /dev/null and b/modules/invenio-accounts/invenio_accounts/translations/ja/LC_MESSAGES/messages.mo differ diff --git a/modules/invenio-accounts/invenio_accounts/translations/ja/LC_MESSAGES/messages.po b/modules/invenio-accounts/invenio_accounts/translations/ja/LC_MESSAGES/messages.po new file mode 100644 index 0000000000..7b0e121f50 --- /dev/null +++ b/modules/invenio-accounts/invenio_accounts/translations/ja/LC_MESSAGES/messages.po @@ -0,0 +1,209 @@ +# Translations template for invenio-accounts. +# Copyright (C) 2017 CERN +# This file is distributed under the same license as the invenio-accounts +# project. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: invenio-accounts 1.0.0b3\n" +"Report-Msgid-Bugs-To: info@inveniosoftware.org\n" +"POT-Creation-Date: 2017-03-20 13:08+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: ja\n" +"Language-Team: ja \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.3.4\n" + +#: invenio_accounts/admin.py:74 +msgid "Current Login IP" +msgstr "" + +#: invenio_accounts/admin.py:75 +msgid "Last Login IP" +msgstr "" + +#: invenio_accounts/admin.py:78 +msgid "Inactivate" +msgstr "" + +#: invenio_accounts/admin.py:79 +msgid "Are you sure you want to inactivate selected users?" +msgstr "" + +#: invenio_accounts/admin.py:88 invenio_accounts/admin.py:111 +msgid "Cannot find user." +msgstr "" + +#: invenio_accounts/admin.py:92 invenio_accounts/admin.py:115 +msgid "User(s) were successfully inactivated." +msgstr "" + +#: invenio_accounts/admin.py:98 +msgid "Failed to inactivate users." +msgstr "" + +#: invenio_accounts/admin.py:101 +msgid "Activate" +msgstr "" + +#: invenio_accounts/admin.py:102 +msgid "Are you sure you want to activate selected users?" +msgstr "" + +#: invenio_accounts/admin.py:121 +msgid "Failed to activate users." +msgstr "" + +#: invenio_accounts/admin.py:143 invenio_accounts/admin.py:149 +msgid "User Management" +msgstr "" + +#: invenio_accounts/forms.py:38 +msgid "Please complete the reCAPTCHA." +msgstr "" + +#: invenio_accounts/views.py:78 +msgid "Account" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:29 +#: invenio_accounts/views.py:83 invenio_accounts/views.py:101 +msgid "Change password" +msgstr "" + +#. NOTE: Menu item text (icon replaced by a user icon). +#: invenio_accounts/views.py:91 +#, python-format +msgid "%(icon)s Change password" +msgstr "" + +#. NOTE: Menu item text (icon replaced by a user icon). +#: invenio_accounts/views.py:123 +#, python-format +msgid "%(icon)s Security" +msgstr "" + +#: invenio_accounts/views.py:125 +msgid "Security" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:38 +msgid "Current password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:42 +msgid "New password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:46 +msgid "Confirm new password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/change_password.html:50 +msgid "Change Password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:35 +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:45 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:36 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:46 +msgid "Reset Password" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:41 +msgid "" +"Enter your email address below and we will send you a link to reset your " +"password." +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:53 +#: invenio_accounts/templates/invenio_accounts/login_user.html:43 +#: invenio_accounts/templates/invenio_accounts/register_user.html:54 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:54 +msgid "Log In" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:53 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:54 +msgid "or" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/forgot_password.html:53 +#: invenio_accounts/templates/invenio_accounts/login_user.html:51 +#: invenio_accounts/templates/invenio_accounts/register_user.html:48 +#: invenio_accounts/templates/invenio_accounts/reset_password.html:54 +msgid "Sign Up" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/login_user.html:34 +msgid "Log in to account" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/login_user.html:51 +#, python-format +msgid "New to %(sitename)s?" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/login_user.html:58 +msgid "Forgot password?" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/register_user.html:34 +#, python-format +msgid "Sign up for a %(sitename)s account!" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/register_user.html:53 +msgid "Already have an account?" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/send_confirmation.html:35 +msgid "Resend Confirmation Email" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/send_confirmation.html:36 +msgid "" +"Enter your email address below and we will send you an email confirmation" +" link." +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/send_confirmation.html:41 +msgid "Send Confirmation" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:27 +msgid "Sessions" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:32 +msgid "Revoke" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:36 +msgid "This is a list of devices that have logged into your account." +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:50 +msgid "Session identifier" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:52 +msgid "Signed in" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:54 +msgid "current session" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:62 +msgid "Logout" +msgstr "" + +#: invenio_accounts/templates/invenio_accounts/settings/security.html:34 +msgid "MSG_INVENIO_ACCOUNT_AUTHENTICATED_IDP" +msgstr "(%(orgniazation_name)s で認証)" diff --git a/modules/invenio-accounts/requirements.txt b/modules/invenio-accounts/requirements.txt index 2e20530da5..4bf77e2ced 100644 --- a/modules/invenio-accounts/requirements.txt +++ b/modules/invenio-accounts/requirements.txt @@ -159,7 +159,7 @@ Flask-Plugins==1.6.1 bibtexparser==1.0.1 github3.py==1.1.0 feedgen==0.7.0 -boto3==1.7.84 +boto3==1.9.83 s3fs==0.1.6 #fpdf==1.7.2 -e git+https://github.com/RCOSDP/pyfpdf.git@fix/nii#egg=fpdf diff --git a/modules/invenio-accounts/requirements2.txt b/modules/invenio-accounts/requirements2.txt index 40e717e79a..cd3f5baf07 100644 --- a/modules/invenio-accounts/requirements2.txt +++ b/modules/invenio-accounts/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,12 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-accounts/tests/test_admin.py b/modules/invenio-accounts/tests/test_admin.py index 8a6cb02806..88826344a3 100644 --- a/modules/invenio-accounts/tests/test_admin.py +++ b/modules/invenio-accounts/tests/test_admin.py @@ -211,7 +211,7 @@ def test_admin_sessions_action_delete(app): with app.test_client() as client: login_user_via_view(client=client, email=user2_email, password=user2_pw) - + with patch('invenio_accounts.admin.db.session.commit', side_effect=Exception('')): view.action_delete([user1_sid]) sessions = SessionActivity.query.all() @@ -282,4 +282,4 @@ def test_userview_edit_form(app, users): """Test edit_form for super role user.""" view = UserView(User, db.session) form = view.edit_form() - assert form.data['active'] is False \ No newline at end of file + assert form.data['active'] is False diff --git a/modules/invenio-accounts/tests/test_models.py b/modules/invenio-accounts/tests/test_models.py index 89d372ac6c..6866b888ec 100644 --- a/modules/invenio-accounts/tests/test_models.py +++ b/modules/invenio-accounts/tests/test_models.py @@ -14,7 +14,7 @@ from sqlalchemy import inspect from invenio_accounts import testutils -from invenio_accounts.models import SessionActivity +from invenio_accounts.models import SessionActivity, User def test_session_activity_model(app): @@ -67,3 +67,9 @@ def test_session_activity_model(app): database.session.commit() assert len(user.active_sessions) == 1 assert user.active_sessions[0].sid_s != session_to_delete.sid_s + +def test_get_email_by_id(app, users): + with app.app_context(): + with app.test_client() as client: + lst = User.get_email_by_id(1) + assert len(lst) > 0 diff --git a/modules/invenio-accounts/tests/test_sessions.py b/modules/invenio-accounts/tests/test_sessions.py index 65833882b4..0bf973c047 100644 --- a/modules/invenio-accounts/tests/test_sessions.py +++ b/modules/invenio-accounts/tests/test_sessions.py @@ -10,24 +10,26 @@ import datetime import time - import flask_security import pytest +import flask from flask import current_app, session from flask_security import url_for_security from invenio_db import db from simplekv.memory.redisstore import RedisStore from werkzeug.local import LocalProxy +from mock import patch from invenio_accounts import testutils from invenio_accounts.models import SessionActivity -from invenio_accounts.sessions import delete_session +from invenio_accounts.sessions import delete_session, add_session _sessionstore = LocalProxy(lambda: current_app. extensions['invenio-accounts'].sessionstore) _datastore = LocalProxy(lambda: current_app.extensions['security'].datastore) +# .tox/c1/bin/pytest --cov=invenio_accounts tests/test_sessions.py::test_login_listener -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-accounts/.tox/c1/tmp def test_login_listener(app): """Test login listener.""" with app.app_context(): @@ -37,8 +39,12 @@ def test_login_listener(app): query = db.session.query(SessionActivity) assert query.count() == 0 - testutils.login_user_via_view(client, user.email, - user.password_plaintext) + testutils.login_user_via_view( + client, + user.email, + user.password_plaintext + ) + assert testutils.client_authenticated(client) # After logging in, a SessionActivity has been created # corresponding to the user's session. @@ -49,7 +55,6 @@ def test_login_listener(app): assert session_entry.user_id == user.id assert session_entry.sid_s == session.sid_s - def test_repeated_login_session_population(app): """Verify session population on repeated login.""" with app.app_context(): @@ -298,3 +303,54 @@ def test_session_ip_no_country(app, users): [session] = SessionActivity.query.all() assert session.country is None assert session.ip == '139.191.247.1' + + +# .tox/c1/bin/pytest --cov=invenio_accounts tests/test_sessions.py::test_add_session -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-accounts/.tox/c1/tmp +def test_add_session(app): + """Test add_session function.""" + with app.app_context(): + user = testutils.create_test_user('test@example.org') + with app.test_client() as client: + query = db.session.query(SessionActivity) + assert query.count() == 0 + + # Create a test request context + with app.test_request_context(): + session['user_id'] = user.id + session.sid_s = 'test_sid_s' # Mock session ID + # Mock request.get_json to return specific data + with patch('flask.request.get_json', return_value={'jao': '相うえおIdP'}): + add_session(session) + + query = db.session.query(SessionActivity) + assert query.count() == 1 + + session_entry = query.first() + assert session_entry.user_id == user.id + assert session_entry.sid_s == 'test_sid_s' + assert session_entry.orgniazation_name == '相うえおIdP' + + +def test_add_session_no_org(app): + """Test add_session function.""" + with app.app_context(): + user = testutils.create_test_user('test@example.org') + with app.test_client() as client: + query = db.session.query(SessionActivity) + assert query.count() == 0 + + # Create a test request context + with app.test_request_context(): + session['user_id'] = user.id + session.sid_s = 'test_sid_s' # Mock session ID + # Mock request.get_json to return specific data + with patch('flask.request.get_json', return_value={}): + add_session(session) + + query = db.session.query(SessionActivity) + assert query.count() == 1 + + session_entry = query.first() + assert session_entry.user_id == user.id + assert session_entry.sid_s == 'test_sid_s' + assert session_entry.orgniazation_name is None diff --git a/modules/invenio-accounts/tests/test_utils.py b/modules/invenio-accounts/tests/test_utils.py index 0413d6b805..7c0f6d0122 100644 --- a/modules/invenio-accounts/tests/test_utils.py +++ b/modules/invenio-accounts/tests/test_utils.py @@ -178,7 +178,7 @@ def test_get_user_ids_by_role_returns_user_ids(app): """Test get_user_ids_by_role.""" with app.app_context(): ds = app.extensions['invenio-accounts'].datastore - + user1 = ds.create_user(email='test1@test.org', active=True) user2 = ds.create_user(email='test2@test.org', active=True) role = ds.create_role(name='superuser', description='1234') @@ -201,4 +201,4 @@ def test_get_user_ids_by_role_returns_empty_list(app, role_id, expected): """Test get_user_ids_by_role.""" with app.app_context(): user_ids = get_user_ids_by_role(role_id) - assert user_ids == expected \ No newline at end of file + assert user_ids == expected diff --git a/modules/invenio-communities/invenio_communities/admin.py b/modules/invenio-communities/invenio_communities/admin.py index 85e381d9a9..7aab422cd7 100644 --- a/modules/invenio-communities/invenio_communities/admin.py +++ b/modules/invenio-communities/invenio_communities/admin.py @@ -110,10 +110,10 @@ def validate_input_id(id): m = re.match(the_patterns['ASCII_LETTER_PATTERN'], id) if m is None: raise ValidationError(the_result['ASCII_LETTER_PATTERN']) - + if self.can_create is False: abort(403) - + form = self.create_form() if(request.method == 'POST'): @@ -581,7 +581,7 @@ def _validate_input_id(self, field): 'maxlength': 100, } } - + @property def can_create(self): """Check permission for creating.""" diff --git a/modules/invenio-communities/invenio_communities/models.py b/modules/invenio-communities/invenio_communities/models.py index c4c768d320..553ebea644 100644 --- a/modules/invenio-communities/invenio_communities/models.py +++ b/modules/invenio-communities/invenio_communities/models.py @@ -256,10 +256,10 @@ class Community(db.Model, Timestamp): nullable=False ) """Id of Root Node""" - + content_policy = db.Column(db.Text, nullable=True, default='') """Community content policy.""" - + group_id = db.Column( db.Integer, db.ForeignKey(Role.id), @@ -284,7 +284,7 @@ class Community(db.Model, Timestamp): backref='index', foreign_keys=[root_node_id]) """Relation to the owner (Index) of the community.""" - + group = db.relationship(Role, backref='group', foreign_keys=[group_id]) @@ -371,7 +371,7 @@ def filter_communities(cls, p, so, with_deleted=False): else: query = query.order_by(db.desc(cls.ranking)) return query - + @classmethod def get_repositories_by_user(cls, user): """Get repository ids for user.""" @@ -468,7 +468,7 @@ def undelete(self): def to_dict(self): """Convert the Community object to a dictionary. - + Returns: dict: Dictionary representation of the Community object. """ diff --git a/modules/invenio-communities/invenio_communities/static/js/invenio_communities/app.js b/modules/invenio-communities/invenio_communities/static/js/invenio_communities/app.js index 95115060bd..3a70f31aa7 100644 --- a/modules/invenio-communities/invenio_communities/static/js/invenio_communities/app.js +++ b/modules/invenio-communities/invenio_communities/static/js/invenio_communities/app.js @@ -1,16 +1,16 @@ (function (angular) { // Bootstrap it! - angular.element(document).ready(function() { - angular.module('wekoRecords.controllers', []); + angular.element(document).ready(function () { + angular.module('wekoRecords.controllers', []); - function WekoRecordsCtrl($scope, $rootScope, $modal, InvenioRecordsAPI){ - $rootScope.$on('invenio.records.loading.stop', function (ev) { - setTimeout(function () { - let model = $rootScope.recordsVM.invenioRecordsModel; - CustomBSDatePicker.setDataFromFieldToModel(model, true); - }, 1000); - }); - $scope.saveData = function(){ + function WekoRecordsCtrl($scope, $rootScope, $modal, InvenioRecordsAPI) { + $rootScope.$on('invenio.records.loading.stop', function (ev) { + setTimeout(function () { + let model = $rootScope.recordsVM.invenioRecordsModel; + CustomBSDatePicker.setDataFromFieldToModel(model, true); + }, 1000); + }); + $scope.saveData = function () { const form1Data = new FormData(document.getElementsByClassName('admin-form form-horizontal')[0]); @@ -19,12 +19,12 @@ form2Data.append('catalog_data', JSON.stringify(metainfo)); for (const [key, value] of form2Data) { - form1Data.append(key, value); + form1Data.append(key, value); } fetch('', { - method: 'POST', - body: form1Data, + method: 'POST', + body: form1Data, }).then(response => { if (response.ok) { const url = new URL(response.url); @@ -42,52 +42,52 @@ } }).catch(error => { alert("An error has occurred. : " + error.message); // エラーメッセージを表示 - }); + }); + } } - } - // Inject depedencies - WekoRecordsCtrl.$inject = [ - '$scope', - '$rootScope', - '$modal', - 'InvenioRecordsAPI', - ]; - angular.module('wekoRecords.controllers') - .controller('WekoRecordsCtrl', WekoRecordsCtrl); + // Inject depedencies + WekoRecordsCtrl.$inject = [ + '$scope', + '$rootScope', + '$modal', + 'InvenioRecordsAPI', + ]; + angular.module('wekoRecords.controllers') + .controller('WekoRecordsCtrl', WekoRecordsCtrl); - var ModalInstanceCtrl = function($scope, $modalInstance, items) { - $scope.items = items; - $scope.searchKey = ''; - $scope.selected = { - item : $scope.items[0] - }; - $scope.ok = function() { - $modalInstance.close($scope.selected); - }; - $scope.cancel = function() { - $modalInstance.dismiss('cancel'); + var ModalInstanceCtrl = function ($scope, $modalInstance, items) { + $scope.items = items; + $scope.searchKey = ''; + $scope.selected = { + item: $scope.items[0] + }; + $scope.ok = function () { + $modalInstance.close($scope.selected); + }; + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + $scope.search = function () { + $scope.items.push($scope.searchKey); + } }; - $scope.search = function() { - $scope.items.push($scope.searchKey); - } - }; - angular.module('wekoRecords', [ - 'invenioRecords', - 'wekoRecords.controllers', - ]); + angular.module('wekoRecords', [ + 'invenioRecords', + 'wekoRecords.controllers', + ]); - angular.bootstrap( - document.getElementById('weko-records'), [ + angular.bootstrap( + document.getElementById('weko-records'), [ 'wekoRecords', 'invenioRecords', 'schemaForm', 'mgcrea.ngStrap', 'mgcrea.ngStrap.modal', 'pascalprecht.translate', 'ui.sortable', 'ui.select', 'mgcrea.ngStrap.select', 'mgcrea.ngStrap.datepicker', 'mgcrea.ngStrap.helpers.dateParser', 'mgcrea.ngStrap.tooltip', 'invenioFiles' ] - ); -}); + ); + }); })(angular); @@ -240,7 +240,7 @@ var CustomBSDatePicker = { * @param {[object]} element is date input control. */ initAttributeForModel: function (model, element) { - if($(element).val().length == 0) return; + if ($(element).val().length == 0) return; let ng_model = $(element).attr('ng-model').replace(/']/g, ''); let arr = ng_model.split("['"); //Init attribute of model object if them undefine. @@ -306,16 +306,16 @@ var CustomBSDatePicker = { * If input empty, this attribute delete. * Fix bug: not enter data for date field. */ - removeLastAttr: function(model){ + removeLastAttr: function (model) { let cls = CustomBSDatePicker.option.cls; let element_arr = $('.' + cls); $.each(element_arr, function (ind, val) { - if($(val).val().length > 0){ + if ($(val).val().length > 0) { CustomBSDatePicker.initAttributeForModel(model, val); let ng_model = $(val).attr('ng-model'); let last_index = ng_model.lastIndexOf('['); let previous_attr = ng_model.substring(0, last_index); - let str_code = "if("+ng_model+"==''){"+previous_attr+"={}}"; + let str_code = "if(" + ng_model + "==''){" + previous_attr + "={}}"; eval(str_code); } }); diff --git a/modules/invenio-communities/invenio_communities/static/js/invenio_communities/communities_list.js b/modules/invenio-communities/invenio_communities/static/js/invenio_communities/communities_list.js index c9c7e28a1b..57484ded0b 100644 --- a/modules/invenio-communities/invenio_communities/static/js/invenio_communities/communities_list.js +++ b/modules/invenio-communities/invenio_communities/static/js/invenio_communities/communities_list.js @@ -30,10 +30,10 @@ function printCatalogInfo(data) { if ("contributor_names" in contributor) { contributor.contributor_names.forEach(name => { if ("contributor_name" in name) { - string_contributors += ", " + name.contributor_name; + string_contributors += ", " + name.contributor_name; } if ("contributor_name_language" in name) { - string_contributors += ", " + name.contributor_name_language; + string_contributors += ", " + name.contributor_name_language; } }); } diff --git a/modules/invenio-communities/invenio_communities/utils.py b/modules/invenio-communities/invenio_communities/utils.py index 6c1059cfe0..bce256d33f 100644 --- a/modules/invenio-communities/invenio_communities/utils.py +++ b/modules/invenio-communities/invenio_communities/utils.py @@ -256,7 +256,7 @@ def get_repository_id_by_item_id(item_id): if not index.parent: break index = Index.get_index_by_id(index.parent) - + return repository_id def delete_empty(data): diff --git a/modules/invenio-communities/requirements.txt b/modules/invenio-communities/requirements.txt index 2e20530da5..4bf77e2ced 100644 --- a/modules/invenio-communities/requirements.txt +++ b/modules/invenio-communities/requirements.txt @@ -159,7 +159,7 @@ Flask-Plugins==1.6.1 bibtexparser==1.0.1 github3.py==1.1.0 feedgen==0.7.0 -boto3==1.7.84 +boto3==1.9.83 s3fs==0.1.6 #fpdf==1.7.2 -e git+https://github.com/RCOSDP/pyfpdf.git@fix/nii#egg=fpdf diff --git a/modules/invenio-communities/requirements2.txt b/modules/invenio-communities/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-communities/requirements2.txt +++ b/modules/invenio-communities/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-communities/tests/conftest.py b/modules/invenio-communities/tests/conftest.py index 7f61d315fd..7dcd3b98e4 100644 --- a/modules/invenio-communities/tests/conftest.py +++ b/modules/invenio-communities/tests/conftest.py @@ -272,7 +272,7 @@ def users(app, db): ds.add_role_to_user(originalroleuser2, repoadmin_role) ds.add_role_to_user(subrepoadmin, group_role) ds.add_role_to_user(subrepoadmin, comadmin_role) - + return [ diff --git a/modules/invenio-communities/tests/test_admin.py b/modules/invenio-communities/tests/test_admin.py index ee311e16c6..ec7eff8b18 100644 --- a/modules/invenio-communities/tests/test_admin.py +++ b/modules/invenio-communities/tests/test_admin.py @@ -117,8 +117,8 @@ def test_role_query_cond(self, setup_view_community, users): # role_idss is true result = view.role_query_cond([1,2]) assert str(result) == "communities_community.group_id IN (:group_id_1, :group_id_2)" - - # def get_query(self): + + # def get_query(self): # .tox/c1/bin/pytest --cov=invenio_communities tests/test_admin.py::TestCommunityModelView::test_get_query -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-communities/.tox/c1/tmp def test_get_query(self,setup_view_community,users): _, _, _, user, view = setup_view_community diff --git a/modules/invenio-communities/tests/test_models.py b/modules/invenio-communities/tests/test_models.py index a4c73d237b..21ccd97ed9 100644 --- a/modules/invenio-communities/tests/test_models.py +++ b/modules/invenio-communities/tests/test_models.py @@ -219,13 +219,13 @@ def test_get_repositories_by_user(self, db, communities, users): result = Community.get_repositories_by_user(user) assert len(result) == 1 assert result[0].id == comm.id - + # non matching group_id - comm.group_id = user.roles[0].id + 1 + comm.group_id = user.roles[0].id + 1 db.session.commit() result = Community.get_repositories_by_user(user) assert len(result) == 0 - + # user has no roles user.roles = [] db.session.commit() diff --git a/modules/invenio-communities/tests/test_utils.py b/modules/invenio-communities/tests/test_utils.py index 496624f27d..fcac408523 100644 --- a/modules/invenio-communities/tests/test_utils.py +++ b/modules/invenio-communities/tests/test_utils.py @@ -261,7 +261,7 @@ def test_get_repository_id_by_item_id(app, db, users, mocker): root_node_id=index1.id, group_id=1) db.session.commit() - + # Setup mock data record_data = {1: {"path": [1]}, 2: {"path": [2]}, 3: {"path": [3]},} @@ -283,7 +283,7 @@ def mock_get_index_by_id(index_id): repository_id = get_repository_id_by_item_id(1) assert repository_id == comm1.id - # + # repository_id = get_repository_id_by_item_id(2) assert repository_id == comm1.id @@ -293,4 +293,4 @@ def mock_get_index_by_id(index_id): # item_id does not exist with pytest.raises(NoResultFound): - repository_id = get_repository_id_by_item_id(999) \ No newline at end of file + repository_id = get_repository_id_by_item_id(999) diff --git a/modules/invenio-communities/tests/test_views_ui.py b/modules/invenio-communities/tests/test_views_ui.py index 37ea8c01f6..5b747147ea 100644 --- a/modules/invenio-communities/tests/test_views_ui.py +++ b/modules/invenio-communities/tests/test_views_ui.py @@ -149,7 +149,7 @@ def test_view(client,app,db,communities,mocker): def test_content_policy(client, app, db, communities, mocker): comm = communities[0] mock_render = mocker.patch("invenio_communities.views.ui.render_template",return_value=make_response()) - + # valid community url = url_for("invenio_communities.content_policy", community_id=comm.id) response = client.get(url) @@ -157,7 +157,7 @@ def test_content_policy(client, app, db, communities, mocker): args, kwargs = mock_render.call_args assert args[0] == "invenio_communities/content_policy.html" assert kwargs["community"].id == comm.id - + # invalid community url = url_for("invenio_communities.content_policy", community_id="invalid_id") response = client.get(url) diff --git a/modules/invenio-db/requirements.txt b/modules/invenio-db/requirements.txt index 2e20530da5..4bf77e2ced 100644 --- a/modules/invenio-db/requirements.txt +++ b/modules/invenio-db/requirements.txt @@ -159,7 +159,7 @@ Flask-Plugins==1.6.1 bibtexparser==1.0.1 github3.py==1.1.0 feedgen==0.7.0 -boto3==1.7.84 +boto3==1.9.83 s3fs==0.1.6 #fpdf==1.7.2 -e git+https://github.com/RCOSDP/pyfpdf.git@fix/nii#egg=fpdf diff --git a/modules/invenio-db/requirements2.txt b/modules/invenio-db/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-db/requirements2.txt +++ b/modules/invenio-db/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-deposit/requirements2.txt b/modules/invenio-deposit/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-deposit/requirements2.txt +++ b/modules/invenio-deposit/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-files-rest/invenio_files_rest/admin.py b/modules/invenio-files-rest/invenio_files_rest/admin.py index 840cdf043a..94d95af90b 100644 --- a/modules/invenio-files-rest/invenio_files_rest/admin.py +++ b/modules/invenio-files-rest/invenio_files_rest/admin.py @@ -23,9 +23,9 @@ from invenio_admin.forms import LazyChoices from markupsafe import Markup from wtforms.fields import PasswordField -from wtforms.fields import StringField +from wtforms.fields import StringField, SelectField, IntegerField from wtforms.fields import BooleanField -from wtforms.validators import ValidationError +from wtforms.validators import ValidationError, NumberRange, Length, Optional from wtforms.widgets import PasswordInput from .models import Bucket, FileInstance, Location, MultipartObject, \ @@ -76,7 +76,12 @@ class LocationModelView(ModelView): access_key=_('Access Key'), secret_key=_('Secret Key'), s3_endpoint_url=_('S3_ENDPOINT_URL'), - s3_send_file_directly=_('S3_SEND_FILE_DIRECTLY') + s3_send_file_directly=_('S3_SEND_FILE_DIRECTLY'), + s3_default_block_size=_('S3_DEFAULT_BLOCK_SIZE'), + s3_maximum_number_of_parts=_('S3_MAXIMUM_NUMBER_OF_PARTS'), + s3_region_name=_('S3_REGION_NAME'), + s3_signature_version=_('S3_SIGNATURE_VERSION'), + s3_url_expiration=_('S3_URL_EXPIRATION'), ) column_filters = ('default', 'created', 'updated', ) column_searchable_list = ('uri', 'name') @@ -85,6 +90,8 @@ class LocationModelView(ModelView): form_columns = ( 'name', 'uri', 'type', 'access_key', 'secret_key', 's3_endpoint_url', 's3_send_file_directly', + 's3_default_block_size', 's3_maximum_number_of_parts', + 's3_region_name', 's3_signature_version', 's3_url_expiration', 'quota_size', 'default') form_choices = { 'type': LazyChoices( @@ -96,7 +103,18 @@ class LocationModelView(ModelView): 'secret_key': PasswordField('secret_key', widget=PasswordInput(hide_value=False)), 's3_endpoint_url': StringField('endpoint_url'), - 's3_send_file_directly': BooleanField('send_file_directly') + 's3_send_file_directly': BooleanField('send_file_directly'), + 's3_default_block_size': IntegerField('default_block_size', validators=[NumberRange(min=0), Optional()]), + 's3_maximum_number_of_parts': IntegerField('maximum_number_of_parts', validators=[NumberRange(min=0), Optional()]), + 's3_region_name': StringField('region_name', validators=[ + Length(max=120, message='max 120 characters') + ]), + 's3_signature_version': SelectField('signature_version', + choices=[ + ('s3', 's3'), + ('s3v4', 's3v4'), + ]), + 's3_url_expiration': IntegerField('url_expiration', validators=[NumberRange(min=0), Optional()]), } form_args = dict( name=dict(validators=[require_slug]) @@ -108,6 +126,27 @@ class LocationModelView(ModelView): _system_role = os.environ.get('INVENIO_ROLE_SYSTEM', 'System Administrator') + def on_model_change(self, form, model, is_created): + if is_created: + if model.type == current_app.config['FILES_REST_LOCATION_TYPE_S3_PATH_VALUE'] or \ + model.type == current_app.config['FILES_REST_LOCATION_TYPE_S3_VIRTUAL_HOST_VALUE']: + model.s3_signature_version = None + else: + model.s3_default_block_size = None + model.s3_maximum_number_of_parts = None + model.s3_region_name = None + model.s3_signature_version = None + model.s3_url_expiration = None + else: + if model.type == current_app.config['FILES_REST_LOCATION_TYPE_S3_VIRTUAL_HOST_VALUE']: + model.s3_endpoint_url = '' + elif model.type != current_app.config['FILES_REST_LOCATION_TYPE_S3_PATH_VALUE']: + model.s3_default_block_size = None + model.s3_maximum_number_of_parts = None + model.s3_url_expiration = None + model.s3_region_name = None + model.s3_signature_version = None + @property def can_create(self): """Check permission for creating.""" diff --git a/modules/invenio-files-rest/invenio_files_rest/alembic/8644b32a3eec_add_column_files_location.py b/modules/invenio-files-rest/invenio_files_rest/alembic/8644b32a3eec_add_column_files_location.py new file mode 100644 index 0000000000..ec4c18fa48 --- /dev/null +++ b/modules/invenio-files-rest/invenio_files_rest/alembic/8644b32a3eec_add_column_files_location.py @@ -0,0 +1,35 @@ +# +# This file is part of Invenio. +# Copyright (C) 2016-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. + +"""add_column_files_location""" + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '8644b32a3eec' +down_revision = '8ae99b034410' +branch_labels = () +depends_on = None + + +def upgrade(): + """Upgrade database.""" + op.add_column('files_location', sa.Column('s3_default_block_size', sa.BigInteger(), nullable=True)) + op.add_column('files_location', sa.Column('s3_maximum_number_of_parts', sa.BigInteger(), nullable=True)) + op.add_column('files_location', sa.Column('s3_region_name', sa.String(length=128), nullable=True)) + op.add_column('files_location', sa.Column('s3_signature_version', sa.String(length=20), nullable=True)) + op.add_column('files_location', sa.Column('s3_url_expiration', sa.BigInteger(), nullable=True)) + +def downgrade(): + """Downgrade database.""" + op.drop_column('files_location', 's3_default_block_size') + op.drop_column('files_location', 's3_maximum_number_of_parts') + op.drop_column('files_location', 's3_region_name') + op.drop_column('files_location', 's3_signature_version') + op.drop_column('files_location', 's3_url_expiration') diff --git a/modules/invenio-files-rest/invenio_files_rest/config.py b/modules/invenio-files-rest/invenio_files_rest/config.py index b36e4bbd15..01f76c7a75 100644 --- a/modules/invenio-files-rest/invenio_files_rest/config.py +++ b/modules/invenio-files-rest/invenio_files_rest/config.py @@ -11,7 +11,7 @@ import tempfile from datetime import timedelta -MAX_CONTENT_LENGTH = 64424509440 +MAX_CONTENT_LENGTH = 64424509440 """Maximum allowed content length for form data. This value limits the maximum file upload size via multipart-formdata and is @@ -119,9 +119,13 @@ FILES_REST_TASK_WAIT_MAX_SECONDS = 600 """Maximum number of seconds to wait for a task to finish.""" -FILES_REST_LOCATION_TYPE_LIST = [('s3', 'Amazon S3')] +FILES_REST_LOCATION_TYPE_LIST = [('s3', 'S3 Path'), ('s3_vh', 'S3 Virtural Host')] """Location type list""" +FILES_REST_LOCATION_TYPE_S3_PATH_VALUE = 's3' + +FILES_REST_LOCATION_TYPE_S3_VIRTUAL_HOST_VALUE = 's3_vh' + FILES_REST_UPLOAD_OWNER_FACTORIES = 'invenio_files_rest.serializer.file_uploaded_owner' """file update version""" diff --git a/modules/invenio-files-rest/invenio_files_rest/models.py b/modules/invenio-files-rest/invenio_files_rest/models.py index e3842ce5eb..835bcbf43a 100644 --- a/modules/invenio-files-rest/invenio_files_rest/models.py +++ b/modules/invenio-files-rest/invenio_files_rest/models.py @@ -291,13 +291,23 @@ class Location(db.Model, Timestamp): s3_endpoint_url = db.Column(db.String(128), nullable=True) s3_send_file_directly = db.Column(db.Boolean(name='s3_send_file_directly'), nullable=False, default=True) - + size = db.Column(db.BigInteger, default=0, nullable=True) quota_size = db.Column(db.BigInteger, nullable=True) max_file_size = db.Column(db.BigInteger, nullable=True) + s3_default_block_size = db.Column(db.BigInteger, nullable=True) + + s3_maximum_number_of_parts = db.Column(db.BigInteger, nullable=True) + + s3_region_name = db.Column(db.String(128), nullable=True) + + s3_signature_version = db.Column(db.String(20), nullable=True) + + s3_url_expiration = db.Column(db.BigInteger, nullable=True) + @validates('name') def validate_name(self, key, name): """Validate name.""" @@ -313,6 +323,12 @@ def get_by_name(cls, name): name=name, ).one() + def get_by_uri(cls, uri): + """Fetch a specific location object by uri.""" + return cls.query.filter_by( + uri=uri, + ).one() + @classmethod def get_default(cls): """Fetch the default location object.""" @@ -758,6 +774,11 @@ def get_location_by_file_instance(cls): .filter(FileInstance.id == cls.id) \ .first() + @classmethod + def get_location_all(cls): + """Get all location .""" + return db.session.query(Location) + @classmethod def create(cls): """Create a file instance. @@ -934,6 +955,19 @@ def send_file(self, filename, restricted=True, mimetype=None, if not os.path.isfile(pdf_dir + pdf_filename): convert_dir = path+"/convert_"+str(self.id) target_uri = self.uri + tmp_uri = self.uri + if tmp_uri.startswith('https://'): + if tmp_uri.startswith('https://s3'): + # ex: https://s3.amazonaws.com/bucket_name/file_name + parts = tmp_uri.split('/') + tmp_uri = 's3://' + '/'.join(parts[3:]) + self.uri = tmp_uri + else: + # ex: https://bucket_name.s3.us-east-1.amazonaws.com/file_name + parts = tmp_uri.split('/') + sub_parts = parts[2].split('.') + tmp_uri = 's3://' + sub_parts[0] + '/' + '/'.join(parts[3:]) + self.uri = tmp_uri if self.uri.startswith("s3://"): target_uri = convert_dir+"/"+self.uri.split("/")[-1] if os.path.exists(convert_dir): diff --git a/modules/invenio-files-rest/invenio_files_rest/storage/pyfs.py b/modules/invenio-files-rest/invenio_files_rest/storage/pyfs.py index ab4d0e0871..03a246f3d3 100644 --- a/modules/invenio-files-rest/invenio_files_rest/storage/pyfs.py +++ b/modules/invenio-files-rest/invenio_files_rest/storage/pyfs.py @@ -38,10 +38,11 @@ class PyFSFileStorage(FileStorage): """ - def __init__(self, fileurl, size=None, modified=None, clean_dir=True): + def __init__(self, fileurl, size=None, modified=None, clean_dir=True, location=None): """Storage initialization.""" self.fileurl = fileurl self.clean_dir = clean_dir + self.location = location super(PyFSFileStorage, self).__init__(size=size, modified=modified) def _get_fs(self, create_dir=True): @@ -215,6 +216,7 @@ def pyfs_storage_factory(fileinstance=None, default_location=None, fileurl = fileinstance.uri else: assert default_location + tmp_uri = default_location # Generate a new URL. fileurl = make_path( default_location, @@ -224,8 +226,17 @@ def pyfs_storage_factory(fileinstance=None, default_location=None, current_app.config['FILES_REST_STORAGE_PATH_SPLIT_LENGTH'], ) + locationList = fileinstance.get_location_all() + location = next((loc for loc in locationList if str(loc.uri) == str(default_location)), None) + if location is None: + location = next((loc for loc in locationList if str(loc.uri) in str(fileurl)), None) + + print(locationList) + print(fileurl) + print('取得元') + print(location) return filestorage_class( - fileurl, size=size, modified=modified, clean_dir=clean_dir) + fileurl, size=size, modified=modified, clean_dir=clean_dir, location=location) def remove_dir_with_file(path): diff --git a/modules/invenio-files-rest/invenio_files_rest/templates/admin/location_edit.html b/modules/invenio-files-rest/invenio_files_rest/templates/admin/location_edit.html index 0caaf47bba..2cd8f4414c 100644 --- a/modules/invenio-files-rest/invenio_files_rest/templates/admin/location_edit.html +++ b/modules/invenio-files-rest/invenio_files_rest/templates/admin/location_edit.html @@ -16,8 +16,32 @@ var s3EndpointUrlInput = getElementByXpath("//label[@for='s3_endpoint_url']/..//input") var s3SendFileDirectlyField = getElementByXpath("//label[@for='s3_send_file_directly']/..") var s3SendFileDirectlyInput = getElementByXpath("//label[@for='s3_send_file_directly']/..//input") + var s3DefaultBlockSize = getElementByXpath("//label[@for='s3_default_block_size']/..") + var s3DefaultBlockSizeInput = getElementByXpath("//label[@for='s3_default_block_size']/..//input") + var s3MaximumNumberOfParts = getElementByXpath("//label[@for='s3_maximum_number_of_parts']/..") + var s3MaximumNumberOfPartsInput = getElementByXpath("//label[@for='s3_maximum_number_of_parts']/..//input") + var s3RegionName = getElementByXpath("//label[@for='s3_region_name']/..") + var s3RegionNameInput = getElementByXpath("//label[@for='s3_region_name']/..//input") + var s3SignatureVersion = getElementByXpath("//label[@for='s3_signature_version']/..") + var s3SignatureVersionInput = getElementByXpath("//label[@for='s3_signature_version']/..//input") + var s3UrlExpiration = getElementByXpath("//label[@for='s3_url_expiration']/..") + var s3UrlExpirationInput = getElementByXpath("//label[@for='s3_url_expiration']/..//input") + function checkLocationType() { - if (typeSpan.getAttribute('title') == 'Amazon S3') { + const labels = document.querySelectorAll('label'); + let content = ''; + content = s3MaximumNumberOfParts.textContent; + labels.forEach(label => { + const forId = label.getAttribute('for'); + if (forId === "s3_maximum_number_of_parts") { + content = label.textContent; + if (content.length > 8) { + content = content.slice(0, 8) + '\n' + content.slice(8); + label.textContent = content; + } + } + }); + if (typeSpan.getAttribute('title') == 'S3 Path') { accessKeyField.style.display = '' accessKeyInput.required = true secretKeyField.style.display = '' @@ -25,21 +49,55 @@ s3EndpointUrlField.style.display = '' s3SendFileDirectlyField.style.display = '' s3SendFileDirectlyInput.checked = true + if (s3DefaultBlockSizeInput.value === ''){ + s3DefaultBlockSizeInput.value = 5242880 + } + if (s3MaximumNumberOfPartsInput.value === ''){ + s3MaximumNumberOfPartsInput.value = 10000 + } + if (s3UrlExpirationInput.value === ''){ + s3UrlExpirationInput.value = 60 + } + } else if (typeSpan.getAttribute('title') == 'S3 Virtural Host') { + accessKeyField.style.display = '' + accessKeyInput.required = true + secretKeyField.style.display = '' + secretKeyInput.required = true + s3EndpointUrlField.style.display = 'none' + s3SendFileDirectlyField.style.display = '' + s3SendFileDirectlyInput.checked = true + if (s3DefaultBlockSizeInput.value === ''){ + s3DefaultBlockSizeInput.value = 5242880 + } + if (s3MaximumNumberOfPartsInput.value === ''){ + s3MaximumNumberOfPartsInput.value = 10000 + } + if (s3UrlExpirationInput.value === ''){ + s3UrlExpirationInput.value = 60 + } } else { accessKeyField.style.display = 'none' secretKeyField.style.display = 'none' s3EndpointUrlField.style.display = 'none' s3SendFileDirectlyField.style.display = 'none' + s3DefaultBlockSize.style.display = 'none' + s3MaximumNumberOfParts.style.display = 'none' + s3RegionName.style.display = 'none' + s3SignatureVersion.style.display = 'none' + s3UrlExpiration.style.display = 'none' accessKeyInput.value = null accessKeyInput.required = false secretKeyInput.value = null secretKeyInput.required = false s3EndpointUrlInput.value = null s3SendFileDirectlyInput.checked = true + s3DefaultBlockSizeInput.value = null + s3MaximumNumberOfPartsInput.value = null + s3RegionNameInput.value = null + s3UrlExpirationInput.value = null } } checkLocationType() - typeSpan.addEventListener('DOMSubtreeModified', checkLocationType) } {% endblock %} diff --git a/modules/invenio-files-rest/requirements2.txt b/modules/invenio-files-rest/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-files-rest/requirements2.txt +++ b/modules/invenio-files-rest/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-iiif/requirements2.txt b/modules/invenio-iiif/requirements2.txt index 41c12fbcfe..cd3f5baf07 100644 --- a/modules/invenio-iiif/requirements2.txt +++ b/modules/invenio-iiif/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -200,6 +201,8 @@ python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-indexer/invenio_indexer/api.py b/modules/invenio-indexer/invenio_indexer/api.py index d0fe39cde6..be57694e68 100644 --- a/modules/invenio-indexer/invenio_indexer/api.py +++ b/modules/invenio-indexer/invenio_indexer/api.py @@ -185,6 +185,7 @@ def process_bulk_queue(self, es_bulk_kwargs=None,with_deleted=False): :param dict es_bulk_kwargs: Passed to :func:`elasticsearch:elasticsearch.helpers.bulk`. """ + from weko_deposit.utils import update_pdf_contents_es success = 0 fail = 0 self.count = 0 @@ -208,15 +209,18 @@ def process_bulk_queue(self, es_bulk_kwargs=None,with_deleted=False): es_bulk_kwargs = es_bulk_kwargs or {} with consumer: try: + messages = list(consumer.iterqueue()) + ids = [message.decode().get("id") for message in messages] _success,_fail = bulk( self.client, - self._actionsiter(consumer.iterqueue(),with_deleted=with_deleted), + self._actionsiter(messages,with_deleted=with_deleted), stats_only=True, request_timeout=req_timeout, # raise_on_error=True, # raise_on_exception=True, **es_bulk_kwargs ) + update_pdf_contents_es(ids) success = success + _success fail = fail + _fail except BulkIndexError as be: @@ -237,9 +241,12 @@ def process_bulk_queue(self, es_bulk_kwargs=None,with_deleted=False): # raise_on_exception=True, **es_bulk_kwargs ) + update_pdf_contents_es(error_ids) success = success + _success fail = fail + _fail except BulkIndexError as be2: + success_retrys = list(set(error_ids)-set([error['index']['_id'] for error in be2.errors])) + update_pdf_contents_es(success_retrys) success = success + (len(error_ids)-len(be2.errors)) fail = fail + len(be2.errors) for error in be2.errors: @@ -260,6 +267,7 @@ def process_bulk_queue(self, es_bulk_kwargs=None,with_deleted=False): # raise_on_exception=True, **es_bulk_kwargs ) + update_pdf_contents_es(error_ids) success = success + _success fail = fail + _fail except ConnectionTimeout as ce: diff --git a/modules/invenio-indexer/requirements.txt b/modules/invenio-indexer/requirements.txt index 97f8290c6b..6b5c0e0eb5 100644 --- a/modules/invenio-indexer/requirements.txt +++ b/modules/invenio-indexer/requirements.txt @@ -70,7 +70,7 @@ ipython==6.2.1 ipython-genutils==0.2.0 itsdangerous==0.24 # moto==3.1.2 -moto==1.3.5 +moto==3.1.2 jedi==0.11.0 Jinja2==2.10 jinja2-cli==0.6.0 @@ -161,7 +161,7 @@ Flask-Plugins==1.6.1 bibtexparser==1.0.1 github3.py==1.1.0 feedgen==0.7.0 -boto3==1.7.84 +boto3==1.9.83 s3fs==0.1.6 #fpdf==1.7.2 -e git+https://github.com/RCOSDP/pyfpdf.git@fix/nii#egg=fpdf diff --git a/modules/invenio-indexer/requirements2.txt b/modules/invenio-indexer/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-indexer/requirements2.txt +++ b/modules/invenio-indexer/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-mail/requirements2.txt b/modules/invenio-mail/requirements2.txt index 41c12fbcfe..cd3f5baf07 100644 --- a/modules/invenio-mail/requirements2.txt +++ b/modules/invenio-mail/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -200,6 +201,8 @@ python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-oaiharvester/invenio_oaiharvester/admin.py b/modules/invenio-oaiharvester/invenio_oaiharvester/admin.py index e683975692..a2dc01c828 100644 --- a/modules/invenio-oaiharvester/invenio_oaiharvester/admin.py +++ b/modules/invenio-oaiharvester/invenio_oaiharvester/admin.py @@ -254,7 +254,7 @@ def _index_filter(self): index = Indexes.get_child_list_recursive(repository.root_node_id) index_list.extend(index) return self.model.index_id.in_([int(index) for index in index_list]) - + details_template = 'invenio_oaiharvester/details.html' edit_template = 'invenio_oaiharvester/edit.html' can_create = True @@ -288,7 +288,7 @@ def _index_filter(self): 'OAIHARVESTER_UPDATE_STYLE_OPTIONS'].items()), auto_distribution=LazyChoices(lambda: current_app.config[ 'OAIHARVESTER_AUTO_DISTRIBUTION_OPTIONS'].items())) - + form_args = { 'target_index': { 'query_factory': index_query diff --git a/modules/invenio-oaiharvester/requirements2.txt b/modules/invenio-oaiharvester/requirements2.txt index 1aa00ff2df..cd3f5baf07 100644 --- a/modules/invenio-oaiharvester/requirements2.txt +++ b/modules/invenio-oaiharvester/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,17 +194,18 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 -responses==0.10.3 rocrate==0.6.0 resync==2.0.1 s3fs==0.1.6 diff --git a/modules/invenio-oaiharvester/tests/conftest.py b/modules/invenio-oaiharvester/tests/conftest.py index f9ad6c4cdf..1cbc985c8f 100644 --- a/modules/invenio-oaiharvester/tests/conftest.py +++ b/modules/invenio-oaiharvester/tests/conftest.py @@ -68,7 +68,7 @@ from weko_theme import WekoTheme from weko_deposit import WekoDeposit from weko_records import WekoRecords -from weko_records.api import ItemsMetadata +from weko_records.api import ItemsMetadata from weko_records_ui.config import WEKO_PERMISSION_SUPER_ROLE_USER @@ -213,7 +213,7 @@ def users(app, db): originalroleuser = create_test_user(email='originalroleuser@test.org') originalroleuser2 = create_test_user(email='originalroleuser2@test.org') student = User.query.filter_by(email='student@test.org').first() - + role_count = Role.query.filter_by(name='System Administrator').count() if role_count != 1: sysadmin_role = ds.create_role(name='System Administrator') @@ -349,7 +349,7 @@ def base_index(id, parent, position, public_date=None, coverpage_state=False, re online_issn=online_issn, harvest_spec=harvest_spec ) - + with db.session.begin_nested(): db.session.add(base_index(1, 0, 0, datetime(2022, 1, 1), True, True, True, True, True, '1234-5678')) db.session.add(base_index(2, 0, 1)) @@ -810,7 +810,7 @@ def reset_class_value(): ) BaseMapper.itemtype_map = {} BaseMapper.identifiers = [] - + DCMapper.itemtype_map = {} DCMapper.identifiers = [] DDIMapper.itemtype_map = {} @@ -829,7 +829,7 @@ def create_record(db, record_data, item_data): db.session.add(rel) parent=None doi = None - + if '.' in record_data["recid"]: parent = PersistentIdentifier.get("recid",int(float(record_data["recid"]))) recid_p = PIDRelation.get_child_relations(parent).one_or_none() @@ -851,7 +851,7 @@ def create_record(db, record_data, item_data): deposit = WekoDeposit(record, record.model) deposit.commit() - + return recid, depid, record, item, parent, doi, deposit @pytest.fixture() @@ -859,11 +859,10 @@ def db_records(app,db): record_datas = list() with open("tests/data/test_record/record_metadata.json") as f: record_datas = json.load(f) - + item_datas = list() with open("tests/data/test_record/item_metadata.json") as f: item_datas = json.load(f) - + for i in range(len(record_datas)): recid, depid, record, item, parent, doi, deposit = create_record(db,record_datas[i],item_datas[i]) - \ No newline at end of file diff --git a/modules/invenio-oaiharvester/tests/test_admin.py b/modules/invenio-oaiharvester/tests/test_admin.py index d181cb0164..a54505bfa4 100644 --- a/modules/invenio-oaiharvester/tests/test_admin.py +++ b/modules/invenio-oaiharvester/tests/test_admin.py @@ -26,7 +26,7 @@ def test_admin(app, db): """Test Flask-Admin interace.""" - + admin = Admin(app, name='Test') assert 'model' in harvest_admin_view @@ -148,17 +148,17 @@ def test_run_stats(app,db): class MockM: def __init__(self,id): self.id=id - + test_func = run_stats() result = test_func(None,None,MockM(1),None) assert result == "Harvesting is not running" - + result = test_func(None,None,MockM(2),None) assert result == "Harvesting is paused with resumption token: test_token" - + result = test_func(None,None,MockM(3),None) assert result == "Harvesting is running at task id:test_task
1 items processed" - + # .tox/c1/bin/pytest --cov=invenio_oaiharvester tests/test_admin.py::test_control_btns -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-oaiharvester/.tox/c1/tmp def test_control_btns(app,db): @@ -169,7 +169,7 @@ def test_control_btns(app,db): model = copy_adminview.pop('model') view = copy_adminview.pop('modelview') admin.add_view(view(model, db.session, **copy_adminview)) - + index = Index() db.session.add(index) db.session.commit() @@ -200,21 +200,21 @@ def test_control_btns(app,db): ) db.session.add_all([not_task,resumption,task]) db.session.commit() - + class MockM: def __init__(self,id): self.id=id - + test_func = control_btns() # task_id is None, resumption_token is None result = test_func(None,None,MockM(1),None) assert result == 'Run' - + # task_id is None, resumption_token is not None result = test_func(None,None,MockM(2),None) assert result == 'Resume'\ 'Clear' - + # task_id is not None, resumption_token is not None result = test_func(None,None,MockM(3),None) assert result == 'Pause' @@ -282,7 +282,7 @@ def test_pause(self,app,db,setup_admin,mocker): assert res.status_code == 200 mock_revoke.assert_called_with("test_task",terminate=True) mock_redirect.assert_called_with("/admin/harvestsettings/details/?id=1") - + # .tox/c1/bin/pytest --cov=invenio_oaiharvester tests/test_admin.py::TestHarvestSettingView::test_clear -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-oaiharvester/.tox/c1/tmp def test_clear(self,app,db,setup_admin,mocker): url = url_for("harvestsettings.clear",id=1) @@ -303,9 +303,9 @@ def test_clear(self,app,db,setup_admin,mocker): ) db.session.add(logs) db.session.commit() - + mocker.patch("invenio_oaiharvester.admin.send_run_status_mail") - + with app.test_client() as client: mock_redirect = mocker.patch("invenio_oaiharvester.admin.redirect",return_value=make_response()) res = client.get(url) @@ -315,7 +315,7 @@ def test_clear(self,app,db,setup_admin,mocker): assert harvesting.task_id == None logs = HarvestLogs.query.filter_by(id=1).first() assert logs.status == "Cancel" - + # .tox/c1/bin/pytest --cov=invenio_oaiharvester tests/test_admin.py::TestHarvestSettingView::test_get_logs -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-oaiharvester/.tox/c1/tmp def test_get_logs(self,app,db,setup_admin): url = url_for("harvestsettings.get_logs",id=1) @@ -330,7 +330,7 @@ def test_get_logs(self,app,db,setup_admin): ) db.session.add_all([logs1,logs2]) db.session.commit() - + with app.test_client() as client: test = [ {"counter":{},"end_time":None,"errmsg":None,"harvest_setting_id":1,"id":2,"requrl":None,"setting":{},"start_time":"2021-01-10T20:22:33+09:00","status":"Running"}, @@ -373,7 +373,7 @@ def test_set_schedule(self,app,db,setup_admin,mocker): db.session.commit() with app.test_client() as client: - + data = { "dis_enable_schedule":"True", "frequency":"daily" @@ -399,7 +399,7 @@ def test_set_schedule(self,app,db,setup_admin,mocker): assert setting.schedule_enable == False assert setting.schedule_frequency == "weekly" assert setting.schedule_details == 1 - + data = { "dis_enable_schedule":"True", "frequency":"monthly", @@ -420,7 +420,7 @@ def test_get_query(self,app,db,users,mocker): model = copy_adminview.pop('model') view = copy_adminview.pop('modelview') view = view(model, db.session, **copy_adminview) - + index = Index() db.session.add(index) db.session.commit() @@ -434,14 +434,14 @@ def test_get_query(self,app,db,users,mocker): ) db.session.add(setting) db.session.commit() - + # super role user user = users[0]["obj"] mocker.patch("flask_login.utils._get_user",return_value=user) query = view.get_query() assert query.count() == 1 assert query.first() == setting - + # community role user with repository user = users[2]["obj"] repository = Community(root_node_id=index.id) @@ -451,25 +451,25 @@ def test_get_query(self,app,db,users,mocker): query = view.get_query() assert query.count() == 1 assert query.first() == setting - + # community role user with no repository mocker.patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[]) query = view.get_query() assert query.count() == 0 - + # community role user with repository but no index mocker.patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[repository]) mocker.patch("weko_index_tree.api.Indexes.get_child_list_recursive", return_value=[]) query = view.get_query() assert query.count() == 0 - + # .tox/c1/bin/pytest --cov=invenio_oaiharvester tests/test_admin.py::TestHarvestSettingView::test_get_count_query -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-oaiharvester/.tox/c1/tmp def test_get_count_query(self,app,db,users,mocker): copy_adminview = copy.deepcopy(harvest_admin_view) model = copy_adminview.pop('model') view = copy_adminview.pop('modelview') view = view(model, db.session, **copy_adminview) - + index = Index() db.session.add(index) db.session.commit() @@ -483,13 +483,13 @@ def test_get_count_query(self,app,db,users,mocker): ) db.session.add(setting) db.session.commit() - + # super role user user = users[0]["obj"] mocker.patch("flask_login.utils._get_user",return_value=user) query = view.get_count_query() assert query.scalar() == 1 - + # community role user with repository user = users[2]["obj"] repository = Community(root_node_id=index.id) @@ -498,12 +498,12 @@ def test_get_count_query(self,app,db,users,mocker): mocker.patch("weko_index_tree.api.Indexes.get_child_list_recursive", return_value=[index.id]) query = view.get_count_query() assert query.scalar() == 1 - + # community role user with no repository mocker.patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[]) query = view.get_count_query() assert query.scalar() == 0 - + # community role user with repository but no index mocker.patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[repository]) mocker.patch("weko_index_tree.api.Indexes.get_child_list_recursive", return_value=[]) @@ -519,7 +519,7 @@ def test_index_query(app, db, users, mocker): db.session.add(index1) db.session.add(index2) db.session.commit() - + # super role user user = users[0]["obj"] mocker.patch("flask_login.utils._get_user",return_value=user) @@ -527,7 +527,7 @@ def test_index_query(app, db, users, mocker): assert len(result) == 2 assert index1 in result assert index2 in result - + # community role user with repository repository = Community(root_node_id=index1.id) user = users[2]["obj"] @@ -537,25 +537,25 @@ def test_index_query(app, db, users, mocker): result = index_query() assert len(result) == 1 assert index1 in result - + # community role user with no repository mocker.patch("invenio_communities.models.Community.get_repositories_by_user",return_value=[]) result = index_query() assert len(result) == 0 - + # community role user with repository but no index mocker.patch("invenio_communities.models.Community.get_repositories_by_user",return_value=[repository]) mocker.patch("weko_index_tree.api.Indexes.get_child_list_recursive", return_value=[]) result = index_query() assert len(result) == 0 - + # get_repositories_by_user raise exception mocker.patch("invenio_communities.models.Community.get_repositories_by_user",side_effect=Exception) with pytest.raises(Exception): result = index_query() - + # get_child_list_recursive raise exception mocker.patch("weko_index_tree.api.Indexes.get_child_list_recursive",side_effect=Exception) with pytest.raises(Exception): - result = index_query() \ No newline at end of file + result = index_query() diff --git a/modules/invenio-oaiserver/invenio_oaiserver/query.py b/modules/invenio-oaiserver/invenio_oaiserver/query.py index 8fcaa5ebd2..9c492bca1d 100644 --- a/modules/invenio-oaiserver/invenio_oaiserver/query.py +++ b/modules/invenio-oaiserver/invenio_oaiserver/query.py @@ -121,6 +121,39 @@ def add_condition_doi_and_future_date(query): **{'must_not': [ {'term': {'_id': str(record.id)}}]}) + def get_descendant_ids(index_id): + """Get all descendant index IDs using CTE.""" + from sqlalchemy.orm import aliased + from invenio_db import db + # Create an alias for the Index table + parent = aliased(Index) + child = aliased(Index) + + # Define the CTE for recursive query + cte = db.session.query( + parent.id.label('ancestor_id'), + child.id.label('descendant_id') + ).filter( + parent.id == index_id, + child.parent == parent.id + ).cte(name='descendants', recursive=True) + + # Recursive part of the CTE + cte = cte.union_all( + db.session.query( + cte.c.ancestor_id, + child.id + ).filter( + child.parent == cte.c.descendant_id + ) + ) + + # Query the CTE to get all descendant IDs + descendant_ids = db.session.query(cte.c.descendant_id).all() + + return [descendant_id[0] for descendant_id in descendant_ids] + + page_ = kwargs.get('resumptionToken', {}).get('page', 1) size_ = current_app.config['OAISERVER_PAGE_SIZE'] scroll = current_app.config['OAISERVER_RESUMPTION_TOKEN_EXPIRE_TIME'] @@ -139,15 +172,27 @@ def add_condition_doi_and_future_date(query): {'control_number': {'order': 'asc'}} )[(page_ - 1) * size_:page_ * size_] - sets = [] if 'set' in kwargs: - if ":" in kwargs['set']: - sets = kwargs['set'].split(':')[-1] + if kwargs['set'][0].isdigit(): + # set is index_id + if ":" in kwargs['set']: + sets = kwargs['set'].split(':')[-1] + else: + sets = kwargs['set'] else: - sets = kwargs['set'] + # set is community_id + from .utils import get_community_index_from_set + sets = get_community_index_from_set(kwargs['set']) + #search = search.query('match', **{'path': kwargs['set']}) - search = search.query('match', **{'_oai.sets': sets}) + #search = search.query('match', **{'_oai.sets': sets}) #search = search.query('terms', **{'_oai.sets': sets}) + + if not sets: + search = search.query('match_none') + else: + index_ids = [sets] + get_descendant_ids(sets) + search = search.query('terms', **{'_oai.sets': index_ids}) time_range = {} if 'from_' in kwargs: diff --git a/modules/invenio-oaiserver/invenio_oaiserver/response.py b/modules/invenio-oaiserver/invenio_oaiserver/response.py index 3e91ca6d00..c0de940939 100644 --- a/modules/invenio-oaiserver/invenio_oaiserver/response.py +++ b/modules/invenio-oaiserver/invenio_oaiserver/response.py @@ -36,7 +36,7 @@ from .resumption_token import serialize from .utils import HARVEST_PRIVATE, OUTPUT_HARVEST, PRIVATE_INDEX, \ datetime_to_datestamp, get_index_state, handle_license_free, \ - is_output_harvest, serializer + is_output_harvest, serializer, get_community_index_from_set NS_OAIPMH = 'http://www.openarchives.org/OAI/2.0/' NS_OAIPMH_XSD = 'http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd' @@ -476,9 +476,16 @@ def listidentifiers(**kwargs): set_is_output = 0 if 'set' in kwargs: set_obj = OAISet.get_set_by_spec(kwargs['set']) + if not set_obj: + com_prefix = current_app.config.get('COMMUNITIES_OAI_FORMAT').replace('{community_id}', '') + set_obj = OAISet.get_set_by_spec(com_prefix + kwargs['set']) if not set_obj: return error(get_error_code_msg(), **kwargs) - path = kwargs['set'].replace(':', '/') + set_value = kwargs['set'] + if set_value and set_value[0].isdigit(): + path = set_value.replace(':', '/') + else: + path = get_community_index_from_set(set_value) set_is_output = is_output_harvest([path], index_state) if set_is_output == HARVEST_PRIVATE: return error(get_error_code_msg(), **kwargs) @@ -495,8 +502,7 @@ def listidentifiers(**kwargs): set_identifier(record, record) path_list = record.get('path') if 'path' in record else [] - _is_output = is_output_harvest(path_list, index_state) \ - if 'set' not in kwargs else set_is_output + _is_output = is_output_harvest(path_list, index_state) current_app.logger.debug("pid:{}".format(pid)) current_app.logger.debug("_is_output:{}".format(_is_output)) current_app.logger.debug("path_list:{}".format(path_list)) @@ -573,10 +579,16 @@ def listrecords(**kwargs): set_is_output = 0 if 'set' in kwargs: set_obj = OAISet.get_set_by_spec(kwargs['set']) + if not set_obj: + com_prefix = current_app.config.get('COMMUNITIES_OAI_FORMAT').replace('{community_id}', '') + set_obj = OAISet.get_set_by_spec(com_prefix + kwargs['set']) if not set_obj: return error(get_error_code_msg(), **kwargs) - current_app.logger.debug("set: {}".format(set_obj.spec)) - path = kwargs['set'].replace(':', '/') + set_value = kwargs['set'] + if set_value and set_value[0].isdigit(): + path = set_value.replace(':', '/') + else: + path = get_community_index_from_set(set_value) set_is_output = is_output_harvest([path], index_state) current_app.logger.debug("set_is_output: {}".format(set_is_output)) if set_is_output == HARVEST_PRIVATE: @@ -593,8 +605,7 @@ def listrecords(**kwargs): record = WekoRecord.get_record_by_uuid(pid_object.object_uuid) set_identifier(record, record) path_list = record.get('path') if 'path' in record else [] - _is_output = is_output_harvest(path_list, index_state) \ - if 'set' not in kwargs else set_is_output + _is_output = is_output_harvest(path_list, index_state) current_app.logger.debug("pid:{}".format(pid)) current_app.logger.debug("_is_output:{}".format(_is_output)) diff --git a/modules/invenio-oaiserver/invenio_oaiserver/utils.py b/modules/invenio-oaiserver/invenio_oaiserver/utils.py index 3e6634e3e5..43c329f1a6 100644 --- a/modules/invenio-oaiserver/invenio_oaiserver/utils.py +++ b/modules/invenio-oaiserver/invenio_oaiserver/utils.py @@ -254,3 +254,20 @@ def _check(index_id): index_id = path.split('/')[-1] result = max([result, _check(index_id)]) return result if result != 0 else HARVEST_PRIVATE + + +def get_community_index_from_set(set): + """Get community index from set. + + Args: + set (str): Set string. + + Returns: + str: index_id of community. + """ + from invenio_communities.models import Community + com_prefix = current_app.config['COMMUNITIES_OAI_FORMAT'].replace( + '{community_id}', '') + com_id = set.replace(com_prefix, '') + com = Community.query.filter_by(id=com_id).first() + return str(com.root_node_id) if com else None \ No newline at end of file diff --git a/modules/invenio-oaiserver/requirements2.txt b/modules/invenio-oaiserver/requirements2.txt index 41c12fbcfe..cd3f5baf07 100644 --- a/modules/invenio-oaiserver/requirements2.txt +++ b/modules/invenio-oaiserver/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -200,6 +201,8 @@ python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-oaiserver/tests/conftest.py b/modules/invenio-oaiserver/tests/conftest.py index 5ec6b2c9c4..545ffae7a9 100644 --- a/modules/invenio-oaiserver/tests/conftest.py +++ b/modules/invenio-oaiserver/tests/conftest.py @@ -30,6 +30,7 @@ from invenio_accounts.models import User, Role from invenio_accounts.testutils import create_test_user from invenio_access.models import ActionUsers,ActionRoles +from invenio_communities.config import COMMUNITIES_OAI_FORMAT from invenio_communities.models import Community from invenio_db import InvenioDB from invenio_db import db as db_ @@ -100,7 +101,8 @@ def base_app(instance_path): INDEXER_DEFAULT_INDEX="{}-weko-item-v1.0.0".format("test"), SEARCH_UI_SEARCH_INDEX="{}-weko".format("test"), SEARCH_ELASTIC_HOSTS="elasticsearch", - SEARCH_INDEX_PREFIX="test-" + SEARCH_INDEX_PREFIX="test-", + COMMUNITIES_OAI_FORMAT=COMMUNITIES_OAI_FORMAT, ) if not hasattr(app_, 'cli'): from flask_cli import FlaskCLI @@ -408,6 +410,8 @@ def records(app, db): } } }, + "_oai":{"sets":[]}, + "item_type_id":"1" }, { "publish_date":"2000-08-09", @@ -422,7 +426,9 @@ def records(app, db): } } } - } + }, + "_oai":{"sets":[]}, + "item_type_id":"1" }, { "path":["1557819692844"], diff --git a/modules/invenio-oaiserver/tests/test_query.py b/modules/invenio-oaiserver/tests/test_query.py index fae1d2f3dd..63c18b2515 100644 --- a/modules/invenio-oaiserver/tests/test_query.py +++ b/modules/invenio-oaiserver/tests/test_query.py @@ -180,3 +180,127 @@ def test_get_records(es_app,db, mock_execute): assert result.next_num == 2 result_items = [r for r in result.items] assert result_items == test + +# .tox/c1/bin/pytest --cov=invenio_oaiserver tests/test_query.py::test_get_records_with_set -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-oaiserver/.tox/c1/tmp +def test_get_records_with_set(es_app,db, users): + from elasticsearch import Elasticsearch + from invenio_communities.models import Community + indexes = list() + ids = [123,456,789] + for i in range(3): + indexes.append( + Index( + id=ids[i], + parent=ids[i-1] if i > 0 else 0, + position=1, + index_name_english=f"test_index{i}", + index_link_name_english=f"test_index_link{i}", + harvest_public_state=True, + public_state=True, + public_date=datetime(2000,1,1), + browsing_role="3,-99" + ) + ) + + rec_uuid1 = uuid.uuid4() + rec_data1 = {"title":["test_item1"], + "path":["123"], + "_oai":{"id":"oai:test:00001","sets":["123"]}, + "relation_version_is_last":"true", + "control_number":"1", + "publish_status":"0", + "_updated": "2022-01-01T00:00:00" + } + rec1 = RecordMetadata(id=rec_uuid1,json=rec_data1) + + rec_uuid2 = uuid.uuid4() + rec_data2 = {"title":["test_item2"], + "path":["456"], + "_oai":{"id":"oai:test:00002", "sets":["456"]}, + "relation_version_is_last":"true", + "control_number":"2", + "publish_status":"0", + "_updated": "2022-01-01T00:00:00" + } + rec2 = RecordMetadata(id=rec_uuid2,json=rec_data2) + + rec_uuid3 = uuid.uuid4() + rec_data3 = {"title":["test_item3"], + "path":["789"], + "_oai":{"id":"oai:test:00003", "sets":["789"]}, + "relation_version_is_last":"true", + "control_number":"3", + "publish_status":"0", + "_updated": "2022-01-01T00:00:00" + } + rec3 = RecordMetadata(id=rec_uuid3,json=rec_data3) + + db.session.add_all(indexes) + db.session.add(rec1) + db.session.add(rec2) + db.session.add(rec3) + db.session.commit() + + es_info = dict(index=current_app.config['INDEXER_DEFAULT_INDEX'], + doc_type=current_app.config['INDEXER_DEFAULT_DOCTYPE'], + version=1, + version_type="external_gte", + refresh="wait_for") + body1 = dict(id=str(rec_uuid1), body=rec_data1) + body2 = dict(id=str(rec_uuid2), body=rec_data2) + body3 = dict(id=str(rec_uuid3), body=rec_data3) + current_search_client.index(**es_info,**body1) + current_search_client.index(**es_info,**body2) + current_search_client.index(**es_info,**body3) + + comm1 = Community.create(community_id="test_comm", role_id=users[0]["id"], + id_user=users[0]["id"], title="test community", + description="this is test community", + root_node_id=indexes[0].id) + db.session.add(comm1) + db.session.commit() + + data = {"set":"123"} + result = get_records(**data) + assert result.total == 3 + result_items = [r for r in result.items] + assert result_items[0]["json"]["_source"] == rec_data1 + assert result_items[1]["json"]["_source"] == rec_data2 + assert result_items[2]["json"]["_source"] == rec_data3 + + data = {"set":"123:456"} + result = get_records(**data) + assert result.total == 2 + result_items = [r for r in result.items] + assert result_items[0]["json"]["_source"] == rec_data2 + assert result_items[1]["json"]["_source"] == rec_data3 + + data = {"set":"123:456:789"} + result = get_records(**data) + assert result.total == 1 + result_items = [r for r in result.items] + assert result_items[0]["json"]["_source"] == rec_data3 + + data = {"set":"user-test_comm"} + result = get_records(**data) + assert result.total == 3 + result_items = [r for r in result.items] + assert result_items[0]["json"]["_source"] == rec_data1 + assert result_items[1]["json"]["_source"] == rec_data2 + assert result_items[2]["json"]["_source"] == rec_data3 + + data = {"set":"test_comm"} + result = get_records(**data) + assert result.total == 3 + result_items = [r for r in result.items] + assert result_items[0]["json"]["_source"] == rec_data1 + assert result_items[1]["json"]["_source"] == rec_data2 + assert result_items[2]["json"]["_source"] == rec_data3 + + data = {"set":"999"} + result = get_records(**data) + assert result.total == 0 + + data = {"set":"aaa"} + result = get_records(**data) + assert result.total == 0 diff --git a/modules/invenio-oaiserver/tests/test_response.py b/modules/invenio-oaiserver/tests/test_response.py index 9444eec88a..7790ab92a5 100644 --- a/modules/invenio-oaiserver/tests/test_response.py +++ b/modules/invenio-oaiserver/tests/test_response.py @@ -674,6 +674,12 @@ def items(self): assert res.xpath('/x:OAI-PMH/x:ListIdentifiers/x:header[6]/x:identifier/text()', namespaces=NAMESPACES) == [str(records[6][0])] assert len(res.xpath('/x:OAI-PMH/x:ListIdentifiers/x:header[6][@status="deleted"]', namespaces=NAMESPACES)) == 1 + with patch("invenio_oaiserver.response.is_deleted_workflow", return_value=False): + with patch("invenio_oaiserver.response.is_private_workflow", return_value=False): + with patch("invenio_oaiserver.response.is_pubdate_in_future", return_value=False): + res=listidentifiers(**kwargs) + assert len(res.xpath('/x:OAI-PMH/x:ListIdentifiers/x:header', namespaces=NAMESPACES)) == 6 + # etree_reocrd.get("system_identifier_doi") with patch("invenio_oaiserver.response.is_exists_doi",return_value=True): res=listidentifiers(**kwargs) @@ -697,6 +703,40 @@ def items(self): assert res.xpath('/x:OAI-PMH/x:ListIdentifiers/x:header[5]/x:identifier/text()', namespaces=NAMESPACES) == [] assert len(res.xpath('/x:OAI-PMH/x:ListIdentifiers/x:header[5][@status="deleted"]', namespaces=NAMESPACES)) == 0 + # community id in set + with patch("invenio_oaiserver.response.get_records",return_value=MockPagenation(dummy_data)),\ + patch("invenio_oaiserver.response.is_output_harvest",return_value=OUTPUT_HARVEST),\ + patch("invenio_oaiserver.response.is_exists_doi",return_value=False): + # community id with prefix + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",return_value=OAISet(spec="user-test_comm")): + with patch("invenio_oaiserver.response.get_community_index_from_set",return_value="1557819692844"): + kwargs_1 = dict( + metadataPrefix='jpcoar_1.0', + verb="ListIdentifiers", + set="user-test_comm", + ) + res=listidentifiers(**kwargs_1) + assert len(res.xpath('/x:OAI-PMH/x:ListIdentifiers/x:header', namespaces=NAMESPACES)) == 6 + # community id without prefix + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",side_effect=[None, OAISet(spec="user-test_comm")]): + with patch("invenio_oaiserver.response.get_community_index_from_set",return_value="1557819692844"): + kwargs_1.update(set="test_comm") + res=listidentifiers(**kwargs_1) + assert len(res.xpath('/x:OAI-PMH/x:ListIdentifiers/x:header', namespaces=NAMESPACES)) == 6 + # community id not found + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",side_effect=[None, None]): + res=listidentifiers(**kwargs_1) + assert res.xpath("/x:OAI-PMH/x:error",namespaces=NAMESPACES)[0].attrib["code"] == "noRecordsMatch" + # index id with ':' + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",return_value=OAISet(spec="123:1557819692844")): + kwargs_1.update(set="123:1557819692844") + res=listidentifiers(**kwargs_1) + assert len(res.xpath('/x:OAI-PMH/x:ListIdentifiers/x:header', namespaces=NAMESPACES)) == 6 + # index id not found + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",return_value=None): + kwargs_1.update(set="123") + res=listidentifiers(**kwargs_1) + assert res.xpath("/x:OAI-PMH/x:error",namespaces=NAMESPACES)[0].attrib["code"] == "noRecordsMatch" # not identify with patch("invenio_oaiserver.response.OaiIdentify.get_all",return_value=None): @@ -975,6 +1015,12 @@ def items(self): assert res.xpath('/x:OAI-PMH/x:ListRecords/x:record[6]/x:header/x:identifier/text()', namespaces=NAMESPACES) == [str(records[6][0])] assert len(res.xpath('/x:OAI-PMH/x:ListRecords/x:record[6]/x:header[@status="deleted"]', namespaces=NAMESPACES)) == 1 + with patch("invenio_oaiserver.response.is_deleted_workflow", return_value=False): + with patch("invenio_oaiserver.response.is_private_workflow", return_value=False): + with patch("invenio_oaiserver.response.is_pubdate_in_future", return_value=False): + res=listrecords(**kwargs) + assert len(res.xpath('/x:OAI-PMH/x:ListRecords/x:record', namespaces=NAMESPACES)) == 6 + # etree_reocrd.get("system_identifier_doi") with patch("invenio_oaiserver.response.is_exists_doi",return_value=True): res=listrecords(**kwargs) @@ -998,6 +1044,40 @@ def items(self): assert res.xpath('/x:OAI-PMH/x:ListRecords/x:record[5]/x:header/x:identifier/text()', namespaces=NAMESPACES) == [] assert len(res.xpath('/x:OAI-PMH/x:ListRecords/x:record[5]/x:header[@status="deleted"]', namespaces=NAMESPACES)) == 0 + # community id in set + with patch("invenio_oaiserver.response.get_records",return_value=MockPagenation(dummy_data)),\ + patch("invenio_oaiserver.response.is_output_harvest",return_value=OUTPUT_HARVEST),\ + patch("invenio_oaiserver.response.is_exists_doi",return_value=False): + # community id with prefix + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",return_value=OAISet(spec="user-test_comm")): + with patch("invenio_oaiserver.response.get_community_index_from_set",return_value="1557819692844"): + kwargs_1 = dict( + metadataPrefix='jpcoar_1.0', + verb="ListRecords", + set="user-test_comm", + ) + res=listrecords(**kwargs_1) + assert len(res.xpath('/x:OAI-PMH/x:ListRecords/x:record', namespaces=NAMESPACES)) == 6 + # community id without prefix + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",side_effect=[None, OAISet(spec="user-test_comm")]): + with patch("invenio_oaiserver.response.get_community_index_from_set",return_value="1557819692844"): + kwargs_1.update(set="test_comm") + res=listrecords(**kwargs_1) + assert len(res.xpath('/x:OAI-PMH/x:ListRecords/x:record', namespaces=NAMESPACES)) == 6 + # community id not found + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",side_effect=[None, None]): + res=listrecords(**kwargs_1) + assert res.xpath("/x:OAI-PMH/x:error",namespaces=NAMESPACES)[0].attrib["code"] == "noRecordsMatch" + # index id with ':' + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",return_value=OAISet(spec="123:1557819692844")): + kwargs_1.update(set="123:1557819692844") + res=listrecords(**kwargs_1) + assert len(res.xpath('/x:OAI-PMH/x:ListRecords/x:record', namespaces=NAMESPACES)) == 6 + # index id not found + with patch("invenio_oaiserver.response.OAISet.get_set_by_spec",return_value=None): + kwargs_1.update(set="123") + res=listrecords(**kwargs_1) + assert res.xpath("/x:OAI-PMH/x:error",namespaces=NAMESPACES)[0].attrib["code"] == "noRecordsMatch" # not identify with patch("invenio_oaiserver.response.OaiIdentify.get_all",return_value=None): diff --git a/modules/invenio-oaiserver/tests/test_utils.py b/modules/invenio-oaiserver/tests/test_utils.py index 2933ba7ab7..c79a32c44c 100644 --- a/modules/invenio-oaiserver/tests/test_utils.py +++ b/modules/invenio-oaiserver/tests/test_utils.py @@ -15,7 +15,8 @@ eprints_description, handle_license_free, get_index_state, - is_output_harvest + is_output_harvest, + get_community_index_from_set ) from tests.helpers import create_record2 @@ -191,4 +192,42 @@ def test_is_output_harvest(app): } path_list = ["1","2","1000"] result = is_output_harvest(path_list,index_state) - assert result == 3 \ No newline at end of file + assert result == 3 + +#def get_community_index_from_set(set): +# .tox/c1/bin/pytest --cov=invenio_oaiserver tests/test_utils.py::test_get_community_index_from_set -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-oaiserver/.tox/c1/tmp +def test_get_community_index_from_set(app, db, users): + from invenio_communities.models import Community + from weko_index_tree.models import Index + + index1 = Index( + parent=0, + position=1, + index_name_english="test_index1", + index_link_name_english="test_index_link1", + harvest_public_state=True, + public_state=True, + browsing_role="3,-99" + ) + db.session.add(index1) + db.session.commit() + + comm1 = Community.create(community_id="test_comm", role_id=users[0]["id"], + id_user=users[0]["id"], title="test community", + description="this is test community", + root_node_id=index1.id) + db.session.add(comm1) + db.session.commit() + + exist_com = comm1.id + result = get_community_index_from_set(exist_com) + assert result == str(index1.id) + + # exist community with prefix + result = get_community_index_from_set("user-" + exist_com) + assert result == str(index1.id) + + # no exist community + result = get_community_index_from_set("no_exist_comm") + assert result is None + \ No newline at end of file diff --git a/modules/invenio-oaiserver/tox.ini b/modules/invenio-oaiserver/tox.ini index 429d6966ad..bbfb9b90d3 100644 --- a/modules/invenio-oaiserver/tox.ini +++ b/modules/invenio-oaiserver/tox.ini @@ -61,6 +61,7 @@ commands = pytest --cov=invenio_oaiserver tests -v --cov-report=term --basetemp="{envtmpdir}" {posargs} [testenv:c1] +setuptools_version = 57.5.0 passenv = LANG deps = pytest>=3 @@ -68,7 +69,7 @@ deps = -rrequirements2.txt commands = #pytest --cov=invenio_oaiserver tests -v --cov-branch --cov-report=term --cov-report=xml --cov-report=html --basetemp="{envtmpdir}" {posargs} - pytest --cov=invenio_oaiserver tests -v -vv -s --cov-branch --cov-report=term --basetemp="{envtmpdir}" {posargs} + pytest --cov=invenio_oaiserver tests -v -vv -s --cov-branch --cov-report=term --cov-report=html --basetemp="{envtmpdir}" {posargs} [testenv:lint] passenv = LANG diff --git a/modules/invenio-oauth2server/invenio_oauth2server/models.py b/modules/invenio-oauth2server/invenio_oauth2server/models.py index 58f134584a..cc05209b65 100644 --- a/modules/invenio-oauth2server/invenio_oauth2server/models.py +++ b/modules/invenio-oauth2server/invenio_oauth2server/models.py @@ -284,6 +284,29 @@ def get_users(self): ).count() return no_users + @classmethod + def get_client_id_by_user_id(cls, user_id): + """Get client_id, name by user_id. """ + query = db.session.query(cls).with_entities(cls.client_id, cls.name).filter(cls.user_id == user_id) + return query.all() + + @classmethod + def get_client_id_all(cls): + """Get client_id all. """ + query = db.session.query(cls).with_entities(cls.client_id, cls.name) + return query.all() + + @classmethod + def get_name_by_client_id(cls, client_id): + """Get name by client_id. """ + query = db.session.query(cls).with_entities(cls.name).filter(cls.client_id == client_id) + return query.first() + + @classmethod + def get_user_id_by_client_id(cls, client_id): + """Get user_id by client_id. """ + query = db.session.query(cls).with_entities(cls.user_id).filter(cls.client_id == client_id) + return query.first() class Token(db.Model): """A bearer token is the final token that can be used by the client.""" diff --git a/modules/invenio-oauth2server/requirements2.txt b/modules/invenio-oauth2server/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-oauth2server/requirements2.txt +++ b/modules/invenio-oauth2server/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-oauth2server/tests/test_models.py b/modules/invenio-oauth2server/tests/test_models.py index 66508a16c5..7fbd8c66f4 100644 --- a/modules/invenio-oauth2server/tests/test_models.py +++ b/modules/invenio-oauth2server/tests/test_models.py @@ -254,3 +254,27 @@ def test_deletion_of_token2(models_fixture): # delete consumer db.session.delete(User.query.get(app.consumer_id)) + +def test_get_client_id_by_user_id(models_fixture): + app = models_fixture + with app.app_context(): + lst = Client.get_client_id_by_user_id(app.resource_owner_id) + assert len(lst) > 0 + +def test_get_client_id_all(models_fixture): + app = models_fixture + with app.app_context(): + lst = Client.get_client_id_all() + assert len(lst) > 0 + +def test_get_name_by_client_id(models_fixture): + app = models_fixture + with app.app_context(): + lst = Client.get_name_by_client_id(app.u1c1_id) + assert len(lst) > 0 + +def test_get_user_id_by_client_id(models_fixture): + app = models_fixture + with app.app_context(): + lst = Client.get_user_id_by_client_id(app.u1c1_id) + assert len(lst) > 0 diff --git a/modules/invenio-previewer/requirements2.txt b/modules/invenio-previewer/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-previewer/requirements2.txt +++ b/modules/invenio-previewer/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-queues/requirements2.txt b/modules/invenio-queues/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-queues/requirements2.txt +++ b/modules/invenio-queues/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-records-rest/invenio_records_rest/views.py b/modules/invenio-records-rest/invenio_records_rest/views.py index 8e7745f5d4..0c6d23db5f 100644 --- a/modules/invenio-records-rest/invenio_records_rest/views.py +++ b/modules/invenio-records-rest/invenio_records_rest/views.py @@ -157,7 +157,7 @@ def create_blueprint(endpoints): __name__, url_prefix='', ) - + @blueprint.teardown_request def dbsession_clean(exception): current_app.logger.debug("invenio_records_rest dbsession_clean: {}".format(exception)) @@ -584,9 +584,9 @@ def get(self, **kwargs): def get_item_sort_value(search_result): return list(search_result["hits"]["hits"][-1]["sort"]) - + def set_sort_value(sort_condition, sort_value): - + sort_fields = [list(condition.keys())[0] for condition in sort_condition] new_data = {} for i, field in enumerate(sort_fields): @@ -603,9 +603,9 @@ def set_sort_value(sort_condition, sort_value): new_data[sort_field] = sort_value[i] return new_data - + def get_new_search_after(_page, search_after_size, cache_data, search_query, last_sort_value, first_page_over_max_results, sort_condition): - + conn_num = "" # The first page whose page*size is greater than max_result_window page_list = [] @@ -695,7 +695,7 @@ def get_max_result_sort_value(max_size_key, search, sort_condition, max_cache_da # This exception is for when sort type "relevance" is used sort_element = "control_number" relevance_sort_is_used = True - + if relevance_sort_is_used: sort_key = sort_element else: @@ -710,7 +710,7 @@ def get_max_result_sort_value(max_size_key, search, sort_condition, max_cache_da cache_name = User.query.get(current_user.id).email else: cache_name = "anonymous_user" - + # For saving a specific page for search_after use as cache key cache_key = str(page) @@ -749,7 +749,7 @@ def url_args_check(): RECORDS_REST_DEFAULT_TTL_VALUE ) ) - + url_args_check() next_items_sort_value = [] if page * size > self.max_result_window: @@ -769,11 +769,11 @@ def url_args_check(): search_after_start_value = search[start_value:end_value] search_after_start_value = search_after_start_value.execute() last_sort_value = get_item_sort_value(search_after_start_value) - + sort_condition=search.to_dict().get("sort") #sort_fields = [list(condition.keys())[0] for condition in sort_condition] - + if not sessionstorage.redis.exists(f"{cache_name}_max_result"): sessionstorage.put( f"{cache_name}_max_result", @@ -927,7 +927,7 @@ def post(self, **kwargs): db.session.rollback() current_app.logger.error(e) response = self.make_response(None, None, 500) - + return response @@ -1215,4 +1215,4 @@ def get(self, **kwargs): for field, val, opts in completions: result[field] = response[field] - return make_response(jsonify(result)) \ No newline at end of file + return make_response(jsonify(result)) diff --git a/modules/invenio-records-rest/requirements.txt b/modules/invenio-records-rest/requirements.txt index a3e12953cc..010ed4a598 100644 --- a/modules/invenio-records-rest/requirements.txt +++ b/modules/invenio-records-rest/requirements.txt @@ -161,7 +161,7 @@ Flask-Plugins==1.6.1 bibtexparser==1.0.1 github3.py==1.1.0 feedgen==0.7.0 -boto3==1.7.84 +boto3==1.9.83 s3fs==0.1.6 #fpdf==1.7.2 -e git+https://github.com/RCOSDP/pyfpdf.git@fix/nii#egg=fpdf diff --git a/modules/invenio-records-rest/requirements2.txt b/modules/invenio-records-rest/requirements2.txt index 00ce98bd94..cd3f5baf07 100644 --- a/modules/invenio-records-rest/requirements2.txt +++ b/modules/invenio-records-rest/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -34,7 +34,6 @@ counter-robots==2018.6 cryptography==2.1.4 datacite==1.0.1 DateTime==4.9 -dcxml==0.1.0 decorator==4.1.2 defusedxml==0.5.0 dictdiffer==0.7.0 @@ -187,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -194,14 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock -pytest-invenio +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-records/requirements2.txt b/modules/invenio-records/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-records/requirements2.txt +++ b/modules/invenio-records/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-resourcesyncclient/invenio_resourcesyncclient/static/js/invenio_resourcesyncclient/resync_client.js b/modules/invenio-resourcesyncclient/invenio_resourcesyncclient/static/js/invenio_resourcesyncclient/resync_client.js index 1849ede516..434252e271 100644 --- a/modules/invenio-resourcesyncclient/invenio_resourcesyncclient/static/js/invenio_resourcesyncclient/resync_client.js +++ b/modules/invenio-resourcesyncclient/invenio_resourcesyncclient/static/js/invenio_resourcesyncclient/resync_client.js @@ -38,17 +38,17 @@ const default_state = { }; const default_label = { - repository_name: "Repository Name", - status: 'Status', - index_id: "Index Id", - index_name: "Index Name", - base_url: "Base Url", - resync_mode: "Mode", - saving_format: "Saving Format", - from_date:"From Date", - to_date: "To Date", - interval_by_day: "Interval by Day" - } + repository_name: "Repository Name", + status: 'Status', + index_id: "Index Id", + index_name: "Index Name", + base_url: "Base Url", + resync_mode: "Mode", + saving_format: "Saving Format", + from_date: "From Date", + to_date: "To Date", + interval_by_day: "Interval by Day" +} class MainLayout extends React.Component { constructor(props) { @@ -83,7 +83,7 @@ class MainLayout extends React.Component { this.handleChangeTab = this.handleChangeTab.bind(this); } - componentDidMount() {} + componentDidMount() { } handleChangeTab(select_tab, select_item = null) { const { tabs } = this.state; @@ -362,21 +362,21 @@ class CreateResyncComponent extends React.Component { const { repository_id } = state; const url_path = window.location.origin + "/resync/" + repository_id; this.handleChangeState("url_path", url_path); -} + } handleSubmit(add_another) { - if((this.state.from_date || this.state.resync_mode === 'Incremental') && !moment(this.state.from_date,'YYYY/MM/DD', true).isValid()) { + if ((this.state.from_date || this.state.resync_mode === 'Incremental') && !moment(this.state.from_date, 'YYYY/MM/DD', true).isValid()) { alert(LABELS['lblResyncClientFromDate FormatErrorMsg']); return; } - if(this.state.to_date && !moment(this.state.to_date,'YYYY/MM/DD', true).isValid()) { + if (this.state.to_date && !moment(this.state.to_date, 'YYYY/MM/DD', true).isValid()) { alert(LABELS['lblResyncClientUntilDate FormatErrorMsg']); return; } const new_data = { ...this.state }; delete new_data.tree_list; - const {mode} = this.props - const url = mode ==="edit" ? urlUpdate+"/"+new_data.id : urlCreate + const { mode } = this.props + const url = mode === "edit" ? urlUpdate + "/" + new_data.id : urlCreate fetch(url, { method: "POST", body: JSON.stringify(new_data), @@ -387,7 +387,7 @@ class CreateResyncComponent extends React.Component { .then(res => res.json()) .then(res => { if (res.success) { - if(add_another){ + if (add_another) { this.setState({ ...default_state }) @@ -420,17 +420,17 @@ class CreateResyncComponent extends React.Component { componentDidMount() { this.getRepositoryList(); - const {mode} = this.props + const { mode } = this.props initDatepicker(); - this.state.from_date = this.state.from_date ? moment(this.state.from_date).format("YYYY/MM/DD"):""; - this.state.to_date = this.state.to_date ? moment(this.state.to_date).format("YYYY/MM/DD"):""; + this.state.from_date = this.state.from_date ? moment(this.state.from_date).format("YYYY/MM/DD") : ""; + this.state.to_date = this.state.to_date ? moment(this.state.to_date).format("YYYY/MM/DD") : ""; $("#from_date").val(this.state.from_date); $("#to_date").val(this.state.to_date); } render() { const { state } = this; - const {mode} = this.props + const { mode } = this.props return (
//repository_name @@ -445,9 +445,9 @@ class CreateResyncComponent extends React.Component { value={state.repository_name} name="repository_name" onChange={e => { - const value = e.target.value; - this.handleChangeState("repository_name", value); - }} + const value = e.target.value; + this.handleChangeState("repository_name", value); + }} >
@@ -463,9 +463,9 @@ class CreateResyncComponent extends React.Component { value={state.base_url} name="base_url" onChange={e => { - const value = e.target.value; - this.handleChangeState("base_url", value); - }} + const value = e.target.value; + this.handleChangeState("base_url", value); + }} > @@ -477,29 +477,29 @@ class CreateResyncComponent extends React.Component {
-
- { - Object.keys(status).map((item, key) => { - return( -
- { - const value = e.target.value; - this.handleChangeState("status", value); - }} - > -
{status[item]}
-
- ) - }) - } +
+ { + Object.keys(status).map((item, key) => { + return ( +
+ { + const value = e.target.value; + this.handleChangeState("status", value); + }} + > +
{status[item]}
+
+ ) + }) + } +
-
//interval_by_day { @@ -517,7 +517,7 @@ class CreateResyncComponent extends React.Component { name="interval_by_day" onChange={e => { let value = e.target.value; - value = value >=1 ? value : 1 + value = value >= 1 ? value : 1 this.handleChangeState("interval_by_day", parseInt(value)); }} > @@ -628,33 +628,33 @@ class CreateResyncComponent extends React.Component {
- { - mode === 'create' ? - - - : - - - } + { + mode === 'create' ? + + + : + + + } + className={`btn ${this.state.is_running ? "btn-success" : "btn-danger"}`} + onClick={() => { this.toggleRunning() }} + >{this.state.is_running === true ? "ON" : "OFF"} ) : ( @@ -831,13 +831,13 @@ class DetailResourceComponent extends React.Component { { this.state.resync_mode !== resync_mode.audit && + className="btn btn-primary" + onClick={() => this.handleImport()} + >Import } @@ -848,53 +848,53 @@ class DetailResourceComponent extends React.Component {

Running logs

-
- - - - - - - - - - - - - - - - - - { - this.state.logs.map((item,key) => { - return ( - - - - - - - - - - - - - - ) - }) - } - -
#{LABELS['lblResyncClientStart Time']}{LABELS['lblResyncClientEnd Time']}{LABELS['lblResyncClientStatus']}{LABELS['lblResyncClientLog Type']}{LABELS['lblResyncClientProcessed Items']}{LABELS['lblResyncClientCreated Items']}{LABELS['lblResyncClientUpdated Items']}{LABELS['lblResyncClientDeleted Items']}{LABELS['lblResyncClientError Items']}Error Message, Url
{key+1}{item.start_time}{item.end_time}{item.status}{item.log_type}{item.counter.processed_items}{item.counter.created_items}{item.counter.updated_items}{item.counter.deleted_items}{item.counter.error_items}{item.errmsg}
-
+
+ + + + + + + + + + + + + + + + + + { + this.state.logs.map((item, key) => { + return ( + + + + + + + + + + + + + + ) + }) + } + +
#{LABELS['lblResyncClientStart Time']}{LABELS['lblResyncClientEnd Time']}{LABELS['lblResyncClientStatus']}{LABELS['lblResyncClientLog Type']}{LABELS['lblResyncClientProcessed Items']}{LABELS['lblResyncClientCreated Items']}{LABELS['lblResyncClientUpdated Items']}{LABELS['lblResyncClientDeleted Items']}{LABELS['lblResyncClientError Items']}Error Message, Url
{key + 1}{item.start_time}{item.end_time}{item.status}{item.log_type}{item.counter.processed_items}{item.counter.created_items}{item.counter.updated_items}{item.counter.deleted_items}{item.counter.error_items}{item.errmsg}
+
); } } -$(function() { +$(function () { ReactDOM.render(, document.getElementById("root")); }); @@ -915,24 +915,24 @@ class ComponentDatePicker extends React.Component { } } - componentDidMount(){ + componentDidMount() { const that = this - const {props} = this - $("#"+this.props.id_component).change( - function(event) { - const value = event.target.value; - that.props.onChange(that.props.name,value) - } + const { props } = this + $("#" + this.props.id_component).change( + function (event) { + const value = event.target.value; + that.props.onChange(that.props.name, value) + } ) } - componentWillUnmount(){ - const {props} = this - $("#"+props.id_component).off('change'); + componentWillUnmount() { + const { props } = this + $("#" + props.id_component).off('change'); } render() { - const {props} = this + const { props } = this return (
@@ -941,12 +941,12 @@ class ComponentDatePicker extends React.Component { name={props.component_name} id={props.id_component} type="text" - /> + />
Format is incorrect!
+ >Format is incorrect!
) @@ -959,13 +959,13 @@ function initDatepicker() { autoclose: true, forceParse: false }) - .on("changeDate", function(e) { - }); + .on("changeDate", function (e) { + }); $("#to_date").datepicker({ format: "yyyy/mm/dd", autoclose: true, forceParse: false }) - .on("changeDate", function(e) { - }); + .on("changeDate", function (e) { + }); } diff --git a/modules/invenio-resourcesyncclient/requirements2.txt b/modules/invenio-resourcesyncclient/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-resourcesyncclient/requirements2.txt +++ b/modules/invenio-resourcesyncclient/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-resourcesyncclient/tests/test_admin.py b/modules/invenio-resourcesyncclient/tests/test_admin.py index 083585b4e8..2dee416aed 100644 --- a/modules/invenio-resourcesyncclient/tests/test_admin.py +++ b/modules/invenio-resourcesyncclient/tests/test_admin.py @@ -219,7 +219,7 @@ def test_AdminResyncClient_toggle_auto(app, client, test_resync, users, test_ind # .tox/c1/bin/pytest --cov=invenio_resourcesyncclient tests/test_admin.py::test_get_repository -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-resourcesyncclient/.tox/c1/tmp def test_get_repository(app, client, users): url = url_for('resync.get_repository') - + # super role user login_user_via_session(client, email=users[2]["email"]) data = {'id': 1, 'name': 'root', 'children': []} @@ -228,7 +228,7 @@ def test_get_repository(app, client, users): assert res.status_code == 200 assert json.loads(res.data) == [{'id': 0, 'value': 'Root Index'}, {'id': 1, 'value': 'root '}] - + # super role user with children data = {'id': 1, 'name': 'root', 'children': [{'id': 2, 'name': 'child', 'children': []}]} with patch("weko_index_tree.api.Indexes.get_index_tree", return_value=[data]): @@ -237,7 +237,7 @@ def test_get_repository(app, client, users): assert json.loads(res.data) == [{'id': 0, 'value': 'Root Index'}, {'id': 1, 'value': 'root '}, {'id': 2, 'value': 'root / child '}] - + # comadmin role user with repository login_user_via_session(client, email=users[3]["email"]) mock_repo = Community(root_node_id=1) @@ -247,16 +247,16 @@ def test_get_repository(app, client, users): res = client.get(url) assert res.status_code == 200 assert json.loads(res.data) == [{'id': 1, 'value': 'root '}] - + # comadmin role user with no repository with patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[]): res = client.get(url) assert res.status_code == 200 assert json.loads(res.data) == [] - + # comadmin role user with repository but no index with patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[mock_repo]): with patch("weko_index_tree.api.Indexes.get_index_tree", return_value=[]): res = client.get(url) assert res.status_code == 200 - assert json.loads(res.data) == [] \ No newline at end of file + assert json.loads(res.data) == [] diff --git a/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/static/js/invenio-resourcesyncserver/change_list.js b/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/static/js/invenio-resourcesyncserver/change_list.js index 5d2244dc59..baf6a6de89 100644 --- a/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/static/js/invenio-resourcesyncserver/change_list.js +++ b/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/static/js/invenio-resourcesyncserver/change_list.js @@ -11,7 +11,7 @@ const default_state = { status: null, repository_id: "", change_dump_manifest: false, - change_tracking_state: ['created', 'updated','deleted'], + change_tracking_state: ['created', 'updated', 'deleted'], url_path: "", interval_by_date: 1, max_changes_size: 10000, @@ -60,7 +60,7 @@ class MainLayout extends React.Component { this.handleChangeTab = this.handleChangeTab.bind(this); } - componentDidMount() {} + componentDidMount() { } handleChangeTab(select_tab, select_item = {}) { const { tabs } = this.state; @@ -325,7 +325,7 @@ class CreateResourceComponent extends React.Component { .then(res => res.json()) .then(res => { if (res.success) { - if(add_another){ + if (add_another) { this.setState({ ...default_state }) @@ -369,36 +369,36 @@ class CreateResourceComponent extends React.Component {
-
-
- { - const value = e.target.value; - this.handleChangeState("status", value==="Publish"); - }} - > -
Publish
-
-
- { - const value = e.target.value; - this.handleChangeState("status", value==="Publish"); - }} +
+
+ { + const value = e.target.value; + this.handleChangeState("status", value === "Publish"); + }} + > +
Publish
+
+
+ { + const value = e.target.value; + this.handleChangeState("status", value === "Publish"); + }} >
Private
+
-
@@ -469,7 +469,7 @@ class CreateResourceComponent extends React.Component { value={state.interval_by_date} onChange={e => { let value = e.target.value; - value = value >=1 ? value : 1 + value = value >= 1 ? value : 1 this.handleChangeState("interval_by_date", parseInt(value)); }} > @@ -484,29 +484,29 @@ class CreateResourceComponent extends React.Component {
{ - tracker_state_list.map((item, key) => { - return( -
- { - let {change_tracking_state} = state - const value = e.target.checked; - if(value){ - change_tracking_state.push(item.value) - } - else { - change_tracking_state = change_tracking_state.filter(i => i !== item.value) - } - this.handleChangeState("change_tracking_state", change_tracking_state); - }} - > -
{item.name}
-
- ) - }) + tracker_state_list.map((item, key) => { + return ( +
+ { + let { change_tracking_state } = state + const value = e.target.checked; + if (value) { + change_tracking_state.push(item.value) + } + else { + change_tracking_state = change_tracking_state.filter(i => i !== item.value) + } + this.handleChangeState("change_tracking_state", change_tracking_state); + }} + > +
{item.name}
+
+ ) + }) }
@@ -607,7 +607,7 @@ class EditResourceComponent extends React.Component { { ...state, [name]: value - },() => { + }, () => { if (name === "repository_id") { this.handleChangeURL(); } @@ -689,7 +689,7 @@ class EditResourceComponent extends React.Component { value="Publish" onChange={e => { const value = e.target.value; - this.handleChangeState("status", value==="Publish"); + this.handleChangeState("status", value === "Publish"); }} >
Publish
@@ -702,10 +702,10 @@ class EditResourceComponent extends React.Component { value="Private" onChange={e => { const value = e.target.value; - this.handleChangeState("status", value==="Publish"); + this.handleChangeState("status", value === "Publish"); }} - > -
Private
+ > +
Private
@@ -780,7 +780,7 @@ class EditResourceComponent extends React.Component { value={state.interval_by_date} onChange={e => { let value = e.target.value; - value = value >=1 ? value : 1 + value = value >= 1 ? value : 1 this.handleChangeState("interval_by_date", parseInt(value)); }} > @@ -796,29 +796,29 @@ class EditResourceComponent extends React.Component {
{ - tracker_state_list.map((item, key) => { - return( -
- { - let {change_tracking_state} = state - const value = e.target.checked; - if(value){ - change_tracking_state.push(item.value) - } - else { - change_tracking_state = change_tracking_state.filter(i => i !== item.value) - } - this.handleChangeState("change_tracking_state", change_tracking_state); - }} - > -
{item.name}
-
- ) - }) + tracker_state_list.map((item, key) => { + return ( +
+ { + let { change_tracking_state } = state + const value = e.target.checked; + if (value) { + change_tracking_state.push(item.value) + } + else { + change_tracking_state = change_tracking_state.filter(i => i !== item.value) + } + this.handleChangeState("change_tracking_state", change_tracking_state); + }} + > +
{item.name}
+
+ ) + }) }
@@ -904,7 +904,7 @@ class DetailResourceComponent extends React.Component { return
Deatil ne
; } } -$(function() { +$(function () { ReactDOM.render(, document.getElementById("root")); initDatepicker() }); @@ -926,31 +926,31 @@ class ComponentDatePicker extends React.Component { } } - componentDidMount(){ + componentDidMount() { const that = this $("#publish_date").change( - function(event) { - const value = event.target.value; - if (moment(value,'MM/DD/YYYY').isValid()) { - if (that.props.onChange){ - that.props.onChange(that.props.name,value) - } + function (event) { + const value = event.target.value; + if (moment(value, 'MM/DD/YYYY').isValid()) { + if (that.props.onChange) { + that.props.onChange(that.props.name, value) } } + } ) } - componentWillUnmount(){ + componentWillUnmount() { $("#publish_date").off('change'); } render() { - const {props} = this + const { props } = this return (
- -
Format is incorrect!
+ +
Format is incorrect!
) @@ -964,9 +964,9 @@ function initDatepicker() { autoclose: true, forceParse: false }) - .on("changeDate", function(e) { - if (document.getElementById("publish_date_picker").classList.contains('has-error')) { - document.getElementById("publish_date_picker").classList.remove('has-error'); - } - }); + .on("changeDate", function (e) { + if (document.getElementById("publish_date_picker").classList.contains('has-error')) { + document.getElementById("publish_date_picker").classList.remove('has-error'); + } + }); } diff --git a/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/static/js/invenio-resourcesyncserver/resource.js b/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/static/js/invenio-resourcesyncserver/resource.js index 53e211f611..2e2a787c87 100644 --- a/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/static/js/invenio-resourcesyncserver/resource.js +++ b/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/static/js/invenio-resourcesyncserver/resource.js @@ -42,7 +42,7 @@ class MainLayout extends React.Component { this.handleChangeTab = this.handleChangeTab.bind(this); } - componentDidMount() {} + componentDidMount() { } handleChangeTab(select_tab, select_item = {}) { const { tabs } = this.state; @@ -305,7 +305,7 @@ class CreateResourceComponent extends React.Component { .then(res => res.json()) .then(res => { if (res.success) { - if(add_another){ + if (add_another) { this.setState({ ...default_state }) @@ -349,38 +349,38 @@ class CreateResourceComponent extends React.Component {
-
-
- { - const value = e.target.value; - this.handleChangeState("status", value==="Publish"); - }} - > -
Publish
-
-
- { - const value = e.target.value; - this.handleChangeState("status", value==="Publish"); - }} +
+
+ { + const value = e.target.value; + this.handleChangeState("status", value === "Publish"); + }} + > +
Publish
+
+
+ { + const value = e.target.value; + this.handleChangeState("status", value === "Publish"); + }} >
Private
-
+
+
-
@@ -500,7 +500,7 @@ class EditResourceComponent extends React.Component { { ...state, [name]: value - },() => { + }, () => { if (name === "repository_id") { this.handleChangeURL(); } @@ -573,16 +573,16 @@ class EditResourceComponent extends React.Component {
{ - const value = e.target.value; - this.handleChangeState("status", value==="Publish"); - }} - > -
Publish
+ checked={state.status} + type="radio" + name="status" + value="Publish" + onChange={e => { + const value = e.target.value; + this.handleChangeState("status", value === "Publish"); + }} + > +
Publish
{ const value = e.target.value; - this.handleChangeState("status", value==="Publish"); + this.handleChangeState("status", value === "Publish"); }} - > -
Private
+ > +
Private
@@ -703,6 +703,6 @@ class DetailResourceComponent extends React.Component { return
Deatil ne
; } } -$(function() { +$(function () { ReactDOM.render(, document.getElementById("root")); }); diff --git a/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/views.py b/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/views.py index 10caeaa76f..c3c8246699 100644 --- a/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/views.py +++ b/modules/invenio-resourcesyncserver/invenio_resourcesyncserver/views.py @@ -203,4 +203,4 @@ def dbsession_clean(exception): db.session.commit() except: db.session.rollback() - db.session.remove() \ No newline at end of file + db.session.remove() diff --git a/modules/invenio-resourcesyncserver/requirements2.txt b/modules/invenio-resourcesyncserver/requirements2.txt index 9913675b07..cd3f5baf07 100644 --- a/modules/invenio-resourcesyncserver/requirements2.txt +++ b/modules/invenio-resourcesyncserver/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,13 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-resourcesyncserver/tests/test_admin.py b/modules/invenio-resourcesyncserver/tests/test_admin.py index b23642d31a..776226efcc 100644 --- a/modules/invenio-resourcesyncserver/tests/test_admin.py +++ b/modules/invenio-resourcesyncserver/tests/test_admin.py @@ -55,7 +55,7 @@ def test_create_AdminResourceListView(i18n_app): with patch("invenio_resourcesyncserver.api.ResourceListHandler.create", return_value=data): assert test_1.create() - + assert test_1.create() # def update(self, resource_id): @@ -65,7 +65,7 @@ def test_update_AdminResourceListView(i18n_app, db): id=1, repository_id=2, ) - + db.session.add(test) db.session.commit() @@ -86,7 +86,7 @@ def test_delete_AdminResourceListView(i18n_app, db): id=1, repository_id=2 ) - + db.session.add(test) db.session.commit() @@ -119,7 +119,7 @@ def test_get_change_list_AdminChangeListView(i18n_app, db): assert test_2.get_change_list(1) # def create(self): -def test_create_AdminChangeListView(i18n_app, db): +def test_create_AdminChangeListView(i18n_app, db): sample = MagicMock() def to_dict(): return {"A": 1} @@ -141,7 +141,7 @@ def to_dict(): "success": 1, "message": "message" } - + with patch("flask.request.get_json", return_value=data): data["data"] = sample with patch("invenio_resourcesyncserver.api.ChangeListHandler.save", return_value=data): @@ -179,4 +179,4 @@ def test_delete_AdminChangeListView(i18n_app, db): db.session.commit() with patch("flask.request.get_json", return_value=data): - assert test_2.delete(1) \ No newline at end of file + assert test_2.delete(1) diff --git a/modules/invenio-resourcesyncserver/tests/test_api.py b/modules/invenio-resourcesyncserver/tests/test_api.py index f4ddb38c47..477f37a18c 100644 --- a/modules/invenio-resourcesyncserver/tests/test_api.py +++ b/modules/invenio-resourcesyncserver/tests/test_api.py @@ -24,7 +24,7 @@ def sample_ResourceListHandler(): test.created = "test" test.updated = "test" test.index = "test" - + return test @@ -51,7 +51,7 @@ def _func(keyword): test = ChangeListHandler( change_tracking_state=["test"] ) - + test.id = "test" test.status = "test" test.repository_id = "Root Index" @@ -65,7 +65,7 @@ def _func(keyword): test.interval_by_date = 2 return test - + return _func(key) @@ -84,7 +84,7 @@ def test_to_dict_ResourceListHandler(i18n_app): data = MagicMock() data.index_name_english = "test" test.index = data - + assert test.to_dict() @@ -127,8 +127,8 @@ def test_get_resource_ResourceListHandler(i18n_app, db): assert test.get_resource(1) assert not test.get_resource("a") - - + + # .tox/c1/bin/pytest --cov=invenio_resourcesyncserver tests/test_api.py::test_get_list_resource_ResourceListHandler -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-resourcesyncserver/.tox/c1/tmp # def get_list_resource(cls, type_result='obj'): def test_get_list_resource_ResourceListHandler(i18n_app, db, users): @@ -142,20 +142,20 @@ def test_get_list_resource_ResourceListHandler(i18n_app, db, users): assert len(result) assert isinstance(result[0], ResourceListIndexes) assert result[0].id == 1 - + # no user result = test.get_list_resource() assert len(result) == 1 assert isinstance(result[0], ResourceListHandler) assert result[0].id == 1 - + # super role user user = users[3]["obj"] result = test.get_list_resource(user=user) assert len(result) == 1 assert isinstance(result[0], ResourceListHandler) assert result[0].id == 1 - + # comadmin role user with repository user = users[4]["obj"] mock_repo = Community(root_node_id=1) @@ -165,12 +165,12 @@ def test_get_list_resource_ResourceListHandler(i18n_app, db, users): assert len(result) == 1 assert isinstance(result[0], ResourceListHandler) assert result[0].id == 1 - + # comadmin role user with no repository with patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[]): result = test.get_list_resource(user=user) assert len(result) == 0 - + # comadmin role user with repository but no index user = users[4]["obj"] with patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[mock_repo]): @@ -206,18 +206,18 @@ def test__validation_ResourceListHandler(i18n_app): with patch("weko_deposit.api.WekoRecord.get_record_by_pid", return_value=return_data): with patch("invenio_resourcesyncserver.utils.get_real_path", return_value=["33"]): assert test._validation(record_id=1) == True - + with patch("invenio_resourcesyncserver.utils.get_real_path", return_value=[]): assert test._validation(record_id=1) == False - + assert test._validation() == True - + test.repository_id = "Root Index" - + assert test._validation() == True - -# def get_resource_list_xml(self, from_date=None, to_date=None): ERR ~ + +# def get_resource_list_xml(self, from_date=None, to_date=None): ERR ~ def test_get_resource_list_xml_ResourceListHandler(i18n_app, indices): test = sample_ResourceListHandler() test.repository_id = "Root Index" @@ -245,7 +245,7 @@ def as_xml(): assert test.get_resource_list_xml(from_date=from_date, to_date=to_date) -# def get_resource_dump_xml(self, from_date=None, to_date=None): ERR ~ +# def get_resource_dump_xml(self, from_date=None, to_date=None): ERR ~ def test_get_resource_dump_xml_ResourceListHandler(i18n_app): test = sample_ResourceListHandler() test.repository_id = "Root Index" @@ -310,7 +310,7 @@ def as_xml_sample(): assert test.get_resource_dump_manifest(record_id) except: pass - + # def get_record_content_file(self, record_id): def test_get_record_content_file_ResourceListHandler(i18n_app): @@ -372,7 +372,7 @@ def test_save_ChangeListHandler(i18n_app): with patch("invenio_resourcesyncserver.api.ChangeListHandler.get_change_list_by_repo_id", return_value="test"): test_str.id = None - assert test_str.save() + assert test_str.save() # def get_change_list_content_xml(self, from_date, @@ -399,7 +399,7 @@ def test_get_change_list_content_xml_ChangeListHandler(i18n_app, db, users): from_date_args = "2022/11/3 0:00:00" to_date_args = "2022/11/4 0:00:00" return_data = MagicMock() - + with patch("invenio_resourcesyncserver.api.ChangeListHandler._validation", return_value=""): assert not test_str.get_change_list_content_xml( from_date=from_date, @@ -423,7 +423,7 @@ def test_get_change_list_content_xml_ChangeListHandler(i18n_app, db, users): from_date=from_date, from_date_args=from_date_args, to_date_args=to_date_args - ) + ) with patch("invenio_resourcesyncserver.api.PIDVersioning", return_value=return_data): return_data.last_child = MagicMock() @@ -438,7 +438,7 @@ def test_get_change_list_content_xml_ChangeListHandler(i18n_app, db, users): def test_get_change_list_index_ChangeListHandler(i18n_app): test_str = sample_ChangeListHandler("str") test_str.publish_date = datetime.datetime.now() - datetime.timedelta(hours=1) - + with patch("invenio_resourcesyncserver.api.ChangeListHandler._validation", return_value=""): assert not test_str.get_change_list_index() @@ -452,13 +452,13 @@ def test_get_change_dump_index_ChangeListHandler(i18n_app): def _validation(): return True - + def _not_validation(): return False test_str._validation = _validation test_str.publish_date = datetime.datetime.now() - datetime.timedelta(hours=1) - + assert test_str.get_change_dump_index() test_str._validation = _not_validation @@ -470,11 +470,11 @@ def _not_validation(): def test_get_change_dump_xml_ChangeListHandler(i18n_app): test_str = sample_ChangeListHandler("str") from_date = datetime.datetime.now() - datetime.timedelta(hours=1) - + def _validation(): return True - + def _not_validation(): return False @@ -512,7 +512,7 @@ def test__validation_ChangeListHandler(i18n_app): assert test_str._validation() test_str.repository_id = None - + assert test_str._validation() test_str.status = False @@ -604,7 +604,7 @@ def test_get_change_list_ChangeListHandler(i18n_app, db): assert test_str.get_change_list(changelist_id, "modal") assert test_str.get_change_list(changelist_id) - + # def get_all(cls): # def convert_modal_to_obj(cls, model=ChangeListIndexes()): @@ -633,14 +633,14 @@ def test_get_all_ChangeListHandler(i18n_app, db, users): result = test_str.get_all() assert len(result) == 1 assert result[0].id == test.id - + # super role user user = users[3]["obj"] result = test_str.get_all(user=user) assert len(result) == 1 assert isinstance(result[0], ChangeListHandler) assert result[0].id == test.id - + # comadmin role user with repository user = users[4]["obj"] mock_repo = Community(root_node_id=1) @@ -650,12 +650,12 @@ def test_get_all_ChangeListHandler(i18n_app, db, users): assert len(result) == 1 assert isinstance(result[0], ChangeListHandler) assert result[0].id == test.id - + # comadmin role user with no repository with patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[]): result = test_str.get_all(user=user) assert len(result) == 0 - + # comadmin role user with repository but no index with patch("invenio_communities.models.Community.get_repositories_by_user", return_value=[mock_repo]): with patch("weko_index_tree.api.Indexes.get_child_list_recursive", return_value=[]): @@ -698,7 +698,7 @@ def test__is_record_in_index_ChangeListHandler(i18n_app): with patch("weko_deposit.api.WekoRecord.get_record", return_value=MagicMock()): with patch("invenio_resourcesyncserver.utils.get_real_path", return_value=test_str.repository_id): assert test_str._is_record_in_index(record_id) - + test_str.repository_id = "Index" assert test_str._is_record_in_index(record_id) @@ -715,7 +715,7 @@ def test_get_record_content_file_ChangeListHandler(i18n_app, es_records): def _is_record_in_index(key): return True - + def _is_not_record_in_index(key): return False @@ -780,7 +780,7 @@ def test__next_change_ChangeListHandler(i18n_app): assert test_str._next_change(data, [changes]) changes["record_version"] = 1 - + assert not test_str._next_change(data, [changes]) @@ -821,4 +821,4 @@ def test__get_record_changes_ChangeListHandler(i18n_app): assert test_str._get_record_changes(repo_id, from_date, until_date) # Exception coverage - assert not test_str._get_record_changes(repo_id, from_date, until_date) \ No newline at end of file + assert not test_str._get_record_changes(repo_id, from_date, until_date) diff --git a/modules/invenio-s3/.travis.yml b/modules/invenio-s3/.travis.yml index 457e7b897f..1782748acd 100644 --- a/modules/invenio-s3/.travis.yml +++ b/modules/invenio-s3/.travis.yml @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019, 2020 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -22,13 +22,17 @@ cache: - pip env: - - REQUIREMENTS=lowest - - REQUIREMENTS=release DEPLOY=true - - REQUIREMENTS=devel + global: + - BOTO_CONFIG=/dev/null + matrix: + - REQUIREMENTS=lowest + - REQUIREMENTS=release DEPLOY=true + - REQUIREMENTS=devel python: - - "2.7" - - "3.5" + - "3.6" + - "3.7" + - "3.8" before_install: - "nvm install 6; nvm use 6" @@ -41,6 +45,7 @@ before_install: install: - "travis_retry pip install -r .travis-${REQUIREMENTS}-requirements.txt" - "travis_retry pip install -e .[all]" + - "pip freeze" script: - "./run-tests.sh" @@ -56,6 +61,6 @@ deploy: distributions: "sdist bdist_wheel" on: tags: true - python: "2.7" + python: "3.6" repo: inveniosoftware/invenio-s3 condition: $DEPLOY = true diff --git a/modules/invenio-s3/CHANGES.rst b/modules/invenio-s3/CHANGES.rst index fb3f6d974d..4f6a6efce2 100644 --- a/modules/invenio-s3/CHANGES.rst +++ b/modules/invenio-s3/CHANGES.rst @@ -1,11 +1,29 @@ .. - Copyright (C) 2018 Esteban J. G. Gabancho. + Copyright (C) 2018, 2019, 2020 Esteban J. G. Gabancho. Invenio-S3 is free software; you can redistribute it and/or modify it under the terms of the MIT License; see LICENSE file for more details. Changes ======= +Version 1.0.3 (released 2020-04-25) + +- Allow for dynamic part size for multipart uploads. +- Adds new configuration variables to define default part size and maximum + number of parts. + +Version 1.0.2 (released 2020-02-17) + +- Fixes typos on configuration variables and cached properties. +- Adds AWS region name and signature version to configuration. + +Version 1.0.1 (released 2019-01-23) + +- New configuration variable for URL expiration. +- Enhances file serving. +- Unpins Boto3 library. +- Fixes test suit configuration. + Version 1.0.0 (released 2018-09-19) - Initial public release. diff --git a/modules/invenio-s3/MANIFEST.in b/modules/invenio-s3/MANIFEST.in index 98f1d30811..69131548dd 100644 --- a/modules/invenio-s3/MANIFEST.in +++ b/modules/invenio-s3/MANIFEST.in @@ -21,7 +21,3 @@ recursive-include docs Makefile recursive-include examples *.py recursive-include examples *.sh recursive-include tests *.py - -# added by check-manifest -include *.properties -include tox.ini diff --git a/modules/invenio-s3/coverage.xml b/modules/invenio-s3/coverage.xml index f789598844..c4b3fb69ee 100644 --- a/modules/invenio-s3/coverage.xml +++ b/modules/invenio-s3/coverage.xml @@ -1,22 +1,22 @@ - + /code/modules/invenio-s3/invenio_s3 - + - - - - - + + + + + @@ -27,103 +27,102 @@ - + + + + + + - + - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -136,90 +135,98 @@ - - - - - - - - + + + + + + + - - + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/invenio-s3/invenio_s3/__init__.py b/modules/invenio-s3/invenio_s3/__init__.py index 98fef6e0a8..cc440646c0 100644 --- a/modules/invenio-s3/invenio_s3/__init__.py +++ b/modules/invenio-s3/invenio_s3/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019, 2020 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -16,8 +16,28 @@ `_ for each S3 bucket. This is just for simplicity, it can used however needed. -This module doesn't create S3 buckets automatically, so before starting they -need to be created. +When creating a new location which will use the S3 API, the URI needs to start +with ``s3://``, for example +``invenio files location s3_default s3://my-bucket --default`` will +create a new location, set it as default location for your instance and use the +bucket ``my-bucket``. For more information about this command check +`Invenio-Files-Rest `_ +documentation. + +Then, there are a few configuration variables that need to be set on your +instance, like the endpoint, the access key and the secret access key, see a +more detailed description in :any:`configuration`. + +.. note:: + + This module doesn't create S3 buckets automatically, so before starting they + need to be created. + + You might also want to set the correct `CORS configuration + `_ so files can + be used by your interface for things like previewing a PDF with some + Javascript library. + """ from __future__ import absolute_import, print_function diff --git a/modules/invenio-s3/invenio_s3/config.py b/modules/invenio-s3/invenio_s3/config.py index 86002d5f4c..b158823c60 100644 --- a/modules/invenio-s3/invenio_s3/config.py +++ b/modules/invenio-s3/invenio_s3/config.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019, 2020 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -15,11 +15,21 @@ a service. If set to a value (including the "http/https" scheme) it will be passed as -``endpoint_url`` to boto3`client +``endpoint_url`` to boto3 `client `_. """ -S3_ACCCESS_KEY_ID = None +S3_REGION_NAME = None +"""S3 region name + +This is entirely optional, and if not provided, the region name will be +automatically set to 'us-east-1'. + +If set to a value it will be passed as ``region_name`` to boto3 `client +`_. +""" + +S3_ACCESS_KEY_ID = None """The access key to use when creating the client. This is entirely optional, and if not provided, the credentials configured for @@ -29,7 +39,7 @@ for more information. """ -S3_SECRECT_ACCESS_KEY = None +S3_SECRET_ACCESS_KEY = None """The secret key to use when creating the client. This is entirely optional, and if not provided, the credentials configured for @@ -39,9 +49,39 @@ for more information. """ +S3_URL_EXPIRATION = 60 +"""Number of seconds the file serving URL will be valid. + +See `Amazon Boto3 documentation on presigned URLs +`_ +for more information. +""" + +S3_SIGNATURE_VERSION = 's3v4' +"""Version of the S3 signature algorithm. Can be 's3' (v2) or 's3v4' (v4). +See `Amazon Boto3 documentation on configuration variables +`_ +for more information. +""" + S3_SEND_FILE_DIRECTLY = True """The flag to use when send file to the client. If this flag is false, system will redirects the file to the client. When redirecting, S3_ENDPOINT_URL need to be set except for US region. """ + +S3_MAXIMUM_NUMBER_OF_PARTS = 10000 +"""Maximum number of parts to be used. +See `AWS Multipart Upload Overview +`_ for more +information. +""" + +S3_DEFAULT_BLOCK_SIZE = 5 * 2**20 +"""Default block size value used to send multi-part uploads to S3. +Typically 5Mb is minimum allowed by the API.""" + +S3_LOCATION_TYPE_S3_PATH_VALUE = 's3' + +S3_LOCATION_TYPE_S3_VIRTUAL_HOST_VALUE = 's3_vh' diff --git a/modules/invenio-s3/invenio_s3/ext.py b/modules/invenio-s3/invenio_s3/ext.py index e3fe6cabb7..d0deced433 100644 --- a/modules/invenio-s3/invenio_s3/ext.py +++ b/modules/invenio-s3/invenio_s3/ext.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -8,6 +8,8 @@ from __future__ import absolute_import, print_function +import warnings + import boto3 from flask import current_app from invenio_files_rest.models import Location @@ -25,25 +27,60 @@ def __init__(self, app=None): self.init_app(app) @cached_property - def init_s3f3_info(self): + def init_s3fs_info(self, location): """Gather all the information needed to start the S3FSFileSystem.""" + if 'S3_ACCCESS_KEY_ID' in current_app.config: + current_app.config['S3_ACCESS_KEY_ID'] = current_app.config[ + 'S3_ACCCESS_KEY_ID'] + warnings.warn( + 'Key S3_ACCCESS_KEY_ID contained a typo and has been ' + 'corrected to S3_ACCESS_KEY_ID, support for the ' + 'flawed version will be removed.', + DeprecationWarning + ) + + if 'S3_SECRECT_ACCESS_KEY' in current_app.config: + current_app.config['S3_SECRET_ACCESS_KEY'] = current_app.config[ + 'S3_SECRECT_ACCESS_KEY'] + warnings.warn( + 'Key S3_SECRECT_ACCESS_KEY contained a typo and has been ' + 'corrected to S3_SECRET_ACCESS_KEY, support for the ' + 'flawed version will be removed.', + DeprecationWarning + ) + info = dict( - key=current_app.config.get('S3_ACCCESS_KEY_ID', ''), - secret=current_app.config.get('S3_SECRECT_ACCESS_KEY', ''), + key=current_app.config.get('S3_ACCESS_KEY_ID', ''), + secret=current_app.config.get('S3_SECRET_ACCESS_KEY', ''), client_kwargs={}, + config_kwargs={ + 's3': { + 'addressing_style': 'path', + }, + 'signature_version': current_app.config.get( + 'S3_SIGNATURE_VERSION', 's3v4' + ), + }, ) - s3_endpoint_url = current_app.config.get('S3_ENDPOINT_URL', None) - if s3_endpoint_url: - info['client_kwargs']['endpoint_url'] = s3_endpoint_url - default_location = Location.query.filter_by(default=True).first() - if default_location.type == 's3': - if default_location.access_key != '': - info['key'] = default_location.access_key - if default_location.secret_key != '': - info['secret'] = default_location.secret_key - if default_location.s3_endpoint_url != '': - info['client_kwargs']['endpoint_url'] = default_location.s3_endpoint_url - + + s3_endpoint = current_app.config.get('S3_ENDPOINT_URL', None) + if s3_endpoint: + info['client_kwargs']['endpoint_url'] = s3_endpoint + + region_name = current_app.config.get('S3_REGION_NAME', None) + if region_name: + info['client_kwargs']['region_name'] = region_name + + if location.type == current_app.config.get('S3_LOCATION_TYPE_S3_PATH_VALUE') or \ + location.type == current_app.config.get('S3_LOCATION_TYPE_S3_VIRTUAL_HOST_VALUE'): + info['key'] = location.access_key + info['secret'] = location.secret_key + info['client_kwargs']['endpoint_url'] = location.s3_endpoint_url + region_name = location.s3_region_name + if region_name: + info['client_kwargs']['region_name'] = region_name + info['config_kwargs']['signature_version'] = location.s3_signature_version + return info def init_app(self, app): diff --git a/modules/invenio-s3/invenio_s3/helpers.py b/modules/invenio-s3/invenio_s3/helpers.py index 69d41ba7d8..bcded1e26e 100644 --- a/modules/invenio-s3/invenio_s3/helpers.py +++ b/modules/invenio-s3/invenio_s3/helpers.py @@ -1,40 +1,32 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. + """File serving helpers for S3 files.""" import mimetypes import unicodedata -from time import time -from flask import current_app, request + +from flask import current_app from invenio_files_rest.helpers import chunk_size_or_default, sanitize_mimetype from werkzeug.datastructures import Headers -from werkzeug.urls import url_quote +from urllib.parse import quote as url_quote - -def redirect_stream(url, +def redirect_stream(s3_url_builder, filename, - size, - mtime, mimetype=None, restricted=True, as_attachment=False, - etag=None, - content_md5=None, - chunk_size=None, - conditional=True, trusted=False): """Redirect to URL to serve the file directly from there. :param url: redirection URL - :return: Flaks response. + :return: Flask response. """ - chunk_size = chunk_size_or_default(chunk_size) - # Guess mimetype from filename if not provided. if mimetype is None and filename: mimetype = mimetypes.guess_type(filename)[0] @@ -43,11 +35,6 @@ def redirect_stream(url, # Construct headers headers = Headers() - headers['Content-Length'] = size - if content_md5: - headers['Content-MD5'] = content_md5 - # Add redirect url as localtion - headers['Location'] = url if not trusted: # Sanitize MIME type @@ -82,32 +69,26 @@ def redirect_stream(url, else: headers.add('Content-Disposition', 'inline') + url = s3_url_builder( + ResponseContentType=mimetype, + ResponseContentDisposition=headers.get('Content-Disposition')) + headers['Location'] = url + + # TODO: Set cache-control + # if not restricted: + # rv.cache_control.public = True + # cache_timeout = current_app.get_send_file_max_age(filename) + # if cache_timeout is not None: + # rv.cache_control.max_age = cache_timeout + # rv.expires = int(time() + cache_timeout) # Construct response object. + rv = current_app.response_class( url, status=302, - mimetype=mimetype, headers=headers, + mimetype=mimetype, direct_passthrough=True, ) - # Set etag if defined - if etag: - rv.set_etag(etag) - - # Set last modified time - if mtime is not None: - rv.last_modified = int(mtime) - - # Set cache-control - if not restricted: - rv.cache_control.public = True - cache_timeout = current_app.get_send_file_max_age(filename) - if cache_timeout is not None: - rv.cache_control.max_age = cache_timeout - rv.expires = int(time() + cache_timeout) - - if conditional: - rv = rv.make_conditional(request) - return rv diff --git a/modules/invenio-s3/invenio_s3/storage.py b/modules/invenio-s3/invenio_s3/storage.py index 7e594f9e9b..f8a344dcac 100644 --- a/modules/invenio-s3/invenio_s3/storage.py +++ b/modules/invenio-s3/invenio_s3/storage.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019, 2020, 2021 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. + """S3 file storage interface.""" -from __future__ import absolute_import, print_function -from io import BytesIO +from functools import partial, wraps +from math import ceil import s3fs from flask import current_app @@ -17,25 +18,69 @@ from .config import S3_SEND_FILE_DIRECTLY from .helpers import redirect_stream +from .ext import InvenioS3 + +def set_blocksize(f): + """Decorator to set the correct block size according to file size.""" + @wraps(f) + def inner(self, *args, **kwargs): + size = kwargs.get('size', None) + + block_size = ( + size // current_app.config['S3_MAXIMUM_NUMBER_OF_PARTS'] # Integer + if size + else current_app.config['S3_DEFAULT_BLOCK_SIZE'] + ) + if self.location: + if self.location.type != None: + block_size = ( + ceil(size / self.location.s3_maximum_number_of_parts) + if size + else self.location.s3_default_block_size + ) + if block_size > self.block_size: + self.block_size = block_size + return f(self, *args, **kwargs) + + return inner class S3FSFileStorage(PyFSFileStorage): """File system storage using Amazon S3 API for accessing files.""" - def __init__(self, fileurl, **kwargs): + def __init__(self, fileurl, size, modified, clean_dir, location): """Storage initialization.""" - super(S3FSFileStorage, self).__init__(fileurl, **kwargs) + self.block_size = current_app.config['S3_DEFAULT_BLOCK_SIZE'] + if location: + if location.type != None: + self.block_size = location.s3_default_block_size + super(S3FSFileStorage, self).__init__(fileurl, size, modified, clean_dir, location) def _get_fs(self, *args, **kwargs): - """Ge PyFilesystem instance and S3 real path.""" - if not self.fileurl.startswith('s3://'): + """Get PyFilesystem instance and S3 real path.""" + if self.location.type == None: return super(S3FSFileStorage, self)._get_fs(*args, **kwargs) - info = current_app.extensions['invenio-s3'].init_s3f3_info - fs = s3fs.S3FileSystem(**info) + url = self.fileurl + if self.location.type == current_app.config.get('S3_LOCATION_TYPE_S3_VIRTUAL_HOST_VALUE'): + if url.startswith('https://s3'): + # ex: https://s3.amazonaws.com/bucket_name/file_name + parts = url.split('/') + url = 's3://' + '/'.join(parts[3:]) + self.location.s3_endpoint_url = parts[0] + '//' + parts[2] + else: + # ex: https://bucket_name.s3.us-east-1.amazonaws.com/file_name + parts = url.split('/') + sub_parts = parts[2].split('.') + url = 's3://' + sub_parts[0] + '/' + '/'.join(parts[3:]) + self.location.s3_endpoint_url = parts[0] + '//' + parts[2] + + info = current_app.extensions['invenio-s3'].init_s3fs_info(location=self.location) + fs = s3fs.S3FileSystem(default_block_size=self.block_size, **info) - return (fs, self.fileurl) + return (fs, url) + @set_blocksize def initialize(self, size=0): """Initialize file on storage and truncate to given size.""" fs, path = self._get_fs() @@ -47,8 +92,9 @@ def initialize(self, size=0): to_write = size fs_chunk_size = fp.blocksize # Force write every time while to_write > 0: - current_chunk_size = (to_write if to_write <= fs_chunk_size - else fs_chunk_size) + current_chunk_size = ( + to_write if to_write <= fs_chunk_size else fs_chunk_size + ) fp.write(b'\0' * current_chunk_size) to_write -= current_chunk_size except Exception: @@ -76,23 +122,30 @@ def delete(self): self.remove(fs, path) return True - def update(self, - incoming_stream, - seek=0, - size=None, - chunk_size=None, - progress_callback=None): + @set_blocksize + def update( + self, + incoming_stream, + seek=0, + size=None, + chunk_size=None, + progress_callback=None, + ): """Update a file in the file system.""" old_fp = self.open(mode='rb') - updated_fp = S3FSFileStorage( - self.fileurl, size=self._size).open(mode='wb') + updated_fp = S3FSFileStorage(self.fileurl, size=self._size).open( + mode='wb' + ) try: if seek >= 0: to_write = seek fs_chunk_size = updated_fp.blocksize while to_write > 0: - current_chunk_size = (to_write if to_write <= fs_chunk_size - else fs_chunk_size) + current_chunk_size = ( + to_write + if to_write <= fs_chunk_size + else fs_chunk_size + ) updated_fp.write(old_fp.read(current_chunk_size)) to_write -= current_chunk_size @@ -101,15 +154,19 @@ def update(self, updated_fp, chunk_size=chunk_size, size=size, - progress_callback=progress_callback) + progress_callback=progress_callback, + ) if (bytes_written + seek) < self._size: old_fp.seek((bytes_written + seek)) to_write = self._size - (bytes_written + seek) fs_chunk_size = updated_fp.blocksize while to_write > 0: - current_chunk_size = (to_write if to_write <= fs_chunk_size - else fs_chunk_size) + current_chunk_size = ( + to_write + if to_write <= fs_chunk_size + else fs_chunk_size + ) updated_fp.write(old_fp.read(current_chunk_size)) to_write -= current_chunk_size finally: @@ -118,8 +175,16 @@ def update(self, return bytes_written, checksum - def send_file(self, filename, mimetype=None, restricted=True, checksum=None, - trusted=False, chunk_size=None, as_attachment=False): + def send_file( + self, + filename, + mimetype=None, + restricted=True, + checksum=None, + trusted=False, + chunk_size=None, + as_attachment=False, + ): """Send the file to the client.""" s3_send_file_directly = current_app.config.get('S3_SEND_FILE_DIRECTLY', None) default_location = Location.query.filter_by(default=True).first() @@ -128,51 +193,64 @@ def send_file(self, filename, mimetype=None, restricted=True, checksum=None, s3_send_file_directly = default_location.s3_send_file_directly if s3_send_file_directly: - return super(S3FSFileStorage, self).send_file(filename, - mimetype=mimetype, - restricted=restricted, - checksum=checksum, - trusted=trusted, - chunk_size=chunk_size, - as_attachment=as_attachment) + return super(S3FSFileStorage, self).send_file( + filename, + mimetype=mimetype, + restricted=restricted, + checksum=checksum, + trusted=trusted, + chunk_size=chunk_size, + as_attachment=as_attachment + ) + try: fs, path = self._get_fs() - url = fs.url(path, expires=60) - - md5_checksum = None - if checksum: - algo, value = checksum.split(':') - if algo == 'md5': - md5_checksum = value + s3_url_builder = partial( + fs.url, path, expires=current_app.config['S3_URL_EXPIRATION'] + ) + if self.location: + if self.location.type != None: + s3_url_builder = partial( + fs.url, path, expires=self.location.s3_url_expiration + ) return redirect_stream( - url, + s3_url_builder, filename, - self._size, - self._modified, mimetype=mimetype, restricted=restricted, - etag=checksum, - content_md5=md5_checksum, - chunk_size=chunk_size, trusted=trusted, as_attachment=as_attachment, ) except Exception as e: raise StorageError('Could not send file: {}'.format(e)) + @set_blocksize def copy(self, src, *args, **kwargs): """Copy data from another file instance. If the source is an S3 stored object the copy process happens on the S3 server side, otherwise we use the normal ``FileStorage`` copy method. """ - if src.fileurl.startswith('s3://'): - fs, path = self._get_fs() - fs.copy(src.fileurl, path) + if self.location: + if self.location.type != None: + fs, path = self._get_fs() + fs.copy(src.fileurl, path) + else: + # institutional repository + super(S3FSFileStorage, self).copy(src, *args, **kwargs) else: + # institutional repository super(S3FSFileStorage, self).copy(src, *args, **kwargs) + @set_blocksize + def save(self, *args, **kwargs): + """Save incoming stream to storage. + + Just overwrite parent method to allow set the correct block size. + """ + return super(S3FSFileStorage, self).save(*args, **kwargs) + def s3fs_storage_factory(**kwargs): """File storage factory for S3.""" diff --git a/modules/invenio-s3/invenio_s3/version.py b/modules/invenio-s3/invenio_s3/version.py index ef4582cd7a..57e7b52bae 100644 --- a/modules/invenio-s3/invenio_s3/version.py +++ b/modules/invenio-s3/invenio_s3/version.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019, 2020 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -13,4 +13,4 @@ from __future__ import absolute_import, print_function -__version__ = '1.0.0' +__version__ = '1.0.3' diff --git a/modules/invenio-s3/requirements-devel.txt b/modules/invenio-s3/requirements-devel.txt index 39651a0d93..457c51eeaa 100644 --- a/modules/invenio-s3/requirements-devel.txt +++ b/modules/invenio-s3/requirements-devel.txt @@ -6,4 +6,4 @@ # under the terms of the MIT License; see LICEN. -e git+https://github.com/inveniosoftware/invenio-files-rest.git#egg=invenio-files-rest --e git+https://github.com/dask/s3fs.git#egg=s3fs +-e git+git://github.com/dask/s3fs.git#egg=s3fs diff --git a/modules/invenio-s3/requirements.txt b/modules/invenio-s3/requirements.txt index 8a5d255154..ba39f1d361 100644 --- a/modules/invenio-s3/requirements.txt +++ b/modules/invenio-s3/requirements.txt @@ -70,7 +70,7 @@ ipython==6.2.1 ipython-genutils==0.2.0 itsdangerous==0.24 # moto==3.1.2 -moto==1.3.5 +moto==3.1.2 jedi==0.11.0 Jinja2==2.10 jinja2-cli==0.6.0 @@ -161,7 +161,7 @@ Flask-Plugins==1.6.1 bibtexparser==1.0.1 github3.py==1.1.0 feedgen==0.7.0 -boto3==1.7.84 +boto3==1.9.83 s3fs==0.1.6 #fpdf==1.7.2 -e git+https://github.com/RCOSDP/pyfpdf.git@fix/nii#egg=fpdf diff --git a/modules/invenio-s3/requirements2.txt b/modules/invenio-s3/requirements2.txt index cac347c073..cd3f5baf07 100644 --- a/modules/invenio-s3/requirements2.txt +++ b/modules/invenio-s3/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -163,7 +163,6 @@ maxminddb-geolite2==2017.803 mistune==0.8.3 more-itertools==8.10.0 mock==5.0.2 -moto==1.3.6 msgpack==0.6.2 nbconvert==5.3.1 nbformat==4.4.0 @@ -187,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -194,14 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock -pytest-invenio==1.2.1 +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/invenio-s3/run-tests.sh b/modules/invenio-s3/run-tests.sh old mode 100755 new mode 100644 index 17a9756561..718cac2423 --- a/modules/invenio-s3/run-tests.sh +++ b/modules/invenio-s3/run-tests.sh @@ -1,13 +1,19 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# This file is part of Invenio. +# Copyright (C) 2018-2020 CERN. # -# Invenio-S3 is free software; you can redistribute it and/or modify it +# 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. -pydocstyle invenio_s3 tests docs && \ -isort -rc -c -df && \ -check-manifest --ignore ".travis-*" && \ -sphinx-build -qnNW docs docs/_build/html && \ -python setup.py test +# Quit on errors +set -o errexit + +# Quit on unbound symbols +set -o nounset + +python -m check_manifest --ignore ".*-requirements.txt" +python -m sphinx.cmd.build -qnNW docs docs/_build/html +python -m pytest +python -m sphinx.cmd.build -qnNW -b doctest docs docs/_build/doctest diff --git a/modules/invenio-s3/setup.py b/modules/invenio-s3/setup.py index a0fc0187d8..e11c8cd833 100644 --- a/modules/invenio-s3/setup.py +++ b/modules/invenio-s3/setup.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019, 2020 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. - """S3 file storage support for Invenio. """ import os @@ -38,18 +37,17 @@ extras_require['all'].extend(reqs) setup_requires = [ - 'pytest-runner>=3.0.0,<5', + 'pytest-runner>=3.0.0,<5', ] install_requires = [ - 'boto3==1.7.84', # See https://github.com/spulec/moto/issues/1793 - 's3fs>=0.1.5', - 'invenio-files-rest>=1.0.0a23' + 'boto3>=1.9.83', + 'invenio-files-rest>=1.0.0', + 's3fs>=0.1.5,<0.3.0', # Newer versions only allow python >= 3.5 ] packages = find_packages() - # Get the version string. Cannot be done with import! g = {} with open(os.path.join('invenio_s3', 'version.py'), 'rt') as fp: diff --git a/modules/invenio-s3/tests/conftest.py b/modules/invenio-s3/tests/conftest.py index e912f13304..aca0f26ec1 100644 --- a/modules/invenio-s3/tests/conftest.py +++ b/modules/invenio-s3/tests/conftest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -15,6 +15,7 @@ import boto3 import pytest from flask import Flask, current_app +from invenio_app.factory import create_api from invenio_db import InvenioDB from invenio_db import db as db_ from invenio_db.utils import drop_alembic_version_table @@ -26,20 +27,19 @@ from invenio_s3 import InvenioS3, S3FSFileStorage - @pytest.fixture(scope='module') def app_config(app_config): """Customize application configuration.""" - app_config['FILES_REST_STORAGE_FACTORY'] = 'invenio_s3:s3_storage_factory' + app_config[ + 'FILES_REST_STORAGE_FACTORY'] = 'invenio_s3.s3fs_storage_factory' app_config['S3_ENDPOINT_URL'] = None - app_config['S3_ACCCESS_KEY_ID'] = '' - app_config['S3_SECRECT_ACCESS_KEY'] = '' - # app_config['SQLALCHEMY_DATABASE_URI'] = os.environ.get( - # 'SQLALCHEMY_DATABASE_URI', 'sqlite:///test.db') + app_config['SQLALCHEMY_DATABASE_URI'] = os.getenv('SQLALCHEMY_DATABASE_URI', 'postgresql+psycopg2://invenio:dbpass123@postgresql:5432/wekotest') app_config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True app_config['TESTING'] = True + app_config['S3_ACCESS_KEY_ID'] = 'test' + app_config['S3_SECRET_ACCESS_KEY'] = 'test' return app_config @@ -66,8 +66,8 @@ def base_app(): app.config.update( FILES_REST_STORAGE_FACTORY='invenio_s3:s3_storage_factory', S3_ENDPOINT_URL=None, - S3_ACCCESS_KEY_ID= '', - S3_SECRECT_ACCESS_KEY = '', + S3_ACCESS_KEY_ID= '', + S3_SECRET_ACCESS_KEY = '', SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_URI', 'postgresql+psycopg2://invenio:dbpass123@postgresql:5432/wekotest'), SQLALCHEMY_TRACK_MODIFICATIONS = True, @@ -123,15 +123,14 @@ def location(location_path, database): database.session.commit() return loc - @pytest.fixture(scope='function') def s3_bucket(appctx): """S3 bucket fixture.""" with mock_s3(): session = boto3.Session( - aws_access_key_id=current_app.config.get('S3_ACCCESS_KEY_ID'), + aws_access_key_id=current_app.config.get('S3_ACCESS_KEY_ID'), aws_secret_access_key=current_app.config.get( - 'S3_SECRECT_ACCESS_KEY'), + 'S3_SECRET_ACCESS_KEY'), ) s3 = session.resource('s3') bucket = s3.create_bucket(Bucket='test_invenio_s3') diff --git a/modules/invenio-s3/tests/test_invenio_s3.py b/modules/invenio-s3/tests/test_invenio_s3.py index f1e6f7a889..761a3f5d12 100644 --- a/modules/invenio-s3/tests/test_invenio_s3.py +++ b/modules/invenio-s3/tests/test_invenio_s3.py @@ -1,13 +1,12 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. """Module tests.""" from __future__ import absolute_import, print_function - from invenio_files_rest.models import Location from invenio_s3 import InvenioS3 @@ -29,11 +28,11 @@ def test_init(base_app, location, database): with base_app.app_context(): base_app.config['S3_ENDPOINT_URL'] = 'https://example.com:1234' - s3_connection_info = base_app.extensions['invenio-s3'].init_s3f3_info + s3_connection_info = base_app.extensions['invenio-s3'].init_s3fs_info assert s3_connection_info['client_kwargs'][ 'endpoint_url'] == 'https://example.com:1234' - +# .tox/c1/bin/pytest --cov=invenio_s3 tests/test_invenio_s3.py::test_init2 -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-s3/.tox/c1/tmp def test_init2(location, database): """Test extension initialization.""" default_location = Location.query.filter_by(default=True).first() @@ -44,8 +43,31 @@ def test_init2(location, database): database.session.commit() invenio_s3 = InvenioS3() - s3_connection_info = invenio_s3.init_s3f3_info + s3_connection_info = invenio_s3.init_s3fs_info assert s3_connection_info['key'] == 'accesskey' assert s3_connection_info['secret'] == 'secretkey' - assert s3_connection_info['client_kwargs'][ - 'endpoint_url'] == 'https://example.com:5678' \ No newline at end of file + +# .tox/c1/bin/pytest --cov=invenio_s3 tests/test_invenio_s3.py::test_access_key -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-s3/.tox/c1/tmp +def test_access_key(location, base_app): + """Test correct access key works together with flawed one.""" + base_app.config['S3_ACCESS_KEY_ID'] = 'secret' + try: + # Delete the cached value in case it's there already + del base_app.extensions['invenio-s3'].__dict__['init_s3fs_info'] + except KeyError: + pass + s3_connection_info = base_app.extensions['invenio-s3'].init_s3fs_info + assert s3_connection_info['key'] == 'secret' + + +# .tox/c1/bin/pytest --cov=invenio_s3 tests/test_invenio_s3.py::test_secret_key -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/invenio-s3/.tox/c1/tmp +def test_secret_key(location, base_app): + """Test correct secret key works together with flawed one.""" + base_app.config['S3_SECRET_ACCESS_KEY'] = 'secret' + try: + # Delete the cached value in case it's there already + del base_app.extensions['invenio-s3'].__dict__['init_s3fs_info'] + except KeyError: + pass + s3_connection_info = base_app.extensions['invenio-s3'].init_s3fs_info + assert s3_connection_info['secret'] == 'secret' diff --git a/modules/invenio-s3/tests/test_storage.py b/modules/invenio-s3/tests/test_storage.py index 3bcffc545e..0dcf0be18d 100644 --- a/modules/invenio-s3/tests/test_storage.py +++ b/modules/invenio-s3/tests/test_storage.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2018 Esteban J. G. Gabancho. +# Copyright (C) 2018, 2019, 2020 Esteban J. G. Gabancho. # # Invenio-S3 is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. @@ -134,7 +134,7 @@ def test_save_failcleanup(location, s3fs, s3fs_testpath, get_md5): data = b'somedata' def fail_callback(total, size): - # assert exists(s3fs_testpath) + # assert fs.exists(s3fs_testpath) raise Exception('Something bad happened') pytest.raises( @@ -233,9 +233,8 @@ def test_update(location, s3fs, get_md5, file_size): def test_update_fail(location, s3fs, s3fs_testpath, get_md5): """Test update of file.""" - def fail_callback(total, size): - assert exists(s3fs_testpath) + assert fs.exists(s3fs_testpath) raise Exception('Something bad happened') s3fs.initialize(size=100) @@ -274,9 +273,7 @@ def callback(total, size): uri, size, save_checksum = s3fs.save( fp, size=os.path.getsize('LICENSE')) # assert checksum == save_checksum - # assert checksum == s3fs.checksum( - # chunk_size=2, - # progress_callback=callback) + # assert checksum == s3fs.checksum(chunk_size=2, progress_callback=callback) # assert counter['size'] == size # assert counter['size'] == os.path.getsize('LICENSE') @@ -332,7 +329,7 @@ def test_send_file(base_app, location, s3fs, database): def test_send_directly(): res = s3fs.send_file( - 'test.txt', mimetype='text/plain', checksum=checksum) + 'test.txt', mimetype='text/plain', checksum=checksum) assert res.status_code == 200 h = res.headers assert h['Content-Type'] == 'text/plain; charset=utf-8' @@ -346,7 +343,7 @@ def test_send_directly(): # Cache-Control: max-age=43200, public # Expires: Sat, 23 Jan 2016 19:21:04 GMT # Date: Sat, 23 Jan 2016 07:21:04 GMT - + res = s3fs.send_file( 'myfilename.txt', mimetype='text/plain', checksum='crc32:test') assert res.status_code == 200 @@ -355,7 +352,7 @@ def test_send_directly(): # Test for absence of Content-Disposition header to make sure that # it's not present when as_attachment=False res = s3fs.send_file('myfilename.txt', mimetype='text/plain', - checksum=checksum, as_attachment=False) + checksum=checksum, as_attachment=False) assert res.status_code == 200 assert 'attachment' not in res.headers['Content-Disposition'] @@ -376,6 +373,44 @@ def test_send_indirectly(): assert res.status_code == 302 assert 'Content-MD5' not in dict(res.headers) + with base_app.test_request_context(): + base_app.config['S3_SEND_FILE_DIRECTLY'] = True + test_send_directly() + + base_app.config['S3_SEND_FILE_DIRECTLY'] = False + test_send_indirectly() + + default_location.type = 's3' + database.session.commit() + + base_app.config['S3_SEND_FILE_DIRECTLY'] = True + test_send_directly() + + base_app.config['S3_SEND_FILE_DIRECTLY'] = False + test_send_directly() + + default_location.s3_send_file_directly = False + database.session.commit() + + base_app.config['S3_SEND_FILE_DIRECTLY'] = True + test_send_indirectly() + + base_app.config['S3_SEND_FILE_DIRECTLY'] = False + test_send_indirectly() + + checksum = 'md5:value' + test_send_indirectly() + + with patch('invenio_s3.storage.redirect_stream') as rs: + rs.side_effect = Exception + pytest.raises(StorageError, s3fs.send_file, 'test.txt') + + base_app.config['S3_SEND_FILE_DIRECTLY'] = True + default_location.s3_send_file_directly = True + database.session.commit() + + assert 'Content-MD5' not in dict(res.headers) + with base_app.test_request_context(): base_app.config['S3_SEND_FILE_DIRECTLY'] = True @@ -420,6 +455,8 @@ def test_send_file_fail(base_app, location, s3fs): with patch('invenio_s3.storage.redirect_stream') as redirect_stream: redirect_stream.side_effect = OSError(errno.EPERM, "Permission problem") + with base_app.test_request_context(): + pytest.raises(StorageError, s3fs.send_file, 'test.txt') def test_send_file_xss_prevention(base_app, location, s3fs): @@ -477,13 +514,28 @@ def test_non_unicode_filename(base_app, location, s3fs): u'żółć.dat', mimetype='application/octet-stream', checksum=checksum) - assert res.status_code == 200 + assert res.status_code == 302 # assert set(res.headers['Content-Disposition'].split('; ')) == \ - # set(["attachment", "filename=zoc.dat", - # "filename*=UTF-8''%C5%BC%C3%B3%C5%82%C4%87.dat"]) + # set(["attachment", "filename=zoc.dat", + # "filename*=UTF-8''%C5%BC%C3%B3%C5%82%C4%87.dat"]) with base_app.test_request_context(): res = s3fs.send_file( 'żółć.txt', mimetype='text/plain', checksum=checksum) - assert res.status_code == 200 + assert res.status_code == 302 assert res.headers['Content-Disposition'] == 'inline' + + +def test_block_size(base_app, s3_bucket, s3fs_testpath, s3fs, get_md5): + """Test block size update on the S3FS client.""" + # Set file size to 4 times the default block size + data = b'a' * appctx.config['S3_DEFAULT_BLOCK_SIZE'] * 4 + # Set the number of maximum parts to two + appctx.config['S3_MAXIMUM_NUMBER_OF_PARTS'] = 2 + uri, size, checksum = s3fs.save(BytesIO(data), + size=len(data)) + # The block size should be 2 times the default block size + assert s3fs.block_size == appctx.config['S3_DEFAULT_BLOCK_SIZE'] * 2 + assert uri == s3fs_testpath + assert size == len(data) + assert checksum == get_md5(data) diff --git a/modules/invenio-stats/invenio_stats/queries.py b/modules/invenio-stats/invenio_stats/queries.py index 71ea3f0753..9ebd277da2 100644 --- a/modules/invenio-stats/invenio_stats/queries.py +++ b/modules/invenio-stats/invenio_stats/queries.py @@ -567,7 +567,7 @@ def _apply_metric_aggs(agg): agg_query = agg_query.filter('bool', should=should_clauses, minimum_should_match=1) else: agg_query = agg_query.filter('terms', **kwargs.get('agg_filter')) - + if kwargs.get('wildcard'): agg_query = agg_query.filter('wildcard', **kwargs.get('wildcard')) @@ -628,7 +628,7 @@ def run(self, start_date=None, end_date=None, **kwargs): agg_query = self.build_query(**kwargs) query_result = agg_query.execute().to_dict() - + return query_result @@ -710,4 +710,4 @@ def run(self, start_date=None, end_date=None, **kwargs): current_app.logger.debug(agg_query.to_dict()) query_result = agg_query.execute().to_dict() - return self.process_query_result(query_result, start_date, end_date) \ No newline at end of file + return self.process_query_result(query_result, start_date, end_date) diff --git a/modules/invenio-stats/invenio_stats/utils.py b/modules/invenio-stats/invenio_stats/utils.py index 0d00d0257c..01363ac081 100644 --- a/modules/invenio-stats/invenio_stats/utils.py +++ b/modules/invenio-stats/invenio_stats/utils.py @@ -218,14 +218,14 @@ def is_valid_access(): def chunk_list(iterable, size): """ Split a List into chunks of a specified size. - + Args: iterable (list): The List to be split. size (int): The size of each chunk. - + Yields: list: A chunk of the original List with the specified size. - + """ it = iter(iterable) while True: @@ -291,7 +291,7 @@ def calc_file_stats_reports(cls, res, data_list, all_groups): group_list = i['user_group_names'] data['group_counts'] = cls.calc_per_group_counts( group_list, data['group_counts'], count) - + # Keep track of groups seen all_groups.update(data['group_counts'].keys()) @@ -338,7 +338,7 @@ def get_file_stats_report(cls, is_billing_item=False, **kwargs): year = kwargs.get('year') month = kwargs.get('month') repository_id = kwargs.get('repository_id') - + if repository_id and repository_id != 'Root Index': repository = Community.query.get(repository_id) index_list = get_descendant_index_names(repository.root_node_id) if repository else [] @@ -500,7 +500,7 @@ def get(cls, **kwargs): current_report['search_key'] = report['search_key'] current_report['count'] = report['count'] all.append(current_report) - all = sorted(all, key=lambda x:x['count'], reverse=True) + all = sorted(all, key=lambda x:x['count'], reverse=True) result['all'] = all except es_exceptions.NotFoundError as e: current_app.logger.debug( @@ -818,8 +818,8 @@ class QueryRecordViewReportHelper(object): @classmethod def Calculation(cls, res, data_list): """Create response object.""" - for item in res['buckets']: - data = { + for item in res['buckets']: + data = { 'record_id': item['record_id'], 'record_name': item['record_name'], 'index_names': item['record_index_names'], @@ -985,7 +985,7 @@ def get(cls, **kwargs): int(getattr(config, 'REPORTS_PER_PAGE'))) # get page_index from request params page_index = kwargs.get('page_index', 0) - + repository_id = kwargs.get('repository_id') if repository_id and repository_id != 'Root Index': repository = Community.query.get(repository_id) @@ -1353,8 +1353,8 @@ class QueryRankingHelper(object): @classmethod def Calculation(cls, res, data_list): """Create response object.""" - for item in res['aggregations']['my_buckets']['buckets']: - data = { + for item in res['aggregations']['my_buckets']['buckets']: + data = { 'key': item['key'], 'count': int(item['my_sum']['value']) } diff --git a/modules/invenio-stats/invenio_stats/views.py b/modules/invenio-stats/invenio_stats/views.py index 50793c446a..42d2805ce4 100644 --- a/modules/invenio-stats/invenio_stats/views.py +++ b/modules/invenio-stats/invenio_stats/views.py @@ -114,7 +114,7 @@ class QueryRecordViewCount(WekoQuery): def _get_data(self, record_id, query_date=None, get_period=False): """Get data.""" - + result = {} period = [] country = {} @@ -143,7 +143,7 @@ def _get_data(self, record_id, query_date=None, get_period=False): query_total = query_total_cfg.query_class( **query_total_cfg.query_config) res_total = query_total.run(**params) - + result['total'] = res_total['count'] for d in res_total['buckets']: country[d['key']] = d['count'] @@ -655,4 +655,4 @@ def dbsession_clean(exception): db.session.commit() except: db.session.rollback() - db.session.remove() \ No newline at end of file + db.session.remove() diff --git a/modules/invenio-stats/requirements2.txt b/modules/invenio-stats/requirements2.txt index bf93f2420a..cd3f5baf07 100644 --- a/modules/invenio-stats/requirements2.txt +++ b/modules/invenio-stats/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,14 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-invenio==1.2.1 -pytest-mock +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 @@ -282,4 +284,3 @@ xmlschema==0.9.30 xmltodict==0.12.0 zipp==3.6.0 zope.interface==5.5.2 -tika==2.6.0 diff --git a/modules/invenio-stats/tests/conftest.py b/modules/invenio-stats/tests/conftest.py index 3128b0d513..bd04e7feba 100644 --- a/modules/invenio-stats/tests/conftest.py +++ b/modules/invenio-stats/tests/conftest.py @@ -428,7 +428,7 @@ def role_users(app, db): ds.add_role_to_user(originalroleuser, originalrole) ds.add_role_to_user(originalroleuser2, originalrole) ds.add_role_to_user(originalroleuser2, repoadmin_role) - + return [ {"email": contributor.email, "id": contributor.id, "obj": contributor}, @@ -492,7 +492,7 @@ def exists(self, index, **kwargs): return False def flush(self,index): pass - + def search(self,index,doc_type,body,**kwargs): pass @@ -948,7 +948,7 @@ def base_event(id, event_type): source=json.dumps({'test': 'test'}), date=datetime.datetime(2023, 1, 1, 1, 0, 0) ) - + try: with db.session.begin_nested(): db.session.add(base_event(1, 'top-view')) diff --git a/modules/invenio-stats/tests/test_queries.py b/modules/invenio-stats/tests/test_queries.py index ae0b3695f2..33de410b3e 100644 --- a/modules/invenio-stats/tests/test_queries.py +++ b/modules/invenio-stats/tests/test_queries.py @@ -80,7 +80,7 @@ def test_date_histogram_query(app, mocker): assert query.build_query('month', None, None, file_key='test_key').to_dict() == {'query': {'bool': {'filter': [{'term': {'file_key': 'test_key'}}]}}, 'aggs': {'histogram': {'date_histogram': {'field': 'timestamp', 'interval': 'month', 'time_zone': 'Asia/Tokyo'}, 'aggs': {'value': {'sum': {'field': 'count'}}, 'top_hit': {'top_hits': {'size': 1, 'sort': {'timestamp': 'desc'}}}}}}, 'from': 0, 'size': 0} assert query.build_query('month', None, None, file_key=['key1', 'key2']).to_dict() == {'query': {'bool': {'filter': [{'terms': {'file_key': ['key1', 'key2']}}]}}, 'aggs': {'histogram': {'date_histogram': {'field': 'timestamp', 'interval': 'month', 'time_zone': 'Asia/Tokyo'}, 'aggs': {'value': {'sum': {'field': 'count'}}, 'top_hit': {'top_hits': {'size': 1, 'sort': {'timestamp': 'desc'}}}}}}, 'from': 0, 'size': 0} assert query.build_query('month', None, None, file_key=['key1', 'key2', 'key3', 'key4']).to_dict() == {'query': {'bool': {'filter': [{'bool': {'should': [{'terms': {'file_key': ['key1', 'key2', 'key3']}}, {'terms': {'file_key': ['key4']}}]}}]}}, 'aggs': {'histogram': {'date_histogram': {'field': 'timestamp', 'interval': 'month', 'time_zone': 'Asia/Tokyo'}, 'aggs': {'value': {'sum': {'field': 'count'}}, 'top_hit': {'top_hits': {'size': 1, 'sort': {'timestamp': 'desc'}}}}}}, 'from': 0, 'size': 0} - + query = ESDateHistogramQuery( query_name='test_total_count', **histogram_config, @@ -164,7 +164,7 @@ def test_date_histogram_query(app, mocker): [(["tests/data/ESTermsQuery_execute01.json"], 1, "tests/data/ESTermsQuery_result01.json"), - (["tests/data/ESTermsQuery_execute02.json", + (["tests/data/ESTermsQuery_execute02.json", "tests/data/ESTermsQuery_execute01.json"], 1, "tests/data/ESTermsQuery_result02.json"), @@ -193,7 +193,7 @@ def test_terms_query2(app): query_configs = register_queries() terms_config = query_configs[config_num]['query_config'] app.config['STATS_WEKO_DEFAULT_TIMEZONE'] = MagicMock(return_value='Asia/Tokyo') - + # validate_arguments query = ESTermsQuery( query_name='test_total_count', @@ -221,7 +221,7 @@ def test_terms_query2(app): assert query.build_query(None, None, task_name='task1').to_dict() == {'query': {'bool': {'filter': [{'term': {'task_name': 'task1'}}]}}, 'aggs': {'value': {'sum': {'field': 'count'}}}, 'from': 0, 'size': 0} assert query.build_query(None, None, task_name=['task1', 'task2']).to_dict() == {'query': {'bool': {'filter': [{'terms': {'task_name': ['task1', 'task2']}}]}}, 'aggs': {'value': {'sum': {'field': 'count'}}}, 'from': 0, 'size': 0} assert query.build_query(None, None, task_name=['task1', 'task2', 'task3', 'task4']).to_dict() == {'query': {'bool': {'filter': [{'bool': {'should': [{'terms': {'task_name': ['task1', 'task2', 'task3']}}, {'terms': {'task_name': ['task4']}}]}}]}}, 'aggs': {'value': {'sum': {'field': 'count'}}}, 'from': 0, 'size': 0} - + # process_query_result _res = { "aggregations": { diff --git a/modules/invenio-stats/tests/test_utils.py b/modules/invenio-stats/tests/test_utils.py index abee88bc15..d8e7053eb8 100644 --- a/modules/invenio-stats/tests/test_utils.py +++ b/modules/invenio-stats/tests/test_utils.py @@ -131,7 +131,7 @@ def test_agg_bucket_sort(app): # .tox/c1/bin/pytest --cov=invenio_stats tests/test_utils.py::test_parse_bucket_response -v -s -vv --cov-branch --cov-report=term --cov-config=tox.ini --basetemp=/code/modules/invenio-stats/.tox/c1/tmp def test_parse_bucket_response(app): _raw_res = {'buckets': [{'key': 'test_value'}], 'field': 'test_name'} - + res = parse_bucket_response(_raw_res, {}) assert res=={'test_name': 'test_value'} @@ -145,7 +145,7 @@ def test_get_doctype(app): def test_is_valid_access(app): res = is_valid_access() assert res==True - + with patch("invenio_stats.utils.get_remote_addr", return_value='0.0.0.0'): app.config['STATS_EXCLUDED_ADDRS'] = ['0.0.0.0'] res = is_valid_access() @@ -275,23 +275,23 @@ def test_query_file_reports_helper(app, event_queues, aggregated_file_download_e 'get-file-download-per-user-report': None, 'get-file-preview-per-user-report': None}, _data_list) - assert _data_list=={} + assert _data_list=={} QueryFileReportsHelper.Calculation(_res, _data_list) assert _data_list=={ 1: {'cur_user_id': 1, 'total_download': 2, 'total_preview': 5}, 2: {'cur_user_id': 2, 'total_download': 3}, 3: {'cur_user_id': 3, 'total_download': 4}, 4: {'cur_user_id': 4, 'total_preview': 1}} - + # get_file_stats_report - res = QueryFileReportsHelper.get_file_stats_report(event='file_downlaod', year=2022, month=10) - assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} + res = QueryFileReportsHelper.get_file_stats_report(event='file_downlaod', year=2022, month=10) + assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} res = QueryFileReportsHelper.get_file_stats_report(event='file_preview', year=2022, month=10) - assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} - res = QueryFileReportsHelper.get_file_stats_report(event='billing_file_download', year=2022, month=10) - assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} - + assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} + res = QueryFileReportsHelper.get_file_stats_report(event='billing_file_download', year=2022, month=10) + assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} + res = QueryFileReportsHelper.get_file_stats_report(event='file_downlaod', year=2022, month=10, repository_id='com1') assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} res = QueryFileReportsHelper.get_file_stats_report(event='file_downlaod', year=2022, month=10, repository_id='Root Index') @@ -303,9 +303,9 @@ def test_query_file_reports_helper(app, event_queues, aggregated_file_download_e # get res = QueryFileReportsHelper.get(year=2022, month=10, event='file_download') - assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} + assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} res = QueryFileReportsHelper.get(year=2022, month=10, event='billing_file_download') - assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} + assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} res = QueryFileReportsHelper.get(year=2022, month=10, event='file_using_per_user') assert res=={'all': {}, 'date': '2022-10'} res = QueryFileReportsHelper.get(year=2022, month=10, event='test') @@ -316,14 +316,14 @@ def test_query_file_reports_helper(app, event_queues, aggregated_file_download_e def test_query_file_reports_helper_error(mock_Community, mock_get_descendant_index_names, app, mocker): # get res = QueryFileReportsHelper.get(year=2022, month=10, event='file_download') - assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} + assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} res = QueryFileReportsHelper.get(year=2022, month=10, event='billing_file_download') - assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} + assert res=={'all': [], 'all_groups': [], 'date': '2022-10', 'open_access': []} res = QueryFileReportsHelper.get(year=2022, month=10, event='file_using_per_user') assert res=={'all': {}, 'date': '2022-10'} res = QueryFileReportsHelper.get(year=2022, month=10, event='test') assert res==[] - + mock_Community.query.get.return_value = MagicMock(root_node_id=1, group_id=1) mock_get_descendant_index_names.return_value = [] res = QueryFileReportsHelper.get(event='file_download', year=2022, month=10, repository_id='com1') @@ -395,12 +395,12 @@ def test_query_search_report_helper(app, es): res = QuerySearchReportHelper.get( year=2022, month=10, start_date='2022-10-01', end_date='2022-10-31') assert res=={'all': [{'search_key': 'key2', 'count': 7}, {'search_key': 'key1', 'count': 4}]} - + with patch('invenio_stats.queries.ESWekoTermsQuery.run', return_value=_raw_res2): res = QuerySearchReportHelper.get( year=2022, month=10, start_date='2022-10-01', end_date='2022-10-31', repository_id='com1') assert res=={'all': [{'search_key': 'key2', 'count': 7}, {'search_key': 'key1', 'count': 4}]} - + with patch('invenio_stats.queries.ESWekoTermsQuery.run', return_value=_raw_res2): res = QuerySearchReportHelper.get( year=2022, month=10, start_date='2022-10-01', end_date='2022-10-31', repository_id='Root Index') @@ -448,7 +448,7 @@ def test_query_common_reports_helper(mock_Community, mock_get_descendant_index_n mock_get_descendant_index_names.return_value = ['index1'] res = QueryCommonReportsHelper.get(event='top_page_access', year=2022, month=10, start_date='2022-10-01', end_date='2022-10-10', repository_id='com1') assert res=={'date': '2022-10-01-2022-10-10', 'all': {'localhost': {'host': 'name2', 'ip': 'localhost', 'count': 2}}} - + _res = { 'buckets': [ { @@ -525,13 +525,13 @@ def test_query_common_reports_helper_error(app): def test_query_record_view_per_index_report_helper(mock_Community, mock_get_descendant_index_names, app, es): mock_Community.query.get.return_value = MagicMock(root_node_id=1) mock_get_descendant_index_names.return_value = ['index1'] - + # build_query res = QueryRecordViewPerIndexReportHelper.build_query(None, None, 'test_key') assert res.to_dict()=={'aggs': {'record_index_list': {'nested': {'path': 'record_index_list'}, 'aggs': {'my_buckets': {'composite': {'size': 6000, 'sources': [{'record_index_list.index_id': {'terms': {'field': 'record_index_list.index_id'}}}, {'record_index_list.index_name': {'terms': {'field': 'record_index_list.index_name'}}}], 'after': 'test_key'}}}}}, 'from': 0, 'size': 0} res = QueryRecordViewPerIndexReportHelper.build_query(None, None, index_list=['index1']) assert res.to_dict()=={'query': {'bool': {'filter': [{'nested': {'path': 'record_index_list', 'query': {'terms': {'record_index_list.index_name': ['index1']}}}}]}}, 'from': 0, 'size': 0, 'aggs': {'record_index_list': {'nested': {'path': 'record_index_list'}, 'aggs': {'my_buckets': {'composite': {'size': 6000, 'sources': [{'record_index_list.index_id': {'terms': {'field': 'record_index_list.index_id'}}}, {'record_index_list.index_name': {'terms': {'field': 'record_index_list.index_name'}}}]}}}}}} - + # parse_bucket_response _aggs = { 'my_buckets': { @@ -698,7 +698,7 @@ def test_query_item_reg_report_helper(mock_Community, mock_get_descendant_index_ assert res=={'num_page': 1, 'page': 1, 'data': [{'count': 1, 'start_date': '2022-10-01', 'end_date': '2022-10-01'}]} res = QueryItemRegReportHelper.get(target_report='1', unit='Day', start_date='0', end_date='0', repository_id='com1') assert res=={'num_page': 1, 'page': 1, 'data': [{'count': 1, 'start_date': '2022-10-01', 'end_date': '2022-10-01'}]} - + res = QueryItemRegReportHelper.get(target_report='1', unit='Week', start_date='2022-09-01', end_date='2022-09-15') assert res=={'num_page': 1, 'page': 1, 'data': [{'start_date': '2022-09-01 00:00:00', 'end_date': '2022-09-07 23:59:59', 'is_restricted': False, 'count': 0.0}, {'start_date': '2022-09-08 00:00:00', 'end_date': '2022-09-14 23:59:59', 'is_restricted': False, 'count': 0.0}, {'start_date': '2022-09-15 00:00:00', 'end_date': '2022-09-15 23:59:59', 'is_restricted': False, 'count': 0.0}]} @@ -1045,7 +1045,7 @@ def test_StatsCliUtil(app, db): ) assert not stats_cli.delete_data(True) - + stats_cli = StatsCliUtil( StatsCliUtil.EVENTS_TYPE, _empty_types, verbose=False diff --git a/modules/invenio-stats/tests/test_views.py b/modules/invenio-stats/tests/test_views.py index 53c17df991..576c462354 100644 --- a/modules/invenio-stats/tests/test_views.py +++ b/modules/invenio-stats/tests/test_views.py @@ -135,7 +135,7 @@ def __init__(self, child): # .tox/c1/bin/pytest --cov=invenio_stats tests/test_views.py::test_query_record_view_count -v -s -vv --cov-branch --cov-report=term --cov-config=tox.ini --basetemp=/code/modules/invenio-stats/.tox/c1/tmp def test_query_record_view_count(client, db, es, records): _uuid = str(records[0][0].object_uuid) - + # get res = client.get( url_for('invenio_stats.get_record_view_count', record_id=_uuid)) @@ -273,7 +273,7 @@ def test_query_item_reg_report(client, role_users, id, status_code): url_for('invenio_stats.get_item_registration_report', target_report='1', start_date='0', end_date='0', unit='Year', p='A')) assert res.status_code==status_code - + res = client.get( url_for('invenio_stats.get_item_registration_report', target_report='1', start_date='0', end_date='0', unit='Year', repo='comm1')) @@ -297,7 +297,7 @@ def test_query_record_view_report(client, role_users, id, status_code): res = client.get( url_for('invenio_stats.get_record_view_report', year=2022, month=9)) assert res.status_code==status_code - + res = client.get( url_for('invenio_stats.get_record_view_report', year=2022, month=9, repository_id='comm1')) assert res.status_code==status_code @@ -321,7 +321,7 @@ def test_query_record_view_per_index_report(client, role_users, id, status_code) res = client.get( url_for('invenio_stats.get_record_view_per_index_report', year=2022, month=9)) assert res.status_code==status_code - + res = client.get( url_for('invenio_stats.get_record_view_per_index_report', year=2022, month=9, repository_id='comm1')) assert res.status_code==status_code @@ -345,7 +345,7 @@ def test_query_file_reports(client, role_users, id, status_code): res = client.get( url_for('invenio_stats.get_file_reports', event='file_download', year=2022, month=9)) assert res.status_code==status_code - + res = client.get( url_for('invenio_stats.get_file_reports', event='file_download', year=2022, month=9, repository_id='comm1')) assert res.status_code==status_code @@ -358,7 +358,7 @@ def test_query_common_reports(client): res = client.get( url_for('invenio_stats.get_common_report', event='top_page_access', year=2022, month=9)) assert res.status_code==200 - + res = client.get( url_for('invenio_stats.get_common_report', event='top_page_access', year=2022, month=9, repository_id='comm1')) assert res.status_code==200 @@ -421,7 +421,7 @@ def test_query_search_report(client, role_users, id, status_code): res = client.get( url_for('invenio_stats.get_search_report', year=2022, month=9)) assert res.status_code==status_code - + res = client.get( url_for('invenio_stats.get_search_report', year=2022, month=9, repository_id='comm1')) assert res.status_code==status_code @@ -432,4 +432,4 @@ def test_query_search_report(client, role_users, id, status_code): def test_dbsession_clean(app): with patch("invenio_db.db.session.commit", side_effect=Exception): assert not dbsession_clean(None) - assert not dbsession_clean('') \ No newline at end of file + assert not dbsession_clean('') diff --git a/modules/weko-accounts/requirements.txt b/modules/weko-accounts/requirements.txt index 2e20530da5..4bf77e2ced 100644 --- a/modules/weko-accounts/requirements.txt +++ b/modules/weko-accounts/requirements.txt @@ -159,7 +159,7 @@ Flask-Plugins==1.6.1 bibtexparser==1.0.1 github3.py==1.1.0 feedgen==0.7.0 -boto3==1.7.84 +boto3==1.9.83 s3fs==0.1.6 #fpdf==1.7.2 -e git+https://github.com/RCOSDP/pyfpdf.git@fix/nii#egg=fpdf diff --git a/modules/weko-accounts/requirements2.txt b/modules/weko-accounts/requirements2.txt index 608fab3168..cd3f5baf07 100644 --- a/modules/weko-accounts/requirements2.txt +++ b/modules/weko-accounts/requirements2.txt @@ -16,8 +16,8 @@ billiard==3.6.3.0 binaryornot==0.4.4 bleach==3.1.0 blinker==1.4 -boto3==1.7.84 -botocore==1.10.84 +boto3==1.9.83 +botocore==1.12.209 cachelib==0.1 captcha==0.4 cachetools==4.2.4 @@ -186,6 +186,7 @@ prompt-toolkit==1.0.15 psycopg2==2.7.3.2 ptyprocess==0.5.2 py==1.11.0 +py-ldnlib==0.1.3 pycparser==2.18 Pygments==2.2.0 PyJWT==1.5.3 @@ -193,14 +194,15 @@ PyLD==2.0.3 pyparsing==3.1.0 -e git+https://github.com/RCOSDP/PyPDF2.git@fefc684a3a74aff6f99e5dff24f9b4dd1c95169d#egg=PyPDF2 pyPEG2==2.15.2 -pytest==4.2.0 -pytest-mock -pytest-invenio +pytest>=4.2.0 +pytest-mock==3.6.1 python-dateutil==2.8.2 python-editor==1.0.3 python-geoip==1.2 pytz==2017.3 pyzmq==17.0.0 +rdflib==5.0.0 +rdflib-jsonld==0.6.1 redis==2.10.6 requests==2.18.4 requests-oauthlib==1.1.0 diff --git a/modules/weko-accounts/setup.py b/modules/weko-accounts/setup.py index b3eccbe33d..3079aa2ff3 100644 --- a/modules/weko-accounts/setup.py +++ b/modules/weko-accounts/setup.py @@ -109,6 +109,7 @@ 'weko_accounts_embedded_ds_multi_lang_js = ' 'weko_accounts.bundles:embedded_ds_multi_language_js', 'weko_accounts_suggest_js = weko_accounts.bundles:suggest_js', + 'weko_accounts_shibuser_css = weko_accounts.bundles:shibuser_css', ], }, extras_require=extras_require, diff --git a/modules/weko-accounts/tests/conftest.py b/modules/weko-accounts/tests/conftest.py index 7cbe5af4a0..582333f579 100644 --- a/modules/weko-accounts/tests/conftest.py +++ b/modules/weko-accounts/tests/conftest.py @@ -46,6 +46,7 @@ from weko_index_tree.models import Index from weko_records_ui import WekoRecordsUI from weko_redis.redis import RedisConnection +from weko_search_ui import WekoSearchUI from weko_user_profiles import WekoUserProfiles from weko_accounts import WekoAccounts, WekoAccountsREST @@ -102,13 +103,14 @@ def base_app(instance_path): WekoUserProfiles(app_) app_.register_blueprint(blueprint) WekoAccountsREST(app_) + WekoSearchUI(app_) return app_ @pytest.yield_fixture() def app(base_app): """Flask application fixture.""" - + with base_app.app_context(): yield base_app @pytest.yield_fixture() @@ -125,7 +127,7 @@ def db(app): db_.session.remove() db_.drop_all() # drop_database(str(db_.engine.url)) - + @pytest.yield_fixture() def client(app,session_time): """make a test client. @@ -136,7 +138,7 @@ def client(app,session_time): """ with app.test_client() as client: yield client - + @pytest.fixture() def users(app, db): @@ -163,7 +165,7 @@ def users(app, db): originalroleuser = create_test_user(email='originalroleuser@test.org') originalroleuser2 = create_test_user(email='originalroleuser2@test.org') student = User.query.filter_by(email='student@test.org').first() - + role_count = Role.query.filter_by(name='System Administrator').count() if role_count != 1: sysadmin_role = ds.create_role(name='System Administrator') @@ -189,7 +191,7 @@ def users(app, db): ds.add_role_to_user(generaluser, general_role) ds.add_role_to_user(originalroleuser, originalrole) ds.add_role_to_user(originalroleuser2, originalrole) - ds.add_role_to_user(originalroleuser2, repoadmin_role) + # ds.add_role_to_user(originalroleuser2, repoadmin_role) ds.add_role_to_user(student,studentrole) # Assign access authorization @@ -274,10 +276,10 @@ def users(app, db): @pytest.fixture() def session_time(app,db): session_lifetime = SessionLifetime(lifetime=60,is_delete=False) - + with db.session.begin_nested(): db.session.add(session_lifetime) - + @pytest.fixture def redis_connect(app): redis_connection = RedisConnection().connection(db=app.config['CACHE_REDIS_DB'], kv = True) diff --git a/modules/weko-accounts/tests/test_admin.py b/modules/weko-accounts/tests/test_admin.py index 53786a5e5d..b3e9e34567 100644 --- a/modules/weko-accounts/tests/test_admin.py +++ b/modules/weko-accounts/tests/test_admin.py @@ -1,24 +1,29 @@ import pytest +import json from mock import patch from flask import current_app,url_for,make_response from invenio_accounts.testutils import login_user_via_session as login +from weko_admin.models import AdminSettings +from invenio_accounts.models import User +from sqlalchemy.orm.session import object_session + class TestShibSettingView: - + def test_index_acl_guest(self,client,session_time): url = url_for("shibboleth.index",_external=True) res = client.get(url) assert res.status_code == 302 @pytest.mark.parametrize('user_index, is_can',[ (0,True), - (1,False), + (1,True), (2,False), (3,False), (4,False), (5,False), (6,False), ]) - def test_index_acl(self,client,users,user_index,is_can,mocker): + def test_index_acl(self,client,users,user_index,is_can,mocker, admin_settings): mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) login(client=client,email=users[user_index]["email"]) url = url_for("shibboleth.index",_external=True) @@ -28,43 +33,191 @@ def test_index_acl(self,client,users,user_index,is_can,mocker): else: assert res.status_code == 403 # .tox/c1/bin/pytest --cov=weko_accounts tests/test_admin.py::TestShibSettingView::test_index -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp - def test_index(self,client,users,mocker): - login(client=client,email=users[0]["email"]) - url = url_for("shibboleth.index") - sibuser_html = 'weko_accounts/setting/shibuser.html' - mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) - current_app.config.update( - WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True - ) - # shib_flg = 1 - res = client.post(url,data=dict( - submit="shib_form", - shibbolethRadios="1" - )) - assert current_app.config["WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED"] == True - assert res.status_code==200 - mock_render.assert_called_with(sibuser_html,shib_flg="1") - - mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) - current_app.config.update( - WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=False - ) - # shib_flg = 0 - res = client.post(url,data=dict( - submit="shib_form", - shibbolethRadios="0" - )) - assert res.status_code == 200 - assert current_app.config["WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED"] == False - mock_render.assert_called_with(sibuser_html,shib_flg="0") - - # raise BaseException - with patch("weko_accounts.admin.ShibSettingView.render",side_effect=BaseException): - res = client.post(url) - assert res.status_code == 400 - - # method is GET - mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) - res = client.get(url) - assert res.status_code == 200 - mock_render.assert_called_with(sibuser_html,shib_flg="0") \ No newline at end of file + def test_index(self,app,client,users,mocker, admin_settings, db): + with app.app_context(): + user = User.query.filter_by(email=users[0]['email']).first() + login(client=client, user=user) + url = url_for("shibboleth.index") + sibuser_html = 'weko_accounts/setting/shibuser.html' + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + current_app.config["WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED"] = True + + # モックに渡す変数を設定 + role_list = current_app.config['WEKO_ACCOUNTS_ROLE_LIST'] + attr_list = current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_LIST'] + block_user_list = admin_settings[0].settings['blocked_ePPNs'] + roles = admin_settings[2].settings + set_language = "en" + shib_flg = "1" + attributes = admin_settings[3].settings + + data = { + "submit": "shib_form", + "shibbolethRadios": "1", + "block-eppn-option-list": json.dumps(block_user_list), + "enable-login-user-list": [], + } + + for i, (_, value) in enumerate(roles.items()): + data[f"role-lists{i}"] = value + + for i, (_, value) in enumerate(attributes.items()): + data[f"attr-lists{i}"] = value + + # new_shib_flg = 1 + res = client.post(url, data=data) + + assert admin_settings[1].settings["shib_flg"] is True + assert res.status_code==200 + + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + current_app.config["WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED"] = False + + data["shibbolethRadios"] = "0" + + # new_shib_flg = 0 + res = client.post(url, data=data) + + assert res.status_code == 200 + assert admin_settings[1].settings["shib_flg"] is False + shib_flg = "0" + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + + # デフォルトロールを変更 + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + roles = { + "gakunin_role": "Repository Administrator", + "orthros_outside_role": "None", + "extra_role": "Contributor"} + + for i, (_, value) in enumerate(roles.items()): + data[f"role-lists{i}"] = value + + res = client.post(url, data=data) + + assert res.status_code == 200 + assert admin_settings[2].settings["gakunin_role"] == "Repository Administrator" + assert admin_settings[2].settings["orthros_outside_role"] == "None" + assert admin_settings[2].settings["extra_role"] == "Contributor" + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + + # 属性を変更 + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + attributes = { + "shib_eppn": "eduPersonAffiliation", + "shib_role_authority_name": "eppn", + "shib_mail": "DisplayName", + "shib_user_name": "sn"} + + for i, (_, value) in enumerate(attributes.items()): + data[f"attr-lists{i}"] = value + + res = client.post(url, data=data) + + assert res.status_code == 200 + assert admin_settings[3].settings["shib_eppn"] == "eduPersonAffiliation" + assert admin_settings[3].settings["shib_role_authority_name"] == "eppn" + assert admin_settings[3].settings["shib_mail"] == "DisplayName" + assert admin_settings[3].settings["shib_user_name"] == "sn" + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + + # ブロックユーザーを変更 + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + block_user_list = ['test1','test2','test3'] + data["block-eppn-option-list"] = json.dumps(block_user_list) + + res = client.post(url, data=data) + + assert res.status_code == 200 + assert "test1" in admin_settings[0].settings["blocked_ePPNs"] + + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=str(block_user_list), + enable_login_user_list=[], + **roles, + **attributes + ) + + # raise BaseException + with patch("weko_accounts.admin.ShibSettingView.render",side_effect=BaseException): + res = client.post(url) + assert res.status_code == 400 + + # method is GET + mock_render = mocker.patch("weko_accounts.admin.ShibSettingView.render",return_value=make_response()) + res = client.get(url) + assert res.status_code == 200 + mock_render.assert_called_with( + sibuser_html, + shib_flg=shib_flg, + set_language=set_language, + role_list=role_list, + attr_list=attr_list, + block_user_list=block_user_list, + enable_login_user_list=[], + **roles, + **attributes + ) + + @pytest.fixture + def admin_settings(self, db): + settings = list() + settings.append(AdminSettings(id=6,name="blocked_user_settings",settings={"blocked_ePPNs": []})) + settings.append(AdminSettings(id=7,name="shib_login_enable",settings={"shib_flg": False})) + settings.append(AdminSettings(id=8,name="default_role_settings",settings={ + "gakunin_role": "Contributor", + "orthros_outside_role": "Community Administrator", + "extra_role": "None"})) + settings.append(AdminSettings(id=9,name="attribute_mapping",settings={ + "shib_eppn": "eppn", + "shib_role_authority_name": "eduPersonAffiliation", + "shib_mail": "mail", + "shib_user_name": "DisplayName"})) + db.session.add_all(settings) + db.session.commit() + return settings diff --git a/modules/weko-accounts/tests/test_api.py b/modules/weko-accounts/tests/test_api.py index a8b5a07a0a..ff28292c30 100644 --- a/modules/weko-accounts/tests/test_api.py +++ b/modules/weko-accounts/tests/test_api.py @@ -26,16 +26,16 @@ def test_init(self,db,users): assert shibuser.shib_attr == attr assert shibuser.user == None assert shibuser.shib_user == None - - + + # 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-workflow/.tox/c1/tmp def test_set_weko_user_role(self,app,db,users): - + role_sysadmin = Role.query.filter_by(name='System Administrator').first() role_repoadmin = Role.query.filter_by(name='Repository Administrator').first() role_original = Role.query.filter_by(name='Original Role').first() - + user = users[6]["obj"] attr = { "shib_eppn":"test_eppn" @@ -44,16 +44,16 @@ def test_set_weko_user_role(self,app,db,users): db.session.add(s_user) s_user.shib_roles.append(role_original) db.session.commit() - + shibuser = ShibUser(attr) shibuser.shib_user = s_user shibuser.user=user - + roles = ['System Administrator','Repository Administrator'] result = shibuser._set_weko_user_role(roles) assert shibuser.user.roles == [role_repoadmin,role_sysadmin] assert shibuser.shib_user.shib_roles == [role_sysadmin] - + # raise Exception error = Exception("test_error") with patch("weko_accounts.api.db.session.begin_nested",side_effect=error): @@ -72,7 +72,7 @@ def test_get_site_license(self): # def get_relation_info(self): # .tox/c1/bin/pytest --cov=weko_accounts tests/test_api.py::TestShibUser::test_get_relation_info -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp def test_get_relation_info(self,app,db,users): - + user1 = users[0]["obj"] user2 = users[1]["obj"] attr = { @@ -93,7 +93,7 @@ def test_get_relation_info(self,app,db,users): assert result.shib_mail == "shib.user@test.org" assert result.shib_user_name == "shib name1" assert result.shib_role_authority_name == "shib auth" - + # not exist shib_eppn,not exist shib_user.weko_user attr = { "shib_eppn":"", @@ -105,7 +105,7 @@ def test_get_relation_info(self,app,db,users): shibuser = ShibUser(attr) result = shibuser.get_relation_info() assert result == None - + # not exist shib_eppn, exist shib_user.weko_user,exist self.user, raise Exception s_user2.weko_user = user2 s_user2.weko_uid = user2.id @@ -120,16 +120,16 @@ def test_get_relation_info(self,app,db,users): def test_check_weko_user(self,app,users): user = users[0]["obj"] password = user.password_plaintext - + # exist wkeo_user, correct password shibuser = ShibUser({}) result = shibuser.check_weko_user(user.email,password) assert result == True - + # not exist weko_user result = shibuser.check_weko_user("not.exist.user@test.org",password) assert result == False - + # exist weko_user, not correct password result = shibuser.check_weko_user(user.email,"wrong passwd") assert result == False @@ -148,7 +148,7 @@ def test_bind_relation_info(self,app,users): assert users[0]["obj"].email == "new.sysadmin_mail@test.org" assert shibuser.shib_attr["shib_eppn"] == "shib name" assert result == ShibbolethUser.query.filter_by(shib_eppn="shib name").one_or_none() - + # exist shib_eppn, raise Exception user = users[1]["email"] attr = { @@ -166,7 +166,7 @@ def test_new_relation_info(self,users,mocker): today = datetime(2022,10,6,1,2,3,4) datetime_mock.utcnow.return_value=today mocker.patch("weko_accounts.api.ShibUser.new_shib_profile") - + # exist user user = users[0]["obj"] attr = { @@ -177,7 +177,7 @@ def test_new_relation_info(self,users,mocker): result = shibuser.new_relation_info() assert result.shib_eppn == "test_eppn1" assert result.weko_uid == user.id - + # not exist user attr = { "shib_mail":"newuser@test.org", @@ -200,11 +200,11 @@ def test_new_shib_profile(self,db,users): shibuser = ShibUser(attr) shibuser.shib_user = s_user shibuser.user=user - + result = shibuser.new_shib_profile() profile = UserProfile.query.filter_by(user_id=user.id).one_or_none() assert result==profile - + # def shib_user_login(self): # .tox/c1/bin/pytest --cov=weko_accounts tests/test_api.py::TestShibUser::test_shib_user_login -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp def test_shib_user_login(self,request_context,users,mocker): @@ -219,13 +219,13 @@ def test_shib_user_login(self,request_context,users,mocker): # def assign_user_role(self): # .tox/c1/bin/pytest --cov=weko_accounts tests/test_api.py::TestShibUser::test_assign_user_role -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp def test_assign_user_role(self,users,mocker): - + # not exist self.user shibuser = ShibUser({}) flg, ret = shibuser.assign_user_role() assert flg == False assert ret == "Can't get relation Weko User." - + # exist self.user, issubset, ret is None attr = { "shib_role_authority_name":"管理者;図書館員" @@ -237,7 +237,7 @@ def test_assign_user_role(self,users,mocker): mock_set_role.assert_called_with(['System Administrator','Repository Administrator']) assert flg == True assert ret == None - + # ret is error error = Exception("test_error") mock_set_role=mocker.patch("weko_accounts.api.ShibUser._set_weko_user_role",return_value=error) @@ -245,7 +245,7 @@ def test_assign_user_role(self,users,mocker): mock_set_role.assert_called_with(['System Administrator','Repository Administrator']) assert flg == False assert ret == error - + # not issubset attr = { "shib_role_authority_name":"異常役員" @@ -347,6 +347,9 @@ def test_check_in(self, app, mocker): shibuser.user.roles = MagicMock() with app.app_context(): + # テストでは_find_organization_nameは常にFalseを返すようにモック化 + mocker.patch.object(shibuser, '_find_organization_name', return_value=False) + # assign_user_roleがFalseを返す場合のテスト mocker.patch.object(shibuser, 'assign_user_role', return_value=(False, "test_error")) result = shibuser.check_in() @@ -451,6 +454,58 @@ def test_get_roles_to_add(self, app, mocker): with pytest.raises(KeyError, match='WEKO_ACCOUNTS_IDP_ENTITY_ID is missing in config'): shibuser._get_roles_to_add() +#.tox/c1/bin/pytest --cov=weko_accounts tests/test_api.py::TestShibUser::test_find_organization_name -vv -s --cov-branch --cov-report=html --basetemp=/code/modules/weko-workflow/.tox/c1/tmp + def test_find_organization_name(self, shib_user_a, app, mocker): + with app.app_context(): + with patch('weko_accounts.api.db.session') as mock_db_session, \ + patch('weko_accounts.api.current_app') as mock_current_app: + + mock_current_app.config = { + "WEKO_ACCOUNTS_GAKUNIN_ROLE": { + 'defaultRole': 'Contributor', + 'organizationName': ['Gakunin', 'Gakunin2'] + }, + "WEKO_ACCOUNTS_ORTHROS_INSIDE_ROLE": { + 'defaultRole': 'Repository Administrator', + 'organizationName': ['Orthros'] + }, + "WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE": { + 'defaultRole': 'Community Administrator', + 'organizationName': ['OutsideOrthros'] + }, + "WEKO_ACCOUNTS_EXTRA_ROLE": { + 'defaultRole': 'None', + 'organizationName': ['Extra'] + } + } + + group_ids = ['test_group_id'] + + # 学認IdPのorganizationNameに登録がある場合のテスト + mocker.patch("weko_accounts.api.ShibUser.get_organization_from_api", return_value="Gakunin2") + result = shib_user_a._find_organization_name(group_ids) + assert result == True + + # 機関内のOrthrosのorganizationNameに登録がある場合のテスト + mocker.patch("weko_accounts.api.ShibUser.get_organization_from_api", return_value="Orthros") + result = shib_user_a._find_organization_name(group_ids) + assert result == True + + # 機関外のOrthrosのorganizationNameに登録がある場合のテスト + mocker.patch("weko_accounts.api.ShibUser.get_organization_from_api", return_value="OutsideOrthros") + result = shib_user_a._find_organization_name(group_ids) + assert result == True + + # その他のorganizationNameに登録がある場合のテスト + mocker.patch("weko_accounts.api.ShibUser.get_organization_from_api", return_value="Extra") + result = shib_user_a._find_organization_name(group_ids) + assert result == True + + # organizationNameに登録がない場合のテスト + mocker.patch("weko_accounts.api.ShibUser.get_organization_from_api", return_value="invalid") + result = shib_user_a._find_organization_name(group_ids) + assert result == False + #.tox/c1/bin/pytest --cov=weko_accounts tests/test_api.py::TestShibUser::test_assign_roles_to_user -vv -s --cov-branch --cov-report=html --basetemp=/code/modules/weko-workflow/.tox/c1/tmp def test_assign_roles_to_user(self, shib_user_a, app, mocker): with app.app_context(): @@ -558,6 +613,37 @@ def test_assign_roles_to_user_exception(self, shib_user_a, app, mocker): assert mock_db_session.commit.call_count == 1 assert mock_db_session.rollback.call_count == 1 +# .tox/c1/bin/pytest --cov=weko_accounts tests/test_api.py::TestShibUser::test_get_ouganization_from_api -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp + def test_get_ouganization_from_api(self, app): + """ + PeopleAPIからorganization_nameを取得するメソッドテスト + """ + test_response = { + "entry": [ + { + "organizations": [ + {"type": "organization", "value": { + "name": "Orthros" + } + } + ] + } + ] + } + group_id = "test_group_id" + shibuser = ShibUser({}) + with app.app_context(): + with patch('requests.get') as mock_get, \ + patch('weko_accounts.api.current_app') as mock_current_app: + # モックが返すレスポンスを設定 + mock_get.return_value.status_code = 200 + mock_get.return_value.json = lambda: test_response + + # ShibUserクラスのメソッドを呼び出し、結果を確認 + result = shibuser.get_organization_from_api(group_id) + assert result == "Orthros" # 期待値を比較 + + # @classmethod # def shib_user_logout(cls): # .tox/c1/bin/pytest --cov=weko_accounts tests/test_api.py::TestShibUser::test_shib_user_logout -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp @@ -898,4 +984,4 @@ def test_update_and_remove_contribute_role(app, db): # クリーンアップ db.session.delete(index) - db.session.commit() \ No newline at end of file + db.session.commit() diff --git a/modules/weko-accounts/tests/test_views.py b/modules/weko-accounts/tests/test_views.py index ab532ab6d4..bb2937ceaa 100644 --- a/modules/weko-accounts/tests/test_views.py +++ b/modules/weko-accounts/tests/test_views.py @@ -14,8 +14,12 @@ _has_admin_access, init_menu, _redirect_method, + find_user_by_email shib_sp_login, + find_user_by_email ) +from weko_admin.models import AdminSettings + def set_session(client,data): with client.session_transaction() as session: for k, v in data.items(): @@ -46,22 +50,22 @@ def test_redirect_method(app,mocker): mock_render = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) _redirect_method(False) mock_render.assert_called_with(url_for('security.login')) - + mock_render = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) _redirect_method(True) mock_render.assert_called_with(url_for('security.login',next=url)) - + current_app.config.update( WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True ) mock_render = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) _redirect_method(False) mock_render.assert_called_with("http://TEST_SERVER.localdomain/secure/login.php") - + mock_render = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) _redirect_method(True) mock_render.assert_called_with("http://TEST_SERVER.localdomain/secure/login.php?next="+url) - + #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): @@ -78,15 +82,15 @@ def test_shib_auto_login(client,redis_connect,mocker): mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",return_value=make_response()) client.get(url) mock_redirect_.assert_called_once() - - + + 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()) client.get(url+"?SHIB_ATTR_SESSION_ID=2222") mock_redirect_.assert_called_once() - - + + redis_connect.put("Shib-Session-1111",bytes("","utf-8")) # not cache_val @@ -94,11 +98,11 @@ def test_shib_auto_login(client,redis_connect,mocker): client.get(url+"?SHIB_ATTR_SESSION_ID=1111") mock_redirect_.assert_called_once() assert redis_connect.redis.exists("Shib-Session-1111") == False - + 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()) @@ -107,19 +111,19 @@ def test_shib_auto_login(client,redis_connect,mocker): mock_get_relation.assert_called_once() mock_redirect_.assert_called_once() assert redis_connect.redis.exists("Shib-Session-1111") == False - + 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 - 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 - + # is_auto_bind is true,shib_user exis redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) set_session(client,{"shib_session_id":"1111","next":"/next_page"}) @@ -142,26 +146,26 @@ def test_confirm_user(client,redis_connect,mocker): mocker.patch("weko_accounts.views.RedisConnection.connection",return_value=redis_connect) mocker.patch("weko_accounts.views.ShibUser.shib_user_login") url = url_for("weko_accounts.confirm_user") - + # not correct csrf_random set_session(client,{"csrf_random":"xxxx"}) form = {"csrf_random":"test_csrf"} mock_flash = mocker.patch("weko_accounts.views.flash") client.post(url,data=form) mock_flash.assert_called_with("csrf_random",category="error") - + # not exist shib_session_id set_session(client,{"csrf_random":"test_csrf","shib_session_id":None}) 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 cache_key set_session(client,{"csrf_random":"test_csrf","shib_session_id":"2222"}) mock_flash = mocker.patch("weko_accounts.views.flash") client.post(url,data=form) mock_flash.assert_called_with("cache_key",category="error") - + set_session(client,{"csrf_random":"test_csrf","shib_session_id":"1111"}) # not exist cache_value redis_connect.put("Shib-Session-1111",bytes("","utf-8")) @@ -169,7 +173,7 @@ def test_confirm_user(client,redis_connect,mocker): client.post(url,data=form) mock_flash.assert_called_with("cache_val",category="error") assert redis_connect.redis.exists("Shib-Session-1111") is False - + # 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): @@ -200,7 +204,7 @@ def test_confirm_user(client,redis_connect,mocker): 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")) @@ -213,31 +217,115 @@ def test_confirm_user(client,redis_connect,mocker): client.post(url,data=form) mock_redirect.assert_called_with("/next_page") assert redis_connect.redis.exists("Shib-Session-1111") is False - + # raise BaseException with patch("weko_accounts.views._redirect_method",side_effect=BaseException("test_error")): res = client.post(url,data=form) assert res.status_code == 400 +#def confirm_user_without_page(): +# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_confirm_user_without_page -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp +def test_confirm_user_without_page(client,redis_connect,mocker): + mocker.patch("weko_accounts.views.RedisConnection.connection",return_value=redis_connect) + mocker.patch("weko_accounts.views.ShibUser.shib_user_login") + url = url_for("weko_accounts.confirm_user_without_page") + + # not exist shib_session_id + set_session(client,{"shib_session_id":None}) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("shib_session_id",category="error") + + # not exist cache_key + set_session(client,{"shib_session_id":"2222"}) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("cache_key",category="error") + + set_session(client,{"shib_session_id":"1111"}) + # not exist cache_value + redis_connect.put("Shib-Session-1111",bytes("","utf-8")) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("cache_val",category="error") + assert redis_connect.redis.exists("Shib-Session-1111") is False + + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + with patch("weko_accounts.views.ShibUser.check_weko_user",return_value=True): + # shib_user.bind_relation_info is false + with patch("weko_accounts.views.ShibUser.bind_relation_info",return_value=False): + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("FAILED bind_relation_info!",category="error") + assert redis_connect.redis.exists("Shib-Session-1111") is False + with patch("weko_accounts.views.ShibUser.bind_relation_info",return_value=True): + # ShibUser.check_in is error + with patch("weko_accounts.views.ShibUser.check_in",return_value="test_error"): + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_flash.assert_called_with("test_error",category="error") + assert redis_connect.redis.exists("Shib-Session-1111") is False + with patch("weko_accounts.views.ShibUser.check_in",return_value=None): + # ShibUser.shib_user is None,not exist next in session + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) + client.get(url) + mock_redirect.assert_called_with("/") + assert redis_connect.redis.exists("Shib-Session-1111") is False + + # exist ShibUser.shib_user + set_session(client,{"shib_session_id":"1111"}) + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + + shibuser = ShibUser({}) + shibuser.shib_user = "test_user" + with patch("weko_accounts.views.ShibUser",return_value=shibuser): + mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_redirect.assert_called_with("/") + assert redis_connect.redis.exists("Shib-Session-1111") is False + + # exist ShibUser.shib_user + set_session(client,{"shib_session_id":"1111","next":"/next_page"}) + redis_connect.put("Shib-Session-1111",bytes('{"shib_eppn":"test_eppn"}',"utf-8")) + + shibuser = ShibUser({}) + shibuser.shib_user = "test_user" + with patch("weko_accounts.views.ShibUser",return_value=shibuser): + mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) + mock_flash = mocker.patch("weko_accounts.views.flash") + client.get(url) + mock_redirect.assert_called_with("/next_page") + assert redis_connect.redis.exists("Shib-Session-1111") is False + + # raise BaseException + with patch("weko_accounts.views._redirect_method",side_effect=BaseException("test_error")): + res = client.get(url) + assert res.status_code == 400 + + #def shib_login(): # .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_shib_login -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp def test_shib_login(client,redis_connect,users,mocker): 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") - + # not shib_session_id mock_flash = mocker.patch("weko_accounts.views.flash") client.get(url_base) mock_flash.assert_called_with("Missing SHIB_ATTR_SESSION_ID!",category="error") - + url = url_base+"?SHIB_ATTR_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") - + url = url_base+"?SHIB_ATTR_SESSION_ID=1111" # not cache_val redis_connect.put("Shib-Session-1111",bytes('',"utf-8")) @@ -245,19 +333,19 @@ def test_shib_login(client,redis_connect,users,mocker): client.get(url) mock_flash.assert_called_with("Missing SHIB_ATTR!",category="error") assert redis_connect.redis.exists("Shib-Session-1111") is False - + # 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()) client.get(url) 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()) client.get(url) 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) @@ -275,20 +363,55 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users): client.post(url,data={}) mock_flash.assert_called_with("Missing SHIB_ATTR_SESSION_ID!",category="error") mock_redirect.assert_called_with(url_for("security.login")) - + current_app.config.update( WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True ) form = { "SHIB_ATTR_SESSION_ID":"1111" } - + # parse_attribute is error with patch("weko_accounts.views.parse_attributes",return_value=("attr",True)): mock_flash = mocker.patch("weko_accounts.views.flash") client.post(url,data=form) mock_flash.assert_called_with("Missing SHIB_ATTRs!",category="error") - + + # Check if shib_eppn is not included in the blocked user list + try: + db.session.add(AdminSettings( + id=11, + name="blocked_user_settings", + settings='{"blocked_ePPNs": ["ePPN1", "ePPN2", "ePPN3", "ePPN5", "ePPP*"]}' + )) + db.session.commit() + except Exception as e: + db.session.rollback() + raise + finally: + db.session.remove() + + # Match with blocked user + mock_flash = mocker.patch("weko_accounts.views.flash") + form = { + "SHIB_ATTR_SESSION_ID":"1111", + "SHIB_ATTR_EPPN":"ePPN3" + } + client.post(url,data=form) + mock_flash.assert_called_with("Failed to login.",category="error") + mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",return_value=make_response()) + + # Match found with a blocked user from the wildcard + mock_flash = mocker.patch("weko_accounts.views.flash") + form = { + "SHIB_ATTR_SESSION_ID":"1111", + "SHIB_ATTR_EPPN":"ePPP3" + } + client.post(url,data=form) + mock_flash.assert_called_with("Failed to login.",category="error") + mock_redirect_ = mocker.patch("weko_accounts.views._redirect_method",return_value=make_response()) + + # Not a blocked user form = { "SHIB_ATTR_SESSION_ID":"1111", "SHIB_ATTR_EPPN":"test_eppn" @@ -322,20 +445,42 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users): and patch("weko_accounts.views.redirect",return_value=make_response()): res = client.post(url,data=form) assert res.status_code == 200 - #assert res.url == "/weko/shib/login?SHIB_ATTR_SESSION_ID=1111&_method=GET" + assert res.data.decode() == "/weko/shib/login?SHIB_ATTR_SESSION_ID=1111&next=%2F" # shib_user.get_relation_info is not None with patch("weko_accounts.views.ShibUser.get_relation_info",return_value="chib_user")\ and patch("weko_accounts.views.redirect",return_value=make_response()): res = client.post(url,data=form) assert res.status_code == 200 - #assert res == "/weko/auto/login?SHIB_ATTR_SESSION_ID=1111&_method=GET" - + assert res.data.decode() == "/weko/auto/login?SHIB_ATTR_SESSION_ID=1111&next=%2F" + + current_app.config.update( + WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True, + WEKO_ACCOUNTS_SKIP_CONFIRMATION_PAGE=True + ) + # shib_user.get_relation_info is None + with patch("weko_accounts.views.ShibUser.get_relation_info",return_value=None): + with patch("weko_accounts.views.find_user_by_email",return_value="shib_user"): + res = client.post(url,data=form) + assert res.status_code == 200 + assert res.data.decode() == "/weko/confim/user/skip?SHIB_ATTR_SESSION_ID=1111&next=%2F" + + with patch("weko_accounts.views.find_user_by_email",return_value=None): + res = client.post(url,data=form) + assert res.status_code == 200 + assert res.data.decode() == "/weko/auto/login?SHIB_ATTR_SESSION_ID=1111&next=%2F" + + # shib_user.get_relation_info is not None + with patch("weko_accounts.views.ShibUser.get_relation_info",return_value="shib_user"): + res = client.post(url,data=form) + assert res.status_code == 200 + assert res.data.decode() == "/weko/auto/login?SHIB_ATTR_SESSION_ID=1111&next=%2F" + # raise BaseException with patch("weko_accounts.views.flash",side_effect=BaseException("test_error"))\ and patch("weko_accounts.views._redirect_method",return_value=make_response()) as mock_redirect_: res = client.post(url,data={}) mock_redirect_.assert_called_once() - + # all attributes have value and some shibboleth_user records don't have target eppn current_app.config.update( WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True, @@ -513,15 +658,16 @@ def test_shib_sp_login(client, redis_connect,mocker, db, users): assert res.status_code == 302 assert res.headers['Location'] == 'http://{}/login/'.format(current_app.config["SERVER_NAME"]) + #def shib_stub_login(): # .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_shib_stub_login -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp def test_shib_stub_login(client,mocker): url = url_for("weko_accounts.shib_stub_login")+"?next=/next_page" - + # WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED is false res = client.get(url) assert res.status_code == 403 - + current_app.config.update( WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED=True ) @@ -529,7 +675,7 @@ def test_shib_stub_login(client,mocker): mock_redirect = mocker.patch("weko_accounts.views.redirect",return_value=make_response()) res = client.get(url) mock_redirect.assert_called_with("http://test_server.localdomain/secure/login.php") - + current_app.config.update( WEKO_ACCOUNTS_SHIB_IDP_LOGIN_ENABLED=False ) @@ -537,10 +683,22 @@ def test_shib_stub_login(client,mocker): 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") - + #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 def test_shib_logout(client, mocker): mocker.patch("weko_accounts.views.ShibUser.shib_user_logout") res = client.get(url_for("weko_accounts.shib_logout")) - assert res.data == bytes("logout success","utf-8") \ No newline at end of file + assert res.data == bytes("logout success","utf-8") + +# def find_user_by_email(shib_attributes): +# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_find_user_by_email -vv -s --cov-branch --cov-report=term --cov-report=html --basetemp=/code/modules/weko-workflow/.tox/c1/tmp +def test_find_user_by_email(app, users): + + with app.test_request_context(): + user = find_user_by_email({"shib_mail": users[0].get("email")}) + assert user.email == users[0].get("email") + assert user.id == users[0].get("id") + + user = find_user_by_email({"shib_mail": "invalid.email@nii.ac.jp"}) + assert user is None diff --git a/modules/weko-accounts/weko_accounts/admin.py b/modules/weko-accounts/weko_accounts/admin.py index a10c25fa68..9e41789a0c 100644 --- a/modules/weko-accounts/weko_accounts/admin.py +++ b/modules/weko-accounts/weko_accounts/admin.py @@ -21,14 +21,17 @@ """WEKO3 module docstring.""" import sys +import json from flask import abort, current_app, flash, request from flask_admin import BaseView, expose from flask_babelex import gettext as _ from werkzeug.local import LocalProxy -_app = LocalProxy(lambda: current_app.extensions['weko-admin'].app) +from weko_admin.models import AdminSettings, db +from weko_accounts.models import ShibbolethUser, db +_app = LocalProxy(lambda: current_app.extensions['weko-admin'].app) class ShibSettingView(BaseView): """ShibSettingView.""" @@ -37,31 +40,97 @@ class ShibSettingView(BaseView): def index(self): """Index.""" try: - shib_flg = '0' - if current_app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED']: - shib_flg = '1' + # Shibbolethログイン可否 + shib_login_enable = AdminSettings.get('shib_login_enable', dict_to_object=False) + shib_flg = '0' if not shib_login_enable.get('shib_flg', current_app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED']) else '1' + + role_list = current_app.config['WEKO_ACCOUNTS_ROLE_LIST'] + attr_list = current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_LIST'] + set_language = _('language') + + block_user_settings = AdminSettings.get('blocked_user_settings') + block_user_list = block_user_settings.__dict__['blocked_ePPNs'] + + shib_eppns = db.session.query(ShibbolethUser.shib_eppn).all() + enable_login_user_list = [shib_eppn[0] for shib_eppn in shib_eppns] + + # デフォルトロール + default_roles = AdminSettings.get('default_role_settings', dict_to_object=False) + roles = { + 'gakunin_role': default_roles.get('gakunin_role', current_app.config['WEKO_ACCOUNTS_GAKUNIN_ROLE']['defaultRole']), + 'orthros_outside_role': default_roles.get('orthros_outside_role', current_app.config['WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE']['defaultRole']), + 'extra_role': default_roles.get('extra_role', current_app.config['WEKO_ACCOUNTS_EXTRA_ROLE']['defaultRole']) + } + + # 属性マッピング + attribute_mappings = AdminSettings.get('attribute_mapping', dict_to_object=False) + attributes = { + 'shib_eppn': attribute_mappings.get('shib_eppn', current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP']['shib_eppn']), + 'shib_role_authority_name': attribute_mappings.get('shib_role_authority_name', current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP']['shib_role_authority_name']), + 'shib_mail': attribute_mappings.get('shib_mail', current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP']['shib_mail']), + 'shib_user_name': attribute_mappings.get('shib_user_name', current_app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP']['shib_user_name']) + } + + # ブロックユーザー + block_user_settings = AdminSettings.get('blocked_user_settings', dict_to_object=False) + block_user_list = block_user_settings.get('blocked_ePPNs', []) if request.method == 'POST': # Process forms form = request.form.get('submit', None) + new_shib_flg = request.form.get('shibbolethRadios', '0') + new_roles = {key: request.form.get(f'role-lists{i}', []) for i, key in enumerate(roles)} + new_attributes = {key: request.form.get(f'attr-lists{i}', []) for i, key in enumerate(attributes)} + new_block_user_list = request.form.get('block-eppn-option-list', "[]") + if form == 'shib_form': - shib_flg = request.form.get('shibbolethRadios', '0') - if shib_flg == '1': - _app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED'] = True - else: - _app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED'] = False - flash( - _('Shibboleth flag was updated.'), - category='success') + if shib_flg != new_shib_flg: + shib_flg = new_shib_flg + AdminSettings.update('shib_login_enable', {"shib_flg": (shib_flg == '1')}) + flash(_('Shibboleth flag was updated.'), category='success') + + # デフォルトロールの更新 + for key in roles: + if roles[key] != new_roles[key]: + roles[key] = new_roles[key] + flash(_(f'{key.replace("_", " ").title()} was updated.'), category='success') + AdminSettings.update('default_role_settings', roles) + + # 属性マッピングの更新 + for key in attributes: + if attributes[key] != new_attributes[key]: + attributes[key] = new_attributes[key] + flash(_(f'{key.replace("_", " ").title()} mapping was updated.'), category='success') + AdminSettings.update('attribute_mapping', attributes) + + # ブロックユーザーの更新 + if block_user_list != json.loads(new_block_user_list): + new_eppn_list = json.loads(new_block_user_list) + new_eppn_list.sort() + updateSettings = {'blocked_ePPNs': new_eppn_list} + AdminSettings.update('blocked_user_settings', updateSettings) + flash( + _('Blocked user list was updated.'), + category='success') + block_user_list = str(new_eppn_list).replace('"', '\\"') + + self.get_latest_current_app() return self.render( current_app.config['WEKO_ACCOUNTS_SET_SHIB_TEMPLATE'], - shib_flg=shib_flg) + shib_flg=shib_flg, set_language=set_language, role_list=role_list, attr_list=attr_list, block_user_list=block_user_list, enable_login_user_list=enable_login_user_list, **roles, **attributes ) except BaseException: current_app.logger.error( 'Unexpected error: {}'.format(sys.exc_info())) return abort(400) + def get_latest_current_app(self): + _app.config['WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED'] = AdminSettings.get('shib_login_enable', dict_to_object=False)['shib_flg'] + default_roles = AdminSettings.get('default_role_settings', dict_to_object=False) + _app.config['WEKO_ACCOUNTS_GAKUNIN_ROLE']['defaultRole'] = default_roles['gakunin_role'] + _app.config['WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE']['defaultRole'] = default_roles['orthros_outside_role'] + _app.config['WEKO_ACCOUNTS_EXTRA_ROLE']['defaultRole'] = default_roles['extra_role'] + _app.config['WEKO_ACCOUNTS_ATTRIBUTE_MAP'] = AdminSettings.get('attribute_mapping', dict_to_object=False) shib_adminview = { 'view_class': ShibSettingView, diff --git a/modules/weko-accounts/weko_accounts/api.py b/modules/weko-accounts/weko_accounts/api.py index db3a0ba6fb..c5f00958eb 100644 --- a/modules/weko-accounts/weko_accounts/api.py +++ b/modules/weko-accounts/weko_accounts/api.py @@ -27,6 +27,10 @@ from .models import ShibbolethUser from weko_index_tree.models import Index +import urllib.parse +import requests + + _datastore = LocalProxy(lambda: current_app.extensions['security'].datastore) @@ -157,7 +161,7 @@ def bind_relation_info(self, account): self.user = User.query.filter_by(email=account).first() shib_username_config = current_app.config[ 'WEKO_ACCOUNTS_SHIB_ALLOW_USERNAME_INST_EPPN'] - + with db.session.begin_nested(): self.user.email = self.shib_attr['shib_mail'] @@ -287,7 +291,8 @@ def check_in(self): try: if current_app.config['WEKO_ACCOUNTS_SHIB_BIND_GAKUNIN_MAP_GROUPS']: roles_add = self._get_roles_to_add() - self._assign_roles_to_user(roles_add) + if not self._find_organization_name(roles_add): + self._assign_roles_to_user(roles_add) except Exception as ex: return str(ex) @@ -328,6 +333,40 @@ def _get_roles_to_add(self): return shib_attr_is_member_of + def _find_organization_name(self, group_ids): + """ + 事前に各ロールにorganization_nameが登録されていた場合、そのロールを割り当てる + """ + try: + with db.session.begin_nested(): + for group in group_ids: + group_id = urllib.parse.quote(group) + organization_name = self.get_organization_from_api(group_id) + + setting_role = "" + if organization_name in current_app.config["WEKO_ACCOUNTS_GAKUNIN_ROLE"]["organizationName"]: + setting_role = current_app.config["WEKO_ACCOUNTS_GAKUNIN_ROLE"]["defaultRole"].replace(" ", "_") + elif organization_name in current_app.config["WEKO_ACCOUNTS_ORTHROS_INSIDE_ROLE"]["organizationName"]: + setting_role = current_app.config["WEKO_ACCOUNTS_ORTHROS_INSIDE_ROLE"]["defaultRole"].replace(" ", "_") + elif organization_name in current_app.config["WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE"]["organizationName"]: + setting_role = current_app.config["WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE"]["defaultRole"].replace(" ", "_") + elif organization_name in current_app.config["WEKO_ACCOUNTS_EXTRA_ROLE"]["organizationName"]: + setting_role = current_app.config["WEKO_ACCOUNTS_EXTRA_ROLE"]["defaultRole"].replace(" ", "_") + + if len(setting_role) > 0: + role = Role.query.filter_by(name=setting_role).one_or_none() + _datastore.add_role_to_user(self.user, role) + self.shib_user.shib_roles.append(role) + return True + + db.session.commit() + return False + except Exception as ex: + current_app.logger.error(f"Error assigning roles: {ex}") + db.session.rollback() + raise ex + + def _assign_roles_to_user(self, map_group_names): try: # WEKO_ACCOUNTS_GAKUNIN_GROUP_PATTERN_DICT設定を取得 @@ -372,6 +411,27 @@ def _assign_roles_to_user(self, map_group_names): db.session.rollback() raise ex + def get_organization_from_api(self, group_id): + url = f"https://cg.gakunin.jp/api/people/@me/{group_id}" + headers = { + 'Content-Type': 'application/json', + } + + response = requests.get(url, headers=headers) + if response.status_code == 200: + data = response.json() + entries = data.get("entry", []) + for entry in entries: + organizations = entry.get("organizations", []) + for organization in organizations: + if organization.get("type") == "organization": + return organization.get("value", {}).get("name") + raise ValueError(f"Organization not found in response: {data}") + else: + raise ValueError(f"API returned error: {response.status_code}") + + + # ! NEED RELATION SHIB_ATTR # check_license, error = self.valid_site_license() # if not check_license: diff --git a/modules/weko-accounts/weko_accounts/bundles.py b/modules/weko-accounts/weko_accounts/bundles.py index 07602ae331..5ecb5fc829 100644 --- a/modules/weko-accounts/weko_accounts/bundles.py +++ b/modules/weko-accounts/weko_accounts/bundles.py @@ -42,3 +42,9 @@ 'js/weko_accounts/suggest.js', output="gen/weko_accounts_suggest.js", ) + +shibuser_css = Bundle( + 'css/weko_accounts/styles.bundle.css', + filters='cleancss', + output='gen/weko_accounts_styles.%(version)s.css', +) diff --git a/modules/weko-accounts/weko_accounts/config.py b/modules/weko-accounts/weko_accounts/config.py index ce7c0b5537..e67c08b6f1 100644 --- a/modules/weko-accounts/weko_accounts/config.py +++ b/modules/weko-accounts/weko_accounts/config.py @@ -63,28 +63,91 @@ WEKO_ACCOUNTS_SSO_ATTRIBUTE_MAP = { 'SHIB_ATTR_EPPN': (False, 'shib_eppn'), - # "SHIB_ATTR_LOGIN_ID": (False, 'shib_uid'), - # "SHIB_ATTR_HANDLE": (False, 'shib_handle'), + # 'SHIB_ATTR_LOGIN_ID': (False, 'shib_uid'), + # 'SHIB_ATTR_HANDLE': (False, 'shib_handle'), 'SHIB_ATTR_ROLE_AUTHORITY_NAME': (False, 'shib_role_authority_name'), - # "SHIB_ATTR_PAGE_NAME": (False, 'shib_page_name'), - # "SHIB_ATTR_ACTIVE_FLAG": (False, 'shib_active_flag'), + # 'SHIB_ATTR_PAGE_NAME': (False, 'shib_page_name'), + # 'SHIB_ATTR_ACTIVE_FLAG': (False, 'shib_active_flag'), 'SHIB_ATTR_SITE_USER_WITHIN_IP_RANGE_FLAG': (False, 'shib_ip_range_flag'), 'SHIB_ATTR_MAIL': (False, 'shib_mail'), 'SHIB_ATTR_USER_NAME': (False, 'shib_user_name'), } """IdP attribute map.""" -WEKO_ACCOUNTS_SHIB_ROLE_RELATION = { - '管理者': 'System Administrator', - '図書館員': 'Repository Administrator', - '教員': 'Contributor', - '教官': 'Contributor' +WEKO_ACCOUNTS_ATTRIBUTE_MAP = { + 'shib_eppn': 'eppn', + 'shib_role_authority_name': 'eduPersonAffiliation', + 'shib_mail': 'mail', + 'shib_user_name': 'DisplayName' } -"""Role relation.""" +"""IdP attribute map.""" + +WEKO_ACCOUNTS_ATTRIBUTE_LIST = [ + 'eppn', + 'DisplayName', + 'mail', + 'eduPersonOrcid', + 'jasn', + 'jaGivenName', + 'jaDisplayName', + 'jao', + 'jaou', + 'isMemberOf', + 'sn', + 'o', + 'ou', + 'givenName', + 'eduPersonAffiliation', + 'eduPersonScopedAffiliation', + 'eduPersonTargetedID' +] +"""Attribute List.""" + +WEKO_ACCOUNTS_ROLE_LIST = [ + 'System Administrator', + 'Repository Administrator', + 'Community Administrator', + 'Contributor', + 'None' +] +"""Role List.""" WEKO_ACCOUNTS_GENERAL_ROLE = 'Contributor' """Default role.""" +WEKO_ACCOUNTS_GAKUNIN_ROLE = { + 'defaultRole': 'Contributor', + 'organizationName': [] +} +"""Gakunin Default role.""" + +WEKO_ACCOUNTS_ORTHROS_INSIDE_ROLE = { + 'defaultRole': 'Repository Administrator', + 'organizationName': [] +} +"""Orthros (Inside) Default role.""" + +WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE = { + 'defaultRole': 'Community Administrator', + 'organizationName': [] +} +"""Orthros (Outside) Default role.""" + +WEKO_ACCOUNTS_EXTRA_ROLE = { + 'defaultRole': 'None', # ロール無 + 'organizationName': [] +} +"""Extra Default role.""" + +WEKO_ACCOUNTS_SHIB_ROLE_RELATION = { + '管理者': 'System Administrator', + '学認IdP': WEKO_ACCOUNTS_GAKUNIN_ROLE['defaultRole'], + '機関内のOrthros': WEKO_ACCOUNTS_ORTHROS_INSIDE_ROLE['defaultRole'], + '機関外のOrthros': WEKO_ACCOUNTS_ORTHROS_OUTSIDE_ROLE['defaultRole'], + 'その他': WEKO_ACCOUNTS_EXTRA_ROLE['defaultRole'] +} +"""Role relation.""" + WEKO_ACCOUNTS_SHIB_IDP_LOGIN_ENABLED = True """Shibboleth login pattern.""" @@ -96,17 +159,17 @@ WEKO_ACCOUNTS_SHIB_ALLOW_USERNAME_INST_EPPN = True """Allow using SHIB_ATTR_USER_NAME instead of SHIB_ATTR_EPPN.""" -WEKO_ACCOUNTS_LOGIN_LABEL = "Log in to account" +WEKO_ACCOUNTS_LOGIN_LABEL = 'Log in to account' """The login label""" -WEKO_ACCOUNTS_REGISTER_LABEL = "Sign up for a %(sitename)s account!" +WEKO_ACCOUNTS_REGISTER_LABEL = 'Sign up for a %(sitename)s account!' """The register label""" WEKO_ACCOUNTS_REAL_IP = None # X-Real-IP > X-Forwarded-For[0] > remote_addr -# WEKO_ACCOUNTS_REAL_IP = "remote_add" # remote_addr -# WEKO_ACCOUNTS_REAL_IP = "x_real_ip" # X-Real-IP > remote_addr -# WEKO_ACCOUNTS_REAL_IP = "x_forwarded_for" # X-Forwarded-For[first] > remote_addr -# WEKO_ACCOUNTS_REAL_IP = "x_forwarded_for_rev" # X-Forwarded-For[last] > remote_addr +# WEKO_ACCOUNTS_REAL_IP = 'remote_add' # remote_addr +# WEKO_ACCOUNTS_REAL_IP = 'x_real_ip' # X-Real-IP > remote_addr +# WEKO_ACCOUNTS_REAL_IP = 'x_forwarded_for' # X-Forwarded-For[first] > remote_addr +# WEKO_ACCOUNTS_REAL_IP = 'x_forwarded_for_rev' # X-Forwarded-For[last] > remote_addr WEKO_ACCOUNTS_REST_ENDPOINTS = { 'login': { @@ -121,9 +184,13 @@ WEKO_ACCOUNTS_API_LIMIT_RATE_DEFAULT = ['100 per minute'] -WEKO_API_LIMIT_RATE_DEFAULT = ["100 per minute"] +WEKO_API_LIMIT_RATE_DEFAULT = ['100 per minute'] """Default rate limit per endpoint for one user in the WEKO API.""" + +WEKO_ACCOUNTS_SKIP_CONFIRMATION_PAGE = False +"""Skip shibboleth confirmation page.""" + WEKO_ACCOUNTS_IDP_ENTITY_ID = '' """IdP entity ID that institution owned.""" @@ -152,4 +219,5 @@ """閲覧権限のデフォルト権限を設定する""" WEKO_INNDEXTREE_GAKUNIN_GROUP_DEFAULT_CONTRIBUTE_PERMISSION = False -"""投稿権限のデフォルト権限を設定する""" \ No newline at end of file +"""投稿権限のデフォルト権限を設定する""" + diff --git a/modules/weko-accounts/weko_accounts/ext.py b/modules/weko-accounts/weko_accounts/ext.py index 088ae11ae6..5327a4f4a0 100644 --- a/modules/weko-accounts/weko_accounts/ext.py +++ b/modules/weko-accounts/weko_accounts/ext.py @@ -22,6 +22,7 @@ from flask_babelex import gettext as _ from flask_login import user_logged_in, user_logged_out +from weko_admin.models import AdminSettings, db from . import config diff --git a/modules/weko-accounts/weko_accounts/rest.py b/modules/weko-accounts/weko_accounts/rest.py index a76bc97b78..5abd0f985a 100644 --- a/modules/weko-accounts/weko_accounts/rest.py +++ b/modules/weko-accounts/weko_accounts/rest.py @@ -32,9 +32,7 @@ from invenio_rest import ContentNegotiatedMethodView from .errors import VersionNotFoundRESTError, UserAllreadyLoggedInError, UserNotFoundError, InvalidPasswordError, DisabledUserError -from .utils import create_limiter - -limiter = create_limiter() +from .utils import limiter def create_blueprint(app, endpoints): diff --git a/modules/weko-accounts/weko_accounts/static/css/weko_accounts/styles.bundle.css b/modules/weko-accounts/weko_accounts/static/css/weko_accounts/styles.bundle.css index 672e0dcb0a..721309540b 100644 --- a/modules/weko-accounts/weko_accounts/static/css/weko_accounts/styles.bundle.css +++ b/modules/weko-accounts/weko_accounts/static/css/weko_accounts/styles.bundle.css @@ -1,3 +1,282 @@ -.Mymodal{position:fixed;z-index:1000;padding-top:25px;left:25%;top:20%;width:50%; - /* height: 40%; */overflow:auto - /* background-color: rgb(117, 116, 116); */}.node-menu{position:relative;width:150px}.node-menu .node-menu-content{width:100%;padding:5px;position:absolute;border:1px solid #bdbdbd;border-radius:5%;-webkit-box-shadow:0 0 5px #bdbdbd;box-shadow:0 0 5px #bdbdbd;background-color:#eee;color:#212121;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;z-index:999}.node-menu .node-menu-content li.node-menu-item{list-style:none;padding:3px}.node-menu .node-menu-content .node-menu-item:hover{border-radius:5%;opacity:unset;cursor:pointer;background-color:#bdbdbd;-webkit-transition:background-color .2s ease-out;transition:background-color .2s ease-out}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon{display:inline-block;width:16px}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon.new-tag:before{content:"\25CF"}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon.new-folder:before{content:"\25B6"}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon.rename:before{content:"\270E"}.node-menu .node-menu-content .node-menu-item .node-menu-item-icon.remove:before{content:"\2716"}.node-menu .node-menu-content .node-menu-item .node-menu-item-value{margin-left:5px}tree-internal ul{padding:3px 0 3px 25px}tree-internal li{padding:0;margin:0;list-style:none}tree-internal .over-drop-target{border:4px solid #757575;-webkit-transition:padding .2s ease-out;transition:padding .2s ease-out;padding:5px;border-radius:5%}tree-internal .tree{-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica Neue,Helvetica,Arial,sans-serif}tree-internal .tree li{list-style:none;cursor:default}tree-internal .tree li div{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box}tree-internal .tree .node-value{display:inline-block;color:#212121}tree-internal .tree .node-value:after{display:block;padding-top:-3px;width:0;height:2px;background-color:#212121;content:"";-webkit-transition:width .3s;transition:width .3s}tree-internal .tree .node-value:hover:after{width:100%}tree-internal .tree .node-left-menu{display:inline-block;height:100%;width:auto}tree-internal .tree .node-left-menu span:before{content:"\2026";color:#757575}tree-internal .tree .node-selected:after{width:100%}tree-internal .tree .folding{width:25px;display:inline-block;line-height:1px;padding:0 5px;font-weight:700}tree-internal .tree .folding.node-collapsed{cursor:pointer}tree-internal .tree .folding.node-collapsed:before{content:"\25B6";color:#757575}tree-internal .tree .folding.node-expanded{cursor:pointer}tree-internal .tree .folding.node-expanded:before{content:"\25BC";color:#757575}tree-internal .tree .folding.node-empty{color:#212121;text-align:center;font-size:.89em}tree-internal .tree .folding.node-empty:before{content:"\25B6";color:#757575}tree-internal .tree .folding.node-leaf{color:#212121;text-align:center;font-size:.89em}tree-internal .tree .folding.node-leaf:before{content:"\25CF";color:#757575}tree-internal ul.rootless{padding:0}tree-internal div.rootless{display:none!important}tree-internal .loading-children:after{content:" loading ...";color:#6a1b9a;font-style:italic;font-size:.9em;-webkit-animation-name:loading-children;animation-name:loading-children;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}@-webkit-keyframes loading-children{0%{color:#f3e5f5}12.5%{color:#e1bee7}25%{color:#ce93d8}37.5%{color:#ba68c8}50%{color:#ab47bc}62.5%{color:#9c27b0}75%{color:#8e24aa}87.5%{color:#7b1fa2}to{color:#6a1b9a}}@keyframes loading-children{0%{color:#f3e5f5}12.5%{color:#e1bee7}25%{color:#ce93d8}37.5%{color:#ba68c8}50%{color:#ab47bc}62.5%{color:#9c27b0}75%{color:#8e24aa}87.5%{color:#7b1fa2}to{color:#6a1b9a}} \ No newline at end of file +.Mymodal { + position: fixed; + z-index: 1000; + padding-top: 25px; + left: 25%; + top: 20%; + width: 50%; + /* height: 40%; */ + overflow: auto; + /* background-color: rgb(117, 116, 116); */ +} +.node-menu { + position: relative; + width: 150px; +} +.node-menu .node-menu-content { + width: 100%; + padding: 5px; + position: absolute; + border: 1px solid #bdbdbd; + border-radius: 5%; + -webkit-box-shadow: 0 0 5px #bdbdbd; + box-shadow: 0 0 5px #bdbdbd; + background-color: #eee; + color: #212121; + font-family: Helvetica Neue, Helvetica, Arial, sans-serif; + z-index: 999; +} +.node-menu .node-menu-content li.node-menu-item { + list-style: none; + padding: 3px; +} +.node-menu .node-menu-content .node-menu-item:hover { + border-radius: 5%; + opacity: unset; + cursor: pointer; + background-color: #bdbdbd; + -webkit-transition: background-color 0.2s ease-out; + transition: background-color 0.2s ease-out; +} +.node-menu .node-menu-content .node-menu-item .node-menu-item-icon { + display: inline-block; + width: 16px; +} +.node-menu + .node-menu-content + .node-menu-item + .node-menu-item-icon.new-tag:before { + content: "\25CF"; +} +.node-menu + .node-menu-content + .node-menu-item + .node-menu-item-icon.new-folder:before { + content: "\25B6"; +} +.node-menu + .node-menu-content + .node-menu-item + .node-menu-item-icon.rename:before { + content: "\270E"; +} +.node-menu + .node-menu-content + .node-menu-item + .node-menu-item-icon.remove:before { + content: "\2716"; +} +.node-menu .node-menu-content .node-menu-item .node-menu-item-value { + margin-left: 5px; +} +tree-internal ul { + padding: 3px 0 3px 25px; +} +tree-internal li { + padding: 0; + margin: 0; + list-style: none; +} +tree-internal .over-drop-target { + border: 4px solid #757575; + -webkit-transition: padding 0.2s ease-out; + transition: padding 0.2s ease-out; + padding: 5px; + border-radius: 5%; +} +tree-internal .tree { + -webkit-box-sizing: border-box; + box-sizing: border-box; + font-family: Helvetica Neue, Helvetica, Arial, sans-serif; +} +tree-internal .tree li { + list-style: none; + cursor: default; +} +tree-internal .tree li div { + display: inline-block; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +tree-internal .tree .node-value { + display: inline-block; + color: #212121; +} +tree-internal .tree .node-value:after { + display: block; + padding-top: -3px; + width: 0; + height: 2px; + background-color: #212121; + content: ""; + -webkit-transition: width 0.3s; + transition: width 0.3s; +} +tree-internal .tree .node-value:hover:after { + width: 100%; +} +tree-internal .tree .node-left-menu { + display: inline-block; + height: 100%; + width: auto; +} +tree-internal .tree .node-left-menu span:before { + content: "\2026"; + color: #757575; +} +tree-internal .tree .node-selected:after { + width: 100%; +} +tree-internal .tree .folding { + width: 25px; + display: inline-block; + line-height: 1px; + padding: 0 5px; + font-weight: 700; +} +tree-internal .tree .folding.node-collapsed { + cursor: pointer; +} +tree-internal .tree .folding.node-collapsed:before { + content: "\25B6"; + color: #757575; +} +tree-internal .tree .folding.node-expanded { + cursor: pointer; +} +tree-internal .tree .folding.node-expanded:before { + content: "\25BC"; + color: #757575; +} +tree-internal .tree .folding.node-empty { + color: #212121; + text-align: center; + font-size: 0.89em; +} +tree-internal .tree .folding.node-empty:before { + content: "\25B6"; + color: #757575; +} +tree-internal .tree .folding.node-leaf { + color: #212121; + text-align: center; + font-size: 0.89em; +} +tree-internal .tree .folding.node-leaf:before { + content: "\25CF"; + color: #757575; +} +tree-internal ul.rootless { + padding: 0; +} +tree-internal div.rootless { + display: none !important; +} +tree-internal .loading-children:after { + content: " loading ..."; + color: #6a1b9a; + font-style: italic; + font-size: 0.9em; + -webkit-animation-name: loading-children; + animation-name: loading-children; + -webkit-animation-duration: 2s; + animation-duration: 2s; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; +} +@-webkit-keyframes loading-children { + 0% { + color: #f3e5f5; + } + 12.5% { + color: #e1bee7; + } + 25% { + color: #ce93d8; + } + 37.5% { + color: #ba68c8; + } + 50% { + color: #ab47bc; + } + 62.5% { + color: #9c27b0; + } + 75% { + color: #8e24aa; + } + 87.5% { + color: #7b1fa2; + } + to { + color: #6a1b9a; + } +} +@keyframes loading-children { + 0% { + color: #f3e5f5; + } + 12.5% { + color: #e1bee7; + } + 25% { + color: #ce93d8; + } + 37.5% { + color: #ba68c8; + } + 50% { + color: #ab47bc; + } + 62.5% { + color: #9c27b0; + } + 75% { + color: #8e24aa; + } + 87.5% { + color: #7b1fa2; + } + to { + color: #6a1b9a; + } +} + +#default-role-list, +#attr-list, +#block-user-setting { + margin-bottom: 2rem; +} + +.select-list { + display: inline-block; + width: 20%; + margin-bottom: 3px; +} + +#block_eppn { + width: 75%; +} + +.block-user-area { + margin-left: 20%; +} + +#block-user-list { + display: inline-block; + width: 80%; + margin-left: 20%; +} + +#btn_delete_block_eppn { + margin-bottom: 2rem; + margin-left: 2px; +} + +#shib-save-button { + text-align: right; + width: 100%; +} diff --git a/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds.js b/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds.js index b4666e18fd..270adb450e 100644 --- a/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds.js +++ b/modules/weko-accounts/weko_accounts/static/js/weko_accounts/embedded_ds.js @@ -25,7 +25,13 @@ var wayf_hide_idps; var wayf_unhide_idps; var wayf_show_remember_checkbox; var wayf_force_remember_for_session; -var wayf_additional_idps; +var wayf_additional_idps = [ + { + "entityID": "https://core.orthros.gakunin.nii.ac.jp/idp", + "name": "Orthros", + "search": ["https://core.orthros.gakunin.nii.ac.jp/idp", "Orthros"] + }, +]; var wayf_discofeed_url; var wayf_sp_cookie_path; var wayf_list_height; @@ -34,1440 +40,1440 @@ var wayf_sp_samlACURL; var wayf_html = ""; var wayf_idps = { - "https://shib-idp01.iic.hokudai.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Hokkaido University", - search: ["https://shib-idp01.iic.hokudai.ac.jp/idp/shibboleth", "Hokkaido", "Hokkaido University", "Hokkaido University", "北海道大学"], - SAML1SSOurl: "https://shib-idp01.iic.hokudai.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.asahikawa-med.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Asahikawa Medical University", - search: ["https://idp.asahikawa-med.ac.jp/idp/shibboleth", "Hokkaido", "Asahikawa Medical University", "Asahikawa Medical University", "旭川医科大学"], - SAML1SSOurl: "https://idp.asahikawa-med.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.msls.kushiro-ct.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "National Institute of Technology,Kushiro College", - search: ["https://idp.msls.kushiro-ct.ac.jp/idp/shibboleth", "Hokkaido", "National Institute of Technology,Kushiro College", "National Institute of Technology,Kushiro College", "釧路工業高等専門学校"], - SAML1SSOurl: "https://idp.msls.kushiro-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shibboleth.lib.kitami-it.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Kitami Institute of Technology", - search: ["https://shibboleth.lib.kitami-it.ac.jp/idp/shibboleth", "Hokkaido", "Kitami Institute of Technology", "Kitami Institute of Technology", "北見工業大学"], - SAML1SSOurl: "https://shibboleth.lib.kitami-it.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://sso.sapmed.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Sapporo Medical University", - search: ["https://sso.sapmed.ac.jp/idp/shibboleth", "Hokkaido", "Sapporo Medical University", "Sapporo Medical University", "札幌医科大学"], - SAML1SSOurl: "https://sso.sapmed.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.tomakomai-ct.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "National Institute of Technology,Tomakomai College", - search: ["https://kidp.tomakomai-ct.ac.jp/idp/shibboleth", "Hokkaido", "National Institute of Technology,Tomakomai College", "National Institute of Technology,Tomakomai College", "苫小牧工業高等専門学校"], - SAML1SSOurl: "https://kidp.tomakomai-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.hakodate-ct.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "National Institute of Technology,Hakodate College", - search: ["https://kidp.hakodate-ct.ac.jp/idp/shibboleth", "Hokkaido", "National Institute of Technology,Hakodate College", "National Institute of Technology,Hakodate College", "函館工業高等専門学校"], - SAML1SSOurl: "https://kidp.hakodate-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.asahikawa-nct.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "National Institute of Technology,Asahikawa College", - search: ["https://kidp.asahikawa-nct.ac.jp/idp/shibboleth", "Hokkaido", "National Institute of Technology,Asahikawa College", "National Institute of Technology,Asahikawa College", "旭川工業高等専門学校"], - SAML1SSOurl: "https://kidp.asahikawa-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.sgu.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Sapporo Gakuin University", - search: ["https://idp.sgu.ac.jp/idp/shibboleth", "Hokkaido", "Sapporo Gakuin University", "Sapporo Gakuin University", "札幌学院大学"], - SAML1SSOurl: "https://idp.sgu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin-idp01.mmm.muroran-it.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Muroran Institute of Technology", - search: ["https://gakunin-idp01.mmm.muroran-it.ac.jp/idp/shibboleth", "Hokkaido", "Muroran Institute of Technology", "Muroran Institute of Technology", "室蘭工業大学"], - SAML1SSOurl: "https://gakunin-idp01.mmm.muroran-it.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.fun.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Future University Hakodate", - search: ["https://idp.fun.ac.jp/idp/shibboleth", "Hokkaido", "Future University Hakodate", "Future University Hakodate", "公立はこだて未来大学"], - SAML1SSOurl: "https://idp.fun.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.hokkyodai.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Hokkaido University of Education", - search: ["https://idp.hokkyodai.ac.jp/idp/shibboleth", "Hokkaido", "Hokkaido University of Education", "Hokkaido University of Education", "北海道教育大学"], - SAML1SSOurl: "https://idp.hokkyodai.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://sib-idp.obihiro.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Obihiro University of Agriculture and Veterinary Medicine", - search: ["https://sib-idp.obihiro.ac.jp/idp/shibboleth", "Hokkaido", "Obihiro University of Agriculture and Veterinary Medicine", "Obihiro University of Agriculture and Veterinary Medicine", "帯広畜産大学"], - SAML1SSOurl: "https://sib-idp.obihiro.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.scu.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Sapporo City University", - search: ["https://idp.scu.ac.jp/idp/shibboleth", "Hokkaido", "Sapporo City University", "Sapporo City University", "札幌市立大学"], - SAML1SSOurl: "https://idp.scu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://rg-lshib01.rakuno.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "RAKUNO GAKUEN UNIVERSITY", - search: ["https://rg-lshib01.rakuno.ac.jp/idp/shibboleth", "Hokkaido", "RAKUNO GAKUEN UNIVERSITY", "RAKUNO GAKUEN UNIVERSITY", "酪農学園大学"], - SAML1SSOurl: "https://rg-lshib01.rakuno.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ictc-idp01.otaru-uc.ac.jp/idp/shibboleth": { - type: "hokkaido", - name: "Otaru University of Commerce", - search: ["https://ictc-idp01.otaru-uc.ac.jp/idp/shibboleth", "Hokkaido", "Otaru University of Commerce", "Otaru University of Commerce", "⼩樽商科⼤学"], - SAML1SSOurl: "https://ictc-idp01.otaru-uc.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://upki.yamagata-u.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "Yamagata University", - search: ["https://upki.yamagata-u.ac.jp/idp/shibboleth", "Tohoku", "Yamagata University", "Yamagata University", "山形大学"], - SAML1SSOurl: "https://upki.yamagata-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.miyakyo-u.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "Miyagi University of Education", - search: ["https://idp.miyakyo-u.ac.jp/idp/shibboleth", "Tohoku", "Miyagi University of Education", "Miyagi University of Education", "宮城教育大学"], - SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" - }, - "https://kidp.ichinoseki.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "National Institute of Technology,Ichinoseki College", - search: ["https://kidp.ichinoseki.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Ichinoseki College", "National Institute of Technology,Ichinoseki College", "一関工業高等専門学校"], - SAML1SSOurl: "https://kidp.ichinoseki.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.hachinohe-ct.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "National Institute of Technology,Hachinohe College", - search: ["https://kidp.hachinohe-ct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Hachinohe College", "National Institute of Technology,Hachinohe College", "八戸工業高等専門学校"], - SAML1SSOurl: "https://kidp.hachinohe-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ksidp.sendai-nct.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "National Institute of Technology,Sendai College,Hirose", - search: ["https://ksidp.sendai-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Sendai College,Hirose", "National Institute of Technology,Sendai College,Hirose", "仙台高等専門学校 広瀬キャンパス"], - SAML1SSOurl: "https://ksidp.sendai-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.akita-nct.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "National Institute of Technology,Akita College", - search: ["https://kidp.akita-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Akita College", "National Institute of Technology,Akita College", "秋田工業高等専門学校"], - SAML1SSOurl: "https://kidp.akita-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.auth.tohoku.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "Tohoku University", - search: ["https://idp.auth.tohoku.ac.jp/idp/shibboleth", "Tohoku", "Tohoku University", "Tohoku University", "東北大学"], - SAML1SSOurl: "https://idp.auth.tohoku.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.tsuruoka-nct.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "National Institute of Technology,Tsuruoka College", - search: ["https://kidp.tsuruoka-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Tsuruoka College", "National Institute of Technology,Tsuruoka College", "鶴岡工業高等専門学校"], - SAML1SSOurl: "https://kidp.tsuruoka-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.fukushima-nct.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "National Institute of Technology,Fukushima College", - search: ["https://kidp.fukushima-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Fukushima College", "National Institute of Technology,Fukushima College", "福島工業高等専門学校"], - SAML1SSOurl: "https://kidp.fukushima-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://knidp.sendai-nct.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "National Institute of Technology,Sendai College,Natori", - search: ["https://knidp.sendai-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Sendai College,Natori", "National Institute of Technology,Sendai College,Natori", "仙台高等専門学校 名取キャンパス"], - SAML1SSOurl: "https://knidp.sendai-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.tohtech.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "Tohoku Institute of Technology", - search: ["https://idp.tohtech.ac.jp/idp/shibboleth", "Tohoku", "Tohoku Institute of Technology", "Tohoku Institute of Technology", "東北工業大学"], - SAML1SSOurl: "https://idp.tohtech.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://auas.akita-u.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "Akita University", - search: ["https://auas.akita-u.ac.jp/idp/shibboleth", "Tohoku", "Akita University", "Akita University", "秋田大学"], - SAML1SSOurl: "https://auas.akita-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp01.gn.hirosaki-u.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "Hirosaki University", - search: ["https://idp01.gn.hirosaki-u.ac.jp/idp/shibboleth", "Tohoku", "Hirosaki University", "Hirosaki University", "弘前大学"], - SAML1SSOurl: "https://idp01.gn.hirosaki-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.u-aizu.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "The University of Aizu", - search: ["https://idp.u-aizu.ac.jp/idp/shibboleth", "Tohoku", "The University of Aizu", "The University of Aizu", "会津大学"], - SAML1SSOurl: "https://idp.u-aizu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://axl.aiu.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "Akita International University", - search: ["https://axl.aiu.ac.jp/idp/shibboleth", "Tohoku", "Akita International University", "Akita International University", "国際教養大学"], - SAML1SSOurl: "https://axl.aiu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.fmu.ac.jp/idp/shibboleth": { - type: "tohoku", - name: "Fukushima Medical University", - search: ["https://idp.fmu.ac.jp/idp/shibboleth", "Tohoku", "Fukushima Medical University", "Fukushima Medical University", "福島県立医科大学"], - SAML1SSOurl: "https://idp.fmu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://tg.ex-tic.com/auth/gakunin/saml2/assertions": { - type: "tohoku", - name: "Tohoku Gakuin University", - search: ["https://tg.ex-tic.com/auth/gakunin/saml2/assertions", "Tohoku", "Tohoku Gakuin University", "Tohoku Gakuin University", "東北学院大学"], - SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" - }, - "https://idp.nii.ac.jp/idp/shibboleth": { - type: "kanto", - name: "National Institute of Informatics", - search: ["https://idp.nii.ac.jp/idp/shibboleth", "Kanto", "National Institute of Informatics", "National Institute of Informatics", "国立情報学研究所"], - SAML1SSOurl: "https://idp.nii.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://upki-idp.chiba-u.jp/idp/shibboleth": { - type: "kanto", - name: "Chiba University", - search: ["https://upki-idp.chiba-u.jp/idp/shibboleth", "Kanto", "Chiba University", "Chiba University", "千葉大学"], - SAML1SSOurl: "https://upki-idp.chiba-u.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.account.tsukuba.ac.jp/idp/shibboleth": { - type: "kanto", - name: "University of Tsukuba", - search: ["https://idp.account.tsukuba.ac.jp/idp/shibboleth", "Kanto", "University of Tsukuba", "University of Tsukuba", "筑波大学"], - SAML1SSOurl: "https://idp.account.tsukuba.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://asura.seijo.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Seijo University", - search: ["https://asura.seijo.ac.jp/idp/shibboleth", "Kanto", "Seijo University", "Seijo University", "成城大学"], - SAML1SSOurl: "https://asura.seijo.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://upki.toho-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Toho University", - search: ["https://upki.toho-u.ac.jp/idp/shibboleth", "Kanto", "Toho University", "Toho University", "東邦大学"], - SAML1SSOurl: "https://upki.toho-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shibboleth.nihon-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Nihon University", - search: ["https://shibboleth.nihon-u.ac.jp/idp/shibboleth", "Kanto", "Nihon University", "Nihon University", "日本大学"], - SAML1SSOurl: "https://shibboleth.nihon-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://upki-idp.rikkyo.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Rikkyo University", - search: ["https://upki-idp.rikkyo.ac.jp/idp/shibboleth", "Kanto", "Rikkyo University", "Rikkyo University", "立教大学"], - SAML1SSOurl: "https://upki-idp.rikkyo.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://servs.lib.meiji.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Meiji University", - search: ["https://servs.lib.meiji.ac.jp/idp/shibboleth", "Kanto", "Meiji University", "Meiji University", "明治大学"], - SAML1SSOurl: "https://servs.lib.meiji.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ws1.jichi.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Jichi Medical University(old)", - search: ["https://ws1.jichi.ac.jp/idp/shibboleth", "Kanto", "Jichi Medical University(old)", "Jichi Medical University(old)", "自治医科大学(旧)"], - SAML1SSOurl: "https://ws1.jichi.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin-idp.ynu.ac.jp/": { - type: "kanto", - name: "Yokohama National University", - search: ["https://gakunin-idp.ynu.ac.jp/", "Kanto", "Yokohama National University", "Yokohama National University", "横浜国立大学"], - SAML1SSOurl: "https://gakunin-idp.ynu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://saml-2.tmd.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo Medical and Dental University", - search: ["https://saml-2.tmd.ac.jp/idp/shibboleth", "Kanto", "Tokyo Medical and Dental University", "Tokyo Medical and Dental University", "東京医科歯科大学"], - SAML1SSOurl: "https://saml-2.tmd.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.kosen-k.go.jp/idp/shibboleth": { - type: "kanto", - name: "National Institute of Technology", - search: ["https://kidp.kosen-k.go.jp/idp/shibboleth", "Kanto", "National Institute of Technology", "National Institute of Technology", "国立高等専門学校機構"], - SAML1SSOurl: "https://kidp.kosen-k.go.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.tdc.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo Dental College", - search: ["https://idp.tdc.ac.jp/idp/shibboleth", "Kanto", "Tokyo Dental College", "Tokyo Dental College", "東京歯科大学"], - SAML1SSOurl: "https://idp.tdc.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shib.ap.showa-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Showa University", - search: ["https://shib.ap.showa-u.ac.jp/idp/shibboleth", "Kanto", "Showa University", "Showa University", "昭和大学"], - SAML1SSOurl: "https://shib.ap.showa-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ill.lib.kth.isp.ntt-east.co.jp/idp/shibboleth": { - type: "kanto", - name: "NTT Medical Center Tokyo", - search: ["https://ill.lib.kth.isp.ntt-east.co.jp/idp/shibboleth", "Kanto", "NTT Medical Center Tokyo", "NTT Medical Center Tokyo", "NTT東日本関東病院"], - SAML1SSOurl: "https://ill.lib.kth.isp.ntt-east.co.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp01.ipc.kaiyodai.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo University of Marine Science and Technology", - search: ["https://idp01.ipc.kaiyodai.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of Marine Science and Technology", "Tokyo University of Marine Science and Technology", "東京海洋大学"], - SAML1SSOurl: "https://idp01.ipc.kaiyodai.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.soka.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Soka University", - search: ["https://idp.soka.ac.jp/idp/shibboleth", "Kanto", "Soka University", "Soka University", "創価大学"], - SAML1SSOurl: "https://idp.soka.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.igakuken.or.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo Metropolitan Institute of Medical Science", - search: ["https://idp.igakuken.or.jp/idp/shibboleth", "Kanto", "Tokyo Metropolitan Institute of Medical Science", "Tokyo Metropolitan Institute of Medical Science", "東京都医学総合研究所"], - SAML1SSOurl: "https://idp.igakuken.or.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakuninidp.sic.shibaura-it.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Shibaura Institute of Technology", - search: ["https://gakuninidp.sic.shibaura-it.ac.jp/idp/shibboleth", "Kanto", "Shibaura Institute of Technology", "Shibaura Institute of Technology", "芝浦工業大学"], - SAML1SSOurl: "https://gakuninidp.sic.shibaura-it.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://tgu.u-gakugei.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo Gakugei University", - search: ["https://tgu.u-gakugei.ac.jp/idp/shibboleth", "Kanto", "Tokyo Gakugei University", "Tokyo Gakugei University", "東京学芸大学"], - SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" - }, - "https://idp.musashi.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Musashi Academy", - search: ["https://idp.musashi.ac.jp/idp/shibboleth", "Kanto", "Musashi Academy", "Musashi Academy", "武蔵学園"], - SAML1SSOurl: "https://idp.musashi.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.it-chiba.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Chiba Institute of Technology", - search: ["https://idp.it-chiba.ac.jp/idp/shibboleth", "Kanto", "Chiba Institute of Technology", "Chiba Institute of Technology", "千葉工業大学"], - SAML1SSOurl: "https://idp.it-chiba.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shibboleth.tama.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tama University", - search: ["https://shibboleth.tama.ac.jp/idp/shibboleth", "Kanto", "Tama University", "Tama University", "多摩大学"], - SAML1SSOurl: "https://shibboleth.tama.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://upkishib.cc.ocha.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Ochanomizu University", - search: ["https://upkishib.cc.ocha.ac.jp/idp/shibboleth", "Kanto", "Ochanomizu University", "Ochanomizu University", "お茶の水女子大学"], - SAML1SSOurl: "https://upkishib.cc.ocha.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.tokyo-ct.ac.jp/idp/shibboleth": { - type: "kanto", - name: "National Institute of Technology,Tokyo College", - search: ["https://kidp.tokyo-ct.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Tokyo College", "National Institute of Technology,Tokyo College", "東京工業高等専門学校"], - SAML1SSOurl: "https://kidp.tokyo-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.gunma-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Gunma University", - search: ["https://idp.gunma-u.ac.jp/idp/shibboleth", "Kanto", "Gunma University", "Gunma University", "群馬大学"], - SAML1SSOurl: "https://idp.gunma-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.oyama-ct.ac.jp/idp/shibboleth": { - type: "kanto", - name: "National Institute of Technology,Oyama College", - search: ["https://kidp.oyama-ct.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Oyama College", "National Institute of Technology,Oyama College", "小山工業高等専門学校"], - SAML1SSOurl: "https://kidp.oyama-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin1.keio.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Keio University", - search: ["https://gakunin1.keio.ac.jp/idp/shibboleth", "Kanto", "Keio University", "Keio University", "慶應義塾大学"], - SAML1SSOurl: "https://gakunin1.keio.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.gunma-ct.ac.jp/idp/shibboleth": { - type: "kanto", - name: "National Institute of Technology,Gunma College", - search: ["https://kidp.gunma-ct.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Gunma College", "National Institute of Technology,Gunma College", "群馬工業高等専門学校"], - SAML1SSOurl: "https://kidp.gunma-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shibboleth-idp.dokkyomed.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Dokkyo Medical University", - search: ["https://shibboleth-idp.dokkyomed.ac.jp/idp/shibboleth", "Kanto", "Dokkyo Medical University", "Dokkyo Medical University", "獨協医科大学"], - SAML1SSOurl: "https://shibboleth-idp.dokkyomed.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://iccoam.tufs.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo University of Foreign Studies", - search: ["https://iccoam.tufs.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of Foreign Studies", "Tokyo University of Foreign Studies", "東京外国語大学"], - SAML1SSOurl: "https://iccoam.tufs.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.ibaraki-ct.ac.jp/idp/shibboleth": { - type: "kanto", - name: "National Institute of Technology,Ibaraki College", - search: ["https://kidp.ibaraki-ct.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Ibaraki College", "National Institute of Technology,Ibaraki College", "茨城工業高等専門学校"], - SAML1SSOurl: "https://kidp.ibaraki-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shibboleth.cc.uec.ac.jp/idp/shibboleth": { - type: "kanto", - name: "The University of Electro-Communications", - search: ["https://shibboleth.cc.uec.ac.jp/idp/shibboleth", "Kanto", "The University of Electro-Communications", "The University of Electro-Communications", "電気通信大学"], - SAML1SSOurl: "https://shibboleth.cc.uec.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.sys.affrc.go.jp/idp/shibboleth": { - type: "kanto", - name: "AFFRIT/MAFFIN", - search: ["https://idp.sys.affrc.go.jp/idp/shibboleth", "Kanto", "AFFRIT/MAFFIN", "AFFRIT/MAFFIN", "AFFRIT/MAFFIN"], - SAML1SSOurl: "https://idp.sys.affrc.go.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.kisarazu.ac.jp/idp/shibboleth": { - type: "kanto", - name: "National Institute of Technology,Kisarazu College", - search: ["https://kidp.kisarazu.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Kisarazu College", "National Institute of Technology,Kisarazu College", "木更津工業高等専門学校"], - SAML1SSOurl: "https://kidp.kisarazu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin-idp.c.chuo-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Chuo University", - search: ["https://gakunin-idp.c.chuo-u.ac.jp/idp/shibboleth", "Kanto", "Chuo University", "Chuo University", "中央大学"], - SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" - }, - "https://gidp.adm.u-tokyo.ac.jp/idp/shibboleth": { - type: "kanto", - name: "The University of Tokyo", - search: ["https://gidp.adm.u-tokyo.ac.jp/idp/shibboleth", "Kanto", "The University of Tokyo", "The University of Tokyo", "東京大学"], - SAML1SSOurl: "https://gidp.adm.u-tokyo.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.dendai.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo Denki University", - search: ["https://idp.dendai.ac.jp/idp/shibboleth", "Kanto", "Tokyo Denki University", "Tokyo Denki University", "東京電機大学"], - SAML1SSOurl: "https://idp.dendai.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.cc.seikei.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Seikei University", - search: ["https://idp.cc.seikei.ac.jp/idp/shibboleth", "Kanto", "Seikei University", "Seikei University", "成蹊大学"], - SAML1SSOurl: "https://idp.cc.seikei.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.teikyo-u.ac.jp/AccessManager/shibboleth": { - type: "kanto", - name: "Teikyo University", - search: ["https://idp.teikyo-u.ac.jp/AccessManager/shibboleth", "Kanto", "Teikyo University", "Teikyo University", "帝京大学"], - SAML1SSOurl: "https://idp.teikyo-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.tau.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo Ariake University of Medical and Health Sciences", - search: ["https://idp.tau.ac.jp/idp/shibboleth", "Kanto", "Tokyo Ariake University of Medical and Health Sciences", "Tokyo Ariake University of Medical and Health Sciences", "東京有明医療大学"], - SAML1SSOurl: "https://idp.tau.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.tokyo-kasei.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo Kasei University", - search: ["https://idp.tokyo-kasei.ac.jp/idp/shibboleth", "Kanto", "Tokyo Kasei University", "Tokyo Kasei University", "東京家政大学"], - SAML1SSOurl: "https://idp.tokyo-kasei.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.grips.ac.jp/idp/shibboleth": { - type: "kanto", - name: "National Graduate Institute for Policy Studies", - search: ["https://idp.grips.ac.jp/idp/shibboleth", "Kanto", "National Graduate Institute for Policy Studies", "National Graduate Institute for Policy Studies", "政策研究大学院大学"], - SAML1SSOurl: "https://idp.grips.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakuninshib.tmu.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo Metropolitan University", - search: ["https://gakuninshib.tmu.ac.jp/idp/shibboleth", "Kanto", "Tokyo Metropolitan University", "Tokyo Metropolitan University", "首都大学東京"], - SAML1SSOurl: "https://gakuninshib.tmu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp-gakunin.nap.gsic.titech.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo Institute of Technology", - search: ["https://idp-gakunin.nap.gsic.titech.ac.jp/idp/shibboleth", "Kanto", "Tokyo Institute of Technology", "Tokyo Institute of Technology", "東京工業大学"], - SAML1SSOurl: "https://idp-gakunin.nap.gsic.titech.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.tsurumi-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tsurumi University", - search: ["https://idp.tsurumi-u.ac.jp/idp/shibboleth", "Kanto", "Tsurumi University", "Tsurumi University", "鶴見大学"], - SAML1SSOurl: "https://idp.tsurumi-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://sidp.ibaraki.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Ibaraki University", - search: ["https://sidp.ibaraki.ac.jp/idp/shibboleth", "Kanto", "Ibaraki University", "Ibaraki University", "茨城大学"], - SAML1SSOurl: "https://sidp.ibaraki.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.nims.go.jp/idp/shibboleth": { - type: "kanto", - name: "National Institute for Materials Science", - search: ["https://idp.nims.go.jp/idp/shibboleth", "Kanto", "National Institute for Materials Science", "National Institute for Materials Science", "物質・材料研究機構"], - SAML1SSOurl: "https://idp.nims.go.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.toyaku.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo University of Pharmacy and Life Sciences", - search: ["https://idp.toyaku.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of Pharmacy and Life Sciences", "Tokyo University of Pharmacy and Life Sciences", "東京薬科大学"], - SAML1SSOurl: "https://idp.toyaku.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin2.tuat.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo University of Agriculture and Technology", - search: ["https://gakunin2.tuat.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of Agriculture and Technology", "Tokyo University of Agriculture and Technology", "東京農工大学"], - SAML1SSOurl: "https://gakunin2.tuat.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin-idp.shodai.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Yokohama College of Commerce", - search: ["https://gakunin-idp.shodai.ac.jp/idp/shibboleth", "Kanto", "Yokohama College of Commerce", "Yokohama College of Commerce", "横浜商科大学"], - SAML1SSOurl: "https://gakunin-idp.shodai.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://koma-sso.komazawa-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Komazawa University", - search: ["https://koma-sso.komazawa-u.ac.jp/idp/shibboleth", "Kanto", "Komazawa University", "Komazawa University", "駒澤大学"], - SAML1SSOurl: "https://koma-sso.komazawa-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp3.qst.go.jp/idp/shibboleth": { - type: "kanto", - name: "National Institutes for Quantum and Radiological Science and Technology", - search: ["https://idp3.qst.go.jp/idp/shibboleth", "Kanto", "National Institutes for Quantum and Radiological Science and Technology", "National Institutes for Quantum and Radiological Science and Technology", "量子科学技術研究開発機構"], - SAML1SSOurl: "https://idp3.qst.go.jp/idp/profile/Shibboleth/SSO" - }, - "https://rprx.rku.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Ryutsu Keizai University", - search: ["https://rprx.rku.ac.jp/idp/shibboleth", "Kanto", "Ryutsu Keizai University", "Ryutsu Keizai University", "流通経済大学"], - SAML1SSOurl: "https://rprx.rku.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kanagawa-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Kanagawa University", - search: ["https://idp.kanagawa-u.ac.jp/idp/shibboleth", "Kanto", "Kanagawa University", "Kanagawa University", "神奈川大学"], - SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" - }, - "https://idp.ide.go.jp/idp/shibboleth": { - type: "kanto", - name: "IDE-JETRO", - search: ["https://idp.ide.go.jp/idp/shibboleth", "Kanto", "IDE-JETRO", "IDE-JETRO", "アジア経済研究所"], - SAML1SSOurl: "https://idp.ide.go.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.senshu-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Senshu University", - search: ["https://idp.senshu-u.ac.jp/idp/shibboleth", "Kanto", "Senshu University", "Senshu University", "専修大学"], - SAML1SSOurl: "https://idp.senshu-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.geidai.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Tokyo University of the Arts", - search: ["https://idp.geidai.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of the Arts", "Tokyo University of the Arts", "東京藝術大学"], - SAML1SSOurl: "https://idp.geidai.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.aoyama.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Aoyama Gakuin University", - search: ["https://idp.aoyama.ac.jp/idp/shibboleth", "Kanto", "Aoyama Gakuin University", "Aoyama Gakuin University", "青山学院大学"], - SAML1SSOurl: "https://idp.aoyama.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://sso.internet.ac.jp": { - type: "kanto", - name: "Tokyo Online University", - search: ["https://sso.internet.ac.jp", "Kanto", "Tokyo Online University", "Tokyo Online University", "東京通信大学"], - SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" - }, - "https://idp.itc.saitama-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Saitama University", - search: ["https://idp.itc.saitama-u.ac.jp/idp/shibboleth", "Kanto", "Saitama University", "Saitama University", "埼玉大学"], - SAML1SSOurl: "https://idp.itc.saitama-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://lib.nmct.ntt-east.co.jp/idp/shibboleth": { - type: "kanto", - name: "NTT Medical Center Tokyo Library", - search: ["https://lib.nmct.ntt-east.co.jp/idp/shibboleth", "Kanto", "NTT Medical Center Tokyo Library", "NTT Medical Center Tokyo Library", "NTT東日本関東病院図書館"], - SAML1SSOurl: "https://lib.nmct.ntt-east.co.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.my-pharm.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Meiji Pharmaceutical University", - search: ["https://idp.my-pharm.ac.jp/idp/shibboleth", "Kanto", "Meiji Pharmaceutical University", "Meiji Pharmaceutical University", "明治薬科大学"], - SAML1SSOurl: "https://idp.my-pharm.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin.st.daito.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Daito Bunka University", - search: ["https://gakunin.st.daito.ac.jp/idp/shibboleth", "Kanto", "Daito Bunka University", "Daito Bunka University", "大東文化大学"], - SAML1SSOurl: "https://gakunin.st.daito.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kiryu-u.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Kiryu University", - search: ["https://idp.kiryu-u.ac.jp/idp/shibboleth", "Kanto", "Kiryu University", "Kiryu University", "桐生大学"], - SAML1SSOurl: "https://idp.kiryu-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://slink.secioss.com/icu.ac.jp": { - type: "kanto", - name: "International Christian University", - search: ["https://slink.secioss.com/icu.ac.jp", "Kanto", "International Christian University", "International Christian University", "国際基督教大学"], - SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" - }, - "https://iaidp.ia.waseda.jp/idp/shibboleth": { - type: "kanto", - name: "Waseda University", - search: ["https://iaidp.ia.waseda.jp/idp/shibboleth", "Kanto", "Waseda University", "Waseda University", "早稲田大学"], - SAML1SSOurl: "https://iaidp.ia.waseda.jp/idp/profile/Shibboleth/SSO" - }, - "https://sh-idp.riken.jp/idp/shibboleth": { - type: "kanto", - name: "RIKEN", - search: ["https://sh-idp.riken.jp/idp/shibboleth", "Kanto", "RIKEN", "RIKEN", "理化学研究所"], - SAML1SSOurl: "https://sh-idp.riken.jp/idp/profile/Shibboleth/SSO" - }, - "https://obirin.ex-tic.com/auth/gakunin/saml2/assertions": { - type: "kanto", - name: "J. F. Oberlin University", - search: ["https://obirin.ex-tic.com/auth/gakunin/saml2/assertions", "Kanto", "J. F. Oberlin University", "J. F. Oberlin University", "桜美林大学"], - SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" - }, - "https://idp.jichi.ac.jp/idp/shibboleth": { - type: "kanto", - name: "Jichi Medical University", - search: ["https://idp.jichi.ac.jp/idp/shibboleth", "Kanto", "Jichi Medical University", "Jichi Medical University", "自治医科大学"], - SAML1SSOurl: "https://idp.jichi.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://fed.mie-u.ac.jp/idp": { - type: "chubu", - name: "Mie University", - search: ["https://fed.mie-u.ac.jp/idp", "Chubu", "Mie University", "Mie University", "三重大学"], - SAML1SSOurl: "https://fed.mie-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin.ealps.shinshu-u.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Shinshu University", - search: ["https://gakunin.ealps.shinshu-u.ac.jp/idp/shibboleth", "Chubu", "Shinshu University", "Shinshu University", "信州大学"], - SAML1SSOurl: "https://gakunin.ealps.shinshu-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gknidp.ict.nitech.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Nagoya Institute of Technology", - search: ["https://gknidp.ict.nitech.ac.jp/idp/shibboleth", "Chubu", "Nagoya Institute of Technology", "Nagoya Institute of Technology", "名古屋工業大学"], - SAML1SSOurl: "https://gknidp.ict.nitech.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.yamanashi.ac.jp/idp/shibboleth": { - type: "chubu", - name: "University of Yamanashi", - search: ["https://idp.yamanashi.ac.jp/idp/shibboleth", "Chubu", "University of Yamanashi", "University of Yamanashi", "山梨大学"], - SAML1SSOurl: "https://idp.yamanashi.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.suzuka-ct.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Suzuka College", - search: ["https://idp.suzuka-ct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Suzuka College", "National Institute of Technology,Suzuka College", "鈴鹿工業高等専門学校"], - SAML1SSOurl: "https://idp.suzuka-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.imc.tut.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Toyohashi University of Technology", - search: ["https://idp.imc.tut.ac.jp/idp/shibboleth", "Chubu", "Toyohashi University of Technology", "Toyohashi University of Technology", "豊橋技術科学大学"], - SAML1SSOurl: "https://idp.imc.tut.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.fukui-nct.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Fukui College", - search: ["https://kidp.fukui-nct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Fukui College", "National Institute of Technology,Fukui College", "福井工業高等専門学校"], - SAML1SSOurl: "https://kidp.fukui-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.shizuoka.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Shizuoka University", - search: ["https://idp.shizuoka.ac.jp/idp/shibboleth", "Chubu", "Shizuoka University", "Shizuoka University", "静岡大学"], - SAML1SSOurl: "https://idp.shizuoka.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://wagner.isc.chubu.ac.jp/idp/shibboleth": { - type: "chubu", - name: "CHUBU UNIVERSITY", - search: ["https://wagner.isc.chubu.ac.jp/idp/shibboleth", "Chubu", "CHUBU UNIVERSITY", "CHUBU UNIVERSITY", "中部大学"], - SAML1SSOurl: "https://wagner.isc.chubu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.nagaoka-ct.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Nagaoka College", - search: ["https://kidp.nagaoka-ct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Nagaoka College", "National Institute of Technology,Nagaoka College", "長岡工業高等専門学校"], - SAML1SSOurl: "https://kidp.nagaoka-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.numazu-ct.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Numazu College", - search: ["https://kidp.numazu-ct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Numazu College", "National Institute of Technology,Numazu College", "沼津工業高等専門学校"], - SAML1SSOurl: "https://kidp.numazu-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.nagano-nct.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Nagano College", - search: ["https://kidp.nagano-nct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Nagano College", "National Institute of Technology,Nagano College", "長野工業高等専門学校"], - SAML1SSOurl: "https://kidp.nagano-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.ishikawa-nct.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Ishikawa College", - search: ["https://kidp.ishikawa-nct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Ishikawa College", "National Institute of Technology,Ishikawa College", "石川工業高等専門学校"], - SAML1SSOurl: "https://kidp.ishikawa-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kiidp.nc-toyama.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Toyama College", - search: ["https://kiidp.nc-toyama.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Toyama College", "National Institute of Technology,Toyama College", "富山高等専門学校"], - SAML1SSOurl: "https://kiidp.nc-toyama.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.toba-cmt.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Toba College", - search: ["https://kidp.toba-cmt.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Toba College", "National Institute of Technology,Toba College", "鳥羽商船高等専門学校"], - SAML1SSOurl: "https://kidp.toba-cmt.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.gifu-nct.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Gifu College", - search: ["https://kidp.gifu-nct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Gifu College", "National Institute of Technology,Gifu College", "岐阜工業高等専門学校"], - SAML1SSOurl: "https://kidp.gifu-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://sso.sugiyama-u.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Sugiyama Jogakuen University", - search: ["https://sso.sugiyama-u.ac.jp/idp/shibboleth", "Chubu", "Sugiyama Jogakuen University", "Sugiyama Jogakuen University", "椙山女学園大学"], - SAML1SSOurl: "https://sso.sugiyama-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.toyota-ct.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute of Technology,Toyota College", - search: ["https://kidp.toyota-ct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Toyota College", "National Institute of Technology,Toyota College", "豊田工業高等専門学校"], - SAML1SSOurl: "https://kidp.toyota-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shib.nagoya-u.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Nagoya University", - search: ["https://shib.nagoya-u.ac.jp/idp/shibboleth", "Chubu", "Nagoya University", "Nagoya University", "名古屋大学"], - SAML1SSOurl: "https://shib.nagoya-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://islwpi01.auecc.aichi-edu.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Aichi University of Education", - search: ["https://islwpi01.auecc.aichi-edu.ac.jp/idp/shibboleth", "Chubu", "Aichi University of Education", "Aichi University of Education", "愛知教育大学"], - SAML1SSOurl: "https://islwpi01.auecc.aichi-edu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ams.juen.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Joetsu University of Education", - search: ["https://ams.juen.ac.jp/idp/shibboleth", "Chubu", "Joetsu University of Education", "Joetsu University of Education", "上越教育大学"], - SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" - }, - "https://idp1.b.cii.u-fukui.ac.jp/idp/shibboleth": { - type: "chubu", - name: "University of Fukui", - search: ["https://idp1.b.cii.u-fukui.ac.jp/idp/shibboleth", "Chubu", "University of Fukui", "University of Fukui", "福井大学"], - SAML1SSOurl: "https://idp1.b.cii.u-fukui.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin.gifu-u.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Gifu University", - search: ["https://gakunin.gifu-u.ac.jp/idp/shibboleth", "Chubu", "Gifu University", "Gifu University", "岐阜大学"], - SAML1SSOurl: "https://gakunin.gifu-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.iamas.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Institute of Advanced Media Arts and Sciences", - search: ["https://idp.iamas.ac.jp/idp/shibboleth", "Chubu", "Institute of Advanced Media Arts and Sciences", "Institute of Advanced Media Arts and Sciences", "情報科学芸術大学院大学"], - SAML1SSOurl: "https://idp.iamas.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin.aitech.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Aichi Institute of Technology", - search: ["https://gakunin.aitech.ac.jp/idp/shibboleth", "Chubu", "Aichi Institute of Technology", "Aichi Institute of Technology", "愛知工業大学"], - SAML1SSOurl: "https://gakunin.aitech.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ipcm2.nagaokaut.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Nagaoka University of Technology", - search: ["https://ipcm2.nagaokaut.ac.jp/idp/shibboleth", "Chubu", "Nagaoka University of Technology", "Nagaoka University of Technology", "長岡技術科学大学"], - SAML1SSOurl: "https://ipcm2.nagaokaut.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shibboleth.niigata-cn.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Niigata College of Nursing", - search: ["https://shibboleth.niigata-cn.ac.jp/idp/shibboleth", "Chubu", "Niigata College of Nursing", "Niigata College of Nursing", "新潟県立看護大学"], - SAML1SSOurl: "https://shibboleth.niigata-cn.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.nifs.ac.jp/idp/shibboleth": { - type: "chubu", - name: "National Institute for Fusion Science", - search: ["https://idp.nifs.ac.jp/idp/shibboleth", "Chubu", "National Institute for Fusion Science", "National Institute for Fusion Science", "核融合科学研究所"], - SAML1SSOurl: "https://idp.nifs.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shib.chukyo-u.ac.jp/idp/shibboleth": { - type: "chubu", - name: "CHUKYO UNIVERSITY", - search: ["https://shib.chukyo-u.ac.jp/idp/shibboleth", "Chubu", "CHUKYO UNIVERSITY", "CHUKYO UNIVERSITY", "中京大学"], - SAML1SSOurl: "https://shib.chukyo-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.cais.niigata-u.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Niigata University", - search: ["https://idp.cais.niigata-u.ac.jp/idp/shibboleth", "Chubu", "Niigata University", "Niigata University", "新潟大学"], - SAML1SSOurl: "https://idp.cais.niigata-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.fujita-hu.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Fujita Health University", - search: ["https://idp.fujita-hu.ac.jp/idp/shibboleth", "Chubu", "Fujita Health University", "Fujita Health University", "藤田医科大学"], - SAML1SSOurl: "https://idp.fujita-hu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ku-sso.cis.kanazawa-u.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Kanazawa University", - search: ["https://ku-sso.cis.kanazawa-u.ac.jp/idp/shibboleth", "Chubu", "Kanazawa University", "Kanazawa University", "金沢大学"], - SAML1SSOurl: "https://ku-sso.cis.kanazawa-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.gifu.shotoku.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Gifu Shotoku Gakuen University", - search: ["https://idp.gifu.shotoku.ac.jp/idp/shibboleth", "Chubu", "Gifu Shotoku Gakuen University", "Gifu Shotoku Gakuen University", "岐阜聖徳学園大学"], - SAML1SSOurl: "https://idp.gifu.shotoku.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp02.u-toyama.ac.jp/idp/shibboleth": { - type: "chubu", - name: "University of Toyama", - search: ["https://idp02.u-toyama.ac.jp/idp/shibboleth", "Chubu", "University of Toyama", "University of Toyama", "富山大学"], - SAML1SSOurl: "https://idp02.u-toyama.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin.ims.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Institute for Molecular Science", - search: ["https://gakunin.ims.ac.jp/idp/shibboleth", "Chubu", "Institute for Molecular Science", "Institute for Molecular Science", "分子科学研究所"], - SAML1SSOurl: "https://gakunin.ims.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.tsuru.ac.jp/idp/shibboleth": { - type: "chubu", - name: "Tsuru University", - search: ["https://idp.tsuru.ac.jp/idp/shibboleth", "Chubu", "Tsuru University", "Tsuru University", "都留文科大学"], - SAML1SSOurl: "https://idp.tsuru.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://authidp1.iimc.kyoto-u.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kyoto University", - search: ["https://authidp1.iimc.kyoto-u.ac.jp/idp/shibboleth", "Kinki", "Kyoto University", "Kyoto University", "京都大学"], - SAML1SSOurl: "https://authidp1.iimc.kyoto-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin.kyoto-su.ac.jp/idp": { - type: "kinki", - name: "Kyoto Sangyo University", - search: ["https://gakunin.kyoto-su.ac.jp/idp", "Kinki", "Kyoto Sangyo University", "Kyoto Sangyo University", "京都産業大学"], - SAML1SSOurl: "https://gakunin.kyoto-su.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://fed.center.kobe-u.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kobe University", - search: ["https://fed.center.kobe-u.ac.jp/idp/shibboleth", "Kinki", "Kobe University", "Kobe University", "神戸大学"], - SAML1SSOurl: "https://fed.center.kobe-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.naist.jp/idp/shibboleth": { - type: "kinki", - name: "Nara Institute of Science and Technology", - search: ["https://idp.naist.jp/idp/shibboleth", "Kinki", "Nara Institute of Science and Technology", "Nara Institute of Science and Technology", "奈良先端科学技術大学院大学"], - SAML1SSOurl: "https://idp.naist.jp/idp/profile/Shibboleth/SSO" - }, - "https://shib-idp.nara-edu.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Nara University of Education", - search: ["https://shib-idp.nara-edu.ac.jp/idp/shibboleth", "Kinki", "Nara University of Education", "Nara University of Education", "奈良教育大学"], - SAML1SSOurl: "https://shib-idp.nara-edu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.ritsumei.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Ritsumeikan University", - search: ["https://idp.ritsumei.ac.jp/idp/shibboleth", "Kinki", "Ritsumeikan University", "Ritsumeikan University", "立命館大学"], - SAML1SSOurl: "https://idp.ritsumei.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp1.itc.kansai-u.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kansai University", - search: ["https://idp1.itc.kansai-u.ac.jp/idp/shibboleth", "Kinki", "Kansai University", "Kansai University", "関西大学"], - SAML1SSOurl: "https://idp1.itc.kansai-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shib.osaka-kyoiku.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Osaka Kyoiku University", - search: ["https://shib.osaka-kyoiku.ac.jp/idp/shibboleth", "Kinki", "Osaka Kyoiku University", "Osaka Kyoiku University", "大阪教育大学"], - SAML1SSOurl: "https://shib.osaka-kyoiku.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp1.kyokyo-u.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kyoto University of Education", - search: ["https://idp1.kyokyo-u.ac.jp/idp/shibboleth", "Kinki", "Kyoto University of Education", "Kyoto University of Education", "京都教育大学"], - SAML1SSOurl: "https://idp1.kyokyo-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://authsv.kpu.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kyoto Prefectural University", - search: ["https://authsv.kpu.ac.jp/idp/shibboleth", "Kinki", "Kyoto Prefectural University", "Kyoto Prefectural University", "京都府立大学"], - SAML1SSOurl: "https://authsv.kpu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.tezukayama-u.ac.jp/idp/shibboleth": { - type: "kinki", - name: "TEZUKAYAMA UNIVERSITY", - search: ["https://idp.tezukayama-u.ac.jp/idp/shibboleth", "Kinki", "TEZUKAYAMA UNIVERSITY", "TEZUKAYAMA UNIVERSITY", "帝塚山大学"], - SAML1SSOurl: "https://idp.tezukayama-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.tieskun.net/idp/shibboleth": { - type: "kinki", - name: "CCC-TIES", - search: ["https://idp.tieskun.net/idp/shibboleth", "Kinki", "CCC-TIES", "CCC-TIES", "CCC-TIES"], - SAML1SSOurl: "https://idp.tieskun.net/idp/profile/Shibboleth/SSO" - }, - "https://idp.ouhs.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Osaka University of Health and Sport Sciences", - search: ["https://idp.ouhs.ac.jp/idp/shibboleth", "Kinki", "Osaka University of Health and Sport Sciences", "Osaka University of Health and Sport Sciences", "大阪体育大学"], - SAML1SSOurl: "https://idp.ouhs.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.maizuru-ct.ac.jp/idp/shibboleth": { - type: "kinki", - name: "National Institute of Technology,Maizuru College", - search: ["https://kidp.maizuru-ct.ac.jp/idp/shibboleth", "Kinki", "National Institute of Technology,Maizuru College", "National Institute of Technology,Maizuru College", "舞鶴工業高等専門学校"], - SAML1SSOurl: "https://kidp.maizuru-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.wakayama-nct.ac.jp/idp/shibboleth": { - type: "kinki", - name: "National Institute of Technology,Wakayama College", - search: ["https://kidp.wakayama-nct.ac.jp/idp/shibboleth", "Kinki", "National Institute of Technology,Wakayama College", "National Institute of Technology,Wakayama College", "和歌山工業高等専門学校"], - SAML1SSOurl: "https://kidp.wakayama-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.akashi.ac.jp/idp/shibboleth": { - type: "kinki", - name: "National Institute of Technology,Akashi College", - search: ["https://kidp.akashi.ac.jp/idp/shibboleth", "Kinki", "National Institute of Technology,Akashi College", "National Institute of Technology,Akashi College", "明石工業高等専門学校"], - SAML1SSOurl: "https://kidp.akashi.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://g-shib.auth.oit.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Osaka Institute of Technology", - search: ["https://g-shib.auth.oit.ac.jp/idp/shibboleth", "Kinki", "Osaka Institute of Technology", "Osaka Institute of Technology", "大阪工業大学"], - SAML1SSOurl: "https://g-shib.auth.oit.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.nara-k.ac.jp/idp/shibboleth": { - type: "kinki", - name: "National Institute of Technology,Nara College", - search: ["https://kidp.nara-k.ac.jp/idp/shibboleth", "Kinki", "National Institute of Technology,Nara College", "National Institute of Technology,Nara College", "奈良工業高等専門学校"], - SAML1SSOurl: "https://kidp.nara-k.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kobe-cufs.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kobe City University of Foreign Studies", - search: ["https://idp.kobe-cufs.ac.jp/idp/shibboleth", "Kinki", "Kobe City University of Foreign Studies", "Kobe City University of Foreign Studies", "神戸市外国語大学"], - SAML1SSOurl: "https://idp.kobe-cufs.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://sumsidp.shiga-med.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Shiga University of Medical Science", - search: ["https://sumsidp.shiga-med.ac.jp/idp/shibboleth", "Kinki", "Shiga University of Medical Science", "Shiga University of Medical Science", "滋賀医科大学"], - SAML1SSOurl: "https://sumsidp.shiga-med.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kobe-tokiwa.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kobe Tokiwa University", - search: ["https://idp.kobe-tokiwa.ac.jp/idp/shibboleth", "Kinki", "Kobe Tokiwa University", "Kobe Tokiwa University", "神戸常盤大学"], - SAML1SSOurl: "https://idp.kobe-tokiwa.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gakunin.osaka-cu.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Osaka City University", - search: ["https://gakunin.osaka-cu.ac.jp/idp/shibboleth", "Kinki", "Osaka City University", "Osaka City University", "大阪市立大学"], - SAML1SSOurl: "https://gakunin.osaka-cu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.doshisha.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Doshisha University", - search: ["https://idp.doshisha.ac.jp/idp/shibboleth", "Kinki", "Doshisha University", "Doshisha University", "同志社大学"], - SAML1SSOurl: "https://idp.doshisha.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kpu-m.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kyoto Prefectural University of Medicine", - search: ["https://idp.kpu-m.ac.jp/idp/shibboleth", "Kinki", "Kyoto Prefectural University of Medicine", "Kyoto Prefectural University of Medicine", "京都府立医科大学"], - SAML1SSOurl: "https://idp.kpu-m.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.otani.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Otani University", - search: ["https://idp.otani.ac.jp/idp/shibboleth", "Kinki", "Otani University", "Otani University", "大谷大学"], - SAML1SSOurl: "https://idp.otani.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gidp.ryukoku.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Ryukoku University", - search: ["https://gidp.ryukoku.ac.jp/idp/shibboleth", "Kinki", "Ryukoku University", "Ryukoku University", "龍谷大学"], - SAML1SSOurl: "https://gidp.ryukoku.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://naraidp.cc.nara-wu.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Nara Women\'s University", - search: ["https://naraidp.cc.nara-wu.ac.jp/idp/shibboleth", "Kinki", "Nara Women\'s University", "Nara Women\'s University", "奈良女子大学"], - SAML1SSOurl: "https://naraidp.cc.nara-wu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gk-idp.auth.osaka-u.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Osaka University", - search: ["https://gk-idp.auth.osaka-u.ac.jp/idp/shibboleth", "Kinki", "Osaka University", "Osaka University", "大阪大学"], - SAML1SSOurl: "https://gk-idp.auth.osaka-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://heimdall.osaka-aoyama.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Osaka Aoyama University", - search: ["https://heimdall.osaka-aoyama.ac.jp/idp/shibboleth", "Kinki", "Osaka Aoyama University", "Osaka Aoyama University", "大阪青山大学"], - SAML1SSOurl: "https://heimdall.osaka-aoyama.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kindai.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kindai University", - search: ["https://idp.kindai.ac.jp/idp/shibboleth", "Kinki", "Kindai University", "Kindai University", "近畿大学"], - SAML1SSOurl: "https://idp.kindai.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.cis.kit.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kyoto Institute of Technology", - search: ["https://idp.cis.kit.ac.jp/idp/shibboleth", "Kinki", "Kyoto Institute of Technology", "Kyoto Institute of Technology", "京都工芸繊維大学"], - SAML1SSOurl: "https://idp.cis.kit.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.andrew.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Momoyama Gakuin University", - search: ["https://idp.andrew.ac.jp/idp/shibboleth", "Kinki", "Momoyama Gakuin University", "Momoyama Gakuin University", "桃山学院大学"], - SAML1SSOurl: "https://idp.andrew.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kobe-ccn.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kobe City College of Nursing", - search: ["https://idp.kobe-ccn.ac.jp/idp/shibboleth", "Kinki", "Kobe City College of Nursing", "Kobe City College of Nursing", "神戸市看護大学"], - SAML1SSOurl: "https://idp.kobe-ccn.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.u-hyogo.ac.jp/idp/shibboleth": { - type: "kinki", - name: "University of Hyogo", - search: ["https://idp.u-hyogo.ac.jp/idp/shibboleth", "Kinki", "University of Hyogo", "University of Hyogo", "兵庫県立大学"], - SAML1SSOurl: "https://idp.u-hyogo.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.wakayama-u.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Wakayama University", - search: ["https://idp.wakayama-u.ac.jp/idp/shibboleth", "Kinki", "Wakayama University", "Wakayama University", "和歌山大学"], - SAML1SSOurl: "https://idp.wakayama-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.21c.osakafu-u.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Osaka Prefecture University", - search: ["https://idp.21c.osakafu-u.ac.jp/idp/shibboleth", "Kinki", "Osaka Prefecture University", "Osaka Prefecture University", "大阪府立大学"], - SAML1SSOurl: "https://idp.21c.osakafu-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://dc-shibsv.shiga-u.ac.jp/idp/shibboleth/": { - type: "kinki", - name: "SHIGA UNIVERSITY", - search: ["https://dc-shibsv.shiga-u.ac.jp/idp/shibboleth/", "Kinki", "SHIGA UNIVERSITY", "SHIGA UNIVERSITY", "滋賀大学"], - SAML1SSOurl: "https://dc-shibsv.shiga-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gkn.kbu.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Kyoto Bunkyo University", - search: ["https://gkn.kbu.ac.jp/idp/shibboleth", "Kinki", "Kyoto Bunkyo University", "Kyoto Bunkyo University", "京都文教大学"], - SAML1SSOurl: "https://gkn.kbu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.osaka-ue.ac.jp/idp/shibboleth": { - type: "kinki", - name: "Osaka University of Economics", - search: ["https://idp.osaka-ue.ac.jp/idp/shibboleth", "Kinki", "Osaka University of Economics", "Osaka University of Economics", "大阪経済大学"], - SAML1SSOurl: "https://idp.osaka-ue.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.hiroshima-u.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Hiroshima University", - search: ["https://idp.hiroshima-u.ac.jp/idp/shibboleth", "Chugoku", "Hiroshima University", "Hiroshima University", "広島大学"], - SAML1SSOurl: "https://idp.hiroshima-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://odidp.cc.okayama-u.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Okayama University", - search: ["https://odidp.cc.okayama-u.ac.jp/idp/shibboleth", "Chugoku", "Okayama University", "Okayama University", "岡山大学"], - SAML1SSOurl: "https://odidp.cc.okayama-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://fed.ipc.hiroshima-cu.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Hiroshima City University", - search: ["https://fed.ipc.hiroshima-cu.ac.jp/idp/shibboleth", "Chugoku", "Hiroshima City University", "Hiroshima City University", "広島市立大学"], - SAML1SSOurl: "https://fed.ipc.hiroshima-cu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.it-hiroshima.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Hiroshima Institute of Technology", - search: ["https://idp.it-hiroshima.ac.jp/idp/shibboleth", "Chugoku", "Hiroshima Institute of Technology", "Hiroshima Institute of Technology", "広島工業大学"], - SAML1SSOurl: "https://idp.it-hiroshima.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.shudo-u.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Hiroshima Shudo University", - search: ["https://idp.shudo-u.ac.jp/idp/shibboleth", "Chugoku", "Hiroshima Shudo University", "Hiroshima Shudo University", "広島修道大学"], - SAML1SSOurl: "https://idp.shudo-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.oshima-k.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "National Institute of Technology,Oshima College", - search: ["https://kidp.oshima-k.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Oshima College", "National Institute of Technology,Oshima College", "大島商船高等専門学校"], - SAML1SSOurl: "https://kidp.oshima-k.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.kure-nct.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "National Institute of Technology,Kure College", - search: ["https://kidp.kure-nct.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Kure College", "National Institute of Technology,Kure College", "呉工業高等専門学校"], - SAML1SSOurl: "https://kidp.kure-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.hiroshima-cmt.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "National Institute of Technology,Hiroshima College", - search: ["https://kidp.hiroshima-cmt.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Hiroshima College", "National Institute of Technology,Hiroshima College", "広島商船高等専門学校"], - SAML1SSOurl: "https://kidp.hiroshima-cmt.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.yonago-k.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "National Institute of Technology,Yonago College", - search: ["https://kidp.yonago-k.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Yonago College", "National Institute of Technology,Yonago College", "米子工業高等専門学校"], - SAML1SSOurl: "https://kidp.yonago-k.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.tsuyama-ct.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "National Institute of Technology,Tsuyama College", - search: ["https://kidp.tsuyama-ct.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Tsuyama College", "National Institute of Technology,Tsuyama College", "津山工業高等専門学校"], - SAML1SSOurl: "https://kidp.tsuyama-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.ube-k.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "National Institute of Technology,Ube College", - search: ["https://kidp.ube-k.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Ube College", "National Institute of Technology,Ube College", "宇部工業高等専門学校"], - SAML1SSOurl: "https://kidp.ube-k.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.tokuyama.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "National Institute of Technology,Tokuyama College", - search: ["https://kidp.tokuyama.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Tokuyama College", "National Institute of Technology,Tokuyama College", "徳山工業高等専門学校"], - SAML1SSOurl: "https://kidp.tokuyama.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.matsue-ct.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "National Institute of Technology,Matsue College", - search: ["https://idp.matsue-ct.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Matsue College", "National Institute of Technology,Matsue College", "松江工業高等専門学校"], - SAML1SSOurl: "https://idp.matsue-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.tottori-u.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Tottori University", - search: ["https://idp.tottori-u.ac.jp/idp/shibboleth", "Chugoku", "Tottori University", "Tottori University", "鳥取大学"], - SAML1SSOurl: "https://idp.tottori-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.shimane-u.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Shimane University", - search: ["https://idp.shimane-u.ac.jp/idp/shibboleth", "Chugoku", "Shimane University", "Shimane University", "島根大学"], - SAML1SSOurl: "https://idp.shimane-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.oka-pu.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Okayama Prefectural University", - search: ["https://idp.oka-pu.ac.jp/idp/shibboleth", "Chugoku", "Okayama Prefectural University", "Okayama Prefectural University", "岡山県立大学"], - SAML1SSOurl: "https://idp.oka-pu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.pu-hiroshima.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Prefectural University of Hiroshima", - search: ["https://idp.pu-hiroshima.ac.jp/idp/shibboleth", "Chugoku", "Prefectural University of Hiroshima", "Prefectural University of Hiroshima", "県立広島大学"], - SAML1SSOurl: "https://idp.pu-hiroshima.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://auth.socu.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Sanyo-Onoda City University", - search: ["https://auth.socu.ac.jp/idp/shibboleth", "Chugoku", "Sanyo-Onoda City University", "Sanyo-Onoda City University", "山陽小野田市立山口東京理科大学"], - SAML1SSOurl: "https://auth.socu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.cc.yamaguchi-u.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Yamaguchi University", - search: ["https://idp.cc.yamaguchi-u.ac.jp/idp/shibboleth", "Chugoku", "Yamaguchi University", "Yamaguchi University", "山口大学"], - SAML1SSOurl: "https://idp.cc.yamaguchi-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.pub.ous.ac.jp/idp/shibboleth": { - type: "chugoku", - name: "Okayama University of Science", - search: ["https://idp.pub.ous.ac.jp/idp/shibboleth", "Chugoku", "Okayama University of Science", "Okayama University of Science", "岡山理科大学"], - SAML1SSOurl: "https://idp.pub.ous.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.cc.ehime-u.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "Ehime University", - search: ["https://idp.cc.ehime-u.ac.jp/idp/shibboleth", "Shikoku", "Ehime University", "Ehime University", "愛媛大学"], - SAML1SSOurl: "https://idp.cc.ehime-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://gidp.ait230.tokushima-u.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "Tokushima University", - search: ["https://gidp.ait230.tokushima-u.ac.jp/idp/shibboleth", "Shikoku", "Tokushima University", "Tokushima University", "徳島大学"], - SAML1SSOurl: "https://gidp.ait230.tokushima-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.kochi-ct.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "National Institute of Technology,Kochi College", - search: ["https://kidp.kochi-ct.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Kochi College", "National Institute of Technology,Kochi College", "高知工業高等専門学校"], - SAML1SSOurl: "https://kidp.kochi-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ktidp.kagawa-nct.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "National Institute of Technology,Kagawa College", - search: ["https://ktidp.kagawa-nct.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Kagawa College", "National Institute of Technology,Kagawa College", "香川高等専門学校"], - SAML1SSOurl: "https://ktidp.kagawa-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.yuge.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "National Institute of Technology,Yuge College", - search: ["https://kidp.yuge.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Yuge College", "National Institute of Technology,Yuge College", "弓削商船高等専門学校"], - SAML1SSOurl: "https://kidp.yuge.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.niihama-nct.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "National Institute of Technology,Niihama College", - search: ["https://kidp.niihama-nct.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Niihama College", "National Institute of Technology,Niihama College", "新居浜工業高等専門学校"], - SAML1SSOurl: "https://kidp.niihama-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.anan-nct.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "National Institute of Technology,Anan College", - search: ["https://kidp.anan-nct.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Anan College", "National Institute of Technology,Anan College", "阿南工業高等専門学校"], - SAML1SSOurl: "https://kidp.anan-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kochi-tech.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "Kochi University of Technology", - search: ["https://idp.kochi-tech.ac.jp/idp/shibboleth", "Shikoku", "Kochi University of Technology", "Kochi University of Technology", "高知工科大学"], - SAML1SSOurl: "https://idp.kochi-tech.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://aries.naruto-u.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "Naruto University of Education", - search: ["https://aries.naruto-u.ac.jp/idp/shibboleth", "Shikoku", "Naruto University of Education", "Naruto University of Education", "鳴門教育大学"], - SAML1SSOurl: "https://aries.naruto-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp1.matsuyama-u.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "Matsuyama University", - search: ["https://idp1.matsuyama-u.ac.jp/idp/shibboleth", "Shikoku", "Matsuyama University", "Matsuyama University", "松山大学"], - SAML1SSOurl: "https://idp1.matsuyama-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kochi-u.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "Kochi University", - search: ["https://idp.kochi-u.ac.jp/idp/shibboleth", "Shikoku", "Kochi University", "Kochi University", "高知大学"], - SAML1SSOurl: "https://idp.kochi-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.itc.kagawa-u.ac.jp/idp/shibboleth": { - type: "shikoku", - name: "Kagawa University", - search: ["https://idp.itc.kagawa-u.ac.jp/idp/shibboleth", "Shikoku", "Kagawa University", "Kagawa University", "香川大学"], - SAML1SSOurl: "https://idp.itc.kagawa-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ssoidp.cc.saga-u.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Saga University", - search: ["https://ssoidp.cc.saga-u.ac.jp/idp/shibboleth", "Kyushu", "Saga University", "Saga University", "佐賀大学"], - SAML1SSOurl: "https://ssoidp.cc.saga-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.isc.kyutech.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Kyushu Institute of Technology", - search: ["https://idp.isc.kyutech.ac.jp/idp/shibboleth", "Kyushu", "Kyushu Institute of Technology", "Kyushu Institute of Technology", "九州工業大学"], - SAML1SSOurl: "https://idp.isc.kyutech.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kyushu-u.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Kyushu University", - search: ["https://idp.kyushu-u.ac.jp/idp/shibboleth", "Kyushu", "Kyushu University", "Kyushu University", "九州大学"], - SAML1SSOurl: "https://idp.kyushu-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://um-idp.cc.miyazaki-u.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "University of Miyazaki", - search: ["https://um-idp.cc.miyazaki-u.ac.jp/idp/shibboleth", "Kyushu", "University of Miyazaki", "University of Miyazaki", "宮崎大学"], - SAML1SSOurl: "https://um-idp.cc.miyazaki-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://fed.u-ryukyu.ac.jp/shibboleth": { - type: "kyushu", - name: "University of the Ryukyus", - search: ["https://fed.u-ryukyu.ac.jp/shibboleth", "Kyushu", "University of the Ryukyus", "University of the Ryukyus", "琉球大学"], - SAML1SSOurl: "https://fed.u-ryukyu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.kct.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Technology,Kitakyushu College", - search: ["https://kidp.kct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Kitakyushu College", "National Institute of Technology,Kitakyushu College", "北九州工業高等専門学校"], - SAML1SSOurl: "https://kidp.kct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shibboleth-idp.bene.fit.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Fukuoka Institute of Technology", - search: ["https://shibboleth-idp.bene.fit.ac.jp/idp/shibboleth", "Kyushu", "Fukuoka Institute of Technology", "Fukuoka Institute of Technology", "福岡工業大学"], - SAML1SSOurl: "https://shibboleth-idp.bene.fit.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shib.kumamoto-u.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Kumamoto University", - search: ["https://shib.kumamoto-u.ac.jp/idp/shibboleth", "Kyushu", "Kumamoto University", "Kumamoto University", "熊本大学"], - SAML1SSOurl: "https://shib.kumamoto-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.oita-ct.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Technology,Oita College", - search: ["https://kidp.oita-ct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Oita College", "National Institute of Technology,Oita College", "大分工業高等専門学校"], - SAML1SSOurl: "https://kidp.oita-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.sasebo.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Technology,Sasebo College", - search: ["https://kidp.sasebo.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Sasebo College", "National Institute of Technology,Sasebo College", "佐世保工業高等専門学校"], - SAML1SSOurl: "https://kidp.sasebo.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.kagoshima-ct.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Technology,Kagoshima College", - search: ["https://kidp.kagoshima-ct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Kagoshima College", "National Institute of Technology,Kagoshima College", "鹿児島工業高等専門学校"], - SAML1SSOurl: "https://kidp.kagoshima-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.kurume-nct.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Technology,Kurume College", - search: ["https://kidp.kurume-nct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Kurume College", "National Institute of Technology,Kurume College", "久留米工業高等専門学校"], - SAML1SSOurl: "https://kidp.kurume-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.miyakonojo-nct.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Technology,Miyakonojo College", - search: ["https://kidp.miyakonojo-nct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Miyakonojo College", "National Institute of Technology,Miyakonojo College", "都城工業高等専門学校"], - SAML1SSOurl: "https://kidp.miyakonojo-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.ariake-nct.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Technology,Ariake College", - search: ["https://kidp.ariake-nct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Ariake College", "National Institute of Technology,Ariake College", "有明工業高等専門学校"], - SAML1SSOurl: "https://kidp.ariake-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.kumamoto-nct.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Technology,Kumamoto College", - search: ["https://kidp.kumamoto-nct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Kumamoto College", "National Institute of Technology,Kumamoto College", "熊本高等専門学校"], - SAML1SSOurl: "https://kidp.kumamoto-nct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.okinawa-ct.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Technology,Okinawa College", - search: ["https://kidp.okinawa-ct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Okinawa College", "National Institute of Technology,Okinawa College", "沖縄工業高等専門学校"], - SAML1SSOurl: "https://kidp.okinawa-ct.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://shibboleth-idp.kyusan-u.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Kyushu Sangyo University", - search: ["https://shibboleth-idp.kyusan-u.ac.jp/idp/shibboleth", "Kyushu", "Kyushu Sangyo University", "Kyushu Sangyo University", "九州産業大学"], - SAML1SSOurl: "https://shibboleth-idp.kyusan-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://logon.oist.jp/idp/shibboleth": { - type: "kyushu", - name: "Okinawa Institute of Science and Technology Graduate University", - search: ["https://logon.oist.jp/idp/shibboleth", "Kyushu", "Okinawa Institute of Science and Technology Graduate University", "Okinawa Institute of Science and Technology Graduate University", "沖縄科学技術大学院大学"], - SAML1SSOurl: "https://logon.oist.jp/idp/profile/Shibboleth/SSO" - }, - "https://nuidp.nagasaki-u.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Nagasaki University", - search: ["https://nuidp.nagasaki-u.ac.jp/idp/shibboleth", "Kyushu", "Nagasaki University", "Nagasaki University", "長崎大学"], - SAML1SSOurl: "https://nuidp.nagasaki-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.seinan-gu.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Seinan Gakuin University", - search: ["https://idp.seinan-gu.ac.jp/idp/shibboleth", "Kyushu", "Seinan Gakuin University", "Seinan Gakuin University", "西南学院大学"], - SAML1SSOurl: "https://idp.seinan-gu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.net.oita-u.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Oita University", - search: ["https://idp.net.oita-u.ac.jp/idp/shibboleth", "Kyushu", "Oita University", "Oita University", "大分大学"], - SAML1SSOurl: "https://idp.net.oita-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://kidp.cc.kagoshima-u.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Kagoshima University", - search: ["https://kidp.cc.kagoshima-u.ac.jp/idp/shibboleth", "Kyushu", "Kagoshima University", "Kagoshima University", "鹿児島大学"], - SAML1SSOurl: "https://kidp.cc.kagoshima-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.nifs-k.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "National Institute of Fitness and Sports in KANOYA", - search: ["https://idp.nifs-k.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Fitness and Sports in KANOYA", "National Institute of Fitness and Sports in KANOYA", "鹿屋体育大学"], - SAML1SSOurl: "https://idp.nifs-k.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://ss.fukuoka-edu.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "University of Teacher Education Fukuoka", - search: ["https://ss.fukuoka-edu.ac.jp/idp/shibboleth", "Kyushu", "University of Teacher Education Fukuoka", "University of Teacher Education Fukuoka", "福岡教育大学"], - SAML1SSOurl: "https://ss.fukuoka-edu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.sun.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "University of Nagasaki", - search: ["https://idp.sun.ac.jp/idp/shibboleth", "Kyushu", "University of Nagasaki", "University of Nagasaki", "長崎県立大学"], - SAML1SSOurl: "https://idp.sun.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.okiu.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Okinawa International University", - search: ["https://idp.okiu.ac.jp/idp/shibboleth", "Kyushu", "Okinawa International University", "Okinawa International University", "沖縄国際大学"], - SAML1SSOurl: "https://idp.okiu.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.sojo-u.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "SOJO University", - search: ["https://idp.sojo-u.ac.jp/idp/shibboleth", "Kyushu", "SOJO University", "SOJO University", "崇城大学"], - SAML1SSOurl: "https://idp.sojo-u.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.kurume-it.ac.jp/idp/shibboleth": { - type: "kyushu", - name: "Kurume Institute of Technology", - search: ["https://idp.kurume-it.ac.jp/idp/shibboleth", "Kyushu", "Kurume Institute of Technology", "Kurume Institute of Technology", "久留米工業大学"], - SAML1SSOurl: "https://idp.kurume-it.ac.jp/idp/profile/Shibboleth/SSO" - }, - "https://idp.gakunin.nii.ac.jp/idp/shibboleth": { - type: "others", - name: "GakuNin IdP", - search: ["https://idp.gakunin.nii.ac.jp/idp/shibboleth", "Others", "GakuNin IdP", "GakuNin IdP", "学認IdP"], - SAML1SSOurl: "https://idp.gakunin.nii.ac.jp/idp/profile/Shibboleth/SSO" - } + "https://shib-idp01.iic.hokudai.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Hokkaido University", + search: ["https://shib-idp01.iic.hokudai.ac.jp/idp/shibboleth", "Hokkaido", "Hokkaido University", "Hokkaido University", "北海道大学"], + SAML1SSOurl: "https://shib-idp01.iic.hokudai.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.asahikawa-med.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Asahikawa Medical University", + search: ["https://idp.asahikawa-med.ac.jp/idp/shibboleth", "Hokkaido", "Asahikawa Medical University", "Asahikawa Medical University", "旭川医科大学"], + SAML1SSOurl: "https://idp.asahikawa-med.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.msls.kushiro-ct.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "National Institute of Technology,Kushiro College", + search: ["https://idp.msls.kushiro-ct.ac.jp/idp/shibboleth", "Hokkaido", "National Institute of Technology,Kushiro College", "National Institute of Technology,Kushiro College", "釧路工業高等専門学校"], + SAML1SSOurl: "https://idp.msls.kushiro-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shibboleth.lib.kitami-it.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Kitami Institute of Technology", + search: ["https://shibboleth.lib.kitami-it.ac.jp/idp/shibboleth", "Hokkaido", "Kitami Institute of Technology", "Kitami Institute of Technology", "北見工業大学"], + SAML1SSOurl: "https://shibboleth.lib.kitami-it.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://sso.sapmed.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Sapporo Medical University", + search: ["https://sso.sapmed.ac.jp/idp/shibboleth", "Hokkaido", "Sapporo Medical University", "Sapporo Medical University", "札幌医科大学"], + SAML1SSOurl: "https://sso.sapmed.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.tomakomai-ct.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "National Institute of Technology,Tomakomai College", + search: ["https://kidp.tomakomai-ct.ac.jp/idp/shibboleth", "Hokkaido", "National Institute of Technology,Tomakomai College", "National Institute of Technology,Tomakomai College", "苫小牧工業高等専門学校"], + SAML1SSOurl: "https://kidp.tomakomai-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.hakodate-ct.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "National Institute of Technology,Hakodate College", + search: ["https://kidp.hakodate-ct.ac.jp/idp/shibboleth", "Hokkaido", "National Institute of Technology,Hakodate College", "National Institute of Technology,Hakodate College", "函館工業高等専門学校"], + SAML1SSOurl: "https://kidp.hakodate-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.asahikawa-nct.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "National Institute of Technology,Asahikawa College", + search: ["https://kidp.asahikawa-nct.ac.jp/idp/shibboleth", "Hokkaido", "National Institute of Technology,Asahikawa College", "National Institute of Technology,Asahikawa College", "旭川工業高等専門学校"], + SAML1SSOurl: "https://kidp.asahikawa-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.sgu.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Sapporo Gakuin University", + search: ["https://idp.sgu.ac.jp/idp/shibboleth", "Hokkaido", "Sapporo Gakuin University", "Sapporo Gakuin University", "札幌学院大学"], + SAML1SSOurl: "https://idp.sgu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin-idp01.mmm.muroran-it.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Muroran Institute of Technology", + search: ["https://gakunin-idp01.mmm.muroran-it.ac.jp/idp/shibboleth", "Hokkaido", "Muroran Institute of Technology", "Muroran Institute of Technology", "室蘭工業大学"], + SAML1SSOurl: "https://gakunin-idp01.mmm.muroran-it.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.fun.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Future University Hakodate", + search: ["https://idp.fun.ac.jp/idp/shibboleth", "Hokkaido", "Future University Hakodate", "Future University Hakodate", "公立はこだて未来大学"], + SAML1SSOurl: "https://idp.fun.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.hokkyodai.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Hokkaido University of Education", + search: ["https://idp.hokkyodai.ac.jp/idp/shibboleth", "Hokkaido", "Hokkaido University of Education", "Hokkaido University of Education", "北海道教育大学"], + SAML1SSOurl: "https://idp.hokkyodai.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://sib-idp.obihiro.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Obihiro University of Agriculture and Veterinary Medicine", + search: ["https://sib-idp.obihiro.ac.jp/idp/shibboleth", "Hokkaido", "Obihiro University of Agriculture and Veterinary Medicine", "Obihiro University of Agriculture and Veterinary Medicine", "帯広畜産大学"], + SAML1SSOurl: "https://sib-idp.obihiro.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.scu.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Sapporo City University", + search: ["https://idp.scu.ac.jp/idp/shibboleth", "Hokkaido", "Sapporo City University", "Sapporo City University", "札幌市立大学"], + SAML1SSOurl: "https://idp.scu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://rg-lshib01.rakuno.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "RAKUNO GAKUEN UNIVERSITY", + search: ["https://rg-lshib01.rakuno.ac.jp/idp/shibboleth", "Hokkaido", "RAKUNO GAKUEN UNIVERSITY", "RAKUNO GAKUEN UNIVERSITY", "酪農学園大学"], + SAML1SSOurl: "https://rg-lshib01.rakuno.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ictc-idp01.otaru-uc.ac.jp/idp/shibboleth": { + type: "hokkaido", + name: "Otaru University of Commerce", + search: ["https://ictc-idp01.otaru-uc.ac.jp/idp/shibboleth", "Hokkaido", "Otaru University of Commerce", "Otaru University of Commerce", "⼩樽商科⼤学"], + SAML1SSOurl: "https://ictc-idp01.otaru-uc.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://upki.yamagata-u.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "Yamagata University", + search: ["https://upki.yamagata-u.ac.jp/idp/shibboleth", "Tohoku", "Yamagata University", "Yamagata University", "山形大学"], + SAML1SSOurl: "https://upki.yamagata-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.miyakyo-u.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "Miyagi University of Education", + search: ["https://idp.miyakyo-u.ac.jp/idp/shibboleth", "Tohoku", "Miyagi University of Education", "Miyagi University of Education", "宮城教育大学"], + SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" + }, + "https://kidp.ichinoseki.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "National Institute of Technology,Ichinoseki College", + search: ["https://kidp.ichinoseki.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Ichinoseki College", "National Institute of Technology,Ichinoseki College", "一関工業高等専門学校"], + SAML1SSOurl: "https://kidp.ichinoseki.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.hachinohe-ct.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "National Institute of Technology,Hachinohe College", + search: ["https://kidp.hachinohe-ct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Hachinohe College", "National Institute of Technology,Hachinohe College", "八戸工業高等専門学校"], + SAML1SSOurl: "https://kidp.hachinohe-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ksidp.sendai-nct.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "National Institute of Technology,Sendai College,Hirose", + search: ["https://ksidp.sendai-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Sendai College,Hirose", "National Institute of Technology,Sendai College,Hirose", "仙台高等専門学校 広瀬キャンパス"], + SAML1SSOurl: "https://ksidp.sendai-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.akita-nct.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "National Institute of Technology,Akita College", + search: ["https://kidp.akita-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Akita College", "National Institute of Technology,Akita College", "秋田工業高等専門学校"], + SAML1SSOurl: "https://kidp.akita-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.auth.tohoku.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "Tohoku University", + search: ["https://idp.auth.tohoku.ac.jp/idp/shibboleth", "Tohoku", "Tohoku University", "Tohoku University", "東北大学"], + SAML1SSOurl: "https://idp.auth.tohoku.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.tsuruoka-nct.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "National Institute of Technology,Tsuruoka College", + search: ["https://kidp.tsuruoka-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Tsuruoka College", "National Institute of Technology,Tsuruoka College", "鶴岡工業高等専門学校"], + SAML1SSOurl: "https://kidp.tsuruoka-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.fukushima-nct.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "National Institute of Technology,Fukushima College", + search: ["https://kidp.fukushima-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Fukushima College", "National Institute of Technology,Fukushima College", "福島工業高等専門学校"], + SAML1SSOurl: "https://kidp.fukushima-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://knidp.sendai-nct.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "National Institute of Technology,Sendai College,Natori", + search: ["https://knidp.sendai-nct.ac.jp/idp/shibboleth", "Tohoku", "National Institute of Technology,Sendai College,Natori", "National Institute of Technology,Sendai College,Natori", "仙台高等専門学校 名取キャンパス"], + SAML1SSOurl: "https://knidp.sendai-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.tohtech.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "Tohoku Institute of Technology", + search: ["https://idp.tohtech.ac.jp/idp/shibboleth", "Tohoku", "Tohoku Institute of Technology", "Tohoku Institute of Technology", "東北工業大学"], + SAML1SSOurl: "https://idp.tohtech.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://auas.akita-u.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "Akita University", + search: ["https://auas.akita-u.ac.jp/idp/shibboleth", "Tohoku", "Akita University", "Akita University", "秋田大学"], + SAML1SSOurl: "https://auas.akita-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp01.gn.hirosaki-u.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "Hirosaki University", + search: ["https://idp01.gn.hirosaki-u.ac.jp/idp/shibboleth", "Tohoku", "Hirosaki University", "Hirosaki University", "弘前大学"], + SAML1SSOurl: "https://idp01.gn.hirosaki-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.u-aizu.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "The University of Aizu", + search: ["https://idp.u-aizu.ac.jp/idp/shibboleth", "Tohoku", "The University of Aizu", "The University of Aizu", "会津大学"], + SAML1SSOurl: "https://idp.u-aizu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://axl.aiu.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "Akita International University", + search: ["https://axl.aiu.ac.jp/idp/shibboleth", "Tohoku", "Akita International University", "Akita International University", "国際教養大学"], + SAML1SSOurl: "https://axl.aiu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.fmu.ac.jp/idp/shibboleth": { + type: "tohoku", + name: "Fukushima Medical University", + search: ["https://idp.fmu.ac.jp/idp/shibboleth", "Tohoku", "Fukushima Medical University", "Fukushima Medical University", "福島県立医科大学"], + SAML1SSOurl: "https://idp.fmu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://tg.ex-tic.com/auth/gakunin/saml2/assertions": { + type: "tohoku", + name: "Tohoku Gakuin University", + search: ["https://tg.ex-tic.com/auth/gakunin/saml2/assertions", "Tohoku", "Tohoku Gakuin University", "Tohoku Gakuin University", "東北学院大学"], + SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" + }, + "https://idp.nii.ac.jp/idp/shibboleth": { + type: "kanto", + name: "National Institute of Informatics", + search: ["https://idp.nii.ac.jp/idp/shibboleth", "Kanto", "National Institute of Informatics", "National Institute of Informatics", "国立情報学研究所"], + SAML1SSOurl: "https://idp.nii.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://upki-idp.chiba-u.jp/idp/shibboleth": { + type: "kanto", + name: "Chiba University", + search: ["https://upki-idp.chiba-u.jp/idp/shibboleth", "Kanto", "Chiba University", "Chiba University", "千葉大学"], + SAML1SSOurl: "https://upki-idp.chiba-u.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.account.tsukuba.ac.jp/idp/shibboleth": { + type: "kanto", + name: "University of Tsukuba", + search: ["https://idp.account.tsukuba.ac.jp/idp/shibboleth", "Kanto", "University of Tsukuba", "University of Tsukuba", "筑波大学"], + SAML1SSOurl: "https://idp.account.tsukuba.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://asura.seijo.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Seijo University", + search: ["https://asura.seijo.ac.jp/idp/shibboleth", "Kanto", "Seijo University", "Seijo University", "成城大学"], + SAML1SSOurl: "https://asura.seijo.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://upki.toho-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Toho University", + search: ["https://upki.toho-u.ac.jp/idp/shibboleth", "Kanto", "Toho University", "Toho University", "東邦大学"], + SAML1SSOurl: "https://upki.toho-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shibboleth.nihon-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Nihon University", + search: ["https://shibboleth.nihon-u.ac.jp/idp/shibboleth", "Kanto", "Nihon University", "Nihon University", "日本大学"], + SAML1SSOurl: "https://shibboleth.nihon-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://upki-idp.rikkyo.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Rikkyo University", + search: ["https://upki-idp.rikkyo.ac.jp/idp/shibboleth", "Kanto", "Rikkyo University", "Rikkyo University", "立教大学"], + SAML1SSOurl: "https://upki-idp.rikkyo.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://servs.lib.meiji.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Meiji University", + search: ["https://servs.lib.meiji.ac.jp/idp/shibboleth", "Kanto", "Meiji University", "Meiji University", "明治大学"], + SAML1SSOurl: "https://servs.lib.meiji.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ws1.jichi.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Jichi Medical University(old)", + search: ["https://ws1.jichi.ac.jp/idp/shibboleth", "Kanto", "Jichi Medical University(old)", "Jichi Medical University(old)", "自治医科大学(旧)"], + SAML1SSOurl: "https://ws1.jichi.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin-idp.ynu.ac.jp/": { + type: "kanto", + name: "Yokohama National University", + search: ["https://gakunin-idp.ynu.ac.jp/", "Kanto", "Yokohama National University", "Yokohama National University", "横浜国立大学"], + SAML1SSOurl: "https://gakunin-idp.ynu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://saml-2.tmd.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo Medical and Dental University", + search: ["https://saml-2.tmd.ac.jp/idp/shibboleth", "Kanto", "Tokyo Medical and Dental University", "Tokyo Medical and Dental University", "東京医科歯科大学"], + SAML1SSOurl: "https://saml-2.tmd.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.kosen-k.go.jp/idp/shibboleth": { + type: "kanto", + name: "National Institute of Technology", + search: ["https://kidp.kosen-k.go.jp/idp/shibboleth", "Kanto", "National Institute of Technology", "National Institute of Technology", "国立高等専門学校機構"], + SAML1SSOurl: "https://kidp.kosen-k.go.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.tdc.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo Dental College", + search: ["https://idp.tdc.ac.jp/idp/shibboleth", "Kanto", "Tokyo Dental College", "Tokyo Dental College", "東京歯科大学"], + SAML1SSOurl: "https://idp.tdc.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shib.ap.showa-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Showa University", + search: ["https://shib.ap.showa-u.ac.jp/idp/shibboleth", "Kanto", "Showa University", "Showa University", "昭和大学"], + SAML1SSOurl: "https://shib.ap.showa-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ill.lib.kth.isp.ntt-east.co.jp/idp/shibboleth": { + type: "kanto", + name: "NTT Medical Center Tokyo", + search: ["https://ill.lib.kth.isp.ntt-east.co.jp/idp/shibboleth", "Kanto", "NTT Medical Center Tokyo", "NTT Medical Center Tokyo", "NTT東日本関東病院"], + SAML1SSOurl: "https://ill.lib.kth.isp.ntt-east.co.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp01.ipc.kaiyodai.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo University of Marine Science and Technology", + search: ["https://idp01.ipc.kaiyodai.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of Marine Science and Technology", "Tokyo University of Marine Science and Technology", "東京海洋大学"], + SAML1SSOurl: "https://idp01.ipc.kaiyodai.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.soka.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Soka University", + search: ["https://idp.soka.ac.jp/idp/shibboleth", "Kanto", "Soka University", "Soka University", "創価大学"], + SAML1SSOurl: "https://idp.soka.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.igakuken.or.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo Metropolitan Institute of Medical Science", + search: ["https://idp.igakuken.or.jp/idp/shibboleth", "Kanto", "Tokyo Metropolitan Institute of Medical Science", "Tokyo Metropolitan Institute of Medical Science", "東京都医学総合研究所"], + SAML1SSOurl: "https://idp.igakuken.or.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakuninidp.sic.shibaura-it.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Shibaura Institute of Technology", + search: ["https://gakuninidp.sic.shibaura-it.ac.jp/idp/shibboleth", "Kanto", "Shibaura Institute of Technology", "Shibaura Institute of Technology", "芝浦工業大学"], + SAML1SSOurl: "https://gakuninidp.sic.shibaura-it.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://tgu.u-gakugei.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo Gakugei University", + search: ["https://tgu.u-gakugei.ac.jp/idp/shibboleth", "Kanto", "Tokyo Gakugei University", "Tokyo Gakugei University", "東京学芸大学"], + SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" + }, + "https://idp.musashi.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Musashi Academy", + search: ["https://idp.musashi.ac.jp/idp/shibboleth", "Kanto", "Musashi Academy", "Musashi Academy", "武蔵学園"], + SAML1SSOurl: "https://idp.musashi.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.it-chiba.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Chiba Institute of Technology", + search: ["https://idp.it-chiba.ac.jp/idp/shibboleth", "Kanto", "Chiba Institute of Technology", "Chiba Institute of Technology", "千葉工業大学"], + SAML1SSOurl: "https://idp.it-chiba.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shibboleth.tama.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tama University", + search: ["https://shibboleth.tama.ac.jp/idp/shibboleth", "Kanto", "Tama University", "Tama University", "多摩大学"], + SAML1SSOurl: "https://shibboleth.tama.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://upkishib.cc.ocha.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Ochanomizu University", + search: ["https://upkishib.cc.ocha.ac.jp/idp/shibboleth", "Kanto", "Ochanomizu University", "Ochanomizu University", "お茶の水女子大学"], + SAML1SSOurl: "https://upkishib.cc.ocha.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.tokyo-ct.ac.jp/idp/shibboleth": { + type: "kanto", + name: "National Institute of Technology,Tokyo College", + search: ["https://kidp.tokyo-ct.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Tokyo College", "National Institute of Technology,Tokyo College", "東京工業高等専門学校"], + SAML1SSOurl: "https://kidp.tokyo-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.gunma-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Gunma University", + search: ["https://idp.gunma-u.ac.jp/idp/shibboleth", "Kanto", "Gunma University", "Gunma University", "群馬大学"], + SAML1SSOurl: "https://idp.gunma-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.oyama-ct.ac.jp/idp/shibboleth": { + type: "kanto", + name: "National Institute of Technology,Oyama College", + search: ["https://kidp.oyama-ct.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Oyama College", "National Institute of Technology,Oyama College", "小山工業高等専門学校"], + SAML1SSOurl: "https://kidp.oyama-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin1.keio.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Keio University", + search: ["https://gakunin1.keio.ac.jp/idp/shibboleth", "Kanto", "Keio University", "Keio University", "慶應義塾大学"], + SAML1SSOurl: "https://gakunin1.keio.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.gunma-ct.ac.jp/idp/shibboleth": { + type: "kanto", + name: "National Institute of Technology,Gunma College", + search: ["https://kidp.gunma-ct.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Gunma College", "National Institute of Technology,Gunma College", "群馬工業高等専門学校"], + SAML1SSOurl: "https://kidp.gunma-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shibboleth-idp.dokkyomed.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Dokkyo Medical University", + search: ["https://shibboleth-idp.dokkyomed.ac.jp/idp/shibboleth", "Kanto", "Dokkyo Medical University", "Dokkyo Medical University", "獨協医科大学"], + SAML1SSOurl: "https://shibboleth-idp.dokkyomed.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://iccoam.tufs.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo University of Foreign Studies", + search: ["https://iccoam.tufs.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of Foreign Studies", "Tokyo University of Foreign Studies", "東京外国語大学"], + SAML1SSOurl: "https://iccoam.tufs.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.ibaraki-ct.ac.jp/idp/shibboleth": { + type: "kanto", + name: "National Institute of Technology,Ibaraki College", + search: ["https://kidp.ibaraki-ct.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Ibaraki College", "National Institute of Technology,Ibaraki College", "茨城工業高等専門学校"], + SAML1SSOurl: "https://kidp.ibaraki-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shibboleth.cc.uec.ac.jp/idp/shibboleth": { + type: "kanto", + name: "The University of Electro-Communications", + search: ["https://shibboleth.cc.uec.ac.jp/idp/shibboleth", "Kanto", "The University of Electro-Communications", "The University of Electro-Communications", "電気通信大学"], + SAML1SSOurl: "https://shibboleth.cc.uec.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.sys.affrc.go.jp/idp/shibboleth": { + type: "kanto", + name: "AFFRIT/MAFFIN", + search: ["https://idp.sys.affrc.go.jp/idp/shibboleth", "Kanto", "AFFRIT/MAFFIN", "AFFRIT/MAFFIN", "AFFRIT/MAFFIN"], + SAML1SSOurl: "https://idp.sys.affrc.go.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.kisarazu.ac.jp/idp/shibboleth": { + type: "kanto", + name: "National Institute of Technology,Kisarazu College", + search: ["https://kidp.kisarazu.ac.jp/idp/shibboleth", "Kanto", "National Institute of Technology,Kisarazu College", "National Institute of Technology,Kisarazu College", "木更津工業高等専門学校"], + SAML1SSOurl: "https://kidp.kisarazu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin-idp.c.chuo-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Chuo University", + search: ["https://gakunin-idp.c.chuo-u.ac.jp/idp/shibboleth", "Kanto", "Chuo University", "Chuo University", "中央大学"], + SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" + }, + "https://gidp.adm.u-tokyo.ac.jp/idp/shibboleth": { + type: "kanto", + name: "The University of Tokyo", + search: ["https://gidp.adm.u-tokyo.ac.jp/idp/shibboleth", "Kanto", "The University of Tokyo", "The University of Tokyo", "東京大学"], + SAML1SSOurl: "https://gidp.adm.u-tokyo.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.dendai.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo Denki University", + search: ["https://idp.dendai.ac.jp/idp/shibboleth", "Kanto", "Tokyo Denki University", "Tokyo Denki University", "東京電機大学"], + SAML1SSOurl: "https://idp.dendai.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.cc.seikei.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Seikei University", + search: ["https://idp.cc.seikei.ac.jp/idp/shibboleth", "Kanto", "Seikei University", "Seikei University", "成蹊大学"], + SAML1SSOurl: "https://idp.cc.seikei.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.teikyo-u.ac.jp/AccessManager/shibboleth": { + type: "kanto", + name: "Teikyo University", + search: ["https://idp.teikyo-u.ac.jp/AccessManager/shibboleth", "Kanto", "Teikyo University", "Teikyo University", "帝京大学"], + SAML1SSOurl: "https://idp.teikyo-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.tau.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo Ariake University of Medical and Health Sciences", + search: ["https://idp.tau.ac.jp/idp/shibboleth", "Kanto", "Tokyo Ariake University of Medical and Health Sciences", "Tokyo Ariake University of Medical and Health Sciences", "東京有明医療大学"], + SAML1SSOurl: "https://idp.tau.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.tokyo-kasei.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo Kasei University", + search: ["https://idp.tokyo-kasei.ac.jp/idp/shibboleth", "Kanto", "Tokyo Kasei University", "Tokyo Kasei University", "東京家政大学"], + SAML1SSOurl: "https://idp.tokyo-kasei.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.grips.ac.jp/idp/shibboleth": { + type: "kanto", + name: "National Graduate Institute for Policy Studies", + search: ["https://idp.grips.ac.jp/idp/shibboleth", "Kanto", "National Graduate Institute for Policy Studies", "National Graduate Institute for Policy Studies", "政策研究大学院大学"], + SAML1SSOurl: "https://idp.grips.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakuninshib.tmu.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo Metropolitan University", + search: ["https://gakuninshib.tmu.ac.jp/idp/shibboleth", "Kanto", "Tokyo Metropolitan University", "Tokyo Metropolitan University", "首都大学東京"], + SAML1SSOurl: "https://gakuninshib.tmu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp-gakunin.nap.gsic.titech.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo Institute of Technology", + search: ["https://idp-gakunin.nap.gsic.titech.ac.jp/idp/shibboleth", "Kanto", "Tokyo Institute of Technology", "Tokyo Institute of Technology", "東京工業大学"], + SAML1SSOurl: "https://idp-gakunin.nap.gsic.titech.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.tsurumi-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tsurumi University", + search: ["https://idp.tsurumi-u.ac.jp/idp/shibboleth", "Kanto", "Tsurumi University", "Tsurumi University", "鶴見大学"], + SAML1SSOurl: "https://idp.tsurumi-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://sidp.ibaraki.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Ibaraki University", + search: ["https://sidp.ibaraki.ac.jp/idp/shibboleth", "Kanto", "Ibaraki University", "Ibaraki University", "茨城大学"], + SAML1SSOurl: "https://sidp.ibaraki.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.nims.go.jp/idp/shibboleth": { + type: "kanto", + name: "National Institute for Materials Science", + search: ["https://idp.nims.go.jp/idp/shibboleth", "Kanto", "National Institute for Materials Science", "National Institute for Materials Science", "物質・材料研究機構"], + SAML1SSOurl: "https://idp.nims.go.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.toyaku.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo University of Pharmacy and Life Sciences", + search: ["https://idp.toyaku.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of Pharmacy and Life Sciences", "Tokyo University of Pharmacy and Life Sciences", "東京薬科大学"], + SAML1SSOurl: "https://idp.toyaku.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin2.tuat.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo University of Agriculture and Technology", + search: ["https://gakunin2.tuat.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of Agriculture and Technology", "Tokyo University of Agriculture and Technology", "東京農工大学"], + SAML1SSOurl: "https://gakunin2.tuat.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin-idp.shodai.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Yokohama College of Commerce", + search: ["https://gakunin-idp.shodai.ac.jp/idp/shibboleth", "Kanto", "Yokohama College of Commerce", "Yokohama College of Commerce", "横浜商科大学"], + SAML1SSOurl: "https://gakunin-idp.shodai.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://koma-sso.komazawa-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Komazawa University", + search: ["https://koma-sso.komazawa-u.ac.jp/idp/shibboleth", "Kanto", "Komazawa University", "Komazawa University", "駒澤大学"], + SAML1SSOurl: "https://koma-sso.komazawa-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp3.qst.go.jp/idp/shibboleth": { + type: "kanto", + name: "National Institutes for Quantum and Radiological Science and Technology", + search: ["https://idp3.qst.go.jp/idp/shibboleth", "Kanto", "National Institutes for Quantum and Radiological Science and Technology", "National Institutes for Quantum and Radiological Science and Technology", "量子科学技術研究開発機構"], + SAML1SSOurl: "https://idp3.qst.go.jp/idp/profile/Shibboleth/SSO" + }, + "https://rprx.rku.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Ryutsu Keizai University", + search: ["https://rprx.rku.ac.jp/idp/shibboleth", "Kanto", "Ryutsu Keizai University", "Ryutsu Keizai University", "流通経済大学"], + SAML1SSOurl: "https://rprx.rku.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kanagawa-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Kanagawa University", + search: ["https://idp.kanagawa-u.ac.jp/idp/shibboleth", "Kanto", "Kanagawa University", "Kanagawa University", "神奈川大学"], + SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" + }, + "https://idp.ide.go.jp/idp/shibboleth": { + type: "kanto", + name: "IDE-JETRO", + search: ["https://idp.ide.go.jp/idp/shibboleth", "Kanto", "IDE-JETRO", "IDE-JETRO", "アジア経済研究所"], + SAML1SSOurl: "https://idp.ide.go.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.senshu-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Senshu University", + search: ["https://idp.senshu-u.ac.jp/idp/shibboleth", "Kanto", "Senshu University", "Senshu University", "専修大学"], + SAML1SSOurl: "https://idp.senshu-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.geidai.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Tokyo University of the Arts", + search: ["https://idp.geidai.ac.jp/idp/shibboleth", "Kanto", "Tokyo University of the Arts", "Tokyo University of the Arts", "東京藝術大学"], + SAML1SSOurl: "https://idp.geidai.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.aoyama.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Aoyama Gakuin University", + search: ["https://idp.aoyama.ac.jp/idp/shibboleth", "Kanto", "Aoyama Gakuin University", "Aoyama Gakuin University", "青山学院大学"], + SAML1SSOurl: "https://idp.aoyama.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://sso.internet.ac.jp": { + type: "kanto", + name: "Tokyo Online University", + search: ["https://sso.internet.ac.jp", "Kanto", "Tokyo Online University", "Tokyo Online University", "東京通信大学"], + SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" + }, + "https://idp.itc.saitama-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Saitama University", + search: ["https://idp.itc.saitama-u.ac.jp/idp/shibboleth", "Kanto", "Saitama University", "Saitama University", "埼玉大学"], + SAML1SSOurl: "https://idp.itc.saitama-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://lib.nmct.ntt-east.co.jp/idp/shibboleth": { + type: "kanto", + name: "NTT Medical Center Tokyo Library", + search: ["https://lib.nmct.ntt-east.co.jp/idp/shibboleth", "Kanto", "NTT Medical Center Tokyo Library", "NTT Medical Center Tokyo Library", "NTT東日本関東病院図書館"], + SAML1SSOurl: "https://lib.nmct.ntt-east.co.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.my-pharm.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Meiji Pharmaceutical University", + search: ["https://idp.my-pharm.ac.jp/idp/shibboleth", "Kanto", "Meiji Pharmaceutical University", "Meiji Pharmaceutical University", "明治薬科大学"], + SAML1SSOurl: "https://idp.my-pharm.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin.st.daito.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Daito Bunka University", + search: ["https://gakunin.st.daito.ac.jp/idp/shibboleth", "Kanto", "Daito Bunka University", "Daito Bunka University", "大東文化大学"], + SAML1SSOurl: "https://gakunin.st.daito.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kiryu-u.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Kiryu University", + search: ["https://idp.kiryu-u.ac.jp/idp/shibboleth", "Kanto", "Kiryu University", "Kiryu University", "桐生大学"], + SAML1SSOurl: "https://idp.kiryu-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://slink.secioss.com/icu.ac.jp": { + type: "kanto", + name: "International Christian University", + search: ["https://slink.secioss.com/icu.ac.jp", "Kanto", "International Christian University", "International Christian University", "国際基督教大学"], + SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" + }, + "https://iaidp.ia.waseda.jp/idp/shibboleth": { + type: "kanto", + name: "Waseda University", + search: ["https://iaidp.ia.waseda.jp/idp/shibboleth", "Kanto", "Waseda University", "Waseda University", "早稲田大学"], + SAML1SSOurl: "https://iaidp.ia.waseda.jp/idp/profile/Shibboleth/SSO" + }, + "https://sh-idp.riken.jp/idp/shibboleth": { + type: "kanto", + name: "RIKEN", + search: ["https://sh-idp.riken.jp/idp/shibboleth", "Kanto", "RIKEN", "RIKEN", "理化学研究所"], + SAML1SSOurl: "https://sh-idp.riken.jp/idp/profile/Shibboleth/SSO" + }, + "https://obirin.ex-tic.com/auth/gakunin/saml2/assertions": { + type: "kanto", + name: "J. F. Oberlin University", + search: ["https://obirin.ex-tic.com/auth/gakunin/saml2/assertions", "Kanto", "J. F. Oberlin University", "J. F. Oberlin University", "桜美林大学"], + SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" + }, + "https://idp.jichi.ac.jp/idp/shibboleth": { + type: "kanto", + name: "Jichi Medical University", + search: ["https://idp.jichi.ac.jp/idp/shibboleth", "Kanto", "Jichi Medical University", "Jichi Medical University", "自治医科大学"], + SAML1SSOurl: "https://idp.jichi.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://fed.mie-u.ac.jp/idp": { + type: "chubu", + name: "Mie University", + search: ["https://fed.mie-u.ac.jp/idp", "Chubu", "Mie University", "Mie University", "三重大学"], + SAML1SSOurl: "https://fed.mie-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin.ealps.shinshu-u.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Shinshu University", + search: ["https://gakunin.ealps.shinshu-u.ac.jp/idp/shibboleth", "Chubu", "Shinshu University", "Shinshu University", "信州大学"], + SAML1SSOurl: "https://gakunin.ealps.shinshu-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gknidp.ict.nitech.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Nagoya Institute of Technology", + search: ["https://gknidp.ict.nitech.ac.jp/idp/shibboleth", "Chubu", "Nagoya Institute of Technology", "Nagoya Institute of Technology", "名古屋工業大学"], + SAML1SSOurl: "https://gknidp.ict.nitech.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.yamanashi.ac.jp/idp/shibboleth": { + type: "chubu", + name: "University of Yamanashi", + search: ["https://idp.yamanashi.ac.jp/idp/shibboleth", "Chubu", "University of Yamanashi", "University of Yamanashi", "山梨大学"], + SAML1SSOurl: "https://idp.yamanashi.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.suzuka-ct.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Suzuka College", + search: ["https://idp.suzuka-ct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Suzuka College", "National Institute of Technology,Suzuka College", "鈴鹿工業高等専門学校"], + SAML1SSOurl: "https://idp.suzuka-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.imc.tut.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Toyohashi University of Technology", + search: ["https://idp.imc.tut.ac.jp/idp/shibboleth", "Chubu", "Toyohashi University of Technology", "Toyohashi University of Technology", "豊橋技術科学大学"], + SAML1SSOurl: "https://idp.imc.tut.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.fukui-nct.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Fukui College", + search: ["https://kidp.fukui-nct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Fukui College", "National Institute of Technology,Fukui College", "福井工業高等専門学校"], + SAML1SSOurl: "https://kidp.fukui-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.shizuoka.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Shizuoka University", + search: ["https://idp.shizuoka.ac.jp/idp/shibboleth", "Chubu", "Shizuoka University", "Shizuoka University", "静岡大学"], + SAML1SSOurl: "https://idp.shizuoka.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://wagner.isc.chubu.ac.jp/idp/shibboleth": { + type: "chubu", + name: "CHUBU UNIVERSITY", + search: ["https://wagner.isc.chubu.ac.jp/idp/shibboleth", "Chubu", "CHUBU UNIVERSITY", "CHUBU UNIVERSITY", "中部大学"], + SAML1SSOurl: "https://wagner.isc.chubu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.nagaoka-ct.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Nagaoka College", + search: ["https://kidp.nagaoka-ct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Nagaoka College", "National Institute of Technology,Nagaoka College", "長岡工業高等専門学校"], + SAML1SSOurl: "https://kidp.nagaoka-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.numazu-ct.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Numazu College", + search: ["https://kidp.numazu-ct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Numazu College", "National Institute of Technology,Numazu College", "沼津工業高等専門学校"], + SAML1SSOurl: "https://kidp.numazu-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.nagano-nct.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Nagano College", + search: ["https://kidp.nagano-nct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Nagano College", "National Institute of Technology,Nagano College", "長野工業高等専門学校"], + SAML1SSOurl: "https://kidp.nagano-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.ishikawa-nct.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Ishikawa College", + search: ["https://kidp.ishikawa-nct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Ishikawa College", "National Institute of Technology,Ishikawa College", "石川工業高等専門学校"], + SAML1SSOurl: "https://kidp.ishikawa-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kiidp.nc-toyama.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Toyama College", + search: ["https://kiidp.nc-toyama.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Toyama College", "National Institute of Technology,Toyama College", "富山高等専門学校"], + SAML1SSOurl: "https://kiidp.nc-toyama.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.toba-cmt.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Toba College", + search: ["https://kidp.toba-cmt.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Toba College", "National Institute of Technology,Toba College", "鳥羽商船高等専門学校"], + SAML1SSOurl: "https://kidp.toba-cmt.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.gifu-nct.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Gifu College", + search: ["https://kidp.gifu-nct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Gifu College", "National Institute of Technology,Gifu College", "岐阜工業高等専門学校"], + SAML1SSOurl: "https://kidp.gifu-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://sso.sugiyama-u.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Sugiyama Jogakuen University", + search: ["https://sso.sugiyama-u.ac.jp/idp/shibboleth", "Chubu", "Sugiyama Jogakuen University", "Sugiyama Jogakuen University", "椙山女学園大学"], + SAML1SSOurl: "https://sso.sugiyama-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.toyota-ct.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute of Technology,Toyota College", + search: ["https://kidp.toyota-ct.ac.jp/idp/shibboleth", "Chubu", "National Institute of Technology,Toyota College", "National Institute of Technology,Toyota College", "豊田工業高等専門学校"], + SAML1SSOurl: "https://kidp.toyota-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shib.nagoya-u.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Nagoya University", + search: ["https://shib.nagoya-u.ac.jp/idp/shibboleth", "Chubu", "Nagoya University", "Nagoya University", "名古屋大学"], + SAML1SSOurl: "https://shib.nagoya-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://islwpi01.auecc.aichi-edu.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Aichi University of Education", + search: ["https://islwpi01.auecc.aichi-edu.ac.jp/idp/shibboleth", "Chubu", "Aichi University of Education", "Aichi University of Education", "愛知教育大学"], + SAML1SSOurl: "https://islwpi01.auecc.aichi-edu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ams.juen.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Joetsu University of Education", + search: ["https://ams.juen.ac.jp/idp/shibboleth", "Chubu", "Joetsu University of Education", "Joetsu University of Education", "上越教育大学"], + SAML1SSOurl: "https://no.saml1.sso.url.defined.com/error" + }, + "https://idp1.b.cii.u-fukui.ac.jp/idp/shibboleth": { + type: "chubu", + name: "University of Fukui", + search: ["https://idp1.b.cii.u-fukui.ac.jp/idp/shibboleth", "Chubu", "University of Fukui", "University of Fukui", "福井大学"], + SAML1SSOurl: "https://idp1.b.cii.u-fukui.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin.gifu-u.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Gifu University", + search: ["https://gakunin.gifu-u.ac.jp/idp/shibboleth", "Chubu", "Gifu University", "Gifu University", "岐阜大学"], + SAML1SSOurl: "https://gakunin.gifu-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.iamas.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Institute of Advanced Media Arts and Sciences", + search: ["https://idp.iamas.ac.jp/idp/shibboleth", "Chubu", "Institute of Advanced Media Arts and Sciences", "Institute of Advanced Media Arts and Sciences", "情報科学芸術大学院大学"], + SAML1SSOurl: "https://idp.iamas.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin.aitech.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Aichi Institute of Technology", + search: ["https://gakunin.aitech.ac.jp/idp/shibboleth", "Chubu", "Aichi Institute of Technology", "Aichi Institute of Technology", "愛知工業大学"], + SAML1SSOurl: "https://gakunin.aitech.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ipcm2.nagaokaut.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Nagaoka University of Technology", + search: ["https://ipcm2.nagaokaut.ac.jp/idp/shibboleth", "Chubu", "Nagaoka University of Technology", "Nagaoka University of Technology", "長岡技術科学大学"], + SAML1SSOurl: "https://ipcm2.nagaokaut.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shibboleth.niigata-cn.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Niigata College of Nursing", + search: ["https://shibboleth.niigata-cn.ac.jp/idp/shibboleth", "Chubu", "Niigata College of Nursing", "Niigata College of Nursing", "新潟県立看護大学"], + SAML1SSOurl: "https://shibboleth.niigata-cn.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.nifs.ac.jp/idp/shibboleth": { + type: "chubu", + name: "National Institute for Fusion Science", + search: ["https://idp.nifs.ac.jp/idp/shibboleth", "Chubu", "National Institute for Fusion Science", "National Institute for Fusion Science", "核融合科学研究所"], + SAML1SSOurl: "https://idp.nifs.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shib.chukyo-u.ac.jp/idp/shibboleth": { + type: "chubu", + name: "CHUKYO UNIVERSITY", + search: ["https://shib.chukyo-u.ac.jp/idp/shibboleth", "Chubu", "CHUKYO UNIVERSITY", "CHUKYO UNIVERSITY", "中京大学"], + SAML1SSOurl: "https://shib.chukyo-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.cais.niigata-u.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Niigata University", + search: ["https://idp.cais.niigata-u.ac.jp/idp/shibboleth", "Chubu", "Niigata University", "Niigata University", "新潟大学"], + SAML1SSOurl: "https://idp.cais.niigata-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.fujita-hu.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Fujita Health University", + search: ["https://idp.fujita-hu.ac.jp/idp/shibboleth", "Chubu", "Fujita Health University", "Fujita Health University", "藤田医科大学"], + SAML1SSOurl: "https://idp.fujita-hu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ku-sso.cis.kanazawa-u.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Kanazawa University", + search: ["https://ku-sso.cis.kanazawa-u.ac.jp/idp/shibboleth", "Chubu", "Kanazawa University", "Kanazawa University", "金沢大学"], + SAML1SSOurl: "https://ku-sso.cis.kanazawa-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.gifu.shotoku.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Gifu Shotoku Gakuen University", + search: ["https://idp.gifu.shotoku.ac.jp/idp/shibboleth", "Chubu", "Gifu Shotoku Gakuen University", "Gifu Shotoku Gakuen University", "岐阜聖徳学園大学"], + SAML1SSOurl: "https://idp.gifu.shotoku.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp02.u-toyama.ac.jp/idp/shibboleth": { + type: "chubu", + name: "University of Toyama", + search: ["https://idp02.u-toyama.ac.jp/idp/shibboleth", "Chubu", "University of Toyama", "University of Toyama", "富山大学"], + SAML1SSOurl: "https://idp02.u-toyama.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin.ims.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Institute for Molecular Science", + search: ["https://gakunin.ims.ac.jp/idp/shibboleth", "Chubu", "Institute for Molecular Science", "Institute for Molecular Science", "分子科学研究所"], + SAML1SSOurl: "https://gakunin.ims.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.tsuru.ac.jp/idp/shibboleth": { + type: "chubu", + name: "Tsuru University", + search: ["https://idp.tsuru.ac.jp/idp/shibboleth", "Chubu", "Tsuru University", "Tsuru University", "都留文科大学"], + SAML1SSOurl: "https://idp.tsuru.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://authidp1.iimc.kyoto-u.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kyoto University", + search: ["https://authidp1.iimc.kyoto-u.ac.jp/idp/shibboleth", "Kinki", "Kyoto University", "Kyoto University", "京都大学"], + SAML1SSOurl: "https://authidp1.iimc.kyoto-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin.kyoto-su.ac.jp/idp": { + type: "kinki", + name: "Kyoto Sangyo University", + search: ["https://gakunin.kyoto-su.ac.jp/idp", "Kinki", "Kyoto Sangyo University", "Kyoto Sangyo University", "京都産業大学"], + SAML1SSOurl: "https://gakunin.kyoto-su.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://fed.center.kobe-u.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kobe University", + search: ["https://fed.center.kobe-u.ac.jp/idp/shibboleth", "Kinki", "Kobe University", "Kobe University", "神戸大学"], + SAML1SSOurl: "https://fed.center.kobe-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.naist.jp/idp/shibboleth": { + type: "kinki", + name: "Nara Institute of Science and Technology", + search: ["https://idp.naist.jp/idp/shibboleth", "Kinki", "Nara Institute of Science and Technology", "Nara Institute of Science and Technology", "奈良先端科学技術大学院大学"], + SAML1SSOurl: "https://idp.naist.jp/idp/profile/Shibboleth/SSO" + }, + "https://shib-idp.nara-edu.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Nara University of Education", + search: ["https://shib-idp.nara-edu.ac.jp/idp/shibboleth", "Kinki", "Nara University of Education", "Nara University of Education", "奈良教育大学"], + SAML1SSOurl: "https://shib-idp.nara-edu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.ritsumei.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Ritsumeikan University", + search: ["https://idp.ritsumei.ac.jp/idp/shibboleth", "Kinki", "Ritsumeikan University", "Ritsumeikan University", "立命館大学"], + SAML1SSOurl: "https://idp.ritsumei.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp1.itc.kansai-u.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kansai University", + search: ["https://idp1.itc.kansai-u.ac.jp/idp/shibboleth", "Kinki", "Kansai University", "Kansai University", "関西大学"], + SAML1SSOurl: "https://idp1.itc.kansai-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shib.osaka-kyoiku.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Osaka Kyoiku University", + search: ["https://shib.osaka-kyoiku.ac.jp/idp/shibboleth", "Kinki", "Osaka Kyoiku University", "Osaka Kyoiku University", "大阪教育大学"], + SAML1SSOurl: "https://shib.osaka-kyoiku.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp1.kyokyo-u.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kyoto University of Education", + search: ["https://idp1.kyokyo-u.ac.jp/idp/shibboleth", "Kinki", "Kyoto University of Education", "Kyoto University of Education", "京都教育大学"], + SAML1SSOurl: "https://idp1.kyokyo-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://authsv.kpu.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kyoto Prefectural University", + search: ["https://authsv.kpu.ac.jp/idp/shibboleth", "Kinki", "Kyoto Prefectural University", "Kyoto Prefectural University", "京都府立大学"], + SAML1SSOurl: "https://authsv.kpu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.tezukayama-u.ac.jp/idp/shibboleth": { + type: "kinki", + name: "TEZUKAYAMA UNIVERSITY", + search: ["https://idp.tezukayama-u.ac.jp/idp/shibboleth", "Kinki", "TEZUKAYAMA UNIVERSITY", "TEZUKAYAMA UNIVERSITY", "帝塚山大学"], + SAML1SSOurl: "https://idp.tezukayama-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.tieskun.net/idp/shibboleth": { + type: "kinki", + name: "CCC-TIES", + search: ["https://idp.tieskun.net/idp/shibboleth", "Kinki", "CCC-TIES", "CCC-TIES", "CCC-TIES"], + SAML1SSOurl: "https://idp.tieskun.net/idp/profile/Shibboleth/SSO" + }, + "https://idp.ouhs.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Osaka University of Health and Sport Sciences", + search: ["https://idp.ouhs.ac.jp/idp/shibboleth", "Kinki", "Osaka University of Health and Sport Sciences", "Osaka University of Health and Sport Sciences", "大阪体育大学"], + SAML1SSOurl: "https://idp.ouhs.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.maizuru-ct.ac.jp/idp/shibboleth": { + type: "kinki", + name: "National Institute of Technology,Maizuru College", + search: ["https://kidp.maizuru-ct.ac.jp/idp/shibboleth", "Kinki", "National Institute of Technology,Maizuru College", "National Institute of Technology,Maizuru College", "舞鶴工業高等専門学校"], + SAML1SSOurl: "https://kidp.maizuru-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.wakayama-nct.ac.jp/idp/shibboleth": { + type: "kinki", + name: "National Institute of Technology,Wakayama College", + search: ["https://kidp.wakayama-nct.ac.jp/idp/shibboleth", "Kinki", "National Institute of Technology,Wakayama College", "National Institute of Technology,Wakayama College", "和歌山工業高等専門学校"], + SAML1SSOurl: "https://kidp.wakayama-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.akashi.ac.jp/idp/shibboleth": { + type: "kinki", + name: "National Institute of Technology,Akashi College", + search: ["https://kidp.akashi.ac.jp/idp/shibboleth", "Kinki", "National Institute of Technology,Akashi College", "National Institute of Technology,Akashi College", "明石工業高等専門学校"], + SAML1SSOurl: "https://kidp.akashi.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://g-shib.auth.oit.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Osaka Institute of Technology", + search: ["https://g-shib.auth.oit.ac.jp/idp/shibboleth", "Kinki", "Osaka Institute of Technology", "Osaka Institute of Technology", "大阪工業大学"], + SAML1SSOurl: "https://g-shib.auth.oit.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.nara-k.ac.jp/idp/shibboleth": { + type: "kinki", + name: "National Institute of Technology,Nara College", + search: ["https://kidp.nara-k.ac.jp/idp/shibboleth", "Kinki", "National Institute of Technology,Nara College", "National Institute of Technology,Nara College", "奈良工業高等専門学校"], + SAML1SSOurl: "https://kidp.nara-k.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kobe-cufs.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kobe City University of Foreign Studies", + search: ["https://idp.kobe-cufs.ac.jp/idp/shibboleth", "Kinki", "Kobe City University of Foreign Studies", "Kobe City University of Foreign Studies", "神戸市外国語大学"], + SAML1SSOurl: "https://idp.kobe-cufs.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://sumsidp.shiga-med.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Shiga University of Medical Science", + search: ["https://sumsidp.shiga-med.ac.jp/idp/shibboleth", "Kinki", "Shiga University of Medical Science", "Shiga University of Medical Science", "滋賀医科大学"], + SAML1SSOurl: "https://sumsidp.shiga-med.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kobe-tokiwa.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kobe Tokiwa University", + search: ["https://idp.kobe-tokiwa.ac.jp/idp/shibboleth", "Kinki", "Kobe Tokiwa University", "Kobe Tokiwa University", "神戸常盤大学"], + SAML1SSOurl: "https://idp.kobe-tokiwa.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gakunin.osaka-cu.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Osaka City University", + search: ["https://gakunin.osaka-cu.ac.jp/idp/shibboleth", "Kinki", "Osaka City University", "Osaka City University", "大阪市立大学"], + SAML1SSOurl: "https://gakunin.osaka-cu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.doshisha.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Doshisha University", + search: ["https://idp.doshisha.ac.jp/idp/shibboleth", "Kinki", "Doshisha University", "Doshisha University", "同志社大学"], + SAML1SSOurl: "https://idp.doshisha.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kpu-m.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kyoto Prefectural University of Medicine", + search: ["https://idp.kpu-m.ac.jp/idp/shibboleth", "Kinki", "Kyoto Prefectural University of Medicine", "Kyoto Prefectural University of Medicine", "京都府立医科大学"], + SAML1SSOurl: "https://idp.kpu-m.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.otani.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Otani University", + search: ["https://idp.otani.ac.jp/idp/shibboleth", "Kinki", "Otani University", "Otani University", "大谷大学"], + SAML1SSOurl: "https://idp.otani.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gidp.ryukoku.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Ryukoku University", + search: ["https://gidp.ryukoku.ac.jp/idp/shibboleth", "Kinki", "Ryukoku University", "Ryukoku University", "龍谷大学"], + SAML1SSOurl: "https://gidp.ryukoku.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://naraidp.cc.nara-wu.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Nara Women\'s University", + search: ["https://naraidp.cc.nara-wu.ac.jp/idp/shibboleth", "Kinki", "Nara Women\'s University", "Nara Women\'s University", "奈良女子大学"], + SAML1SSOurl: "https://naraidp.cc.nara-wu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gk-idp.auth.osaka-u.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Osaka University", + search: ["https://gk-idp.auth.osaka-u.ac.jp/idp/shibboleth", "Kinki", "Osaka University", "Osaka University", "大阪大学"], + SAML1SSOurl: "https://gk-idp.auth.osaka-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://heimdall.osaka-aoyama.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Osaka Aoyama University", + search: ["https://heimdall.osaka-aoyama.ac.jp/idp/shibboleth", "Kinki", "Osaka Aoyama University", "Osaka Aoyama University", "大阪青山大学"], + SAML1SSOurl: "https://heimdall.osaka-aoyama.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kindai.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kindai University", + search: ["https://idp.kindai.ac.jp/idp/shibboleth", "Kinki", "Kindai University", "Kindai University", "近畿大学"], + SAML1SSOurl: "https://idp.kindai.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.cis.kit.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kyoto Institute of Technology", + search: ["https://idp.cis.kit.ac.jp/idp/shibboleth", "Kinki", "Kyoto Institute of Technology", "Kyoto Institute of Technology", "京都工芸繊維大学"], + SAML1SSOurl: "https://idp.cis.kit.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.andrew.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Momoyama Gakuin University", + search: ["https://idp.andrew.ac.jp/idp/shibboleth", "Kinki", "Momoyama Gakuin University", "Momoyama Gakuin University", "桃山学院大学"], + SAML1SSOurl: "https://idp.andrew.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kobe-ccn.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kobe City College of Nursing", + search: ["https://idp.kobe-ccn.ac.jp/idp/shibboleth", "Kinki", "Kobe City College of Nursing", "Kobe City College of Nursing", "神戸市看護大学"], + SAML1SSOurl: "https://idp.kobe-ccn.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.u-hyogo.ac.jp/idp/shibboleth": { + type: "kinki", + name: "University of Hyogo", + search: ["https://idp.u-hyogo.ac.jp/idp/shibboleth", "Kinki", "University of Hyogo", "University of Hyogo", "兵庫県立大学"], + SAML1SSOurl: "https://idp.u-hyogo.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.wakayama-u.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Wakayama University", + search: ["https://idp.wakayama-u.ac.jp/idp/shibboleth", "Kinki", "Wakayama University", "Wakayama University", "和歌山大学"], + SAML1SSOurl: "https://idp.wakayama-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.21c.osakafu-u.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Osaka Prefecture University", + search: ["https://idp.21c.osakafu-u.ac.jp/idp/shibboleth", "Kinki", "Osaka Prefecture University", "Osaka Prefecture University", "大阪府立大学"], + SAML1SSOurl: "https://idp.21c.osakafu-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://dc-shibsv.shiga-u.ac.jp/idp/shibboleth/": { + type: "kinki", + name: "SHIGA UNIVERSITY", + search: ["https://dc-shibsv.shiga-u.ac.jp/idp/shibboleth/", "Kinki", "SHIGA UNIVERSITY", "SHIGA UNIVERSITY", "滋賀大学"], + SAML1SSOurl: "https://dc-shibsv.shiga-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gkn.kbu.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Kyoto Bunkyo University", + search: ["https://gkn.kbu.ac.jp/idp/shibboleth", "Kinki", "Kyoto Bunkyo University", "Kyoto Bunkyo University", "京都文教大学"], + SAML1SSOurl: "https://gkn.kbu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.osaka-ue.ac.jp/idp/shibboleth": { + type: "kinki", + name: "Osaka University of Economics", + search: ["https://idp.osaka-ue.ac.jp/idp/shibboleth", "Kinki", "Osaka University of Economics", "Osaka University of Economics", "大阪経済大学"], + SAML1SSOurl: "https://idp.osaka-ue.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.hiroshima-u.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Hiroshima University", + search: ["https://idp.hiroshima-u.ac.jp/idp/shibboleth", "Chugoku", "Hiroshima University", "Hiroshima University", "広島大学"], + SAML1SSOurl: "https://idp.hiroshima-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://odidp.cc.okayama-u.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Okayama University", + search: ["https://odidp.cc.okayama-u.ac.jp/idp/shibboleth", "Chugoku", "Okayama University", "Okayama University", "岡山大学"], + SAML1SSOurl: "https://odidp.cc.okayama-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://fed.ipc.hiroshima-cu.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Hiroshima City University", + search: ["https://fed.ipc.hiroshima-cu.ac.jp/idp/shibboleth", "Chugoku", "Hiroshima City University", "Hiroshima City University", "広島市立大学"], + SAML1SSOurl: "https://fed.ipc.hiroshima-cu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.it-hiroshima.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Hiroshima Institute of Technology", + search: ["https://idp.it-hiroshima.ac.jp/idp/shibboleth", "Chugoku", "Hiroshima Institute of Technology", "Hiroshima Institute of Technology", "広島工業大学"], + SAML1SSOurl: "https://idp.it-hiroshima.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.shudo-u.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Hiroshima Shudo University", + search: ["https://idp.shudo-u.ac.jp/idp/shibboleth", "Chugoku", "Hiroshima Shudo University", "Hiroshima Shudo University", "広島修道大学"], + SAML1SSOurl: "https://idp.shudo-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.oshima-k.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "National Institute of Technology,Oshima College", + search: ["https://kidp.oshima-k.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Oshima College", "National Institute of Technology,Oshima College", "大島商船高等専門学校"], + SAML1SSOurl: "https://kidp.oshima-k.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.kure-nct.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "National Institute of Technology,Kure College", + search: ["https://kidp.kure-nct.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Kure College", "National Institute of Technology,Kure College", "呉工業高等専門学校"], + SAML1SSOurl: "https://kidp.kure-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.hiroshima-cmt.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "National Institute of Technology,Hiroshima College", + search: ["https://kidp.hiroshima-cmt.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Hiroshima College", "National Institute of Technology,Hiroshima College", "広島商船高等専門学校"], + SAML1SSOurl: "https://kidp.hiroshima-cmt.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.yonago-k.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "National Institute of Technology,Yonago College", + search: ["https://kidp.yonago-k.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Yonago College", "National Institute of Technology,Yonago College", "米子工業高等専門学校"], + SAML1SSOurl: "https://kidp.yonago-k.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.tsuyama-ct.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "National Institute of Technology,Tsuyama College", + search: ["https://kidp.tsuyama-ct.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Tsuyama College", "National Institute of Technology,Tsuyama College", "津山工業高等専門学校"], + SAML1SSOurl: "https://kidp.tsuyama-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.ube-k.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "National Institute of Technology,Ube College", + search: ["https://kidp.ube-k.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Ube College", "National Institute of Technology,Ube College", "宇部工業高等専門学校"], + SAML1SSOurl: "https://kidp.ube-k.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.tokuyama.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "National Institute of Technology,Tokuyama College", + search: ["https://kidp.tokuyama.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Tokuyama College", "National Institute of Technology,Tokuyama College", "徳山工業高等専門学校"], + SAML1SSOurl: "https://kidp.tokuyama.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.matsue-ct.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "National Institute of Technology,Matsue College", + search: ["https://idp.matsue-ct.ac.jp/idp/shibboleth", "Chugoku", "National Institute of Technology,Matsue College", "National Institute of Technology,Matsue College", "松江工業高等専門学校"], + SAML1SSOurl: "https://idp.matsue-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.tottori-u.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Tottori University", + search: ["https://idp.tottori-u.ac.jp/idp/shibboleth", "Chugoku", "Tottori University", "Tottori University", "鳥取大学"], + SAML1SSOurl: "https://idp.tottori-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.shimane-u.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Shimane University", + search: ["https://idp.shimane-u.ac.jp/idp/shibboleth", "Chugoku", "Shimane University", "Shimane University", "島根大学"], + SAML1SSOurl: "https://idp.shimane-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.oka-pu.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Okayama Prefectural University", + search: ["https://idp.oka-pu.ac.jp/idp/shibboleth", "Chugoku", "Okayama Prefectural University", "Okayama Prefectural University", "岡山県立大学"], + SAML1SSOurl: "https://idp.oka-pu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.pu-hiroshima.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Prefectural University of Hiroshima", + search: ["https://idp.pu-hiroshima.ac.jp/idp/shibboleth", "Chugoku", "Prefectural University of Hiroshima", "Prefectural University of Hiroshima", "県立広島大学"], + SAML1SSOurl: "https://idp.pu-hiroshima.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://auth.socu.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Sanyo-Onoda City University", + search: ["https://auth.socu.ac.jp/idp/shibboleth", "Chugoku", "Sanyo-Onoda City University", "Sanyo-Onoda City University", "山陽小野田市立山口東京理科大学"], + SAML1SSOurl: "https://auth.socu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.cc.yamaguchi-u.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Yamaguchi University", + search: ["https://idp.cc.yamaguchi-u.ac.jp/idp/shibboleth", "Chugoku", "Yamaguchi University", "Yamaguchi University", "山口大学"], + SAML1SSOurl: "https://idp.cc.yamaguchi-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.pub.ous.ac.jp/idp/shibboleth": { + type: "chugoku", + name: "Okayama University of Science", + search: ["https://idp.pub.ous.ac.jp/idp/shibboleth", "Chugoku", "Okayama University of Science", "Okayama University of Science", "岡山理科大学"], + SAML1SSOurl: "https://idp.pub.ous.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.cc.ehime-u.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "Ehime University", + search: ["https://idp.cc.ehime-u.ac.jp/idp/shibboleth", "Shikoku", "Ehime University", "Ehime University", "愛媛大学"], + SAML1SSOurl: "https://idp.cc.ehime-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://gidp.ait230.tokushima-u.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "Tokushima University", + search: ["https://gidp.ait230.tokushima-u.ac.jp/idp/shibboleth", "Shikoku", "Tokushima University", "Tokushima University", "徳島大学"], + SAML1SSOurl: "https://gidp.ait230.tokushima-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.kochi-ct.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "National Institute of Technology,Kochi College", + search: ["https://kidp.kochi-ct.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Kochi College", "National Institute of Technology,Kochi College", "高知工業高等専門学校"], + SAML1SSOurl: "https://kidp.kochi-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ktidp.kagawa-nct.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "National Institute of Technology,Kagawa College", + search: ["https://ktidp.kagawa-nct.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Kagawa College", "National Institute of Technology,Kagawa College", "香川高等専門学校"], + SAML1SSOurl: "https://ktidp.kagawa-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.yuge.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "National Institute of Technology,Yuge College", + search: ["https://kidp.yuge.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Yuge College", "National Institute of Technology,Yuge College", "弓削商船高等専門学校"], + SAML1SSOurl: "https://kidp.yuge.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.niihama-nct.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "National Institute of Technology,Niihama College", + search: ["https://kidp.niihama-nct.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Niihama College", "National Institute of Technology,Niihama College", "新居浜工業高等専門学校"], + SAML1SSOurl: "https://kidp.niihama-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.anan-nct.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "National Institute of Technology,Anan College", + search: ["https://kidp.anan-nct.ac.jp/idp/shibboleth", "Shikoku", "National Institute of Technology,Anan College", "National Institute of Technology,Anan College", "阿南工業高等専門学校"], + SAML1SSOurl: "https://kidp.anan-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kochi-tech.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "Kochi University of Technology", + search: ["https://idp.kochi-tech.ac.jp/idp/shibboleth", "Shikoku", "Kochi University of Technology", "Kochi University of Technology", "高知工科大学"], + SAML1SSOurl: "https://idp.kochi-tech.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://aries.naruto-u.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "Naruto University of Education", + search: ["https://aries.naruto-u.ac.jp/idp/shibboleth", "Shikoku", "Naruto University of Education", "Naruto University of Education", "鳴門教育大学"], + SAML1SSOurl: "https://aries.naruto-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp1.matsuyama-u.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "Matsuyama University", + search: ["https://idp1.matsuyama-u.ac.jp/idp/shibboleth", "Shikoku", "Matsuyama University", "Matsuyama University", "松山大学"], + SAML1SSOurl: "https://idp1.matsuyama-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kochi-u.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "Kochi University", + search: ["https://idp.kochi-u.ac.jp/idp/shibboleth", "Shikoku", "Kochi University", "Kochi University", "高知大学"], + SAML1SSOurl: "https://idp.kochi-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.itc.kagawa-u.ac.jp/idp/shibboleth": { + type: "shikoku", + name: "Kagawa University", + search: ["https://idp.itc.kagawa-u.ac.jp/idp/shibboleth", "Shikoku", "Kagawa University", "Kagawa University", "香川大学"], + SAML1SSOurl: "https://idp.itc.kagawa-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ssoidp.cc.saga-u.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Saga University", + search: ["https://ssoidp.cc.saga-u.ac.jp/idp/shibboleth", "Kyushu", "Saga University", "Saga University", "佐賀大学"], + SAML1SSOurl: "https://ssoidp.cc.saga-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.isc.kyutech.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Kyushu Institute of Technology", + search: ["https://idp.isc.kyutech.ac.jp/idp/shibboleth", "Kyushu", "Kyushu Institute of Technology", "Kyushu Institute of Technology", "九州工業大学"], + SAML1SSOurl: "https://idp.isc.kyutech.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kyushu-u.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Kyushu University", + search: ["https://idp.kyushu-u.ac.jp/idp/shibboleth", "Kyushu", "Kyushu University", "Kyushu University", "九州大学"], + SAML1SSOurl: "https://idp.kyushu-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://um-idp.cc.miyazaki-u.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "University of Miyazaki", + search: ["https://um-idp.cc.miyazaki-u.ac.jp/idp/shibboleth", "Kyushu", "University of Miyazaki", "University of Miyazaki", "宮崎大学"], + SAML1SSOurl: "https://um-idp.cc.miyazaki-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://fed.u-ryukyu.ac.jp/shibboleth": { + type: "kyushu", + name: "University of the Ryukyus", + search: ["https://fed.u-ryukyu.ac.jp/shibboleth", "Kyushu", "University of the Ryukyus", "University of the Ryukyus", "琉球大学"], + SAML1SSOurl: "https://fed.u-ryukyu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.kct.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Technology,Kitakyushu College", + search: ["https://kidp.kct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Kitakyushu College", "National Institute of Technology,Kitakyushu College", "北九州工業高等専門学校"], + SAML1SSOurl: "https://kidp.kct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shibboleth-idp.bene.fit.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Fukuoka Institute of Technology", + search: ["https://shibboleth-idp.bene.fit.ac.jp/idp/shibboleth", "Kyushu", "Fukuoka Institute of Technology", "Fukuoka Institute of Technology", "福岡工業大学"], + SAML1SSOurl: "https://shibboleth-idp.bene.fit.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shib.kumamoto-u.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Kumamoto University", + search: ["https://shib.kumamoto-u.ac.jp/idp/shibboleth", "Kyushu", "Kumamoto University", "Kumamoto University", "熊本大学"], + SAML1SSOurl: "https://shib.kumamoto-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.oita-ct.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Technology,Oita College", + search: ["https://kidp.oita-ct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Oita College", "National Institute of Technology,Oita College", "大分工業高等専門学校"], + SAML1SSOurl: "https://kidp.oita-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.sasebo.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Technology,Sasebo College", + search: ["https://kidp.sasebo.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Sasebo College", "National Institute of Technology,Sasebo College", "佐世保工業高等専門学校"], + SAML1SSOurl: "https://kidp.sasebo.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.kagoshima-ct.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Technology,Kagoshima College", + search: ["https://kidp.kagoshima-ct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Kagoshima College", "National Institute of Technology,Kagoshima College", "鹿児島工業高等専門学校"], + SAML1SSOurl: "https://kidp.kagoshima-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.kurume-nct.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Technology,Kurume College", + search: ["https://kidp.kurume-nct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Kurume College", "National Institute of Technology,Kurume College", "久留米工業高等専門学校"], + SAML1SSOurl: "https://kidp.kurume-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.miyakonojo-nct.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Technology,Miyakonojo College", + search: ["https://kidp.miyakonojo-nct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Miyakonojo College", "National Institute of Technology,Miyakonojo College", "都城工業高等専門学校"], + SAML1SSOurl: "https://kidp.miyakonojo-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.ariake-nct.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Technology,Ariake College", + search: ["https://kidp.ariake-nct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Ariake College", "National Institute of Technology,Ariake College", "有明工業高等専門学校"], + SAML1SSOurl: "https://kidp.ariake-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.kumamoto-nct.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Technology,Kumamoto College", + search: ["https://kidp.kumamoto-nct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Kumamoto College", "National Institute of Technology,Kumamoto College", "熊本高等専門学校"], + SAML1SSOurl: "https://kidp.kumamoto-nct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.okinawa-ct.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Technology,Okinawa College", + search: ["https://kidp.okinawa-ct.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Technology,Okinawa College", "National Institute of Technology,Okinawa College", "沖縄工業高等専門学校"], + SAML1SSOurl: "https://kidp.okinawa-ct.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://shibboleth-idp.kyusan-u.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Kyushu Sangyo University", + search: ["https://shibboleth-idp.kyusan-u.ac.jp/idp/shibboleth", "Kyushu", "Kyushu Sangyo University", "Kyushu Sangyo University", "九州産業大学"], + SAML1SSOurl: "https://shibboleth-idp.kyusan-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://logon.oist.jp/idp/shibboleth": { + type: "kyushu", + name: "Okinawa Institute of Science and Technology Graduate University", + search: ["https://logon.oist.jp/idp/shibboleth", "Kyushu", "Okinawa Institute of Science and Technology Graduate University", "Okinawa Institute of Science and Technology Graduate University", "沖縄科学技術大学院大学"], + SAML1SSOurl: "https://logon.oist.jp/idp/profile/Shibboleth/SSO" + }, + "https://nuidp.nagasaki-u.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Nagasaki University", + search: ["https://nuidp.nagasaki-u.ac.jp/idp/shibboleth", "Kyushu", "Nagasaki University", "Nagasaki University", "長崎大学"], + SAML1SSOurl: "https://nuidp.nagasaki-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.seinan-gu.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Seinan Gakuin University", + search: ["https://idp.seinan-gu.ac.jp/idp/shibboleth", "Kyushu", "Seinan Gakuin University", "Seinan Gakuin University", "西南学院大学"], + SAML1SSOurl: "https://idp.seinan-gu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.net.oita-u.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Oita University", + search: ["https://idp.net.oita-u.ac.jp/idp/shibboleth", "Kyushu", "Oita University", "Oita University", "大分大学"], + SAML1SSOurl: "https://idp.net.oita-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://kidp.cc.kagoshima-u.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Kagoshima University", + search: ["https://kidp.cc.kagoshima-u.ac.jp/idp/shibboleth", "Kyushu", "Kagoshima University", "Kagoshima University", "鹿児島大学"], + SAML1SSOurl: "https://kidp.cc.kagoshima-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.nifs-k.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "National Institute of Fitness and Sports in KANOYA", + search: ["https://idp.nifs-k.ac.jp/idp/shibboleth", "Kyushu", "National Institute of Fitness and Sports in KANOYA", "National Institute of Fitness and Sports in KANOYA", "鹿屋体育大学"], + SAML1SSOurl: "https://idp.nifs-k.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://ss.fukuoka-edu.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "University of Teacher Education Fukuoka", + search: ["https://ss.fukuoka-edu.ac.jp/idp/shibboleth", "Kyushu", "University of Teacher Education Fukuoka", "University of Teacher Education Fukuoka", "福岡教育大学"], + SAML1SSOurl: "https://ss.fukuoka-edu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.sun.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "University of Nagasaki", + search: ["https://idp.sun.ac.jp/idp/shibboleth", "Kyushu", "University of Nagasaki", "University of Nagasaki", "長崎県立大学"], + SAML1SSOurl: "https://idp.sun.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.okiu.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Okinawa International University", + search: ["https://idp.okiu.ac.jp/idp/shibboleth", "Kyushu", "Okinawa International University", "Okinawa International University", "沖縄国際大学"], + SAML1SSOurl: "https://idp.okiu.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.sojo-u.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "SOJO University", + search: ["https://idp.sojo-u.ac.jp/idp/shibboleth", "Kyushu", "SOJO University", "SOJO University", "崇城大学"], + SAML1SSOurl: "https://idp.sojo-u.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.kurume-it.ac.jp/idp/shibboleth": { + type: "kyushu", + name: "Kurume Institute of Technology", + search: ["https://idp.kurume-it.ac.jp/idp/shibboleth", "Kyushu", "Kurume Institute of Technology", "Kurume Institute of Technology", "久留米工業大学"], + SAML1SSOurl: "https://idp.kurume-it.ac.jp/idp/profile/Shibboleth/SSO" + }, + "https://idp.gakunin.nii.ac.jp/idp/shibboleth": { + type: "others", + name: "GakuNin IdP", + search: ["https://idp.gakunin.nii.ac.jp/idp/shibboleth", "Others", "GakuNin IdP", "GakuNin IdP", "学認IdP"], + SAML1SSOurl: "https://idp.gakunin.nii.ac.jp/idp/profile/Shibboleth/SSO" + } }; var wayf_hint_list = []; var inc_search_list = []; @@ -1494,7 +1500,7 @@ var sp_domain = "https://idp.repo.nii.ac.jp"; var wayf_sp_entityID = sp_domain + "/shibboleth-sp"; var wayf_sp_handlerURL = sp_domain + "/Shibboleth.sso"; var wayf_return_url = sp_domain + "/loginproxy/getAuthInfo"; -var wayf_discofeed_url = "https://ds.gakunin.nii.ac.jp/DiscoFeed/PS0054JP"; +var wayf_discofeed_url = "https://ds.gakunin.nii.ac.jp/DiscoFeed/PS0054JP"; var wayf_width = "390"; var wayf_background_color = '#F8F8FF'; var wayf_font_color = '#000080'; @@ -1503,3843 +1509,3843 @@ var wayf_border_color = '#214890'; // Define functions function submitForm() { - var NonFedEntityID; - var idp_name = document.getElementById('keytext').value.toLowerCase(); - var chkFlg = false; - if (hiddenKeyText != '') idp_name = hiddenKeyText.toLowerCase(); - - if (inc_search_list.length > 0) { - submit_check_list = inc_search_list; - } - if (favorite_list.length > 0) { - submit_check_list = favorite_list.concat(submit_check_list); - } - if (hint_list.length > 0) { - submit_check_list = hint_list.concat(submit_check_list); - } - - for (var i = 0; i < submit_check_list.length; i++) { - for (var j = 3, len2 = submit_check_list[i].length; j < len2; j++) { - var list_idp_name = submit_check_list[i][j].toLowerCase(); - if (idp_name == list_idp_name) { - NonFedEntityID = submit_check_list[i][0]; - document.getElementById('user_idp').value = submit_check_list[i][0]; - chkFlg = true; - if (safekind > 0 && safekind != 3) { - // Store SAML domain cookie for this foreign IdP - setCookie('_saml_idp', encodeBase64(submit_check_list[i][0]), 100); - } - break; - } - } - if (chkFlg) { - break; - } + var NonFedEntityID; + var idp_name = document.getElementById('keytext').value.toLowerCase(); + var chkFlg = false; + if (hiddenKeyText != '') idp_name = hiddenKeyText.toLowerCase(); + + if (inc_search_list.length > 0) { + submit_check_list = inc_search_list; + } + if (favorite_list.length > 0) { + submit_check_list = favorite_list.concat(submit_check_list); + } + if (hint_list.length > 0) { + submit_check_list = hint_list.concat(submit_check_list); + } + + for (var i = 0; i < submit_check_list.length; i++) { + for (var j = 3, len2 = submit_check_list[i].length; j < len2; j++) { + var list_idp_name = submit_check_list[i][j].toLowerCase(); + if (idp_name == list_idp_name) { + NonFedEntityID = submit_check_list[i][0]; + document.getElementById('user_idp').value = submit_check_list[i][0]; + chkFlg = true; + if (safekind > 0 && safekind != 3) { + // Store SAML domain cookie for this foreign IdP + setCookie('_saml_idp', encodeBase64(submit_check_list[i][0]), 100); + } + break; + } } - if (!chkFlg) { - alert('You must select a valid Home Organisation.'); - return false; + if (chkFlg) { + break; } + } + if (!chkFlg) { + alert('You must select a valid Home Organisation.'); + return false; + } - // User chose non-federation IdP - // TODO: FIX windows error - // 4 >= (8 - 3/4) - if ( - i >= (submit_check_list.length - wayf_additional_idps.length)) { - - var redirect_url; + // User chose non-federation IdP + // TODO: FIX windows error + // 4 >= (8 - 3/4) + if ( + i >= (submit_check_list.length - wayf_additional_idps.length)) { - // Store SAML domain cookie for this foreign IdP - setCookie('_saml_idp', encodeBase64(NonFedEntityID), 100); + var redirect_url; - // Redirect user to SP handler - if (wayf_use_discovery_service) { - redirect_url = wayf_sp_samlDSURL + (wayf_sp_samlDSURL.indexOf('?') >= 0 ? '&' : '?') + 'entityID=' + - encodeURIComponent(NonFedEntityID) + - '&target=' + encodeURIComponent(wayf_return_url); + // Store SAML domain cookie for this foreign IdP + setCookie('_saml_idp', encodeBase64(NonFedEntityID), 100); - // Make sure the redirect always is being done in parent window - if (window.parent) { - window.parent.location = redirect_url; - } else { - window.location = redirect_url; - } + // Redirect user to SP handler + if (wayf_use_discovery_service) { + redirect_url = wayf_sp_samlDSURL + (wayf_sp_samlDSURL.indexOf('?') >= 0 ? '&' : '?') + 'entityID=' + + encodeURIComponent(NonFedEntityID) + + '&target=' + encodeURIComponent(wayf_return_url); - } else { - redirect_url = wayf_sp_handlerURL + '?providerId=' + - encodeURIComponent(NonFedEntityID) + - '&target=' + encodeURIComponent(wayf_return_url); + // Make sure the redirect always is being done in parent window + if (window.parent) { + window.parent.location = redirect_url; + } else { + window.location = redirect_url; + } - // Make sure the redirect always is being done in parent window - if (window.parent) { - window.parent.location = redirect_url; - } else { - window.location = redirect_url; - } + } else { + redirect_url = wayf_sp_handlerURL + '?providerId=' + + encodeURIComponent(NonFedEntityID) + + '&target=' + encodeURIComponent(wayf_return_url); - } + // Make sure the redirect always is being done in parent window + if (window.parent) { + window.parent.location = redirect_url; + } else { + window.location = redirect_url; + } - // If input type button is used for submit, we must return false - return false; - } else { - if (safekind == 0 || safekind == 3) { - // delete local cookie - setCookie('_saml_idp', encodeBase64(submit_check_list[i][0]), -1); - } - // User chose federation IdP entry - document.IdPList.submit(); } + + // If input type button is used for submit, we must return false return false; + } else { + if (safekind == 0 || safekind == 3) { + // delete local cookie + setCookie('_saml_idp', encodeBase64(submit_check_list[i][0]), -1); + } + // User chose federation IdP entry + document.IdPList.submit(); + } + return false; } function writeHTML(a) { - wayf_html += a; + wayf_html += a; } function pushIncSearchList(IdP) { - inc_search_list.push(wayf_idps[IdP].search.slice()); - for (var i in wayf_hint_list) { - if (wayf_hint_list[i] == IdP) { - hint_list.push(wayf_idps[IdP].search.slice()); - hint_list[hint_list.length - 1][1] = hint_idp_group; - } + inc_search_list.push(wayf_idps[IdP].search.slice()); + for (var i in wayf_hint_list) { + if (wayf_hint_list[i] == IdP) { + hint_list.push(wayf_idps[IdP].search.slice()); + hint_list[hint_list.length - 1][1] = hint_idp_group; } + } } function isAllowedType(IdP, type) { - for (var i = 0; i <= wayf_hide_categories.length; i++) { - - if (wayf_hide_categories[i] == type || wayf_hide_categories[i] == "all") { - - for (var i = 0; i <= wayf_unhide_idps.length; i++) { - // Show IdP if it has to be unhidden - if (wayf_unhide_idps[i] == IdP) { - return true; - } - } - // If IdP is not unhidden, the default applies - return false; + for (var i = 0; i <= wayf_hide_categories.length; i++) { + + if (wayf_hide_categories[i] == type || wayf_hide_categories[i] == "all") { + + for (var i = 0; i <= wayf_unhide_idps.length; i++) { + // Show IdP if it has to be unhidden + if (wayf_unhide_idps[i] == IdP) { + return true; } + } + // If IdP is not unhidden, the default applies + return false; } + } - // Category was not hidden - return true; + // Category was not hidden + return true; } function isAllowedCategory(category) { - if (!category || category == '') { - return true; - } + if (!category || category == '') { + return true; + } - for (var i = 0; i <= wayf_hide_categories.length; i++) { + for (var i = 0; i <= wayf_hide_categories.length; i++) { - if (wayf_hide_categories[i] == category || wayf_hide_categories[i] == "all") { - return false; - } + if (wayf_hide_categories[i] == category || wayf_hide_categories[i] == "all") { + return false; } + } - // Category was not hidden - return true; + // Category was not hidden + return true; } function isAllowedIdP(IdP) { - for (var i = 0; i <= wayf_hide_idps.length; i++) { - if (wayf_hide_idps[i] == IdP) { - return false; - } + for (var i = 0; i <= wayf_hide_idps.length; i++) { + if (wayf_hide_idps[i] == IdP) { + return false; } - // IdP was not hidden - return true; + } + // IdP was not hidden + return true; } function setCookie(c_name, value, expiredays) { - var exdate = new Date(); - exdate.setDate(exdate.getDate() + expiredays); - document.cookie = c_name + "=" + escape(value) + - ((expiredays == null) ? "" : "; expires=" + exdate.toGMTString()) + - ((wayf_sp_cookie_path == "") ? "" : "; path=" + wayf_sp_cookie_path) + "; secure"; + var exdate = new Date(); + exdate.setDate(exdate.getDate() + expiredays); + document.cookie = c_name + "=" + escape(value) + + ((expiredays == null) ? "" : "; expires=" + exdate.toGMTString()) + + ((wayf_sp_cookie_path == "") ? "" : "; path=" + wayf_sp_cookie_path) + "; secure"; } function getCookie(check_name) { - // First we split the cookie up into name/value pairs - // Note: document.cookie only returns name=value, not the other components - var a_all_cookies = document.cookie.split(';'); - var a_temp_cookie = ''; - var cookie_name = ''; - var cookie_value = ''; - - for (var i = 0; i < a_all_cookies.length; i++) { - // now we'll split apart each name=value pair - a_temp_cookie = a_all_cookies[i].split('='); - - - // and trim left/right whitespace while we're at it - cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, ''); - - // if the extracted name matches passed check_name - if (cookie_name == check_name) { - // We need to handle case where cookie has no value but exists (no = sign, that is): - if (a_temp_cookie.length > 1) { - cookie_value = unescape(a_temp_cookie[1].replace(/^\s+|\s+$/g, '')); - } - // note that in cases where cookie is initialized but no value, null is returned - return cookie_value; - break; - } - a_temp_cookie = null; - cookie_name = ''; + // First we split the cookie up into name/value pairs + // Note: document.cookie only returns name=value, not the other components + var a_all_cookies = document.cookie.split(';'); + var a_temp_cookie = ''; + var cookie_name = ''; + var cookie_value = ''; + + for (var i = 0; i < a_all_cookies.length; i++) { + // now we'll split apart each name=value pair + a_temp_cookie = a_all_cookies[i].split('='); + + + // and trim left/right whitespace while we're at it + cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, ''); + + // if the extracted name matches passed check_name + if (cookie_name == check_name) { + // We need to handle case where cookie has no value but exists (no = sign, that is): + if (a_temp_cookie.length > 1) { + cookie_value = unescape(a_temp_cookie[1].replace(/^\s+|\s+$/g, '')); + } + // note that in cases where cookie is initialized but no value, null is returned + return cookie_value; + break; } + a_temp_cookie = null; + cookie_name = ''; + } - return null; + return null; } // Checks if there exists a cookie containing check_name in its name function isCookie(check_name) { - // First we split the cookie up into name/value pairs - // Note: document.cookie only returns name=value, not the other components - var a_all_cookies = document.cookie.split(';'); - var a_temp_cookie = ''; - var cookie_name = ''; + // First we split the cookie up into name/value pairs + // Note: document.cookie only returns name=value, not the other components + var a_all_cookies = document.cookie.split(';'); + var a_temp_cookie = ''; + var cookie_name = ''; - for (var i = 0; i < a_all_cookies.length; i++) { - // now we'll split apart each name=value pair - a_temp_cookie = a_all_cookies[i].split('='); + for (var i = 0; i < a_all_cookies.length; i++) { + // now we'll split apart each name=value pair + a_temp_cookie = a_all_cookies[i].split('='); - // and trim left/right whitespace while we're at it - cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, ''); + // and trim left/right whitespace while we're at it + cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, ''); - // if the extracted name matches passed check_name + // if the extracted name matches passed check_name - if (cookie_name.search(check_name) >= 0) { - return true; - } + if (cookie_name.search(check_name) >= 0) { + return true; } + } - // Shibboleth session cookie has not been found - return false; + // Shibboleth session cookie has not been found + return false; } function encodeBase64(input) { - var base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - var output = "", - c1, c2, c3, e1, e2, e3, e4; - - for (var i = 0; i < input.length;) { - c1 = input.charCodeAt(i++); - c2 = input.charCodeAt(i++); - c3 = input.charCodeAt(i++); - e1 = c1 >> 2; - e2 = ((c1 & 3) << 4) + (c2 >> 4); - e3 = ((c2 & 15) << 2) + (c3 >> 6); - e4 = c3 & 63; - if (isNaN(c2)) { - e3 = e4 = 64; - } else if (isNaN(c3)) { - e4 = 64; - } - output += base64chars.charAt(e1) + base64chars.charAt(e2) + base64chars.charAt(e3) + base64chars.charAt(e4); + var base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var output = "", + c1, c2, c3, e1, e2, e3, e4; + + for (var i = 0; i < input.length;) { + c1 = input.charCodeAt(i++); + c2 = input.charCodeAt(i++); + c3 = input.charCodeAt(i++); + e1 = c1 >> 2; + e2 = ((c1 & 3) << 4) + (c2 >> 4); + e3 = ((c2 & 15) << 2) + (c3 >> 6); + e4 = c3 & 63; + if (isNaN(c2)) { + e3 = e4 = 64; + } else if (isNaN(c3)) { + e4 = 64; } + output += base64chars.charAt(e1) + base64chars.charAt(e2) + base64chars.charAt(e3) + base64chars.charAt(e4); + } - return output; + return output; } function decodeBase64(input) { - var base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - var output = "", - chr1, chr2, chr3, enc1, enc2, enc3, enc4; - var i = 0; + var base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var output = "", + chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; - // Remove all characters that are not A-Z, a-z, 0-9, +, /, or = - var base64test = /[^A-Za-z0-9\+\/\=]/g; - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + // Remove all characters that are not A-Z, a-z, 0-9, +, /, or = + var base64test = /[^A-Za-z0-9\+\/\=]/g; + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); - do { - enc1 = base64chars.indexOf(input.charAt(i++)); - enc2 = base64chars.indexOf(input.charAt(i++)); - enc3 = base64chars.indexOf(input.charAt(i++)); - enc4 = base64chars.indexOf(input.charAt(i++)); + do { + enc1 = base64chars.indexOf(input.charAt(i++)); + enc2 = base64chars.indexOf(input.charAt(i++)); + enc3 = base64chars.indexOf(input.charAt(i++)); + enc4 = base64chars.indexOf(input.charAt(i++)); - chr1 = (enc1 << 2) | (enc2 >> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; - output = output + String.fromCharCode(chr1); + output = output + String.fromCharCode(chr1); - if (enc3 != 64) { - output = output + String.fromCharCode(chr2); - } - if (enc4 != 64) { - output = output + String.fromCharCode(chr3); - } + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } - chr1 = chr2 = chr3 = ""; - enc1 = enc2 = enc3 = enc4 = ""; + chr1 = chr2 = chr3 = ""; + enc1 = enc2 = enc3 = enc4 = ""; - } while (i < input.length); + } while (i < input.length); - return output; + return output; } (function () { - var config_ok = true; - - // First lets make sure properties are available - if ( - typeof(wayf_use_discovery_service) == "undefined" || - typeof(wayf_use_discovery_service) != "boolean" - ) { - wayf_use_discovery_service = true; + var config_ok = true; + + // First lets make sure properties are available + if ( + typeof (wayf_use_discovery_service) == "undefined" || + typeof (wayf_use_discovery_service) != "boolean" + ) { + wayf_use_discovery_service = true; + } + + if (typeof (wayf_sp_entityID) == "undefined") { + alert('The mandatory parameter \'wayf_sp_entityID\' is missing. Please add it as a javascript variable on this page.'); + config_ok = false; + } + + if (typeof (wayf_URL) == "undefined") { + alert('The mandatory parameter \'wayf_URL\' is missing. Please add it as a javascript variable on this page.'); + config_ok = false; + } + + if (typeof (wayf_return_url) == "undefined") { + alert('The mandatory parameter \'wayf_return_url\' is missing. Please add it as a javascript variable on this page.'); + config_ok = false; + } + + if (typeof (wayf_discofeed_url) == "undefined") { + wayf_discofeed_url = ''; + } + + if (typeof (wayf_sp_cookie_path) == "undefined") { + wayf_sp_cookie_path = ''; + } + + if ((typeof (wayf_list_height) != "number") || (wayf_list_height < 0)) { + wayf_list_height = '150px'; + } else { + wayf_list_height += 'px'; + } + + if (wayf_use_discovery_service == false && typeof (wayf_sp_handlerURL) == "undefined") { + alert('The mandatory parameter \'wayf_sp_handlerURL\' is missing. Please add it as a javascript variable on this page.'); + config_ok = false; + } + + if (wayf_use_discovery_service == true && typeof (wayf_sp_samlDSURL) == "undefined") { + // Set to default DS handler + wayf_sp_samlDSURL = wayf_sp_handlerURL + "/DS"; + } + + if (typeof (wayf_sp_samlACURL) == "undefined") { + wayf_sp_samlACURL = wayf_sp_handlerURL + '/SAML/POST'; + } + + if (typeof (wayf_font_color) == "undefined") { + wayf_font_color = 'black'; + } + + if ( + typeof (wayf_font_size) == "undefined" || + typeof (wayf_font_size) != "number" + ) { + wayf_font_size = 12; + } + + if (typeof (wayf_border_color) == "undefined") { + wayf_border_color = '#00247D'; + } + + if (typeof (wayf_background_color) == "undefined") { + wayf_background_color = '#F4F7F7'; + } + + if ( + typeof (wayf_use_small_logo) == "undefined" || + typeof (wayf_use_small_logo) != "boolean" + ) { + wayf_use_small_logo = false; + } + + if ( + typeof (wayf_hide_logo) == "undefined" || + typeof (wayf_use_small_logo) != "boolean" + ) { + wayf_hide_logo = false; + } + + if (typeof (wayf_width) == "undefined") { + wayf_width = "auto"; + } else if (typeof (wayf_width) == "number") { + wayf_width += 'px'; + } + + if (typeof (wayf_height) == "undefined") { + wayf_height = "auto"; + } else if (typeof (wayf_height) == "number") { + wayf_height += "px"; + } + + if ( + typeof (wayf_show_remember_checkbox) == "undefined" || + typeof (wayf_show_remember_checkbox) != "boolean" + ) { + wayf_show_remember_checkbox = true; + } + + if ( + typeof (wayf_force_remember_for_session) == "undefined" || + typeof (wayf_force_remember_for_session) != "boolean" + ) { + wayf_force_remember_for_session = false; + } + + if ( + typeof (wayf_auto_login) == "undefined" || + typeof (wayf_auto_login) != "boolean" + ) { + wayf_auto_login = true; + } + + if ( + typeof (wayf_hide_after_login) == "undefined" || + typeof (wayf_hide_after_login) != "boolean" + ) { + wayf_hide_after_login = false; + } + + if (typeof (wayf_logged_in_messsage) == "undefined") { + wayf_logged_in_messsage = "You are already authenticated."; + } + + if ( + typeof (wayf_most_used_idps) == "undefined" || + typeof (wayf_most_used_idps) != "object" + ) { + wayf_most_used_idps = new Array(); + } + + if ( + typeof (wayf_show_categories) == "undefined" || + typeof (wayf_show_categories) != "boolean" + ) { + wayf_show_categories = true; + } + + if ( + typeof (wayf_hide_categories) == "undefined" || + typeof (wayf_hide_categories) != "object" + ) { + wayf_hide_categories = new Array(); + } + + if ( + typeof (wayf_unhide_idps) == "undefined" || + typeof (wayf_unhide_idps) != "object" + ) { + wayf_unhide_idps = new Array(); + } + + // Disable categories if IdPs are unhidden from hidden categories + if (wayf_unhide_idps.length > 0) { + wayf_show_categories = false; + } + + if ( + typeof (wayf_hide_idps) == "undefined" || + typeof (wayf_hide_idps) != "object" + ) { + wayf_hide_idps = new Array(); + } + + if ( + typeof (wayf_additional_idps) == "undefined" || + typeof (wayf_additional_idps) != "object" + ) { + wayf_additional_idps = []; + } + + // Exit without outputting html if config is not ok + if (config_ok != true) { + return; + } + + // Check if user is logged in already: + var user_logged_in = false; + if (typeof (wayf_check_login_state_function) == "undefined" || + typeof (wayf_check_login_state_function) != "function") { + // Use default Shibboleth Service Provider login check + user_logged_in = isCookie('shibsession'); + } else { + // Use custom function + user_logged_in = wayf_check_login_state_function(); + } + + // Check if user is authenticated already and + // whether something has to be drawn + if ( + wayf_hide_after_login && + user_logged_in && + wayf_logged_in_messsage == '' + ) { + + // Exit script without drawing + return; + } + + // Now start generating the HTML for outer box + if ( + wayf_hide_after_login && + user_logged_in + ) { + writeHTML('
'); + } else { + writeHTML('
'); + } + + + // Shall we display the logo + if (wayf_hide_logo != true) { + + // Write header of logo div + writeHTML(''); + } + + // Start login check + // Search for login state cookie + // If one exists, we only draw the logged_in_message + if ( + wayf_hide_after_login && + user_logged_in + ) { + writeHTML('

' + wayf_logged_in_messsage + '

'); + + } else { + // Else draw embedded WAYF + + //Do we have to draw custom text? or any text at all? + if (typeof (wayf_overwrite_intro_text) == "undefined") { + writeHTML('