1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package fr.cnes.doi.application;
20
21 import fr.cnes.doi.client.ClientMDS;
22 import fr.cnes.doi.exception.ClientMdsException;
23 import fr.cnes.doi.services.DOIUsersUpdate;
24 import java.util.Arrays;
25 import java.util.concurrent.TimeUnit;
26
27 import org.apache.logging.log4j.LogManager;
28 import org.apache.logging.log4j.Logger;
29 import org.restlet.Request;
30 import org.restlet.Response;
31 import org.restlet.Restlet;
32 import org.restlet.data.LocalReference;
33 import org.restlet.data.Method;
34 import org.restlet.data.Reference;
35 import org.restlet.data.MediaType;
36 import org.restlet.data.Status;
37 import org.restlet.resource.Directory;
38 import org.restlet.routing.Filter;
39 import org.restlet.routing.Redirector;
40 import org.restlet.routing.Router;
41 import org.restlet.security.ChallengeAuthenticator;
42 import org.restlet.security.Role;
43 import org.restlet.security.RoleAuthorizer;
44 import org.restlet.service.TaskService;
45
46 import fr.cnes.doi.logging.business.JsonMessage;
47 import fr.cnes.doi.resource.admin.AuthenticateResource;
48 import fr.cnes.doi.resource.admin.ConfigIhmResource;
49 import fr.cnes.doi.resource.admin.FooterIhmResource;
50 import fr.cnes.doi.resource.admin.ManageProjectsResource;
51 import fr.cnes.doi.resource.admin.ManageSuperUserResource;
52 import fr.cnes.doi.resource.admin.ManageSuperUsersResource;
53 import fr.cnes.doi.resource.admin.ManageUsersResource;
54 import fr.cnes.doi.resource.admin.RedirectingResource;
55 import fr.cnes.doi.resource.admin.SuffixProjectsDoisResource;
56 import fr.cnes.doi.resource.admin.SuffixProjectsResource;
57 import fr.cnes.doi.resource.admin.TokenResource;
58 import fr.cnes.doi.security.AllowerIP;
59 import fr.cnes.doi.services.LandingPageMonitoring;
60 import fr.cnes.doi.services.UpdateTokenDataBase;
61 import fr.cnes.doi.settings.Consts;
62 import fr.cnes.doi.settings.DoiSettings;
63 import fr.cnes.doi.utils.spec.CoverageAnnotation;
64 import fr.cnes.doi.utils.spec.Requirement;
65 import java.util.List;
66 import org.apache.logging.log4j.ThreadContext;
67 import org.restlet.data.ClientInfo;
68 import org.restlet.routing.Template;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 @Requirement(reqId = Requirement.DOI_SRV_130, reqName = Requirement.DOI_SRV_130_NAME)
117 @Requirement(reqId = Requirement.DOI_SRV_140, reqName = Requirement.DOI_SRV_140_NAME)
118 @Requirement(reqId = Requirement.DOI_SRV_150, reqName = Requirement.DOI_SRV_150_NAME)
119 @Requirement(reqId = Requirement.DOI_SRV_180, reqName = Requirement.DOI_SRV_180_NAME)
120
121 @Requirement(reqId = Requirement.DOI_SRV_190, reqName = Requirement.DOI_SRV_190_NAME,
122 coverage = CoverageAnnotation.NONE)
123 @Requirement(reqId = Requirement.DOI_DISPO_020, reqName = Requirement.DOI_DISPO_020_NAME)
124 public final class AdminApplication extends AbstractApplication {
125
126
127
128
129 public static final String NAME = "Admin Application";
130
131
132
133
134
135 public static final String ADMIN_URI = "/admin";
136
137
138
139
140 public static final String RESOURCE_URI = "/resources";
141
142
143
144
145 public static final String STATUS_URI = "/status";
146
147
148
149
150 public static final String STATS_URI = "/stats";
151
152
153
154
155 public static final String ROLE_USERS_URI = "/roles/users";
156
157
158
159
160 public static final String ROLE_ADMIN_URI = "/roles/admin";
161
162
163
164
165 public static final String SUFFIX_PROJECT_URI = "/projects";
166
167
168
169
170 public static final String SUFFIX_PROJECT_NAME_TEMPLATE = "suffixProject";
171
172
173
174
175 public static final String SUFFIX_PROJECT_NAME = "/{" + SUFFIX_PROJECT_NAME_TEMPLATE + "}";
176
177
178
179
180 public static final String DOIS_URI = "/dois";
181
182
183
184
185 public static final String USERS_URI = "/users";
186
187
188
189
190 public static final String SUPERUSERS_URI = "/superusers";
191
192
193
194
195 public static final String USERS_NAME_TEMPLATE = "userName";
196
197
198
199
200 public static final String USERS_NAME = "/{" + USERS_NAME_TEMPLATE + "}";
201
202
203
204
205 public static final String TOKEN_URI = "/token";
206
207
208
209
210 public static final String TOKEN_TEMPLATE = "tokenID";
211
212
213
214
215 public static final String TOKEN_NAME_URI = "/{" + TOKEN_TEMPLATE + "}";
216
217
218
219
220 public static final String IHM_URI = "/ihm";
221
222
223
224
225 private static final Logger LOG = LogManager.getLogger(AdminApplication.class.getName());
226
227
228
229
230 private static final String IHM_CONFIG_URI = "/js/config.js";
231
232
233
234
235 private static final String IHM_FOOTER_URI = "/footer.txt";
236
237
238
239
240 private static final String STATUS_PAGE_CLASSPATH = "class/website";
241
242
243
244
245 private static final String IHM_CLASSPATH = "class/ihm";
246
247
248
249
250 private static final String API_CLASSPATH = "class/docs";
251
252
253
254
255 private static final int PERIOD_SCHEDULER = 1;
256
257
258
259
260
261 private static final int PERIOD_SCHEDULER_FOR_TOKEN_DB = 1;
262
263
264
265
266 private static final TimeUnit PERIOD_UNIT = TimeUnit.DAYS;
267
268
269
270
271 private static final String TARGET_URL = "http://status.datacite.org";
272
273
274
275
276 private static final String TARGET_STATS_URL = "https://stats.datacite.org/#tab-prefixes";
277
278
279
280
281 private final ClientMDS client;
282
283
284
285
286
287 public AdminApplication(final ClientMDS client) {
288 super();
289 this.client = client;
290 init();
291 }
292
293
294
295
296 private void init() {
297 setName(NAME);
298 setDescription("Provides an application for handling features related to "
299 + "the administration system of the DOI server.");
300 this.setTaskService(createTaskService());
301 this.setTaskService(createUpdateDataBaseTaskService());
302 this.setTaskService(periodicalyDeleteExpiredTokenFromDB());
303 getMetadataService().addExtension("xsd", MediaType.TEXT_XML, true);
304 }
305
306
307
308
309
310
311 @Requirement(reqId = Requirement.DOI_DISPO_020, reqName = Requirement.DOI_DISPO_020_NAME)
312 private TaskService createTaskService() {
313 LOG.traceEntry();
314 final TaskService checkLandingPageTask = new TaskService(true);
315 LOG.info("Sets CheckLandingPage running at each {} {}", PERIOD_SCHEDULER, PERIOD_UNIT);
316 checkLandingPageTask.scheduleAtFixedRate(
317 new LandingPageMonitoring(this.client), 0,
318 PERIOD_SCHEDULER, PERIOD_UNIT
319 );
320 return LOG.traceExit(checkLandingPageTask);
321 }
322
323
324
325
326
327
328 private TaskService periodicalyDeleteExpiredTokenFromDB() {
329 LOG.traceEntry();
330 final TaskService checkExpiredTokenTask = new TaskService(true);
331 LOG.info("Sets checkExpiredTokenTask running at each {} {}", PERIOD_SCHEDULER_FOR_TOKEN_DB,
332 PERIOD_UNIT);
333 checkExpiredTokenTask.scheduleAtFixedRate(
334 new UpdateTokenDataBase(), 0,
335 PERIOD_SCHEDULER_FOR_TOKEN_DB, PERIOD_UNIT
336 );
337 return LOG.traceExit(checkExpiredTokenTask);
338 }
339
340
341
342
343
344
345
346 private TaskService createUpdateDataBaseTaskService() {
347 LOG.traceEntry();
348 final TaskService updateDataBaseTask = new TaskService(true);
349 LOG.info("Sets UpdateDataBaseTask running at each {} {}", PERIOD_SCHEDULER, PERIOD_UNIT);
350 try {
351 updateDataBaseTask.scheduleAtFixedRate(
352 new DOIUsersUpdate(), 0,
353 DoiSettings.getInstance().getInt(Consts.DB_UPDATE_JOB_PERIOD), TimeUnit.MINUTES
354 );
355 } catch(NoClassDefFoundError e) {
356 LOG.error(e);
357 }
358 return LOG.traceExit(updateDataBaseTask);
359 }
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380 @Override
381 public Restlet createInboundRoot() {
382 LOG.traceEntry();
383
384 final Filter logContext = new Filter() {
385
386
387
388
389
390
391
392
393 @Override
394 protected int beforeHandle(final Request request, final Response response) {
395 final ClientInfo clientInfo = request.getClientInfo();
396 final String ipAddress = request.getHeaders().getFirstValue(
397 Consts.PROXIFIED_IP, clientInfo.getUpstreamAddress()
398 );
399 ThreadContext.put(Consts.LOG_IP_ADDRESS, ipAddress);
400 return Filter.CONTINUE;
401 }
402 };
403
404
405
406 final ChallengeAuthenticator challAuth = createAuthenticatorLoginBased();
407 challAuth.setOptional(true);
408
409
410 final ChallengeAuthenticator challTokenAuth = createTokenAuthenticator();
411 challTokenAuth.setOptional(false);
412
413 challTokenAuth.setMultiAuthenticating(false);
414
415
416 final RoleAuthorizer authorizer = createRoleAuthorizer();
417
418
419 final String ips = DoiSettings.getInstance().getString(Consts.ADMIN_IP_ALLOWER);
420 final boolean isEnabled = ips != null;
421 final AllowerIP blocker = new AllowerIP(getContext(), isEnabled);
422
423
424 final Router webSiteRouter = createWebSiteRouter();
425 final Router adminRouter = createAdminRouter();
426
427
428 challAuth.setNext(challTokenAuth);
429 challTokenAuth.setNext(authorizer);
430 authorizer.setNext(blocker);
431 blocker.setNext(adminRouter);
432
433 final Router router = new Router(getContext());
434 router.attachDefault(webSiteRouter);
435
436 router.attach(ADMIN_URI, challAuth);
437
438 logContext.setNext(router);
439
440 return LOG.traceExit(logContext);
441 }
442
443
444
445
446
447
448
449
450 private RoleAuthorizer createRoleAuthorizer() {
451 LOG.traceEntry();
452
453 final RoleAuthorizer roleAuth = new RoleAuthorizer() {
454
455
456
457
458
459
460
461
462 @Override
463 public int beforeHandle(final Request request, final Response response) {
464 final Method requestMethod = request.getMethod();
465 final Reference requestReference = request.getOriginalRef();
466 final String lastSeg = requestReference.getLastSegment();
467 final String path = requestReference.getPath();
468 boolean ignoreVerification = false;
469
470 if (requestMethod.equals(Method.OPTIONS)) {
471
472
473 ignoreVerification = true;
474 } else if (!requestMethod.equals(Method.DELETE) && !requestMethod.equals(Method.GET)
475 && requestReference.toString().contains(ADMIN_URI + TOKEN_URI)) {
476
477
478
479 ignoreVerification = true;
480 } else if (requestMethod.equals(Method.GET)) {
481
482 if ("projects".equals(lastSeg) && requestReference.hasQuery()) {
483 ignoreVerification = true;
484 }
485
486 if ("dois".equals(lastSeg)) {
487 ignoreVerification = true;
488 }
489
490 if (path.contains(ADMIN_URI + ROLE_USERS_URI)) {
491 ignoreVerification = true;
492 }
493 }
494
495 final int status;
496 if (ignoreVerification) {
497 response.setStatus(Status.SUCCESS_OK);
498 status = CONTINUE;
499 } else {
500 status = super.beforeHandle(request, response);
501 }
502 return status;
503 }
504
505 };
506 roleAuth.setAuthorizedRoles(
507 Arrays.asList(
508 Role.get(this, fr.cnes.doi.security.RoleAuthorizer.ROLE_ADMIN)
509 )
510 );
511
512 return LOG.traceExit(roleAuth);
513 }
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529 private Router createAdminRouter() {
530 LOG.traceEntry();
531
532 final Router router = new Router(getContext());
533
534 router.attach(SUFFIX_PROJECT_URI, SuffixProjectsResource.class);
535
536 router.attach(SUFFIX_PROJECT_URI + SUFFIX_PROJECT_NAME, ManageProjectsResource.class);
537 router.attach(SUFFIX_PROJECT_URI + SUFFIX_PROJECT_NAME + DOIS_URI,
538 SuffixProjectsDoisResource.class);
539
540 router.attach(SUFFIX_PROJECT_URI + SUFFIX_PROJECT_NAME + USERS_URI,
541 ManageUsersResource.class);
542 router.attach(SUFFIX_PROJECT_URI + SUFFIX_PROJECT_NAME + USERS_URI + USERS_NAME,
543 ManageUsersResource.class);
544
545 router.attach(SUPERUSERS_URI, ManageSuperUsersResource.class);
546 router.attach(SUPERUSERS_URI + USERS_NAME, ManageSuperUserResource.class);
547
548 router.attach(TOKEN_URI, TokenResource.class);
549 router.attach(TOKEN_URI + TOKEN_NAME_URI, TokenResource.class);
550
551 router.attach(ROLE_USERS_URI, AuthenticateResource.class);
552 router.attach(ROLE_ADMIN_URI, AuthenticateResource.class);
553
554 return LOG.traceExit(router);
555 }
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570 private Router createWebSiteRouter() {
571 LOG.traceEntry();
572
573 final Router router = new Router(getContext());
574 addStatusPage(router);
575 addServicesStatus(router);
576 addServicesStats(router);
577 addRouteForWebSite(router);
578
579 return LOG.traceExit(router);
580 }
581
582
583
584
585
586
587
588
589
590 private void addStatusPage(final Router router) {
591 LOG.traceEntry("Parameter\n router: {}", new JsonMessage(router));
592
593 final Directory directory = new Directory(
594 getContext(),
595 LocalReference.createClapReference(STATUS_PAGE_CLASSPATH)
596 );
597 directory.setDeeplyAccessible(true);
598 router.attach(RESOURCE_URI, directory);
599
600 LOG.traceExit();
601 }
602
603
604
605
606
607
608
609
610
611 private void addServices(final Router router, final int redirectorMode, final String target,
612 final String attachURI) {
613 LOG.traceEntry("Parameters\n router: {}\n redirectorMode: {}\n "
614 + "target: {}\n attachURI: {}",
615 new JsonMessage(router), redirectorMode, target, attachURI);
616
617 final Redirector redirector = new Redirector(getContext(), target, redirectorMode);
618
619 final Filter authentication = new Filter() {
620
621
622
623 @Override
624 protected int doHandle(final Request request, final Response response) {
625 response.setLocationRef(target);
626 response.setStatus(Status.REDIRECTION_PERMANENT);
627 return super.doHandle(request, response);
628 }
629 };
630 authentication.setNext(redirector);
631 router.attach(attachURI, authentication);
632 LOG.traceExit();
633 }
634
635
636
637
638
639
640
641 private void addServicesStatus(final Router router) {
642 this.addServices(router, Redirector.MODE_SERVER_OUTBOUND, TARGET_URL, STATUS_URI);
643 }
644
645
646
647
648
649
650 private void addServicesStats(final Router router) {
651 this.addServices(router, Redirector.MODE_CLIENT_PERMANENT, TARGET_STATS_URL, STATS_URI);
652 }
653
654
655
656
657
658
659
660 private void addRouteForWebSite(final Router router) {
661 LOG.traceEntry("Parameter : {}", new JsonMessage(router));
662
663 final Directory ihm = new Directory(
664 getContext(),
665 LocalReference.createClapReference(IHM_CLASSPATH)
666 );
667 ihm.setListingAllowed(false);
668 ihm.setDeeplyAccessible(true);
669 ihm.setIndexName("authentication");
670
671 router.attach(IHM_URI, RedirectingResource.class, Template.MODE_EQUALS);
672 router.attach(IHM_URI + "/", RedirectingResource.class, Template.MODE_EQUALS);
673 router.attach(IHM_URI + IHM_FOOTER_URI, FooterIhmResource.class);
674 router.attach(IHM_URI + IHM_CONFIG_URI, ConfigIhmResource.class);
675 router.attach(IHM_URI, ihm);
676
677 LOG.traceExit();
678 }
679
680
681
682
683
684
685 @Override
686 public Logger getLog() {
687 return LOG;
688 }
689
690
691
692
693
694
695
696
697
698
699
700 public List<String> getDois(final String idProject) throws ClientMdsException {
701 return this.client.getDois(idProject);
702 }
703 }