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 static fr.cnes.doi.client.ClientMDS.SCHEMA_DATACITE;
22
23 import org.apache.logging.log4j.LogManager;
24 import org.apache.logging.log4j.Logger;
25 import org.restlet.Context;
26 import org.restlet.Request;
27 import org.restlet.Response;
28 import org.restlet.Restlet;
29 import org.restlet.data.Method;
30 import org.restlet.data.Reference;
31 import org.restlet.data.Status;
32 import org.restlet.ext.wadl.ApplicationInfo;
33 import org.restlet.ext.wadl.DocumentationInfo;
34 import org.restlet.ext.wadl.GrammarsInfo;
35 import org.restlet.ext.wadl.IncludeInfo;
36 import org.restlet.routing.Filter;
37 import org.restlet.routing.Router;
38 import org.restlet.routing.Template;
39 import org.restlet.security.ChallengeAuthenticator;
40 import org.restlet.security.MethodAuthorizer;
41
42 import fr.cnes.doi.client.ClientMDS;
43 import fr.cnes.doi.db.AbstractTokenDBHelper;
44 import fr.cnes.doi.exception.DoiRuntimeException;
45 import fr.cnes.doi.resource.mds.DoiResource;
46 import fr.cnes.doi.resource.mds.DoisResource;
47 import fr.cnes.doi.resource.mds.MediaResource;
48 import fr.cnes.doi.resource.mds.MetadataResource;
49 import fr.cnes.doi.resource.mds.MetadatasResource;
50 import fr.cnes.doi.security.TokenSecurity;
51 import fr.cnes.doi.settings.Consts;
52 import fr.cnes.doi.utils.spec.Requirement;
53 import org.apache.logging.log4j.ThreadContext;
54 import org.restlet.data.ClientInfo;
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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 @Requirement(reqId = Requirement.DOI_SRV_010, reqName = Requirement.DOI_SRV_010_NAME)
114 @Requirement(reqId = Requirement.DOI_SRV_020, reqName = Requirement.DOI_SRV_020_NAME)
115 @Requirement(reqId = Requirement.DOI_SRV_030, reqName = Requirement.DOI_SRV_030_NAME)
116 @Requirement(reqId = Requirement.DOI_SRV_040, reqName = Requirement.DOI_SRV_040_NAME)
117 @Requirement(reqId = Requirement.DOI_SRV_050, reqName = Requirement.DOI_SRV_050_NAME)
118 @Requirement(reqId = Requirement.DOI_SRV_060, reqName = Requirement.DOI_SRV_060_NAME)
119 @Requirement(reqId = Requirement.DOI_SRV_070, reqName = Requirement.DOI_SRV_070_NAME)
120 @Requirement(reqId = Requirement.DOI_SRV_080, reqName = Requirement.DOI_SRV_080_NAME)
121 @Requirement(reqId = Requirement.DOI_SRV_090, reqName = Requirement.DOI_SRV_090_NAME)
122 @Requirement(reqId = Requirement.DOI_MONIT_020, reqName = Requirement.DOI_MONIT_020_NAME)
123 public final class DoiMdsApplication extends AbstractApplication {
124
125
126
127
128 public static final String DOI_TEMPLATE = "doiName";
129
130
131
132
133 public static final String DOI_URI = "/dois";
134
135
136
137
138 public static final String DOI_NAME_URI = "/{" + DOI_TEMPLATE + "}";
139
140
141
142
143 public static final String METADATAS_URI = "/metadata";
144
145
146
147
148 public static final String MEDIA_URI = "/media";
149
150
151
152
153 public static final String NAME = "Metadata Store Application";
154
155
156
157
158 private static final Logger LOG = LogManager.getLogger(DoiMdsApplication.class.getName());
159
160
161
162
163 private final ClientMDS client;
164
165
166
167
168 private final AbstractTokenDBHelper tokenDB;
169
170
171
172
173
174
175
176 public DoiMdsApplication(final ClientMDS client) {
177 super();
178 setName(NAME);
179 setDescription(
180 "Provides an application for handling Data Object Identifier at CNES<br/>"
181 + "This application provides 3 API:" + "<ul>" + "<li>dois : DOI minting</li>"
182 + "<li>metadata : Registration of the associated metadata</li>"
183 + "<li>media : Possbility to obtain metadata in various formats and/or get "
184 + "automatic, direct access to an object rather than via the \"landing page\"</li>"
185 + "</ul>");
186 this.client = client;
187 this.tokenDB = TokenSecurity.getInstance().getTokenDB();
188 }
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207 @Override
208 public Restlet createInboundRoot() {
209 LOG.traceEntry();
210
211 final Filter logContext = new Filter() {
212
213
214
215
216
217
218
219
220 @Override
221 protected int beforeHandle(final Request request, final Response response) {
222 final ClientInfo clientInfo = request.getClientInfo();
223 final String ipAddress = request.getHeaders().getFirstValue(
224 Consts.PROXIFIED_IP, clientInfo.getUpstreamAddress()
225 );
226 ThreadContext.put(Consts.LOG_IP_ADDRESS, ipAddress);
227 return Filter.CONTINUE;
228 }
229 };
230
231
232
233 final ChallengeAuthenticator challAuth = createAuthenticatorLoginBased();
234 challAuth.setOptional(true);
235
236
237 final ChallengeAuthenticator challTokenAuth = createTokenAuthenticator();
238 challTokenAuth.setOptional(true);
239
240
241 final MethodAuthorizer methodAuth = createMethodAuthorizer();
242
243
244 logContext.setNext(challAuth);
245
246
247 challAuth.setNext(challTokenAuth);
248 challTokenAuth.setNext(methodAuth);
249
250
251 methodAuth.setNext(createRouter());
252
253 final Filter filter = new SecurityPostProcessingFilter(getContext(), logContext);
254 return LOG.traceExit(filter);
255 }
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278 private Router createRouter() {
279 LOG.traceEntry();
280
281 final Router router = new Router(getContext());
282 router.attach(DOI_URI, DoisResource.class);
283 router.attach(DOI_URI + DOI_NAME_URI, DoiResource.class)
284 .getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);
285 router.attach(METADATAS_URI, MetadatasResource.class);
286 router.attach(METADATAS_URI + DOI_NAME_URI, MetadataResource.class)
287 .getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);
288 router.attach(MEDIA_URI + DOI_NAME_URI, MediaResource.class)
289 .getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);
290
291 return LOG.traceExit(router);
292 }
293
294
295
296
297
298
299
300 private MethodAuthorizer createMethodAuthorizer() {
301 LOG.traceEntry();
302
303 final MethodAuthorizer methodAuth = new MethodAuthorizer();
304 methodAuth.getAnonymousMethods().add(Method.GET);
305 methodAuth.getAnonymousMethods().add(Method.OPTIONS);
306 methodAuth.getAuthenticatedMethods().add(Method.GET);
307 methodAuth.getAuthenticatedMethods().add(Method.POST);
308 methodAuth.getAuthenticatedMethods().add(Method.PUT);
309 methodAuth.getAuthenticatedMethods().add(Method.DELETE);
310
311 return LOG.traceExit(methodAuth);
312 }
313
314
315
316
317
318
319 private String getLoginMds() {
320 LOG.traceEntry();
321 return LOG.traceExit(this.getConfig().getSecret(Consts.INIST_LOGIN));
322 }
323
324
325
326
327
328
329 private String getPwdMds() {
330 LOG.traceEntry();
331 return LOG.traceExit(this.getConfig().getSecret(Consts.INIST_PWD));
332 }
333
334
335
336
337
338
339 public String getDataCentrePrefix() {
340 LOG.traceEntry();
341 return LOG.traceExit(this.getConfig().getString(Consts.INIST_DOI));
342 }
343
344
345
346
347
348
349 public ClientMDS getClient() {
350 LOG.traceEntry();
351 return LOG.traceExit(this.client);
352 }
353
354
355
356
357
358
359 @Override
360 public AbstractTokenDBHelper getTokenDB() {
361 LOG.traceEntry();
362 return LOG.traceExit(this.tokenDB);
363 }
364
365
366
367
368
369
370 @Override
371 public Logger getLog() {
372 return LOG;
373 }
374
375
376
377
378
379
380
381
382 @Override
383 public final ApplicationInfo getApplicationInfo(final Request request,
384 final Response response) {
385 final ApplicationInfo result = super.getApplicationInfo(request, response);
386 final DocumentationInfo docInfo = new DocumentationInfo(
387 "DOI server application provides is central service that registers DOI at DataCite"
388 );
389 docInfo.setTitle(this.getName());
390 docInfo.setTextContent(this.getDescription());
391 result.setDocumentation(docInfo);
392 result.getNamespaces().put(SCHEMA_DATACITE, "default");
393 result.getNamespaces().put("http://www.w3.org/2001/XMLSchema", "xsi");
394 final GrammarsInfo grammar = new GrammarsInfo();
395 final IncludeInfo include = new IncludeInfo();
396 include.setTargetRef(new Reference(SCHEMA_DATACITE));
397 grammar.getIncludes().add(include);
398 result.setGrammars(grammar);
399 return result;
400 }
401
402
403
404
405
406
407 public static class SecurityPostProcessingFilter extends Filter {
408
409
410
411
412
413
414
415 public SecurityPostProcessingFilter(final Context context, final Restlet next) {
416 super(context, next);
417 }
418
419
420
421
422
423
424
425 @Override
426 protected void afterHandle(final Request request, final Response response) {
427 final Status status = response.getStatus();
428 final String reason = status.getReasonPhrase();
429 if (status.getCode() == Status.CLIENT_ERROR_UNAUTHORIZED.getCode()
430 && API_MDS.SECURITY_USER_NO_ROLE.getShortMessage().equals(reason)) {
431 response.getHeaders().add("WWW-Authenticate",
432 "Basic realm=\"DOI Server access\", charset=\"UTF-8\"");
433 }
434 }
435 }
436
437
438
439
440
441 public enum API_MDS {
442
443
444
445
446 CREATE_METADATA(Status.SUCCESS_CREATED, "Operation successful"),
447
448
449
450
451
452 SECURITY_USER_NOT_IN_SELECTED_ROLE(Status.CLIENT_ERROR_FORBIDDEN,
453 "Forbidden to use this role"),
454
455
456
457
458
459 SECURITY_USER_NO_ROLE(Status.CLIENT_ERROR_UNAUTHORIZED, "Fail to authorize the user"),
460
461
462
463
464
465
466
467 SECURITY_USER_CONFLICT(Status.CLIENT_ERROR_CONFLICT,
468 "Error when an user is associated to more than one role without setting selectedRole "
469 + "parameter"),
470
471
472
473
474
475
476
477 SECURITY_USER_PERMISSION(Status.CLIENT_ERROR_FORBIDDEN,
478 "User is not allowed to make this operation"),
479
480
481
482
483
484 NETWORK_PROBLEM(Status.CONNECTOR_ERROR_COMMUNICATION, "Cannot access to Datacite"),
485
486
487
488
489
490
491
492
493
494
495
496 METADATA_VALIDATION(Status.CLIENT_ERROR_BAD_REQUEST,
497 "Failed to validate the user inputs parameters"),
498
499
500
501
502
503
504 MEDIA_VALIDATION(Status.CLIENT_ERROR_BAD_REQUEST,
505 "DOI not provided or one or more of the specified mime-types or urls are invalid "
506 + "(e.g. non supported mime-type, not allowed url domain, etc.)"),
507
508
509
510
511
512 LANGING_PAGE_VALIDATION(Status.CLIENT_ERROR_BAD_REQUEST,
513 "Validation error when defining the DOI and its landing page"),
514
515
516
517
518 DOI_VALIDATION(Status.CLIENT_ERROR_BAD_REQUEST, "Character or prefix not allowed in the "
519 + "DOI"),
520
521
522
523
524
525 DATACITE_PROBLEM(Status.SERVER_ERROR_INTERNAL,
526 "Interface problem between Datacite and DOI-Server");
527
528
529
530
531 private final Status status;
532
533
534
535 private final String shortMessage;
536
537
538
539
540
541
542
543 API_MDS(final Status status, final String shortMessage) {
544 this.status = status;
545 this.shortMessage = shortMessage;
546 }
547
548
549
550
551
552
553 public Status getStatus() {
554 return this.status;
555 }
556
557
558
559
560
561
562 public String getShortMessage() {
563 return this.shortMessage;
564 }
565
566 }
567 }