Skip to content

Migrate Social Diagram reads to secured /api/v3/individuals/info/social-data#1635

Open
JasonWildMe wants to merge 13 commits into
fix/api-v3-individuals-getfrom
fix/social-diagram-api-v3-reads
Open

Migrate Social Diagram reads to secured /api/v3/individuals/info/social-data#1635
JasonWildMe wants to merge 13 commits into
fix/api-v3-individuals-getfrom
fix/social-diagram-api-v3-reads

Conversation

@JasonWildMe

Copy link
Copy Markdown
Collaborator

Summary

Migrates the legacy individuals.jsp Social Diagram read paths onto a single new, access-controlled /api/v3 endpoint, and rewrites the diagram's JavaScript to use it. The Social Diagram previously fetched its encounter table, relationship table, and per-partner details through the generic DataNucleus REST servlet (/api/jdoql, /api/{class}/{id}), which is unsuitable for entities with bidirectional relationships and does not apply Wildbook's collaboration/role access control. Loading the diagram for an individual could fail outright and could surface data without the usual view checks.

Scope: reads only. Relationship writes (add/edit/remove) and the bubble-graph data feed intentionally remain on the existing path and are untouched.

New endpoint

GET /api/v3/individuals/info/social-data?id={individualId} — a new branch in org.ecocean.api.MarkedIndividualInfo. No web.xml change, no OpenSearch serializer change.

Access control enforced server-side:

  • anonymous → 401; individual not viewable by the caller → 403.
  • encounter rows are included only for encounters the caller may view (Encounter.canUserView).
  • "occurring with" companions are filtered per each companion's own encounter visibility (not occurrence-level), so a co-occurring individual the caller can't see is never named.
  • relationship rows are included only when the caller may view the partner individual; the partner block is resolved server-side.

Response carries everything the two tables need in one round-trip (no per-partner fan-out). The relationship _id is the same datastore-identity value the prior API emitted, so the retained (legacy) edit/remove buttons keep resolving the correct row.

JavaScript

encounter-calls.js: getEncounterTableData and getRelationshipTableData now read the single new endpoint; the per-partner lookup helper is removed. Render logic and the edit/remove buttons are unchanged. (The file is CRLF in the repo and stays CRLF — the diff is the logical change only.)

Tests

MarkedIndividualInfoTest (JUnit 5 + Testcontainers, real ACL): per-encounter visibility filtering, the individual-level gate, co-occurrence companion filtering, relationship partner filtering, the relationship _id round-trip, dataTypes precedence, status codes, and a large-individual case exercising the memoized assembly paths. 12/12 green; module compiles.

Stacking / sequencing

This branch is stacked on fix/api-v3-individuals-get (PR #1633) — it relies on MarkedIndividual.canUserView introduced there — and is opened with that branch as its base so the diff is feature-only. Merge after #1633, or retarget to main once #1633 lands.

Follow-ups (not in this PR)

  • The relationship row could carry explicit selfRole/partnerRole to remove a client-side identifier comparison (robustness; current behavior preserved from the prior implementation).
  • Securing the bubble-graph companion feed and migrating the relationship write flow are separate efforts.

Design + plan: docs/superpowers/specs/2026-06-24-social-diagram-api-v3-reads-design.md, docs/superpowers/plans/2026-06-24-social-diagram-api-v3-reads.md.

🤖 Generated with Claude Code

JasonWildMe and others added 13 commits June 24, 2026 08:57
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… rel _id, partner resolution, dataTypes)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…per-companion-encounter ACL)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends MarkedIndividualInfo to handle args[2]=="social-data":
- 401 for anonymous callers (statusCode now included in 401 body)
- 400 for blank/missing id
- 404 for unknown individual
- 403 when canUserView returns false (collaborationSecurityEnabled)
- 200 with empty encounters/relationships arrays for viewable individual

Adds MarkedIndividualInfoTest: Testcontainers Postgres + MockedConstruction
pattern with real ACL evaluation (canUserView drives real MarkedIndividual
+ Encounter + Collaboration data against live test DB).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each encounter row in the social-data response now includes `occurringWith`:
a ", "-joined display string of co-occurring individuals' displayNames, gated
per-companion-encounter through encCanView (NEVER occurrence-level auth).
Adds occCache Map<String,String> to avoid repeated Occurrence loads.

Two new tests drive real ACL via the Testcontainers fixture:
  - occurringWithExcludesNonViewableCompanion (security leak guard)
  - occurringWithIncludesViewableCompanion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…compatible _id

- Implement relationship assembly in getSocialData: queries all Relationships
  for focal individual, filters out non-viewable partners, serializes each row
- Partner ACL uses canUserView with per-individual boolean cache to avoid
  repeated JDO traversals in a single request
- _id is the numeric datastore key (before the "[OID]..." suffix) so callers
  can roundtrip via getObjectById(Relationship.class, id)
- relationshipRow falls back to name-based partner lookup when object links
  are unpopulated
- Tests: relationshipIdRoundTripsThroughGetObjectById verifies _id resolves;
  relationshipToNonViewablePartnerOmitted verifies ACL filter hides partners
  whose encounters are owned by a non-collaborating user
- Mock Shepherd in invoke() now stubs getAllRelationshipsForMarkedIndividual
  to delegate to the backing real Shepherd

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n comment

Wrap dispatch+response-write in try/finally so myShepherd.rollbackDBTransaction()
and closeDBTransaction() are guaranteed even if getSocialData throws mid-traversal.
The 401 early-return path remains before the try block (no double-close).

Reword the relationshipDatastoreId comment to accurately describe what the test
verifies: bare-key round-trip only, not the full edit/remove _id+[OID] form.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@JasonWildMe JasonWildMe added this to the 10.12 milestone Jul 2, 2026
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.

1 participant