HttpRestR66Handler.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.openr66.protocol.http.rest;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.waarp.common.command.exception.Reply421Exception;
import org.waarp.common.command.exception.Reply530Exception;
import org.waarp.common.database.DbSession;
import org.waarp.common.database.exception.WaarpDatabaseException;
import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
import org.waarp.common.digest.FilesystemBasedDigest;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.ParametersChecker;
import org.waarp.common.utility.WaarpNettyUtil;
import org.waarp.common.utility.WaarpStringUtils;
import org.waarp.gateway.kernel.exception.HttpInvalidAuthenticationException;
import org.waarp.gateway.kernel.rest.HttpRestHandler;
import org.waarp.gateway.kernel.rest.RestConfiguration;
import org.waarp.openr66.context.R66Session;
import org.waarp.openr66.database.DbConstantR66;
import org.waarp.openr66.database.data.DbHostAuth;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.http.rest.handler.DbConfigurationR66RestMethodHandler;
import org.waarp.openr66.protocol.http.rest.handler.DbHostAuthR66RestMethodHandler;
import org.waarp.openr66.protocol.http.rest.handler.DbHostConfigurationR66RestMethodHandler;
import org.waarp.openr66.protocol.http.rest.handler.DbRuleR66RestMethodHandler;
import org.waarp.openr66.protocol.http.rest.handler.DbTaskRunnerR66RestMethodHandler;
import org.waarp.openr66.protocol.http.rest.handler.HttpRestBandwidthR66Handler;
import org.waarp.openr66.protocol.http.rest.handler.HttpRestBusinessR66Handler;
import org.waarp.openr66.protocol.http.rest.handler.HttpRestConfigR66Handler;
import org.waarp.openr66.protocol.http.rest.handler.HttpRestControlR66Handler;
import org.waarp.openr66.protocol.http.rest.handler.HttpRestInformationR66Handler;
import org.waarp.openr66.protocol.http.rest.handler.HttpRestLogR66Handler;
import org.waarp.openr66.protocol.http.rest.handler.HttpRestServerR66Handler;
import org.waarp.openr66.protocol.localhandler.ServerActions;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* Handler for Rest HTTP support for R66
*/
public class HttpRestR66Handler extends HttpRestHandler {
/**
* Internal Logger
*/
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(HttpRestR66Handler.class);
private static final METHOD[] METHOD_0_LENGTH = new METHOD[0];
private static final HashMap<String, DbSession> dbSessionFromUser =
new HashMap<String, DbSession>();
public enum RESTHANDLERS {
DbHostAuth(DbHostAuthR66RestMethodHandler.BASEURI,
org.waarp.openr66.database.data.DbHostAuth.class),
DbRule(DbRuleR66RestMethodHandler.BASEURI,
org.waarp.openr66.database.data.DbRule.class),
DbTaskRunner(DbTaskRunnerR66RestMethodHandler.BASEURI,
org.waarp.openr66.database.data.DbTaskRunner.class),
DbHostConfiguration(DbHostConfigurationR66RestMethodHandler.BASEURI,
org.waarp.openr66.database.data.DbHostConfiguration.class),
DbConfiguration(DbConfigurationR66RestMethodHandler.BASEURI,
org.waarp.openr66.database.data.DbConfiguration.class),
Bandwidth(HttpRestBandwidthR66Handler.BASEURI, null),
Business(HttpRestBusinessR66Handler.BASEURI, null),
Config(HttpRestConfigR66Handler.BASEURI, null),
Information(HttpRestInformationR66Handler.BASEURI, null),
Log(HttpRestLogR66Handler.BASEURI, null),
Server(HttpRestServerR66Handler.BASEURI, null),
Control(HttpRestControlR66Handler.BASEURI, null);
public final String uri;
@SuppressWarnings("rawtypes")
public final Class clasz;
@SuppressWarnings("rawtypes")
RESTHANDLERS(final String uri, final Class clasz) {
this.uri = uri;
this.clasz = clasz;
}
public static RESTHANDLERS getRESTHANDLER(final String baseUri) {
for (final RESTHANDLERS resthandler : RESTHANDLERS.values()) {
if (resthandler.uri.equals(baseUri)) {
return resthandler;
}
}
return null;
}
}
/**
* To be called once to ensure default is built
*/
public static void defaultHandlers() {
synchronized (defaultConfiguration) {
if (defaultConfiguration.restHashMap.isEmpty()) {
defaultConfiguration.setRestAuthenticated(true);
defaultConfiguration.setResthandlersCrud(
new byte[RESTHANDLERS.values().length]);
Arrays.fill(defaultConfiguration.getResthandlersCrud(), (byte) 0x0F);
final METHOD[] methods = METHOD.values();
defaultConfiguration.restHashMap.put(RESTHANDLERS.DbTaskRunner.uri,
new DbTaskRunnerR66RestMethodHandler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.DbHostAuth.uri,
new DbHostAuthR66RestMethodHandler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.DbRule.uri,
new DbRuleR66RestMethodHandler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(
RESTHANDLERS.DbHostConfiguration.uri,
new DbHostConfigurationR66RestMethodHandler(defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.DbConfiguration.uri,
new DbConfigurationR66RestMethodHandler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.Bandwidth.uri,
new HttpRestBandwidthR66Handler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.Business.uri,
new HttpRestBusinessR66Handler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.Config.uri,
new HttpRestConfigR66Handler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.Information.uri,
new HttpRestInformationR66Handler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.Log.uri,
new HttpRestLogR66Handler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.Server.uri,
new HttpRestServerR66Handler(
defaultConfiguration,
methods));
defaultConfiguration.restHashMap.put(RESTHANDLERS.Control.uri,
new HttpRestControlR66Handler(
defaultConfiguration,
methods));
}
}
}
public HttpRestR66Handler(final RestConfiguration config) {
super(config);
restHashMap = config.restHashMap;
}
protected static METHOD[] getMethods(final byte check) {
final List<METHOD> methods = new ArrayList<METHOD>();
if (RestConfiguration.CRUD.CREATE.isValid(check)) {
methods.add(METHOD.POST);
}
if (RestConfiguration.CRUD.READ.isValid(check)) {
methods.add(METHOD.GET);
}
if (RestConfiguration.CRUD.UPDATE.isValid(check)) {
methods.add(METHOD.PUT);
}
if (RestConfiguration.CRUD.DELETE.isValid(check)) {
methods.add(METHOD.DELETE);
}
return methods.toArray(METHOD_0_LENGTH);
}
public static void instantiateHandlers(
final RestConfiguration restConfiguration) {
defaultHandlers();
if (restConfiguration == null) {
return;
}
byte check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbTaskRunner.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.DbTaskRunner.uri,
new DbTaskRunnerR66RestMethodHandler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbHostAuth.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.DbHostAuth.uri,
new DbHostAuthR66RestMethodHandler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbRule.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.DbRule.uri,
new DbRuleR66RestMethodHandler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbHostConfiguration.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.DbHostConfiguration.uri,
new DbHostConfigurationR66RestMethodHandler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.DbConfiguration.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.DbConfiguration.uri,
new DbConfigurationR66RestMethodHandler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.Bandwidth.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.Bandwidth.uri,
new HttpRestBandwidthR66Handler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.Business.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.Business.uri,
new HttpRestBusinessR66Handler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.Config.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.Config.uri,
new HttpRestConfigR66Handler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.Information.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.Information.uri,
new HttpRestInformationR66Handler(
restConfiguration, methods));
}
check = restConfiguration.getResthandlersCrud()[RESTHANDLERS.Log.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.Log.uri,
new HttpRestLogR66Handler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.Server.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.Server.uri,
new HttpRestServerR66Handler(
restConfiguration, methods));
}
check =
restConfiguration.getResthandlersCrud()[RESTHANDLERS.Control.ordinal()];
if (check != 0) {
final METHOD[] methods = getMethods(check);
restConfiguration.restHashMap.put(RESTHANDLERS.Control.uri,
new HttpRestControlR66Handler(
restConfiguration, methods));
}
logger.debug("Initialized handler: {}", RESTHANDLERS.values().length);
}
/**
* Server Actions handler
*/
private final ServerActions serverHandler = new ServerActions();
@Override
protected final void checkConnection(final ChannelHandlerContext ctx)
throws HttpInvalidAuthenticationException {
logger.debug("Request: {} ### {}", arguments, response);
final String user;
String key = null;
if (restConfiguration.isRestAuthenticated()) {
user = arguments.getXAuthUser();
if (ParametersChecker.isEmpty(user)) {
status = HttpResponseStatus.UNAUTHORIZED;
throw new HttpInvalidAuthenticationException("Empty Authentication");
}
final DbHostAuth host;
try {
host = new DbHostAuth(user);
key = new String(host.getHostkey(), WaarpStringUtils.UTF8);
} catch (final WaarpDatabaseException e) {
// might be global Admin
if (user.equals(Configuration.configuration.getAdminName())) {
key = new String(Configuration.configuration.getServerAdminKey(),
WaarpStringUtils.UTF8);
}
}
if (ParametersChecker.isEmpty(key)) {
status = HttpResponseStatus.UNAUTHORIZED;
throw new HttpInvalidAuthenticationException("Wrong Authentication");
}
if (restConfiguration.isRestSignature()) {
arguments.checkBaseAuthent(restConfiguration.getHmacSha256(), key,
restConfiguration.getRestTimeLimit());
} else {
arguments.checkTime(restConfiguration.getRestTimeLimit());
}
} else {
// User set only for right access, not for signature check
user = Configuration.configuration.getAdminName();
if (restConfiguration.isRestSignature()) {
arguments.checkBaseAuthent(restConfiguration.getHmacSha256(), null,
restConfiguration.getRestTimeLimit());
} else {
arguments.checkTime(restConfiguration.getRestTimeLimit());
}
}
getServerHandler().newSession();
final R66Session session = getServerHandler().getSession();
if (!restConfiguration.isRestAuthenticated()) {
// Default is Admin
session.getAuth().specialNoSessionAuth(true,
Configuration.configuration.getHostSslId());
} else {
// we have one DbSession per connection, only after authentication
DbSession temp = getDbSessionFromUser().get(user);
if (temp == null) {
try {
temp = new DbSession(DbConstantR66.admin, false);
getDbSessionFromUser().put(user, temp);
} catch (final WaarpDatabaseNoConnectionException ignored) {
// nothing
}
}
if (temp != null) {
temp.useConnection();
dbSession = temp;
}
if (key == null) {
status = HttpResponseStatus.UNAUTHORIZED;
throw new HttpInvalidAuthenticationException("Wrong Authentication");
}
try {
session.getAuth().connectionHttps(user,
FilesystemBasedDigest.passwdCrypt(
key.getBytes(
WaarpStringUtils.UTF8)));
} catch (final Reply530Exception e) {
status = HttpResponseStatus.UNAUTHORIZED;
throw new HttpInvalidAuthenticationException("Wrong Authentication", e);
} catch (final Reply421Exception e) {
status = HttpResponseStatus.SERVICE_UNAVAILABLE;
throw new HttpInvalidAuthenticationException("Service unavailable", e);
}
}
arguments.setXAuthRole(session.getAuth().getRole());
arguments.methodFromUri();
arguments.methodFromHeader();
}
@Override
public void channelInactive(final ChannelHandlerContext ctx)
throws Exception {
super.channelInactive(ctx);
getServerHandler().channelClosed();
}
/**
* Initialize the REST service (server side) for one restConfiguration
*
* @param restConfiguration
*/
public static void initializeService(
final RestConfiguration restConfiguration) {
instantiateHandlers(restConfiguration);
if (group == null) {
group = Configuration.configuration.getHttpChannelGroup();
}
// Configure the server.
final ServerBootstrap httpBootstrap = new ServerBootstrap();
WaarpNettyUtil.setServerBootstrap(httpBootstrap,
Configuration.configuration.getHttpWorkerGroup(),
Configuration.configuration.getHttpWorkerGroup(),
(int) Configuration.configuration.getTimeoutCon());
// Set up the event pipeline factory.
if (restConfiguration.isRestSsl()) {
httpBootstrap.childHandler(new HttpRestR66Initializer(false,
Configuration.getWaarpSslContextFactory(),
restConfiguration));
} else {
httpBootstrap.childHandler(
new HttpRestR66Initializer(false, null, restConfiguration));
}
// Bind and start to accept incoming connections.
final ChannelFuture future;
if (restConfiguration != null &&
restConfiguration.getRestAddress() != null &&
!restConfiguration.getRestAddress().isEmpty()) {
future = httpBootstrap.bind(
new InetSocketAddress(restConfiguration.getRestAddress(),
restConfiguration.getRestPort()));
} else {
future = httpBootstrap.bind(
new InetSocketAddress(restConfiguration.getRestPort()));
}
try {
future.await();
} catch (final InterruptedException e) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
}
if (future.isSuccess()) {
group.add(future.channel());
}
}
/**
* @return the dbSessionFromUser
*/
public static HashMap<String, DbSession> getDbSessionFromUser() {
return dbSessionFromUser;
}
/**
* @return the serverHandler
*/
public ServerActions getServerHandler() {
return serverHandler;
}
}