Skip to content

別プロジェクトのWikiを親に設定できないようにする - #812

Open
NishikawaKoharu wants to merge 53 commits into
RCOSDP:developfrom
NishikawaKoharu:feature/fix-wiki-cross-project-parent
Open

NishikawaKoharu wants to merge 53 commits into
RCOSDP:developfrom
NishikawaKoharu:feature/fix-wiki-cross-project-parent

Conversation

@NishikawaKoharu

Copy link
Copy Markdown

Purpose

Prevent a Wiki sort update from saving a parent page that belongs to another project.

project_update_wiki_page_sort resolved the parent with a global GUID lookup (WikiPage.objects.get(guids___id=parent_wiki_id)). A user with write access on their own project could therefore nest a local Wiki page under another project's Wiki if they knew its ID. The practical impact is limited (the other page's title can be read through the parent link), but the relationship should not be stored.

This PR accepts a parent only when it is an undeleted Wiki page on the same project, and returns 400 otherwise. The same node check also stops a Wiki ID URL on one project from resolving another project's page name.

Changes

  • Look up the sort parent with WikiPage.objects.get_for_node(node, id=...) instead of a global GUID query
  • Return 400 (Invalid parent wiki) when that parent is missing, deleted, or on another project, before bulk_update
  • Restrict WikiPageNodeManager.get_for_node by id to the same node and to non-deleted pages
  • Add tests for a cross-project parent on the sort API and for a 404 when a Wiki ID URL points at another project's page

QA Notes

  • Does this change require a data migration? No
  • Risk level: Low–medium (no new permission decorator; existing WRITE check on sort remains; invalid parents are rejected instead of saved)
  • How to verify (API / UI, no new API version):
    • POST /api/v1/project/<pid_A>/wiki/sort/update/ with a non-home page of project A nested under a Wiki ID from project B → expect 400 (Invalid parent wiki). Reload project A and confirm the parent did not change
    • Reordering or nesting pages inside the same project still saves
    • GET /<pid_A>/wiki/id/<wiki_id_from_B>/ → expect 404
    • GET /<pid_A>/wiki/id/<wiki_id_from_A>/ still redirects to that page
  • Impacted features: Wiki page sort / tree save, Wiki ID URLs (project_wiki_id_page)
  • Performance impact: Negligible (one GUID load plus a node check per parent)

Documentation

No documentation updates required.

Side Effects

A sort request that names a parent outside the project fails as a whole and does not save. Same-project parents, including Home, still work. A Wiki ID URL for a deleted page or another project's page now returns 404. Parents that were already saved across projects before this change are not rewritten.

Ticket

https://redmine.devops.rcos.nii.ac.jp/issues/63288

tma-hcphat and others added 30 commits April 29, 2026 09:23
…i_grdm_202603_step4/2.2_quota_calculation_when_move_file
 + Update email template handle move/copy file quota and max size error
 + Add script migrate missing file info and update quota used
 + Conflict resolve
 + Mail template render
 + Issue data.size is string type
…nt /custom_storage_location/external_acc_update/<access_token>/

/statistics/gather/<access_token>/
Replace the boto 2 client with boto3/botocore so that the S3 addon can talk
to regions that only accept Signature Version 4 (ap-northeast-1 and later).

- utils.py / views.py / models.py build the client through boto3 with a
  region-specific endpoint, and surface botocore exceptions as HTTPError
- folder_id becomes "bucket:/prefix/" so that a subfolder can be selected;
  the legacy "bucket" form is still accepted
- the node/user config JS, the Rubeus config and the log action lists follow
  the new folder shape
- requirements.txt gains boto3/botocore; addons/s3/requirements.txt is kept
  as an empty file because the Jenkins Dockerfile still does
  COPY ./addons/s3/requirements.txt and would fail the image build without it
- MAX_UPLOAD_SIZE stays at 50GB: multipart upload handles up to 5TB, so the
  lower limit the first draft introduced was unnecessary
- the ${ _("...") } gettext wrappers are kept in all three mako templates
  (20 translatable strings); only the aria-label/alt attributes and the th
  class change are new

Co-Authored-By: An Qiuyu <qiuyu.an@hotmail.com>
…lready registered

Registering the same access key twice has to end up on the existing
ExternalAccount instead of creating a second row. The duplicate is caught by
the unique constraint on (provider, provider_id) — provider_id holds
"{aws_account_id}\t{access_key}" (GRDM-53044) — so the save raises
IntegrityError, not the ValidationError the handler was catching, and the
failed INSERT aborted the surrounding transaction.

Wrap the save in transaction.atomic() so the failure only rolls back to a
savepoint, and catch IntegrityError alongside ValidationError before falling
back to the account that is already stored.
…igV4 folder shape

- test_view.py still used assert_equals/assert_in and a missing
  InstitutionFactory import left over from the nose era; replace them with
  plain pytest asserts against rv.text
- _mock_folder_result still returned the pre-SigV4 get_folders payload, so the
  node settings tests asserted against a shape the view no longer produces
Extend addons/s3/tests/test_view.py so the SigV4 flow is exercised end to end:

- both folder_id forms ("bucket" and "bucket:/prefix/") and a non-existent
  bucket raising InvalidFolderError
- the provider_id format "{aws_account_id}\t{access_key}" and the dedup path
  when the same key is registered twice (GRDM-53044)
- the error branches: a missing key field, get_user_info returning None, an
  invalid bucket name, an empty bucket_exists result, can_list returning None,
  and the ClientError / InvalidAuthError conversions

Three test-harness fixes come with it:

- query ExternalAccount by provider_id, a plain CharField. oauth_key is an
  EncryptedTextField (JWE), so its ciphertext differs on every save and
  filtering on it never matches; the plaintext is checked by attribute access
  instead
- use post_json/put_json instead of the json= keyword, which the webtest_plus
  version used in CI does not accept, and pass expect_errors=True wherever a
  4xx/401/403 response is expected
- patch get_user_info with mock.patch(..., return_value=mock.Mock(...)); the
  .return_value attribute on the patcher object never reaches the started mock,
  so user_info.id / user_info.display_name were missing in the view
…display name

- stop lower-casing the bucket name in the settings check. S3 bucket names are
  case-sensitive in the legacy us-east-1 naming rules, so folding the case can
  point the addon at a different bucket; validate_bucket_name already rejects
  the names that are actually invalid
- fall back gracefully when the owner returned by GetUserInfo has no display
  name, instead of raising while building the account label
- keep the Owner entry of the admin storage-location response in its original
  shape so that the existing consumers keep working
…rametrize

pytest.mark.parametrize has no effect on a method of a unittest.TestCase: the
case is collected once and the extra arguments are never supplied, so
test_serialize_settings_supports_legacy_and_prefixed_folder_ids errored on a
missing fixture instead of checking the three folder_id forms. Iterate over
the cases inside the test body instead.
tishin-endou and others added 14 commits October 2, 2026 15:30
get_bucket_location() returns a null LocationConstraint for us-east-1,
but BUCKET_LOCATIONS keys that region as '', so the null fell through to
set_folder and labelled the bucket '(None)' in the node settings UI.
The ListObjects call was unguarded, so expired or revoked keys and a
missing bucket escaped as a raw botocore exception and reached the user
as a 500 with no actionable message.  Map NoCredentialsError to 403 and
reuse get_status_for_error so a ClientError keeps the status S3 returned.
create_bucket only handled NoCredentialsError and ClientError, so the
non-response failures - bad parameters, an unreachable endpoint - left
the user with a 500.  ClientError is not a BotoCoreError subclass and
NoCredentialsError is matched earlier, so the existing clauses keep
their behaviour.
…se repositories with many indices were hard to scroll through
…cause WEKO names indices per language and returns the English one by default
Register R2026b at the head of MATLAB_RELEASES and in
MATLAB_PRODUCTNAMES_MAP, and add its mpm input file. It makes the
release available in the BinderHub addon.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mathworks-ref-arch/matlab-dockerfile@f419020 added a commented-out
supportPackageDestinationFolder section to these files. No product
lines changed, so the generated product name lists are unaffected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mport-api-permission

Feature/fix wiki import api permission
…0261013-#63370

Feature/binderhub 20261013 #63370
…p2/institutional_admin_api_authorization

Feature/nii grdm 202510 step2/institutional admin api authorization
…p4/2.2_quota_calculation_when_move_file_takeback

Feature/nii grdm 202603 step4/2.2 quota calculation when move file takeback
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants