Skip to content

Commit 2bcd3a9

Browse files
JAMES-4210 Introduce shared SASL SPI
Introduce a protocol-neutral SASL SPI in `protocols/api`. The new API models SASL as a stateful exchange with protocol-neutral initial requests, continuation steps, success/failure results, and authentication identities. It also exposes password and bearer-token authentication service contracts through `SaslSessionContext` so future mechanism implementations do not depend directly on IMAP or SMTP classes. Add contract tests covering one-step mechanisms, multi-step mechanisms, password-like authentication through the context service contract, delegated identities, defensive byte-array copying, and exchange cleanup.
1 parent bbce4ea commit 2bcd3a9

11 files changed

Lines changed: 802 additions & 0 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.protocols.api.sasl;
21+
22+
import org.apache.james.core.Username;
23+
24+
/**
25+
* Protocol-provided service used by bearer-token based SASL mechanisms.
26+
*/
27+
public interface BearerTokenSaslAuthenticationService {
28+
/**
29+
* Authenticates the token and returns the authenticated SASL identity.
30+
*/
31+
SaslAuthenticationResult authenticate(String token, Username authorizationId);
32+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.protocols.api.sasl;
21+
22+
import java.util.Optional;
23+
24+
import org.apache.james.core.Username;
25+
26+
/**
27+
* Protocol-provided service used by password based SASL mechanisms.
28+
*/
29+
public interface PasswordSaslAuthenticationService {
30+
/**
31+
* Authenticates the supplied credentials and returns the authenticated SASL identity.
32+
*/
33+
SaslAuthenticationResult authenticate(Username authenticationId, String password, Optional<Username> authorizationId);
34+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.protocols.api.sasl;
21+
22+
/**
23+
* Result returned by protocol-provided authentication services.
24+
*/
25+
public interface SaslAuthenticationResult {
26+
/**
27+
* Successful authentication result.
28+
*/
29+
record Success(SaslIdentity identity, String log) implements SaslAuthenticationResult {
30+
}
31+
32+
/**
33+
* Failed authentication result.
34+
*/
35+
record Failure(String log) implements SaslAuthenticationResult {
36+
}
37+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.protocols.api.sasl;
21+
22+
/**
23+
* Stateful SASL authentication exchange.
24+
*/
25+
public interface SaslExchange extends AutoCloseable {
26+
/**
27+
* Starts the exchange and returns the first server step.
28+
*/
29+
SaslStep firstStep();
30+
31+
/**
32+
* Continues the exchange with a decoded client response.
33+
*/
34+
SaslStep onResponse(byte[] clientResponse);
35+
36+
/**
37+
* Aborts the exchange after a client cancellation or protocol-level failure.
38+
*/
39+
void abort();
40+
41+
@Override
42+
void close();
43+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.protocols.api.sasl;
21+
22+
import org.apache.james.core.Username;
23+
24+
/**
25+
* SASL authentication and authorization identities.
26+
*
27+
* @param authenticationId identity proven by the SASL mechanism
28+
* @param authorizationId identity requested for protocol access
29+
*/
30+
public record SaslIdentity(Username authenticationId, Username authorizationId) {
31+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.protocols.api.sasl;
21+
22+
import java.util.Objects;
23+
import java.util.Optional;
24+
25+
/**
26+
* Protocol-neutral initial SASL request.
27+
*
28+
* @param protocol protocol receiving the SASL exchange
29+
* @param mechanismName requested SASL mechanism name
30+
* @param initialResponse decoded initial client response, when supplied by the client
31+
*/
32+
public record SaslInitialRequest(SaslProtocol protocol, String mechanismName, Optional<byte[]> initialResponse) {
33+
public SaslInitialRequest(SaslProtocol protocol, String mechanismName, Optional<byte[]> initialResponse) {
34+
Objects.requireNonNull(protocol);
35+
Objects.requireNonNull(mechanismName);
36+
Objects.requireNonNull(initialResponse);
37+
38+
this.protocol = protocol;
39+
this.mechanismName = mechanismName;
40+
this.initialResponse = initialResponse.map(byte[]::clone);
41+
}
42+
43+
/**
44+
* Returns a defensive copy of the decoded initial client response.
45+
*/
46+
public Optional<byte[]> initialResponse() {
47+
return initialResponse.map(byte[]::clone);
48+
}
49+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.protocols.api.sasl;
21+
22+
/**
23+
* Protocol-neutral SASL mechanism.
24+
*/
25+
public interface SaslMechanism {
26+
/**
27+
* Returns the SASL mechanism name advertised to clients.
28+
*/
29+
String name();
30+
31+
/**
32+
* Whether this mechanism can be used by the supplied protocol.
33+
*/
34+
boolean supports(SaslProtocol protocol);
35+
36+
/**
37+
* Whether this mechanism is currently usable for the supplied session context.
38+
*/
39+
boolean isAvailable(SaslSessionContext context);
40+
41+
/**
42+
* Starts a new SASL exchange for one client authentication attempt.
43+
*/
44+
SaslExchange start(SaslInitialRequest request, SaslSessionContext context);
45+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.protocols.api.sasl;
21+
22+
/**
23+
* Protocols that can host SASL authentication.
24+
*/
25+
public enum SaslProtocol {
26+
IMAP,
27+
SMTP,
28+
MANAGESIEVE,
29+
POP3
30+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.protocols.api.sasl;
21+
22+
import java.util.Optional;
23+
24+
/**
25+
* Protocol-provided context exposed to SASL mechanisms.
26+
*/
27+
public interface SaslSessionContext {
28+
/**
29+
* Protocol currently running the SASL exchange.
30+
*/
31+
SaslProtocol protocol();
32+
33+
/**
34+
* Whether TLS is active for the current session.
35+
*/
36+
boolean isTlsStarted();
37+
38+
/**
39+
* Looks up optional protocol or server configuration required by a mechanism.
40+
*/
41+
<T> Optional<T> configuration(Class<T> configurationType);
42+
43+
/**
44+
* Looks up protocol-provided services, such as password or bearer-token authentication.
45+
*/
46+
<T> Optional<T> service(Class<T> serviceType);
47+
}

0 commit comments

Comments
 (0)