Author: btellier Date: Thu Dec 17 14:56:12 2015 New Revision: 1720575 URL: http://svn.apache.org/viewvc?rev=1720575&view=rev Log: JAMES-1618 Add a AUTHENTICATE command - RFC-5804 compliant
Added: james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/AuthenticateTest.java james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticate.test james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationProcessor.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/UnknownSaslMechanism.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/PlainAuthenticationProcessor.java Modified: james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/UnauthenticatedTest.java james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/unauthenticate.test james/project/trunk/mpt/impl/managesieve/file/src/test/java/org/apache/james/mpt/managesieve/file/ManageSieveFileTest.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationException.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/Session.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/Authenticate.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/CoreCommands.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/CoreProcessor.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCore.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCoreToLine.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ManageSieveProcessor.java james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/util/SettableSession.java james/project/trunk/protocols/managesieve/src/test/java/org/apache/james/managesieve/core/CoreProcessorTestCase.java Added: james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/AuthenticateTest.java URL: http://svn.apache.org/viewvc/james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/AuthenticateTest.java?rev=1720575&view=auto ============================================================================== --- james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/AuthenticateTest.java (added) +++ james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/AuthenticateTest.java Thu Dec 17 14:56:12 2015 @@ -0,0 +1,47 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.mpt.testsuite; + +import com.google.inject.Inject; +import org.apache.james.mpt.host.ManageSieveHostSystem; +import org.junit.Before; +import org.junit.Test; + +import java.util.Locale; + +public class AuthenticateTest extends ManageSieveMPTTest { + + @Inject + private static ManageSieveHostSystem hostSystem; + + public AuthenticateTest() throws Exception { + super(hostSystem); + } + + @Before + public void setUp() throws Exception { + super.setUp(); + } + + @Test + public void authenticateShouldWork() throws Exception { + scriptTest("authenticate", Locale.US); + } +} Modified: james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/UnauthenticatedTest.java URL: http://svn.apache.org/viewvc/james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/UnauthenticatedTest.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/UnauthenticatedTest.java (original) +++ james/project/trunk/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/UnauthenticatedTest.java Thu Dec 17 14:56:12 2015 @@ -21,6 +21,7 @@ package org.apache.james.mpt.testsuite; import com.google.inject.Inject; import org.apache.james.mpt.host.ManageSieveHostSystem; +import org.junit.Before; import org.junit.Test; import java.util.Locale; @@ -34,6 +35,11 @@ public class UnauthenticatedTest extends super(hostSystem); } + @Before + public void setUp() throws Exception { + super.setUp(); + } + @Test public void unauthenticatedCommandShouldWork() throws Exception { scriptTest("unauthenticate", Locale.US); Added: james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticate.test URL: http://svn.apache.org/viewvc/james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticate.test?rev=1720575&view=auto ============================================================================== --- james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticate.test (added) +++ james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticate.test Thu Dec 17 14:56:12 2015 @@ -0,0 +1,37 @@ +################################################################ +# Licensed to the Apache Software Foundation (ASF) under one # +# or more contributor license agreements. See the NOTICE file # +# distributed with this work for additional information # +# regarding copyright ownership. The ASF licenses this file # +# to you under the Apache License, Version 2.0 (the # +# "License"); you may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, # +# software distributed under the License is distributed on an # +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # +# KIND, either express or implied. See the License for the # +# specific language governing permissions and limitations # +# under the License. # +################################################################ + +C: AUTHENTICATE +S: NO ManageSieve syntax is incorrect : You must specify a SASL mechanism as an argument of AUTHENTICATE command + +C: AUTHENTICATE UNKNOWN +S: NO Unknown SASL mechanism UNKNOWN + +C: AUTHENTICATE "PLAIN" +S: \+ "" +C: GETSCRIPT toto.sieve +S: NO ManageSieve syntax is incorrect : You must supply a password for the authentication mechanism. Formal syntax : <NULL>username<NULL>password + +C: tin password +S: NO authentication failed + +C: AUTHENTICATE "PLAIN" +S: \+ "" +C: user password +S: OK authentication successfull \ No newline at end of file Modified: james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/unauthenticate.test URL: http://svn.apache.org/viewvc/james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/unauthenticate.test?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/unauthenticate.test (original) +++ james/project/trunk/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/unauthenticate.test Thu Dec 17 14:56:12 2015 @@ -17,10 +17,25 @@ # under the License. # ################################################################ +C: GETSCRIPT any +S: NO + C: UNAUTHENTICATE arg S: NO UNAUTHENTICATE do not take arguments C: UNAUTHENTICATE S: NO UNAUTHENTICATE command must be issued in authenticated state -# todo Add authentication and test un authenticate result \ No newline at end of file +C: AUTHENTICATE "PLAIN" +S: \+ "" +C: user password +S: OK authentication successfull + +C: GETSCRIPT any +S: NO \(NONEXISTENT\) "There is no script by that name" + +C: UNAUTHENTICATE +S: OK + +C: GETSCRIPT any +S: NO \ No newline at end of file Modified: james/project/trunk/mpt/impl/managesieve/file/src/test/java/org/apache/james/mpt/managesieve/file/ManageSieveFileTest.java URL: http://svn.apache.org/viewvc/james/project/trunk/mpt/impl/managesieve/file/src/test/java/org/apache/james/mpt/managesieve/file/ManageSieveFileTest.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/mpt/impl/managesieve/file/src/test/java/org/apache/james/mpt/managesieve/file/ManageSieveFileTest.java (original) +++ james/project/trunk/mpt/impl/managesieve/file/src/test/java/org/apache/james/mpt/managesieve/file/ManageSieveFileTest.java Thu Dec 17 14:56:12 2015 @@ -19,6 +19,7 @@ package org.apache.james.mpt.managesieve.file; +import org.apache.james.mpt.testsuite.AuthenticateTest; import org.apache.james.mpt.testsuite.LogoutTest; import org.apache.james.mpt.testsuite.NoopTest; import org.apache.james.mpt.testsuite.UnauthenticatedTest; @@ -32,7 +33,8 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ NoopTest.class, UnauthenticatedTest.class, - LogoutTest.class + LogoutTest.class, + AuthenticateTest.class }) public class ManageSieveFileTest { } Modified: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationException.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationException.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationException.java (original) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationException.java Thu Dec 17 14:56:12 2015 @@ -20,7 +20,9 @@ package org.apache.james.managesieve.api; -public class AuthenticationException extends ManageSieveException -{ - private static final long serialVersionUID = -7564649071256099047L; +public class AuthenticationException extends ManageSieveException { + + public AuthenticationException(String message) { + super(message); + } } \ No newline at end of file Added: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationProcessor.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationProcessor.java?rev=1720575&view=auto ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationProcessor.java (added) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/AuthenticationProcessor.java Thu Dec 17 14:56:12 2015 @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.james.managesieve.api; + +public interface AuthenticationProcessor { + + String initialServerResponse(Session session); + + /** + * @return Null if authentication failed, the authenticated username if authentication is successfull + * @throws SyntaxException + * @throws AuthenticationException + */ + String isAuthenticationSuccesfull(Session session, String suppliedClientData) throws SyntaxException, AuthenticationException; + +} Modified: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/Session.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/Session.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/Session.java (original) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/Session.java Thu Dec 17 14:56:12 2015 @@ -20,18 +20,29 @@ package org.apache.james.managesieve.api; +import org.apache.james.managesieve.api.commands.Authenticate; + public interface Session { - boolean isAuthenticated(); + enum State { + UNAUTHENTICATED, + AUTHENTICATION_IN_PROGRESS, + AUTHENTICATED, + TERMINATED + } - boolean isTerminated(); + boolean isAuthenticated(); String getUser(); - void setAuthentication(boolean isAuthenticated); - void setUser(String user); - void markSessionAsTerminated(); + State getState(); + + void setState(State state); + + Authenticate.SupportedMechanism getChoosedAuthenticationMechanism(); + + void setChoosedAuthenticationMechanism(Authenticate.SupportedMechanism choosedAuthenticationMechanism); } Added: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/UnknownSaslMechanism.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/UnknownSaslMechanism.java?rev=1720575&view=auto ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/UnknownSaslMechanism.java (added) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/UnknownSaslMechanism.java Thu Dec 17 14:56:12 2015 @@ -0,0 +1,10 @@ + + +package org.apache.james.managesieve.api; + +public class UnknownSaslMechanism extends ManageSieveException { + + public UnknownSaslMechanism(String unknownMechanism) { + super("Unknown SASL mechanism " + unknownMechanism); + } +} Modified: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/Authenticate.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/Authenticate.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/Authenticate.java (original) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/Authenticate.java Thu Dec 17 14:56:12 2015 @@ -21,14 +21,30 @@ package org.apache.james.managesieve.api.commands; import org.apache.james.managesieve.api.AuthenticationException; +import org.apache.james.managesieve.api.Session; +import org.apache.james.managesieve.api.SyntaxException; +import org.apache.james.managesieve.api.UnknownSaslMechanism; /** * @see <a href=http://tools.ietf.org/html/rfc5804#section-2.1>RFC 5804 AUTHENTICATE Command</a> */ public interface Authenticate { + + enum SupportedMechanism { + PLAIN; + + public static SupportedMechanism retrieveMechanism(String serializedData) throws UnknownSaslMechanism { + for (SupportedMechanism supportedMechanism : SupportedMechanism.values()) { + if (supportedMechanism.toString().equalsIgnoreCase(serializedData)) { + return supportedMechanism; + } + } + throw new UnknownSaslMechanism(serializedData); + } + } - void authenticate(String mechanism) throws AuthenticationException; + String chooseMechanism(Session session, String mechanism) throws AuthenticationException, UnknownSaslMechanism, SyntaxException; - void authenticate(String mechanism, String initialData) throws AuthenticationException; + String authenticate(Session session, String suppliedData) throws AuthenticationException, SyntaxException; } Modified: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/CoreCommands.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/CoreCommands.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/CoreCommands.java (original) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/api/commands/CoreCommands.java Thu Dec 17 14:56:12 2015 @@ -26,6 +26,6 @@ package org.apache.james.managesieve.api * @see <a href=http://tools.ietf.org/html/rfc5804#section-2>RFC 5804 Commands</a> */ public interface CoreCommands extends Capability, CheckScript, DeleteScript, GetScript, HaveSpace, - ListScripts, PutScript, RenameScript, SetActive, GetActive, Noop, Unauthenticate, Logout { + ListScripts, PutScript, RenameScript, SetActive, GetActive, Noop, Unauthenticate, Logout, Authenticate { } Modified: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/CoreProcessor.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/CoreProcessor.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/CoreProcessor.java (original) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/CoreProcessor.java Thu Dec 17 14:56:12 2015 @@ -20,16 +20,22 @@ package org.apache.james.managesieve.core; +import com.google.common.base.Function; +import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.base.Throwables; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.commons.io.IOUtils; +import org.apache.james.managesieve.api.AuthenticationException; +import org.apache.james.managesieve.api.AuthenticationProcessor; import org.apache.james.managesieve.api.AuthenticationRequiredException; import org.apache.james.managesieve.api.ManageSieveRuntimeException; import org.apache.james.managesieve.api.Session; import org.apache.james.managesieve.api.SessionTerminatedException; import org.apache.james.managesieve.api.SieveParser; import org.apache.james.managesieve.api.SyntaxException; +import org.apache.james.managesieve.api.UnknownSaslMechanism; import org.apache.james.managesieve.api.commands.CoreCommands; import org.apache.james.sieverepository.api.ScriptSummary; import org.apache.james.sieverepository.api.SieveRepository; @@ -44,6 +50,7 @@ import org.apache.james.user.api.UsersRe import org.apache.james.user.api.UsersRepositoryException; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -57,12 +64,15 @@ public class CoreProcessor implements Co private final UsersRepository usersRepository; private final SieveParser parser; private final Map<Capabilities, String> capabilitiesBase; + private final Map<SupportedMechanism, AuthenticationProcessor> authenticationProcessorMap; public CoreProcessor(SieveRepository repository, UsersRepository usersRepository, SieveParser parser) { this.sieveRepository = repository; this.usersRepository = usersRepository; this.parser = parser; - this.capabilitiesBase = precomputeCapabilitiesBase(parser); + this.capabilitiesBase = precomputedCapabilitiesBase(parser); + this.authenticationProcessorMap = new HashMap<SupportedMechanism, AuthenticationProcessor>(); + this.authenticationProcessorMap.put(SupportedMechanism.PLAIN, new PlainAuthenticationProcessor(usersRepository)); } @Override @@ -184,9 +194,40 @@ public class CoreProcessor implements Co } @Override + public String chooseMechanism(Session session, String mechanism) throws AuthenticationException, UnknownSaslMechanism, SyntaxException { + if (Strings.isNullOrEmpty(mechanism)) { + throw new SyntaxException("You must specify a SASL mechanism as an argument of AUTHENTICATE command"); + } + String unquotedMechanism = unquotaIfNeeded(mechanism); + SupportedMechanism supportedMechanism = SupportedMechanism.retrieveMechanism(unquotedMechanism); + + session.setChoosedAuthenticationMechanism(supportedMechanism); + session.setState(Session.State.AUTHENTICATION_IN_PROGRESS); + AuthenticationProcessor authenticationProcessor = authenticationProcessorMap.get(supportedMechanism); + return authenticationProcessor.initialServerResponse(session); + } + + @Override + public String authenticate(Session session, String suppliedData) throws AuthenticationException, SyntaxException { + SupportedMechanism currentAuthenticationMechanism = session.getChoosedAuthenticationMechanism(); + AuthenticationProcessor authenticationProcessor = authenticationProcessorMap.get(currentAuthenticationMechanism); + String authenticatedUsername = authenticationProcessor.isAuthenticationSuccesfull(session, suppliedData); + if (authenticatedUsername != null) { + session.setUser(authenticatedUsername); + session.setState(Session.State.AUTHENTICATED); + return "OK authentication successfull"; + } else { + session.setState(Session.State.UNAUTHENTICATED); + session.setUser(null); + return "NO authentication failed"; + } + } + + @Override public String unauthenticate(Session session) { if (session.isAuthenticated()) { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); + session.setUser(null); return "OK"; } else { return "NO UNAUTHENTICATE command must be issued in authenticated state"; @@ -199,10 +240,10 @@ public class CoreProcessor implements Co } protected void authenticationCheck(Session session) throws AuthenticationRequiredException { - ensureUser(session); if (!session.isAuthenticated()) { throw new AuthenticationRequiredException(); } + ensureUser(session); } private void ensureUser(Session session) { @@ -217,13 +258,7 @@ public class CoreProcessor implements Co private String buildExtensions(SieveParser parser) { - StringBuilder builder = new StringBuilder(); - if (parser.getExtensions() != null) { - for (String extension : parser.getExtensions()) { - builder.append(extension).append(' '); - } - } - return builder.toString().trim(); + return Joiner.on(' ').join(parser.getExtensions()).trim(); } private String taggify(String tag) { @@ -232,6 +267,9 @@ public class CoreProcessor implements Co } private String unquotaIfNeeded(String tag) { + if (Strings.isNullOrEmpty(tag)) { + return ""; + } int startIndex = 0; int stopIndex = tag.length(); if (tag.endsWith("\r\n")) { @@ -246,15 +284,27 @@ public class CoreProcessor implements Co return tag.substring(startIndex, stopIndex); } - private Map<Capabilities, String> precomputeCapabilitiesBase(SieveParser parser) { + private Map<Capabilities, String> precomputedCapabilitiesBase(SieveParser parser) { String extensions = buildExtensions(parser); Map<Capabilities, String> capabilitiesBase = new HashMap<Capabilities, String>(); capabilitiesBase.put(Capabilities.IMPLEMENTATION, IMPLEMENTATION_DESCRIPTION); capabilitiesBase.put(Capabilities.VERSION, MANAGE_SIEVE_VERSION); + capabilitiesBase.put(Capabilities.SASL, constructSaslSupportedAuthenticationMechanisms()); if (!extensions.isEmpty()) { capabilitiesBase.put(Capabilities.SIEVE, extensions); } capabilitiesBase.put(Capabilities.GETACTIVE, null); return capabilitiesBase; } + + private String constructSaslSupportedAuthenticationMechanisms() { + return Joiner.on(' ') + .join(Lists.transform( + Arrays.asList(SupportedMechanism.values()), + new Function<SupportedMechanism, String>() { + public String apply(SupportedMechanism supportedMechanism) { + return supportedMechanism.toString(); + } + })); + } } \ No newline at end of file Added: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/PlainAuthenticationProcessor.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/PlainAuthenticationProcessor.java?rev=1720575&view=auto ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/PlainAuthenticationProcessor.java (added) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/PlainAuthenticationProcessor.java Thu Dec 17 14:56:12 2015 @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.james.managesieve.core; + +import com.google.common.base.Splitter; +import org.apache.james.managesieve.api.AuthenticationException; +import org.apache.james.managesieve.api.AuthenticationProcessor; +import org.apache.james.managesieve.api.Session; +import org.apache.james.managesieve.api.SyntaxException; +import org.apache.james.user.api.UsersRepository; +import org.apache.james.user.api.UsersRepositoryException; +import org.apache.james.user.api.model.User; + +import java.util.Iterator; + +/** + * See RFC-4616 : https://tools.ietf.org/html/rfc4616 + * + * Only differences is that ManageSieve does not handle tags. See https://tools.ietf.org/html/rfc5804#section-2.1 for details + */ +public class PlainAuthenticationProcessor implements AuthenticationProcessor { + + private final UsersRepository usersRepository; + + public PlainAuthenticationProcessor(UsersRepository usersRepository) { + this.usersRepository = usersRepository; + } + + @Override + public String initialServerResponse(Session session) { + return "+ \"\""; + } + + + @Override + public String isAuthenticationSuccesfull(Session session, String suppliedClientData) throws SyntaxException, AuthenticationException { + if (suppliedClientData.contains("\u0000")) { + return authenticateWithSeparator(session, suppliedClientData, '\u0000'); + } else { + return authenticateWithSeparator(session, suppliedClientData, ' '); + } + } + + private String authenticateWithSeparator(Session session, String suppliedClientData, char c) throws SyntaxException, AuthenticationException { + Iterator<String> it = Splitter.on(c).split(suppliedClientData).iterator(); + if (!it.hasNext()) { + throw new SyntaxException("You must supply a username for the authentication mechanism. Formal syntax : <NULL>username<NULL>password"); + } + String userName = it.next(); + if (!it.hasNext()) { + throw new SyntaxException("You must supply a password for the authentication mechanism. Formal syntax : <NULL>username<NULL>password"); + } + String password = it.next(); + session.setUser(userName); + try { + User user = usersRepository.getUserByName(userName); + if (user != null && user.verifyPassword(password)) { + return userName; + } else { + return null; + } + } catch (UsersRepositoryException e) { + throw new AuthenticationException(e.getMessage()); + } + } +} Modified: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCore.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCore.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCore.java (original) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCore.java Thu Dec 17 14:56:12 2015 @@ -22,10 +22,12 @@ package org.apache.james.managesieve.tra import com.google.common.base.Strings; import org.apache.james.managesieve.api.ArgumentException; +import org.apache.james.managesieve.api.AuthenticationException; import org.apache.james.managesieve.api.AuthenticationRequiredException; import org.apache.james.managesieve.api.Session; import org.apache.james.managesieve.api.SessionTerminatedException; import org.apache.james.managesieve.api.SyntaxException; +import org.apache.james.managesieve.api.UnknownSaslMechanism; import org.apache.james.managesieve.api.commands.Capability.Capabilities; import org.apache.james.managesieve.api.commands.CoreCommands; import org.apache.james.managesieve.util.ParserUtils; @@ -75,6 +77,14 @@ public class LineToCore{ public void logout() throws SessionTerminatedException { core.logout(); } + + public String chooseMechanism(Session session, String mechanism) throws AuthenticationException, UnknownSaslMechanism, SyntaxException { + return core.chooseMechanism(session, mechanism); + } + + public String authenticate(Session session, String suppliedData) throws AuthenticationException, SyntaxException { + return core.authenticate(session, suppliedData); + } public void deleteScript(Session session, String args) throws AuthenticationRequiredException, ScriptNotFoundException, IsActiveException, ArgumentException { String scriptName = ParserUtils.getScriptName(args); Modified: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCoreToLine.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCoreToLine.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCoreToLine.java (original) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/LineToCoreToLine.java Thu Dec 17 14:56:12 2015 @@ -21,10 +21,12 @@ package org.apache.james.managesieve.transcode; import org.apache.james.managesieve.api.ArgumentException; +import org.apache.james.managesieve.api.AuthenticationException; import org.apache.james.managesieve.api.AuthenticationRequiredException; import org.apache.james.managesieve.api.Session; import org.apache.james.managesieve.api.SessionTerminatedException; import org.apache.james.managesieve.api.SyntaxException; +import org.apache.james.managesieve.api.UnknownSaslMechanism; import org.apache.james.managesieve.api.commands.Capability.Capabilities; import org.apache.james.sieverepository.api.ScriptSummary; import org.apache.james.sieverepository.api.exception.DuplicateException; @@ -82,6 +84,28 @@ public class LineToCoreToLine { lineToCore.logout(); } + public String chooseMechanism(Session session, String mechanism) { + try { + return lineToCore.chooseMechanism(session, mechanism); + } catch (AuthenticationException e) { + return "NO Authentication failed with " + e.getCause().getClass() + " : " + e.getMessage(); + } catch (UnknownSaslMechanism unknownSaslMechanism) { + return "NO " + unknownSaslMechanism.getMessage(); + } catch (SyntaxException e) { + return "NO ManageSieve syntax is incorrect : " + e.getMessage(); + } + } + + public String authenticate(Session session, String suppliedData) { + try { + return lineToCore.authenticate(session, suppliedData); + } catch (AuthenticationException e) { + return "NO Authentication failed with " + e.getCause().getClass() + " : " + e.getMessage(); + } catch (SyntaxException e) { + return "NO ManageSieve syntax is incorrect : " + e.getMessage(); + } + } + public String checkScript(Session session, String args) { try { List<String> warnings = lineToCore.checkScript(session, args); Modified: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ManageSieveProcessor.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ManageSieveProcessor.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ManageSieveProcessor.java (original) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ManageSieveProcessor.java Thu Dec 17 14:56:12 2015 @@ -81,8 +81,11 @@ public class ManageSieveProcessor { } private String matchCommandWithImplementation(Session session, String arguments, String command) throws SessionTerminatedException { + if (session.getState() == Session.State.AUTHENTICATION_IN_PROGRESS) { + return lineToCoreToLine.authenticate(session, arguments); + } if (command.equals(AUTHENTICATE)) { - return "NO AUTHENTICATE command not yet implemented"; + return lineToCoreToLine.chooseMechanism(session, arguments); } else if (command.equals(CAPABILITY)) { return lineToCoreToLine.capability(session, arguments); } else if (command.equals(CHECKSCRIPT)) { Modified: james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/util/SettableSession.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/util/SettableSession.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/util/SettableSession.java (original) +++ james/project/trunk/protocols/managesieve/src/main/java/org/apache/james/managesieve/util/SettableSession.java Thu Dec 17 14:56:12 2015 @@ -21,16 +21,16 @@ package org.apache.james.managesieve.util; import org.apache.james.managesieve.api.Session; +import org.apache.james.managesieve.api.commands.Authenticate; public class SettableSession implements Session { private String user; - private boolean isAuthenticated; - private boolean isTerminated; + private State state; + private Authenticate.SupportedMechanism choosedAuthenticationMechanism; public SettableSession() { - this.isAuthenticated = false; - this.isTerminated = false; + this.state = State.UNAUTHENTICATED; } public String getUser() { @@ -38,12 +38,7 @@ public class SettableSession implements } public boolean isAuthenticated() { - return isAuthenticated; - } - - public void setAuthentication(boolean isAuthenticated) { - this.isAuthenticated = isAuthenticated; - + return state == State.AUTHENTICATED; } public void setUser(String user) { @@ -51,12 +46,20 @@ public class SettableSession implements } @Override - public boolean isTerminated() { - return isTerminated; + public State getState() { + return state; } @Override - public void markSessionAsTerminated() { - isTerminated = true; + public void setState(State state) { + this.state = state; + } + + public Authenticate.SupportedMechanism getChoosedAuthenticationMechanism() { + return choosedAuthenticationMechanism; + } + + public void setChoosedAuthenticationMechanism(Authenticate.SupportedMechanism choosedAuthenticationMechanism) { + this.choosedAuthenticationMechanism = choosedAuthenticationMechanism; } } Modified: james/project/trunk/protocols/managesieve/src/test/java/org/apache/james/managesieve/core/CoreProcessorTestCase.java URL: http://svn.apache.org/viewvc/james/project/trunk/protocols/managesieve/src/test/java/org/apache/james/managesieve/core/CoreProcessorTestCase.java?rev=1720575&r1=1720574&r2=1720575&view=diff ============================================================================== --- james/project/trunk/protocols/managesieve/src/test/java/org/apache/james/managesieve/core/CoreProcessorTestCase.java (original) +++ james/project/trunk/protocols/managesieve/src/test/java/org/apache/james/managesieve/core/CoreProcessorTestCase.java Thu Dec 17 14:56:12 2015 @@ -28,8 +28,10 @@ import static org.mockito.Mockito.when; import com.google.common.collect.Lists; import org.apache.james.managesieve.api.AuthenticationRequiredException; +import org.apache.james.managesieve.api.Session; import org.apache.james.managesieve.api.SieveParser; import org.apache.james.managesieve.api.SyntaxException; +import org.apache.james.managesieve.api.commands.Authenticate; import org.apache.james.managesieve.api.commands.Capability.Capabilities; import org.apache.james.managesieve.util.SettableSession; import org.apache.james.sieverepository.api.ScriptSummary; @@ -56,13 +58,14 @@ public class CoreProcessorTestCase { private SieveRepository sieveRepository; private CoreProcessor core; + private UsersRepository usersRepository; @Before public void setUp() throws Exception { session = new SettableSession(); sieveParser = mock(SieveParser.class); sieveRepository = mock(SieveRepository.class); - UsersRepository usersRepository = mock(UsersRepository.class); + usersRepository = mock(UsersRepository.class); core = new CoreProcessor(sieveRepository, usersRepository, sieveParser); when(usersRepository.contains(USER)).thenAnswer(new Answer<Boolean>() { public Boolean answer(InvocationOnMock invocationOnMock) throws Throwable { @@ -73,44 +76,48 @@ public class CoreProcessorTestCase { @Test public final void testCapabilityUnauthenticated() { - session.setAuthentication(false); + session.setState(Session.State.AUTHENTICATED); when(sieveParser.getExtensions()).thenAnswer(new Answer<List<String>>() { public List<String> answer(InvocationOnMock invocationOnMock) throws Throwable { return Lists.newArrayList("a", "b", "c"); } }); + core = new CoreProcessor(sieveRepository, usersRepository, sieveParser); assertThat(core.capability(session)).containsEntry(Capabilities.IMPLEMENTATION, CoreProcessor.IMPLEMENTATION_DESCRIPTION) .containsEntry(Capabilities.VERSION, CoreProcessor.MANAGE_SIEVE_VERSION) .containsEntry(Capabilities.SIEVE, "a b c") + .containsEntry(Capabilities.SASL, Authenticate.SupportedMechanism.PLAIN.toString()) .containsKey(Capabilities.GETACTIVE); } @Test public final void testCapabilityAuthenticated() { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); when(sieveParser.getExtensions()).thenAnswer(new Answer<List<String>>() { public List<String> answer(InvocationOnMock invocationOnMock) throws Throwable { return Lists.newArrayList("a", "b", "c"); } }); + core = new CoreProcessor(sieveRepository, usersRepository, sieveParser); session.setUser(USER); assertThat(core.capability(session)).containsEntry(Capabilities.IMPLEMENTATION, CoreProcessor.IMPLEMENTATION_DESCRIPTION) .containsEntry(Capabilities.VERSION, CoreProcessor.MANAGE_SIEVE_VERSION) .containsEntry(Capabilities.SIEVE, "a b c") .containsEntry(Capabilities.OWNER, USER) + .containsEntry(Capabilities.SASL, Authenticate.SupportedMechanism.PLAIN.toString()) .containsKey(Capabilities.GETACTIVE); } @Test(expected = AuthenticationRequiredException.class) public final void testCheckScriptUnauthorised() throws AuthenticationRequiredException, SyntaxException { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); session.setUser(USER); core.checkScript(session, "warning"); } @Test public final void testCheckScript() throws Exception { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); when(sieveParser.parse(CONTENT)).thenAnswer(new Answer<List<String>>() { public List<String> answer(InvocationOnMock invocationOnMock) throws Throwable { @@ -123,14 +130,14 @@ public class CoreProcessorTestCase { @Test(expected = SyntaxException.class) public final void testCheckScriptSyntaxException() throws Exception { doThrow(new SyntaxException("Syntax exception")).when(sieveParser).parse(CONTENT); - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.checkScript(session, CONTENT); } @Test(expected = AuthenticationRequiredException.class) public final void testDeleteScriptUnauthorised() throws Exception { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); session.setUser(USER); core.deleteScript(session, SCRIPT); } @@ -138,7 +145,7 @@ public class CoreProcessorTestCase { @Test(expected = ScriptNotFoundException.class) public final void testDeleteScriptNonExistent() throws Exception { doThrow(new ScriptNotFoundException()).when(sieveRepository).deleteScript(USER, SCRIPT); - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.deleteScript(session, SCRIPT); } @@ -146,7 +153,7 @@ public class CoreProcessorTestCase { @Test public final void testDeleteScriptExistent() throws Exception { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); sieveRepository.putScript(USER, SCRIPT, CONTENT); core.deleteScript(session, SCRIPT); @@ -156,14 +163,14 @@ public class CoreProcessorTestCase { @Test(expected = IsActiveException.class) public final void testDeleteScriptActive() throws Exception { doThrow(new IsActiveException()).when(sieveRepository).deleteScript(USER, SCRIPT); - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.deleteScript(session, SCRIPT); } @Test(expected = AuthenticationRequiredException.class) public final void testGetUnauthorisedScript() throws Exception { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); session.setUser(USER); core.getScript(session, SCRIPT); } @@ -171,14 +178,14 @@ public class CoreProcessorTestCase { @Test(expected = ScriptNotFoundException.class) public final void testGetNonExistentScript() throws Exception { doThrow(new ScriptNotFoundException()).when(sieveRepository).getScript(USER, SCRIPT); - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.getScript(session, SCRIPT); } @Test public final void testGetScript() throws Exception { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); when(sieveRepository.getScript(USER, SCRIPT)).thenAnswer(new Answer<String>() { public String answer(InvocationOnMock invocationOnMock) throws Throwable { @@ -190,14 +197,14 @@ public class CoreProcessorTestCase { @Test(expected = AuthenticationRequiredException.class) public final void testHaveSpaceUnauthorised() throws Exception { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); session.setUser(USER); core.haveSpace(session, SCRIPT, Long.MAX_VALUE); } @Test public final void testHaveSpace() throws Exception { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.haveSpace(session, SCRIPT, Long.MAX_VALUE); verify(sieveRepository).haveSpace(USER, SCRIPT, Long.MAX_VALUE); @@ -205,14 +212,14 @@ public class CoreProcessorTestCase { @Test(expected = AuthenticationRequiredException.class) public final void testListScriptsUnauthorised() throws Exception { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); session.setUser(USER); core.listScripts(session); } @Test public final void testListScripts() throws Exception { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); when(sieveRepository.listScripts(USER)).thenAnswer(new Answer<List<ScriptSummary>>() { @Override @@ -226,14 +233,14 @@ public class CoreProcessorTestCase { @Test(expected = AuthenticationRequiredException.class) public final void testPutScriptUnauthorised() throws Exception { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); session.setUser(USER); core.putScript(session, SCRIPT, CONTENT); } @Test public final void testPutScriptAuthorized() throws Exception { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.putScript(session, SCRIPT, CONTENT); verify(sieveRepository).putScript(USER, SCRIPT, CONTENT); @@ -242,21 +249,21 @@ public class CoreProcessorTestCase { @Test(expected = SyntaxException.class) public final void testPutScriptSyntaxException() throws Exception { doThrow(new SyntaxException("Syntax exception")).when(sieveParser).parse(CONTENT); - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.putScript(session, SCRIPT, CONTENT); } @Test(expected = AuthenticationRequiredException.class) public final void testRenameScriptUnauthorized() throws Exception { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); session.setUser(USER); core.renameScript(session, OLDNAME, NEW_NAME); } @Test public final void testRenameScript() throws Exception { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.renameScript(session, OLDNAME, NEW_NAME); verify(sieveRepository).renameScript(USER, OLDNAME, NEW_NAME); @@ -264,14 +271,14 @@ public class CoreProcessorTestCase { @Test(expected = AuthenticationRequiredException.class) public final void testSetActiveUnauthorised() throws Exception { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); session.setUser(USER); core.setActive(session, SCRIPT); } @Test public final void testSetActive() throws Exception { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.setActive(session, SCRIPT); verify(sieveRepository).setActive(USER, SCRIPT); @@ -279,7 +286,7 @@ public class CoreProcessorTestCase { @Test(expected = AuthenticationRequiredException.class) public final void testGetUnauthorisedActive() throws Exception { - session.setAuthentication(false); + session.setState(Session.State.UNAUTHENTICATED); session.setUser(USER); core.getActive(session); } @@ -287,14 +294,14 @@ public class CoreProcessorTestCase { @Test(expected = ScriptNotFoundException.class) public final void testGetNonExistentActive() throws Exception { doThrow(new ScriptNotFoundException()).when(sieveRepository).getActive(USER); - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); core.getActive(session); } @Test public final void testGetActive() throws Exception { - session.setAuthentication(true); + session.setState(Session.State.AUTHENTICATED); session.setUser(USER); sieveRepository.setActive(USER, SCRIPT); when(sieveRepository.getActive(USER)).thenAnswer(new Answer<String>() { --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org