[ 
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)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to