View Javadoc

1   /*
2    * Copyright (C) 2017-2019 Centre National d'Etudes Spatiales (CNES).
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 3.0 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not, write to the Free Software
16   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17   * MA 02110-1301  USA
18   */
19  package fr.cnes.doi.application;
20  
21  import fr.cnes.doi.db.AbstractTokenDBHelper;
22  import fr.cnes.doi.security.LoginBasedVerifier;
23  import fr.cnes.doi.security.TokenBasedVerifier;
24  import fr.cnes.doi.security.TokenSecurity;
25  import fr.cnes.doi.services.CnesStatusService;
26  import fr.cnes.doi.settings.DoiSettings;
27  import fr.cnes.doi.settings.EmailSettings;
28  import fr.cnes.doi.utils.spec.Requirement;
29  import java.util.Arrays;
30  import java.util.Collections;
31  import java.util.HashSet;
32  import java.util.Set;
33  import org.apache.logging.log4j.LogManager;
34  import org.apache.logging.log4j.Logger;
35  import org.restlet.Request;
36  import org.restlet.Response;
37  import org.restlet.data.ChallengeScheme;
38  import org.restlet.data.Method;
39  import org.restlet.data.Status;
40  import org.restlet.ext.wadl.ApplicationInfo;
41  import org.restlet.ext.wadl.WadlApplication;
42  import org.restlet.ext.wadl.WadlCnesRepresentation;
43  import org.restlet.representation.Representation;
44  import org.restlet.security.ChallengeAuthenticator;
45  import org.restlet.service.CorsService;
46  
47  /**
48   * Creates a base application by setting both the CORS, the DOI status page, the
49   * proxy and the DOI.
50   *
51   * @author Jean-Christophe Malapert (jean-christophe.malapert@cnes.fr)
52   */
53  @Requirement(reqId = Requirement.DOI_DOC_010, reqName = Requirement.DOI_DOC_010_NAME)
54  public abstract class AbstractApplication extends WadlApplication {
55  
56      /**
57       * Default value of 'Access-Control-Allow-Origin' header.
58       */
59      public static final Set DEFAULT_CORS_ORIGIN = Collections.unmodifiableSet(
60              new HashSet(Arrays.asList("*"))
61      );
62  
63      /**
64       * If true, adds 'Access-Control-Allow-Credentials' header.
65       */
66      public static final boolean DEFAULT_CORS_CREDENTIALS = true;
67  
68      /**
69       * Logger.
70       */
71      private static final Logger LOG = LogManager.getLogger(AbstractApplication.class.getName());
72      /**
73       * Token database.
74       */
75      private final AbstractTokenDBHelper tokenDB = TokenSecurity.getInstance().getTokenDB();
76      /**
77       * DOI settings.
78       */
79      private final DoiSettings config = DoiSettings.getInstance();
80  
81      /**
82       * This constructor creates an instance of proxySettings and doiSettings. By
83       * creating the instance, the constructor creates :
84       * <ul>
85       * <li>the CORS service using the default value
86       * {@link AbstractApplication#DEFAULT_CORS_ORIGIN} and
87       * {@link AbstractApplication#DEFAULT_CORS_CREDENTIALS}</li>
88       * <li>the custom status page</li>
89       * </ul>
90       *
91       * @see
92       * <a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a>
93       * @see AbstractApplication#createCoreService
94       */
95      public AbstractApplication() {
96          super();
97          init();
98      }
99  
100     /**
101      * Defined services and metadata.
102      */
103     private void init() {
104         getServices().add(this.createCoreService(DEFAULT_CORS_ORIGIN, DEFAULT_CORS_CREDENTIALS));
105         setStatusService(new CnesStatusService());
106         setOwner("Centre National d'Etudes Spatiales (CNES)");
107         setAuthor("Jean-Christophe Malapert (DNO/ISA/VIP)");
108     }
109 
110     /**
111      * Defines the CORS service.
112      *
113      * @param corsOrigin IP that can access to the service
114      * @param corsCredentials credentials allowed
115      * @return CORS service
116      */
117     protected final CorsService createCoreService(final Set corsOrigin,
118             final boolean corsCredentials) {
119         LOG.traceEntry();
120         final CorsService corsService = new CorsService();
121         LOG.info("Allows all origins {}", corsOrigin);
122         corsService.setAllowedOrigins(corsOrigin);
123         LOG.info("Allows Credientials {}", corsCredentials);
124         corsService.setAllowedCredentials(corsCredentials);
125 
126         return LOG.traceExit(corsService);
127     }
128 
129     /**
130      * Creates the authenticator based on a HTTP basic. Creates the user, role
131      * and mapping user/role.
132      *
133      * @return Authenticator based on a challenge scheme
134      */
135     @Requirement(reqId = Requirement.DOI_AUTH_010, reqName = Requirement.DOI_AUTH_010_NAME)
136     protected ChallengeAuthenticator createAuthenticator() {
137         LOG.traceEntry();
138         final ChallengeAuthenticator guard = new ChallengeAuthenticator(
139                 getContext(), ChallengeScheme.HTTP_BASIC, "realm");
140 
141         guard.setVerifier(this.getContext().getDefaultVerifier());
142         guard.setEnroler(this.getContext().getDefaultEnroler());
143 
144         return LOG.traceExit(guard);
145     }
146 
147     /**
148      * Creates the authenticator based on a HTTP basic. Creates the user, role
149      * and mapping user/role.
150      *
151      * @return Authenticator based on a challenge scheme
152      */
153     @Requirement(reqId = Requirement.DOI_AUTH_010, reqName = Requirement.DOI_AUTH_010_NAME)
154     protected ChallengeAuthenticator createAuthenticatorLoginBased() {
155         LOG.traceEntry();
156         final ChallengeAuthenticator guard = new ChallengeAuthenticator(
157                 getContext(), ChallengeScheme.HTTP_BASIC, "realm") {
158             /**
159              * Cancel verification on pre-flight OPTIONS method
160              *
161              * @param request request
162              * @param response response
163              * @return the result
164              */
165             @Override
166             public int beforeHandle(final Request request, final Response response) {
167                 final int status;
168                 if (request.getMethod().equals(Method.OPTIONS)) {
169                     response.setStatus(Status.SUCCESS_OK);
170                     status = org.restlet.routing.Filter.CONTINUE;
171                 } else {
172                     status = super.beforeHandle(request, response);
173                 }
174                 return status;
175             }
176         };
177 
178         final LoginBasedVerifier verifier = new LoginBasedVerifier();
179         guard.setVerifier(verifier);
180 
181         return LOG.traceExit(guard);
182     }
183 
184     /**
185      * Creates an authentication by token.
186      *
187      * @return the object that contains the business to check
188      * @see TokenBasedVerifier the token verification
189      */
190     @Requirement(reqId = Requirement.DOI_AUTH_020, reqName = Requirement.DOI_AUTH_020_NAME)
191     protected ChallengeAuthenticator createTokenAuthenticator() {
192         LOG.traceEntry();
193         final ChallengeAuthenticator guard = new ChallengeAuthenticator(
194                 getContext(), ChallengeScheme.HTTP_OAUTH_BEARER, "testRealm") {
195             /**
196              * Verifies the token.
197              *
198              * @param request request
199              * @param response response
200              * @return the result
201              */
202             @Override
203             public int beforeHandle(final Request request, final Response response) {
204                 final int status;
205                 if (request.getMethod().equals(Method.OPTIONS)) {
206                     response.setStatus(Status.SUCCESS_OK);
207                     status = org.restlet.routing.Filter.CONTINUE;
208                 } else {
209                     status = super.beforeHandle(request, response);
210                 }
211                 return status;
212             }
213         };
214         final TokenBasedVerifier verifier = new TokenBasedVerifier(getTokenDB());
215         guard.setVerifier(verifier);
216 
217         return LOG.traceExit(guard);
218     }
219 
220     /**
221      * Creates HTML representation of the WADL.
222      *
223      * @param applicationInfo Application description
224      * @return the HTML representation of the WADL
225      */
226     @Override
227     protected Representation createHtmlRepresentation(final ApplicationInfo applicationInfo) {
228         final WadlCnesRepresentation wadl = new WadlCnesRepresentation(applicationInfo);
229         return wadl.getHtmlRepresentation();
230     }
231 
232     /**
233      * Sends alert as an email when DataCite failed.
234      *
235      * @param exception error message to send
236      */
237     public void sendAlertWhenDataCiteFailed(final Exception exception) {
238         LOG.traceEntry("Parameters : {}", exception);
239         final String subject = "Datacite problem";
240         final String message = "Dear administrator, an error has been detected"
241                 + " coming from Datacite, please look to the Service status\n" + exception;
242         EmailSettings.getInstance().sendMessage(subject, message);
243     }
244 
245     /**
246      * Returns the configuration of the DOI server.
247      *
248      * @return the configuration of the DOI server.
249      */
250     protected final DoiSettings getConfig() {
251         LOG.traceEntry();
252         return LOG.traceExit(config);
253     }
254 
255     /**
256      * Returns the token database.
257      *
258      * @return the token database
259      */
260     public AbstractTokenDBHelper getTokenDB() {
261         return this.tokenDB;
262     }
263 
264     /**
265      * Returns the logger.
266      *
267      * @return the logger
268      */
269     public Logger getLog() {
270         return LOG;
271     }
272 
273 }