JAMES-1930 Implement AUTH PLAIN delegation as part of IMAP

Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/157be0db
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/157be0db
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/157be0db

Branch: refs/heads/master
Commit: 157be0db425f59e3751ea4ebdcefe3cf4daf7ec2
Parents: 0175e31
Author: Benoit Tellier <[email protected]>
Authored: Fri Feb 10 11:51:24 2017 +0700
Committer: Antoine Duprat <[email protected]>
Committed: Tue Feb 14 11:29:30 2017 +0100

----------------------------------------------------------------------
 .../imap/api/display/HumanReadableText.java     |   4 +
 .../imap/processor/AbstractAuthProcessor.java   | 165 +++++++++++++++----
 .../imap/processor/AuthenticateProcessor.java   |  49 +-----
 .../james/imap/processor/LoginProcessor.java    |   5 +-
 4 files changed, 143 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/157be0db/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
index 42dba98..57a98e0 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
@@ -113,6 +113,10 @@ public class HumanReadableText {
 
     public static final HumanReadableText COMSUME_UID_FAILED = new 
HumanReadableText("org.apache.james.imap.COMSUME_UID_FAILED", "failed. Failed 
to acquire UID.");
 
+    public static final HumanReadableText USER_DOES_NOT_EXIST = new 
HumanReadableText("org.apache.james.imap.GENERIC_FAILURE_DURING_PROCESSING", 
"User does not exist");
+
+    public static final HumanReadableText NOT_AN_ADMIN = new 
HumanReadableText("org.apache.james.imap.GENERIC_FAILURE_DURING_PROCESSING", 
"Not an admin");
+
     public static final HumanReadableText GENERIC_FAILURE_DURING_PROCESSING = 
new 
HumanReadableText("org.apache.james.imap.GENERIC_FAILURE_DURING_PROCESSING", 
"processing failed.");
 
     public static final HumanReadableText FAILURE_MAILBOX_EXISTS = new 
HumanReadableText("org.apache.james.imap.FAILURE_NO_SUCH_MAILBOX", "failed. 
Mailbox already exists.");

http://git-wip-us.apache.org/repos/asf/james-project/blob/157be0db/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java
index be5460a..5e10aa6 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java
@@ -31,9 +31,14 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.exception.BadCredentialsException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MailboxExistsException;
+import org.apache.james.mailbox.exception.NotAdminException;
+import org.apache.james.mailbox.exception.UserDoesNotExistException;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
 public abstract class AbstractAuthProcessor<M extends ImapRequest> extends 
AbstractMailboxProcessor<M>{
 
     private static final String ATTRIBUTE_NUMBER_OF_FAILURES = 
"org.apache.james.imap.processor.imap4rev1.NUMBER_OF_FAILURES";
@@ -45,57 +50,73 @@ public abstract class AbstractAuthProcessor<M extends 
ImapRequest> extends Abstr
         super(acceptableClass, next, mailboxManager, factory);
     }
 
-    protected void doAuth(String userid, String passwd, ImapSession session, 
String tag, ImapCommand command, Responder responder, HumanReadableText failed) 
{
+    protected void doAuth(AuthenticationAttempt authenticationAttempt, 
ImapSession session, String tag, ImapCommand command, Responder responder, 
HumanReadableText failed) {
+        Preconditions.checkArgument(!authenticationAttempt.isDelegation());
         try {
             boolean authFailure = false;
-            if (userid == null) {
+            if (authenticationAttempt.getAuthenticationId() == null) {
                 authFailure = true;
             }
-            if (authFailure == false) {
+            if (!authFailure) {
                 final MailboxManager mailboxManager = getMailboxManager();
                 try {
-                    final MailboxSession mailboxSession = 
mailboxManager.login(userid, passwd, session.getLog());
+                    final MailboxSession mailboxSession = 
mailboxManager.login(authenticationAttempt.getAuthenticationId(),
+                        authenticationAttempt.getPassword(),
+                        session.getLog());
                     session.authenticated();
                     
session.setAttribute(ImapSessionUtils.MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY, 
mailboxSession);
-                    final MailboxPath inboxPath = 
PathConverter.forSession(session).buildFullPath(MailboxConstants.INBOX);
-                    if (mailboxManager.mailboxExists(inboxPath, 
mailboxSession)) {
-                        if (session.getLog().isDebugEnabled()) {
-                            session.getLog().debug("INBOX exists. No need to 
create it.");
-                        }
-                    } else {
-                        try {
-                            session.getLog().debug("INBOX does not exist. 
Creating it.");
-                            mailboxManager.createMailbox(inboxPath, 
mailboxSession);
-                        } catch (MailboxExistsException e) {
-                            if (session.getLog().isDebugEnabled()) {
-                                session.getLog().debug("Mailbox created by 
concurrent call. Safe to ignore this exception.");
-                            }
-                        }
-                    }
+                    provisionInbox(session, mailboxManager, mailboxSession);
                     okComplete(command, tag, responder);
                 } catch (BadCredentialsException e) {
                     authFailure = true;
                 }
             }
             if (authFailure) {
-                final Integer currentNumberOfFailures = (Integer) 
session.getAttribute(ATTRIBUTE_NUMBER_OF_FAILURES);
-                final int failures;
-                if (currentNumberOfFailures == null) {
-                    failures = 1;
-                } else {
-                    failures = currentNumberOfFailures.intValue() + 1;
-                }
-                if (failures < MAX_FAILURES) {
-                    session.setAttribute(ATTRIBUTE_NUMBER_OF_FAILURES, 
failures);
-                    no(command, tag, responder, failed);
-                } else {
-                    if (session.getLog().isInfoEnabled()) {
-                        session.getLog().info("Too many authentication 
failures. Closing connection.");
-                    }
-                    bye(responder, HumanReadableText.TOO_MANY_FAILURES);
-                    session.logout();
+                manageFailureCount(session, tag, command, responder, failed);
+            }
+        } catch (MailboxException e) {
+            if (session.getLog().isInfoEnabled()) {
+                session.getLog().info("Login failed", e);
+            }
+            no(command, tag, responder, 
HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING);
+        }
+    }
+
+    protected void doAuthWithDelegation(AuthenticationAttempt 
authenticationAttempt, ImapSession session, String tag, ImapCommand command, 
Responder responder, HumanReadableText failed) {
+        Preconditions.checkArgument(authenticationAttempt.isDelegation());
+        try {
+            boolean authFailure = false;
+            if (authenticationAttempt.getAuthenticationId() == null) {
+                authFailure = true;
+            }
+            if (!authFailure) {
+                final MailboxManager mailboxManager = getMailboxManager();
+                try {
+                    final MailboxSession mailboxSession = 
mailboxManager.loginAsOtherUser(authenticationAttempt.getAuthenticationId(),
+                        authenticationAttempt.getPassword(),
+                        authenticationAttempt.getDelegateUserName().get(),
+                        session.getLog());
+                    session.authenticated();
+                    
session.setAttribute(ImapSessionUtils.MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY, 
mailboxSession);
+                    provisionInbox(session, mailboxManager, mailboxSession);
+                    okComplete(command, tag, responder);
+                } catch (BadCredentialsException e) {
+                    authFailure = true;
                 }
             }
+            if (authFailure) {
+                manageFailureCount(session, tag, command, responder, failed);
+            }
+        } catch (UserDoesNotExistException e) {
+            if (session.getLog().isInfoEnabled()) {
+                session.getLog().info("User " + 
authenticationAttempt.getAuthenticationId() + " does not exist", e);
+            }
+            no(command, tag, responder, HumanReadableText.USER_DOES_NOT_EXIST);
+        } catch (NotAdminException e) {
+            if (session.getLog().isInfoEnabled()) {
+                session.getLog().info("User " + 
authenticationAttempt.getDelegateUserName() + " is not an admin", e);
+            }
+            no(command, tag, responder, HumanReadableText.NOT_AN_ADMIN);
         } catch (MailboxException e) {
             if (session.getLog().isInfoEnabled()) {
                 session.getLog().info("Login failed", e);
@@ -103,4 +124,78 @@ public abstract class AbstractAuthProcessor<M extends 
ImapRequest> extends Abstr
             no(command, tag, responder, 
HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING);
         }
     }
+
+    private void provisionInbox(ImapSession session, MailboxManager 
mailboxManager, MailboxSession mailboxSession) throws MailboxException {
+        final MailboxPath inboxPath = 
PathConverter.forSession(session).buildFullPath(MailboxConstants.INBOX);
+        if (mailboxManager.mailboxExists(inboxPath, mailboxSession)) {
+            if (session.getLog().isDebugEnabled()) {
+                session.getLog().debug("INBOX exists. No need to create it.");
+            }
+        } else {
+            try {
+                session.getLog().debug("INBOX does not exist. Creating it.");
+                mailboxManager.createMailbox(inboxPath, mailboxSession);
+            } catch (MailboxExistsException e) {
+                if (session.getLog().isDebugEnabled()) {
+                    session.getLog().debug("Mailbox created by concurrent 
call. Safe to ignore this exception.");
+                }
+            }
+        }
+    }
+
+    protected void manageFailureCount(ImapSession session, String tag, 
ImapCommand command, Responder responder, HumanReadableText failed) {
+        final Integer currentNumberOfFailures = (Integer) 
session.getAttribute(ATTRIBUTE_NUMBER_OF_FAILURES);
+        final int failures;
+        if (currentNumberOfFailures == null) {
+            failures = 1;
+        } else {
+            failures = currentNumberOfFailures + 1;
+        }
+        if (failures < MAX_FAILURES) {
+            session.setAttribute(ATTRIBUTE_NUMBER_OF_FAILURES, failures);
+            no(command, tag, responder, failed);
+        } else {
+            if (session.getLog().isInfoEnabled()) {
+                session.getLog().info("Too many authentication failures. 
Closing connection.");
+            }
+            bye(responder, HumanReadableText.TOO_MANY_FAILURES);
+            session.logout();
+        }
+    }
+
+    protected static AuthenticationAttempt delegation(String authorizeId, 
String authenticationId, String password) {
+        return new AuthenticationAttempt(Optional.of(authorizeId), 
authenticationId, password);
+    }
+
+    protected static AuthenticationAttempt noDelegation(String 
authenticationId, String password) {
+        return new AuthenticationAttempt(Optional.<String>absent(), 
authenticationId, password);
+    }
+
+    protected static class AuthenticationAttempt {
+        private final Optional<String> delegateUserName;
+        private final String authenticationId;
+        private final String password;
+
+        public AuthenticationAttempt(Optional<String> delegateUserName, String 
authenticationId, String password) {
+            this.delegateUserName = delegateUserName;
+            this.authenticationId = authenticationId;
+            this.password = password;
+        }
+
+        public boolean isDelegation() {
+            return delegateUserName.isPresent();
+        }
+
+        public Optional<String> getDelegateUserName() {
+            return delegateUserName;
+        }
+
+        public String getAuthenticationId() {
+            return authenticationId;
+        }
+
+        public String getPassword() {
+            return password;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/157be0db/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java
index 1abc69d..ea8324a 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java
@@ -37,8 +37,6 @@ import 
org.apache.james.imap.message.request.IRAuthenticateRequest;
 import org.apache.james.imap.message.response.AuthenticateResponse;
 import org.apache.james.mailbox.MailboxManager;
 
-import com.google.common.base.Optional;
-
 /**
  * Processor which handles the AUTHENTICATE command. Only authtype of PLAIN is 
supported ATM.
  * 
@@ -103,12 +101,15 @@ public class AuthenticateProcessor extends 
AbstractAuthProcessor<AuthenticateReq
      * @param responder
      */
     protected void doPlainAuth(String initialClientResponse, ImapSession 
session, String tag, ImapCommand command, Responder responder) {
-        AuthPlainAttempt authPlainAttempt = 
parseDelegationAttempt(initialClientResponse);
-        // Authenticate user
-        doAuth(authPlainAttempt.getAuthenticationId(), 
authPlainAttempt.getPassword(), session, tag, command, responder, 
HumanReadableText.AUTHENTICATION_FAILED);
+        AuthenticationAttempt authenticationAttempt = 
parseDelegationAttempt(initialClientResponse);
+        if (authenticationAttempt.isDelegation()) {
+            doAuthWithDelegation(authenticationAttempt, session, tag, command, 
responder, HumanReadableText.AUTHENTICATION_FAILED);
+        } else {
+            doAuth(authenticationAttempt, session, tag, command, responder, 
HumanReadableText.AUTHENTICATION_FAILED);
+        }
     }
 
-    private AuthPlainAttempt parseDelegationAttempt(String 
initialClientResponse) {
+    private AuthenticationAttempt parseDelegationAttempt(String 
initialClientResponse) {
         String token2;
         try {
 
@@ -161,40 +162,4 @@ public class AuthenticateProcessor extends 
AbstractAuthProcessor<AuthenticateReq
         return Collections.unmodifiableList(caps);
     }
 
-    private static AuthPlainAttempt delegation(String authorizeId, String 
authenticationId, String password) {
-        return new AuthPlainAttempt(Optional.of(authorizeId), 
authenticationId, password);
-    }
-
-    private static AuthPlainAttempt noDelegation(String authenticationId, 
String password) {
-        return new AuthPlainAttempt(Optional.<String>absent(), 
authenticationId, password);
-    }
-
-    private static class AuthPlainAttempt {
-        private final Optional<String> authorizeId;
-        private final String authenticationId;
-        private final String password;
-
-        private AuthPlainAttempt(Optional<String> authorizeId, String 
authenticationId, String password) {
-            this.authorizeId = authorizeId;
-            this.authenticationId = authenticationId;
-            this.password = password;
-        }
-
-        public boolean isDelegation() {
-            return authorizeId.isPresent();
-        }
-
-        public Optional<String> getAuthorizeId() {
-            return authorizeId;
-        }
-
-        public String getAuthenticationId() {
-            return authenticationId;
-        }
-
-        public String getPassword() {
-            return password;
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/157be0db/protocols/imap/src/main/java/org/apache/james/imap/processor/LoginProcessor.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/LoginProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/LoginProcessor.java
index 3e824e1..4837446 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/LoginProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/LoginProcessor.java
@@ -48,13 +48,12 @@ public class LoginProcessor extends 
AbstractAuthProcessor<LoginRequest> implemen
      * org.apache.james.imap.api.ImapCommand, 
org.apache.james.imap.api.process.ImapProcessor.Responder)
      */
     protected void doProcess(LoginRequest request, ImapSession session, String 
tag, ImapCommand command, Responder responder) {
-            final String userid = request.getUserid();
-            final String passwd = request.getPassword();
             // check if the login is allowed with LOGIN command. See IMAP-304
             if (session.isPlainAuthDisallowed() && session.isTLSActive() == 
false) {
                 no(command, tag, responder, HumanReadableText.DISABLED_LOGIN);
             } else {
-                doAuth(userid, passwd, session, tag, command, responder, 
HumanReadableText.INVALID_LOGIN);
+                doAuth(noDelegation(request.getUserid(), 
request.getPassword()),
+                    session, tag, command, responder, 
HumanReadableText.INVALID_LOGIN);
             }
     }
 


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

Reply via email to