Release Connectors v0.1.22 with service-desk federation
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# Znuny and OTRS-compatible service-desk connector
|
||||
|
||||
## Boundary
|
||||
|
||||
Connectors owns the governed endpoint profile, GenericInterface REST transport,
|
||||
credential hand-off, provider discovery, bounded synchronization, external
|
||||
references, mapping diagnostics, health, and recovery evidence. It does not own
|
||||
ticket, helpdesk, or case semantics. A synchronized provider ticket remains an
|
||||
external service-desk ticket; creating or relating a GovOPlaN Ticket, Helpdesk
|
||||
item, or Case is the responsibility of the corresponding optional module.
|
||||
|
||||
The provider interface is `connectors.external_service_desk@1.0.0`, and the
|
||||
target-tested provider declaration is `connectors.znuny.tickets`.
|
||||
|
||||
## Governed configuration and routes
|
||||
|
||||
Create an active connector definition/configuration with:
|
||||
|
||||
- `provider`: `znuny`, `otrs`, or `znuny_otrs`;
|
||||
- `protocol`: `generic_interface_rest` (the aliases `rest` and
|
||||
`otrs_generic_interface_rest` are accepted);
|
||||
- an HTTP(S) endpoint that passes the central outbound-request policy; and
|
||||
- an optional scoped Core credential-envelope reference.
|
||||
|
||||
Znuny GenericInterface routes are configured by each deployment rather than
|
||||
being one universal product API. The profile therefore governs relative search,
|
||||
ticket-read, and optional update paths and their supported
|
||||
methods. Ticket and update paths must contain `{ticket_id}`. An optional
|
||||
absolute HTTP(S) browser URL template may contain `{ticket_id}` or
|
||||
`{ticket_number}`. `search_filters` carries up to 100 deployment-supported,
|
||||
secret-free GenericTicket search criteria such as queue identifiers. It cannot
|
||||
override synchronization bounds, ordering, change cursors, or authentication.
|
||||
Routes cannot change authority or contain credentials.
|
||||
|
||||
Header authentication is the recommended default. The adapter supports the
|
||||
documented `X-OTRS-Header-UserLogin`, `X-OTRS-Header-Password`,
|
||||
`X-OTRS-Header-SessionID`, and customer-login headers. A legacy credential may
|
||||
declare `auth_mode: body`, but then every affected route must use POST. Secret
|
||||
fields are never placed in a GET URL, persisted projection, diagnostic, or API
|
||||
response. Core also treats these provider headers as redirect-sensitive and
|
||||
removes them before following any cross-origin redirect.
|
||||
|
||||
Reference configuration examples:
|
||||
|
||||
- [Znuny GenericTicketConnectorREST example](https://doc.znuny.org/znuny/admin/webservices/examples/GenericTicketConnectorREST/index.html)
|
||||
- [Znuny provider and header authentication](https://doc.znuny.org/znuny-7_1/admin/webservices/provider/)
|
||||
- [Znuny web-service configuration](https://doc.znuny.org/znuny-7_3/admin/webservices/config/index.html)
|
||||
|
||||
## Profile policy
|
||||
|
||||
The integration and authority choices are intentionally separate but bounded:
|
||||
|
||||
| Integration mode | Allowed authority | Maturity |
|
||||
| --- | --- | --- |
|
||||
| `link` | `linked_reference` | `link` through `search` |
|
||||
| `import` | `external_authoritative`, `external_mirror` | exactly `read` |
|
||||
| `synchronize` | `external_authoritative`, `governed_sync` | exactly `synchronize` |
|
||||
|
||||
`link` retains stable identity and routing facts and asks the provider not to
|
||||
return articles, attachments, or dynamic fields; any unexpectedly returned
|
||||
content is still discarded. `import` is a deliberate bounded full snapshot and
|
||||
never silently changes to delta synchronization.
|
||||
`synchronize` completes a full reconciliation and then advances to overlap-safe
|
||||
change-time deltas.
|
||||
|
||||
Queue mappings govern inclusion, an optional opaque target queue reference, and
|
||||
tenant or restricted visibility with ACL tokens. A restricted default requires
|
||||
at least one ACL token. Provider-supplied `GovOPlaNVisibility` and
|
||||
`GovOPlaNACL` fields win when valid. Otherwise a reviewed queue mapping wins,
|
||||
then the profile default. Standard GenericInterface installations do not expose
|
||||
a portable ticket-ACL contract, so every fallback is visible as a diagnostic.
|
||||
|
||||
Dynamic-field mappings govern source name, optional target name, inclusion, and
|
||||
`string`, `number`, `boolean`, `date`, or `json` conversion. Conversion loss,
|
||||
unreturned configured fields, provider-specific ticket fields, synthesized
|
||||
identities, and truncation are structured diagnostics rather than silent loss.
|
||||
|
||||
## Discovery and synchronization
|
||||
|
||||
Discovery performs a bounded ticket search and records health, API family,
|
||||
route hash, the exact governed configuration revision/hash, product/version
|
||||
evidence, capabilities, maturity, and diagnostics. A changed endpoint,
|
||||
configuration revision, or route map invalidates that evidence: synchronization,
|
||||
and updates fail closed until discovery is repeated, while prior projections are
|
||||
invalidated and Search stays closed until a new full reconciliation verifies
|
||||
them. Integration, route, queue, or dynamic-field mapping changes also reset the
|
||||
cursor and require a full reconciliation. Recognized Znuny or OTRS major
|
||||
versions 6 and later can reach synchronization maturity. An unverified
|
||||
product/version stays at read maturity. An update route adds the technical
|
||||
`publish` capability, but does not override profile authority.
|
||||
|
||||
Full synchronization first obtains a stable ordered identity set, then reads at
|
||||
most 500 tickets per call. A continued full run stores its offset, identity-set
|
||||
fingerprint, and cumulative high-watermark. If the provider identity set changes
|
||||
mid-run, the cursor is rejected and the operator must restart the full run. A
|
||||
completed full run reconciles local removals and, for `synchronize` mode,
|
||||
transitions to an overlap-safe delta cursor.
|
||||
|
||||
An explicit `full` request always restarts at the beginning; `auto` continues a
|
||||
committed full cursor or advances a completed delta cursor. A caller-supplied
|
||||
cursor must exactly match the profile's committed cursor, and delta mode cannot
|
||||
bootstrap a profile that has not completed its full synchronization.
|
||||
|
||||
Delta synchronization reads the bounded candidate set, orders changes by
|
||||
provider change time and ticket id, and suppresses only the exact ticket
|
||||
revisions already observed at the current timestamp boundary. A previously seen
|
||||
ticket that has changed again is therefore not lost. Every run requires a
|
||||
profile-wide idempotency key. An exact replay returns the committed run without
|
||||
contacting the provider; reuse for a different request or changed profile policy
|
||||
is rejected.
|
||||
|
||||
Operational bounds:
|
||||
|
||||
- at most 10,000 ticket identities per profile search;
|
||||
- at most 500 ticket reads per API call;
|
||||
- at most 10 MB per provider response;
|
||||
- a 20-second outbound timeout; and
|
||||
- a 4,000-character cursor, including timestamp-boundary identities.
|
||||
|
||||
Partition larger or unusually bursty providers into queue-scoped profiles by
|
||||
combining provider-side queue `search_filters` with matching queue mappings. A
|
||||
full-run fingerprint conflict requires a restart. A timestamp-boundary overflow
|
||||
requires a narrower partition. These are explicit safety stops, not partial
|
||||
success claims.
|
||||
|
||||
## Mapping and attachment policy
|
||||
|
||||
Each ticket projection retains stable ticket id/number, title, type, queue,
|
||||
target queue reference, state, priority, owner, responsible user, customer user,
|
||||
organization, service, SLA, creation/change times, mapped dynamic fields,
|
||||
articles, attachment metadata, permission source, ACLs, canonical URL, content
|
||||
hash, provider revision, cursor, observation time, and source provenance.
|
||||
|
||||
Article and attachment references use Core `ExternalObjectReference` values.
|
||||
Article bodies are capped at 200,000 characters. Attachment content is never
|
||||
retained; only stable identity, filename, media type, size, disposition, content
|
||||
id, article/ticket relationship, version, and provenance are mapped. Returned
|
||||
bytes produce an `attachment_content_omitted` diagnostic.
|
||||
|
||||
## Search authorization
|
||||
|
||||
When Search is installed and both desired and discovered maturity permit it,
|
||||
active non-deleted ticket projections are indexed. Search documents carry the
|
||||
current visibility, ACL tokens, external reference, source revision, routing
|
||||
metadata, article text, and bounded dynamic-field keywords.
|
||||
|
||||
Authorization always fails closed unless all of these remain true:
|
||||
|
||||
- the requesting principal belongs to the exact tenant;
|
||||
- the principal has `connectors:service_desk:read`;
|
||||
- the profile remains active and search-capable;
|
||||
- the ticket remains active; and
|
||||
- tenant visibility applies or a current account, membership, identity, group,
|
||||
role, function, or scope ACL token intersects.
|
||||
|
||||
Pausing a profile or changing fallback/queue ACLs updates or removes Search
|
||||
projections immediately. Search also rechecks the current database state for
|
||||
every result, so a delayed index update does not grant access.
|
||||
|
||||
## Governed external updates and recovery
|
||||
|
||||
An external update is allowed only when the profile is active, authority is
|
||||
`governed_sync`, discovery recorded the `publish` capability, and the caller has
|
||||
`connectors:service_desk:update`. The request must include the synchronized
|
||||
provider revision and a new idempotency key. Supported governed fields are
|
||||
title, queue, state, priority, owner, responsible user, and explicitly mapped
|
||||
dynamic fields.
|
||||
|
||||
Before dispatch, the adapter refetches the ticket and rejects a stale revision.
|
||||
After dispatch, it refetches again and verifies both the changed revision and
|
||||
every requested field value. A transport failure before a conclusive provider
|
||||
response, an unchanged revision, or a requested value that cannot be confirmed
|
||||
becomes `outcome_unknown`. Do not retry with another key. Inspect the provider
|
||||
ticket and reconcile its accepted revision through the Core recovery evidence
|
||||
first. Local database rollback cannot undo a remote provider mutation.
|
||||
|
||||
## Administrator verification
|
||||
|
||||
1. Create the governed definition/configuration and scoped credential envelope.
|
||||
2. Create a restricted profile with reviewed routes, queue partitions, dynamic
|
||||
fields, authority, and fallback ACLs.
|
||||
3. Discover and confirm product/version, maturity, capabilities, and diagnostics.
|
||||
4. Finish a keyed full run; continue while its cursor kind is `full`.
|
||||
5. Run a new keyed automatic delta and inspect effects, losses, and health.
|
||||
6. Verify one allowed and one denied Search principal against a restricted ticket.
|
||||
7. If governed writes are enabled, update a non-production ticket with its
|
||||
current revision, then verify provider and recovery evidence.
|
||||
8. Reconcile every `outcome_unknown` run before any retry.
|
||||
Reference in New Issue
Block a user