Problem

Two Orchestra sites name the same person by login, and that is hard-coded. WireAccount turns a uid into a login on the way out and a login into a uid on the way in, and nothing else is possible: whoever a run belongs to crosses the boundary as an account name or not at all.

That fits one deployment and not the others. Two sites behind one identity provider know each other's people by an SSO subject, not by a local login. Two sites that sync their accounts know them by uuid. An organization running both sites knows its people by an employee number held in a field. In each of those the logins differ, so the name arrives and matches nobody, and the half of the work that runs on the other site is unreadable by the person it was started for.

Proposed resolution

A WireIdentity plugin type: one plugin is one way two sites name the same person, with two methods, one per direction. toWire() answers what to put on the wire for a local uid; fromWire() answers which local account a received name is, or none. WireAccount becomes the caller rather than the rule, and today's behaviour ships as the login plugin, which stays the default.

Both sides have to have the same plugin, so a pair of sites works out at the handshake what it can use. The handshake already runs between two sites and already carries a wire version. The receiving site answers with the schemes it will accept, the caller keeps the ones it also has, and it picks one of those as its default. An ordinary start then carries only the name.

A start may also say which plugin minted the name it carries, in a field of its own rather than encoded into it, for a pair that knows its people more than one way; without one the default applies. The id travels at all because a value read under the wrong scheme is worse than no value: an employee number read as a login can land on a real account named 40312.

What a site advertises is what it will accept from that caller, not what it happens to have installed. Capability and policy are different questions - a site can have the email scheme and still not let one partner match people by it - and answering with the allowed list makes every choice the caller can then make one the site will honour.

The receiving site checks each start against that list again rather than trusting what was agreed, and checks it before it resolves anything. The caller's copy is a cache, either side can uninstall a plugin afterwards, and a caller can claim whatever it likes. The order is the point twice over: resolving first would run a lookup the site said no to, and it would settle whether some account matches under a scheme this caller may not use, which a refusal that comes afterwards can no longer take back. An empty intersection fails the handshake, while an operator is setting the pair up and can see it; a start naming a scheme the site no longer accepts is refused in its own words, because what fixes that is another handshake and not another account.

A matcher is an access grant. Orchestra reads a run's initiator as ownership and admits it before the read-access scope is consulted, so a plugin that resolves loosely hands somebody a run that is not theirs. Every plugin matches exactly or answers nobody: a nearest-match plugin is not one this type can have.

API changes

The handshake answers with the schemes a caller may use, and the start can carry one beside the initiator, so OrchestraClientInterface::startProcess() takes it and the HTTP start accepts it. A call that names neither, against a site that agreed nothing, goes on meaning a login, which is what every caller sends today. WireAccount keeps its methods and stops holding the rule.

Data model changes

None stored on a run: the initiator is resolved to a local uid at the door, as it is now. New configuration on the remote entity for the scheme to send, and on the caller for the schemes to accept.

Remaining tasks

Depends on #3624458, which makes a name the receiving site cannot honour refuse the start rather than record nobody. That is what turns a scheme the far side does not have, or a name it cannot place, into something an operator is told about instead of something nobody ever learns.

AI-Generated: Yes (Claude Code was used to help draft this issue summary. I reviewed it myself before posting it.)

Issue fork orchestra-3624470

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mably created an issue. See original summary.

mably’s picture

Issue summary: View changes
mably’s picture

Issue summary: View changes

mably’s picture

Status: Active » Needs review

  • mably committed f64ac358 on 1.x
    feat: #3624470 Let a plugin decide how two sites name the same person...
mably’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.