Coverage Report - com.allanbank.mongodb.client.connection.auth.AuthenticatingConnection
 
Classes in this File Line Coverage Branch Coverage Complexity
AuthenticatingConnection
100%
42/42
100%
14/14
2.667
 
 1  
 /*
 2  
  * #%L
 3  
  * AuthenticatingConnection.java - mongodb-async-driver - Allanbank Consulting, Inc.
 4  
  * %%
 5  
  * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
 6  
  * %%
 7  
  * Licensed under the Apache License, Version 2.0 (the "License");
 8  
  * you may not use this file except in compliance with the License.
 9  
  * You may obtain a copy of the License at
 10  
  * 
 11  
  *      http://www.apache.org/licenses/LICENSE-2.0
 12  
  * 
 13  
  * Unless required by applicable law or agreed to in writing, software
 14  
  * distributed under the License is distributed on an "AS IS" BASIS,
 15  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  
  * See the License for the specific language governing permissions and
 17  
  * limitations under the License.
 18  
  * #L%
 19  
  */
 20  
 
 21  
 package com.allanbank.mongodb.client.connection.auth;
 22  
 
 23  
 import java.util.Collection;
 24  
 import java.util.Iterator;
 25  
 import java.util.Map;
 26  
 import java.util.concurrent.ConcurrentHashMap;
 27  
 
 28  
 import com.allanbank.mongodb.Credential;
 29  
 import com.allanbank.mongodb.MongoClientConfiguration;
 30  
 import com.allanbank.mongodb.MongoDbException;
 31  
 import com.allanbank.mongodb.client.Message;
 32  
 import com.allanbank.mongodb.client.callback.ReplyCallback;
 33  
 import com.allanbank.mongodb.client.connection.Connection;
 34  
 import com.allanbank.mongodb.client.connection.proxy.AbstractProxyConnection;
 35  
 import com.allanbank.mongodb.error.MongoDbAuthenticationException;
 36  
 import com.allanbank.mongodb.util.log.Log;
 37  
 import com.allanbank.mongodb.util.log.LogFactory;
 38  
 
 39  
 /**
 40  
  * AuthenticatingConnection provides a connection that authenticated with the
 41  
  * server for each database before it is used.
 42  
  * 
 43  
  * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
 44  
  *         mutated in incompatible ways between any two releases of the driver.
 45  
  * @copyright 2012-2014, Allanbank Consulting, Inc., All Rights Reserved
 46  
  */
 47  
 public class AuthenticatingConnection extends AbstractProxyConnection {
 48  
 
 49  
     /** The name of the administration database. */
 50  
     public static final String ADMIN_DB_NAME = MongoClientConfiguration.ADMIN_DB_NAME;
 51  
 
 52  
     /** The logger for the authenticator. */
 53  1
     public static final Log LOG = LogFactory
 54  
             .getLog(AuthenticatingConnection.class);
 55  
 
 56  
     /** Map of the authenticators. */
 57  
     private final Map<String, Authenticator> myAuthenticators;
 58  
 
 59  
     /** Set of the databases with authentication failures. */
 60  
     private final Map<String, MongoDbException> myFailures;
 61  
 
 62  
     /**
 63  
      * Creates a new AuthenticatingConnection.
 64  
      * 
 65  
      * @param connection
 66  
      *            The connection to ensure gets authenticated as needed.
 67  
      * @param config
 68  
      *            The MongoDB client configuration.
 69  
      */
 70  
     public AuthenticatingConnection(final Connection connection,
 71  
             final MongoClientConfiguration config) {
 72  21
         super(connection);
 73  
 
 74  21
         myAuthenticators = new ConcurrentHashMap<String, Authenticator>();
 75  21
         myFailures = new ConcurrentHashMap<String, MongoDbException>();
 76  
 
 77  
         // With the advent of delegated credentials we must now authenticate
 78  
         // with all available credentials immediately.
 79  21
         final Collection<Credential> credentials = config.getCredentials();
 80  21
         for (final Credential credential : credentials) {
 81  21
             final Authenticator authenticator = credential.authenticator();
 82  
 
 83  21
             authenticator.startAuthentication(credential, connection);
 84  
 
 85  
             // Boo! MongoDB does not support concurrent authentication attempts.
 86  
             // Block here for the results if more than 1 credential. Boo!
 87  20
             if (credentials.size() > 1) {
 88  
                 try {
 89  4
                     if (!authenticator.result()) {
 90  1
                         myFailures.put(credential.getDatabase(),
 91  
                                 new MongoDbAuthenticationException(
 92  
                                         "Authentication failed for the "
 93  
                                                 + credential.getDatabase()
 94  
                                                 + " database."));
 95  
                     }
 96  
                 }
 97  1
                 catch (final MongoDbException error) {
 98  1
                     myFailures.put(credential.getDatabase(), error);
 99  4
                 }
 100  
             }
 101  
             else {
 102  16
                 myAuthenticators.put(credential.getDatabase(), authenticator);
 103  
             }
 104  20
         }
 105  20
     }
 106  
 
 107  
     /**
 108  
      * {@inheritDoc}
 109  
      * <p>
 110  
      * Makes sure the connection is authenticated for the current database
 111  
      * before forwarding to the proxied connection.
 112  
      * </p>
 113  
      */
 114  
     @Override
 115  
     public void send(final Message message1, final Message message2,
 116  
             final ReplyCallback replyCallback) throws MongoDbException {
 117  1
         ensureAuthenticated(message1);
 118  1
         ensureAuthenticated(message2);
 119  
 
 120  1
         super.send(message1, message2, replyCallback);
 121  1
     }
 122  
 
 123  
     /**
 124  
      * {@inheritDoc}
 125  
      * <p>
 126  
      * Makes sure the connection is authenticated for the current database
 127  
      * before forwarding to the proxied connection.
 128  
      * </p>
 129  
      */
 130  
     @Override
 131  
     public void send(final Message message, final ReplyCallback replyCallback)
 132  
             throws MongoDbException {
 133  21
         ensureAuthenticated(message);
 134  
 
 135  8
         super.send(message, replyCallback);
 136  8
     }
 137  
 
 138  
     /**
 139  
      * {@inheritDoc}
 140  
      * <p>
 141  
      * Overridden to return the socket information.
 142  
      * </p>
 143  
      */
 144  
     @Override
 145  
     public String toString() {
 146  1
         return "Auth(" + getProxiedConnection() + ")";
 147  
     }
 148  
 
 149  
     /**
 150  
      * {@inheritDoc}
 151  
      * <p>
 152  
      * Overridden to give access to the proxied connections to tests.
 153  
      * </p>
 154  
      */
 155  
     @Override
 156  
     protected Connection getProxiedConnection() {
 157  3
         final Connection proxied = super.getProxiedConnection();
 158  
 
 159  3
         return proxied;
 160  
     }
 161  
 
 162  
     /**
 163  
      * Ensures the connection has either already authenticated with the server
 164  
      * or completes the authentication.
 165  
      * 
 166  
      * @param message
 167  
      *            The message to authenticate for.
 168  
      * @throws MongoDbAuthenticationException
 169  
      *             On a failure to authenticate with the MongDB server.
 170  
      */
 171  
     private void ensureAuthenticated(final Message message)
 172  
             throws MongoDbAuthenticationException {
 173  
         // Check the authentication results are done.
 174  23
         if (!myAuthenticators.isEmpty()) {
 175  15
             final Iterator<Map.Entry<String, Authenticator>> iter = myAuthenticators
 176  
                     .entrySet().iterator();
 177  30
             while (iter.hasNext()) {
 178  15
                 final Map.Entry<String, Authenticator> authenticator = iter
 179  
                         .next();
 180  
                 try {
 181  15
                     if (!authenticator.getValue().result()) {
 182  2
                         myFailures.put(authenticator.getKey(),
 183  
                                 new MongoDbAuthenticationException(
 184  
                                         "Authentication failed for the "
 185  
                                                 + authenticator.getKey()
 186  
                                                 + " database."));
 187  
                     }
 188  
                 }
 189  6
                 catch (final MongoDbException error) {
 190  
                     // Just log the error here.
 191  6
                     LOG.warn(error, "Authentication failed: []",
 192  
                             error.getMessage());
 193  
                     // Re-throw if our DB.
 194  6
                     myFailures.put(authenticator.getKey(), error);
 195  
                 }
 196  
                 finally {
 197  15
                     iter.remove();
 198  15
                 }
 199  15
             }
 200  
         }
 201  
 
 202  23
         if (myFailures.containsKey(message.getDatabaseName())) {
 203  13
             throw new MongoDbAuthenticationException(myFailures.get(message
 204  
                     .getDatabaseName()));
 205  
         }
 206  10
     }
 207  
 }