View Javadoc
1   package net.avcompris.examples.users3.core.impl;
2   
3   import static com.google.common.base.Preconditions.checkNotNull;
4   import static net.avcompris.commons3.core.DateTimeHolderImpl.toDateTimeHolder;
5   import static net.avcompris.commons3.core.DateTimeHolderImpl.toDateTimeHolderOrNull;
6   import static net.avcompris.commons3.databeans.DataBeans.instantiate;
7   import static org.apache.commons.lang3.CharEncoding.UTF_8;
8   import static org.apache.commons.lang3.StringUtils.isBlank;
9   
10  import java.io.File;
11  import java.io.IOException;
12  
13  import javax.annotation.Nullable;
14  
15  import org.apache.commons.io.FileUtils;
16  import org.apache.commons.lang3.NotImplementedException;
17  import org.joda.time.DateTime;
18  import org.springframework.beans.factory.annotation.Autowired;
19  import org.springframework.stereotype.Service;
20  
21  import net.avcompris.commons.query.FilterSyntaxException;
22  import net.avcompris.commons.query.impl.FilteringsFactory;
23  import net.avcompris.commons3.api.User;
24  import net.avcompris.commons3.api.UserSession;
25  import net.avcompris.commons3.api.UserSessionFiltering;
26  import net.avcompris.commons3.api.UserSessionFilterings;
27  import net.avcompris.commons3.api.UserSessions;
28  import net.avcompris.commons3.api.UserSessionsQuery;
29  import net.avcompris.commons3.api.UserSessionsQuery.SortBy;
30  import net.avcompris.commons3.api.exception.InvalidQueryFilteringException;
31  import net.avcompris.commons3.api.exception.ServiceException;
32  import net.avcompris.commons3.api.exception.UnauthenticatedException;
33  import net.avcompris.commons3.api.exception.UnexpectedException;
34  import net.avcompris.commons3.core.Permissions;
35  import net.avcompris.commons3.core.impl.AbstractServiceImpl;
36  import net.avcompris.commons3.core.impl.SortBysParser;
37  import net.avcompris.commons3.utils.Clock;
38  import net.avcompris.commons3.utils.LogFactory;
39  import net.avcompris.examples.shared3.Constants.ReservedUsername;
40  import net.avcompris.examples.shared3.Role;
41  import net.avcompris.examples.users3.core.api.MutableUserSession;
42  import net.avcompris.examples.users3.core.api.MutableUserSessions;
43  import net.avcompris.examples.users3.dao.AuthDao;
44  import net.avcompris.examples.users3.dao.UserDto;
45  import net.avcompris.examples.users3.dao.UserSessionDto;
46  import net.avcompris.examples.users3.dao.UserSessionsDto;
47  import net.avcompris.examples.users3.dao.UserSessionsDtoQuery;
48  import net.avcompris.examples.users3.dao.UsersDao;
49  
50  @Service
51  public final class AuthServiceImpl extends AbstractServiceImpl implements MyAuthService {
52  
53  	private final AuthDao authDao;
54  	private final UsersDao usersDao;
55  
56  	@Nullable
57  	private final File superadminAuthorizationFile;
58  
59  	private static final UserSessionsDtoQuery.SortBy[] DEFAULT_SORT_BYS = new UserSessionsDtoQuery.SortBy[] {
60  			UserSessionsDtoQuery.SortBy.SORT_BY_USERNAME };
61  
62  	private static final UserSessionsDtoQuery.Expand[] DEFAULT_EXPANDS = new UserSessionsDtoQuery.Expand[] {
63  			UserSessionsDtoQuery.Expand.EXPAND_ALL };
64  
65  	private static final SortBysParser<SortBy> SORT_BYS_PARSER = new SortBysParser<SortBy>(SortBy.class,
66  			SortBy.values());
67  
68  	@Autowired
69  	public AuthServiceImpl(final Permissions permissions, final Clock clock, final UsersDao usersDao,
70  			final AuthDao authDao) throws IOException {
71  
72  		super(permissions, clock);
73  
74  		this.authDao = checkNotNull(authDao, "authDao");
75  		this.usersDao = checkNotNull(usersDao, "usersDao");
76  
77  		final String superadminAuthorizationFileProperty = System.getProperty("superadmin.authorizationFile");
78  
79  		if (isBlank(superadminAuthorizationFileProperty)) {
80  
81  			System.out.println("=== INFO === No \"superadmin.authorizationFile\" env property has been set.");
82  
83  			superadminAuthorizationFile = null;
84  
85  		} else {
86  
87  			System.out.println("=== INFO === \"superadmin.authorizationFile\" has been set in the System environment: "
88  					+ superadminAuthorizationFileProperty);
89  
90  			superadminAuthorizationFile = new File(superadminAuthorizationFileProperty);
91  
92  			System.out.println("superadmin.authorizationFile: " + superadminAuthorizationFile.getCanonicalPath());
93  		}
94  	}
95  
96  	@Override
97  	@Nullable
98  	public User getAuthenticatedUser(@Nullable final String authorization, @Nullable final String userSessionId)
99  			throws ServiceException {
100 
101 		// 0. PERMISSIONS
102 
103 		// permissions.assertAuthorized(correlationId, user);
104 
105 		// 1. LOGIC
106 
107 		// 1. SUPERADMIN AUTHORIZATION?
108 
109 		final boolean isSuperadminAuthorization;
110 
111 		if (authorization == null) {
112 
113 			isSuperadminAuthorization = false;
114 
115 		} else if (superadminAuthorizationFile != null) {
116 
117 			final String superadminAuthorizationFromFile;
118 
119 			try {
120 
121 				superadminAuthorizationFromFile = FileUtils.readFileToString(superadminAuthorizationFile, UTF_8).trim();
122 
123 			} catch (final IOException e) {
124 
125 				throw new UnexpectedException("Cannot read superadminAuthorization from: " //
126 						+ superadminAuthorizationFile, e);
127 			}
128 
129 			isSuperadminAuthorization = superadminAuthorizationFromFile.contentEquals(authorization);
130 
131 		} else {
132 
133 			isSuperadminAuthorization = false;
134 		}
135 
136 		if (isSuperadminAuthorization) {
137 
138 			// This User object is *not* stored in the database, it’s just here for admin
139 			// purpose.
140 			//
141 			return new User() {
142 
143 				@Override
144 				public String getUsername() {
145 					return ReservedUsername.SUPERADMIN.label(); // Here, we hardcode the "superadmin" username
146 				}
147 
148 				@Override
149 				public Role getRole() {
150 					return Role.SUPERADMIN;
151 				}
152 			};
153 		}
154 
155 		// 2. AUTHORIZATION?
156 
157 		if (authorization != null) {
158 
159 			final String username = wrap(()
160 
161 			-> authDao.getUsernameByAuthorization(authorization, clock.now()));
162 
163 			final UserDto dto = getUserDto(username);
164 
165 			if (dto != null) {
166 
167 				return dto2User(dto);
168 			}
169 		}
170 
171 		// 3. USER SESSION ID?
172 
173 		if (userSessionId != null) {
174 
175 			final String username = wrap(()
176 
177 			-> authDao.getUsernameBySessionId(userSessionId, clock.now()));
178 
179 			final UserDto dto = getUserDto(username);
180 
181 			if (dto != null) {
182 
183 				return dto2User(dto);
184 			}
185 		}
186 
187 		// 4. NO AUTHENTICATED USER
188 
189 		return null;
190 	}
191 
192 	@Nullable
193 	private UserDto getUserDto(@Nullable final String username) throws ServiceException {
194 
195 		if (username == null) {
196 			return null;
197 		}
198 
199 		return wrap(()
200 
201 		-> usersDao.getUser(username));
202 	}
203 
204 	private static User dto2User(final UserDto dto) {
205 
206 		checkNotNull(dto, "dto");
207 
208 		final String username = dto.getUsername();
209 		final Role role = Role.valueOf(dto.getRolename());
210 
211 		return new User() {
212 
213 			@Override
214 			public String getUsername() {
215 				return username;
216 			}
217 
218 			@Override
219 			public Role getRole() {
220 				return role;
221 			}
222 		};
223 	}
224 
225 	@Override
226 	public UserSession authenticate(final String correlationId, final String username, final String password)
227 			throws ServiceException {
228 
229 		checkNotNull(correlationId, "correlationId");
230 		checkNotNull(username, "username");
231 		checkNotNull(password, "password");
232 
233 		LogFactory.setCorrelationId(correlationId);
234 
235 		// 0. PERMISSIONS
236 
237 		// permissions.assertAuthorized(correlationId, user);
238 
239 		// 1. LOGIC
240 
241 		final boolean isValid = wrap(()
242 
243 		-> authDao.isValidUserPassword(username, password));
244 
245 		if (!isValid) {
246 
247 			throw new UnauthenticatedException();
248 		}
249 
250 		final DateTime now = clock.now();
251 
252 		final UserSessionDto dto = wrap(() -> {
253 
254 			final UserSessionDto s = authDao.newUserSession(username, now);
255 
256 			usersDao.setLastActiveAt(username, now);
257 
258 			return s;
259 
260 		});
261 
262 		return dto2UserSession(dto);
263 	}
264 
265 	private static UserSession dto2UserSession(final UserSessionDto dto) {
266 
267 		checkNotNull(dto, "dto");
268 
269 		return instantiate(MutableUserSession.class) //
270 				.setUsername(dto.getUsername()) //
271 				.setUserSessionId(dto.getUserSessionId()) //
272 				.setCreatedAt(toDateTimeHolder(dto.getCreatedAt())) //
273 				.setUpdatedAt(toDateTimeHolder(dto.getUpdatedAt())) //
274 				.setExpiresAt(toDateTimeHolder(dto.getExpiresAt())) //
275 				.setExpiredAt(toDateTimeHolderOrNull(dto.getExpiredAt()));
276 	}
277 
278 	@Override
279 	public UserSession getMySession(final String correlationId, final User user, final String userSessionId)
280 			throws ServiceException {
281 
282 		checkNotNull(correlationId, "correlationId");
283 		checkNotNull(user, "user");
284 		checkNotNull(userSessionId, "userSessionId");
285 
286 		LogFactory.setCorrelationId(correlationId);
287 
288 		// 0. PERMISSIONS
289 
290 		permissions.assertAuthorized(correlationId, user);
291 
292 		// 1. LOGIC
293 
294 		return getUserSession(userSessionId);
295 	}
296 
297 	@Override
298 	public UserSession getUserSession(final String correlationId, final User user, final String userSessionId)
299 			throws ServiceException {
300 
301 		checkNotNull(correlationId, "correlationId");
302 		checkNotNull(user, "user");
303 		checkNotNull(userSessionId, "userSessionId");
304 
305 		LogFactory.setCorrelationId(correlationId);
306 
307 		// 0. PERMISSIONS
308 
309 		permissions.assertAuthorized(correlationId, user);
310 
311 		// 1. LOGIC
312 
313 		return getUserSession(userSessionId);
314 	}
315 
316 	private UserSession getUserSession(final String userSessionId) throws ServiceException {
317 
318 		checkNotNull(userSessionId, "userSessionId");
319 
320 		final UserSessionDto dto = wrap(()
321 
322 		-> authDao.getUserSession(userSessionId, clock.now()));
323 
324 		return (dto != null) ? dto2UserSession(dto) : null;
325 	}
326 
327 	@Override
328 	public UserSession terminateMySession(final String correlationId, final User user, final String userSessionId)
329 			throws ServiceException {
330 
331 		checkNotNull(correlationId, "correlationId");
332 		checkNotNull(user, "user");
333 		checkNotNull(userSessionId, "userSessionId");
334 
335 		LogFactory.setCorrelationId(correlationId);
336 
337 		// 0. PERMISSIONS
338 
339 		permissions.assertAuthorized(correlationId, user);
340 
341 		// 1. LOGIC
342 
343 		final DateTime now = clock.now();
344 
345 		wrap(() -> {
346 
347 			authDao.terminateSession(userSessionId, now, now);
348 
349 			usersDao.setLastActiveAt(user.getUsername(), now);
350 		});
351 
352 		return getUserSession(userSessionId);
353 	}
354 
355 	@Override
356 	public UserSession terminateUserSession(final String correlationId, final User user, final String userSessionId)
357 			throws ServiceException {
358 
359 		checkNotNull(correlationId, "correlationId");
360 		checkNotNull(user, "user");
361 		checkNotNull(userSessionId, "userSessionId");
362 
363 		LogFactory.setCorrelationId(correlationId);
364 
365 		// 0. PERMISSIONS
366 
367 		permissions.assertAuthorized(correlationId, user);
368 
369 		// 1. LOGIC
370 
371 		final DateTime now = clock.now();
372 
373 		wrap(()
374 
375 		-> authDao.terminateSession(userSessionId, null, now));
376 
377 		return getUserSession(userSessionId);
378 	}
379 
380 	@Override
381 	public void setLastActiveAt(final String correlationId, final User user) throws ServiceException {
382 
383 		checkNotNull(correlationId, "correlationId");
384 		checkNotNull(user, "user");
385 
386 		LogFactory.setCorrelationId(correlationId);
387 
388 		// 0. PERMISSIONS
389 
390 		permissions.assertAuthorized(correlationId, user);
391 
392 		// 1. LOGIC
393 
394 		wrap(()
395 
396 		-> usersDao.setLastActiveAt(user.getUsername(), clock.now()));
397 	}
398 
399 	@Override
400 	public UserSessions getUserSessions(final String correlationId, final User user,
401 			@Nullable final UserSessionsQuery query) throws ServiceException {
402 
403 		checkNotNull(correlationId, "correlationId");
404 		checkNotNull(user, "user");
405 
406 		LogFactory.setCorrelationId(correlationId);
407 
408 		// 0. PERMISSIONS
409 
410 		permissions.assertAuthorized(correlationId, user, "query", query);
411 
412 		// 1. LOGIC
413 
414 		return privateGetUserSessions(query);
415 	}
416 
417 	private UserSessions privateGetUserSessions(@Nullable final UserSessionsQuery query) throws ServiceException {
418 
419 		final long startMs = System.currentTimeMillis();
420 
421 		final int start = getQueryStart(query, 0);
422 		final int limit = getQueryLimit(query, 10);
423 
424 		final UserSessionsDtoQuery.Expand[] expandDtos;
425 
426 		if (query == null) {
427 			expandDtos = DEFAULT_EXPANDS;
428 		} else {
429 			expandDtos = expands2Dto(query.getExpands());
430 		}
431 
432 		final UserSessionsDtoQuery dtoQuery = instantiate(UserSessionsDtoQuery.class) //
433 				.setFiltering(query != null ? query.getFiltering() : null) //
434 				.setSortBys(query != null ? sortBys2Dto(query.getSortBys()) : DEFAULT_SORT_BYS) // ;
435 				.setExpands(expandDtos) //
436 				.setStart(start) //
437 				.setLimit(limit); //
438 
439 		final UserSessionsDto userSessionsDto = wrap(()
440 
441 		-> authDao.getUserSessions(dtoQuery));
442 
443 		final MutableUserSessions result = instantiate(MutableUserSessions.class) //
444 				.setStart(start) //
445 				.setLimit(limit) //
446 				.setSize(userSessionsDto.getResults().length) //
447 				.setTotal(userSessionsDto.getTotal()) //
448 				.setSqlWhereClause(userSessionsDto.getSqlWhereClause());
449 
450 		for (final UserSessionDto userSessionDto : userSessionsDto.getResults()) {
451 
452 			result.addToResults(dto2UserSession(userSessionDto));
453 		}
454 
455 		final int tookMs = (int) (System.currentTimeMillis() - startMs);
456 
457 		return result.setTookMs(tookMs);
458 	}
459 
460 	private static UserSessionsDtoQuery.Expand[] expands2Dto(final UserSessionsQuery.Expand... expands) {
461 
462 		if (expands == null || expands.length == 0) {
463 			return DEFAULT_EXPANDS;
464 		}
465 
466 		throw new NotImplementedException("");
467 	}
468 
469 	private static UserSessionsDtoQuery.SortBy[] sortBys2Dto(final UserSessionsQuery.SortBy... sortBys) {
470 
471 		if (sortBys == null || sortBys.length == 0) {
472 			return DEFAULT_SORT_BYS;
473 		}
474 
475 		final UserSessionsDtoQuery.SortBy[] dto = new UserSessionsDtoQuery.SortBy[sortBys.length];
476 
477 		for (int i = 0; i < sortBys.length; ++i) {
478 			dto[i] = sortBy2Dto(sortBys[i]);
479 		}
480 
481 		return dto;
482 	}
483 
484 	private static UserSessionsDtoQuery.SortBy sortBy2Dto(final UserSessionsQuery.SortBy sortBy) {
485 
486 		checkNotNull(sortBy, "sortBy");
487 
488 		switch (sortBy) {
489 		case SORT_BY_USERNAME:
490 			return UserSessionsDtoQuery.SortBy.SORT_BY_USERNAME;
491 		case SORT_BY_USERNAME_DESC:
492 			return UserSessionsDtoQuery.SortBy.SORT_BY_USERNAME_DESC;
493 		case SORT_BY_USER_SESSION_ID:
494 			return UserSessionsDtoQuery.SortBy.SORT_BY_USER_SESSION_ID;
495 		case SORT_BY_USER_SESSION_ID_DESC:
496 			return UserSessionsDtoQuery.SortBy.SORT_BY_USER_SESSION_ID_DESC;
497 		case SORT_BY_CREATED_AT:
498 			return UserSessionsDtoQuery.SortBy.SORT_BY_CREATED_AT;
499 		case SORT_BY_CREATED_AT_DESC:
500 			return UserSessionsDtoQuery.SortBy.SORT_BY_CREATED_AT_DESC;
501 		case SORT_BY_UPDATED_AT:
502 			return UserSessionsDtoQuery.SortBy.SORT_BY_UPDATED_AT;
503 		case SORT_BY_UPDATED_AT_DESC:
504 			return UserSessionsDtoQuery.SortBy.SORT_BY_UPDATED_AT_DESC;
505 		case SORT_BY_EXPIRES_AT:
506 			return UserSessionsDtoQuery.SortBy.SORT_BY_EXPIRES_AT;
507 		case SORT_BY_EXPIRES_AT_DESC:
508 			return UserSessionsDtoQuery.SortBy.SORT_BY_EXPIRES_AT_DESC;
509 		case SORT_BY_EXPIRED_AT:
510 			return UserSessionsDtoQuery.SortBy.SORT_BY_EXPIRED_AT;
511 		case SORT_BY_EXPIRED_AT_DESC:
512 			return UserSessionsDtoQuery.SortBy.SORT_BY_EXPIRED_AT_DESC;
513 		default:
514 			throw new NotImplementedException("sortBy: " + sortBy);
515 		}
516 	}
517 
518 	@Override
519 	@Nullable
520 	public UserSessionsQuery validateUserSessionsQuery(final String correlationId, final User user,
521 			@Nullable final String q, @Nullable final String sort, @Nullable final Integer start,
522 			@Nullable final Integer limit, @Nullable final String expand) throws ServiceException {
523 
524 		checkNotNull(correlationId, "correlationId");
525 		checkNotNull(user, "user");
526 
527 		LogFactory.setCorrelationId(correlationId);
528 
529 		// 0. PERMISSIONS
530 
531 		permissions.assertAuthorized(correlationId, user);
532 
533 		// 1. LOGIC
534 
535 		if (isBlank(q) && isBlank(sort) && start == null && limit == null && isBlank(expand)) {
536 			return null;
537 		}
538 
539 		final UserSessionsQuery query = instantiate(UserSessionsQuery.class);
540 
541 		if (start != null) {
542 
543 			if (start < 0) {
544 				throw new InvalidQueryFilteringException("Query.start should be >= 0, but was: " + start);
545 			}
546 
547 			query.setStart(start);
548 		}
549 
550 		if (limit != null) {
551 
552 			if (limit < 0) {
553 				throw new InvalidQueryFilteringException("Query.limit should be >= 0, but was: " + limit);
554 			}
555 
556 			query.setLimit(limit);
557 		}
558 
559 		if (!isBlank(q)) {
560 
561 			if (q.length() > 1000) {
562 				throw new InvalidQueryFilteringException(
563 						"Query should be at most 1,000 characters, but was: " + q.length());
564 			}
565 
566 			final UserSessionFilterings filterings = FilteringsFactory.instantiate(UserSessionFilterings.class);
567 
568 			final UserSessionFiltering filtering;
569 
570 			try {
571 
572 				filtering = filterings.parse(q);
573 
574 			} catch (final FilterSyntaxException e) {
575 
576 				throw new InvalidQueryFilteringException(e);
577 			}
578 
579 			query.setFiltering(filtering);
580 		}
581 
582 		if (!isBlank(sort)) {
583 
584 			query.setSortBys(SORT_BYS_PARSER.parse(sort));
585 		}
586 
587 		if (!isBlank(expand)) {
588 
589 			throw new NotImplementedException("expand: " + expand);
590 		}
591 
592 		return query;
593 	}
594 }