/*
 * Licensed to the University of California, Berkeley 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 tachyon.security;

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.AuthorizeCallback;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class PlainSaslServerTest{
  private static byte sSEPARATOR = 0x00; // US-ASCII <NUL>
  private PlainSaslServer mPlainSaslServer = null;

  @Before
  public void before() throws Exception {
    mPlainSaslServer = new PlainSaslServer(new MockCallbackHandler());
  }

  /**
   * Simulate the authentication data included user information from client side
   * @param user The name of the user will be transferred from client side
   * @param password The password will be transferred from client side
   * @return The response is simulated from the client side
   */
  private byte[] getUserInfo(String user, String password) throws Exception {
    byte[] auth = user.getBytes("UTF8");
    byte[] pw = password.getBytes("UTF8");
    byte[] result = new byte[pw.length + auth.length + 2];
    int pos = 0;
    result[pos ++] = sSEPARATOR;
    System.arraycopy(auth, 0, result, pos, auth.length);

    pos += auth.length;
    result[pos ++] = sSEPARATOR;

    System.arraycopy(pw, 0, result, pos, pw.length);
    return result;
  }

  @Test
  public void testUserIsNotSet() throws Exception {
    try {
      mPlainSaslServer.evaluateResponse(getUserInfo("", "anonymous"));
      Assert.fail("Expected exception has not been thrown");
    } catch (Exception e) {
      Assert.assertTrue(e.getMessage().contains("No authentication identity provided"));
    }
  }

  @Test
  public void testPasswordIsNotSet() throws Exception {
    try {
      mPlainSaslServer.evaluateResponse(getUserInfo("tachyon", ""));
      Assert.fail("Expected exception has not been thrown");
    } catch (Exception e) {
      Assert.assertTrue(e.getMessage().contains("No password provided"));
    }
  }

  @Test
  public void testAuthenticationHasNotComplete() throws Exception {
    try {
      mPlainSaslServer.getAuthorizationID();
      Assert.fail("Expected exception has not been thrown");
    } catch (Exception e) {
      Assert.assertTrue(e.getMessage().contains("PLAIN authentication not completed"));
    }
  }

  @Test
  public void testUserPasswordReceive() throws Exception {
    String testUser = "tachyon";
    String password = "anonymous";
    mPlainSaslServer.evaluateResponse(getUserInfo(testUser, password));
    Assert.assertEquals(testUser, mPlainSaslServer.getAuthorizationID());
  }

  private static class MockCallbackHandler implements CallbackHandler {
    @Override
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
      AuthorizeCallback ac = null;
      for (Callback callback : callbacks) {
        if (callback instanceof AuthorizeCallback) {
          ac = (AuthorizeCallback)callback;
        }
      }
      ac.setAuthorized(true);
    }
  }
}

@@ -1121,20 +1121,85 @@
 g.junit.
-Test
+Rule;%0Aimport org.junit.Test;%0Aimport org.junit.rules.ExpectedException
 ;%0A%0Apubli
@@ -1338,16 +1338,88 @@
  null;%0A%0A
+  @Rule%0A  public ExpectedException mThrown = ExpectedException.none();%0A%0A
   @Befor
@@ -2323,21 +2323,17 @@
 ic void 
-testU
+u
 serIsNot
@@ -2327,32 +2327,36 @@
 oid userIsNotSet
+Test
 () throws Except
@@ -2369,221 +2369,78 @@
     
-try %7B%0A      mPlainSaslServer.evaluateResponse(getUserInfo(%22%22, %22anonymous%22));%0A      Assert.fail(%22Expected exception has not been thrown%22);%0A    %7D catch (Exception e) %7B%0A      Assert.assertTrue(e.getMessage().contains
+mThrown.expect(IllegalStateException.class);%0A    mThrown.expectMessage
 (%22No
@@ -2478,301 +2478,222 @@
 ed%22)
-)
 ;%0A    
-%7D%0A  %7D%0A%0A  @Test%0A  public void testPasswordIsNotSet() throws Exception %7B%0A    try %7B%0A      mPlainSaslServer.evaluateResponse(getUserInfo(%22tachyon%22, %22%22));%0A      Assert.fail(%22Expected exception has not been thrown%22);%0A    %7D catch (Exception e) %7B%0A      Assert.assertTrue(e.getMessage().contains
+mPlainSaslServer.evaluateResponse(getUserInfo(%22%22, %22anonymous%22));%0A  %7D%0A%0A  @Test%0A  public void passwordIsNotSetTest() throws Exception %7B%0A    mThrown.expect(IllegalStateException.class);%0A    mThrown.expectMessage
 (%22No
@@ -2708,32 +2708,92 @@
 d provided%22)
-)
 ;%0A    
-%7D
+mPlainSaslServer.evaluateResponse(getUserInfo(%22tachyon%22, %22%22));
 %0A  %7D%0A%0A  @Tes
@@ -2812,13 +2812,9 @@
 oid 
-testA
+a
 uthe
@@ -2826,11 +2826,8 @@
 tion
-Has
 NotC
@@ -2833,16 +2833,20 @@
 Complete
+Test
 () throw
@@ -2867,195 +2867,78 @@
     
-try %7B%0A      mPlainSaslServer.getAuthorizationID();%0A      Assert.fail(%22Expected exception has not been thrown%22);%0A    %7D catch (Exception e) %7B%0A      Assert.assertTrue(e.getMessage().contains
+mThrown.expect(IllegalStateException.class);%0A    mThrown.expectMessage
 (%22PL
@@ -2971,24 +2971,60 @@
 pleted%22)
-)
 ;%0A    
-%7D
+mPlainSaslServer.getAuthorizationID();
 %0A  %7D%0A%0A  
@@ -3043,21 +3043,17 @@
 ic void 
-testU
+u
 serPassw
@@ -3062,16 +3062,20 @@
 dReceive
+Test
 () throw

Add a class PlainSaslServer that implements the SaslServer interface -- Change the exception check style and rename test method