kjkmadness 2014-03-07
user: UserApp.isRememberMe is deleted, use UserApp.currentUser
- UserApp.currentUser create user object using cookie when there is no
  session data
- utils.BasicAuthAction is edited because
  UserApp.authenticateWithPlainPassword does not return null anymore
- add tests
@eafd35ddd16c99cd5c7beb5a70fd0243ce3e5ee4
app/Global.java
--- app/Global.java
+++ app/Global.java
@@ -210,12 +210,8 @@
         final long start = System.currentTimeMillis();
         return new Action.Simple() {
             public Result call(Http.Context ctx) throws Throwable {
-                if (ctx.session().get(UserApp.SESSION_USERID) == null) {
-                    UserApp.isRememberMe();
-                } else {
-                    UserApp.updatePreferredLanguage();
-                }
-
+                UserApp.initTokenUser();
+                UserApp.updatePreferredLanguage();
                 ctx.response().setHeader("Date", DateUtils.formatDate(new Date()));
                 ctx.response().setHeader("Cache-Control", "no-cache");
                 Result result = delegate.call(ctx);
app/controllers/UserApp.java
--- app/controllers/UserApp.java
+++ app/controllers/UserApp.java
@@ -25,7 +25,9 @@
 import models.*;
 import models.enumeration.Operation;
 import models.enumeration.UserState;
-import org.apache.commons.lang.StringUtils;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.shiro.crypto.RandomNumberGenerator;
 import org.apache.shiro.crypto.SecureRandomNumberGenerator;
 import org.apache.shiro.crypto.hash.Sha256Hash;
@@ -55,6 +57,8 @@
     public static final String SESSION_LOGINID = "loginId";
     public static final String SESSION_USERNAME = "userName";
     public static final String TOKEN = "yobi.token";
+    public static final String TOKEN_SEPARATOR = ":";
+    public static final int TOKEN_LENGTH = 2;
     public static final int MAX_AGE = 30*24*60*60;
     public static final String DEFAULT_AVATAR_URL
             = routes.Assets.at("images/default-avatar-128.png").url();
@@ -66,6 +70,7 @@
     public static final String DAYS_AGO_COOKIE = "daysAgo";
     public static final String DEFAULT_GROUP = "own";
     public static final String DEFAULT_SELECTED_TAB = "projects";
+    public static final String TOKEN_USER = "TOKEN_USER";
 
     /**
      * ajax 를 이용한 사용자 검색
@@ -173,7 +178,7 @@
 
         User authenticate = authenticateWithPlainPassword(sourceUser.loginId, sourceUser.password);
 
-        if (authenticate != null) {
+        if (!authenticate.isAnonymous()) {
             addUserInfoToSession(authenticate);
             if (sourceUser.rememberMe) {
                 setupRememberMe(authenticate);
@@ -196,55 +201,27 @@
     /**
      * loginId 와 hash 값을 이용해서 사용자 인증.
      * 인증에 성공하면 DB 에서 조회된 사용자 정보를 리턴
-     * 인증에 실패하면 null 리턴
+     * 인증에 실패하면 {@code User.anonymous} 리턴
      *
      * @param loginId 로그인ID
      * @param password hash된 비밀번호
      * @return
      */
     public static User authenticateWithHashedPassword(String loginId, String password) {
-        User user = User.findByLoginId(loginId);
-        return authenticate(user, password);
+        return authenticate(loginId, password, true);
     }
 
     /**
      * loginId 와 plain password 를 이용해서 사용자 인증
      * 인증에 성공하면 DB 에서 조회된 사용자 정보를 리턴
-     * 인증에 실패하면 null 리턴
+     * 인증에 실패하면 {@code User.anonymous} 리턴
      *
      * @param loginId 로그인ID
      * @param password 입력받은 비밀번호
      * @return
      */
     public static User authenticateWithPlainPassword(String loginId, String password) {
-        User user = User.findByLoginId(loginId);
-        if(user == User.anonymous) {
-            return null;
-        }
-        return authenticate(user, hashedPassword(password, user.passwordSalt));
-    }
-
-    /**
-     * 로그인 유지기능 사용 여부
-     * 로그인 유지 기능이 사용중이면 로그인쿠키를 생성하고 true 를 리턴
-     * 사용중이지 않으면 false 리턴
-     *
-     * @return 로그인 유지기능 사용 여부
-     */
-    public static boolean isRememberMe() {
-        // Remember Me
-        Cookie cookie = request().cookies().get(TOKEN);
-        if (cookie != null) {
-            String[] subject = cookie.value().split(":");
-            Logger.debug(cookie.value());
-            if(subject.length < 2) return false;
-            User user = authenticateWithHashedPassword(subject[0], subject[1]);
-            if (user != null) {
-                addUserInfoToSession(user);
-            }
-            return true;
-        }
-        return false;
+        return authenticate(loginId, password, false);
     }
 
     /**
@@ -342,22 +319,108 @@
     }
 
     /**
-     * 세션에 저장된 정보를 이용해서 사용자 객체를 생성한다
-     * 세션에 저장된 정보가 없다면 anonymous 객체가 리턴된다
-     *
-     * @return 세션 정보 기준 조회된 사용자 객체
+     * 세션에 저장된 정보를 이용해서 사용자 객체를 생성한다.
+     * 세션에 저장된 정보가 없다면 토큰 정보를 이용해서 사용자 객체를 생성한다.
+     * 세션과 토큰 쿠키에 저장된 정보가 없다면 anonymous 객체가 리턴된다.
+     * @return
      */
     public static User currentUser() {
+        User user = getUserFromSession();
+        if (!user.isAnonymous()) {
+            return user;
+        }
+        return getUserFromContext();
+    }
+
+    /**
+     * 세션 정보를 이용해서 사용자 객체를 가져온다.
+     * 세션 정보가 없거나 잘못된 정보라면 anonymous 객체를 반환한다.
+     * 잘못된 정보의 경우 세션 정보를 삭제한다.
+     * @return
+     */
+    private static User getUserFromSession() {
         String userId = session().get(SESSION_USERID);
-        if (StringUtils.isEmpty(userId) || !StringUtils.isNumeric(userId)) {
+        if (userId == null) {
             return User.anonymous;
         }
-        User foundUser = User.find.byId(Long.valueOf(userId));
-        if (foundUser == null) {
-            processLogout();
+        if (!StringUtils.isNumeric(userId)) {
+            return invalidSession();
+        }
+        User user = User.find.byId(Long.valueOf(userId));
+        if (user == null) {
+            return invalidSession();
+        }
+        return user;
+    }
+
+    /**
+     * {@link Http.Context#args} 에 저장된 사용자 객체를 가져온다.
+     * 저장된 객체가 없을 경우 {@link UserApp#initTokenUser()} 를 호출해서 객체를 생성, 저장한뒤 가져온다.
+     * @return
+     * @see UserApp#initTokenUser()
+     */
+    private static User getUserFromContext() {
+        Object cached = Http.Context.current().args.get(TOKEN_USER);
+        if (cached instanceof User) {
+            return (User) cached;
+        }
+        initTokenUser();
+        return (User) Http.Context.current().args.get(TOKEN_USER);
+    }
+
+    /**
+     * 토큰 정보를 이용해 생성한 사용자 객체를 {@link Http.Context#args} 에 저장한다.
+     * 생성된 사용자가 {@link User#anonymous} 가 아니고 존재하는 세션 정보가 없다면 세션 정보를 생성한다.
+     * @see UserApp#getUserFromToken()
+     */
+    public static void initTokenUser() {
+        User user = getUserFromToken();
+        Http.Context.current().args.put(TOKEN_USER, user);
+        if (!user.isAnonymous() && getUserFromSession().isAnonymous()) {
+            addUserInfoToSession(user);
+        }
+    }
+
+    /**
+     * 토큰 정보를 이용해서 사용자 객체를 가져온다.
+     * 토큰 정보가 없거나 잘못된 정보라면 anonymous 객체를 반환한다.
+     * 잘못된 정보의 경우 토큰 정보를 삭제한다.
+     * @return
+     */
+    private static User getUserFromToken() {
+        Cookie cookie = request().cookies().get(TOKEN);
+        if (cookie == null) {
             return User.anonymous;
         }
-        return foundUser;
+        String[] subject =  StringUtils.split(cookie.value(), TOKEN_SEPARATOR);
+        if (ArrayUtils.getLength(subject) != TOKEN_LENGTH) {
+            return invalidToken();
+        }
+        User user = authenticateWithHashedPassword(subject[0], subject[1]);
+        if (user.isAnonymous()) {
+            return invalidToken();
+        }
+        return user;
+    }
+
+    /**
+     * 세션 정보가 존재하지만 잘못된 정보일 경우
+     * 정보를 삭제하고 anonymous 를 반환
+     * @return
+     */
+    private static User invalidSession() {
+        session().clear();
+        return User.anonymous;
+    }
+
+    /**
+     * 토큰 정보가 존재하지만 잘못된 정보 일 경우
+     * 정보를 삭제하고 anonymous 를 반환
+     * @return
+     */
+    private static User invalidToken() {
+        response().discardCookie(TOKEN);
+        return User.anonymous;
     }
 
     /**
@@ -761,13 +824,16 @@
      *
      * 사용자 객체와 hash 값을 이용
      */
-    private static User authenticate(User user, String password) {
-        if (!user.isAnonymous()) {
-            if (user.password.equals(password)) {
-                return user;
-            }
+    private static User authenticate(String loginId, String password, boolean hashed) {
+        User user = User.findByLoginId(loginId);
+        if (user.isAnonymous()) {
+            return user;
         }
-        return null;
+        String hashedPassword = hashed ? password : hashedPassword(password, user.passwordSalt);
+        if (StringUtils.equals(user.password, hashedPassword)) {
+            return user;
+        }
+        return User.anonymous;
     }
 
     /*
app/utils/BasicAuthAction.java
--- app/utils/BasicAuthAction.java
+++ app/utils/BasicAuthAction.java
@@ -73,11 +73,7 @@
         if (authUser != null) {
             return UserApp.authenticateWithPlainPassword(authUser.loginId, authUser.password);
         } else {
-            if (isAnonymousSupported) {
-                return User.anonymous;
-            } else {
-                return null;
-            }
+            return User.anonymous;
         }
     }
 
@@ -85,7 +81,6 @@
     public Result call(Context context) throws Throwable {
         User user;
         try {
-
             user = authenticate(context.request());
         } catch (MalformedCredentialsException error) {
             return AccessLogger.log(context.request()
@@ -97,13 +92,11 @@
                     , null);
         }
 
-        if (user == null) {
-            return AccessLogger.log(context.request()
-                    , unauthorized(context.response())
-                    , null);
-        }
-
-        if (!user.isAnonymous()) {
+        if (user.isAnonymous()) {
+            if (!isAnonymousSupported) {
+                return AccessLogger.log(context.request(), unauthorized(context.response()), null);
+            }
+        } else {
             UserApp.addUserInfoToSession(user);
         }
 
conf/test-data.yml
--- conf/test-data.yml
+++ conf/test-data.yml
@@ -36,6 +36,13 @@
         passwordSalt:   '[B@737aa47f'
         email:          alecsiel@nhn.com
         createdDate:           2013-11-18 13:30:00
+    - !!models.User
+        name:           kjkmadness
+        loginId:        kjkmadness
+        password:       ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4=
+        passwordSalt:   '[B@737aa47f'
+        email:          kjkmadness@nhn.com
+        createdDate:           2014-03-06 13:30:00
 
 # Projects
 projects:
test/controllers/UserAppTest.java
--- test/controllers/UserAppTest.java
+++ test/controllers/UserAppTest.java
@@ -2,17 +2,22 @@
 
 import models.User;
 import models.enumeration.UserState;
+
 import org.junit.*;
 
 import java.util.*;
 
 import play.mvc.*;
+import play.mvc.Http.*;
+import support.ContextTest;
 import utils.JodaDateUtil;
 
 import static play.test.Helpers.*;
 import static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.MapAssert.entry;
+import static org.mockito.Mockito.*;
 
-public class UserAppTest {
+public class UserAppTest extends ContextTest {
     @BeforeClass
     public static void beforeClass() {
         callAction(
@@ -165,4 +170,333 @@
             }
         });
     }
+
+    @Test
+    public void authenticateWithPlainPassword() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "kjkmadness";
+                String password = "pass";
+
+                // When
+                User user = UserApp.authenticateWithPlainPassword(loginId, password);
+
+                // Then
+                assertThat(user).isNotNull();
+                assertThat(user.isAnonymous()).isFalse();
+                assertThat(user.loginId).isEqualTo(loginId);
+            }
+        });
+    }
+
+    @Test
+    public void authenticateWithPlainPasswordWrongPassword() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "kjkmadness";
+                String password = "wrong";
+
+                // When
+                User user = UserApp.authenticateWithPlainPassword(loginId, password);
+
+                // Then
+                assertThat(user).isNotNull();
+                assertThat(user.isAnonymous()).isTrue();
+            }
+        });
+    }
+
+    @Test
+    public void authenticateWithPlainPasswordNotExist() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "notexist";
+                String password = "pass";
+
+                // When
+                User user = UserApp.authenticateWithPlainPassword(loginId, password);
+
+                // Then
+                assertThat(user).isNotNull();
+                assertThat(user.isAnonymous()).isTrue();
+            }
+        });
+    }
+
+    @Test
+    public void authenticateWithHashedPassword() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "kjkmadness";
+                String password = "ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4=";
+
+                // When
+                User user = UserApp.authenticateWithHashedPassword(loginId, password);
+
+                // Then
+                assertThat(user).isNotNull();
+                assertThat(user.isAnonymous()).isFalse();
+                assertThat(user.loginId).isEqualTo(loginId);
+            }
+        });
+    }
+
+    @Test
+    public void authenticateWithHashedPasswordWrongPassword() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "kjkmadness";
+                String password = "wrong";
+
+                // When
+                User user = UserApp.authenticateWithHashedPassword(loginId, password);
+
+                // Then
+                assertThat(user).isNotNull();
+                assertThat(user.isAnonymous()).isTrue();
+            }
+        });
+    }
+
+    @Test
+    public void authenticateWithHashedPasswordNotExist() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "notexist";
+                String password = "ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4=";
+
+                // When
+                User user = UserApp.authenticateWithHashedPassword(loginId, password);
+
+                // Then
+                assertThat(user).isNotNull();
+                assertThat(user.isAnonymous()).isTrue();
+            }
+        });
+    }
+
+    @Test
+    public void login() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "kjkmadness";
+                String password = "pass";
+                User user = User.findByLoginId(loginId);
+                Map<String, String> data = new HashMap<>();
+                data.put("loginId", loginId);
+                data.put("password", password);
+
+                // When
+                Result result = callAction(controllers.routes.ref.UserApp.login(), fakeRequest()
+                        .withFormUrlEncodedBody(data));
+
+                // Then
+                assertThat(status(result)).isEqualTo(SEE_OTHER);
+                assertThat(header(LOCATION, result)).isEqualTo(routes.Application.index().url());
+                assertThat(session(result)).includes(
+                        entry(UserApp.SESSION_USERID, String.valueOf(user.id)),
+                        entry(UserApp.SESSION_LOGINID, user.loginId),
+                        entry(UserApp.SESSION_USERNAME, user.name));
+            }
+        });
+    }
+
+    @Test
+    public void loginWrongPassword() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "kjkmadness";
+                String password = "wrong";
+                Map<String, String> data = new HashMap<>();
+                data.put("loginId", loginId);
+                data.put("password", password);
+
+                // When
+                Result result = callAction(controllers.routes.ref.UserApp.login(), fakeRequest()
+                        .withFormUrlEncodedBody(data));
+
+                // Then
+                assertThat(status(result)).isEqualTo(SEE_OTHER);
+                assertThat(header(LOCATION, result)).isEqualTo(routes.UserApp.loginForm().url());
+                assertThat(session(result)).isEmpty();
+            }
+        });
+    }
+
+    @Test
+    public void currentUserContext() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                User expected = User.find.byId(1L);
+                context().withArg(UserApp.TOKEN_USER, expected);
+
+                // When
+                User user = UserApp.currentUser();
+
+                // Then
+                assertThat(user).isEqualTo(expected);
+            }
+        });
+    }
+
+    @Test
+    public void currentUserSession() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                Long id = 1L;
+                context().withSession(UserApp.SESSION_USERID, String.valueOf(id));
+
+                // When
+                User user = UserApp.currentUser();
+
+                // Then
+                assertThat(user).isNotEqualTo(User.anonymous);
+                assertThat(user.id).isEqualTo(id);
+            }
+        });
+    }
+
+    @Test
+    public void currentUserSessionNotNumeric() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                Context context = context().withSession(UserApp.SESSION_USERID, "string");
+
+                // When
+                User user = UserApp.currentUser();
+
+                // Then
+                assertThat(user).isEqualTo(User.anonymous);
+                assertThat(context.session()).isEmpty();
+            }
+        });
+    }
+
+    @Test
+    public void currentUserSessionNoUser() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                Context context = context().withSession(UserApp.SESSION_USERID, "0");
+
+                // When
+                User user = UserApp.currentUser();
+
+                // Then
+                assertThat(user).isEqualTo(User.anonymous);
+                assertThat(context.session()).isEmpty();
+            }
+        });
+    }
+
+    @Test
+    public void currentUserToken() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "kjkmadness";
+                String password = "ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4=";
+                String token = loginId + UserApp.TOKEN_SEPARATOR + password;
+                Context context = context().withCookie(UserApp.TOKEN, token);
+
+                // When
+                User user = UserApp.currentUser();
+
+                // Then
+                assertThat(user).isNotEqualTo(User.anonymous);
+                assertThat(user.loginId).isEqualTo(loginId);
+                assertThat(context.session()).includes(
+                        entry(UserApp.SESSION_USERID, String.valueOf(user.id)),
+                        entry(UserApp.SESSION_LOGINID, user.loginId),
+                        entry(UserApp.SESSION_USERNAME, user.name));
+            }
+        });
+    }
+
+    @Test
+    public void currentUserTokenInvalidLength() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "kjkmadness";
+                String password = "ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4=";
+                String token = loginId + UserApp.TOKEN_SEPARATOR + password
+                        + UserApp.TOKEN_SEPARATOR + "dummy";
+                Context context = context().withCookie(UserApp.TOKEN, token);
+
+                // When
+                User user = UserApp.currentUser();
+
+                // Then
+                assertThat(user).isEqualTo(User.anonymous);
+                assertThat(context.session()).isEmpty();
+                verify(context.response()).discardCookie(UserApp.TOKEN);
+            }
+        });
+    }
+
+    @Test
+    public void currentUserTokenNoUser() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                String loginId = "kjkmadness";
+                String password = "dummy";
+                String token = loginId + UserApp.TOKEN_SEPARATOR + password;
+                Context context = context().withCookie(UserApp.TOKEN, token);
+
+                // When
+                User user = UserApp.currentUser();
+
+                // Then
+                assertThat(user).isEqualTo(User.anonymous);
+                assertThat(context.session()).isEmpty();
+                verify(context.response()).discardCookie(UserApp.TOKEN);
+            }
+        });
+    }
+
+    @Test
+    public void currentUserAnonymous() {
+        running(support.Helpers.makeTestApplication(), new Runnable() {
+            @Override
+            public void run() {
+                // Given
+                Context context = context();
+
+                // When
+                User user = UserApp.currentUser();
+
+                // Then
+                assertThat(user).isEqualTo(User.anonymous);
+                assertThat(context.session()).isEmpty();
+            }
+        });
+    }
 }
test/models/PasswordResetTest.java
--- test/models/PasswordResetTest.java
+++ test/models/PasswordResetTest.java
@@ -136,7 +136,7 @@
 
         //Then
         assertThat(result).isTrue();
-        assertThat(UserApp.authenticateWithPlainPassword(userId, newPassword)).isNotNull();
+        assertThat(UserApp.authenticateWithPlainPassword(userId, newPassword).isAnonymous()).isFalse();
     }
 
     @Test
@@ -153,7 +153,7 @@
 
         //Then
         assertThat(result).isFalse();
-        assertThat(UserApp.authenticateWithPlainPassword(userId, newPassword)).isNull();
+        assertThat(UserApp.authenticateWithPlainPassword(userId, newPassword).isAnonymous()).isTrue();
     }
 
     private static boolean hashStringExist(String loginId) {
test/models/UserTest.java
--- test/models/UserTest.java
+++ test/models/UserTest.java
@@ -70,7 +70,7 @@
         // When
         Map<String, String> userOptions = User.options();
         // Then
-        assertThat(userOptions).hasSize(6);
+        assertThat(userOptions).hasSize(7);
     }
 
 	@Test
 
test/support/ContextTest.java (added)
+++ test/support/ContextTest.java
@@ -0,0 +1,100 @@
+/**
+ * Yobi, Project Hosting SW
+ *
+ * Copyright 2013 NAVER Corp.
+ * http://yobi.io
+ *
+ * @Author kjkmadness
+ *
+ * Licensed 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 support;
+
+import static org.mockito.Mockito.*;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.*;
+
+import play.api.mvc.RequestHeader;
+import play.mvc.Http.*;
+
+public abstract class ContextTest {
+    private FakeContext context;
+
+    @Before
+    public final void initContext() {
+        context = new FakeContext(1L,
+                mock(RequestHeader.class),
+                mock(Request.class),
+                Collections.<String, String> emptyMap(),
+                Collections.<String, String> emptyMap(),
+                Collections.<String, Object> emptyMap());
+        Context.current.set(context);
+    }
+
+    @After
+    public final void removeContext() {
+        Context.current.remove();
+    }
+
+    public FakeContext context() {
+        return context;
+    }
+
+    protected static class FakeContext extends Context {
+        private Response response;
+        private Map<String, String[]> headers = new HashMap<>();
+
+        FakeContext(Long id, RequestHeader header, Request request,
+                Map<String, String> sessionData, Map<String, String> flashData,
+                Map<String, Object> args) {
+            super(id, header, request, sessionData, flashData, args);
+            response = mock(Response.class);
+            when(request.cookies()).thenReturn(mock(Cookies.class));
+            when(request.headers()).thenReturn(headers);
+        }
+
+        @Override
+        public Response response() {
+            return response;
+        }
+
+        public FakeContext withHeader(String name, String value) {
+            headers.put(name, new String[] {value});
+            when(request().getHeader(name)).thenReturn(value);
+            return this;
+        }
+
+        public FakeContext withCookie(String name, String value) {
+            Cookie cookie = mock(Cookie.class);
+            when(cookie.name()).thenReturn(name);
+            when(cookie.value()).thenReturn(value);
+            when(request().cookie(name)).thenReturn(cookie);
+            when(request().cookies().get(name)).thenReturn(cookie);
+            return this;
+        }
+
+        public FakeContext withSession(String name, String value) {
+            session().put(name, value);
+            return this;
+        }
+
+        public FakeContext withArg(String name, Object value) {
+            args.put(name, value);
+            return this;
+        }
+    }
+}
test/utils/BasicAuthActionTest.java
--- test/utils/BasicAuthActionTest.java
+++ test/utils/BasicAuthActionTest.java
@@ -2,17 +2,43 @@
 
 import static org.junit.Assert.*;
 import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.MapAssert.entry;
+import static org.mockito.Mockito.*;
+
+import static play.test.Helpers.*;
+
+import static support.Helpers.*;
 
 import java.io.UnsupportedEncodingException;
 
 import models.User;
 
 import org.apache.commons.codec.binary.Base64;
-import org.junit.Test;
+import org.junit.*;
 
+import controllers.UserApp;
+
+import play.mvc.Action;
+import play.mvc.Http;
+import play.mvc.Http.*;
+import play.test.FakeApplication;
+
+import support.ContextTest;
 import utils.BasicAuthAction;
 
-public class BasicAuthActionTest {
+public class BasicAuthActionTest extends ContextTest {
+    private FakeApplication application;
+
+    @Before
+    public void before() {
+        application = makeTestApplication();
+        start(application);
+    }
+
+    @After
+    public void after() {
+        stop(application);
+    }
 
     @Test
     public void parseCredentials() {
@@ -77,4 +103,41 @@
         }
     }
 
+    @Test
+    public void call() throws Throwable {
+        // Given
+        String loginId = "kjkmadness";
+        String password = "pass";
+        String credential = "Basic "
+                + new String(Base64.encodeBase64((loginId + ":" + password).getBytes("UTF-8")));
+        User user = User.findByLoginId(loginId);
+        Context context = context().withHeader(Http.HeaderNames.AUTHORIZATION, credential);
+        BasicAuthAction action = new BasicAuthAction();
+        action.delegate = mock(Action.class);
+
+        // When
+        action.call(context);
+
+        // Then
+        assertThat(context.session()).includes(
+                entry(UserApp.SESSION_USERID, String.valueOf(user.id)),
+                entry(UserApp.SESSION_LOGINID, user.loginId),
+                entry(UserApp.SESSION_USERNAME, user.name));
+        verify(action.delegate).call(context);
+    }
+
+    @Test
+    public void callAnonymous() throws Throwable {
+        // Given
+        Context context = context();
+        BasicAuthAction action = new BasicAuthAction();
+        action.delegate = mock(Action.class);
+
+        // When
+        action.call(context);
+
+        // Then
+        assertThat(context.session()).isEmpty();
+        verify(action.delegate).call(context);
+    }
 }
Add a comment
List