View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.openr66.protocol.http.rest;
21  
22  import io.netty.bootstrap.ServerBootstrap;
23  import io.netty.channel.ChannelFuture;
24  import io.netty.channel.ChannelHandlerContext;
25  import io.netty.handler.codec.http.HttpResponseStatus;
26  import org.waarp.common.command.exception.Reply421Exception;
27  import org.waarp.common.command.exception.Reply530Exception;
28  import org.waarp.common.database.DbSession;
29  import org.waarp.common.database.exception.WaarpDatabaseException;
30  import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
31  import org.waarp.common.digest.FilesystemBasedDigest;
32  import org.waarp.common.logging.SysErrLogger;
33  import org.waarp.common.logging.WaarpLogger;
34  import org.waarp.common.logging.WaarpLoggerFactory;
35  import org.waarp.common.utility.ParametersChecker;
36  import org.waarp.common.utility.WaarpNettyUtil;
37  import org.waarp.common.utility.WaarpStringUtils;
38  import org.waarp.gateway.kernel.exception.HttpInvalidAuthenticationException;
39  import org.waarp.gateway.kernel.rest.HttpRestHandler;
40  import org.waarp.gateway.kernel.rest.RestConfiguration;
41  import org.waarp.openr66.context.R66Session;
42  import org.waarp.openr66.database.DbConstantR66;
43  import org.waarp.openr66.database.data.DbHostAuth;
44  import org.waarp.openr66.protocol.configuration.Configuration;
45  import org.waarp.openr66.protocol.http.rest.handler.DbConfigurationR66RestMethodHandler;
46  import org.waarp.openr66.protocol.http.rest.handler.DbHostAuthR66RestMethodHandler;
47  import org.waarp.openr66.protocol.http.rest.handler.DbHostConfigurationR66RestMethodHandler;
48  import org.waarp.openr66.protocol.http.rest.handler.DbRuleR66RestMethodHandler;
49  import org.waarp.openr66.protocol.http.rest.handler.DbTaskRunnerR66RestMethodHandler;
50  import org.waarp.openr66.protocol.http.rest.handler.HttpRestBandwidthR66Handler;
51  import org.waarp.openr66.protocol.http.rest.handler.HttpRestBusinessR66Handler;
52  import org.waarp.openr66.protocol.http.rest.handler.HttpRestConfigR66Handler;
53  import org.waarp.openr66.protocol.http.rest.handler.HttpRestControlR66Handler;
54  import org.waarp.openr66.protocol.http.rest.handler.HttpRestInformationR66Handler;
55  import org.waarp.openr66.protocol.http.rest.handler.HttpRestLogR66Handler;
56  import org.waarp.openr66.protocol.http.rest.handler.HttpRestServerR66Handler;
57  import org.waarp.openr66.protocol.localhandler.ServerActions;
58  
59  import java.net.InetSocketAddress;
60  import java.util.ArrayList;
61  import java.util.Arrays;
62  import java.util.HashMap;
63  import java.util.List;
64  
65  /**
66   * Handler for Rest HTTP support for R66
67   */
68  public class HttpRestR66Handler extends HttpRestHandler {
69  
70    /**
71     * Internal Logger
72     */
73    private static final WaarpLogger logger =
74        WaarpLoggerFactory.getLogger(HttpRestR66Handler.class);
75    private static final METHOD[] METHOD_0_LENGTH = new METHOD[0];
76  
77    private static final HashMap<String, DbSession> dbSessionFromUser =
78        new HashMap<String, DbSession>();
79  
80    public enum RESTHANDLERS {
81      DbHostAuth(DbHostAuthR66RestMethodHandler.BASEURI,
82                 org.waarp.openr66.database.data.DbHostAuth.class),
83      DbRule(DbRuleR66RestMethodHandler.BASEURI,
84             org.waarp.openr66.database.data.DbRule.class),
85      DbTaskRunner(DbTaskRunnerR66RestMethodHandler.BASEURI,
86                   org.waarp.openr66.database.data.DbTaskRunner.class),
87      DbHostConfiguration(DbHostConfigurationR66RestMethodHandler.BASEURI,
88                          org.waarp.openr66.database.data.DbHostConfiguration.class),
89      DbConfiguration(DbConfigurationR66RestMethodHandler.BASEURI,
90                      org.waarp.openr66.database.data.DbConfiguration.class),
91      Bandwidth(HttpRestBandwidthR66Handler.BASEURI, null),
92      Business(HttpRestBusinessR66Handler.BASEURI, null),
93      Config(HttpRestConfigR66Handler.BASEURI, null),
94      Information(HttpRestInformationR66Handler.BASEURI, null),
95      Log(HttpRestLogR66Handler.BASEURI, null),
96      Server(HttpRestServerR66Handler.BASEURI, null),
97      Control(HttpRestControlR66Handler.BASEURI, null);
98  
99      public final String uri;
100     @SuppressWarnings("rawtypes")
101     public final Class clasz;
102 
103     @SuppressWarnings("rawtypes")
104     RESTHANDLERS(final String uri, final Class clasz) {
105       this.uri = uri;
106       this.clasz = clasz;
107     }
108 
109     public static RESTHANDLERS getRESTHANDLER(final String baseUri) {
110       for (final RESTHANDLERS resthandler : RESTHANDLERS.values()) {
111         if (resthandler.uri.equals(baseUri)) {
112           return resthandler;
113         }
114       }
115       return null;
116     }
117   }
118 
119   /**
120    * To be called once to ensure default is built
121    */
122   public static void defaultHandlers() {
123     synchronized (defaultConfiguration) {
124       if (defaultConfiguration.restHashMap.isEmpty()) {
125         defaultConfiguration.setRestAuthenticated(true);
126         defaultConfiguration.setResthandlersCrud(
127             new byte[RESTHANDLERS.values().length]);
128         Arrays.fill(defaultConfiguration.getResthandlersCrud(), (byte) 0x0F);
129         final METHOD[] methods = METHOD.values();
130         defaultConfiguration.restHashMap.put(RESTHANDLERS.DbTaskRunner.uri,
131                                              new DbTaskRunnerR66RestMethodHandler(
132                                                  defaultConfiguration,
133                                                  methods));
134         defaultConfiguration.restHashMap.put(RESTHANDLERS.DbHostAuth.uri,
135                                              new DbHostAuthR66RestMethodHandler(
136                                                  defaultConfiguration,
137                                                  methods));
138         defaultConfiguration.restHashMap.put(RESTHANDLERS.DbRule.uri,
139                                              new DbRuleR66RestMethodHandler(
140                                                  defaultConfiguration,
141                                                  methods));
142         defaultConfiguration.restHashMap.put(
143             RESTHANDLERS.DbHostConfiguration.uri,
144             new DbHostConfigurationR66RestMethodHandler(defaultConfiguration,
145                                                         methods));
146         defaultConfiguration.restHashMap.put(RESTHANDLERS.DbConfiguration.uri,
147                                              new DbConfigurationR66RestMethodHandler(
148                                                  defaultConfiguration,
149                                                  methods));
150         defaultConfiguration.restHashMap.put(RESTHANDLERS.Bandwidth.uri,
151                                              new HttpRestBandwidthR66Handler(
152                                                  defaultConfiguration,
153                                                  methods));
154         defaultConfiguration.restHashMap.put(RESTHANDLERS.Business.uri,
155                                              new HttpRestBusinessR66Handler(
156                                                  defaultConfiguration,
157                                                  methods));
158         defaultConfiguration.restHashMap.put(RESTHANDLERS.Config.uri,
159                                              new HttpRestConfigR66Handler(
160                                                  defaultConfiguration,
161                                                  methods));
162         defaultConfiguration.restHashMap.put(RESTHANDLERS.Information.uri,
163                                              new HttpRestInformationR66Handler(
164                                                  defaultConfiguration,
165                                                  methods));
166         defaultConfiguration.restHashMap.put(RESTHANDLERS.Log.uri,
167                                              new HttpRestLogR66Handler(
168                                                  defaultConfiguration,
169                                                  methods));
170         defaultConfiguration.restHashMap.put(RESTHANDLERS.Server.uri,
171                                              new HttpRestServerR66Handler(
172                                                  defaultConfiguration,
173                                                  methods));
174         defaultConfiguration.restHashMap.put(RESTHANDLERS.Control.uri,
175                                              new HttpRestControlR66Handler(
176                                                  defaultConfiguration,
177                                                  methods));
178       }
179     }
180   }
181 
182   public HttpRestR66Handler(final RestConfiguration config) {
183     super(config);
184     restHashMap = config.restHashMap;
185   }
186 
187   protected static METHOD[] getMethods(final byte check) {
188     final List<METHOD> methods = new ArrayList<METHOD>();
189     if (RestConfiguration.CRUD.CREATE.isValid(check)) {
190       methods.add(METHOD.POST);
191     }
192     if (RestConfiguration.CRUD.READ.isValid(check)) {
193       methods.add(METHOD.GET);
194     }
195     if (RestConfiguration.CRUD.UPDATE.isValid(check)) {
196       methods.add(METHOD.PUT);
197     }
198     if (RestConfiguration.CRUD.DELETE.isValid(check)) {
199       methods.add(METHOD.DELETE);
200     }
201     return methods.toArray(METHOD_0_LENGTH);
202   }
203 
204   public static void instantiateHandlers(
205       final RestConfiguration restConfiguration) {
206     defaultHandlers();
207     if (restConfiguration == null) {
208       return;
209     }
210     byte check =
211         restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbTaskRunner.ordinal()];
212     if (check != 0) {
213       final METHOD[] methods = getMethods(check);
214       restConfiguration.restHashMap.put(RESTHANDLERS.DbTaskRunner.uri,
215                                         new DbTaskRunnerR66RestMethodHandler(
216                                             restConfiguration, methods));
217     }
218     check =
219         restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbHostAuth.ordinal()];
220     if (check != 0) {
221       final METHOD[] methods = getMethods(check);
222       restConfiguration.restHashMap.put(RESTHANDLERS.DbHostAuth.uri,
223                                         new DbHostAuthR66RestMethodHandler(
224                                             restConfiguration, methods));
225     }
226     check =
227         restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbRule.ordinal()];
228     if (check != 0) {
229       final METHOD[] methods = getMethods(check);
230       restConfiguration.restHashMap.put(RESTHANDLERS.DbRule.uri,
231                                         new DbRuleR66RestMethodHandler(
232                                             restConfiguration, methods));
233     }
234     check =
235         restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbHostConfiguration.ordinal()];
236     if (check != 0) {
237       final METHOD[] methods = getMethods(check);
238       restConfiguration.restHashMap.put(RESTHANDLERS.DbHostConfiguration.uri,
239                                         new DbHostConfigurationR66RestMethodHandler(
240                                             restConfiguration, methods));
241     }
242     check =
243         restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbConfiguration.ordinal()];
244     if (check != 0) {
245       final METHOD[] methods = getMethods(check);
246       restConfiguration.restHashMap.put(RESTHANDLERS.DbConfiguration.uri,
247                                         new DbConfigurationR66RestMethodHandler(
248                                             restConfiguration, methods));
249     }
250     check =
251         restConfiguration.getResthandlersCrud()[RESTHANDLERS.Bandwidth.ordinal()];
252     if (check != 0) {
253       final METHOD[] methods = getMethods(check);
254       restConfiguration.restHashMap.put(RESTHANDLERS.Bandwidth.uri,
255                                         new HttpRestBandwidthR66Handler(
256                                             restConfiguration, methods));
257     }
258     check =
259         restConfiguration.getResthandlersCrud()[RESTHANDLERS.Business.ordinal()];
260     if (check != 0) {
261       final METHOD[] methods = getMethods(check);
262       restConfiguration.restHashMap.put(RESTHANDLERS.Business.uri,
263                                         new HttpRestBusinessR66Handler(
264                                             restConfiguration, methods));
265     }
266     check =
267         restConfiguration.getResthandlersCrud()[RESTHANDLERS.Config.ordinal()];
268     if (check != 0) {
269       final METHOD[] methods = getMethods(check);
270       restConfiguration.restHashMap.put(RESTHANDLERS.Config.uri,
271                                         new HttpRestConfigR66Handler(
272                                             restConfiguration, methods));
273     }
274     check =
275         restConfiguration.getResthandlersCrud()[RESTHANDLERS.Information.ordinal()];
276     if (check != 0) {
277       final METHOD[] methods = getMethods(check);
278       restConfiguration.restHashMap.put(RESTHANDLERS.Information.uri,
279                                         new HttpRestInformationR66Handler(
280                                             restConfiguration, methods));
281     }
282     check = restConfiguration.getResthandlersCrud()[RESTHANDLERS.Log.ordinal()];
283     if (check != 0) {
284       final METHOD[] methods = getMethods(check);
285       restConfiguration.restHashMap.put(RESTHANDLERS.Log.uri,
286                                         new HttpRestLogR66Handler(
287                                             restConfiguration, methods));
288     }
289     check =
290         restConfiguration.getResthandlersCrud()[RESTHANDLERS.Server.ordinal()];
291     if (check != 0) {
292       final METHOD[] methods = getMethods(check);
293       restConfiguration.restHashMap.put(RESTHANDLERS.Server.uri,
294                                         new HttpRestServerR66Handler(
295                                             restConfiguration, methods));
296     }
297     check =
298         restConfiguration.getResthandlersCrud()[RESTHANDLERS.Control.ordinal()];
299     if (check != 0) {
300       final METHOD[] methods = getMethods(check);
301       restConfiguration.restHashMap.put(RESTHANDLERS.Control.uri,
302                                         new HttpRestControlR66Handler(
303                                             restConfiguration, methods));
304     }
305     logger.debug("Initialized handler: {}", RESTHANDLERS.values().length);
306   }
307 
308   /**
309    * Server Actions handler
310    */
311   private final ServerActions serverHandler = new ServerActions();
312 
313   @Override
314   protected final void checkConnection(final ChannelHandlerContext ctx)
315       throws HttpInvalidAuthenticationException {
316     logger.debug("Request: {} ### {}", arguments, response);
317     final String user;
318     String key = null;
319     if (restConfiguration.isRestAuthenticated()) {
320       user = arguments.getXAuthUser();
321       if (ParametersChecker.isEmpty(user)) {
322         status = HttpResponseStatus.UNAUTHORIZED;
323         throw new HttpInvalidAuthenticationException("Empty Authentication");
324       }
325       final DbHostAuth host;
326       try {
327         host = new DbHostAuth(user);
328         key = new String(host.getHostkey(), WaarpStringUtils.UTF8);
329       } catch (final WaarpDatabaseException e) {
330         // might be global Admin
331         if (user.equals(Configuration.configuration.getAdminName())) {
332           key = new String(Configuration.configuration.getServerAdminKey(),
333                            WaarpStringUtils.UTF8);
334         }
335       }
336       if (ParametersChecker.isEmpty(key)) {
337         status = HttpResponseStatus.UNAUTHORIZED;
338         throw new HttpInvalidAuthenticationException("Wrong Authentication");
339       }
340       if (restConfiguration.isRestSignature()) {
341         arguments.checkBaseAuthent(restConfiguration.getHmacSha256(), key,
342                                    restConfiguration.getRestTimeLimit());
343       } else {
344         arguments.checkTime(restConfiguration.getRestTimeLimit());
345       }
346     } else {
347       // User set only for right access, not for signature check
348       user = Configuration.configuration.getAdminName();
349       if (restConfiguration.isRestSignature()) {
350         arguments.checkBaseAuthent(restConfiguration.getHmacSha256(), null,
351                                    restConfiguration.getRestTimeLimit());
352       } else {
353         arguments.checkTime(restConfiguration.getRestTimeLimit());
354       }
355     }
356     getServerHandler().newSession();
357     final R66Session session = getServerHandler().getSession();
358     if (!restConfiguration.isRestAuthenticated()) {
359       // Default is Admin
360       session.getAuth().specialNoSessionAuth(true,
361                                              Configuration.configuration.getHostSslId());
362     } else {
363       // we have one DbSession per connection, only after authentication
364       DbSession temp = getDbSessionFromUser().get(user);
365       if (temp == null) {
366         try {
367           temp = new DbSession(DbConstantR66.admin, false);
368           getDbSessionFromUser().put(user, temp);
369         } catch (final WaarpDatabaseNoConnectionException ignored) {
370           // nothing
371         }
372       }
373       if (temp != null) {
374         temp.useConnection();
375         dbSession = temp;
376       }
377       if (key == null) {
378         status = HttpResponseStatus.UNAUTHORIZED;
379         throw new HttpInvalidAuthenticationException("Wrong Authentication");
380       }
381       try {
382         session.getAuth().connectionHttps(user,
383                                           FilesystemBasedDigest.passwdCrypt(
384                                               key.getBytes(
385                                                   WaarpStringUtils.UTF8)));
386       } catch (final Reply530Exception e) {
387         status = HttpResponseStatus.UNAUTHORIZED;
388         throw new HttpInvalidAuthenticationException("Wrong Authentication", e);
389       } catch (final Reply421Exception e) {
390         status = HttpResponseStatus.SERVICE_UNAVAILABLE;
391         throw new HttpInvalidAuthenticationException("Service unavailable", e);
392       }
393     }
394     arguments.setXAuthRole(session.getAuth().getRole());
395     arguments.methodFromUri();
396     arguments.methodFromHeader();
397   }
398 
399   @Override
400   public void channelInactive(final ChannelHandlerContext ctx)
401       throws Exception {
402     super.channelInactive(ctx);
403     getServerHandler().channelClosed();
404   }
405 
406   /**
407    * Initialize the REST service (server side) for one restConfiguration
408    *
409    * @param restConfiguration
410    */
411   public static void initializeService(
412       final RestConfiguration restConfiguration) {
413     instantiateHandlers(restConfiguration);
414     if (group == null) {
415       group = Configuration.configuration.getHttpChannelGroup();
416     }
417     // Configure the server.
418     final ServerBootstrap httpBootstrap = new ServerBootstrap();
419     WaarpNettyUtil.setServerBootstrap(httpBootstrap,
420                                       Configuration.configuration.getHttpWorkerGroup(),
421                                       Configuration.configuration.getHttpWorkerGroup(),
422                                       (int) Configuration.configuration.getTimeoutCon());
423     // Set up the event pipeline factory.
424     if (restConfiguration.isRestSsl()) {
425       httpBootstrap.childHandler(new HttpRestR66Initializer(false,
426                                                             Configuration.getWaarpSslContextFactory(),
427                                                             restConfiguration));
428     } else {
429       httpBootstrap.childHandler(
430           new HttpRestR66Initializer(false, null, restConfiguration));
431     }
432     // Bind and start to accept incoming connections.
433     final ChannelFuture future;
434     if (restConfiguration != null &&
435         restConfiguration.getRestAddress() != null &&
436         !restConfiguration.getRestAddress().isEmpty()) {
437       future = httpBootstrap.bind(
438           new InetSocketAddress(restConfiguration.getRestAddress(),
439                                 restConfiguration.getRestPort()));
440     } else {
441       future = httpBootstrap.bind(
442           new InetSocketAddress(restConfiguration.getRestPort()));
443     }
444     try {
445       future.await();
446     } catch (final InterruptedException e) {//NOSONAR
447       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
448     }
449     if (future.isSuccess()) {
450       group.add(future.channel());
451     }
452   }
453 
454   /**
455    * @return the dbSessionFromUser
456    */
457   public static HashMap<String, DbSession> getDbSessionFromUser() {
458     return dbSessionFromUser;
459   }
460 
461   /**
462    * @return the serverHandler
463    */
464   public ServerActions getServerHandler() {
465     return serverHandler;
466   }
467 }