/driver-core/src/main/com/mongodb/connection/SaslAuthenticator.java

https://github.com/d5nguyenvan/mongo-java-driver · Java · 223 lines · 174 code · 33 blank · 16 comment · 17 complexity · d0e73d896411aa4bdf991071dd0fac71 MD5 · raw file

  1. /*
  2. * Copyright 2008-2016 MongoDB, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.mongodb.connection;
  17. import com.mongodb.MongoCredential;
  18. import com.mongodb.MongoSecurityException;
  19. import com.mongodb.ServerAddress;
  20. import com.mongodb.async.SingleResultCallback;
  21. import org.bson.BsonBinary;
  22. import org.bson.BsonDocument;
  23. import org.bson.BsonInt32;
  24. import org.bson.BsonString;
  25. import javax.security.auth.Subject;
  26. import javax.security.sasl.SaslClient;
  27. import javax.security.sasl.SaslException;
  28. import java.security.PrivilegedAction;
  29. import static com.mongodb.MongoCredential.JAVA_SUBJECT_KEY;
  30. import static com.mongodb.connection.CommandHelper.executeCommand;
  31. import static com.mongodb.connection.CommandHelper.executeCommandAsync;
  32. abstract class SaslAuthenticator extends Authenticator {
  33. SaslAuthenticator(final MongoCredential credential) {
  34. super(credential);
  35. }
  36. public void authenticate(final InternalConnection connection, final ConnectionDescription connectionDescription) {
  37. doAsSubject(new PrivilegedAction<Void>() {
  38. @Override
  39. public Void run() {
  40. SaslClient saslClient = createSaslClient(connection.getDescription().getServerAddress());
  41. try {
  42. byte[] response = (saslClient.hasInitialResponse() ? saslClient.evaluateChallenge(new byte[0]) : null);
  43. BsonDocument res = sendSaslStart(response, connection);
  44. BsonInt32 conversationId = res.getInt32("conversationId");
  45. while (!(res.getBoolean("done")).getValue()) {
  46. response = saslClient.evaluateChallenge((res.getBinary("payload")).getData());
  47. if (response == null) {
  48. throw new MongoSecurityException(getCredential(),
  49. "SASL protocol error: no client response to challenge for credential "
  50. + getCredential());
  51. }
  52. res = sendSaslContinue(conversationId, response, connection);
  53. }
  54. } catch (Exception e) {
  55. throw wrapInMongoSecurityException(e);
  56. } finally {
  57. disposeOfSaslClient(saslClient);
  58. }
  59. return null;
  60. }
  61. });
  62. }
  63. @Override
  64. void authenticateAsync(final InternalConnection connection, final ConnectionDescription connectionDescription,
  65. final SingleResultCallback<Void> callback) {
  66. try {
  67. doAsSubject(new PrivilegedAction<Void>() {
  68. @Override
  69. public Void run() {
  70. final SaslClient saslClient = createSaslClient(connection.getDescription().getServerAddress());
  71. try {
  72. byte[] response = (saslClient.hasInitialResponse() ? saslClient.evaluateChallenge(new byte[0]) : null);
  73. sendSaslStartAsync(response, connection, new SingleResultCallback<BsonDocument>() {
  74. @Override
  75. public void onResult(final BsonDocument result, final Throwable t) {
  76. if (t != null) {
  77. callback.onResult(null, wrapInMongoSecurityException(t));
  78. } else if (result.getBoolean("done").getValue()) {
  79. callback.onResult(null, null);
  80. } else {
  81. new Continuator(saslClient, result, connection, callback).start();
  82. }
  83. }
  84. });
  85. } catch (SaslException e) {
  86. throw wrapInMongoSecurityException(e);
  87. }
  88. return null;
  89. }
  90. });
  91. } catch (Throwable t) {
  92. callback.onResult(null, t);
  93. }
  94. }
  95. public abstract String getMechanismName();
  96. protected abstract SaslClient createSaslClient(ServerAddress serverAddress);
  97. private Subject getSubject() {
  98. return getCredential().<Subject>getMechanismProperty(JAVA_SUBJECT_KEY, null);
  99. }
  100. private BsonDocument sendSaslStart(final byte[] outToken, final InternalConnection connection) {
  101. return executeCommand(getCredential().getSource(), createSaslStartCommandDocument(outToken), connection);
  102. }
  103. private BsonDocument sendSaslContinue(final BsonInt32 conversationId, final byte[] outToken, final InternalConnection connection) {
  104. return executeCommand(getCredential().getSource(), createSaslContinueDocument(conversationId, outToken), connection);
  105. }
  106. private void sendSaslStartAsync(final byte[] outToken, final InternalConnection connection,
  107. final SingleResultCallback<BsonDocument> callback) {
  108. executeCommandAsync(getCredential().getSource(), createSaslStartCommandDocument(outToken), connection,
  109. callback);
  110. }
  111. private void sendSaslContinueAsync(final BsonInt32 conversationId, final byte[] outToken, final InternalConnection connection,
  112. final SingleResultCallback<BsonDocument> callback) {
  113. executeCommandAsync(getCredential().getSource(), createSaslContinueDocument(conversationId, outToken), connection,
  114. callback);
  115. }
  116. private BsonDocument createSaslStartCommandDocument(final byte[] outToken) {
  117. return new BsonDocument("saslStart", new BsonInt32(1)).append("mechanism", new BsonString(getMechanismName()))
  118. .append("payload", new BsonBinary(outToken != null ? outToken : new byte[0]));
  119. }
  120. private BsonDocument createSaslContinueDocument(final BsonInt32 conversationId, final byte[] outToken) {
  121. return new BsonDocument("saslContinue", new BsonInt32(1)).append("conversationId", conversationId)
  122. .append("payload", new BsonBinary(outToken));
  123. }
  124. private void disposeOfSaslClient(final SaslClient saslClient) {
  125. try {
  126. saslClient.dispose();
  127. } catch (SaslException e) { // NOPMD
  128. // ignore
  129. }
  130. }
  131. private MongoSecurityException wrapInMongoSecurityException(final Throwable t) {
  132. return t instanceof MongoSecurityException
  133. ? (MongoSecurityException) t
  134. : new MongoSecurityException(getCredential(), "Exception authenticating " + getCredential(), t);
  135. }
  136. void doAsSubject(final java.security.PrivilegedAction<Void> action) {
  137. if (getSubject() == null) {
  138. action.run();
  139. } else {
  140. Subject.doAs(getSubject(), action);
  141. }
  142. }
  143. private final class Continuator implements SingleResultCallback<BsonDocument> {
  144. private final SaslClient saslClient;
  145. private final BsonDocument saslStartDocument;
  146. private final InternalConnection connection;
  147. private final SingleResultCallback<Void> callback;
  148. Continuator(final SaslClient saslClient, final BsonDocument saslStartDocument, final InternalConnection connection,
  149. final SingleResultCallback<Void> callback) {
  150. this.saslClient = saslClient;
  151. this.saslStartDocument = saslStartDocument;
  152. this.connection = connection;
  153. this.callback = callback;
  154. }
  155. @Override
  156. public void onResult(final BsonDocument result, final Throwable t) {
  157. if (t != null) {
  158. callback.onResult(null, wrapInMongoSecurityException(t));
  159. disposeOfSaslClient(saslClient);
  160. } else if (result.getBoolean("done").getValue()) {
  161. callback.onResult(null, null);
  162. disposeOfSaslClient(saslClient);
  163. } else {
  164. continueConversation(result);
  165. }
  166. }
  167. public void start() {
  168. continueConversation(saslStartDocument);
  169. }
  170. private void continueConversation(final BsonDocument result) {
  171. try {
  172. doAsSubject(new PrivilegedAction<Void>() {
  173. @Override
  174. public Void run() {
  175. try {
  176. sendSaslContinueAsync(saslStartDocument.getInt32("conversationId"),
  177. saslClient.evaluateChallenge((result.getBinary("payload")).getData()), connection, Continuator.this);
  178. } catch (SaslException e) {
  179. throw wrapInMongoSecurityException(e);
  180. }
  181. return null;
  182. }
  183. });
  184. } catch (Throwable t) {
  185. callback.onResult(null, t);
  186. disposeOfSaslClient(saslClient);
  187. }
  188. }
  189. }
  190. }