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
102
103
104
105
106
107
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
139
140
141 return new User() {
142
143 @Override
144 public String getUsername() {
145 return ReservedUsername.SUPERADMIN.label();
146 }
147
148 @Override
149 public Role getRole() {
150 return Role.SUPERADMIN;
151 }
152 };
153 }
154
155
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
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
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
236
237
238
239
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
289
290 permissions.assertAuthorized(correlationId, user);
291
292
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
308
309 permissions.assertAuthorized(correlationId, user);
310
311
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
338
339 permissions.assertAuthorized(correlationId, user);
340
341
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
366
367 permissions.assertAuthorized(correlationId, user);
368
369
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
389
390 permissions.assertAuthorized(correlationId, user);
391
392
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
409
410 permissions.assertAuthorized(correlationId, user, "query", query);
411
412
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
530
531 permissions.assertAuthorized(correlationId, user);
532
533
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 }