[jira] [Closed] (JAMES-4210) Generic SASL mechanism extension model for James protocols

"Benoit Tellier (Jira)" <[email protected]>
Newsgroups gmane.comp.jakarta.james.devel
Message-ID <[email protected]>
     [ https://issues.apache.org/jira/browse/JAMES-4210?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Benoit Tellier closed JAMES-4210.
---------------------------------
    Resolution: Fixed

> Generic SASL mechanism extension model for James protocols
> ----------------------------------------------------------
>
>                 Key: JAMES-4210
>                 URL: https://issues.apache.org/jira/browse/JAMES-4210
>             Project: James Server
>          Issue Type: Improvement
>          Components: IMAPServer, POP3Server, sieve, SMTPServer
>    Affects Versions: 3.10
>            Reporter: Tran Hong Quan
>            Priority: Major
>          Time Spent: 17h 10m
>  Remaining Estimate: 0h
>
> I would like to propose a generic SASL mechanism extension model for James protocols.
> Today, when adding a new authentication mechanism for IMAP or SMTP, we need to modify core protocol code, such as IMAP _AuthenticateProcessor_ or SMTP {_}AuthCmdHandler{_}. This makes the protocol handlers accumulate mechanism-specific branches, and would make future mechanisms such as GSSAPI / Kerberos harder to add cleanly.
> The goal is:
>  - adding a new SASL mechanism should not require modifying core IMAP/SMTP command handlers;
>  - protocol code should keep protocol framing and side effects;
>  - SASL mechanism code should own mechanism semantics and exchange state;
>  - existing IMAP/SMTP behavior should remain unchanged when no new configuration is provided.
> This takes inspiration from JMAP authentication strategies configuration and Guice loading.
> *Configuration shape*
> -------------------
> Each protocol would keep an operator-visible _auth.saslMechanisms_ list.
> Example for IMAP:
>     _<auth>_
>       _<saslMechanisms>PlainSaslMechanism,OauthBearerSaslMechanism,XOauth2SaslMechanism,com.example.james.kerberos.GssapiSaslMechanism</saslMechanisms>_
>     _</auth>_
> Example for SMTP:
>     _<auth>_
>       _<saslMechanisms>LoginSaslMechanism,PlainSaslMechanism,OauthBearerSaslMechanism,XOauth2SaslMechanism,com.example.james.kerberos.GssapiSaslMechanism</saslMechanisms>_
>     _</auth>_
> Expected semantics:
>  - if _auth.saslMechanisms_ is absent, load current protocol defaults;
>  - IMAP defaults remain PLAIN, OAUTHBEARER, XOAUTH2;
>  - SMTP defaults remain LOGIN, PLAIN, OAUTHBEARER, XOAUTH2;
>  - if configured, load the configured list in the configured order;
>  - simple class names resolve against James default SASL packages;
>  - fully qualified class names allow external/community extensions;
> Existing protocol settings still decide availability. For example, plain auth restrictions still decide whether PLAIN is advertised, OIDC configuration still decides whether OAuth mechanisms are advertised, and SMTP _auth.announce_ still controls whether SMTP advertises AUTH.
> *Proposed SPI shape*
> ------------------
> I propose introducing protocol-neutral SASL types in {_}protocols/api{_}, for example under {_}org.apache.james.protocols.api.sasl{_}.
> The core idea is a stateful SaslExchange: each SASL mechanism creates one exchange per authentication attempt, and that exchange owns mechanism state across the initial request and continuation responses.
> Core structure:
>     _interface SaslMechanism {_
>         _String name();_
>         _boolean supports(SaslProtocol protocol);_
>         _boolean isAvailable(SaslSessionContext context);_
>         _SaslExchange start(SaslInitialRequest request, SaslSessionContext context);_
>     _}_
>     _enum SaslProtocol {_
>         _IMAP, SMTP, MANAGESIEVE, POP3_
>     _}_
>     _record SaslInitialRequest(SaslProtocol protocol, String mechanismName, Optional<byte[]> initialResponse) {_
>     _}_
>     _record SaslIdentity(Username authenticationId, Username authorizationId) {_
>     _}_
>     _interface SaslSessionContext {_
>         _SaslProtocol protocol();_
>         _boolean isTlsStarted();_
>         _<T> Optional<T> configuration(Class<T> configurationType);_
>     _}_
>     _interface SaslExchange extends AutoCloseable {_
>         _SaslStep firstStep();_
>         _SaslStep onResponse(byte[] clientResponse);_
>         _void abort();_
>         _@Override_
>         _void close();_
>     _}_
>     _interface SaslStep {_
>         _record Challenge(Optional<byte[]> payload) implements SaslStep {_
>         _}_
>         _record Success(SaslIdentity identity, Optional<byte[]> serverData, String log) implements SaslStep {_
>         _}_
>         _record Failure(String log) implements SaslStep {_
>         _}_
>     _}_
> The intended split is:
>  - SASL mechanisms own payload parsing, challenge generation, response validation, final SASL identity extraction, and exchange state.
>  - IMAP/SMTP bridges own base64/wire framing, continuation responses, success/failure session side effects, audit logs, hooks, and protocol-specific error responses.
> For example, PLAIN and OAUTHBEARER can complete from {_}firstStep(){_}, while GSSAPI can return Challenge first and complete later from {_}onResponse(...){_}.
> SaslIdentity carries both the authentication identity and the authorization identity. This keeps delegation expressible by the SPI without granting it automatically. IMAP/SMTP bridges still decide whether delegation is allowed and how to build their protocol session state.
> *Loading and registry*
> --------------------
> Mechanism loading would follow the same spirit as JMAP authentication strategies:
>     _interface SaslMechanismLoader {_
>         _ImmutableList<SaslMechanism> load(Collection<String> classNames);_
>     _}_
>     _class SaslMechanismRegistry {_
>         _Optional<SaslMechanism> find(String mechanismName, SaslProtocol protocol) {_
>             _// match by mechanism name and supports(protocol)_
>         _}_
>         _Stream<SaslMechanism> availableFor(SaslProtocol protocol, SaslSessionContext context) {_
>             _// filter supports(protocol) and isAvailable(context)_
>         _}_
>     _}_
> Then implement a GuiceSaslMechanismLoader to actually load the SASL mechanisms.
> *Incremental implementation plan*
> -------------------------------
> I suggest doing this step by step:
> - {_}Step 1{_}: Start with a POC introducing the shared SaslMechanism SPI and adapting IMAP.
>   This must not introduce breaking changes to existing authentication configs. The SASL SPI can be adopted gradually, protocol by protocol, without changing behavior for protocols that have not adopted it yet.
> - {_}Step 2{_}: Adapt SASL modularization for SMTP.
> - {_}Step 3{_}: Leverage the SASL modularization to implement GSSAPI / Kerberos mechanism support.
> ManageSieve and POP3 are not part of the immediate scope, but the SPI should make later adoption possible.
> *Testing expectations*
> --------------------
> At minimum, I think we should prove:
>  - no breaking change: absent auth.saslMechanisms keeps existing IMAP/SMTP SASL methods;
>  - configured custom SASL mechanism loads and authenticates through real or near-real protocol wiring;
>  - authentication / authorization identity handling preserves existing delegation behavior (we should already have tests for these);
>  - fake multi-step mechanism works for continuation;
>  - exchange cleanup is covered.
> *Feedback requested*
> ------------------
> Does this refactoring direction look reasonable to you? Feedback and discussion are welcome!
> Meanwhile, I would start POC work on my side to see how this design goes...
> Regards,
> Quan



--
This message was sent by Atlassian Jira
(v8.20.10#820010)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.