/** * This file is part of Waarp Project (named also Waarp or GG). * * Copyright 2009, Frederic Bregier, 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 java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.List; 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.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; 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.DbConstant; 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.HttpRestInformationR66Handler; import org.waarp.openr66.protocol.http.rest.handler.HttpRestLogR66Handler; import org.waarp.openr66.protocol.http.rest.handler.HttpRestServerR66Handler; import org.waarp.openr66.protocol.http.rest.handler.HttpRestControlR66Handler; import org.waarp.openr66.protocol.localhandler.ServerActions; /** * Handler for Rest HTTP support for R66 * * @author Frederic Bregier * */ public class HttpRestR66Handler extends HttpRestHandler { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(HttpRestR66Handler.class); private static HashMap<String, DbSession> dbSessionFromUser = new HashMap<String, DbSession>(); public static 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 String uri; @SuppressWarnings("rawtypes") public Class clasz; @SuppressWarnings("rawtypes") RESTHANDLERS(String uri, Class clasz) { this.uri = uri; this.clasz = clasz; } public static RESTHANDLERS getRESTHANDLER(String baseUri) { for (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.REST_AUTHENTICATED = true; defaultConfiguration.RESTHANDLERS_CRUD = new byte[RESTHANDLERS.values().length]; for (int i = 0; i < defaultConfiguration.RESTHANDLERS_CRUD.length; i++) { defaultConfiguration.RESTHANDLERS_CRUD[i] = 0x0F; } 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(RestConfiguration config) { super(config); restHashMap = config.restHashMap; } protected static METHOD[] getMethods(byte check) { 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(new METHOD[0]); } public static void instantiateHandlers(RestConfiguration restConfiguration) { defaultHandlers(); byte check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.DbTaskRunner.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.DbTaskRunner.uri, new DbTaskRunnerR66RestMethodHandler( restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.DbHostAuth.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.DbHostAuth.uri, new DbHostAuthR66RestMethodHandler( restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.DbRule.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.DbRule.uri, new DbRuleR66RestMethodHandler( restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.DbHostConfiguration.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.DbHostConfiguration.uri, new DbHostConfigurationR66RestMethodHandler(restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.DbConfiguration.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.DbConfiguration.uri, new DbConfigurationR66RestMethodHandler(restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.Bandwidth.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.Bandwidth.uri, new HttpRestBandwidthR66Handler( restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.Business.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.Business.uri, new HttpRestBusinessR66Handler( restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.Config.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.Config.uri, new HttpRestConfigR66Handler(restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.Information.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.Information.uri, new HttpRestInformationR66Handler( restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.Log.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.Log.uri, new HttpRestLogR66Handler(restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.Server.ordinal()]; if (check != 0) { METHOD[] methods = getMethods(check); restConfiguration.restHashMap.put(RESTHANDLERS.Server.uri, new HttpRestServerR66Handler(restConfiguration, methods)); } check = restConfiguration.RESTHANDLERS_CRUD[RESTHANDLERS.Control.ordinal()]; if (check != 0) { 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 ServerActions serverHandler = new ServerActions(); @Override protected void checkConnection(ChannelHandlerContext ctx) throws HttpInvalidAuthenticationException { logger.debug("Request: {} ### {}", arguments, response); String user = null; String key = null; if (restConfiguration.REST_AUTHENTICATED) { user = arguments.getXAuthUser(); if (user == null || user.isEmpty()) { status = HttpResponseStatus.UNAUTHORIZED; throw new HttpInvalidAuthenticationException("Empty Authentication"); } DbHostAuth host; try { host = new DbHostAuth(DbConstant.admin.getSession(), user); key = new String(host.getHostkey(), WaarpStringUtils.UTF8); } catch (WaarpDatabaseException e) { // might be global Admin if (user.equals(Configuration.configuration.getADMINNAME())) { key = new String(Configuration.configuration.getSERVERADMINKEY(), WaarpStringUtils.UTF8); } } if (key == null || key.isEmpty()) { status = HttpResponseStatus.UNAUTHORIZED; throw new HttpInvalidAuthenticationException("Wrong Authentication"); } if (restConfiguration.REST_SIGNATURE) { arguments.checkBaseAuthent(restConfiguration.hmacSha256, key, restConfiguration.REST_TIME_LIMIT); } else { arguments.checkTime(restConfiguration.REST_TIME_LIMIT); } } else { // User set only for right access, not for signature check user = Configuration.configuration.getADMINNAME(); if (restConfiguration.REST_SIGNATURE) { arguments.checkBaseAuthent(restConfiguration.hmacSha256, null, restConfiguration.REST_TIME_LIMIT); } else { arguments.checkTime(restConfiguration.REST_TIME_LIMIT); } } getServerHandler().newSession(); R66Session session = getServerHandler().getSession(); if (!restConfiguration.REST_AUTHENTICATED) { // Default is Admin session.getAuth().specialNoSessionAuth(true, Configuration.configuration.getHOST_SSLID()); } else { // we have one DbSession per connection, only after authentication DbSession temp = getDbSessionFromUser().get(user); if (temp == null) { try { temp = new DbSession(DbConstant.admin, false); getDbSessionFromUser().put(user, temp); } catch (WaarpDatabaseNoConnectionException e) { } } if (temp != null) { temp.useConnection(); this.dbSession = temp; } try { session.getAuth().connectionHttps(getDbSession(), user, FilesystemBasedDigest.passwdCrypt(key.getBytes(WaarpStringUtils.UTF8))); } catch (Reply530Exception e) { status = HttpResponseStatus.UNAUTHORIZED; throw new HttpInvalidAuthenticationException("Wrong Authentication", e); } catch (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(ChannelHandlerContext ctx) throws Exception { super.channelInactive(ctx); getServerHandler().channelClosed(ctx); } /** * Called at the beginning of every new request * * Override if needed */ protected void initialize() { super.initialize(); } /** * Initialize the REST service (server side) for one restConfiguration * * @param restConfiguration */ public static void initializeService(RestConfiguration restConfiguration) { instantiateHandlers(restConfiguration); if (group == null) { group = Configuration.configuration.getHttpChannelGroup(); } // Configure the server. ServerBootstrap httpBootstrap = new ServerBootstrap(); WaarpNettyUtil.setServerBootstrap(httpBootstrap, Configuration.configuration.getHttpBossGroup(), Configuration.configuration.getHttpWorkerGroup(), (int) Configuration.configuration.getTIMEOUTCON()); // Set up the event pipeline factory. if (restConfiguration.REST_SSL) { httpBootstrap.childHandler(new HttpRestR66Initializer(false, Configuration.getWaarpSslContextFactory(), restConfiguration)); } else { httpBootstrap.childHandler(new HttpRestR66Initializer(false, null, restConfiguration)); } // Bind and start to accept incoming connections. ChannelFuture future; if (restConfiguration != null && restConfiguration.REST_ADDRESS != null && !restConfiguration.REST_ADDRESS.isEmpty()) { future = httpBootstrap.bind(new InetSocketAddress(restConfiguration.REST_ADDRESS, restConfiguration.REST_PORT)); } else { future = httpBootstrap.bind(new InetSocketAddress(restConfiguration.REST_PORT)); } try { future.await(); } catch (InterruptedException 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; } }