
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
... | ... | @@ -210,12 +210,8 @@ |
210 | 210 |
final long start = System.currentTimeMillis(); |
211 | 211 |
return new Action.Simple() { |
212 | 212 |
public Result call(Http.Context ctx) throws Throwable { |
213 |
- if (ctx.session().get(UserApp.SESSION_USERID) == null) { |
|
214 |
- UserApp.isRememberMe(); |
|
215 |
- } else { |
|
216 |
- UserApp.updatePreferredLanguage(); |
|
217 |
- } |
|
218 |
- |
|
213 |
+ UserApp.initTokenUser(); |
|
214 |
+ UserApp.updatePreferredLanguage(); |
|
219 | 215 |
ctx.response().setHeader("Date", DateUtils.formatDate(new Date())); |
220 | 216 |
ctx.response().setHeader("Cache-Control", "no-cache"); |
221 | 217 |
Result result = delegate.call(ctx); |
--- app/controllers/UserApp.java
+++ app/controllers/UserApp.java
... | ... | @@ -25,7 +25,9 @@ |
25 | 25 |
import models.*; |
26 | 26 |
import models.enumeration.Operation; |
27 | 27 |
import models.enumeration.UserState; |
28 |
-import org.apache.commons.lang.StringUtils; |
|
28 |
+ |
|
29 |
+import org.apache.commons.lang3.ArrayUtils; |
|
30 |
+import org.apache.commons.lang3.StringUtils; |
|
29 | 31 |
import org.apache.shiro.crypto.RandomNumberGenerator; |
30 | 32 |
import org.apache.shiro.crypto.SecureRandomNumberGenerator; |
31 | 33 |
import org.apache.shiro.crypto.hash.Sha256Hash; |
... | ... | @@ -55,6 +57,8 @@ |
55 | 57 |
public static final String SESSION_LOGINID = "loginId"; |
56 | 58 |
public static final String SESSION_USERNAME = "userName"; |
57 | 59 |
public static final String TOKEN = "yobi.token"; |
60 |
+ public static final String TOKEN_SEPARATOR = ":"; |
|
61 |
+ public static final int TOKEN_LENGTH = 2; |
|
58 | 62 |
public static final int MAX_AGE = 30*24*60*60; |
59 | 63 |
public static final String DEFAULT_AVATAR_URL |
60 | 64 |
= routes.Assets.at("images/default-avatar-128.png").url(); |
... | ... | @@ -66,6 +70,7 @@ |
66 | 70 |
public static final String DAYS_AGO_COOKIE = "daysAgo"; |
67 | 71 |
public static final String DEFAULT_GROUP = "own"; |
68 | 72 |
public static final String DEFAULT_SELECTED_TAB = "projects"; |
73 |
+ public static final String TOKEN_USER = "TOKEN_USER"; |
|
69 | 74 |
|
70 | 75 |
/** |
71 | 76 |
* ajax 를 이용한 사용자 검색 |
... | ... | @@ -173,7 +178,7 @@ |
173 | 178 |
|
174 | 179 |
User authenticate = authenticateWithPlainPassword(sourceUser.loginId, sourceUser.password); |
175 | 180 |
|
176 |
- if (authenticate != null) { |
|
181 |
+ if (!authenticate.isAnonymous()) { |
|
177 | 182 |
addUserInfoToSession(authenticate); |
178 | 183 |
if (sourceUser.rememberMe) { |
179 | 184 |
setupRememberMe(authenticate); |
... | ... | @@ -196,55 +201,27 @@ |
196 | 201 |
/** |
197 | 202 |
* loginId 와 hash 값을 이용해서 사용자 인증. |
198 | 203 |
* 인증에 성공하면 DB 에서 조회된 사용자 정보를 리턴 |
199 |
- * 인증에 실패하면 null 리턴 |
|
204 |
+ * 인증에 실패하면 {@code User.anonymous} 리턴 |
|
200 | 205 |
* |
201 | 206 |
* @param loginId 로그인ID |
202 | 207 |
* @param password hash된 비밀번호 |
203 | 208 |
* @return |
204 | 209 |
*/ |
205 | 210 |
public static User authenticateWithHashedPassword(String loginId, String password) { |
206 |
- User user = User.findByLoginId(loginId); |
|
207 |
- return authenticate(user, password); |
|
211 |
+ return authenticate(loginId, password, true); |
|
208 | 212 |
} |
209 | 213 |
|
210 | 214 |
/** |
211 | 215 |
* loginId 와 plain password 를 이용해서 사용자 인증 |
212 | 216 |
* 인증에 성공하면 DB 에서 조회된 사용자 정보를 리턴 |
213 |
- * 인증에 실패하면 null 리턴 |
|
217 |
+ * 인증에 실패하면 {@code User.anonymous} 리턴 |
|
214 | 218 |
* |
215 | 219 |
* @param loginId 로그인ID |
216 | 220 |
* @param password 입력받은 비밀번호 |
217 | 221 |
* @return |
218 | 222 |
*/ |
219 | 223 |
public static User authenticateWithPlainPassword(String loginId, String password) { |
220 |
- User user = User.findByLoginId(loginId); |
|
221 |
- if(user == User.anonymous) { |
|
222 |
- return null; |
|
223 |
- } |
|
224 |
- return authenticate(user, hashedPassword(password, user.passwordSalt)); |
|
225 |
- } |
|
226 |
- |
|
227 |
- /** |
|
228 |
- * 로그인 유지기능 사용 여부 |
|
229 |
- * 로그인 유지 기능이 사용중이면 로그인쿠키를 생성하고 true 를 리턴 |
|
230 |
- * 사용중이지 않으면 false 리턴 |
|
231 |
- * |
|
232 |
- * @return 로그인 유지기능 사용 여부 |
|
233 |
- */ |
|
234 |
- public static boolean isRememberMe() { |
|
235 |
- // Remember Me |
|
236 |
- Cookie cookie = request().cookies().get(TOKEN); |
|
237 |
- if (cookie != null) { |
|
238 |
- String[] subject = cookie.value().split(":"); |
|
239 |
- Logger.debug(cookie.value()); |
|
240 |
- if(subject.length < 2) return false; |
|
241 |
- User user = authenticateWithHashedPassword(subject[0], subject[1]); |
|
242 |
- if (user != null) { |
|
243 |
- addUserInfoToSession(user); |
|
244 |
- } |
|
245 |
- return true; |
|
246 |
- } |
|
247 |
- return false; |
|
224 |
+ return authenticate(loginId, password, false); |
|
248 | 225 |
} |
249 | 226 |
|
250 | 227 |
/** |
... | ... | @@ -342,22 +319,108 @@ |
342 | 319 |
} |
343 | 320 |
|
344 | 321 |
/** |
345 |
- * 세션에 저장된 정보를 이용해서 사용자 객체를 생성한다 |
|
346 |
- * 세션에 저장된 정보가 없다면 anonymous 객체가 리턴된다 |
|
347 |
- * |
|
348 |
- * @return 세션 정보 기준 조회된 사용자 객체 |
|
322 |
+ * 세션에 저장된 정보를 이용해서 사용자 객체를 생성한다. |
|
323 |
+ * 세션에 저장된 정보가 없다면 토큰 정보를 이용해서 사용자 객체를 생성한다. |
|
324 |
+ * 세션과 토큰 쿠키에 저장된 정보가 없다면 anonymous 객체가 리턴된다. |
|
325 |
+ * @return |
|
349 | 326 |
*/ |
350 | 327 |
public static User currentUser() { |
328 |
+ User user = getUserFromSession(); |
|
329 |
+ if (!user.isAnonymous()) { |
|
330 |
+ return user; |
|
331 |
+ } |
|
332 |
+ return getUserFromContext(); |
|
333 |
+ } |
|
334 |
+ |
|
335 |
+ /** |
|
336 |
+ * 세션 정보를 이용해서 사용자 객체를 가져온다. |
|
337 |
+ * 세션 정보가 없거나 잘못된 정보라면 anonymous 객체를 반환한다. |
|
338 |
+ * 잘못된 정보의 경우 세션 정보를 삭제한다. |
|
339 |
+ * @return |
|
340 |
+ */ |
|
341 |
+ private static User getUserFromSession() { |
|
351 | 342 |
String userId = session().get(SESSION_USERID); |
352 |
- if (StringUtils.isEmpty(userId) || !StringUtils.isNumeric(userId)) { |
|
343 |
+ if (userId == null) { |
|
353 | 344 |
return User.anonymous; |
354 | 345 |
} |
355 |
- User foundUser = User.find.byId(Long.valueOf(userId)); |
|
356 |
- if (foundUser == null) { |
|
357 |
- processLogout(); |
|
346 |
+ if (!StringUtils.isNumeric(userId)) { |
|
347 |
+ return invalidSession(); |
|
348 |
+ } |
|
349 |
+ User user = User.find.byId(Long.valueOf(userId)); |
|
350 |
+ if (user == null) { |
|
351 |
+ return invalidSession(); |
|
352 |
+ } |
|
353 |
+ return user; |
|
354 |
+ } |
|
355 |
+ |
|
356 |
+ /** |
|
357 |
+ * {@link Http.Context#args} 에 저장된 사용자 객체를 가져온다. |
|
358 |
+ * 저장된 객체가 없을 경우 {@link UserApp#initTokenUser()} 를 호출해서 객체를 생성, 저장한뒤 가져온다. |
|
359 |
+ * @return |
|
360 |
+ * @see UserApp#initTokenUser() |
|
361 |
+ */ |
|
362 |
+ private static User getUserFromContext() { |
|
363 |
+ Object cached = Http.Context.current().args.get(TOKEN_USER); |
|
364 |
+ if (cached instanceof User) { |
|
365 |
+ return (User) cached; |
|
366 |
+ } |
|
367 |
+ initTokenUser(); |
|
368 |
+ return (User) Http.Context.current().args.get(TOKEN_USER); |
|
369 |
+ } |
|
370 |
+ |
|
371 |
+ /** |
|
372 |
+ * 토큰 정보를 이용해 생성한 사용자 객체를 {@link Http.Context#args} 에 저장한다. |
|
373 |
+ * 생성된 사용자가 {@link User#anonymous} 가 아니고 존재하는 세션 정보가 없다면 세션 정보를 생성한다. |
|
374 |
+ * @see UserApp#getUserFromToken() |
|
375 |
+ */ |
|
376 |
+ public static void initTokenUser() { |
|
377 |
+ User user = getUserFromToken(); |
|
378 |
+ Http.Context.current().args.put(TOKEN_USER, user); |
|
379 |
+ if (!user.isAnonymous() && getUserFromSession().isAnonymous()) { |
|
380 |
+ addUserInfoToSession(user); |
|
381 |
+ } |
|
382 |
+ } |
|
383 |
+ |
|
384 |
+ /** |
|
385 |
+ * 토큰 정보를 이용해서 사용자 객체를 가져온다. |
|
386 |
+ * 토큰 정보가 없거나 잘못된 정보라면 anonymous 객체를 반환한다. |
|
387 |
+ * 잘못된 정보의 경우 토큰 정보를 삭제한다. |
|
388 |
+ * @return |
|
389 |
+ */ |
|
390 |
+ private static User getUserFromToken() { |
|
391 |
+ Cookie cookie = request().cookies().get(TOKEN); |
|
392 |
+ if (cookie == null) { |
|
358 | 393 |
return User.anonymous; |
359 | 394 |
} |
360 |
- return foundUser; |
|
395 |
+ String[] subject = StringUtils.split(cookie.value(), TOKEN_SEPARATOR); |
|
396 |
+ if (ArrayUtils.getLength(subject) != TOKEN_LENGTH) { |
|
397 |
+ return invalidToken(); |
|
398 |
+ } |
|
399 |
+ User user = authenticateWithHashedPassword(subject[0], subject[1]); |
|
400 |
+ if (user.isAnonymous()) { |
|
401 |
+ return invalidToken(); |
|
402 |
+ } |
|
403 |
+ return user; |
|
404 |
+ } |
|
405 |
+ |
|
406 |
+ /** |
|
407 |
+ * 세션 정보가 존재하지만 잘못된 정보일 경우 |
|
408 |
+ * 정보를 삭제하고 anonymous 를 반환 |
|
409 |
+ * @return |
|
410 |
+ */ |
|
411 |
+ private static User invalidSession() { |
|
412 |
+ session().clear(); |
|
413 |
+ return User.anonymous; |
|
414 |
+ } |
|
415 |
+ |
|
416 |
+ /** |
|
417 |
+ * 토큰 정보가 존재하지만 잘못된 정보 일 경우 |
|
418 |
+ * 정보를 삭제하고 anonymous 를 반환 |
|
419 |
+ * @return |
|
420 |
+ */ |
|
421 |
+ private static User invalidToken() { |
|
422 |
+ response().discardCookie(TOKEN); |
|
423 |
+ return User.anonymous; |
|
361 | 424 |
} |
362 | 425 |
|
363 | 426 |
/** |
... | ... | @@ -761,13 +824,16 @@ |
761 | 824 |
* |
762 | 825 |
* 사용자 객체와 hash 값을 이용 |
763 | 826 |
*/ |
764 |
- private static User authenticate(User user, String password) { |
|
765 |
- if (!user.isAnonymous()) { |
|
766 |
- if (user.password.equals(password)) { |
|
767 |
- return user; |
|
768 |
- } |
|
827 |
+ private static User authenticate(String loginId, String password, boolean hashed) { |
|
828 |
+ User user = User.findByLoginId(loginId); |
|
829 |
+ if (user.isAnonymous()) { |
|
830 |
+ return user; |
|
769 | 831 |
} |
770 |
- return null; |
|
832 |
+ String hashedPassword = hashed ? password : hashedPassword(password, user.passwordSalt); |
|
833 |
+ if (StringUtils.equals(user.password, hashedPassword)) { |
|
834 |
+ return user; |
|
835 |
+ } |
|
836 |
+ return User.anonymous; |
|
771 | 837 |
} |
772 | 838 |
|
773 | 839 |
/* |
--- app/utils/BasicAuthAction.java
+++ app/utils/BasicAuthAction.java
... | ... | @@ -73,11 +73,7 @@ |
73 | 73 |
if (authUser != null) { |
74 | 74 |
return UserApp.authenticateWithPlainPassword(authUser.loginId, authUser.password); |
75 | 75 |
} else { |
76 |
- if (isAnonymousSupported) { |
|
77 |
- return User.anonymous; |
|
78 |
- } else { |
|
79 |
- return null; |
|
80 |
- } |
|
76 |
+ return User.anonymous; |
|
81 | 77 |
} |
82 | 78 |
} |
83 | 79 |
|
... | ... | @@ -85,7 +81,6 @@ |
85 | 81 |
public Result call(Context context) throws Throwable { |
86 | 82 |
User user; |
87 | 83 |
try { |
88 |
- |
|
89 | 84 |
user = authenticate(context.request()); |
90 | 85 |
} catch (MalformedCredentialsException error) { |
91 | 86 |
return AccessLogger.log(context.request() |
... | ... | @@ -97,13 +92,11 @@ |
97 | 92 |
, null); |
98 | 93 |
} |
99 | 94 |
|
100 |
- if (user == null) { |
|
101 |
- return AccessLogger.log(context.request() |
|
102 |
- , unauthorized(context.response()) |
|
103 |
- , null); |
|
104 |
- } |
|
105 |
- |
|
106 |
- if (!user.isAnonymous()) { |
|
95 |
+ if (user.isAnonymous()) { |
|
96 |
+ if (!isAnonymousSupported) { |
|
97 |
+ return AccessLogger.log(context.request(), unauthorized(context.response()), null); |
|
98 |
+ } |
|
99 |
+ } else { |
|
107 | 100 |
UserApp.addUserInfoToSession(user); |
108 | 101 |
} |
109 | 102 |
|
--- conf/test-data.yml
+++ conf/test-data.yml
... | ... | @@ -36,6 +36,13 @@ |
36 | 36 |
passwordSalt: '[B@737aa47f' |
37 | 37 |
email: alecsiel@nhn.com |
38 | 38 |
createdDate: 2013-11-18 13:30:00 |
39 |
+ - !!models.User |
|
40 |
+ name: kjkmadness |
|
41 |
+ loginId: kjkmadness |
|
42 |
+ password: ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4= |
|
43 |
+ passwordSalt: '[B@737aa47f' |
|
44 |
+ email: kjkmadness@nhn.com |
|
45 |
+ createdDate: 2014-03-06 13:30:00 |
|
39 | 46 |
|
40 | 47 |
# Projects |
41 | 48 |
projects: |
--- test/controllers/UserAppTest.java
+++ test/controllers/UserAppTest.java
... | ... | @@ -2,17 +2,22 @@ |
2 | 2 |
|
3 | 3 |
import models.User; |
4 | 4 |
import models.enumeration.UserState; |
5 |
+ |
|
5 | 6 |
import org.junit.*; |
6 | 7 |
|
7 | 8 |
import java.util.*; |
8 | 9 |
|
9 | 10 |
import play.mvc.*; |
11 |
+import play.mvc.Http.*; |
|
12 |
+import support.ContextTest; |
|
10 | 13 |
import utils.JodaDateUtil; |
11 | 14 |
|
12 | 15 |
import static play.test.Helpers.*; |
13 | 16 |
import static org.fest.assertions.Assertions.*; |
17 |
+import static org.fest.assertions.MapAssert.entry; |
|
18 |
+import static org.mockito.Mockito.*; |
|
14 | 19 |
|
15 |
-public class UserAppTest { |
|
20 |
+public class UserAppTest extends ContextTest { |
|
16 | 21 |
@BeforeClass |
17 | 22 |
public static void beforeClass() { |
18 | 23 |
callAction( |
... | ... | @@ -165,4 +170,333 @@ |
165 | 170 |
} |
166 | 171 |
}); |
167 | 172 |
} |
173 |
+ |
|
174 |
+ @Test |
|
175 |
+ public void authenticateWithPlainPassword() { |
|
176 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
177 |
+ @Override |
|
178 |
+ public void run() { |
|
179 |
+ // Given |
|
180 |
+ String loginId = "kjkmadness"; |
|
181 |
+ String password = "pass"; |
|
182 |
+ |
|
183 |
+ // When |
|
184 |
+ User user = UserApp.authenticateWithPlainPassword(loginId, password); |
|
185 |
+ |
|
186 |
+ // Then |
|
187 |
+ assertThat(user).isNotNull(); |
|
188 |
+ assertThat(user.isAnonymous()).isFalse(); |
|
189 |
+ assertThat(user.loginId).isEqualTo(loginId); |
|
190 |
+ } |
|
191 |
+ }); |
|
192 |
+ } |
|
193 |
+ |
|
194 |
+ @Test |
|
195 |
+ public void authenticateWithPlainPasswordWrongPassword() { |
|
196 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
197 |
+ @Override |
|
198 |
+ public void run() { |
|
199 |
+ // Given |
|
200 |
+ String loginId = "kjkmadness"; |
|
201 |
+ String password = "wrong"; |
|
202 |
+ |
|
203 |
+ // When |
|
204 |
+ User user = UserApp.authenticateWithPlainPassword(loginId, password); |
|
205 |
+ |
|
206 |
+ // Then |
|
207 |
+ assertThat(user).isNotNull(); |
|
208 |
+ assertThat(user.isAnonymous()).isTrue(); |
|
209 |
+ } |
|
210 |
+ }); |
|
211 |
+ } |
|
212 |
+ |
|
213 |
+ @Test |
|
214 |
+ public void authenticateWithPlainPasswordNotExist() { |
|
215 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
216 |
+ @Override |
|
217 |
+ public void run() { |
|
218 |
+ // Given |
|
219 |
+ String loginId = "notexist"; |
|
220 |
+ String password = "pass"; |
|
221 |
+ |
|
222 |
+ // When |
|
223 |
+ User user = UserApp.authenticateWithPlainPassword(loginId, password); |
|
224 |
+ |
|
225 |
+ // Then |
|
226 |
+ assertThat(user).isNotNull(); |
|
227 |
+ assertThat(user.isAnonymous()).isTrue(); |
|
228 |
+ } |
|
229 |
+ }); |
|
230 |
+ } |
|
231 |
+ |
|
232 |
+ @Test |
|
233 |
+ public void authenticateWithHashedPassword() { |
|
234 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
235 |
+ @Override |
|
236 |
+ public void run() { |
|
237 |
+ // Given |
|
238 |
+ String loginId = "kjkmadness"; |
|
239 |
+ String password = "ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4="; |
|
240 |
+ |
|
241 |
+ // When |
|
242 |
+ User user = UserApp.authenticateWithHashedPassword(loginId, password); |
|
243 |
+ |
|
244 |
+ // Then |
|
245 |
+ assertThat(user).isNotNull(); |
|
246 |
+ assertThat(user.isAnonymous()).isFalse(); |
|
247 |
+ assertThat(user.loginId).isEqualTo(loginId); |
|
248 |
+ } |
|
249 |
+ }); |
|
250 |
+ } |
|
251 |
+ |
|
252 |
+ @Test |
|
253 |
+ public void authenticateWithHashedPasswordWrongPassword() { |
|
254 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
255 |
+ @Override |
|
256 |
+ public void run() { |
|
257 |
+ // Given |
|
258 |
+ String loginId = "kjkmadness"; |
|
259 |
+ String password = "wrong"; |
|
260 |
+ |
|
261 |
+ // When |
|
262 |
+ User user = UserApp.authenticateWithHashedPassword(loginId, password); |
|
263 |
+ |
|
264 |
+ // Then |
|
265 |
+ assertThat(user).isNotNull(); |
|
266 |
+ assertThat(user.isAnonymous()).isTrue(); |
|
267 |
+ } |
|
268 |
+ }); |
|
269 |
+ } |
|
270 |
+ |
|
271 |
+ @Test |
|
272 |
+ public void authenticateWithHashedPasswordNotExist() { |
|
273 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
274 |
+ @Override |
|
275 |
+ public void run() { |
|
276 |
+ // Given |
|
277 |
+ String loginId = "notexist"; |
|
278 |
+ String password = "ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4="; |
|
279 |
+ |
|
280 |
+ // When |
|
281 |
+ User user = UserApp.authenticateWithHashedPassword(loginId, password); |
|
282 |
+ |
|
283 |
+ // Then |
|
284 |
+ assertThat(user).isNotNull(); |
|
285 |
+ assertThat(user.isAnonymous()).isTrue(); |
|
286 |
+ } |
|
287 |
+ }); |
|
288 |
+ } |
|
289 |
+ |
|
290 |
+ @Test |
|
291 |
+ public void login() { |
|
292 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
293 |
+ @Override |
|
294 |
+ public void run() { |
|
295 |
+ // Given |
|
296 |
+ String loginId = "kjkmadness"; |
|
297 |
+ String password = "pass"; |
|
298 |
+ User user = User.findByLoginId(loginId); |
|
299 |
+ Map<String, String> data = new HashMap<>(); |
|
300 |
+ data.put("loginId", loginId); |
|
301 |
+ data.put("password", password); |
|
302 |
+ |
|
303 |
+ // When |
|
304 |
+ Result result = callAction(controllers.routes.ref.UserApp.login(), fakeRequest() |
|
305 |
+ .withFormUrlEncodedBody(data)); |
|
306 |
+ |
|
307 |
+ // Then |
|
308 |
+ assertThat(status(result)).isEqualTo(SEE_OTHER); |
|
309 |
+ assertThat(header(LOCATION, result)).isEqualTo(routes.Application.index().url()); |
|
310 |
+ assertThat(session(result)).includes( |
|
311 |
+ entry(UserApp.SESSION_USERID, String.valueOf(user.id)), |
|
312 |
+ entry(UserApp.SESSION_LOGINID, user.loginId), |
|
313 |
+ entry(UserApp.SESSION_USERNAME, user.name)); |
|
314 |
+ } |
|
315 |
+ }); |
|
316 |
+ } |
|
317 |
+ |
|
318 |
+ @Test |
|
319 |
+ public void loginWrongPassword() { |
|
320 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
321 |
+ @Override |
|
322 |
+ public void run() { |
|
323 |
+ // Given |
|
324 |
+ String loginId = "kjkmadness"; |
|
325 |
+ String password = "wrong"; |
|
326 |
+ Map<String, String> data = new HashMap<>(); |
|
327 |
+ data.put("loginId", loginId); |
|
328 |
+ data.put("password", password); |
|
329 |
+ |
|
330 |
+ // When |
|
331 |
+ Result result = callAction(controllers.routes.ref.UserApp.login(), fakeRequest() |
|
332 |
+ .withFormUrlEncodedBody(data)); |
|
333 |
+ |
|
334 |
+ // Then |
|
335 |
+ assertThat(status(result)).isEqualTo(SEE_OTHER); |
|
336 |
+ assertThat(header(LOCATION, result)).isEqualTo(routes.UserApp.loginForm().url()); |
|
337 |
+ assertThat(session(result)).isEmpty(); |
|
338 |
+ } |
|
339 |
+ }); |
|
340 |
+ } |
|
341 |
+ |
|
342 |
+ @Test |
|
343 |
+ public void currentUserContext() { |
|
344 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
345 |
+ @Override |
|
346 |
+ public void run() { |
|
347 |
+ // Given |
|
348 |
+ User expected = User.find.byId(1L); |
|
349 |
+ context().withArg(UserApp.TOKEN_USER, expected); |
|
350 |
+ |
|
351 |
+ // When |
|
352 |
+ User user = UserApp.currentUser(); |
|
353 |
+ |
|
354 |
+ // Then |
|
355 |
+ assertThat(user).isEqualTo(expected); |
|
356 |
+ } |
|
357 |
+ }); |
|
358 |
+ } |
|
359 |
+ |
|
360 |
+ @Test |
|
361 |
+ public void currentUserSession() { |
|
362 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
363 |
+ @Override |
|
364 |
+ public void run() { |
|
365 |
+ // Given |
|
366 |
+ Long id = 1L; |
|
367 |
+ context().withSession(UserApp.SESSION_USERID, String.valueOf(id)); |
|
368 |
+ |
|
369 |
+ // When |
|
370 |
+ User user = UserApp.currentUser(); |
|
371 |
+ |
|
372 |
+ // Then |
|
373 |
+ assertThat(user).isNotEqualTo(User.anonymous); |
|
374 |
+ assertThat(user.id).isEqualTo(id); |
|
375 |
+ } |
|
376 |
+ }); |
|
377 |
+ } |
|
378 |
+ |
|
379 |
+ @Test |
|
380 |
+ public void currentUserSessionNotNumeric() { |
|
381 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
382 |
+ @Override |
|
383 |
+ public void run() { |
|
384 |
+ // Given |
|
385 |
+ Context context = context().withSession(UserApp.SESSION_USERID, "string"); |
|
386 |
+ |
|
387 |
+ // When |
|
388 |
+ User user = UserApp.currentUser(); |
|
389 |
+ |
|
390 |
+ // Then |
|
391 |
+ assertThat(user).isEqualTo(User.anonymous); |
|
392 |
+ assertThat(context.session()).isEmpty(); |
|
393 |
+ } |
|
394 |
+ }); |
|
395 |
+ } |
|
396 |
+ |
|
397 |
+ @Test |
|
398 |
+ public void currentUserSessionNoUser() { |
|
399 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
400 |
+ @Override |
|
401 |
+ public void run() { |
|
402 |
+ // Given |
|
403 |
+ Context context = context().withSession(UserApp.SESSION_USERID, "0"); |
|
404 |
+ |
|
405 |
+ // When |
|
406 |
+ User user = UserApp.currentUser(); |
|
407 |
+ |
|
408 |
+ // Then |
|
409 |
+ assertThat(user).isEqualTo(User.anonymous); |
|
410 |
+ assertThat(context.session()).isEmpty(); |
|
411 |
+ } |
|
412 |
+ }); |
|
413 |
+ } |
|
414 |
+ |
|
415 |
+ @Test |
|
416 |
+ public void currentUserToken() { |
|
417 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
418 |
+ @Override |
|
419 |
+ public void run() { |
|
420 |
+ // Given |
|
421 |
+ String loginId = "kjkmadness"; |
|
422 |
+ String password = "ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4="; |
|
423 |
+ String token = loginId + UserApp.TOKEN_SEPARATOR + password; |
|
424 |
+ Context context = context().withCookie(UserApp.TOKEN, token); |
|
425 |
+ |
|
426 |
+ // When |
|
427 |
+ User user = UserApp.currentUser(); |
|
428 |
+ |
|
429 |
+ // Then |
|
430 |
+ assertThat(user).isNotEqualTo(User.anonymous); |
|
431 |
+ assertThat(user.loginId).isEqualTo(loginId); |
|
432 |
+ assertThat(context.session()).includes( |
|
433 |
+ entry(UserApp.SESSION_USERID, String.valueOf(user.id)), |
|
434 |
+ entry(UserApp.SESSION_LOGINID, user.loginId), |
|
435 |
+ entry(UserApp.SESSION_USERNAME, user.name)); |
|
436 |
+ } |
|
437 |
+ }); |
|
438 |
+ } |
|
439 |
+ |
|
440 |
+ @Test |
|
441 |
+ public void currentUserTokenInvalidLength() { |
|
442 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
443 |
+ @Override |
|
444 |
+ public void run() { |
|
445 |
+ // Given |
|
446 |
+ String loginId = "kjkmadness"; |
|
447 |
+ String password = "ckJUVVaOHhRDNqwbeF+j4RNqXzodXO95+aQRIbJnDK4="; |
|
448 |
+ String token = loginId + UserApp.TOKEN_SEPARATOR + password |
|
449 |
+ + UserApp.TOKEN_SEPARATOR + "dummy"; |
|
450 |
+ Context context = context().withCookie(UserApp.TOKEN, token); |
|
451 |
+ |
|
452 |
+ // When |
|
453 |
+ User user = UserApp.currentUser(); |
|
454 |
+ |
|
455 |
+ // Then |
|
456 |
+ assertThat(user).isEqualTo(User.anonymous); |
|
457 |
+ assertThat(context.session()).isEmpty(); |
|
458 |
+ verify(context.response()).discardCookie(UserApp.TOKEN); |
|
459 |
+ } |
|
460 |
+ }); |
|
461 |
+ } |
|
462 |
+ |
|
463 |
+ @Test |
|
464 |
+ public void currentUserTokenNoUser() { |
|
465 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
466 |
+ @Override |
|
467 |
+ public void run() { |
|
468 |
+ // Given |
|
469 |
+ String loginId = "kjkmadness"; |
|
470 |
+ String password = "dummy"; |
|
471 |
+ String token = loginId + UserApp.TOKEN_SEPARATOR + password; |
|
472 |
+ Context context = context().withCookie(UserApp.TOKEN, token); |
|
473 |
+ |
|
474 |
+ // When |
|
475 |
+ User user = UserApp.currentUser(); |
|
476 |
+ |
|
477 |
+ // Then |
|
478 |
+ assertThat(user).isEqualTo(User.anonymous); |
|
479 |
+ assertThat(context.session()).isEmpty(); |
|
480 |
+ verify(context.response()).discardCookie(UserApp.TOKEN); |
|
481 |
+ } |
|
482 |
+ }); |
|
483 |
+ } |
|
484 |
+ |
|
485 |
+ @Test |
|
486 |
+ public void currentUserAnonymous() { |
|
487 |
+ running(support.Helpers.makeTestApplication(), new Runnable() { |
|
488 |
+ @Override |
|
489 |
+ public void run() { |
|
490 |
+ // Given |
|
491 |
+ Context context = context(); |
|
492 |
+ |
|
493 |
+ // When |
|
494 |
+ User user = UserApp.currentUser(); |
|
495 |
+ |
|
496 |
+ // Then |
|
497 |
+ assertThat(user).isEqualTo(User.anonymous); |
|
498 |
+ assertThat(context.session()).isEmpty(); |
|
499 |
+ } |
|
500 |
+ }); |
|
501 |
+ } |
|
168 | 502 |
} |
--- test/models/PasswordResetTest.java
+++ test/models/PasswordResetTest.java
... | ... | @@ -136,7 +136,7 @@ |
136 | 136 |
|
137 | 137 |
//Then |
138 | 138 |
assertThat(result).isTrue(); |
139 |
- assertThat(UserApp.authenticateWithPlainPassword(userId, newPassword)).isNotNull(); |
|
139 |
+ assertThat(UserApp.authenticateWithPlainPassword(userId, newPassword).isAnonymous()).isFalse(); |
|
140 | 140 |
} |
141 | 141 |
|
142 | 142 |
@Test |
... | ... | @@ -153,7 +153,7 @@ |
153 | 153 |
|
154 | 154 |
//Then |
155 | 155 |
assertThat(result).isFalse(); |
156 |
- assertThat(UserApp.authenticateWithPlainPassword(userId, newPassword)).isNull(); |
|
156 |
+ assertThat(UserApp.authenticateWithPlainPassword(userId, newPassword).isAnonymous()).isTrue(); |
|
157 | 157 |
} |
158 | 158 |
|
159 | 159 |
private static boolean hashStringExist(String loginId) { |
--- test/models/UserTest.java
+++ test/models/UserTest.java
... | ... | @@ -70,7 +70,7 @@ |
70 | 70 |
// When |
71 | 71 |
Map<String, String> userOptions = User.options(); |
72 | 72 |
// Then |
73 |
- assertThat(userOptions).hasSize(6); |
|
73 |
+ assertThat(userOptions).hasSize(7); |
|
74 | 74 |
} |
75 | 75 |
|
76 | 76 |
@Test |
+++ test/support/ContextTest.java
... | ... | @@ -0,0 +1,100 @@ |
1 | +/** | |
2 | + * Yobi, Project Hosting SW | |
3 | + * | |
4 | + * Copyright 2013 NAVER Corp. | |
5 | + * http://yobi.io | |
6 | + * | |
7 | + * @Author kjkmadness | |
8 | + * | |
9 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
10 | + * you may not use this file except in compliance with the License. | |
11 | + * You may obtain a copy of the License at | |
12 | + * | |
13 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
14 | + * | |
15 | + * Unless required by applicable law or agreed to in writing, software | |
16 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
17 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
18 | + * See the License for the specific language governing permissions and | |
19 | + * limitations under the License. | |
20 | + */ | |
21 | +package support; | |
22 | + | |
23 | +import static org.mockito.Mockito.*; | |
24 | + | |
25 | +import java.util.Collections; | |
26 | +import java.util.HashMap; | |
27 | +import java.util.Map; | |
28 | + | |
29 | +import org.junit.*; | |
30 | + | |
31 | +import play.api.mvc.RequestHeader; | |
32 | +import play.mvc.Http.*; | |
33 | + | |
34 | +public abstract class ContextTest { | |
35 | + private FakeContext context; | |
36 | + | |
37 | + @Before | |
38 | + public final void initContext() { | |
39 | + context = new FakeContext(1L, | |
40 | + mock(RequestHeader.class), | |
41 | + mock(Request.class), | |
42 | + Collections.<String, String> emptyMap(), | |
43 | + Collections.<String, String> emptyMap(), | |
44 | + Collections.<String, Object> emptyMap()); | |
45 | + Context.current.set(context); | |
46 | + } | |
47 | + | |
48 | + @After | |
49 | + public final void removeContext() { | |
50 | + Context.current.remove(); | |
51 | + } | |
52 | + | |
53 | + public FakeContext context() { | |
54 | + return context; | |
55 | + } | |
56 | + | |
57 | + protected static class FakeContext extends Context { | |
58 | + private Response response; | |
59 | + private Map<String, String[]> headers = new HashMap<>(); | |
60 | + | |
61 | + FakeContext(Long id, RequestHeader header, Request request, | |
62 | + Map<String, String> sessionData, Map<String, String> flashData, | |
63 | + Map<String, Object> args) { | |
64 | + super(id, header, request, sessionData, flashData, args); | |
65 | + response = mock(Response.class); | |
66 | + when(request.cookies()).thenReturn(mock(Cookies.class)); | |
67 | + when(request.headers()).thenReturn(headers); | |
68 | + } | |
69 | + | |
70 | + @Override | |
71 | + public Response response() { | |
72 | + return response; | |
73 | + } | |
74 | + | |
75 | + public FakeContext withHeader(String name, String value) { | |
76 | + headers.put(name, new String[] {value}); | |
77 | + when(request().getHeader(name)).thenReturn(value); | |
78 | + return this; | |
79 | + } | |
80 | + | |
81 | + public FakeContext withCookie(String name, String value) { | |
82 | + Cookie cookie = mock(Cookie.class); | |
83 | + when(cookie.name()).thenReturn(name); | |
84 | + when(cookie.value()).thenReturn(value); | |
85 | + when(request().cookie(name)).thenReturn(cookie); | |
86 | + when(request().cookies().get(name)).thenReturn(cookie); | |
87 | + return this; | |
88 | + } | |
89 | + | |
90 | + public FakeContext withSession(String name, String value) { | |
91 | + session().put(name, value); | |
92 | + return this; | |
93 | + } | |
94 | + | |
95 | + public FakeContext withArg(String name, Object value) { | |
96 | + args.put(name, value); | |
97 | + return this; | |
98 | + } | |
99 | + } | |
100 | +} |
--- test/utils/BasicAuthActionTest.java
+++ test/utils/BasicAuthActionTest.java
... | ... | @@ -2,17 +2,43 @@ |
2 | 2 |
|
3 | 3 |
import static org.junit.Assert.*; |
4 | 4 |
import static org.fest.assertions.Assertions.assertThat; |
5 |
+import static org.fest.assertions.MapAssert.entry; |
|
6 |
+import static org.mockito.Mockito.*; |
|
7 |
+ |
|
8 |
+import static play.test.Helpers.*; |
|
9 |
+ |
|
10 |
+import static support.Helpers.*; |
|
5 | 11 |
|
6 | 12 |
import java.io.UnsupportedEncodingException; |
7 | 13 |
|
8 | 14 |
import models.User; |
9 | 15 |
|
10 | 16 |
import org.apache.commons.codec.binary.Base64; |
11 |
-import org.junit.Test; |
|
17 |
+import org.junit.*; |
|
12 | 18 |
|
19 |
+import controllers.UserApp; |
|
20 |
+ |
|
21 |
+import play.mvc.Action; |
|
22 |
+import play.mvc.Http; |
|
23 |
+import play.mvc.Http.*; |
|
24 |
+import play.test.FakeApplication; |
|
25 |
+ |
|
26 |
+import support.ContextTest; |
|
13 | 27 |
import utils.BasicAuthAction; |
14 | 28 |
|
15 |
-public class BasicAuthActionTest { |
|
29 |
+public class BasicAuthActionTest extends ContextTest { |
|
30 |
+ private FakeApplication application; |
|
31 |
+ |
|
32 |
+ @Before |
|
33 |
+ public void before() { |
|
34 |
+ application = makeTestApplication(); |
|
35 |
+ start(application); |
|
36 |
+ } |
|
37 |
+ |
|
38 |
+ @After |
|
39 |
+ public void after() { |
|
40 |
+ stop(application); |
|
41 |
+ } |
|
16 | 42 |
|
17 | 43 |
@Test |
18 | 44 |
public void parseCredentials() { |
... | ... | @@ -77,4 +103,41 @@ |
77 | 103 |
} |
78 | 104 |
} |
79 | 105 |
|
106 |
+ @Test |
|
107 |
+ public void call() throws Throwable { |
|
108 |
+ // Given |
|
109 |
+ String loginId = "kjkmadness"; |
|
110 |
+ String password = "pass"; |
|
111 |
+ String credential = "Basic " |
|
112 |
+ + new String(Base64.encodeBase64((loginId + ":" + password).getBytes("UTF-8"))); |
|
113 |
+ User user = User.findByLoginId(loginId); |
|
114 |
+ Context context = context().withHeader(Http.HeaderNames.AUTHORIZATION, credential); |
|
115 |
+ BasicAuthAction action = new BasicAuthAction(); |
|
116 |
+ action.delegate = mock(Action.class); |
|
117 |
+ |
|
118 |
+ // When |
|
119 |
+ action.call(context); |
|
120 |
+ |
|
121 |
+ // Then |
|
122 |
+ assertThat(context.session()).includes( |
|
123 |
+ entry(UserApp.SESSION_USERID, String.valueOf(user.id)), |
|
124 |
+ entry(UserApp.SESSION_LOGINID, user.loginId), |
|
125 |
+ entry(UserApp.SESSION_USERNAME, user.name)); |
|
126 |
+ verify(action.delegate).call(context); |
|
127 |
+ } |
|
128 |
+ |
|
129 |
+ @Test |
|
130 |
+ public void callAnonymous() throws Throwable { |
|
131 |
+ // Given |
|
132 |
+ Context context = context(); |
|
133 |
+ BasicAuthAction action = new BasicAuthAction(); |
|
134 |
+ action.delegate = mock(Action.class); |
|
135 |
+ |
|
136 |
+ // When |
|
137 |
+ action.call(context); |
|
138 |
+ |
|
139 |
+ // Then |
|
140 |
+ assertThat(context.session()).isEmpty(); |
|
141 |
+ verify(action.delegate).call(context); |
|
142 |
+ } |
|
80 | 143 |
} |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?