1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  package fr.cnes.doi.server;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.security.KeyStore;
26  import java.util.List;
27  import org.apache.logging.log4j.LogManager;
28  import org.apache.logging.log4j.Logger;
29  import org.restlet.Application;
30  import org.restlet.Client;
31  import org.restlet.Component;
32  import org.restlet.Context;
33  import org.restlet.Server;
34  import org.restlet.data.LocalReference;
35  import org.restlet.data.Parameter;
36  import org.restlet.data.Protocol;
37  import org.restlet.engine.Engine;
38  import org.restlet.engine.connector.ConnectorHelper;
39  import org.restlet.ext.httpclient4.HttpDOIClientHelper;
40  import org.restlet.representation.Representation;
41  import org.restlet.resource.ClientResource;
42  import org.restlet.routing.Filter;
43  import org.restlet.service.LogService;
44  import org.restlet.service.Service;
45  import org.restlet.util.Series;
46  
47  import fr.cnes.doi.application.AdminApplication;
48  import fr.cnes.doi.application.DoiCrossCiteApplication;
49  import fr.cnes.doi.application.DoiMdsApplication;
50  import fr.cnes.doi.client.ClientMDS;
51  import fr.cnes.doi.db.AbstractUserRoleDBHelper;
52  import fr.cnes.doi.exception.AuthenticationAccessException;
53  import fr.cnes.doi.db.model.AuthSystemUser;
54  import fr.cnes.doi.logging.api.DoiLogDataServer;
55  import fr.cnes.doi.logging.business.JsonMessage;
56  import fr.cnes.doi.logging.security.DoiSecurityLogFilter;
57  import fr.cnes.doi.security.RoleAuthorizer;
58  import fr.cnes.doi.settings.Consts;
59  import fr.cnes.doi.settings.DoiSettings;
60  import fr.cnes.doi.settings.EmailSettings;
61  import fr.cnes.doi.settings.JettySettings;
62  import fr.cnes.doi.settings.ProxySettings;
63  import fr.cnes.doi.utils.Utils;
64  import fr.cnes.doi.utils.spec.Requirement;
65  import fr.cnes.doi.plugin.PluginFactory;
66  import static fr.cnes.doi.plugin.Utils.addPath;
67  import fr.cnes.doi.db.IAuthenticationDBHelper;
68  import fr.cnes.doi.exception.ClientMdsException;
69  import static fr.cnes.doi.settings.Consts.USE_FORWARDED_FOR_HEADER;
70  
71  
72  
73  
74  
75  
76  
77  public class DoiServer extends Component {
78  
79      
80  
81  
82  
83      public static final String DEFAULT_SSL_CTX = "org.restlet.engine.ssl.DefaultSslContextFactory";
84      
85  
86  
87      public static final String SSL_CTX_FACTORY = "sslContextFactory";
88      
89  
90  
91      public static final String KEY_STORE_PATH = "keyStorePath";
92      
93  
94  
95      public static final String KEY_STORE_PWD = "keyStorePassword";
96      
97  
98  
99      public static final String KEY_STORE_TYPE = "keyStoreType";
100     
101 
102 
103     public static final String KEY_PWD = "keyPassword";
104     
105 
106 
107     public static final String TRUST_STORE_PATH = "trustStorePath";
108     
109 
110 
111     public static final String TRUST_STORE_PWD = "trustStorePassword";
112     
113 
114 
115     public static final String TRUST_STORE_TYPE = "trustStoreType";
116     
117 
118 
119     public static final String JKS_FILE = "doiServerKey.jks";
120 
121     
122 
123 
124     public static final String JKS_DIRECTORY = "jks";
125 
126     
127 
128 
129     public static final String MDS_URI = "/mds";
130 
131     
132 
133 
134     public static final String CITATION_URI = "/citation";
135 
136     
137 
138 
139     public static final String STATUS_URI = "/status";
140 
141     
142 
143 
144     public static final String RESTLET_MAX_TOTAL_CONNECTIONS = "maxTotalConnections";
145 
146     
147 
148 
149     public static final String RESTLET_MAX_CONNECTIONS_PER_HOST = "maxConnectionsPerHost";
150 
151     
152 
153 
154     public static final String DEFAULT_MAX_TOTAL_CONNECTIONS = "-1";
155 
156     
157 
158 
159     public static final String DEFAULT_MAX_CONNECTIONS_PER_HOST = "-1";
160 
161     
162 
163 
164     private static final Logger LOG = LogManager.getLogger(DoiServer.class.getName());
165 
166     
167 
168 
169     private static final String MESSAGE_TPL = "{} : {}";
170 
171     
172 
173 
174     private static final String PARAMETERS = "Parameters";
175 
176     static {
177         final List<ConnectorHelper<Client>> registeredClients = Engine.getInstance().
178                 getRegisteredClients();
179         registeredClients.add(0, new HttpDOIClientHelper(null));
180     }
181 
182     
183 
184 
185     private final DoiSettings settings;
186 
187     
188 
189 
190 
191 
192 
193 
194 
195     public DoiServer(final DoiSettings settings) throws ClientMdsException {
196         super();
197         this.settings = settings;
198         startWithProxy();
199     }
200 
201     
202 
203 
204     private void initLogServices() {
205         LOG.traceEntry();
206 
207         final LogService logServiceApplication = new DoiLogDataServer(Utils.HTTP_LOGGER_NAME, true);
208         this.getServices().add(logServiceApplication);
209 
210         final Service logServiceSecurity = new LogService(true) {
211             
212 
213 
214 
215 
216 
217 
218 
219             @Override
220             public Filter createInboundFilter(final Context context) {
221                 return new DoiSecurityLogFilter();
222             }
223         };
224         this.getServices().add(logServiceSecurity);
225         LOG.traceExit();
226     }
227 
228     
229 
230 
231     @Requirement(reqId = Requirement.DOI_ARCHI_010, reqName = Requirement.DOI_ARCHI_010_NAME)
232     private void configureServer() throws ClientMdsException {
233         LOG.traceEntry();
234         final boolean isHttpStarted = initHttpServer();
235         final boolean isHttpsStarted = initHttpsServer();
236         if (isHttpStarted || isHttpsStarted) {
237             initClients();
238             initAttachApplication();
239         } else {
240             LOG.warn("No server is configured, please check your configuration file");
241         }
242 
243         LOG.traceExit();
244     }
245 
246     
247 
248 
249 
250 
251     private boolean initHttpServer() {
252         LOG.traceEntry();
253         final boolean isConfigured;
254         if (settings.hasValue(Consts.SERVER_HTTP_PORT)) {
255             final String httpPort = settings.getString(Consts.SERVER_HTTP_PORT);
256             final Server serverHttp = startHttpServer(Integer.parseInt(httpPort));
257             this.getServers().add(serverHttp);
258             initJettyConfiguration(serverHttp);
259             isConfigured = true;
260         } else {
261             isConfigured = false;
262         }
263         return LOG.traceExit(isConfigured);
264     }
265 
266     
267 
268 
269 
270 
271     private boolean initHttpsServer() {
272         LOG.traceEntry();
273         final boolean isConfigured;
274         if (settings.hasValue(Consts.SERVER_HTTPS_PORT)) {
275             final String httpsPort = settings.getString(Consts.SERVER_HTTPS_PORT);
276             final Server serverHttps = startHttpsServer(Integer.parseInt(httpsPort));
277             this.getServers().add(serverHttps);
278             initJettyConfiguration(serverHttps);
279             isConfigured = true;
280         } else {
281             isConfigured = false;
282         }
283         return LOG.traceExit(isConfigured);
284     }
285 
286     
287 
288 
289 
290 
291     private void initJettyConfiguration(final Server server) {
292         LOG.traceEntry(new JsonMessage(server));
293         final JettySettings jettyProps = new JettySettings(server, settings);
294         jettyProps.addParamsToServerContext();
295         LOG.traceExit();
296     }
297 
298     
299 
300 
301 
302     private void initClients() {
303         LOG.traceEntry();
304         this.getClients().add(Protocol.HTTP);
305         this.getClients().add(Protocol.HTTPS);
306         this.getClients().add(Protocol.CLAP);
307         this.getClients().add(Protocol.FILE);
308         LOG.traceExit();
309     }
310 
311     
312 
313 
314     private void initAttachApplication() throws ClientMdsException {
315         LOG.traceEntry();
316         final DoiSettings doiConfig = DoiSettings.getInstance();
317         final String contextMode = doiConfig.getString(Consts.CONTEXT_MODE);
318         final ClientMDS client = new ClientMDS(ClientMDS.Context.valueOf(contextMode), 
319                 doiConfig.getSecret(Consts.INIST_LOGIN),
320                 doiConfig.getSecret(Consts.INIST_PWD));          
321         final Application appDoiProject = new DoiMdsApplication(client);
322         final Application appAdmin = new AdminApplication(client);
323         this.getDefaultHost().attach(MDS_URI, appDoiProject);
324         this.getDefaultHost().attach(CITATION_URI, new DoiCrossCiteApplication());
325         this.getDefaultHost().attachDefault(appAdmin);
326         
327         RoleAuthorizer.getInstance().createRealmFor(appDoiProject);
328         RoleAuthorizer.getInstance().createRealmFor(appAdmin);
329         
330         final String doiAdmin = PluginFactory.getAuthenticationSystem().getDOIAdmin();
331         addAuthenticationUserAsAdmin(doiAdmin);
332         LOG.traceExit();
333     }
334 
335     
336 
337 
338 
339 
340     private void addAuthenticationUserAsAdmin(final String username) {
341         LOG.traceEntry("Parameter\n   username: {}", username);
342         final IAuthenticationDBHelper authenticationService = PluginFactory.
343                 getAuthenticationSystem();
344         final AbstractUserRoleDBHelper manageUsers = PluginFactory.getUserManagement();
345         try {
346             boolean isFound = false;
347             final List<AuthSystemUser> authenticationUsers = authenticationService.
348                     getDOIProjectMembers();
349             for (final AuthSystemUser authenticationUser : authenticationUsers) {
350                 if (authenticationUser.getUsername().equals(username)) {
351                     manageUsers.setUserToAdminGroup(authenticationUser.getUsername());
352                     isFound = true;
353                     break;
354                 }
355             }
356             if (!isFound) {
357                 LOG.warn("{} is not registered in the authentication system - Cannot create "
358                         + "the administrator in DOI database",
359                         username
360                 );
361             }
362         } catch (AuthenticationAccessException ex) {
363             LOG.catching(ex);
364             LOG.warn("Cannot create an administrator: {}", ex);
365         }
366 
367         LOG.traceExit();
368     }
369 
370     
371 
372 
373 
374     private void startWithProxy() throws ClientMdsException {
375         LOG.traceEntry();
376         initLogServices();
377         RoleAuthorizer.getInstance();
378         ProxySettings.getInstance();
379         EmailSettings.getInstance();
380         configureServer();
381         LOG.traceExit();
382     }
383 
384     
385 
386 
387 
388 
389 
390     private Server startHttpServer(final Integer port) {
391         LOG.traceEntry(MESSAGE_TPL, PARAMETERS, port);
392         final Server server = new Server(Protocol.HTTP, port, this);
393         return LOG.traceExit(server);
394     }
395 
396     
397 
398 
399 
400 
401 
402     private Server startHttpsServer(final Integer port) {
403         LOG.traceEntry(MESSAGE_TPL, PARAMETERS, port);
404         final String pathKeyStore;
405         if (settings.hasValue(Consts.SERVER_HTTPS_KEYSTORE_PATH)) {
406             pathKeyStore = settings.getString(Consts.SERVER_HTTPS_KEYSTORE_PATH);
407             LOG.debug("path key store value loaded from a custom configuration file");
408         } else {
409             pathKeyStore = extractKeyStoreToPath();
410             LOG.debug("path key store value loaded from an internal configuration");
411         }
412 
413         final String pathKeyTrustStore;
414         if (settings.hasValue(Consts.SERVER_HTTPS_TRUST_STORE_PATH)) {
415             pathKeyTrustStore = settings.getString(Consts.SERVER_HTTPS_TRUST_STORE_PATH);
416         } else {
417             pathKeyTrustStore = extractKeyStoreToPath();
418         }
419 
420         
421         final Server server = new Server(new Context(), Protocol.HTTPS, port, this);
422         final Series<Parameter> parameters = server.getContext().getParameters();
423 
424         LOG.debug(MESSAGE_TPL, USE_FORWARDED_FOR_HEADER, "true", "true");
425         parameters.set(USE_FORWARDED_FOR_HEADER, "true");
426 
427         LOG.debug(MESSAGE_TPL, RESTLET_MAX_TOTAL_CONNECTIONS, DoiSettings.getInstance().getString(
428                 fr.cnes.doi.settings.Consts.RESTLET_MAX_TOTAL_CONNECTIONS,
429                 DEFAULT_MAX_TOTAL_CONNECTIONS));
430         parameters.set(RESTLET_MAX_TOTAL_CONNECTIONS, DoiSettings.getInstance().getString(
431                 fr.cnes.doi.settings.Consts.RESTLET_MAX_TOTAL_CONNECTIONS,
432                 DEFAULT_MAX_TOTAL_CONNECTIONS));
433 
434         LOG.debug(MESSAGE_TPL, RESTLET_MAX_CONNECTIONS_PER_HOST, DoiSettings.getInstance().
435                 getString(fr.cnes.doi.settings.Consts.RESTLET_MAX_CONNECTIONS_PER_HOST,
436                         DEFAULT_MAX_CONNECTIONS_PER_HOST));
437         parameters.set(RESTLET_MAX_CONNECTIONS_PER_HOST, DoiSettings.getInstance().getString(
438                 fr.cnes.doi.settings.Consts.RESTLET_MAX_CONNECTIONS_PER_HOST,
439                 DEFAULT_MAX_CONNECTIONS_PER_HOST));
440 
441         LOG.debug(MESSAGE_TPL, SSL_CTX_FACTORY, DEFAULT_SSL_CTX);
442         parameters.add(SSL_CTX_FACTORY, DEFAULT_SSL_CTX);
443 
444         
445         LOG.debug(MESSAGE_TPL, KEY_STORE_PATH, pathKeyStore);
446         parameters.add(KEY_STORE_PATH, pathKeyStore);
447 
448         
449         LOG.debug(MESSAGE_TPL, KEY_STORE_PWD, "xxxxxxx");
450         parameters.add(KEY_STORE_PWD, settings.getSecret(Consts.SERVER_HTTPS_KEYSTORE_PASSWD));
451 
452         
453         LOG.debug(MESSAGE_TPL, KEY_STORE_TYPE, KeyStore.getDefaultType());
454         parameters.add(KEY_STORE_TYPE, KeyStore.getDefaultType());
455 
456         
457         LOG.debug(MESSAGE_TPL, KEY_PWD, "xxxxxxxx");
458         parameters.add(KEY_PWD, settings.getSecret(Consts.SERVER_HTTPS_SECRET_KEY));
459 
460         
461         LOG.debug(MESSAGE_TPL, TRUST_STORE_PATH, pathKeyTrustStore);
462         parameters.add(TRUST_STORE_PATH, pathKeyTrustStore);
463 
464         
465         LOG.debug(MESSAGE_TPL, TRUST_STORE_PWD, "xxxxx");
466         parameters.add(TRUST_STORE_PWD, settings.getSecret(Consts.SERVER_HTTPS_TRUST_STORE_PASSWD));
467 
468         
469         LOG.debug(MESSAGE_TPL, TRUST_STORE_TYPE, KeyStore.getDefaultType());
470         parameters.add(TRUST_STORE_TYPE, KeyStore.getDefaultType());
471 
472         return LOG.traceExit(server);
473     }
474 
475     
476 
477 
478 
479 
480     private String extractKeyStoreToPath() {
481         LOG.traceEntry();
482         String result;
483         final Representation jks = new ClientResource(
484                 LocalReference.createClapReference("class/" + JKS_FILE)
485         ).get();
486         try {
487             final Path outputDirectory = new File(JKS_DIRECTORY).toPath();
488             if (Files.notExists(outputDirectory)) {
489                 Files.createDirectory(outputDirectory);
490                 LOG.info("Creates {} directory to extract {} in it", outputDirectory, JKS_FILE);
491             }
492             final String outputJksLocation = outputDirectory.getFileName()
493                     + File.separator
494                     + JKS_FILE;
495 
496             final File outputFile = new File(outputJksLocation);
497             Files.copy(jks.getStream(),
498                     outputFile.toPath(),
499                     java.nio.file.StandardCopyOption.REPLACE_EXISTING
500             );
501             LOG.info("Copy/replace if exists {} in {}", JKS_FILE, outputDirectory, JKS_FILE);
502             result = outputJksLocation;
503         } catch (IOException ex) {
504             LOG.fatal("Unable to extract keystore from class/" + JKS_FILE, ex);
505             result = "";
506         }
507         return LOG.traceExit(result);
508     }
509 
510     
511 
512 
513     @Override
514     public synchronized void start() throws Exception {
515         LOG.info("Starting server ...");
516         addPath(DoiSettings.getInstance().getPathApp() + File.separatorChar + "plugins");
517         super.start();
518         LOG.info("Server started");
519     }
520 
521     
522 
523 
524     @Override
525     public synchronized void stop() throws Exception {
526         LOG.info("Stopping the server ...");
527         super.stop();
528         LOG.info("Stopping Authentication plugin");
529         PluginFactory.getAuthenticationSystem().release();
530         LOG.info("Stopping Project plugin");
531         PluginFactory.getProjectSuffix().release();
532         LOG.info("Stopping Token plugin");
533         PluginFactory.getToken().release();
534         LOG.info("Stopping UserManagement plugin");
535         PluginFactory.getUserManagement().release();
536         EmailSettings.getInstance().sendMessage("[DOI] Stopping Server",
537                 "The server has been interrupted");
538         LOG.info("Server stopped");
539     }
540 
541 }