/** * Creates a new HTTP adapter instance. * * @return The new instance. */ @Bean(name = BEAN_NAME_VERTX_BASED_HTTP_PROTOCOL_ADAPTER) @Scope("prototype") public VertxBasedHttpProtocolAdapter vertxBasedHttpProtocolAdapter() { return new VertxBasedHttpProtocolAdapter(); }
@Override protected void addRoutes(final Router router) { if (getConfig().isAuthenticationRequired()) { final ChainAuthHandler authHandler = new HonoChainAuthHandler(); authHandler.append(new X509AuthHandler( Optional.ofNullable(clientCertAuthProvider).orElse( new X509AuthProvider(getCredentialsServiceClient(), getConfig())), getTenantServiceClient(), tracer)); authHandler.append(new HonoBasicAuthHandler( Optional.ofNullable(usernamePasswordAuthProvider).orElse( new UsernamePasswordAuthProvider(getCredentialsServiceClient(), getConfig())), getConfig().getRealm())); addTelemetryApiRoutes(router, authHandler); addEventApiRoutes(router, authHandler); addCommandResponseRoutes(router, authHandler); } else { LOG.warn("device authentication has been disabled"); LOG.warn("any device may publish data on behalf of all other devices"); addTelemetryApiRoutes(router, null); addEventApiRoutes(router, null); addCommandResponseRoutes(router, null); } }
void handlePostCommandResponse(final RoutingContext ctx) { if (Device.class.isInstance(ctx.user())) { final Device device = (Device) ctx.user(); uploadCommandResponseMessage(ctx, device.getTenantId(), device.getDeviceId(), getCommandRequestIdParam(ctx), getCommandResponseStatusParam(ctx)); } else { handle401(ctx); } }
private void addCommandResponseRoutes(final Router router, final Handler<RoutingContext> authHandler) { router.routeWithRegex("\\/control\\/res\\/[^\\/]+\\/[^\\/]+\\/.*").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.PUT) .allowedHeader(Constants.HEADER_COMMAND_RESPONSE_STATUS) .allowedHeader(HttpHeaders.CONTENT_TYPE.toString())); if (getConfig().isAuthenticationRequired()) { router.route("/control/res/*").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.POST) .allowedHeader(Constants.HEADER_COMMAND_RESPONSE_STATUS) HttpMethod.PUT, String.format("/control/res/:%s/:%s/:%s", PARAM_TENANT, PARAM_DEVICE_ID, PARAM_COMMAND_REQUEST_ID)) .handler(ctx -> uploadCommandResponseMessage(ctx, getTenantParam(ctx), getDeviceIdParam(ctx), getCommandRequestIdParam(ctx), getCommandResponseStatusParam(ctx)));
private void addEventApiRoutes(final Router router, final Handler<RoutingContext> authHandler) { // support CORS headers for PUTing events router.routeWithRegex("\\/event\\/[^\\/]+\\/.*").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.PUT) .allowedHeader(Constants.HEADER_TIME_TIL_DISCONNECT) .allowedHeader(HttpHeaders.AUTHORIZATION.toString()) .allowedHeader(HttpHeaders.CONTENT_TYPE.toString())); if (getConfig().isAuthenticationRequired()) { // support CORS headers for POSTing events router.route("/event").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.POST) .allowedHeader(Constants.HEADER_TIME_TIL_DISCONNECT) .allowedHeader(HttpHeaders.AUTHORIZATION.toString()) .allowedHeader(HttpHeaders.CONTENT_TYPE.toString())); // require auth for POSTing events router.route(HttpMethod.POST, "/event").handler(authHandler); // route for posting events using tenant and device ID determined as part of // device authentication router.route(HttpMethod.POST, "/event").handler(this::handlePostEvent); // require auth for PUTing events router.route(HttpMethod.PUT, "/event/*").handler(authHandler); // route for asserting that authenticated device's tenant matches tenant from path variables router.route(HttpMethod.PUT, String.format("/event/:%s/:%s", PARAM_TENANT, PARAM_DEVICE_ID)) .handler(this::assertTenant); } // route for sending event messages router.route(HttpMethod.PUT, String.format("/event/:%s/:%s", PARAM_TENANT, PARAM_DEVICE_ID)) .handler(ctx -> uploadEventMessage(ctx, getTenantParam(ctx), getDeviceIdParam(ctx))); }
private void addTelemetryApiRoutes(final Router router, final Handler<RoutingContext> authHandler) { router.routeWithRegex("\\/telemetry\\/[^\\/]+\\/.*").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.PUT) .allowedHeader(Constants.HEADER_QOS_LEVEL) .allowedHeader(HttpHeaders.CONTENT_TYPE.toString())); if (getConfig().isAuthenticationRequired()) { router.route("/telemetry").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.POST) .allowedHeader(Constants.HEADER_QOS_LEVEL) .handler(ctx -> uploadTelemetryMessage(ctx, getTenantParam(ctx), getDeviceIdParam(ctx)));
void assertTenant(final RoutingContext ctx) { if (Device.class.isInstance(ctx.user())) { final Device device = (Device) ctx.user(); if (device.getTenantId().equals(getTenantParam(ctx))) { ctx.next(); } else { ctx.fail(new ClientErrorException(HttpURLConnection.HTTP_FORBIDDEN, "not authorized to upload data for device from other tenant")); } } else { handle401(ctx); } } }
private void handle401(final RoutingContext ctx) { HttpUtils.unauthorized(ctx, "Basic realm=\"" + getConfig().getRealm() + "\""); }
private void addCommandResponseRoutes(final Router router, final Handler<RoutingContext> authHandler) { router.routeWithRegex("\\/control\\/res\\/[^\\/]+\\/[^\\/]+\\/.*").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.PUT) .allowedHeader(Constants.HEADER_COMMAND_RESPONSE_STATUS) .allowedHeader(HttpHeaders.CONTENT_TYPE.toString())); if (getConfig().isAuthenticationRequired()) { router.route("/control/res/*").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.POST) .allowedHeader(Constants.HEADER_COMMAND_RESPONSE_STATUS) HttpMethod.PUT, String.format("/control/res/:%s/:%s/:%s", PARAM_TENANT, PARAM_DEVICE_ID, PARAM_COMMAND_REQUEST_ID)) .handler(ctx -> uploadCommandResponseMessage(ctx, getTenantParam(ctx), getDeviceIdParam(ctx), getCommandRequestIdParam(ctx), getCommandResponseStatusParam(ctx)));
private void addEventApiRoutes(final Router router, final Handler<RoutingContext> authHandler) { router.routeWithRegex("\\/event\\/[^\\/]+\\/.*").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.PUT) .allowedHeader(Constants.HEADER_TIME_TIL_DISCONNECT) .exposedHeader(Constants.HEADER_COMMAND_REQUEST_ID)); if (getConfig().isAuthenticationRequired()) { router.route(ROUTE_EVENT_ENDPOINT).handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.POST) .allowedHeader(Constants.HEADER_TIME_TIL_DISCONNECT) .handler(ctx -> uploadEventMessage(ctx, getTenantParam(ctx), getDeviceIdParam(ctx)));
private void addTelemetryApiRoutes(final Router router, final Handler<RoutingContext> authHandler) { router.routeWithRegex("\\/telemetry\\/[^\\/]+\\/.*").handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.PUT) .allowedHeader(Constants.HEADER_QOS_LEVEL) .exposedHeader(Constants.HEADER_COMMAND_REQUEST_ID)); if (getConfig().isAuthenticationRequired()) { router.route(ROUTE_TELEMETRY_ENDPOINT).handler(CorsHandler.create(getConfig().getCorsAllowedOrigin()) .allowedMethod(HttpMethod.POST) .allowedHeader(Constants.HEADER_QOS_LEVEL) .handler(ctx -> uploadTelemetryMessage(ctx, getTenantParam(ctx), getDeviceIdParam(ctx)));
void assertTenant(final RoutingContext ctx) { if (Device.class.isInstance(ctx.user())) { final Device device = (Device) ctx.user(); if (device.getTenantId().equals(getTenantParam(ctx))) { ctx.next(); } else { ctx.fail(new ClientErrorException(HttpURLConnection.HTTP_FORBIDDEN, "not authorized to upload data for device from other tenant")); } } else { handle401(ctx); } } }
private void handle401(final RoutingContext ctx) { HttpUtils.unauthorized(ctx, "Basic realm=\"" + getConfig().getRealm() + "\""); }
@Override protected void addRoutes(final Router router) { if (getConfig().isAuthenticationRequired()) { final ChainAuthHandler authHandler = ChainAuthHandler.create(); authHandler.append(new X509AuthHandler( new TenantServiceBasedX509Authentication(getTenantServiceClient(), tracer), Optional.ofNullable(clientCertAuthProvider).orElse( new X509AuthProvider(getCredentialsServiceClient(), getConfig(), tracer)))); authHandler.append(new HonoBasicAuthHandler( Optional.ofNullable(usernamePasswordAuthProvider).orElse( new UsernamePasswordAuthProvider(getCredentialsServiceClient(), getConfig(), tracer)), getConfig().getRealm(), tracer)); addTelemetryApiRoutes(router, authHandler); addEventApiRoutes(router, authHandler); addCommandResponseRoutes(router, authHandler); } else { LOG.warn("device authentication has been disabled"); LOG.warn("any device may publish data on behalf of all other devices"); addTelemetryApiRoutes(router, null); addEventApiRoutes(router, null); addCommandResponseRoutes(router, null); } }
void handlePostCommandResponse(final RoutingContext ctx) { if (Device.class.isInstance(ctx.user())) { final Device device = (Device) ctx.user(); uploadCommandResponseMessage(ctx, device.getTenantId(), device.getDeviceId(), getCommandRequestIdParam(ctx), getCommandResponseStatusParam(ctx)); } else { handle401(ctx); } }
/** * Creates a new HTTP adapter instance. * * @return The new instance. */ @Bean(name = BEAN_NAME_VERTX_BASED_HTTP_PROTOCOL_ADAPTER) @Scope("prototype") public VertxBasedHttpProtocolAdapter vertxBasedHttpProtocolAdapter() { return new VertxBasedHttpProtocolAdapter(); }