diff --git a/3rd-party-libs/Simple-WebSocket-Server/server_ws.hpp b/3rd-party-libs/Simple-WebSocket-Server/server_ws.hpp index 2a74f9a..edc9f2f 100644 --- a/3rd-party-libs/Simple-WebSocket-Server/server_ws.hpp +++ b/3rd-party-libs/Simple-WebSocket-Server/server_ws.hpp @@ -3,7 +3,7 @@ #include "crypto.hpp" #include "utility.hpp" -#include "wschannel.hpp" +#include "WsChannel.hpp" #include #include @@ -109,7 +109,7 @@ namespace SimpleWeb { CaseInsensitiveMultimap header; - wschannel channel; + WsChannel channel; regex::smatch path_match; diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index cf411fe..894d39f 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 2.8) project(w3c-visserver) -set(UNIT_TEST OFF CACHE STRING "Build unit tests") +set(UNIT_TEST ON CACHE STRING "Build unit tests") # Set this variable to ON build an exe set(BUILD_EXE ON) @@ -29,7 +29,6 @@ if(UNIT_TEST) add_definitions(-DUNIT_TEST) endif(UNIT_TEST) -add_definitions(-DDEBUG) add_compile_options(-std=c++11 -pthread -Wall -Wextra -Werror) if ("${ADDRESS_SAN}" STREQUAL "ON" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") @@ -61,7 +60,7 @@ set(Boost_USE_STATIC_LIBS ON) include_directories(${Boost_INCLUDE_DIRS}) message(STATUS " boost includes ${Boost_INCLUDE_DIRS} ") -find_package(Boost 1.64.0 COMPONENTS system thread program_options REQUIRED) +find_package(Boost 1.67.0 COMPONENTS system thread program_options REQUIRED) target_link_libraries(simple-websocket-server INTERFACE ${Boost_LIBRARIES}) target_include_directories(simple-websocket-server INTERFACE ${Boost_INCLUDE_DIR}) @@ -107,15 +106,17 @@ target_link_libraries(${PROJECT_NAME} -lgobject-2.0 -lglib-2.0 -lgio-2.0) if(UNIT_TEST) set(TEST_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/src/vssdatabase.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/vsscommandprocessor.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/accesschecker.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/authenticator.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/subscriptionhandler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/signing.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/wsserver.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/VssDatabase.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/VssCommandProcessor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/AccessChecker.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Authenticator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/SubscriptionHandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/SigningHandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/WsServer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/BasicLogger.cpp - # ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/vssdatabase_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/permmclient.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/JsonResponses.cpp + # ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/VssDatabase_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/w3cunittest.cpp ) add_executable(w3c-unit-test ${TEST_FILES}) @@ -140,12 +141,12 @@ target_include_directories(w3c-unit-test INTERFACE ${OPENSSL_INCLUDE_DIR}) target_link_libraries(w3c-unit-test INTERFACE ${OPENSSL_LIBRARIES}) endif(UNIT_TEST) - - if(BUILD_TEST_CLIENT) add_executable(testclient ${CMAKE_CURRENT_SOURCE_DIR}/test/testclient.cpp) target_link_libraries(testclient simple-websocket-server) target_include_directories(testclient PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/jsoncons) + target_include_directories(testclient INTERFACE ${Boost_INCLUDE_DIR}) + target_link_libraries(testclient ${Boost_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../examples/demo-certificates/Client.pem ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../examples/demo-certificates/Client.key ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../examples/demo-certificates/CA.pem ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) diff --git a/w3c-visserver-api/README.md b/w3c-visserver-api/README.md index 7b76c04..f6a605d 100644 --- a/w3c-visserver-api/README.md +++ b/w3c-visserver-api/README.md @@ -2,29 +2,93 @@ The implementation is based on the [W3C Vehicle Information Service Specification](https://www.w3.org/TR/2018/CR-vehicle-information-service-20180213/) +This implementation can also provide additional functionality not (yet) available in (draft) standard documents. -The implementation provides all the major funtionality defined in the above specification and also uses JWT Token for permissions handling with decent amount of uni-tests covering all the basic funtions. This project uses components from other open source projects namely +The implementation provides all the major functionality defined in the above standard specification. and also uses JWT Token for permissions handling with decent amount of unit-tests covering all the basic funtions. -1. [Simple-WebSocket-Server](https://gitlab.com/eidheim/Simple-WebSocket-Server) which is under MIT license. -2. [jsoncons](https://github.com/danielaparker/jsoncons) which is under Boost Software license. -3. [jwt-cpp](https://github.com/Thalhammer/jwt-cpp)which is under MIT license. +## Features + - Multi-client server implementing Web-Socket platform communication, with support for both secure [SSL] and insecure [plain] connections. Feature status: + - User authorization based on industry standard RFC 7519 as JSON Web Tokens + - Optional JSON signing of messages, described in **_JSON signing_** chapter + - Multi-client server implementing experimental REST API based on standard specification. REST API specification is available as OpenAPI 3.0 definition available in [rest-api.yaml](doc/rest-api.yaml) file. + Specific list of of features is listed in table below: -# How to build -w3c-visserver can be built as a library which could be used in another application. Eg: could be found in the vehicle2cloud app. + | Feature | Status | + | ------------- | ------------- | + | GET/SET | :heavy_check_mark:| + | PUB/SUB | :heavy_check_mark: | + | GETMETA | :heavy_check_mark: | + | Secure Web-Socket | :heavy_check_mark: | + | Authentification | :heavy_check_mark: | + | JSON signing | :heavy_check_mark: | + | REST API | :heavy_check_mark: | + +## Dependencies + +This project uses components from other 3rd party open-source projects: + +| Library | License | Description | +| ------------- | ------------- | ----------- | + [Boost.Beast](https://www.boost.org/doc/libs/1_67_0/libs/beast/doc/html/index.html) | Boost Software license 1.0 | Foundation library for simplified handling of various Web-Socket, HTTP or other protocols with and without security, based on Boost.Asio. + [Simple-WebSocket-Server](https://gitlab.com/eidheim/Simple-WebSocket-Server) _[deprecated]_ | MIT license | Simple implementation of Web-Socket server. + [jsoncons](https://github.com/danielaparker/jsoncons) | Boost Software license 1.0 | Utility library for handling JSON. + [jwt-cpp](https://github.com/Thalhammer/jwt-cpp) | MIT license | Utility library for handling JWT tokens. + +# Building W3C-Server + +[CMake](https://cmake.org/) is tool used to configure, build and package W3C-Server. + +## Configure build + +Build configuration options of W3C-Server are defined in CMakeLists.txt file. + +By changing different option values in CMakeLists.txt file, user can control +different build options of W3C-Server. +Available build options with optional parameters, if available, are presented below. Default parameters are shown in **bold**: + - **BUILD_EXE** [**ON**/OFF] - Default build shall produce W3C-Server executable. + If set to **OFF** W3C-Server shall be built as a library which could be used in another application. + Eg: could be found in the _vehicle2cloud_ app. + - **BUILD_TEST_CLIENT** [**ON**/OFF] - Build separate _testclient_ executable. Test client is a utility to test + Web-Socket request interface of W3C-Server and retrieve responses. + - **UNIT_TEST** [ON/**OFF**] - If enabled, build shall produce separate _w3c-unit-test_ executable which + will run existing tests for server implementation. + - **ADDRESS_SAN** [ON/**OFF**] - If enabled and _Clang_ is used as compiler, _AddressSanitizer_ will be used to build + W3C-Server for verifying run-time execution. + +After changing any of build options, new clean build should be made, as described in **_Building W3C-Server_** chapter. + +## Building W3C-Server + +To generate new clean build (e.g. after git clone or after changing build configuration options), use standard CMake build order as shown below: + + - Go to W3C-Server directory ``` cd w3c-visserver-api +``` + - Make default build directory where build artifacts will be stored, and move into it +``` mkdir build cd build +``` + - Invoke CMake pointing to location of CMakeLists.txt file to generate + Makefile build configuration which will be used to build W3C-Server +``` cmake .. -make ``` + - Run build W3C-Server. Make parameter '_-j_ ' is optional and allows running parallel build jobs to speed up compilation. +``` +make -j +``` +If all completes successfully, build artifacts shall be located in 'build' directory. + +### JSON signing JSON Signing has been introduced additionally to sign the JSON response for GET and SUBSCRIBE Response. By default this has been disabled. To enable this feature go to visconf.hpp file and uncomment the line `define JSON_SIGNING_ON`. Please note, JSON signing works only with a valid pair of public / private certificate. For testing, you could create example certificates by following the below steps. Do not add any passphrase when asked for. ``` -ssh-keygen -t rsa -b 4096 -m PEM -f signing.private.key +ssh-keygen -t rsa -b 4096 -m PEM -f signing.private.key openssl rsa -in signing.private.key -pubout -outform PEM -out signing.public.key ``` @@ -34,17 +98,71 @@ The client also needs to validate the signed JSON using the public certificate w This could also be easily extended to support JSON signing for the requests as well with very little effort. +# Running W3C-Server -# How to run -This application needs the input vss data to create the tree structure. The input file can be taken from https://github.com/GENIVI/vehicle_signal_specification/blob/master/vss_rel_1.0.json. Clone the files and place them in the build folder where the executables are built. Keep the names of the files the same. -Add the files to the location where the application executable is run from. +Depending on build options and provided parameters, W3C-Server will provide different features. +Chapter **_Parameters_** shall describe different mandatory and optional parameters in more detail. +Default configuration shall provide both Web-Socket and REST API connectivity. -# How tos +## Web-Socket specific testing + +This covers only the basic functions like get, set and getmetadata requests. You coulkd skip this and take a look at the unit-test to get better idea about the implementation. + +You could also checkout the in-vehicle apps in the [kuksa.apps](https://github.com/eclipse/kuksa.apps) repo which work with the server. + +Now the apps are ready for testing. Run w3c-visserver using `./w3c-visserver` command and then in a separate terminal start testclient using `./testclient`. + +Testclient should connect to the w3c-visserver and promt a message as below +![Alt text](./doc/pictures/test1.png?raw=true "test1") -Demo Certificates are available in the examples/demo-certificates folder and these certs are automatically copied on building the apps and api. In case you need to create new certs follow the steps below, otherwise skip the steps below. +Authenticate with the server using the JWT token +![Alt text](./doc/pictures/test4.png?raw=true "test4") + +Enter the vss path and function as set and a dummy integer value. +![Alt text](./doc/pictures/test2.png?raw=true "test2") + +Enter the same vss path as above and fuction as get. You should receive the previously set value in the JSON response. +![Alt text](./doc/pictures/test3.png?raw=true "test3") + +## REST API specific testing + +There is number of options to exercise REST API. + +_**NOTE:** If using SSL connections and self-signed certificates, make sure that 'CA.pem' or corresponding file to generated certificates is imported into browser (or other tool) used for testing. Also user can try to disable certificate verification. +Reason for this is that browsers automatically try to verify validity of server certificates, so secured connection shall fail with default configuration._ + +Similar to above mentioned testclient, there is available [client test page](./test/web-client/index.html) in git repo to aid testing. +Test page support custom GET, PUT and POST HTTP requests to W3C-Server. Additional benefit is that it can automatically generate JWT token based on input token value and provided Client key which is used in authorization. Note that if users changes Client key, user must also update 'jwt.pub.key' with corresponding public key. + +Additional tool which is quite useful is [Swagger](https://editor.swagger.io). It is a dual-use tool which allows for writing OpenAPI specifications, but also generates runnable REST API samples for moslient test endpoints. +Open Swagger editor and import our REST API [definition](./doc/rest-api.yaml) file. Swagger shall generate HTML page with API documentation. When one of the endpoints is selected, 'try' button appears which allows for making REST requests directly to running W3C-Server. + +## Parameters +Below are presented W3C-Server parameters available for user control: +- **--help** - Show W3C-Server usage and exit +- **--vss** [mandatory] - Path to VSS data file describing VSS data tree structure which W3C-Server shall handle. Sample 'vss_rel_2.0.json' file can be found [here](./unit-test/vss_rel_2.0.json). +- **--config-file** [optional] - Path to configuration file with W3C-Server input parameters. + Configuration file can replace command-line parameters and through different files multiple configurations can be handled more easily (e.g. test and production setup). + Sample of configuration file parameters is shown below: + ``` + vss=vss_rel_2.0.json + cert-path=. + insecure= + log-level=ALL + ``` +- **--cert-path** [mandatory] - Directory path where 'Server.pem', 'Server.key' and 'jwt.pub.key' are located. Server demo certificates are located in [this](https://github.com/eclipse/kuksa.invehicle/tree/master/examples/demo-certificates) directory of git repo. Certificates from 'demo-certificates' are automatically copied to build directory, so invoking '_--cert-path=._' should be enough when demo certificates are used. +If user needs to use or generate their own certificates, see chapter **_Certificates_** for more details. +For authorizing client, file 'jwt.pub.key' contains public key used to verify that JWT authorization token is valid. To generated different 'jwt.pub.key' file, see chapter **_Permissions_** for more details. +- **--insecure** [optional] - By default, W3C-Server shall accept only SSL (TLS) secured connections. If provided, W3C-Server shall also accept plain un-secured connections for Web-Socket and REST API connections, and also shall not fail connections due to self-signed certificates. +- **--wss-server** [optional][deprecated] - By default, W3C-Server uses Boost.Beast as default connection handler. If provided, W3C-Server shall use deprecated Simple Web-Socket Server, without REST API support. +- **--address** [optional] - If provided, W3C-Server shall use different server address than default _'localhost'_. +- **--port** [optional] - If provided, W3C-Server shall use different server port than default '8090' value. +- **--log-level** [optional] - Enable selected log level value. To allow for different log level combinations, parameter can be provided multiple times with different log level values. + +# How tos -### Create PKI certificates +## Certificates Go to examples/demo-certificates folder. Make changes in the openssl.cnf file regarding the Company name and the allowed IPs and DNS server names. Make sure you also add the IPs and DNS to v3.ext file as well. @@ -59,110 +177,47 @@ Go to examples/demo-certificates folder. Make changes in the openssl.cnf file re Steps were taken from [here]( https://kb.op5.com/pages/viewpage.action?pageId=19073746#sthash.GHsaFkZe.WDGgcOja.dpbs) & [here](https://stackoverflow.com/questions/18233835/creating-an-x509-v3-user-certificate-by-signing-csr). -### Build W3C-Server - -Now enable `BUILD_EXE` and `BUILD_TEST_CLIENT` flags by changing to ON in w3cvisserver/CMakeLists.txt. - -Now build using the commands in How to build section. - -Once the apps are built, copy the server.crt and server.key files to the `w3c-visserver/build` folder. Also copy the https://github.com/GENIVI/vehicle_signal_specification/blob/master/vss_rel_1.0.json files into the `w3c-visserver/build` folder. - -In this case the server and the client are built on the same folder, hence copy the generate Server.pem, Server.key ,CA.key, Client.key and Client.pem into `w3c-visserver/build` folder. +## Permissions -#### Permissions +The W3C-Server needs authorization JWT Token to allow access to server side resources. You can create a dummy JWT Token from https://jwt.io/. Use the RSA256 algorithm from the drop down and enter valid "iat" and "exp" data and set "iss : kuksa" and generate a JWT. Once the JWT is generated on the left side. Copy the Public key from the Text box on the right side to a file and rename the field to jwt.pub.key and copy the file to `w3c-visserver/build` folder. Also store the JWT token somewhere so that you could pass the Token to the server for authentication. -The w3c-visserver needs authentification Token to allow access to server side resources. You can create a dummy JWT Token from https://jwt.io/. Use the RSA256 algorithm from the drop down and enter valid "iat" and "exp" data and set "iss : kuksa" and generate a JWT. Once the JWT is generated on the left side. Copy the Public key from the Text box on the right side to a file and rename the field to jwt.pub.key and copy the file to `w3c-visserver/build` folder. Also store the JWT token somewhere so that you could pass the Token to the server for authentication. - -![Alt text](./pictures/jwt.png?raw=true "jwt") +![Alt text](./doc/pictures/jwt.png?raw=true "jwt") Permissions can be granted by modifying the JSON Claims. 1. The JWT Token should contain a "w3c-vss" claim. 2. Under the "w3c-vss" claim the permissions can be granted using key value pair. The key should be the path in the signal tree and the value should be strings with "r" for READ-ONLY, "w" for WRITE-ONLY and "rw" or "wr" for READ-AND-WRITE permission. See the image above. -3. The permissions can contain wild-cards. For eg "Signal.OBD.*" : "rw" will grant READ-WRITE access to all the signals under Signal.OBD. +3. The permissions can contain wild-cards. For eg "Signal.OBD.\*" : "rw" will grant READ-WRITE access to all the signals under Signal.OBD. 4. The permissions can be granted to a branch. For eg "Signal.Vehicle" : "rw" will grant READ-WRITE access to all the signals under Signal.Vehicle branch. -### Test Run - -This covers only the basic functions like get, set and getmetadata requests. You coulkd skip this and take a look at the unit-test to getter idea about the implementation. - -You could also checkout the in-vehicle apps in the [kuksa.apps](https://github.com/eclipse/kuksa.apps) repo which work with the server. - -Now the apps are ready for testing. Run w3c-visserver using `./w3c-visserver` command and then in a separate terminal start testclient using `./testclient`. - -Testclient should connect to the w3c-visserver and promt a message as below -![Alt text](./pictures/test1.png?raw=true "test1") - -Authenticate with the server using the JWT token -![Alt text](./pictures/test4.png?raw=true "test4") - -Enter the vss path and function as set and a dummy integer value. -![Alt text](./pictures/test2.png?raw=true "test2") - -Enter the same vss path as above and fuction as get. You should receive the previously set value in the JSON response. -![Alt text](./pictures/test3.png?raw=true "test3") - - - - - -### JSON Signing - -JSON Signing has been introduced additionally to sign the JSON response for GET and SUBSCRIBE Response. By default this has been disabled. To enable this feature go to visconf.hpp file and uncomment the line `define JSON_SIGNING_ON`. Please note, JSON signing works only with a valid pair of public / private certificate. For testing, you could create example certificates by following the below steps. -Do not add any passphrase when asked for. - -``` -ssh-keygen -t rsa -b 4096 -m PEM -f signing.private.key -openssl rsa -in signing.private.key -pubout -outform PEM -out signing.public.key -``` - -Copy the files signing.private.key & signing.public.key to the build directory. - -The client also needs to validate the signed JSON using the public certificate when JSON signing is enabled in server. - -This could also be easily extended to support JSON signing for the requests as well with very little effort. - - -### D-BUS Backend Connection +## D-BUS Backend Connection The server also has d-bus connection, which could be used to feed the server with data from various feeders. The W3C Sever exposes the below methods and these methods could be used (as methodcall) to fill the server with data. - - `` - - - - - - - - - - - - - - - - - - - - - `` - - - -# Implementation - -| Feature | Status | -| ------------- | ------------- | -| GET/SET | :heavy_check_mark:| -| PUB/SUB | :heavy_check_mark: | -| GETMETA | :heavy_check_mark: | -| Secure WebSocket | :heavy_check_mark: | -| Authentification | :heavy_check_mark: | -| JSON signing | :heavy_check_mark: | + ``` + + + + + + + + + + + + + + + + + + + + + + + ``` ## Running on AGL on Raspberry Pi 3 @@ -170,11 +225,9 @@ The W3C Sever exposes the below methods and these methods could be used (as meth * Burn the image on to an SD card and boot the image on a Raspi 3. * w3c-visserver is deployed as a systemd service `w3c-visserver.service` which opens a secure websocket connection on port 8090. - -#### On first launch +### On first launch * ssh into the raspi 3 with root. * Go to `/usr/bin/w3c-visserver` using the ssh connection. * copy the vss data file https://github.com/GENIVI/vehicle_signal_specification/blob/master/vss_rel_1.0.json into `./usr/bin/w3c-visserver`. By default the AGL build will contain demo cerificates that work with other apps in the repo. You could create your own cerificates and tokens using the instaructions above. * Reboot the raspi 3 - diff --git a/w3c-visserver-api/pictures/jwt.png b/w3c-visserver-api/doc/pictures/jwt.png similarity index 100% rename from w3c-visserver-api/pictures/jwt.png rename to w3c-visserver-api/doc/pictures/jwt.png diff --git a/w3c-visserver-api/pictures/test1.png b/w3c-visserver-api/doc/pictures/test1.png similarity index 100% rename from w3c-visserver-api/pictures/test1.png rename to w3c-visserver-api/doc/pictures/test1.png diff --git a/w3c-visserver-api/pictures/test2.png b/w3c-visserver-api/doc/pictures/test2.png similarity index 100% rename from w3c-visserver-api/pictures/test2.png rename to w3c-visserver-api/doc/pictures/test2.png diff --git a/w3c-visserver-api/pictures/test3.png b/w3c-visserver-api/doc/pictures/test3.png similarity index 100% rename from w3c-visserver-api/pictures/test3.png rename to w3c-visserver-api/doc/pictures/test3.png diff --git a/w3c-visserver-api/pictures/test4.png b/w3c-visserver-api/doc/pictures/test4.png similarity index 100% rename from w3c-visserver-api/pictures/test4.png rename to w3c-visserver-api/doc/pictures/test4.png diff --git a/w3c-visserver-api/doc/rest-api.yaml b/w3c-visserver-api/doc/rest-api.yaml new file mode 100644 index 0000000..be5ddf7 --- /dev/null +++ b/w3c-visserver-api/doc/rest-api.yaml @@ -0,0 +1,453 @@ +openapi: '3.0.1' +info: + title: W3C Vehicle Information Specification REST API documentation + version: 0.0.1 + description: Documentation for initial implementation of REST API for accessing VIS information + based on Web-Sockets definition.

+ Implemented REST API re-use defined target paths for both requests and responses.

+ Current REST API definition do not support request with passing data in HTTP body of requests + due to current simple use-cases. + But transferring request data through request HTTP body could be investigated as a means of more + complex GET/SET requests; e.g. retrieve/update multiple signals in single request by + providing JSON array of signals, more complex query support of signal states/values, etc...

+ REST API supports both existing Web-socket signal path format with '.' as path separator, and more + 'REST-like' '/' path separator.

+ Due to REST API design supporting by default only client -> server flow, support for + 'subscribe' and 'unsubscribe' requests is intentionally ommitted. + It should be technically possible to add subscribe functionality, but this is currently out-of-scope + and could be discussion point in future.

+ This API definition proposes explicit API path and API version information through base URL. + This allows us to cleanly support different versions of API and|or W3C VIS standard. Aditional REST API + endpoint could be added to support providing information about versions' support of server + (through e.g. /vss/api/versions endpoint), but this is currently out-of-scope and could be + discussion point in future. + + contact: + email: kuksa-dev@eclipse.org + url: 'https://www.eclipse.org/kuksa/' + license: + name: Eclipse Public License v2.0 + url: 'https://www.eclipse.org/legal/epl-2.0/' + termsOfService: 'https://www.eclipse.org/legal/termsofuse.php' +servers: + - url: http://localhost:8090/vss/api/v1/ + - url: https://localhost:8090/vss/api/v1/ +tags: +- name: signals + description: "Acces specific signal or signal branch information" +- name: metadata + description: "Access metadata information" +- name: authorize + description: "Authorize user access" + +components: + parameters: + + signalBranchPathWithDots: + name: signalBranchPathWithDots + in: path + description: String defining branch structure, where branch items are separated by '.' character. + schema: + type: string + pattern: '^[A-Za-z0-9\.]+$' + required: true + example: Vehicle.Drivetrain.Transmission + + signalBranchPathWithSlashes: + name: signalBranchPathWithSlashes + in: path + description: String defining branch structure, where branch items are separated by '/' character. + schema: + type: string + pattern: '^[A-Za-z0-9/]+$' + allowReserved: true + required: true + example: Vehicle/Drivetrain/Transmission + + signalId: + name: signalId + in: path + description: Signal identifier + schema: + type: string + pattern: '^[A-Za-z0-9]+$' + required: true + example: "TravelledDistance" + + token: + name: token + in: query + description: JWT token string in 'base64url' format + required: true + schema: + type: string + format: base64url + + signalValue: + name: value + in: query + description: Signal value + required: true + schema: + type: integer + example: 123 + + responses: + + Success: + description: "Request completed successfully" + content: + application/json: + schema: + $ref: '#/components/schemas/SuccessResponse' + + SuccessAuthorize: + description: "Authorize request completed successfully" + content: + application/json: + schema: + $ref: '#/components/schemas/AuthSuccessResponse' + + BadRequestRsp: + description: "The specified resource was not found" + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestContent' + + UnauthorizedRsp: + description: "Provided JWT token could not be authorized" + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedContent' + + ForbiddenRsp: + description: "Access to resource is forbidden" + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenContent' + + NotFoundRsp: + description: "Requested resource not found" + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundContent' + + schemas: + + Action: + type: string + enum: ['get', 'set', 'getMetadata', 'authorize'] + description: Action request + + Timestamp: + type: integer + description: Timestamp of event + example: 615154112 + + RequestId: + type: integer + description: Unique request identifier + example: 817151 + + SuccessResponse: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + path: + type: string + description: Requested resource path + example: "Vehicle.Drivetrain.Transmission.TravelledDistance" + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' + value: + type: object + description: Response content + + AuthSuccessResponse: + type: object + properties: + TTL: + type: integer + description: Token expiration time + example: 123456 + action: + type: string + example: authorize + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' + + ErrorDesc: + type: object + properties: + number: + type: integer + description: "HTML status code" + example: 400, 401, 403, 404 + reason: + type: string + description: "Error reason" + message: + type: string + description: "Error description" + + BadRequestContent: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + error: + $ref: '#/components/schemas/ErrorDesc' + timestamp: + $ref: '#/components/schemas/Timestamp' + + UnauthorizedContent: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + error: + $ref: '#/components/schemas/ErrorDesc' + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' + + ForbiddenContent: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + error: + $ref: '#/components/schemas/ErrorDesc' + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' + + NotFoundContent: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + error: + $ref: '#/components/schemas/ErrorDesc' + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' +paths: + /signals/{signalBranchPathWithDots}: + get: + tags: + - signals + summary: "Get signal branch information" + description: + Get information for specific signal branch. Branch items are separated by '.' character.
+ Response can contain array of other branches and|or signals. + operationId: getSignalBranchDataWithDots + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' + + /signals/{signalBranchPathWithSlashes}: + get: + tags: + - signals + summary: "Get signal branch information" + description: + Get information for specific signal branch. Branch items are separated by '/' character.
+ Response can contain array of other branches and|or signals. + operationId: getSignalBranchDataWithSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' + + /signals/{signalBranchPathWithDots}.{signalId}: + get: + tags: + - signals + summary: "Get specific signal information" + description: Get complete information for single specific signal + operationId: "getSingleSignalInformationDots" + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + - $ref: '#/components/parameters/signalId' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' + + put: + tags: + - signals + summary: "Set specific signal value" + description: Set value for single specific signal + operationId: "putSingleSignalInformationDots" + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + - $ref: '#/components/parameters/signalId' + - $ref: '#/components/parameters/signalValue' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' + + /signals/{signalBranchPathWithSlashes}/{signalId}: + get: + tags: + - signals + summary: "Get single signal information" + description: Get complete information for single specific signal + operationId: getSingleSignalInformationSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + - $ref: '#/components/parameters/signalId' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' + + put: + tags: + - signals + summary: "Set single signal information" + description: Set value for single specific signal + operationId: putSingleSignalInformationSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + - $ref: '#/components/parameters/signalId' + - $ref: '#/components/parameters/signalValue' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' + + /metadata/{signalBranchPathWithDots}: + get: + tags: + - metadata + summary: "Get metadata for specified branch" + description: + Get metadata information for specific signal branch. Branch items are separated by '.' character.
+ Response can contain array of other branches and|or signals. + operationId: getSignalBranchMetadataWithDots + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /metadata/{signalBranchPathWithSlashes}: + get: + tags: + - metadata + summary: "Get metadata for specified branch" + description: + Get metadata information for specific signal branch. Branch items are separated by '/' character.
+ Response can contain array of other branches and|or signals. + operationId: getSignalBranchMetadataWithSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /metadata/{signalBranchPathWithDots}.{signalId}: + get: + tags: + - metadata + summary: "Get metadata for specific signal" + description: Get metadata information for single specific signal + operationId: getSingleSignalMetadataInformationDots + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + - $ref: '#/components/parameters/signalId' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /metadata/{signalBranchPathWithSlashes}/{signalId}: + get: + tags: + - metadata + summary: "Get metadata for specific signal" + description: Get metadata information for single specific signal + operationId: getSingleSignalMetadataInformationSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + - $ref: '#/components/parameters/signalId' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /authorize: + post: + tags: + - authorize + summary: "Authorize client" + description: + Authorize client with JWT token.
+ Check jwt.io for more details on how JWT is generated. + operationId: postAuthorizeClient + parameters: + - $ref: '#/components/parameters/token' + responses: + '200': + $ref: '#/components/responses/SuccessAuthorize' + '400': + $ref: '#/components/responses/BadRequestRsp' + '401': + $ref: '#/components/responses/UnauthorizedRsp' diff --git a/w3c-visserver-api/include/accesschecker.hpp b/w3c-visserver-api/include/AccessChecker.hpp similarity index 59% rename from w3c-visserver-api/include/accesschecker.hpp rename to w3c-visserver-api/include/AccessChecker.hpp index c377e75..4478e87 100644 --- a/w3c-visserver-api/include/accesschecker.hpp +++ b/w3c-visserver-api/include/AccessChecker.hpp @@ -14,24 +14,20 @@ #ifndef __ACCESSCHECKER_H__ #define __ACCESSCHECKER_H__ -#include -#include -#include "authenticator.hpp" -#include "wschannel.hpp" +#include "IAccessChecker.hpp" -using namespace std; -using namespace jsoncons; -using jsoncons::json; +class IAuthenticator; -class accesschecker { + +class AccessChecker : public IAccessChecker { private: - authenticator *tokenValidator; + std::shared_ptr tokenValidator; public: - accesschecker(authenticator *vdator); - bool checkReadAccess(wschannel &channel, string path); - bool checkWriteAccess(wschannel &channel, string path); - bool checkPathWriteAccess(wschannel &channel, json paths); + AccessChecker(std::shared_ptr vdator); + bool checkReadAccess(WsChannel &channel, const std::string &path); + bool checkWriteAccess(WsChannel &channel, const std::string &path); + bool checkPathWriteAccess(WsChannel &channel, const jsoncons::json &paths); }; #endif diff --git a/w3c-visserver-api/include/authenticator.hpp b/w3c-visserver-api/include/Authenticator.hpp similarity index 66% rename from w3c-visserver-api/include/authenticator.hpp rename to w3c-visserver-api/include/Authenticator.hpp index 1c3b8b3..89792d3 100644 --- a/w3c-visserver-api/include/authenticator.hpp +++ b/w3c-visserver-api/include/Authenticator.hpp @@ -17,27 +17,31 @@ #include #include +#include "IAuthenticator.hpp" + using namespace std; -class wschannel; -class vssdatabase; +class WsChannel; +class IVssDatabase; class ILogger; -class authenticator { +class Authenticator : public IAuthenticator { private: string pubkey = "secret"; string algorithm = "RS256"; std::shared_ptr logger; - int validateToken(wschannel& channel, string authToken); + int validateToken(WsChannel& channel, string authToken); public: - authenticator(std::shared_ptr loggerUtil, string secretkey, string algorithm); - int validate(wschannel &channel, vssdatabase *database, + Authenticator(std::shared_ptr loggerUtil, string secretkey, string algorithm); + + int validate(WsChannel &channel, + std::shared_ptr dn, string authToken); - + void updatePubKey(string key); - bool isStillValid(wschannel &channel); - void resolvePermissions(wschannel &channel, vssdatabase *database); + bool isStillValid(WsChannel &channel); + void resolvePermissions(WsChannel &channel, std::shared_ptr database); }; #endif diff --git a/w3c-visserver-api/include/IAccessChecker.hpp b/w3c-visserver-api/include/IAccessChecker.hpp new file mode 100644 index 0000000..e5ec827 --- /dev/null +++ b/w3c-visserver-api/include/IAccessChecker.hpp @@ -0,0 +1,31 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IACCESSCHECKER_H__ +#define __IACCESSCHECKER_H__ + +#include +#include + +class WsChannel; + +class IAccessChecker { +public: + virtual ~IAccessChecker() {} + + virtual bool checkReadAccess(WsChannel &channel, const std::string &path) = 0; + virtual bool checkWriteAccess(WsChannel &channel, const std::string &path) = 0; + virtual bool checkPathWriteAccess(WsChannel &channel, const jsoncons::json &paths) = 0; +}; + +#endif diff --git a/w3c-visserver-api/include/IAuthenticator.hpp b/w3c-visserver-api/include/IAuthenticator.hpp new file mode 100644 index 0000000..73896df --- /dev/null +++ b/w3c-visserver-api/include/IAuthenticator.hpp @@ -0,0 +1,38 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IAUTHENTICATOR_H__ +#define __IAUTHENTICATOR_H__ + +#include +#include + +using namespace std; + +class WsChannel; +class IVssDatabase; +class ILogger; + +class IAuthenticator { +public: + virtual ~IAuthenticator() {} + + virtual int validate(WsChannel &channel, + std::shared_ptr database, + string authToken) = 0; + virtual void updatePubKey(string key) = 0; + virtual bool isStillValid(WsChannel &channel) = 0; + virtual void resolvePermissions(WsChannel &channel, std::shared_ptr database) = 0; +}; + +#endif diff --git a/w3c-visserver-api/include/IRestHandler.hpp b/w3c-visserver-api/include/IRestHandler.hpp new file mode 100644 index 0000000..7cccb1e --- /dev/null +++ b/w3c-visserver-api/include/IRestHandler.hpp @@ -0,0 +1,49 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IRESTHANDLER_H__ +#define __IRESTHANDLER_H__ + +#include + +/** + * @class IRestHandler + * @brief Interface class for handling REST API and convert them to default JSON + * + * @note Any class implementing this interface shall output JSON requests as defined by + * https://www.w3.org/TR/vehicle-information-service/#message-structure + */ +class IRestHandler { + public: + virtual ~IRestHandler() = default; + + /** + * @brief Get JSON request string based on input REST API request + * @param restMethod REST request method string (get/set/...) + * @param restTarget REST request URI + * @param resultJson Output JSON string for both valid and invalid REST requests. If invalid REST + * request, parameter shall contain JSON describing error + * @return true if REST request correct and JSON request generated + * false otherwise indicating error in REST request. \a jsonRequest shall be updated + * with JSON response providing error details + * + * @note Function can be updated/extended/overloaded to provide additional relevant information, + * (e.g. HTTP header information, body of request with JSON, etc...) + */ + virtual bool GetJson(std::string&& restMethod, + std::string&& restTarget, + std::string& resultJson) = 0; +}; + + +#endif diff --git a/w3c-visserver-api/include/IServer.hpp b/w3c-visserver-api/include/IServer.hpp new file mode 100644 index 0000000..b484a3c --- /dev/null +++ b/w3c-visserver-api/include/IServer.hpp @@ -0,0 +1,41 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ + +#ifndef __IWSSERVER_H__ +#define __IWSSERVER_H__ + +#include +#include + +class IVssCommandProcessor; + +/** + * \class ObserverType + * \brief Server traffic types which can be observed + */ +enum class ObserverType { + WEBSOCKET = 0x01, //!< Receive Web-Socket traffic data + HTTP = 0x02, //!< Receive HTTP traffic data + ALL = 0x03 //!< Receive all traffic +}; + +class IServer { + public: + virtual ~IServer() {} + + virtual void AddListener(ObserverType, std::shared_ptr) = 0; + virtual void RemoveListener(ObserverType, std::shared_ptr) = 0; + virtual void SendToConnection(uint64_t connID, const std::string &message) = 0; +}; +#endif diff --git a/w3c-visserver-api/include/ISigningHandler.hpp b/w3c-visserver-api/include/ISigningHandler.hpp new file mode 100644 index 0000000..d9d77e0 --- /dev/null +++ b/w3c-visserver-api/include/ISigningHandler.hpp @@ -0,0 +1,34 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __ISIGNING_H__ +#define __ISIGNING_H__ + +#include +#include + +class ISigningHandler { + public: + virtual ~ISigningHandler() {} + + virtual std::string getKey(const std::string &fileName) = 0; + virtual std::string getPublicKey(const std::string &fileName) = 0; + virtual std::string sign(const jsoncons::json &data) = 0; + virtual std::string sign(const std::string &data) = 0; + +#ifdef UNIT_TEST + virtual std::string decode(std::string signedData) = 0; +#endif +}; + +#endif diff --git a/w3c-visserver-api/include/ISubscriptionHandler.hpp b/w3c-visserver-api/include/ISubscriptionHandler.hpp new file mode 100644 index 0000000..03d2107 --- /dev/null +++ b/w3c-visserver-api/include/ISubscriptionHandler.hpp @@ -0,0 +1,46 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __ISUBSCRIPTIONHANDLER_H__ +#define __ISUBSCRIPTIONHANDLER_H__ + +#include +#include +#include + +class VssDatabase; +class WsChannel; +class WsServer; +class IVssDatabase; +class IServer; + +using SubscriptionId = uint32_t; + +class ISubscriptionHandler { + public: + virtual ~ISubscriptionHandler() {} + + virtual uint64_t subscribe(WsChannel& channel, + std::shared_ptr db, + const std::string &path) = 0; + virtual int unsubscribe(SubscriptionId subscribeID) = 0; + virtual int unsubscribeAll(SubscriptionId connectionID) = 0; + virtual int updateByUUID(const std::string &signalUUID, const jsoncons::json &value) = 0; + virtual int updateByPath(const std::string &path, const jsoncons::json &value) = 0; + virtual std::shared_ptr getServer() = 0; + virtual int startThread() = 0; + virtual int stopThread() = 0; + virtual bool isThreadRunning() const = 0; + virtual void* subThreadRunner() = 0; +}; +#endif diff --git a/w3c-visserver-api/include/IVssCommandProcessor.hpp b/w3c-visserver-api/include/IVssCommandProcessor.hpp new file mode 100644 index 0000000..0a91ac8 --- /dev/null +++ b/w3c-visserver-api/include/IVssCommandProcessor.hpp @@ -0,0 +1,44 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IVSSCOMMANDPROCESSOR_H__ +#define __IVSSCOMMANDPROCESSOR_H__ + +#include + +#include + +class WsChannel; + +/** + * @class IVssCommandProcessor + * @brief Interface class for handling input JSON requests and providing response + * + * @note Any class implementing this interface shall handle JSON requests as defined by + * https://www.w3.org/TR/vehicle-information-service/#message-structure + */ +class IVssCommandProcessor { + public: + virtual ~IVssCommandProcessor() {} + + /** + * @brief Process JSON request and provide response JSON string + * @param req_json JSON formated request + * @param channel Active channel information on which \a req_json was received + * @return JSON formated response string + */ + virtual std::string processQuery(const std::string &req_json, + WsChannel& channel) = 0; +}; + +#endif diff --git a/w3c-visserver-api/include/IVssDatabase.hpp b/w3c-visserver-api/include/IVssDatabase.hpp new file mode 100644 index 0000000..1adb0d6 --- /dev/null +++ b/w3c-visserver-api/include/IVssDatabase.hpp @@ -0,0 +1,42 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IVSSDATABASE_HPP__ +#define __IVSSDATABASE_HPP__ + +#include + +#include + +class WsChannel; + +class IVssDatabase { + public: + virtual ~IVssDatabase() {} + + virtual void initJsonTree(const std::string &fileName) = 0; + virtual jsoncons::json getMetaData(const std::string &path) = 0; + virtual void setSignal(WsChannel& channel, + const std::string &path, + jsoncons::json value) = 0; + virtual jsoncons::json getSignal(WsChannel& channel, const std::string &path) = 0; + + // TODO: temporary added while components are refactored + jsoncons::json data_tree; + jsoncons::json meta_tree; + virtual std::list getPathForGet(const std::string &path, bool& isBranch) = 0; + virtual std::string getVSSSpecificPath(const std::string &path, bool& isBranch, jsoncons::json& tree) = 0; + virtual jsoncons::json getPathForSet(const std::string &path, jsoncons::json value) = 0; +}; + +#endif diff --git a/w3c-visserver-api/include/JsonResponses.hpp b/w3c-visserver-api/include/JsonResponses.hpp new file mode 100644 index 0000000..da4edb3 --- /dev/null +++ b/w3c-visserver-api/include/JsonResponses.hpp @@ -0,0 +1,60 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ + +#ifndef __DEFAULTJSONRESPONSES___ +#define __DEFAULTJSONRESPONSES___ + +#include +#include + +namespace JsonResponses { + std::string malFormedRequest(uint32_t request_id, + const std::string action, + std::string message); + void malFormedRequest(uint32_t request_id, + const std::string action, + std::string message, + jsoncons::json& jsonResponse); + + std::string malFormedRequest(std::string message); + void malFormedRequest(std::string message, + jsoncons::json& jsonResponse); + + /** A API call requested a non-existant path */ + std::string pathNotFound(uint32_t request_id, + const std::string action, + const std::string path); + void pathNotFound(uint32_t request_id, + const std::string action, + const std::string path, + jsoncons::json& jsonResponse); + + std::string noAccess(uint32_t request_id, + const std::string action, + std::string message); + void noAccess(uint32_t request_id, + const std::string action, + std::string message, + jsoncons::json& jsonResponse); + + std::string valueOutOfBounds(uint32_t request_id, + const std::string action, + const std::string message); + void valueOutOfBounds(uint32_t request_id, + const std::string action, + const std::string message, + jsoncons::json& jsonResponse); +} + +#endif diff --git a/w3c-visserver-api/include/RestV1ApiHandler.hpp b/w3c-visserver-api/include/RestV1ApiHandler.hpp new file mode 100644 index 0000000..1d1a5c6 --- /dev/null +++ b/w3c-visserver-api/include/RestV1ApiHandler.hpp @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __REST2JSONCONVERTER___ +#define __REST2JSONCONVERTER___ + +#include +#include +#include +#include +#include + +#include + +#include "IRestHandler.hpp" + + +class ILogger; + +/** + * @class RestV1ApiHandler + * @brief Handle initial implementation of REST API for VIS server + */ +class RestV1ApiHandler : public IRestHandler { + private: + std::shared_ptr logger_; + std::string docRoot_; + std::vector resourceHandleNames_; + //const std::unordered_map resourceHandlers_; + std::string regexResources_; + std::string regexHttpMethods_; + std::string regexToken_; + + public: + /** + * Enumeration of all available REST API resources + */ + enum class Resources { + Signals, //!< Signals + Metadata, //!< Metadata + Authorize, //!< Authorize + Count + }; + + private: + bool GetSignalPath(uint32_t requestId, + jsoncons::json& json, + std::string& restTarget); + /** + * @brief Verify that HTTP target begins with correct root path and remove it if found + * @param restTarget HTTP target path + * @param path Path to verify if \p restTarget begins with + * @return true if root path found with updated \p restTarget string, false if not found + */ + bool verifyPathAndStrip(std::string& restTarget, std::string& path); + + bool verifySignalResource(std::string& restTarget, jsoncons::json& res); + + public: + RestV1ApiHandler(std::shared_ptr loggerUtil, std::string& docRoot); + ~RestV1ApiHandler(); + + // IRest2JsonConverter + + bool GetJson(std::string&& restMethod, + std::string&& restTarget, + std::string& resultJson); +}; + +#endif diff --git a/w3c-visserver-api/include/signing.hpp b/w3c-visserver-api/include/SigningHandler.hpp similarity index 77% rename from w3c-visserver-api/include/signing.hpp rename to w3c-visserver-api/include/SigningHandler.hpp index 479f139..4361185 100644 --- a/w3c-visserver-api/include/signing.hpp +++ b/w3c-visserver-api/include/SigningHandler.hpp @@ -22,13 +22,15 @@ #include +#include "ISigningHandler.hpp" + using namespace std; using namespace jsoncons; using namespace jwt; class ILogger; -class signing { +class SigningHandler : public ISigningHandler { private: std::shared_ptr logger; string key = ""; @@ -36,11 +38,11 @@ class signing { string algorithm = "RS256"; public: - signing(std::shared_ptr loggerUtil); - string getKey(string fileName); - string getPublicKey(string fileName); - string sign(json data); - string sign(string data); + SigningHandler(std::shared_ptr loggerUtil); + string getKey(const string &fileName); + string getPublicKey(const string &fileName); + string sign(const json &data); + string sign(const string &data); #ifdef UNIT_TEST string decode(string signedData); #endif diff --git a/w3c-visserver-api/include/SubscriptionHandler.hpp b/w3c-visserver-api/include/SubscriptionHandler.hpp new file mode 100644 index 0000000..7233b52 --- /dev/null +++ b/w3c-visserver-api/include/SubscriptionHandler.hpp @@ -0,0 +1,80 @@ +/* + * ****************************************************************************** + * Copyright (c) 2018 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __SUBSCRIPTIONHANDLER_H__ +#define __SUBSCRIPTIONHANDLER_H__ + +#include +#include +#include +#include +#include +#include + +#include + +#include "visconf.hpp" +#include "ISubscriptionHandler.hpp" +#include "IAuthenticator.hpp" +#include "IAccessChecker.hpp" +#include "IServer.hpp" + +class AccessChecker; +class Authenticator; +class VssDatabase; +class WsChannel; +class WsServer; +class ILogger; + + +using SubConnId = uint64_t; + +// Subscription ID: Client ID +using subscriptions_t = std::unordered_map; + +// Subscription UUID +typedef std::string uuid_t; + +class SubscriptionHandler : public ISubscriptionHandler { + private: + std::shared_ptr logger; + std::unordered_map subscribeHandle; + std::shared_ptr server; + std::shared_ptr validator; + std::shared_ptr checkAccess; + std::mutex subMutex; + std::thread subThread; + bool threadRun; + std::queue> buffer; + + public: + SubscriptionHandler(std::shared_ptr loggerUtil, + std::shared_ptr wserver, + std::shared_ptr authenticate, + std::shared_ptr checkAccess); + ~SubscriptionHandler(); + + uint64_t subscribe(WsChannel& channel, + std::shared_ptr db, + const std::string &path); + int unsubscribe(SubscriptionId subscribeID); + int unsubscribeAll(SubscriptionId connectionID); + int updateByUUID(const std::string &signalUUID, const jsoncons::json &value); + int updateByPath(const std::string &path, const jsoncons::json &value); + std::shared_ptr getServer(); + int startThread(); + int stopThread(); + bool isThreadRunning() const; + void* subThreadRunner(); +}; +#endif diff --git a/w3c-visserver-api/include/vsscommandprocessor.hpp b/w3c-visserver-api/include/VssCommandProcessor.hpp similarity index 51% rename from w3c-visserver-api/include/vsscommandprocessor.hpp rename to w3c-visserver-api/include/VssCommandProcessor.hpp index fb492eb..ebcb868 100644 --- a/w3c-visserver-api/include/vsscommandprocessor.hpp +++ b/w3c-visserver-api/include/VssCommandProcessor.hpp @@ -19,44 +19,47 @@ #include -class vssdatabase; -class subscriptionhandler; -class authenticator; -class accesschecker; -class wschannel; +#include "IVssCommandProcessor.hpp" + +class IVssDatabase; +class ISubscriptionHandler; +class IAuthenticator; +class IAccessChecker; class ILogger; +class WsChannel; + -class vsscommandprocessor { +class VssCommandProcessor : public IVssCommandProcessor { private: std::shared_ptr logger; - vssdatabase* database = NULL; - subscriptionhandler* subHandler = NULL; - authenticator* tokenValidator = NULL; - accesschecker* accessValidator = NULL; + std::shared_ptr database; + std::shared_ptr subHandler; + std::shared_ptr tokenValidator; + std::shared_ptr accessValidator; #ifdef JSON_SIGNING_ON - signing* signer = NULL; + std::shared_ptr signer; #endif - std::string processGet(wschannel& channel, uint32_t request_id, std::string path); - std::string processSet(wschannel& channel, uint32_t request_id, std::string path, + std::string processGet(WsChannel& channel, uint32_t request_id, std::string path); + std::string processSet(WsChannel& channel, uint32_t request_id, std::string path, jsoncons::json value); - std::string processSubscribe(wschannel& channel, uint32_t request_id, - std::string path, uint32_t connectionID); + std::string processSubscribe(WsChannel& channel, uint32_t request_id, std::string path); std::string processUnsubscribe(uint32_t request_id, uint32_t subscribeID); std::string processGetMetaData(uint32_t request_id, std::string path); - std::string processAuthorize(wschannel& channel, uint32_t request_id, + std::string processAuthorize(WsChannel& channel, uint32_t request_id, std::string token); - std::string processAuthorizeWithPermManager(wschannel &channel, uint32_t request_id, + std::string processAuthorizeWithPermManager(WsChannel &channel, uint32_t request_id, std::string client, std::string clientSecret); public: - vsscommandprocessor(std::shared_ptr loggerUtil, - vssdatabase* database, - authenticator* vdator, - subscriptionhandler* subhandler); - ~vsscommandprocessor(); - std::string processQuery(std::string req_json, wschannel& channel); + VssCommandProcessor(std::shared_ptr loggerUtil, + std::shared_ptr database, + std::shared_ptr vdator, + std::shared_ptr subhandler); + ~VssCommandProcessor(); + + std::string processQuery(const std::string &req_json, WsChannel& channel); }; #endif diff --git a/w3c-visserver-api/include/VssDatabase.hpp b/w3c-visserver-api/include/VssDatabase.hpp new file mode 100644 index 0000000..edeac92 --- /dev/null +++ b/w3c-visserver-api/include/VssDatabase.hpp @@ -0,0 +1,63 @@ +/* + * ****************************************************************************** + * Copyright (c) 2018 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __VSSDATABASE_HPP__ +#define __VSSDATABASE_HPP__ + +#include +#include +#include +#include + +#include + +#include "IVssDatabase.hpp" + +class WsChannel; +class IAccessChecker; +class ISubscriptionHandler; +class ILogger; + +class VssDatabase : public IVssDatabase { +#ifdef UNIT_TEST + friend class w3cunittest; +#endif + + private: + std::shared_ptr logger; + std::mutex rwMutex; + + std::shared_ptr subHandler; + std::shared_ptr accessValidator; + std::string getPathForMetadata(std::string path, bool& isBranch); + std::string getReadablePath(std::string jsonpath); + void checkSetPermission(WsChannel& channel, jsoncons::json valueJson); + + public: + VssDatabase(std::shared_ptr loggerUtil, + std::shared_ptr subHandle, + std::shared_ptr accValidator); + ~VssDatabase(); + + void initJsonTree(const std::string &fileName); + jsoncons::json getMetaData(const std::string &path); + void setSignal(WsChannel& channel, const std::string &path, jsoncons::json value); + void setSignal(const std::string &path, jsoncons::json value); + jsoncons::json getSignal(WsChannel& channel, const std::string &path); + + std::list getPathForGet(const std::string &path, bool& isBranch); + std::string getVSSSpecificPath(const std::string &path, bool& isBranch, + jsoncons::json& tree); + jsoncons::json getPathForSet(const std::string &path, jsoncons::json value); +}; +#endif diff --git a/w3c-visserver-api/include/WebSockHttpFlexServer.hpp b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp new file mode 100644 index 0000000..7c28289 --- /dev/null +++ b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp @@ -0,0 +1,98 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __WEBSOCKHTTPFLEXSERVER_H__ +#define __WEBSOCKHTTPFLEXSERVER_H__ + +#include "IServer.hpp" + +#include +#include +#include +#include + +class WsChannel; +class IRestHandler; +class ILogger; + +/** + * \class WebSockHttpFlexServer + * \brief Combined Web-socket and HTTP server for both plain and SSL connections + * Implementation of \ref IServer interface, based on Boost.Beast library + * and its flex example code + */ +class WebSockHttpFlexServer : public IServer { + private: + std::vector>> listeners_; + std::mutex mutex_; + std::shared_ptr logger_; + std::shared_ptr restHandler_; + + bool isInitialized = false; + std::string docRoot_; + + const uint8_t NumOfThreads = 1; + + /// Default name for server certificate file + static const std::string serverCertFilename_; + /// Default name for server key file + static const std::string serverKeyFilename_; + + /** + * @brief Load server SSL certificates + * @param certPath Directory path where 'Server.pem' and 'Server.key' are located + * @param ctx ssl context to which certificates will be added + * @note 'Server.pem' and 'Server.key' needs to be located with executable + */ + void LoadCertData(std::string & certPath, boost::asio::ssl::context& ctx); + /** + * @brief Handle incoming data requests + * @param req_json Request message from connection + * @param channel Connection identifier + * @return Response JSON message for client + */ + std::string HandleRequest(const std::string &req_json, WsChannel &channel); + public: + WebSockHttpFlexServer(std::shared_ptr loggerUtil, + std::shared_ptr rest2jsonUtil); + ~WebSockHttpFlexServer(); + + /** + * @brief Initialize Boost.Beast server + * @param host Hostname for server connection + * @param port Port where to wait for server connections + * @param docRoot URL path that is handled + * @param certPath Directory path where 'Server.pem' and 'Server.key' are located + * @param allowInsecure If true, plain connections are allowed, otherwise SSL is mandatory + */ + void Initialize(std::string host, + int port, + std::string && docRoot, + std::string certPath, + bool allowInsecure = false); + /** + * @brief Start server + * Server needs to be initialized before is started + */ + void Start(); + + // IServer + + void AddListener(ObserverType type, std::shared_ptr listener); + void RemoveListener(ObserverType type, std::shared_ptr listener); + void SendToConnection(uint64_t connID, const std::string &message); +}; + + + +#endif /* __WEBSOCKHTTPFLEXSERVER_H__ */ diff --git a/w3c-visserver-api/include/wschannel.hpp b/w3c-visserver-api/include/WsChannel.hpp similarity index 75% rename from w3c-visserver-api/include/wschannel.hpp rename to w3c-visserver-api/include/WsChannel.hpp index 2b3d405..d350e1b 100644 --- a/w3c-visserver-api/include/wschannel.hpp +++ b/w3c-visserver-api/include/WsChannel.hpp @@ -22,25 +22,33 @@ using namespace std; using namespace jsoncons; using jsoncons::json; -class wschannel { +class WsChannel { + public: + enum class Type { + WEBSOCKET_PLAIN, + WEBSOCKET_SSL, + HTTP_PLAIN, + HTTP_SSL + }; private: - uint32_t connectionID; + uint64_t connectionID; bool authorized = false; string authToken; json permissions; + Type typeOfConnection; public: - void setConnID(uint32_t conID) { connectionID = conID; } + + void setConnID(uint64_t conID) { connectionID = conID; } void setAuthorized(bool isauth) { authorized = isauth; } void setAuthToken(string tok) { authToken = tok; } void setPermissions(json perm) { permissions = perm; } + void setType(Type type) { typeOfConnection = type; } - uint32_t getConnID() { return connectionID; } - + uint64_t getConnID() { return connectionID; } bool isAuthorized() { return authorized; } - string getAuthToken() { return authToken; } - json getPermissions() { return permissions; } + Type getType() { return typeOfConnection; } }; #endif diff --git a/w3c-visserver-api/include/wsserver.hpp b/w3c-visserver-api/include/WsServer.hpp similarity index 54% rename from w3c-visserver-api/include/wsserver.hpp rename to w3c-visserver-api/include/WsServer.hpp index 5ac5a40..53b0ddb 100644 --- a/w3c-visserver-api/include/wsserver.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -15,36 +15,40 @@ #ifndef __WSSERVER_H__ #define __WSSERVER_H__ +#include + #include "server_wss.hpp" +#include "IServer.hpp" + -class vsscommandprocessor; -class vsscommandprocessor; -class subscriptionhandler; -class authenticator; -class vssdatabase; -class accesschecker; +class IVssCommandProcessor; class ILogger; -class wsserver { +class WsServer : public IServer { private: SimpleWeb::SocketServer *secureServer_; SimpleWeb::SocketServer *insecureServer_; std::shared_ptr logger; bool isSecure_; - std::string configFileName_; + bool isInitialized_; public: - vsscommandprocessor* cmdProcessor; - subscriptionhandler* subHandler; - authenticator* tokenValidator; - vssdatabase* database; - accesschecker* accessCheck; - - wsserver(std::shared_ptr loggerUtil, int port, std::string configFileName, bool secure); - ~wsserver(); - void startServer(std::string endpointName); - void sendToConnection(uint32_t connID, std::string message); - vssdatabase* start(); + std::shared_ptr cmdProcessor; + + WsServer(); + bool Initialize(std::shared_ptr loggerUtil, + std::shared_ptr processor, + bool secure, + int port); + ~WsServer(); + void startServer(const std::string &endpointName); + bool start(); + + // IServer + + void AddListener(ObserverType, std::shared_ptr); + void RemoveListener(ObserverType, std::shared_ptr); + void SendToConnection(uint64_t connID, const std::string &message); }; #endif diff --git a/w3c-visserver-api/include/detect_ssl.hpp b/w3c-visserver-api/include/detect_ssl.hpp new file mode 100644 index 0000000..6fd1d65 --- /dev/null +++ b/w3c-visserver-api/include/detect_ssl.hpp @@ -0,0 +1,463 @@ +// +// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_EXAMPLE_COMMON_DETECT_SSL_HPP +#define BOOST_BEAST_EXAMPLE_COMMON_DETECT_SSL_HPP + +#include +#include + +//------------------------------------------------------------------------------ +// +// Example: Detect TLS/SSL +// +//------------------------------------------------------------------------------ + +//[example_core_detect_ssl_1 + +#include +#include +#include + +/** Return `true` if a buffer contains a TLS/SSL client handshake. + + This function returns `true` if the beginning of the buffer + indicates that a TLS handshake is being negotiated, and that + there are at least four octets in the buffer. + + If the content of the buffer cannot possibly be a TLS handshake + request, the function returns `false`. Otherwise, if additional + octets are required, `boost::indeterminate` is returned. + + @param buffer The input buffer to inspect. This type must meet + the requirements of @b ConstBufferSequence. + + @return `boost::tribool` indicating whether the buffer contains + a TLS client handshake, does not contain a handshake, or needs + additional octets. + + @see + + http://www.ietf.org/rfc/rfc2246.txt + 7.4. Handshake protocol +*/ +template +boost::tribool +is_ssl_handshake(ConstBufferSequence const& buffers); + +//] + +//[example_core_detect_ssl_2 + +template< + class ConstBufferSequence> +boost::tribool +is_ssl_handshake( + ConstBufferSequence const& buffers) +{ + // Make sure buffers meets the requirements + static_assert( + boost::asio::is_const_buffer_sequence::value, + "ConstBufferSequence requirements not met"); + + // We need at least one byte to really do anything + if(boost::asio::buffer_size(buffers) < 1) + return boost::indeterminate; + + // Extract the first byte, which holds the + // "message" type for the Handshake protocol. + unsigned char v; + boost::asio::buffer_copy(boost::asio::buffer(&v, 1), buffers); + + // Check that the message type is "SSL Handshake" (rfc2246) + if(v != 0x16) + { + // This is definitely not a handshake + return false; + } + + // At least four bytes are needed for the handshake + // so make sure that we get them before returning `true` + if(boost::asio::buffer_size(buffers) < 4) + return boost::indeterminate; + + // This can only be a TLS/SSL handshake + return true; +} + +//] + +//[example_core_detect_ssl_3 + +/** Detect a TLS/SSL handshake on a stream. + + This function reads from a stream to determine if a TLS/SSL + handshake is being received. The function call will block + until one of the following conditions is true: + + @li The disposition of the handshake is determined + + @li An error occurs + + Octets read from the stream will be stored in the passed dynamic + buffer, which may be used to perform the TLS handshake if the + detector returns true, or otherwise consumed by the caller based + on the expected protocol. + + @param stream The stream to read from. This type must meet the + requirements of @b SyncReadStream. + + @param buffer The dynamic buffer to use. This type must meet the + requirements of @b DynamicBuffer. + + @param ec Set to the error if any occurred. + + @return `boost::tribool` indicating whether the buffer contains + a TLS client handshake, does not contain a handshake, or needs + additional octets. If an error occurs, the return value is + undefined. +*/ +template< + class SyncReadStream, + class DynamicBuffer> +boost::tribool +detect_ssl( + SyncReadStream& stream, + DynamicBuffer& buffer, + boost::beast::error_code& ec) +{ + namespace beast = boost::beast; + + // Make sure arguments meet the requirements + static_assert(beast::is_sync_read_stream::value, + "SyncReadStream requirements not met"); + static_assert( + boost::asio::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + + // Loop until an error occurs or we get a definitive answer + for(;;) + { + // There could already be data in the buffer + // so we do this first, before reading from the stream. + auto const result = is_ssl_handshake(buffer.data()); + + // If we got an answer, return it + if(! boost::indeterminate(result)) + { + // This is a fast way to indicate success + // without retrieving the default category. + ec.assign(0, ec.category()); + return result; + } + + // The algorithm should never need more than 4 bytes + BOOST_ASSERT(buffer.size() < 4); + + // Prepare the buffer's output area. + auto const mutable_buffer = buffer.prepare(beast::read_size(buffer, 1536)); + + // Try to fill our buffer by reading from the stream + std::size_t const bytes_transferred = stream.read_some(mutable_buffer, ec); + + // Check for an error + if(ec) + break; + + // Commit what we read into the buffer's input area. + buffer.commit(bytes_transferred); + } + + // error + return false; +} + +//] + +//[example_core_detect_ssl_4 + +/** Detect a TLS/SSL handshake asynchronously on a stream. + + This function is used to asynchronously determine if a TLS/SSL + handshake is being received. + The function call always returns immediately. The asynchronous + operation will continue until one of the following conditions + is true: + + @li The disposition of the handshake is determined + + @li An error occurs + + This operation is implemented in terms of zero or more calls to + the next layer's `async_read_some` function, and is known as a + composed operation. The program must ensure that the + stream performs no other operations until this operation completes. + + Octets read from the stream will be stored in the passed dynamic + buffer, which may be used to perform the TLS handshake if the + detector returns true, or otherwise consumed by the caller based + on the expected protocol. + + @param stream The stream to read from. This type must meet the + requirements of @b AsyncReadStream. + + @param buffer The dynamic buffer to use. This type must meet the + requirements of @b DynamicBuffer. + + @param handler Invoked when the operation completes. + The handler may be moved or copied as needed. + The equivalent function signature of the handler must be: + @code + void handler( + error_code const& error, // Set to the error, if any + boost::tribool result // The result of the detector + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `boost::asio::io_context::post`. +*/ +template< + class AsyncReadStream, + class DynamicBuffer, + class CompletionToken> +BOOST_ASIO_INITFN_RESULT_TYPE( /*< `BOOST_ASIO_INITFN_RESULT_TYPE` customizes the return value based on the completion token >*/ + CompletionToken, + void(boost::beast::error_code, boost::tribool)) /*< This is the signature for the completion handler >*/ +async_detect_ssl( + AsyncReadStream& stream, + DynamicBuffer& buffer, + CompletionToken&& token); + +//] + +//[example_core_detect_ssl_5 + +// This is the composed operation. +template< + class AsyncReadStream, + class DynamicBuffer, + class Handler> +class detect_ssl_op; + +// Here is the implementation of the asynchronous initation function +template< + class AsyncReadStream, + class DynamicBuffer, + class CompletionToken> +BOOST_ASIO_INITFN_RESULT_TYPE( + CompletionToken, + void(boost::beast::error_code, boost::tribool)) +async_detect_ssl( + AsyncReadStream& stream, + DynamicBuffer& buffer, + CompletionToken&& token) +{ + namespace beast = boost::beast; + + // Make sure arguments meet the requirements + static_assert(beast::is_async_read_stream::value, + "SyncReadStream requirements not met"); + static_assert( + boost::asio::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + + // This helper manages some of the handler's lifetime and + // uses the result and handler specializations associated with + // the completion token to help customize the return value. + // + boost::asio::async_completion< + CompletionToken, void(beast::error_code, boost::tribool)> init{token}; + + // Create the composed operation and launch it. This is a constructor + // call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE + // to convert the completion token into the correct handler type, + // allowing user defined specializations of the async result template + // to take effect. + // + detect_ssl_op< + AsyncReadStream, + DynamicBuffer, + BOOST_ASIO_HANDLER_TYPE( + CompletionToken, void(beast::error_code, boost::tribool))>{ + stream, buffer, init.completion_handler}(beast::error_code{}, 0); + + // This hook lets the caller see a return value when appropriate. + // For example this might return std::future if + // CompletionToken is boost::asio::use_future. + // + // If a coroutine is used for the token, the return value from + // this function will be the `boost::tribool` representing the result. + // + return init.result.get(); +} + +//] + +//[example_core_detect_ssl_6 + +// Read from a stream to invoke is_tls_handshake asynchronously. +// This will be implemented using Asio's "stackless coroutines" +// which are based on macros forming a switch statement. The +// operation is derived from `coroutine` for this reason. +// +template< + class AsyncReadStream, + class DynamicBuffer, + class Handler> +class detect_ssl_op : public boost::asio::coroutine +{ + // This composed operation has trivial state, + // so it is just kept inside the class and can + // be cheaply copied as needed by the implementation. + + AsyncReadStream& stream_; + DynamicBuffer& buffer_; + Handler handler_; + boost::tribool result_ = false; + +public: + // Boost.Asio requires that handlers are CopyConstructible. + // The state for this operation is cheap to copy. + detect_ssl_op(detect_ssl_op const&) = default; + + // The constructor just keeps references the callers varaibles. + // + template + detect_ssl_op( + AsyncReadStream& stream, + DynamicBuffer& buffer, + DeducedHandler&& handler) + : stream_(stream) + , buffer_(buffer) + , handler_(std::forward(handler)) + { + } + + // Associated allocator support. This is Asio's system for + // allowing the final completion handler to customize the + // memory allocation strategy used for composed operation + // states. A composed operation needs to use the same allocator + // as the final handler. These declarations achieve that. + + using allocator_type = + boost::asio::associated_allocator_t; + + allocator_type + get_allocator() const noexcept + { + return (boost::asio::get_associated_allocator)(handler_); + } + + // Executor hook. This is Asio's system for customizing the + // manner in which asynchronous completion handlers are invoked. + // A composed operation needs to use the same executor to invoke + // intermediate completion handlers as that used to invoke the + // final handler. + + using executor_type = boost::asio::associated_executor_t< + Handler, decltype(std::declval().get_executor())>; + + executor_type get_executor() const noexcept + { + return (boost::asio::get_associated_executor)(handler_, stream_.get_executor()); + } + + // Our main entry point. This will get called as our + // intermediate operations complete. Definition below. + // + void operator()(boost::beast::error_code ec, std::size_t bytes_transferred); +}; + +//] + +//[example_core_detect_ssl_7 + +// detect_ssl_op is callable with the signature +// void(error_code, bytes_transferred), +// allowing `*this` to be used as a ReadHandler +// +template< + class AsyncStream, + class DynamicBuffer, + class Handler> +void +detect_ssl_op:: +operator()(boost::beast::error_code ec, std::size_t bytes_transferred) +{ + namespace beast = boost::beast; + + // This introduces the scope of the stackless coroutine + BOOST_ASIO_CORO_REENTER(*this) + { + // There could already be data in the buffer + // so we do this first, before reading from the stream. + result_ = is_ssl_handshake(buffer_.data()); + + // If we got an answer, return it + if(! boost::indeterminate(result_)) + { + // We need to invoke the handler, but the guarantee + // is that the handler will not be called before the + // call to async_detect_ssl returns, so we must post + // the operation to the executor. The helper function + // `bind_handler` lets us bind arguments in a safe way + // that preserves the type customization hooks of the + // original handler. + BOOST_ASIO_CORO_YIELD + boost::asio::post( + stream_.get_executor(), + beast::bind_handler(std::move(*this), ec, 0)); + } + else + { + // Loop until an error occurs or we get a definitive answer + for(;;) + { + // The algorithm should never need more than 4 bytes + BOOST_ASSERT(buffer_.size() < 4); + + BOOST_ASIO_CORO_YIELD + { + // Prepare the buffer's output area. + auto const mutable_buffer = buffer_.prepare(beast::read_size(buffer_, 1536)); + + // Try to fill our buffer by reading from the stream + stream_.async_read_some(mutable_buffer, std::move(*this)); + } + + // Check for an error + if(ec) + break; + + // Commit what we read into the buffer's input area. + buffer_.commit(bytes_transferred); + + // See if we can detect the handshake + result_ = is_ssl_handshake(buffer_.data()); + + // If it is detected, call the handler + if(! boost::indeterminate(result_)) + { + // We don't need bind_handler here because we were invoked + // as a result of an intermediate asynchronous operation. + break; + } + } + } + + // Invoke the final handler. + handler_(ec, result_); + } +} + +//] + +#endif diff --git a/w3c-visserver-api/include/exception.hpp b/w3c-visserver-api/include/exception.hpp index aeb3af2..6db6ecf 100644 --- a/w3c-visserver-api/include/exception.hpp +++ b/w3c-visserver-api/include/exception.hpp @@ -63,4 +63,16 @@ class outOfBoundException : public std::exception { virtual const char* what() const throw() { return message.c_str(); } }; + +// value out of bound exception +class notValidException : public std::exception { + private: + std::string message; + + public: + notValidException(std::string msg) { message = msg; } + + virtual const char* what() const throw() { return message.c_str(); } +}; + #endif diff --git a/w3c-visserver-api/include/ssl_stream.hpp b/w3c-visserver-api/include/ssl_stream.hpp new file mode 100644 index 0000000..85bea9d --- /dev/null +++ b/w3c-visserver-api/include/ssl_stream.hpp @@ -0,0 +1,325 @@ +// +// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_EXAMPLE_COMMON_SSL_STREAM_HPP +#define BOOST_BEAST_EXAMPLE_COMMON_SSL_STREAM_HPP + +// This include is necessary to work with `ssl::stream` and `boost::beast::websocket::stream` +#include + +#include +#include +#include +#include +#include +#include + +/** C++11 enabled SSL socket wrapper + + This wrapper provides an interface identical to `boost::asio::ssl::stream`, + with the following additional properties: + + @li Satisfies @b MoveConstructible + + @li Satisfies @b MoveAssignable + + @li Constructible from a moved socket. +*/ +template +class ssl_stream + : public boost::asio::ssl::stream_base +{ + using stream_type = boost::asio::ssl::stream; + + std::unique_ptr p_; + boost::asio::ssl::context* ctx_; + +public: + /// The native handle type of the SSL stream. + using native_handle_type = typename stream_type::native_handle_type; + + /// Structure for use with deprecated impl_type. + using impl_struct = typename stream_type::impl_struct; + + /// The type of the next layer. + using next_layer_type = typename stream_type::next_layer_type; + + /// The type of the lowest layer. + using lowest_layer_type = typename stream_type::lowest_layer_type; + + /// The type of the executor associated with the object. + using executor_type = typename stream_type::executor_type; + + template + ssl_stream( + Arg&& arg, + boost::asio::ssl::context& ctx) + : p_(new stream_type{ + std::forward(arg), ctx}) + , ctx_(&ctx) + { + } + + ssl_stream(ssl_stream&& other) + : p_(std::move(other.p_)) + , ctx_(other.ctx_) + { + } + + ssl_stream& operator=(ssl_stream&& other) + { + p_ = std::move(other.p_); + ctx_ = other.ctx_; + return *this; + } + + executor_type + get_executor() noexcept + { + return p_->get_executor(); + } + + native_handle_type + native_handle() + { + return p_->native_handle(); + } + + next_layer_type const& + next_layer() const + { + return p_->next_layer(); + } + + next_layer_type& + next_layer() + { + return p_->next_layer(); + } + + lowest_layer_type& + lowest_layer() + { + return p_->lowest_layer(); + } + + lowest_layer_type const& + lowest_layer() const + { + return p_->lowest_layer(); + } + + void + set_verify_mode(boost::asio::ssl::verify_mode v) + { + p_->set_verify_mode(v); + } + + boost::system::error_code + set_verify_mode(boost::asio::ssl::verify_mode v, + boost::system::error_code& ec) + { + return p_->set_verify_mode(v, ec); + } + + void + set_verify_depth(int depth) + { + p_->set_verify_depth(depth); + } + + boost::system::error_code + set_verify_depth( + int depth, boost::system::error_code& ec) + { + return p_->set_verify_depth(depth, ec); + } + + template + void + set_verify_callback(VerifyCallback callback) + { + p_->set_verify_callback(callback); + } + + template + boost::system::error_code + set_verify_callback(VerifyCallback callback, + boost::system::error_code& ec) + { + return p_->set_verify_callback(callback, ec); + } + + void + handshake(handshake_type type) + { + p_->handshake(type); + } + + boost::system::error_code + handshake(handshake_type type, + boost::system::error_code& ec) + { + return p_->handshake(type, ec); + } + + template + void + handshake( + handshake_type type, ConstBufferSequence const& buffers) + { + p_->handshake(type, buffers); + } + + template + boost::system::error_code + handshake(handshake_type type, + ConstBufferSequence const& buffers, + boost::system::error_code& ec) + { + return p_->handshake(type, buffers, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler, + void(boost::system::error_code)) + async_handshake(handshake_type type, + BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler) + { + return p_->async_handshake(type, + BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler)); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, + void (boost::system::error_code, std::size_t)) + async_handshake(handshake_type type, ConstBufferSequence const& buffers, + BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler) + { + return p_->async_handshake(type, buffers, + BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler)); + } + + void + shutdown() + { + p_->shutdown(); + } + + boost::system::error_code + shutdown(boost::system::error_code& ec) + { + return p_->shutdown(ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ShutdownHandler, + void (boost::system::error_code)) + async_shutdown(BOOST_ASIO_MOVE_ARG(ShutdownHandler) handler) + { + return p_->async_shutdown( + BOOST_ASIO_MOVE_CAST(ShutdownHandler)(handler)); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers) + { + return p_->write_some(buffers); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers, + boost::system::error_code& ec) + { + return p_->write_some(buffers, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t)) + async_write_some(ConstBufferSequence const& buffers, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + return p_->async_write_some(buffers, + BOOST_ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers) + { + return p_->read_some(buffers); + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers, + boost::system::error_code& ec) + { + return p_->read_some(buffers, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_read_some(MutableBufferSequence const& buffers, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + return p_->async_read_some(buffers, + BOOST_ASIO_MOVE_CAST(ReadHandler)(handler)); + } + + template + friend + void + teardown(boost::beast::websocket::role_type, + ssl_stream& stream, + boost::system::error_code& ec); + + template + friend + void + async_teardown(boost::beast::websocket::role_type, + ssl_stream& stream, TeardownHandler&& handler); +}; + +// These hooks are used to inform boost::beast::websocket::stream on +// how to tear down the connection as part of the WebSocket +// protocol specifications + +template +inline +void +teardown( + boost::beast::websocket::role_type role, + ssl_stream& stream, + boost::system::error_code& ec) +{ + // Just forward it to the wrapped ssl::stream + using boost::beast::websocket::teardown; + teardown(role, *stream.p_, ec); +} + +template +inline +void +async_teardown( + boost::beast::websocket::role_type role, + ssl_stream& stream, + TeardownHandler&& handler) +{ + // Just forward it to the wrapped ssl::stream + using boost::beast::websocket::async_teardown; + async_teardown(role, + *stream.p_, std::forward(handler)); +} + +#endif diff --git a/w3c-visserver-api/include/subscriptionhandler.hpp b/w3c-visserver-api/include/subscriptionhandler.hpp deleted file mode 100644 index d7b7d67..0000000 --- a/w3c-visserver-api/include/subscriptionhandler.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * ****************************************************************************** - * Copyright (c) 2018 Robert Bosch GmbH. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/org/documents/epl-2.0/index.php - * - * Contributors: - * Robert Bosch GmbH - initial API and functionality - * ***************************************************************************** - */ -#ifndef __SUBSCRIPTIONHANDLER_H__ -#define __SUBSCRIPTIONHANDLER_H__ - -#include -#include -#include -#include "visconf.hpp" -#include -#include -#include - -#include - -class accesschecker; -class authenticator; -class vssdatabase; -class wschannel; -class wsserver; -class ILogger; - -// Subscription ID: Client ID -typedef std::unordered_map subscriptions_t; - -// Subscription UUID -typedef std::string uuid_t; - -class subscriptionhandler { - private: - std::shared_ptr logger; - std::unordered_map subscribeHandle; - wsserver* server; - authenticator* validator; - accesschecker* checkAccess; - std::mutex subMutex; - std::thread subThread; - bool threadRun; - std::queue> buffer; - - public: - subscriptionhandler(std::shared_ptr loggerUtil, - wsserver* wserver, - authenticator* authenticate, - accesschecker* checkAccess); - ~subscriptionhandler(); - - uint32_t subscribe(wschannel& channel, vssdatabase* db, - uint32_t channelID, std::string path); - int unsubscribe(uint32_t subscribeID); - int unsubscribeAll(uint32_t connectionID); - int updateByUUID(std::string signalUUID, jsoncons::json value); - int updateByPath(std::string path, jsoncons::json value); - wsserver* getServer(); - int startThread(); - int stopThread(); - bool isThreadRunning(); - void* subThreadRunner(); -}; -#endif diff --git a/w3c-visserver-api/include/vssdatabase.hpp b/w3c-visserver-api/include/vssdatabase.hpp deleted file mode 100644 index beb7f34..0000000 --- a/w3c-visserver-api/include/vssdatabase.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * ****************************************************************************** - * Copyright (c) 2018 Robert Bosch GmbH. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/org/documents/epl-2.0/index.php - * - * Contributors: - * Robert Bosch GmbH - initial API and functionality - * ***************************************************************************** - */ -#ifndef __VSSDATABASE_HPP__ -#define __VSSDATABASE_HPP__ - -#include -#include -#include -#include - -#include - -class subscriptionhandler; -class accesschecker; -class wschannel; -class ILogger; - -class vssdatabase { - friend class subscriptionhandler; - friend class authenticator; -#ifdef UNIT_TEST - friend class w3cunittest; -#endif - - private: - std::shared_ptr logger; - std::mutex rwMutex; - jsoncons::json data_tree; - jsoncons::json meta_tree; - subscriptionhandler* subHandler; - accesschecker* accessValidator; - std::string getVSSSpecificPath(std::string path, bool& isBranch, jsoncons::json& tree); - std::string getPathForMetadata(std::string path, bool& isBranch); - std::list getPathForGet(std::string path, bool& isBranch); - jsoncons::json getPathForSet(std::string path, jsoncons::json value); - std::string getReadablePath(std::string jsonpath); - void checkSetPermission(wschannel& channel, jsoncons::json valueJson); - - public: - vssdatabase(std::shared_ptr loggerUtil, - subscriptionhandler* subHandle, - accesschecker* accValidator); - ~vssdatabase(); - void initJsonTree(std::string fileName); - jsoncons::json getMetaData(std::string path); - void setSignal(wschannel& channel, std::string path, jsoncons::json value); - void setSignal(std::string path, jsoncons::json value); - jsoncons::json getSignal(wschannel& channel, std::string path); -}; -#endif diff --git a/w3c-visserver-api/src/accesschecker.cpp b/w3c-visserver-api/src/AccessChecker.cpp similarity index 68% rename from w3c-visserver-api/src/accesschecker.cpp rename to w3c-visserver-api/src/AccessChecker.cpp index 9991e9a..fed60b0 100644 --- a/w3c-visserver-api/src/accesschecker.cpp +++ b/w3c-visserver-api/src/AccessChecker.cpp @@ -12,16 +12,24 @@ * ***************************************************************************** */ -#include "accesschecker.hpp" +#include "AccessChecker.hpp" + +#include +#include + +#include "IAuthenticator.hpp" +#include "WsChannel.hpp" using namespace std; +using namespace jsoncons; +//using jsoncons::json; -accesschecker::accesschecker(authenticator *vdator) { +AccessChecker::AccessChecker(std::shared_ptr vdator) { tokenValidator = vdator; } -// check the permissions json in wschannel if path has read access -bool accesschecker::checkReadAccess(wschannel &channel, string path) { +// check the permissions json in WsChannel if path has read access +bool AccessChecker::checkReadAccess(WsChannel &channel, const string &path) { json permissions = channel.getPermissions(); string perm = permissions.get_with_default(path, ""); @@ -31,8 +39,8 @@ bool accesschecker::checkReadAccess(wschannel &channel, string path) { return false; } -// check the permissions json in wschannel if path has write access -bool accesschecker::checkWriteAccess(wschannel &channel, string path) { +// check the permissions json in WsChannel if path has write access +bool AccessChecker::checkWriteAccess(WsChannel &channel, const string &path) { json permissions = channel.getPermissions(); string perm = permissions.get_with_default(path, ""); @@ -44,7 +52,7 @@ bool accesschecker::checkWriteAccess(wschannel &channel, string path) { // Checks if all the paths have write access.If even 1 path in the list does not // have write access, this method returns false. -bool accesschecker::checkPathWriteAccess(wschannel &channel, json paths) { +bool AccessChecker::checkPathWriteAccess(WsChannel &channel, const json &paths) { for (size_t i = 0; i < paths.size(); i++) { json item = paths[i]; string jPath = item["path"].as(); diff --git a/w3c-visserver-api/src/authenticator.cpp b/w3c-visserver-api/src/Authenticator.cpp similarity index 66% rename from w3c-visserver-api/src/authenticator.cpp rename to w3c-visserver-api/src/Authenticator.cpp index bdec023..ffbc71d 100644 --- a/w3c-visserver-api/src/authenticator.cpp +++ b/w3c-visserver-api/src/Authenticator.cpp @@ -11,7 +11,7 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#include "authenticator.hpp" +#include "Authenticator.hpp" #include "ILogger.hpp" #include #include @@ -20,8 +20,8 @@ #include #include -#include "vssdatabase.hpp" -#include "wschannel.hpp" +#include "VssDatabase.hpp" +#include "WsChannel.hpp" using namespace std; @@ -38,40 +38,47 @@ namespace { } } - -void authenticator::updatePubKey(string key) { +void Authenticator::updatePubKey(string key) { pubkey = key; if(pubkey == "") pubkey = getPublicKeyFromFile("jwt.pub.key"); } // utility method to validate token. -int authenticator::validateToken(wschannel& channel, string authToken) { - auto decoded = jwt::decode(authToken); +int Authenticator::validateToken(WsChannel& channel, string authToken) { json claims; - (void) channel; - for (auto& e : decoded.get_payload_claims()) { - logger->Log(LogLevel::INFO, e.first + " = " + e.second.as_string()); - claims[e.first] = e.second.to_json().to_str(); - } - - auto verifier = jwt::verify().allow_algorithm( - jwt::algorithm::rs256(pubkey, "", "", "")); + int ttl = -1; + try { - verifier.verify(decoded); - } catch (const std::runtime_error& e) { - logger->Log(LogLevel::ERROR, "authenticator::validate: " + string(e.what()) - + " Exception occured while authentication. Token is not valid!"); - return -1; - } + auto decoded = jwt::decode(authToken); - int ttl = claims["exp"].as(); - channel.setAuthorized(true); - channel.setAuthToken(authToken); + for (auto& e : decoded.get_payload_claims()) { + logger->Log(LogLevel::INFO, e.first + " = " + e.second.to_json().to_str()); + claims[e.first] = e.second.to_json().to_str(); + } + + auto verifier = jwt::verify().allow_algorithm( + jwt::algorithm::rs256(pubkey, "", "", "")); + try { + verifier.verify(decoded); + } catch (const std::runtime_error& e) { + logger->Log(LogLevel::ERROR, "Authenticator::validate: " + string(e.what()) + + " Exception occurred while authentication. Token is not valid!"); + return -1; + } + + channel.setAuthorized(true); + channel.setAuthToken(authToken); + ttl = claims["exp"].as(); + } + catch (std::exception &e) { + logger->Log(LogLevel::ERROR, "Authenticator::validate: " + string(e.what()) + + " Exception occurred while decoding token!"); + } return ttl; } -authenticator::authenticator(std::shared_ptr loggerUtil, string secretkey, string algo) { +Authenticator::Authenticator(std::shared_ptr loggerUtil, string secretkey, string algo) { logger = loggerUtil; algorithm = algo; pubkey = secretkey; @@ -79,7 +86,7 @@ authenticator::authenticator(std::shared_ptr loggerUtil, string secretk // validates the token against expiry date/time. should be extended to check // some other claims. -int authenticator::validate(wschannel& channel, vssdatabase* db, +int Authenticator::validate(WsChannel& channel, std::shared_ptr db, string authToken) { int ttl = validateToken(channel, authToken); if (ttl > 0) { @@ -92,7 +99,7 @@ int authenticator::validate(wschannel& channel, vssdatabase* db, // Checks if the token is still valid for the requests from the channel(client). // Internally check this before publishing messages for previously subscribed // signals. -bool authenticator::isStillValid(wschannel& channel) { +bool Authenticator::isStillValid(WsChannel& channel) { string token = channel.getAuthToken(); int ret = validateToken(channel, token); @@ -106,9 +113,9 @@ bool authenticator::isStillValid(wschannel& channel) { // **Do this only once for authenticate request** // resolves the permission in the JWT token and store the absolute path to the -// signals in permissions JSON in wschannel. -void authenticator::resolvePermissions(wschannel& channel, - vssdatabase* database) { +// signals in permissions JSON in WsChannel. +void Authenticator::resolvePermissions(WsChannel& channel, + std::shared_ptr database) { string authToken = channel.getAuthToken(); auto decoded = jwt::decode(authToken); json claims; diff --git a/w3c-visserver-api/src/JsonResponses.cpp b/w3c-visserver-api/src/JsonResponses.cpp new file mode 100644 index 0000000..3f790c5 --- /dev/null +++ b/w3c-visserver-api/src/JsonResponses.cpp @@ -0,0 +1,131 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#include "JsonResponses.hpp" + +namespace JsonResponses { + void malFormedRequest(uint32_t request_id, + const std::string action, + std::string message, + jsoncons::json& jsonResponse) { + jsonResponse["action"] = action; + jsonResponse["requestId"] = request_id; + jsoncons::json error; + error["number"] = 400; + error["reason"] = "Bad Request"; + error["message"] = message; + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string malFormedRequest(uint32_t request_id, + const std::string action, + std::string message) { + jsoncons::json answer; + malFormedRequest(request_id, action, message, answer); + + std::stringstream ss; + ss << pretty_print(answer); + return ss.str(); + } + + void malFormedRequest(std::string message, jsoncons::json& jsonResponse) { + jsoncons::json error; + + error["number"] = 400; + error["reason"] = "Bad Request"; + error["message"] = message; + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string malFormedRequest(std::string message) { + jsoncons::json answer; + malFormedRequest(message, answer); + + std::stringstream ss; + ss << pretty_print(answer); + return ss.str(); + } + + /** A API call requested a non-existant path */ + void pathNotFound(uint32_t request_id, + const std::string action, + const std::string path, + jsoncons::json& jsonResponse) { + jsonResponse["action"] = action; + jsonResponse["requestId"] = request_id; + jsoncons::json error; + error["number"] = 404; + error["reason"] = "Path not found"; + error["message"] = "I can not find " + path + " in my db"; + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string pathNotFound(uint32_t request_id, + const std::string action, + const std::string path) { + jsoncons::json answer; + pathNotFound(request_id, action, path, answer); + + std::stringstream ss; + ss << pretty_print(answer); + return ss.str(); + } + + void noAccess(uint32_t request_id, + const std::string action, + std::string message, + jsoncons::json& jsonResponse) { + jsoncons::json error; + jsonResponse["action"] = action; + jsonResponse["requestId"] = request_id; + error["number"] = 403; + error["reason"] = "Forbidden"; + error["message"] = message; + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string noAccess(uint32_t request_id, + const std::string action, + std::string message) { + jsoncons::json answer; + noAccess(request_id, action, message, answer); + + std::stringstream ss; + ss << pretty_print(answer); + return ss.str(); + } + + void valueOutOfBounds(uint32_t request_id, + const std::string action, + const std::string message, + jsoncons::json& jsonResponse) { + jsonResponse["action"] = action; + jsonResponse["requestId"] = request_id; + jsoncons::json error; + error["number"] = 400; + error["reason"] = "Value passed is out of bounds"; + error["message"] = message; + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string valueOutOfBounds(uint32_t request_id, + const std::string action, + const std::string message) { + jsoncons::json answer; + valueOutOfBounds(request_id, action, message, answer); + + std::stringstream ss; + ss << pretty_print(answer); + return ss.str(); + } +} diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp new file mode 100644 index 0000000..8908e78 --- /dev/null +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -0,0 +1,373 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ + +#include "RestV1ApiHandler.hpp" + +#include +#include +#include +#include + +#include "JsonResponses.hpp" +#include "ILogger.hpp" + + +RestV1ApiHandler::RestV1ApiHandler(std::shared_ptr loggerUtil, std::string& docRoot) + : logger_(loggerUtil), + docRoot_(docRoot) { + + // Supported HTTP methods + regexHttpMethods_ = "^(GET|POST|PUT|OPTIONS)"; + + // Resource strings for REST API hooks. Order must match order in RestV1ApiHandler::Resources enum + resourceHandleNames_ = std::vector{std::string{"signals"}, + std::string{"metadata"}, + std::string{"authorize"}}; + // verify that sizes match + assert(resourceHandleNames_.size() == static_cast(Resources::Count)); + + // fill regex automatically with all available resources + regexResources_= std::string("^("); + for (unsigned i = 0; i < resourceHandleNames_.size() - 1u; ++i) { + regexResources_ += resourceHandleNames_[i] + std::string("|"); + } + regexResources_ += resourceHandleNames_[resourceHandleNames_.size() - 1u] + std::string(")"); + + // base64 url allowed characters + regexToken_ = std::string("[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+"); +} + +RestV1ApiHandler::~RestV1ApiHandler() { +} + +bool RestV1ApiHandler::verifyPathAndStrip(std::string& restTarget, std::string& path) { + size_t pos = restTarget.find(path); + + // found doc root at correct position + if (pos == 0) { + // remove root path to leave only next level + restTarget.erase(pos, path.length()); + return true; + } + + return false; +} + +bool RestV1ApiHandler::GetSignalPath(uint32_t requestId, + jsoncons::json& json, + std::string& restTarget) { + std::string signalPath; + std::string foundStr; + std::string restDelimiter("/"); + std::string defaultDelimiter("."); + std::string queryDelimiter("?"); + std::smatch sm; + bool ret = true; + + if (restTarget.size() && verifyPathAndStrip(restTarget, restDelimiter)) { + while (restTarget.size()) { + // we only accept clean printable characters + const std::regex regexValidWord("^([A-Za-z0-9]+)"); + + if (std::regex_search(restTarget, sm, regexValidWord)) { + foundStr = sm.str(1); + if (foundStr.size() == 0) { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path not valid", + json); + ret = false; + break; + } + signalPath += foundStr; + if (verifyPathAndStrip(restTarget, foundStr)) { + if ((restTarget.size() == 0)) { + break; + } + // we support both '/' and '.' as branch/signal delimiters + else if (verifyPathAndStrip(restTarget, restDelimiter)) { + signalPath += defaultDelimiter; + } + // we support both '/' and '.' as branch/signal delimiters + else if (verifyPathAndStrip(restTarget, defaultDelimiter)) { + signalPath += defaultDelimiter; + } + // if we got to query start '?' char, end extraction if signal path + else if (restTarget[0] == '?') { + break; + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path delimiter not valid", + json); + ret = false; + } + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path not valid", + json); + ret = false; + break; + } + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path URI not valid", + json); + ret = false; + break; + } + } + } + else { + // not supporting retrieving of all signals by default + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signals cannot be retrieved in bulk request for now, only through single signal requests", + json); + ret = false; + } + + // update signal path if all is OK + if (ret) { + json["path"] = signalPath; + } + + return ret; +} + +// Basic implementation of REST API handling +// For now governed by KISS principle, but in future, it should be re-factored +// to handle independently different methods and resources for easier maintenance.. +// possibly by providing hooks for each resource and|or API version, etc... +bool RestV1ApiHandler::GetJson(std::string&& restMethod, + std::string&& restTarget, + std::string& jsonRequest) { + bool ret = true; + std::smatch sm; + jsoncons::json json; + + // TODO: should client provide request ID when using REST API? + uint32_t requestId = std::rand() % std::numeric_limits::max(); + json["requestId"] = requestId; + + // search for supported HTTP requests + const std::regex regSupportedHttpActions(regexHttpMethods_, std::regex_constants::icase); + // check REST action method + std::regex_match (restMethod, sm, regSupportedHttpActions); + // if supported methods found, parse further + if (sm.size()) { + std::string httpMethod = sm.str(1); + boost::algorithm::to_lower(httpMethod); + json["action"] = httpMethod; + + if (verifyPathAndStrip(restTarget, docRoot_)) { + const std::regex regResources(regexResources_, std::regex_constants::icase); + + // get requested resource type + std::regex_search(restTarget, sm, regResources); + if (sm.size()) { + std::string foundStr = sm.str(1); + + if (verifyPathAndStrip(restTarget, foundStr)) { + // ////// + // signals handler + if (foundStr.compare("signals") == 0) { + // handler CORS pre-flight requests from browsers + if (httpMethod.compare("options") == 0) { + json["action"] = "options"; + json["methods"] = "PUT, GET, OPTIONS"; + json["headers"] = "X-PINGOTHER, Content-Type"; + json["max-age"] = "86400"; + json["origin"] = "*"; + } + else if (httpMethod.compare("get") == 0) { + if (GetSignalPath(requestId, json, restTarget)) { + json["action"] = "get"; + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Invalid path", + json); + ret = false; + } + } + else if (httpMethod.compare("put") == 0) { + if (GetSignalPath(requestId, json, restTarget)) { + json["action"] = "set"; + std::string queryStr("?value="); + + if (verifyPathAndStrip(restTarget, queryStr)) { + json["value"] = restTarget; + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Invalid query parameter", + json); + } + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Invalid path", + json); + ret = false; + } + } + else { + // TODO: handle signal POST + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "HTTP method not yet supported for 'signals' resource", + json); + ret = false; + } + } + // ////// + // metadata handler + else if (foundStr.compare("metadata") == 0) { + // handler CORS pre-flight requests from browsers + if (httpMethod.compare("options") == 0) { + json["action"] = "options"; + json["methods"] = "GET, OPTIONS"; + json["headers"] = "X-PINGOTHER, Content-Type"; + json["max-age"] = "86400"; + json["origin"] = "*"; + } + else if (httpMethod.compare("get") == 0) { + ret = GetSignalPath(requestId, json, restTarget); + + json["action"] = "getMetadata"; + } + else { + // TODO: handle signal POST + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "POST method not supported for 'metadata' resource", + json); + ret = false; + } + } + // ////// + // authorize handler + else if (foundStr == "authorize") { + // handler CORS pre-flight requests from browsers + if (httpMethod.compare("options") == 0) { + json["action"] = "options"; + json["methods"] = "POST, OPTIONS"; + json["headers"] = "X-PINGOTHER, Content-Type"; + json["max-age"] = "86400"; + json["origin"] = "*"; + } + else if (httpMethod == "post") { + std::string tokenParam("?token="); + if (verifyPathAndStrip(restTarget, tokenParam)) { + const std::regex regToken(regexToken_); + std::regex_search(restTarget, sm, regToken); + + sleep(1); + if (sm.size()) { + json["action"] = "authorize"; + json["tokens"] = sm.str(0); + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Token for 'authorize' not valid", + json); + ret = false; + } + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Parameters for 'authorize' not valid", + json); + ret = false; + } + } + else { + // GET not supported for authorize resource + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "GET method not supported for 'authorize' resource", + json); + ret = false; + } + } + else { + JsonResponses::pathNotFound( + requestId, + json["action"].as_string(), + "Requested resource do not exist", + json); + ret = false; + } + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path URI not valid", + json); + ret = false; + } + } + else + { + JsonResponses::pathNotFound( + requestId, + json["action"].as_string(), + "Requested resource do not exist", + json); + ret = false; + } + } + } + else + { + // TODO: evaluate what and how we should support HTTP methods (put, patch, ...) + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Requested HTTP method is not supported", + json); + ret = false; + } + + std::stringstream ss; + ss << pretty_print(json); + jsonRequest = ss.str(); + + return ret; +} diff --git a/w3c-visserver-api/src/signing.cpp b/w3c-visserver-api/src/SigningHandler.cpp similarity index 79% rename from w3c-visserver-api/src/signing.cpp rename to w3c-visserver-api/src/SigningHandler.cpp index 9881d65..e04ff00 100644 --- a/w3c-visserver-api/src/signing.cpp +++ b/w3c-visserver-api/src/SigningHandler.cpp @@ -12,25 +12,25 @@ * ***************************************************************************** */ -#include "signing.hpp" +#include "SigningHandler.hpp" #include "ILogger.hpp" -#define PRIVATEFILENAME "signing.private.key" -#define PUBLICFILENAME "signing.public.key" +#define PRIVATEFILENAME "SigningHandler.private.key" +#define PUBLICFILENAME "SigningHandler.public.key" /** Constructor */ -signing::signing(std::shared_ptr loggerUtil) : logger(loggerUtil) { +SigningHandler::SigningHandler(std::shared_ptr loggerUtil) : logger(loggerUtil) { getKey(PRIVATEFILENAME); getPublicKey(PUBLICFILENAME); } /** - Get the private key for signing. + Get the private key for SigningHandler. */ -string signing::getKey(string fileName) { +string SigningHandler::getKey(const string &fileName) { std::ifstream fileStream(fileName); std::string privatekey((std::istreambuf_iterator(fileStream)), (std::istreambuf_iterator())); @@ -39,9 +39,9 @@ string signing::getKey(string fileName) { } /** - Get the public key for signing. + Get the public key for SigningHandler. */ -string signing::getPublicKey(string fileName) { +string SigningHandler::getPublicKey(const string &fileName) { std::ifstream fileStream(fileName); std::string privatekey((std::istreambuf_iterator(fileStream)), (std::istreambuf_iterator())); @@ -52,7 +52,7 @@ string signing::getPublicKey(string fileName) { /** Signs the JSON and returns a string token */ -string signing::sign(json data) { +string SigningHandler::sign(const json &data) { auto algo = jwt::algorithm::rs256(pubkey, key, "", ""); auto encode = [](const std::string& data) { auto base = base::encode(data); @@ -76,7 +76,7 @@ string signing::sign(json data) { /** Signs the JSON and returns a string token */ -string signing::sign(string data) { +string SigningHandler::sign(const string &data) { auto algo = jwt::algorithm::rs256(pubkey, key, "", ""); auto encode = [](const std::string& data) { auto base = base::encode(data); @@ -98,7 +98,7 @@ string signing::sign(string data) { } #ifdef UNIT_TEST -string signing::decode(string signedData) { +string SigningHandler::decode(string signedData) { auto verify = jwt::verify().allow_algorithm(jwt::algorithm::rs256(pubkey, "", "", "")); @@ -106,7 +106,7 @@ string signing::decode(string signedData) { try { verify.verify(decoded_token); } catch (exception& e) { - logger->Log(LogLevel::ERROR, "Error while verifying JSON signing " + string(e.what())); + logger->Log(LogLevel::ERROR, "Error while verifying JSON SigningHandler " + string(e.what())); return ""; } diff --git a/w3c-visserver-api/src/subscriptionhandler.cpp b/w3c-visserver-api/src/SubscriptionHandler.cpp similarity index 59% rename from w3c-visserver-api/src/subscriptionhandler.cpp rename to w3c-visserver-api/src/SubscriptionHandler.cpp index c5d57e1..709d5cb 100644 --- a/w3c-visserver-api/src/subscriptionhandler.cpp +++ b/w3c-visserver-api/src/SubscriptionHandler.cpp @@ -11,30 +11,29 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#include "subscriptionhandler.hpp" +#include "SubscriptionHandler.hpp" #include // usleep #include #include -#include "accesschecker.hpp" -#include "authenticator.hpp" +#include "AccessChecker.hpp" +#include "Authenticator.hpp" #include "exception.hpp" #include "visconf.hpp" -#include "vssdatabase.hpp" -#include "wsserver.hpp" +#include "WsChannel.hpp" +#include "VssDatabase.hpp" #include "ILogger.hpp" using namespace std; -// using namespace jsoncons; using namespace jsoncons::jsonpath; -// using jsoncons::jsoncons::jsoncons::json; +using jsoncons::json; -subscriptionhandler::subscriptionhandler(std::shared_ptr loggerUtil, - wsserver* wserver, - authenticator* authenticate, - accesschecker* checkAcc) { +SubscriptionHandler::SubscriptionHandler(std::shared_ptr loggerUtil, + std::shared_ptr wserver, + std::shared_ptr authenticate, + std::shared_ptr checkAcc) { logger = loggerUtil; server = wserver; validator = authenticate; @@ -42,17 +41,17 @@ subscriptionhandler::subscriptionhandler(std::shared_ptr loggerUtil, startThread(); } -subscriptionhandler::~subscriptionhandler() { +SubscriptionHandler::~SubscriptionHandler() { stopThread(); } -uint32_t subscriptionhandler::subscribe(wschannel& channel, - vssdatabase* db, - uint32_t channelID, string path) { +uint64_t SubscriptionHandler::subscribe(WsChannel& channel, + std::shared_ptr db, + const string &path) { // generate subscribe ID "randomly". - uint32_t subId = rand() % 9999999; + uint64_t subId = rand() % 9999999; // embed connection ID into subID. - subId = channelID + subId; + subId = channel.getConnID() + subId; bool isBranch = false; string jPath = db->getVSSSpecificPath(path, isBranch, db->data_tree); @@ -65,7 +64,6 @@ uint32_t subscriptionhandler::subscribe(wschannel& channel, throw noPermissionException(msg.str()); } - int clientID = channelID / CLIENT_MASK; jsoncons::json resArray = jsonpath::json_query(db->data_tree, jPath); if (resArray.is_array() && resArray.size() == 1) { @@ -74,15 +72,15 @@ uint32_t subscriptionhandler::subscribe(wschannel& channel, auto handle = subscribeHandle.find(sigUUID); if (handle != subscribeHandle.end()) { - logger->Log(LogLevel::VERBOSE, string("subscriptionhandler::subscribe: Updating the previous subscribe ") + logger->Log(LogLevel::VERBOSE, string("SubscriptionHandler::subscribe: Updating the previous subscribe ") + string("ID with a new one")); } - subscribeHandle[sigUUID][subId] = clientID; + subscribeHandle[sigUUID][subId] = channel.getConnID(); return subId; } else if (resArray.is_array()) { - logger->Log(LogLevel::INFO, "subscriptionhandler::subscribe :signals found in path" + path + logger->Log(LogLevel::INFO, "SubscriptionHandler::subscribe :signals found in path" + path + "Array size: " + to_string(resArray.size()) + ". Subscribe works for 1 signal at a time"); stringstream msg; @@ -90,7 +88,7 @@ uint32_t subscriptionhandler::subscribe(wschannel& channel, << ". Subscribe works for 1 signal at a time"; throw noPathFoundonTree(msg.str()); } else { - logger->Log(LogLevel::ERROR, string("subscriptionhandler::subscribe: some error occurred while adding ") + logger->Log(LogLevel::ERROR, string("SubscriptionHandler::subscribe: some error occurred while adding ") + string("subscription")); stringstream msg; msg << "some error occured while adding subscription for path = " << path; @@ -98,7 +96,7 @@ uint32_t subscriptionhandler::subscribe(wschannel& channel, } } -int subscriptionhandler::unsubscribe(uint32_t subscribeID) { +int SubscriptionHandler::unsubscribe(uint32_t subscribeID) { for (auto& uuid : subscribeHandle) { auto subscriptions = &(uuid.second); auto subscription = subscriptions->find(subscribeID); @@ -109,11 +107,11 @@ int subscriptionhandler::unsubscribe(uint32_t subscribeID) { return 0; } -int subscriptionhandler::unsubscribeAll(uint32_t connectionID) { +int SubscriptionHandler::unsubscribeAll(uint32_t connectionID) { for (auto& uuid : subscribeHandle) { auto subscriptions = &(uuid.second); for (auto& subscription : *subscriptions) { - if (subscription.second == (connectionID / CLIENT_MASK)) { + if (subscription.second == (connectionID)) { subscriptions->erase(subscription.first); } } @@ -121,29 +119,29 @@ int subscriptionhandler::unsubscribeAll(uint32_t connectionID) { return 0; } -int subscriptionhandler::updateByUUID(string UUID, jsoncons::json value) { - auto handle = subscribeHandle.find(UUID); +int SubscriptionHandler::updateByUUID(const string &signalUUID, + const jsoncons::json &value) { + auto handle = subscribeHandle.find(signalUUID); if (handle == subscribeHandle.end()) { // UUID not found return 0; } for (auto subID : handle->second) { - subMutex.lock(); - pair newSub; - newSub = std::make_pair(subID.first, value); + std::lock_guard lock(subMutex); + tuple newSub; + newSub = std::make_tuple(subID.first, subID.second, value); buffer.push(newSub); - subMutex.unlock(); } return 0; } -wsserver* subscriptionhandler::getServer() { +std::shared_ptr SubscriptionHandler::getServer() { return server; } -int subscriptionhandler::updateByPath(string path, json value) { +int SubscriptionHandler::updateByPath(const string &path, const json &value) { /* TODO: Implement */ (void) path; (void) value; @@ -151,23 +149,21 @@ int subscriptionhandler::updateByPath(string path, json value) { return 0; } -void* subscriptionhandler::subThreadRunner() { - // subscriptionhandler* handler = (subscriptionhandler*)instance; - +void* SubscriptionHandler::subThreadRunner() { logger->Log(LogLevel::VERBOSE, "SubscribeThread: Started Subscription Thread!"); while (isThreadRunning()) { - subMutex.lock(); if (buffer.size() > 0) { - pair newSub = buffer.front(); + std::lock_guard lock(subMutex); + auto newSub = buffer.front(); buffer.pop(); - uint32_t subID = newSub.first; - jsoncons::json value = newSub.second; + auto connId = std::get<1>(newSub); + jsoncons::json value = std::get<2>(newSub); jsoncons::json answer; answer["action"] = "subscribe"; - answer["subscriptionId"] = subID; + answer["subscriptionId"] = std::get<0>(newSub); answer.insert_or_assign("value", value); answer["timestamp"] = time(NULL); @@ -175,10 +171,8 @@ void* subscriptionhandler::subThreadRunner() { ss << pretty_print(answer); string message = ss.str(); - uint32_t connectionID = (subID / CLIENT_MASK) * CLIENT_MASK; - getServer()->sendToConnection(connectionID, message); + getServer()->SendToConnection(connId, message); } - subMutex.unlock(); // sleep 10 ms usleep(10000); } @@ -187,25 +181,17 @@ void* subscriptionhandler::subThreadRunner() { return NULL; } -int subscriptionhandler::startThread() { - subThread = thread(&subscriptionhandler::subThreadRunner, this); - /* - if (pthread_create(&subscription_thread, NULL, &subThread, this)) { - logger->Log(LogLevel::ERROR, "subscriptionhandler::startThread: Error creating subscription " - + "handler thread"); - return 1; - } - */ +int SubscriptionHandler::startThread() { + subThread = thread(&SubscriptionHandler::subThreadRunner, this); threadRun = true; return 0; } -int subscriptionhandler::stopThread() { - subMutex.lock(); +int SubscriptionHandler::stopThread() { + std::lock_guard lock(subMutex); threadRun = false; subThread.join(); - subMutex.unlock(); return 0; } -bool subscriptionhandler::isThreadRunning() { return threadRun; } +bool SubscriptionHandler::isThreadRunning() const { return threadRun; } diff --git a/w3c-visserver-api/src/vsscommandprocessor.cpp b/w3c-visserver-api/src/VssCommandProcessor.cpp similarity index 67% rename from w3c-visserver-api/src/vsscommandprocessor.cpp rename to w3c-visserver-api/src/VssCommandProcessor.cpp index 529b530..06472e2 100644 --- a/w3c-visserver-api/src/vsscommandprocessor.cpp +++ b/w3c-visserver-api/src/VssCommandProcessor.cpp @@ -12,7 +12,7 @@ * ***************************************************************************** */ -#include "vsscommandprocessor.hpp" +#include "VssCommandProcessor.hpp" #include #include @@ -22,116 +22,48 @@ #include "permmclient.hpp" #include "exception.hpp" #include "server_ws.hpp" + +#include "JsonResponses.hpp" #include "visconf.hpp" -#include "vssdatabase.hpp" -#include "accesschecker.hpp" -#include "subscriptionhandler.hpp" +#include "VssDatabase.hpp" +#include "AccessChecker.hpp" +#include "SubscriptionHandler.hpp" #include "ILogger.hpp" +#include "IVssDatabase.hpp" +#include "IAuthenticator.hpp" +#include "ISubscriptionHandler.hpp" #ifdef JSON_SIGNING_ON -#include "signing.hpp" +#include "SigningHandler.hpp" #endif using namespace std; -string malFormedRequestResponse(uint32_t request_id, const string action, string message) { - jsoncons::json answer; - answer["action"] = action; - answer["requestId"] = request_id; - jsoncons::json error; - error["number"] = 400; - error["reason"] = "Bad Request"; - error["message"] = message; - answer["error"] = error; - answer["timestamp"] = time(NULL); - stringstream ss; - ss << pretty_print(answer); - return ss.str(); -} - -string malFormedRequestResponse(string message) { - jsoncons::json answer; - jsoncons::json error; - - error["number"] = 400; - error["reason"] = "Bad Request"; - error["message"] = message; - answer["error"] = error; - answer["timestamp"] = time(NULL); - stringstream ss; - ss << pretty_print(answer); - return ss.str(); -} - -/** A API call requested a non-existant path */ -string pathNotFoundResponse(uint32_t request_id, const string action, - const string path) { - jsoncons::json answer; - answer["action"] = action; - answer["requestId"] = request_id; - jsoncons::json error; - error["number"] = 404; - error["reason"] = "Path not found"; - error["message"] = "I can not find " + path + " in my db"; - answer["error"] = error; - answer["timestamp"] = time(NULL); - stringstream ss; - ss << pretty_print(answer); - return ss.str(); -} - -string noAccessResponse(uint32_t request_id, const string action, - string message) { - jsoncons::json result; - jsoncons::json error; - result["action"] = action; - result["requestId"] = request_id; - error["number"] = 403; - error["reason"] = "Forbidden"; - error["message"] = message; - result["error"] = error; - result["timestamp"] = time(NULL); - std::stringstream ss; - ss << pretty_print(result); - return ss.str(); -} - -string valueOutOfBoundsResponse(uint32_t request_id, const string action, - const string message) { - jsoncons::json answer; - answer["action"] = action; - answer["requestId"] = request_id; - jsoncons::json error; - error["number"] = 400; - error["reason"] = "Value passed is out of bounds"; - error["message"] = message; - answer["error"] = error; - answer["timestamp"] = time(NULL); - stringstream ss; - ss << pretty_print(answer); - return ss.str(); -} - -vsscommandprocessor::vsscommandprocessor( +VssCommandProcessor::VssCommandProcessor( std::shared_ptr loggerUtil, - vssdatabase *dbase, - authenticator *vdator, - subscriptionhandler *subhandler) { + std::shared_ptr dbase, + std::shared_ptr vdator, + std::shared_ptr subhandler) { logger = loggerUtil; database = dbase; tokenValidator = vdator; subHandler = subhandler; - accessValidator = new accesschecker(tokenValidator); + // TODO: add accessValidator as dependency + accessValidator = std::make_shared(tokenValidator); #ifdef JSON_SIGNING_ON - signer = new signing(); + // TODO: add signer as dependency + signer = std::make_shared(); #endif } -vsscommandprocessor::~vsscommandprocessor() { - delete accessValidator; +VssCommandProcessor::~VssCommandProcessor() { + accessValidator.reset(); +#ifdef JSON_SIGNING_ON + signer.reset(); +#endif } -string vsscommandprocessor::processGet(wschannel &channel, +string VssCommandProcessor::processGet(WsChannel &channel, uint32_t request_id, string path) { logger->Log(LogLevel::VERBOSE, "GET :: path received from client = " + path); jsoncons::json res; @@ -139,10 +71,10 @@ string vsscommandprocessor::processGet(wschannel &channel, res = database->getSignal(channel, path); } catch (noPermissionException &nopermission) { logger->Log(LogLevel::ERROR, string(nopermission.what())); - return noAccessResponse(request_id, "get", nopermission.what()); + return JsonResponses::noAccess(request_id, "get", nopermission.what()); } if (!res.has_key("value")) { - return pathNotFoundResponse(request_id, "get", path); + return JsonResponses::pathNotFound(request_id, "get", path); } else { res["action"] = "get"; res["requestId"] = request_id; @@ -153,10 +85,10 @@ string vsscommandprocessor::processGet(wschannel &channel, } } -string vsscommandprocessor::processSet(wschannel &channel, +string VssCommandProcessor::processSet(WsChannel &channel, uint32_t request_id, string path, jsoncons::json value) { - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processSet: path received from client" + path); + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processSet: path received from client" + path); try { database->setSignal(channel, path, value); @@ -180,13 +112,13 @@ string vsscommandprocessor::processSet(wschannel &channel, return ss.str(); } catch (noPathFoundonTree &e) { logger->Log(LogLevel::ERROR, string(e.what())); - return pathNotFoundResponse(request_id, "set", path); + return JsonResponses::pathNotFound(request_id, "set", path); } catch (outOfBoundException &outofboundExp) { logger->Log(LogLevel::ERROR, string(outofboundExp.what())); - return valueOutOfBoundsResponse(request_id, "set", outofboundExp.what()); + return JsonResponses::valueOutOfBounds(request_id, "set", outofboundExp.what()); } catch (noPermissionException &nopermission) { logger->Log(LogLevel::ERROR, string(nopermission.what())); - return noAccessResponse(request_id, "set", nopermission.what()); + return JsonResponses::noAccess(request_id, "set", nopermission.what()); } jsoncons::json answer; @@ -199,25 +131,24 @@ string vsscommandprocessor::processSet(wschannel &channel, return ss.str(); } -string vsscommandprocessor::processSubscribe(wschannel &channel, - uint32_t request_id, string path, - uint32_t connectionID) { - logger->Log(LogLevel::VERBOSE, string("vsscommandprocessor::processSubscribe: path received from client ") +string VssCommandProcessor::processSubscribe(WsChannel &channel, + uint32_t request_id, string path) { + logger->Log(LogLevel::VERBOSE, string("VssCommandProcessor::processSubscribe: path received from client ") + string("for subscription")); uint32_t subId = -1; try { - subId = subHandler->subscribe(channel, database, connectionID, path); + subId = subHandler->subscribe(channel, database, path); } catch (noPathFoundonTree &noPathFound) { logger->Log(LogLevel::ERROR, string(noPathFound.what())); - return pathNotFoundResponse(request_id, "subscribe", path); + return JsonResponses::pathNotFound(request_id, "subscribe", path); } catch (genException &outofboundExp) { logger->Log(LogLevel::ERROR, string(outofboundExp.what())); - return valueOutOfBoundsResponse(request_id, "subscribe", + return JsonResponses::valueOutOfBounds(request_id, "subscribe", outofboundExp.what()); } catch (noPermissionException &nopermission) { logger->Log(LogLevel::ERROR, string(nopermission.what())); - return noAccessResponse(request_id, "subscribe", nopermission.what()); + return JsonResponses::noAccess(request_id, "subscribe", nopermission.what()); } if (subId > 0) { @@ -251,7 +182,7 @@ string vsscommandprocessor::processSubscribe(wschannel &channel, } } -string vsscommandprocessor::processUnsubscribe(uint32_t request_id, +string VssCommandProcessor::processUnsubscribe(uint32_t request_id, uint32_t subscribeID) { int res = subHandler->unsubscribe(subscribeID); if (res == 0) { @@ -284,7 +215,7 @@ string vsscommandprocessor::processUnsubscribe(uint32_t request_id, } } -string vsscommandprocessor::processGetMetaData(uint32_t request_id, +string VssCommandProcessor::processGetMetaData(uint32_t request_id, string path) { jsoncons::json st = database->getMetaData(path); @@ -300,10 +231,10 @@ string vsscommandprocessor::processGetMetaData(uint32_t request_id, return ss.str(); } -// Talks to the permission managent daemon and processes the token received. -string vsscommandprocessor::processAuthorizeWithPermManager(wschannel &channel, - uint32_t request_id, - string client, string clientSecret) { +// Talks to the permission management daemon and processes the token received. +string VssCommandProcessor::processAuthorizeWithPermManager(WsChannel &channel, + uint32_t request_id, + string client, string clientSecret) { jsoncons::json response; // Get Token from permission management daemon. @@ -366,8 +297,7 @@ string vsscommandprocessor::processAuthorizeWithPermManager(wschannel &channel, } } - -string vsscommandprocessor::processAuthorize(wschannel &channel, +string VssCommandProcessor::processAuthorize(WsChannel &channel, uint32_t request_id, string token) { tokenValidator->updatePubKey(""); @@ -402,8 +332,8 @@ string vsscommandprocessor::processAuthorize(wschannel &channel, } } -string vsscommandprocessor::processQuery(string req_json, - wschannel &channel) { +string VssCommandProcessor::processQuery(const string &req_json, + WsChannel &channel) { jsoncons::json root; string response; try { @@ -413,14 +343,14 @@ string vsscommandprocessor::processQuery(string req_json, if (action == "authorize") { string token = root["tokens"].as(); uint32_t request_id = root["requestId"].as(); - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: authorize query with token = " + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: authorize query with token = " + token + " with request id " + to_string(request_id)); response = processAuthorize(channel, request_id, token); } else if (action == "unsubscribe") { uint32_t request_id = root["requestId"].as(); uint32_t subscribeID = root["subscriptionId"].as(); - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: unsubscribe query for sub ID = " + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: unsubscribe query for sub ID = " + to_string(subscribeID) + " with request id " + to_string(request_id)); response = processUnsubscribe(request_id, subscribeID); @@ -438,7 +368,7 @@ string vsscommandprocessor::processQuery(string req_json, uint32_t request_id = root["requestId"].as(); if (action == "get") { - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: get query for " + path + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: get query for " + path + " with request id " + to_string(request_id)); response = processGet(channel, request_id, path); @@ -448,28 +378,28 @@ string vsscommandprocessor::processQuery(string req_json, } else if (action == "set") { jsoncons::json value = root["value"]; - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: set query for " + path + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: set query for " + path + " with request id " + to_string(request_id) + " value " + value.as_string()); response = processSet(channel, request_id, path, value); } else if (action == "subscribe") { - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: subscribe query for " + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: subscribe query for " + path + " with request id " + to_string(request_id)); response = - processSubscribe(channel, request_id, path, channel.getConnID()); + processSubscribe(channel, request_id, path); } else if (action == "getMetadata") { - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: metadata query for " + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: metadata query for " + path + " with request id " + to_string(request_id)); response = processGetMetaData(request_id, path); } else { - logger->Log(LogLevel::INFO, "vsscommandprocessor::processQuery: Unknown action " + action); + logger->Log(LogLevel::INFO, "VssCommandProcessor::processQuery: Unknown action " + action); } } } catch (jsoncons::json_parse_exception e) { - return malFormedRequestResponse(e.what()); + return JsonResponses::malFormedRequest(e.what()); } catch (jsoncons::key_not_found e) { - return malFormedRequestResponse(e.what()); + return JsonResponses::malFormedRequest(e.what()); } catch (jsoncons::not_an_object e) { - return malFormedRequestResponse(e.what()); + return JsonResponses::malFormedRequest(e.what()); } diff --git a/w3c-visserver-api/src/vssdatabase.cpp b/w3c-visserver-api/src/VssDatabase.cpp similarity index 83% rename from w3c-visserver-api/src/vssdatabase.cpp rename to w3c-visserver-api/src/VssDatabase.cpp index 2200041..2dac680 100644 --- a/w3c-visserver-api/src/vssdatabase.cpp +++ b/w3c-visserver-api/src/VssDatabase.cpp @@ -11,130 +11,152 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#include "vssdatabase.hpp" + +#include #include #include #include +#include + #include "exception.hpp" #include "visconf.hpp" #include "ILogger.hpp" - -#include - -#include "accesschecker.hpp" -#include "subscriptionhandler.hpp" +#include "IAccessChecker.hpp" +#include "ISubscriptionHandler.hpp" +#include "VssDatabase.hpp" using namespace std; using namespace jsoncons::jsonpath; +using jsoncons::json; namespace { + // Check if the value can be converted to requested output type + template + void ValidateValue(std::shared_ptr logger, const jsoncons::json &val, OutType &outVal) { + try{ + outVal = val.as(); + } + catch (exception &e) { + std::stringstream msg; + msg << "The input value '" + val.as_string() + "' is not valid!"; + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); + + throw notValidException(msg.str()); + } + } + // Check the value type and if the value is within the range void checkTypeAndBound(std::shared_ptr logger, string value_type, jsoncons::json val) { bool typeValid = false; + boost::algorithm::to_lower(value_type); + if (value_type == "uint8") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } - } else if (value_type == "uint16") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } - } else if (value_type == "uint32") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } - } else if (value_type == "int8") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } } else if (value_type == "int16") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } - } else if (value_type == "int32") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } } else if (value_type == "float") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); float max = numeric_limits::max(); float min = numeric_limits::lowest(); if (!((longDoubleVal <= max) && (longDoubleVal >= min))) { std::stringstream msg; msg << "The type " << value_type << " with value '" << val.as() << "' is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } } else if (value_type == "double") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); double max = numeric_limits::max(); double min = numeric_limits::lowest(); if (!((longDoubleVal <= max) && (longDoubleVal >= min))) { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } - } else if (value_type == "boolean") { typeValid = true; } else if (value_type == "string") { @@ -143,7 +165,7 @@ namespace { if (!typeValid) { string msg = "The type " + value_type + " is not supported "; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg); throw genException(msg); } @@ -157,7 +179,7 @@ namespace { string key) { if (!source.has_key("type")) { string msg = "Unknown type for signal found at " + key; - logger->Log(LogLevel::ERROR, "vssdatabase::setJsonValue : " + msg); + logger->Log(LogLevel::ERROR, "VssDatabase::setJsonValue : " + msg); throw genException(msg); } @@ -166,24 +188,24 @@ namespace { } // Constructor -vssdatabase::vssdatabase(std::shared_ptr loggerUtil, - subscriptionhandler* subHandle, - accesschecker* accValidator) { +VssDatabase::VssDatabase(std::shared_ptr loggerUtil, + std::shared_ptr subHandle, + std::shared_ptr accValidator) { logger = loggerUtil; subHandler = subHandle; accessValidator = accValidator; } -vssdatabase::~vssdatabase() {} +VssDatabase::~VssDatabase() {} // Initializer -void vssdatabase::initJsonTree(string fileName) { +void VssDatabase::initJsonTree(const string &fileName) { try { std::ifstream is(fileName); is >> data_tree; meta_tree = data_tree; - logger->Log(LogLevel::VERBOSE, "vssdatabase::vssdatabase : VSS tree initialized using JSON file = " + logger->Log(LogLevel::VERBOSE, "VssDatabase::VssDatabase : VSS tree initialized using JSON file = " + fileName); is.close(); } catch (exception const& e) { @@ -211,7 +233,7 @@ vector getPathTokens(string path) { // readable path. // Eg: jsonpath = $['Signal']['children']['OBD']['children']['RPM'] // The method returns Signal.OBD.RPM -string vssdatabase::getReadablePath(string jsonpath) { +string VssDatabase::getReadablePath(string jsonpath) { stringstream ss; // regex to remove special characters from JSONPath and make it VSS // compatible. @@ -234,7 +256,7 @@ string vssdatabase::getReadablePath(string jsonpath) { // Eg: path = Signal.OBD.RPM // The method returns $['Signal']['children']['OBD']['children']['RPM'] and // updates isBranch to false. -string vssdatabase::getVSSSpecificPath(string path, bool& isBranch, +string VssDatabase::getVSSSpecificPath(const string &path, bool& isBranch, jsoncons::json& tree) { vector tokens = getPathTokens(path); int tokLength = tokens.size(); @@ -264,12 +286,12 @@ string vssdatabase::getVSSSpecificPath(string path, bool& isBranch, isBranch = false; } else { isBranch = false; - logger->Log(LogLevel::ERROR, "vssdatabase::getVSSSpecificPath : Path " + logger->Log(LogLevel::ERROR, "VssDatabase::getVSSSpecificPath : Path " + format_path + " is invalid or is an empty tag!"); return ""; } } catch (exception& e) { - logger->Log(LogLevel::ERROR, "vssdatabase::getVSSSpecificPath :Exception \"" + logger->Log(LogLevel::ERROR, "VssDatabase::getVSSSpecificPath :Exception \"" + string(e.what()) + "\" occured while querying JSON. Check Path!"); isBranch = false; return ""; @@ -279,7 +301,7 @@ string vssdatabase::getVSSSpecificPath(string path, bool& isBranch, // Returns the absolute path but does not resolve wild card. Used only for // metadata request. -string vssdatabase::getPathForMetadata(string path, bool& isBranch) { +string VssDatabase::getPathForMetadata(string path, bool& isBranch) { string format_path = getVSSSpecificPath(path, isBranch, meta_tree); jsoncons::json pathRes = json_query(meta_tree, format_path, result_type::path); if (pathRes.size() > 0) { @@ -296,7 +318,7 @@ string vssdatabase::getPathForMetadata(string path, bool& isBranch) { // For eg : path = Signal.*.RPM // The method would return a list containing 1 signal path = // $['Signal']['children']['OBD']['children']['RPM'] -list vssdatabase::getPathForGet(string path, bool& isBranch) { +list VssDatabase::getPathForGet(const string &path, bool& isBranch) { list paths; string format_path = getVSSSpecificPath(path, isBranch, data_tree); jsoncons::json pathRes = json_query(data_tree, format_path, result_type::path); @@ -343,7 +365,7 @@ vector getVSSTokens(string path) { } // Returns the response JSON for metadata request. -jsoncons::json vssdatabase::getMetaData(string path) { +jsoncons::json VssDatabase::getMetaData(const std::string &path) { string format_path = "$"; bool isBranch = false; rwMutex.lock(); @@ -353,7 +375,7 @@ jsoncons::json vssdatabase::getMetaData(string path) { if (jPath == "") { return NULL; } - logger->Log(LogLevel::VERBOSE, "vssdatabase::getMetaData: VSS specific path =" + jPath); + logger->Log(LogLevel::VERBOSE, "VssDatabase::getMetaData: VSS specific path =" + jPath); vector tokens = getVSSTokens(jPath); int tokLength = tokens.size(); @@ -385,7 +407,7 @@ jsoncons::json vssdatabase::getMetaData(string path) { } } else { // handle exception. - logger->Log(LogLevel::ERROR, string("vssdatabase::getMetaData : More than 1 Branch/ value found! ") + logger->Log(LogLevel::ERROR, string("VssDatabase::getMetaData : More than 1 Branch/ value found! ") + string("Path requested needs to be more refined")); return NULL; } @@ -422,18 +444,20 @@ jsoncons::json vssdatabase::getMetaData(string path) { // The function would return = {{"path" : "$.Signal.children.OBD.children.RPM", // "value" : 23}, {"path" : "$.Signal.children.OBD.children.Speed", "value" : // 10}} -jsoncons::json vssdatabase::getPathForSet(string path, jsoncons::json values) { +jsoncons::json VssDatabase::getPathForSet(const string &path, jsoncons::json values) { jsoncons::json setValues; + string updatedPath = path; + if (values.is_array()) { - std::size_t found = path.find("*"); + std::size_t found = updatedPath.find("*"); if (found == std::string::npos) { - path = path + "."; - found = path.length(); + updatedPath = updatedPath + "."; + found = updatedPath.length(); } setValues = jsoncons::json::make_array(values.size()); for (size_t i = 0; i < values.size(); i++) { jsoncons::json value = values[i]; - string readablePath = path; + string readablePath = updatedPath; string replaceString; auto iter = value.object_range(); @@ -445,7 +469,7 @@ jsoncons::json vssdatabase::getPathForSet(string path, jsoncons::json values) { } } else { stringstream msg; - msg << "More than 1 signal found while setting for path = " << path + msg << "More than 1 signal found while setting for path = " << updatedPath << " with values " << pretty_print(values); throw genException(msg.str()); } @@ -456,7 +480,7 @@ jsoncons::json vssdatabase::getPathForSet(string path, jsoncons::json values) { getVSSSpecificPath(readablePath, isBranch, data_tree); if (isBranch) { stringstream msg; - msg << "Path = " << path << " with values " << pretty_print(values) + msg << "Path = " << updatedPath << " with values " << pretty_print(values) << " points to a branch. Needs to point to a signal"; throw genException(msg.str()); } @@ -469,10 +493,10 @@ jsoncons::json vssdatabase::getPathForSet(string path, jsoncons::json values) { setValues = jsoncons::json::make_array(1); jsoncons::json pathValue; bool isBranch = false; - string absolutePath = getVSSSpecificPath(path, isBranch, data_tree); + string absolutePath = getVSSSpecificPath(updatedPath, isBranch, data_tree); if (isBranch) { stringstream msg; - msg << "Path = " << path << " with values " << pretty_print(values) + msg << "Path = " << updatedPath << " with values " << pretty_print(values) << " points to a branch. Needs to point to a signal"; throw genException(msg.str()); } @@ -483,7 +507,7 @@ jsoncons::json vssdatabase::getPathForSet(string path, jsoncons::json values) { return setValues; } -void vssdatabase::checkSetPermission(wschannel& channel, jsoncons::json valueJson) { +void VssDatabase::checkSetPermission(WsChannel& channel, jsoncons::json valueJson) { // check if all the paths have write access. bool haveAccess = accessValidator->checkPathWriteAccess(channel, valueJson); if (!haveAccess) { @@ -494,7 +518,8 @@ void vssdatabase::checkSetPermission(wschannel& channel, jsoncons::json valueJso } // Method for setting values to signals. -void vssdatabase::setSignal(wschannel& channel, string path, +void VssDatabase::setSignal(WsChannel& channel, + const string &path, jsoncons::json valueJson) { if (path == "") { string msg = "Path is empty while setting"; @@ -510,10 +535,8 @@ void vssdatabase::setSignal(wschannel& channel, string path, for (size_t i = 0; i < setValues.size(); i++) { jsoncons::json item = setValues[i]; string jPath = item["path"].as(); -#ifdef DEBUG logger->Log(LogLevel::VERBOSE, "vssdatabase::setSignal: path found = " + jPath); logger->Log(LogLevel::VERBOSE, "value to set asstring = " + item["value"].as()); -#endif rwMutex.lock(); jsoncons::json resArray = json_query(data_tree, jPath); rwMutex.unlock(); @@ -529,9 +552,7 @@ void vssdatabase::setSignal(wschannel& channel, string path, rwMutex.lock(); json_replace(data_tree, jPath, resJson); rwMutex.unlock(); -#ifdef DEBUG logger->Log(LogLevel::VERBOSE, "vssdatabase::setSignal: new value set at path " + jPath); -#endif string uuid = resJson["uuid"].as(); @@ -558,7 +579,7 @@ void vssdatabase::setSignal(wschannel& channel, string path, } // Method for setting values to signals. -void vssdatabase::setSignal(string path, +void VssDatabase::setSignal(const string &path, jsoncons::json valueJson) { if (path == "") { string msg = "Path is empty while setting"; @@ -573,7 +594,7 @@ void vssdatabase::setSignal(string path, jsoncons::json item = setValues[i]; string jPath = item["path"].as(); - logger->Log(LogLevel::VERBOSE, "vssdatabase::setSignal: path found = " + jPath + logger->Log(LogLevel::VERBOSE, "VssDatabase::setSignal: path found = " + jPath + "value to set asstring = " + item["value"].as()); rwMutex.lock(); @@ -592,7 +613,7 @@ void vssdatabase::setSignal(string path, rwMutex.lock(); json_replace(data_tree, jPath, resJson); rwMutex.unlock(); - logger->Log(LogLevel::VERBOSE, "vssdatabase::setSignal: new value set at path " + jPath); + logger->Log(LogLevel::VERBOSE, "VssDatabase::setSignal: new value set at path " + jPath); string uuid = resJson["uuid"].as(); @@ -608,20 +629,20 @@ void vssdatabase::setSignal(string path, } else if (resArray.is_array()) { stringstream msg; msg << "Path " << jPath << " has " << resArray.size() << " signals, the path needs refinement"; - logger->Log(LogLevel::WARNING, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::WARNING, "VssDatabase::setSignal: " + msg.str()); throw genException(msg.str()); } } } else { string msg = "Exception occurred while setting data for " + path; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg); throw genException(msg); } } // Returns response JSON for get request. -jsoncons::json vssdatabase::getSignal(class wschannel& channel, string path) { +jsoncons::json VssDatabase::getSignal(class WsChannel& channel, const string &path) { bool isBranch = false; rwMutex.lock(); @@ -633,11 +654,11 @@ jsoncons::json vssdatabase::getSignal(class wschannel& channel, string path) { return answer; } - logger->Log(LogLevel::VERBOSE, "vssdatabase::getSignal: " + to_string(pathsFound) + logger->Log(LogLevel::VERBOSE, "VssDatabase::getSignal: " + to_string(pathsFound) + " signals found under path = \"" + path + "\""); if (isBranch) { jsoncons::json answer; - logger->Log(LogLevel::VERBOSE, " vssdatabase::getSignal : \"" + path + "\" is a Branch."); + logger->Log(LogLevel::VERBOSE, " VssDatabase::getSignal : \"" + path + "\" is a Branch."); if (pathsFound == 0) { throw noPathFoundonTree(path); diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp new file mode 100644 index 0000000..0bf73ae --- /dev/null +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -0,0 +1,1516 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "detect_ssl.hpp" +#include "ssl_stream.hpp" + +#include "WebSockHttpFlexServer.hpp" + +#include "IRestHandler.hpp" +#include "IVssCommandProcessor.hpp" +#include "WsChannel.hpp" +#include "ILogger.hpp" + +using RequestHandler = std::function; +using Listeners = std::vector>>; +using tcp = boost::asio::ip::tcp; // from +namespace ssl = boost::asio::ssl; // from +namespace http = boost::beast::http; // from +namespace websocket = boost::beast::websocket; // from + +namespace { + // forward declaration for classes that are defined below + class PlainWebsocketSession; + class SslWebsocketSession; + class PlainHttpSession; + class SslHttpSession; + class BeastListener; + +/** + * @class ConnectionHandler + * @brief Helper class to handle connection between \ref WsChannel and actual sessions + */ + class ConnectionHandler { + // Allow access to connection information from this file implementation details + friend class ::WebSockHttpFlexServer; + + private: + std::mutex mPlainWebSock_; + std::mutex mSslWebSock_; + std::mutex mPlainHttp_; + std::mutex mSslHttp_; + + std::unordered_map> connPlainWebSock_; + std::unordered_map> connSslWebSock_; + std::unordered_map> connPlainHttp_; + std::unordered_map> connSslHttp_; + + public: + ConnectionHandler() = default; + ~ConnectionHandler() = default; + + /** + * @brief Add new client for plain Web-Socket session + * @param session New session to add + * @return \ref WsChannel with new connection information + */ + WsChannel& AddClient(const PlainWebsocketSession * session) { + auto newChannel = std::make_shared(); + std::lock_guard lock(mPlainWebSock_); + + newChannel->setConnID(reinterpret_cast(session)); + newChannel->setType(WsChannel::Type::WEBSOCKET_PLAIN); + connPlainWebSock_[session] = newChannel; + return *newChannel; + } + + /** + * @brief Add new client for SSL Web-Socket session + * @param session New session to add + * @return \ref WsChannel with new connection information + */ + WsChannel& AddClient(const SslWebsocketSession * session) { + auto newChannel = std::make_shared(); + std::lock_guard lock(mSslWebSock_); + + newChannel->setConnID(reinterpret_cast(session)); + newChannel->setType(WsChannel::Type::WEBSOCKET_SSL); + connSslWebSock_[session] = newChannel; + + return *newChannel; + } + + /** + * @brief Add new client for plain HTTP session + * @param session New session to add + * @return \ref WsChannel with new connection information + */ + WsChannel& AddClient(const PlainHttpSession * session) { + auto newChannel = std::make_shared(); + std::lock_guard lock(mPlainHttp_); + + newChannel->setConnID(reinterpret_cast(session)); + newChannel->setType(WsChannel::Type::HTTP_PLAIN); + connPlainHttp_[session] = newChannel; + + return *newChannel; + } + + /** + * @brief Add new client for SSL HTTP session + * @param session New session to add + * @return \ref WsChannel with new connection information + */ + WsChannel& AddClient(const SslHttpSession * session) { + auto newChannel = std::make_shared(); + std::lock_guard lock(mSslHttp_); + + newChannel->setConnID(reinterpret_cast(session)); + newChannel->setType(WsChannel::Type::HTTP_SSL); + connSslHttp_[session] = newChannel; + + return *newChannel; + } + + /** + * @brief Remove client for plain Web-Socket session + * @param session Existing session to remove + */ + void RemoveClient(const PlainWebsocketSession * session) { + std::lock_guard lock(mPlainWebSock_); + connPlainWebSock_.erase(session); + } + + /** + * @brief Remove client for SSL Web-Socket session + * @param session Existing session to remove + */ + void RemoveClient(const SslWebsocketSession * session) { + std::lock_guard lock(mSslWebSock_); + connSslWebSock_.erase(session); + } + + /** + * @brief Remove client for plain HTTP session + * @param session Existing session to remove + */ + void RemoveClient(const PlainHttpSession * session) { + std::lock_guard lock(mPlainHttp_); + connPlainHttp_.erase(session); + } + + /** + * @brief Remove client for SSL HTTP session + * @param session Existing session to remove + */ + void RemoveClient(const SslHttpSession * session) { + std::lock_guard lock(mSslHttp_); + connSslHttp_.erase(session); + } + }; + + /**** Local variables ****/ + + // Boost.Beast helper state variables + ConnectionHandler connHandler; + std::shared_ptr connListener; + std::shared_ptr ioc; + ssl::context ctx{ssl::context::sslv23}; + std::vector iocRunners; + + /// Are allowed plain Web-socket/HTTP connections + bool allowInsecureConns = false; + + std::shared_ptr logger; + std::shared_ptr restHandler; + + const unsigned DEFAULT_TIMEOUT_VALUE = std::numeric_limits::max(); // in seconds + const unsigned WEBSOCKET_TIMEOUT_VALUE = DEFAULT_TIMEOUT_VALUE; + const unsigned HTTP_TIMEOUT_VALUE = DEFAULT_TIMEOUT_VALUE; + + + /**** Boost.Beast implementation below ****/ + + /// Report a failure + void + fail(boost::system::error_code ec, char const* what) + { + logger->Log(LogLevel::ERROR, std::string(what) + ": " + ec.message()); + } + + /// Report a failure and remove client from active connections that are tracked + template + void fail(const T* fromType, boost::system::error_code ec, char const* what) { + fail(ec, what); + logger->Log(LogLevel::ERROR, "Connection error detected, remove client from active connections"); + connHandler.RemoveClient(fromType); + } + + //------------------------------------------------------------------------------ + // This uses the Curiously Recurring Template Pattern so that + // the same code works with both SSL streams and regular sockets. + template + class WebSocketSession { + // Access the derived class, this is part of + // the Curiously Recurring Template Pattern idiom. + Derived& derived() { + return static_cast(*this); + } + + boost::beast::multi_buffer bufferRead_; + boost::beast::multi_buffer bufferWrite_; + char ping_state_ = 0; + + protected: + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + boost::asio::steady_timer timer_; + RequestHandler requestHandler_; + WsChannel channel; + std::list writeQueue_; + public: + // Construct the session + explicit WebSocketSession(boost::asio::io_context& ioc, + RequestHandler requestHandler) + : strand_(ioc.get_executor()) + , timer_(ioc, + (std::chrono::steady_clock::time_point::max)()) + , requestHandler_(requestHandler) { + } + + // Start the asynchronous operation + template + void doAccept(http::request> req) { + // Set the control callback. This will be called + // on every incoming ping, pong, and close frame. + derived().ws().control_callback( + std::bind( + &WebSocketSession::on_control_callback, + this, + std::placeholders::_1, + std::placeholders::_2)); + + // Set the timer + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); + + // Accept the websocket handshake + derived().ws().async_accept( + req, + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onAccept, + derived().shared_from_this(), + std::placeholders::_1))); + } + + void onAccept(boost::system::error_code ec) { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(&derived(), ec, "accept"); + return; + } + + // Read a message + doRead(); + } + + // Called when the timer expires. + void onTimer(boost::system::error_code ec) { + if(ec && ec != boost::asio::error::operation_aborted) { + fail<>(&derived(), ec, "timer"); + return; + } + + // See if the timer really expired since the deadline may have moved. + if(timer_.expiry() <= std::chrono::steady_clock::now()) { + // If this is the first time the timer expired, + // send a ping to see if the other end is there. + if(derived().ws().is_open() && ping_state_ == 0) { + // Note that we are sending a ping + ping_state_ = 1; + + // Set the timer + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); + + // Now send the ping + derived().ws().async_ping({}, + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onPing, + derived().shared_from_this(), + std::placeholders::_1))); + } + else { + // The timer expired while trying to handshake, + // or we sent a ping and it never completed or + // we never got back a control frame, so close. + + derived().doTimeout(); + return; + } + } + + // Wait on the timer + timer_.async_wait( + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onTimer, + derived().shared_from_this(), + std::placeholders::_1))); + } + + // Called to indicate activity from the remote peer + void activity() { + // Note that the connection is alive + ping_state_ = 0; + + // Set the timer + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); + } + + // Called after a ping is sent. + void onPing(boost::system::error_code ec) { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(&derived(), ec, "ping"); + return; + } + + // Note that the ping was sent. + if(ping_state_ == 1) { + ping_state_ = 2; + } + else { + // ping_state_ could have been set to 0 + // if an incoming control frame was received + // at exactly the same time we sent a ping. + BOOST_ASSERT(ping_state_ == 0); + } + } + + void on_control_callback( + websocket::frame_type kind, + boost::beast::string_view payload) { + boost::ignore_unused(kind, payload); + + // Note that there is activity + activity(); + } + + void doRead() { + // Read a message into our buffer + derived().ws().async_read( + bufferRead_, + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onRead, + derived().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + + void onRead(boost::system::error_code ec, std::size_t bytesTransferred) { + boost::ignore_unused(bytesTransferred); + + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + // This indicates that the websocket_session was closed + if(ec == websocket::error::closed) + return; + + if(ec) + fail(ec, "read"); + + // Note that there is activity + activity(); + + derived().ws().text(derived().ws().got_text()); + + std::string response = requestHandler_(boost::beast::buffers_to_string(bufferRead_.data()), channel); + bufferRead_.consume(bytesTransferred); // clear existing buffer data + + // send response + write(response); + + // do another read + doRead(); + } + + void write(const std::string &message) { + writeQueue_.push_back(message); + + // there can be only one async_write request at any single time, + // so queue additional transfers + if (writeQueue_.size() > 1) { + return; + } + + boost::asio::buffer_copy(bufferWrite_.prepare(message.size()), boost::asio::buffer(message)); + bufferWrite_.commit(message.size()); // commit copied data for write + + // send message + derived().ws().async_write( + bufferWrite_.data(), + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onWrite, + derived().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + + void onWrite(boost::system::error_code ec, std::size_t bytesTransferred) { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(&derived(), ec, "write"); + return; + } + + // Clear the buffer + bufferWrite_.consume(bytesTransferred); + + writeQueue_.pop_front(); + + // check if there is more to write + if (!writeQueue_.empty()) { + auto message = writeQueue_.front(); + boost::asio::buffer_copy(bufferWrite_.prepare(message.size()), boost::asio::buffer(message)); + bufferWrite_.commit(message.size()); // commit copied data for write + + // send message + derived().ws().async_write( + bufferWrite_.data(), + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onWrite, + derived().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + } + }; + + // Handles a plain WebSocket connection + class PlainWebsocketSession : public WebSocketSession, + public std::enable_shared_from_this { + websocket::stream ws_; + bool close_ = false; + + public: + // Create the session + explicit PlainWebsocketSession(tcp::socket socket, RequestHandler requestHandler) + : WebSocketSession(socket.get_executor().context(), requestHandler), + ws_(std::move(socket)) { + } + + // Called by the base class + websocket::stream& ws() { + return ws_; + } + + // Start the asynchronous operation + template + void run(http::request> req) { + channel = connHandler.AddClient(this); + + // Run the timer. The timer is operated + // continuously, this simplifies the code. + onTimer({}); + + // Accept the WebSocket upgrade request + doAccept(std::move(req)); + } + + void doTimeout() { + // This is so the close can have a timeout + if(close_) + return; + close_ = true; + + // Set the timer + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); + + // Close the WebSocket Connection + ws_.async_close( + websocket::close_code::normal, + boost::asio::bind_executor( + strand_, + std::bind( + &PlainWebsocketSession::on_close, + shared_from_this(), + std::placeholders::_1))); + } + + void on_close(boost::system::error_code ec) { + // Happens when close times out + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(this, ec, "close"); + return; + } + + // At this point the connection is gracefully closed + } + }; + + // Handles an SSL WebSocket connection + class SslWebsocketSession : public WebSocketSession, + public std::enable_shared_from_this { + websocket::stream> ws_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + bool eof_ = false; + + public: + // Create the http_session + explicit SslWebsocketSession(ssl_stream stream, RequestHandler requestHandler) + : WebSocketSession( + stream.get_executor().context(), requestHandler) + , ws_(std::move(stream)) + , strand_(ws_.get_executor()) { + } + + // Called by the base class + websocket::stream>& + ws() + { + return ws_; + } + + // Start the asynchronous operation + template + void run(http::request> req) { + channel = connHandler.AddClient(this); + + // Run the timer. The timer is operated + // continuously, this simplifies the code. + onTimer({}); + + // Accept the WebSocket upgrade request + doAccept(std::move(req)); + } + + void doEof() { + eof_ = true; + + // Set the timer + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); + + // Perform the SSL shutdown + ws_.next_layer().async_shutdown( + boost::asio::bind_executor( + strand_, + std::bind( + &SslWebsocketSession::onShutdown, + shared_from_this(), + std::placeholders::_1))); + } + + void onShutdown(boost::system::error_code ec) { + // Happens when the shutdown times out + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(this, ec, "shutdown"); + return; + } + + connHandler.RemoveClient(this); + // At this point the connection is closed gracefully + } + + void doTimeout() { + // If this is true it means we timed out performing the shutdown + if(eof_) + return; + + // Start the timer again + timer_.expires_at( + (std::chrono::steady_clock::time_point::max)()); + onTimer({}); + doEof(); + } + }; + + template + void makeWebsocketSession(tcp::socket socket, + http::request> req, + RequestHandler requestHandler) { + std::make_shared( + std::move(socket), requestHandler)->run(std::move(req)); + } + + template + void makeWebsocketSession(ssl_stream stream, + http::request> req, + RequestHandler requestHandler) { + std::make_shared( + std::move(stream), requestHandler)->run(std::move(req)); + } + + //------------------------------------------------------------------------------ + + // Handles an HTTP server connection. + // This uses the Curiously Recurring Template Pattern so that + // the same code works with both SSL streams and regular sockets. + template + class HttpSession { + // Access the derived class, this is part of + // the Curiously Recurring Template Pattern idiom. + Derived& derived() { + return static_cast(*this); + } + + // This queue is used for HTTP pipelining. + class queue { + enum { + // Maximum number of responses we will queue + limit = 8 + }; + + // The type-erased, saved work item + struct work { + virtual ~work() = default; + virtual void operator()() = 0; + }; + + HttpSession& self_; + std::vector> items_; + + public: + explicit queue(HttpSession& self) : self_(self) { + static_assert(limit > 0, "queue limit must be positive"); + items_.reserve(limit); + } + + // Returns `true` if we have reached the queue limit + bool is_full() const { + return items_.size() >= limit; + } + + // Called when a message finishes sending + // Returns `true` if the caller should initiate a read + bool onWrite() { + BOOST_ASSERT(! items_.empty()); + auto const was_full = is_full(); + items_.erase(items_.begin()); + if(! items_.empty()) + (*items_.front())(); + return was_full; + } + + // Called by the HTTP handler to send a response. + template + void operator()(http::message&& msg) { + // This holds a work item + struct work_impl : work { + HttpSession& self_; + http::message msg_; + + work_impl( + HttpSession& self, + http::message&& msg) + : self_(self) + , msg_(std::move(msg)) { + } + + void + operator()() { + http::async_write( + self_.derived().stream(), + msg_, + boost::asio::bind_executor( + self_.strand_, + std::bind( + &HttpSession::onWrite, + self_.derived().shared_from_this(), + std::placeholders::_1, + msg_.need_eof()))); + } + }; + + // Allocate and store the work + items_.push_back( + boost::make_unique(self_, std::move(msg))); + + // If there was no previous work, start this one + if(items_.size() == 1) + (*items_.front())(); + } + }; + + std::string const& doc_root_; + http::request req_; + queue queue_; + + protected: + boost::asio::steady_timer timer_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + boost::beast::flat_buffer bufferRead_; + RequestHandler requestHandler_; + WsChannel channel; + + public: + // Construct the session + HttpSession(boost::asio::io_context& ioc, + boost::beast::flat_buffer buffer, + std::string const& doc_root, + RequestHandler requestHandler) + : doc_root_(doc_root) + , queue_(*this) + , timer_(ioc, + (std::chrono::steady_clock::time_point::max)()) + , strand_(ioc.get_executor()) + , bufferRead_(std::move(buffer)) + , requestHandler_(requestHandler) { + } + + void doRead() { + // Set the timer + timer_.expires_after(std::chrono::seconds(HTTP_TIMEOUT_VALUE)); + + // Make the request empty before reading, + // otherwise the operation behavior is undefined. + req_ = {}; + + // Read a request + http::async_read( + derived().stream(), + bufferRead_, + req_, + boost::asio::bind_executor( + strand_, + std::bind( + &HttpSession::onRead, + derived().shared_from_this(), + std::placeholders::_1))); + } + + // Called when the timer expires. + void onTimer(boost::system::error_code ec) { + if(ec && ec != boost::asio::error::operation_aborted) { + fail<>(&derived(), ec, "timer"); + return; + } + + // Verify that the timer really expired since the deadline may have moved. + if(timer_.expiry() <= std::chrono::steady_clock::now()) + return derived().doTimeout(); + + // Wait on the timer + timer_.async_wait( + boost::asio::bind_executor( + strand_, + std::bind( + &HttpSession::onTimer, + derived().shared_from_this(), + std::placeholders::_1))); + } + + void onRead(boost::system::error_code ec) { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + // This means they closed the connection + if(ec == http::error::end_of_stream) + return derived().doEof(); + + if(ec) + fail(ec, "read"); + + // See if it is a WebSocket Upgrade + if(websocket::is_upgrade(req_)) { + // Transfer the stream to a new WebSocket session + return makeWebsocketSession( + derived().release_stream(), + std::move(req_), requestHandler_); + } + + // Otherwise handle pure HTTP connection + std::string jsonRequest; + + // if we got correct JSON request back, send it to handler + auto res = restHandler->GetJson(std::string(req_.method_string()), + std::string(req_.target()), + jsonRequest); + + // regex for extracting HTTP status code from error response + const std::regex getErrNumber("\"number\":\\ +(\\d+)"); + std::smatch sm; + std::string response; + + // HTTP response object + http::response httpResponse{ + std::piecewise_construct, + std::make_tuple(""), + std::make_tuple(http::status::ok, req_.version())}; + + // HTTP 'OPTIONS' method is handled differently to support CORS pre-flight checks from browsers + if (req_.method_string().compare("OPTIONS") != 0) { + // if all ok, process further generated JSON request + if (res) { + response = requestHandler_(jsonRequest, channel); + + // check if error was returned so we can set correct HTTP response status + std::regex_search (response, sm, getErrNumber); + if (sm.size()) { + // set error response + httpResponse.result(std::stoi(sm.str(1))); + } + httpResponse.body() = response; + } + // if getting of JSON request failed, set error HTTP response + else { + // check if there was error so we can set correct HTTP response status + std::regex_search (jsonRequest, sm, getErrNumber); + if (sm.size()) { + // set error response + httpResponse.result(std::stoi(sm.str(1))); + } + httpResponse.body() = jsonRequest; + } + } + // handle HTTP 'OPTIONS' method + else { + // if all ok, prepare OPTIONS response + if (res) { + auto jsonReq = jsoncons::json::parse(jsonRequest); + httpResponse.set(http::field::access_control_allow_methods, jsonReq["methods"].as_string()); + httpResponse.set(http::field::access_control_allow_headers, jsonReq["headers"].as_string()); + httpResponse.set(http::field::access_control_max_age, jsonReq["max-age"].as_string()); + } + else { + // handle error + httpResponse.body() = jsonRequest; + // check if there was error so we can set correct HTTP response status + std::regex_search (jsonRequest, sm, getErrNumber); + if (sm.size()) { + // set error response + httpResponse.result(std::stoi(sm.str(1))); + } + } + } + httpResponse.set(http::field::server, BOOST_BEAST_VERSION_STRING); + httpResponse.set(http::field::content_type, "application/json"); + + // allow cross-domain calls with setting below header + // TODO: evaluate this header for production level SW + httpResponse.set(http::field::access_control_allow_origin, "*"); + + httpResponse.keep_alive(req_.keep_alive()); + httpResponse.prepare_payload(); + + // add response object in transmit queue + queue_(std::move(httpResponse)); + + // If we aren't at the queue limit, try to pipeline another request + if(! queue_.is_full()) + doRead(); + } + + void onWrite(boost::system::error_code ec, bool close) { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return;// provide error JSON + + if(ec) { + fail<>(&derived(), ec, "write"); + return; + } + + if(close) { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + return derived().doEof(); + } + + // Inform the queue that a write completed + if(queue_.onWrite()) { + // Read another request + doRead(); + } + } + }; + + // Handles a plain HTTP connection + class PlainHttpSession : public HttpSession, + public std::enable_shared_from_this { + tcp::socket socket_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + + public: + // Create the http_session + PlainHttpSession(tcp::socket socket, + boost::beast::flat_buffer buffer, + std::string const& doc_root, + RequestHandler requestHandler) + : HttpSession( + socket.get_executor().context(), + std::move(buffer), + doc_root, + requestHandler) + , socket_(std::move(socket)) + , strand_(socket_.get_executor()) { + } + + // Called by the base class + tcp::socket& stream() { + return socket_; + } + + // Called by the base class + tcp::socket release_stream() { + return std::move(socket_); + } + + // Start the asynchronous operation + void run() { + channel = connHandler.AddClient(this); + + // Run the timer. The timer is operated + // continuously, this simplifies the code. + onTimer({}); + + doRead(); + } + + void doEof() { + // Send a TCP shutdown + boost::system::error_code ec; + socket_.shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully + } + + void doTimeout() { + // Closing the socket cancels all outstanding operations. They + // will complete with boost::asio::error::operation_aborted + boost::system::error_code ec; + socket_.shutdown(tcp::socket::shutdown_both, ec); + socket_.close(ec); + } + }; + + // Handles an SSL HTTP connection + class SslHttpSession : public HttpSession, + public std::enable_shared_from_this { + ssl_stream stream_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + bool eof_ = false; + + public: + // Create the http_session + SslHttpSession(tcp::socket socket, + ssl::context& ctx, + boost::beast::flat_buffer buffer, + std::string const& doc_root, + RequestHandler requestHandler) + : HttpSession( + socket.get_executor().context(), + std::move(buffer), + doc_root, + requestHandler) + , stream_(std::move(socket), ctx) + , strand_(stream_.get_executor()) { + } + + // Called by the base class + ssl_stream& stream() { + return stream_; + } + + // Called by the base class + ssl_stream release_stream() { + return std::move(stream_); + } + + // Start the asynchronous operation + void run() { + channel = connHandler.AddClient(this); + + // Run the timer. The timer is operated + // continuously, this simplifies the code. + /* TODO: There is a crash when using SSL to communicate + * when line below is un-commented.. Evaluate later + */ + //onTimer({}); + + // Set the timer + timer_.expires_after(std::chrono::seconds(HTTP_TIMEOUT_VALUE)); + + // Perform the SSL handshake + // Note, this is the buffered version of the handshake. + stream_.async_handshake( + ssl::stream_base::server, + bufferRead_.data(), + boost::asio::bind_executor( + strand_, + std::bind( + &SslHttpSession::onHandshake, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + void onHandshake(boost::system::error_code ec, + std::size_t bytes_used) { + // Happens when the handshake times out + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) + return fail(ec, "handshake"); + + // Consume the portion of the buffer used by the handshake + bufferRead_.consume(bytes_used); + + doRead(); + } + + void doEof() { + eof_ = true; + + // Set the timer + timer_.expires_after(std::chrono::seconds(HTTP_TIMEOUT_VALUE)); + + // Perform the SSL shutdown + stream_.async_shutdown( + boost::asio::bind_executor( + strand_, + std::bind( + &SslHttpSession::onShutdown, + shared_from_this(), + std::placeholders::_1))); + } + + void onShutdown(boost::system::error_code ec) { + // Happens when the shutdown times out + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) + return fail(ec, "shutdown"); + + connHandler.RemoveClient(this); + // At this point the connection is closed gracefully + } + + void doTimeout() { + // If this is true it means we timed out performing the shutdown + if(eof_) + return; + + // Start the timer again + timer_.expires_at( + (std::chrono::steady_clock::time_point::max)()); + onTimer({}); + //doEof(); + } + }; + + //------------------------------------------------------------------------------ + // Detects SSL handshakes + class DetectSession : public std::enable_shared_from_this { + tcp::socket socket_; + ssl::context& ctx_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + std::string const& doc_root_; + boost::beast::flat_buffer buffer_; + RequestHandler requestHandler_; + + public: + explicit DetectSession(tcp::socket socket, + ssl::context& ctx, + std::string const& doc_root, + RequestHandler requestHandler) + : socket_(std::move(socket)) + , ctx_(ctx) + , strand_(socket_.get_executor()) + , doc_root_(doc_root) + , requestHandler_(requestHandler) { + } + + // Launch the detector + void run() { + async_detect_ssl( + socket_, + buffer_, + boost::asio::bind_executor( + strand_, + std::bind( + &DetectSession::onDetect, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + + void onDetect(boost::system::error_code ec, boost::tribool result) { + if(ec) + return fail(ec, "detect"); + + if(result) + { + // Launch SSL session + std::make_shared( + std::move(socket_), + ctx_, + std::move(buffer_), + doc_root_, + requestHandler_)->run(); + return; + } + + if (allowInsecureConns) + { + // Launch plain session + std::make_shared( + std::move(socket_), + std::move(buffer_), + doc_root_, + requestHandler_)->run(); + } + else + { + logger->Log(LogLevel::ERROR, "Plain (non-SSL) connection not allowed. Stopping connection."); + } + } + }; + + // Accepts incoming connections and launches the sessions + class BeastListener : public std::enable_shared_from_this { + ssl::context& ctx_; + tcp::acceptor acceptor_; + tcp::socket socket_; + std::string const& doc_root_; + RequestHandler requestHandler_; + + public: + BeastListener(boost::asio::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + std::string const& doc_root, + RequestHandler requestHandler) + : ctx_(ctx) + , acceptor_(ioc) + , socket_(ioc) + , doc_root_(doc_root) + , requestHandler_(requestHandler) { + boost::system::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(boost::asio::socket_base::reuse_address(true)); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + boost::asio::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void run() { + if(! acceptor_.is_open()) + return; + doAccept(); + } + + void doAccept() { + acceptor_.async_accept( + socket_, + std::bind( + &BeastListener::onAccept, + shared_from_this(), + std::placeholders::_1)); + } + + void onAccept(boost::system::error_code ec) { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the detector http_session and run it + std::make_shared( + std::move(socket_), + ctx_, + doc_root_, + requestHandler_)->run(); + } + + // Accept another connection + doAccept(); + } + }; +} // end namespace + + +const std::string WebSockHttpFlexServer::serverCertFilename_ = "Server.pem"; +const std::string WebSockHttpFlexServer::serverKeyFilename_ = "Server.key"; + + +WebSockHttpFlexServer::WebSockHttpFlexServer(std::shared_ptr loggerUtil, + std::shared_ptr restHandlerUtil) + : logger_(loggerUtil), + restHandler_(restHandlerUtil) { + logger = logger_; + restHandler = restHandler_; +} + +WebSockHttpFlexServer::~WebSockHttpFlexServer() { + ioc->stop(); // stop execution of io runner + + // wait to finish + for(auto& thread : iocRunners) { + thread.join(); + } +} +void WebSockHttpFlexServer::Initialize(std::string host, + int port, + std::string && docRoot, + std::string certPath, + bool allowInsecure) { + logger_->Log(LogLevel::INFO, "Initializing Boost.Beast web-socket and http server"); + + docRoot_ = docRoot; + + allowInsecureConns = allowInsecure; + + ioc = std::make_shared(NumOfThreads); + ctx.set_options(ssl::context::default_workarounds); + + boost::asio::ip::tcp::resolver resolver{*ioc}; + boost::asio::ip::tcp::resolver::query query(host, to_string(port)); + boost::asio::ip::tcp::resolver::iterator resolvedHost = resolver.resolve(query); + + // load required certificates for SSL connections + LoadCertData(certPath, ctx); + + // handling function redirecting requests to registered listeners + RequestHandler reqHndl = std::bind(&WebSockHttpFlexServer::HandleRequest, + this, + std::placeholders::_1, + std::placeholders::_2); + + // create listener for handling incoming connections + connListener = std::make_shared( + *ioc, + ctx, + resolvedHost->endpoint(), + std::move(docRoot_), + std::bind(&WebSockHttpFlexServer::HandleRequest, + this, + std::placeholders::_1, + std::placeholders::_2)); +} + +std::string WebSockHttpFlexServer::HandleRequest(const std::string &req_json, WsChannel &channel) { + std::string response; + auto const type = channel.getType(); + ObserverType handlerType; + + if ((type == WsChannel::Type::WEBSOCKET_PLAIN) || + (type == WsChannel::Type::WEBSOCKET_SSL )) + { + handlerType = ObserverType::WEBSOCKET; + } + else + { + handlerType = ObserverType::HTTP; + } + + for (auto const& handler : listeners_) + { + if ((handler.first == ObserverType::ALL) || (handler.first == handlerType)) + { + response = handler.second->processQuery(req_json, channel); + } + } + + return response; +} + +void WebSockHttpFlexServer::AddListener(ObserverType type, + std::shared_ptr listener) { + listeners_.push_back(std::make_pair(type, listener)); +} + +void WebSockHttpFlexServer::RemoveListener(ObserverType type, + std::shared_ptr listener) { + auto listenerToRemove = std::make_pair(type, listener); + + for (auto item = std::begin(listeners_); item < std::end(listeners_); ++item) + { + if (*item == listenerToRemove) + { + listeners_.erase(item); + return; + } + } + + logger_->Log(LogLevel::WARNING, "Could not find listener to remove. Ignoring..."); +} + +void WebSockHttpFlexServer::LoadCertData(std::string & certPath, boost::asio::ssl::context& ctx) { + std::string cert; + std::string key; + std::string line; + bool isValid = false; + + std::string delimiter = "/"; + + // if path not passed with '/' at the end, no update needed + // otherwise we need to provide path delimiter + if (certPath[certPath.size() - 1] == '/') + { + delimiter = ""; + } + + // read certificate + { + std::ifstream inputFile (certPath + delimiter + serverCertFilename_); + isValid = inputFile.good(); + if (isValid && inputFile.is_open()) + { + while (getline(inputFile, line) ) + { + cert.append(line); + cert.append("\n"); + } + inputFile.close(); + } + else { + throw std::runtime_error("Could not load server certificate"); + } + } + // read key + { + std::ifstream inputFile (certPath + delimiter + serverKeyFilename_); + isValid = inputFile.good(); + if (isValid && inputFile.is_open()) + { + while (getline(inputFile, line) ) + { + key.append(line); + key.append("\n"); + } + inputFile.close(); + } + else { + throw std::runtime_error("Could not load server key"); + } + } + + // add cert and key to security context for using with new connections + ctx.use_certificate_chain( + boost::asio::buffer(cert.data(), cert.size())); + + ctx.use_private_key( + boost::asio::buffer(key.data(), key.size()), + boost::asio::ssl::context::file_format::pem); + + isInitialized = true; +} + +void WebSockHttpFlexServer::SendToConnection(uint64_t connID, const std::string &message) { + if (!isInitialized) + { + std::string err("Cannot send to connection, server not initialized!"); + logger_->Log(LogLevel::ERROR, err); + throw std::runtime_error(err); + } + + bool isFound = false; + + // try to find active connection to send data to + + if (!isFound) { + auto session = reinterpret_cast(connID); + std::lock_guard lock(connHandler.mPlainWebSock_); + auto iter = connHandler.connPlainWebSock_.find(session); + if (iter != std::end(connHandler.connPlainWebSock_)) + { + isFound = true; + session->write(message); + } + } + if (!isFound) { + auto session = reinterpret_cast(connID); + std::lock_guard lock(connHandler.mSslWebSock_); + auto iter = connHandler.connSslWebSock_.find(session); + if (iter != std::end(connHandler.connSslWebSock_)) + { + isFound = true; + session->write(message); + } + } + if (!isFound) { + auto session = reinterpret_cast(connID); + std::lock_guard lock(connHandler.mPlainHttp_); + auto iter = connHandler.connPlainHttp_.find(session); + if (iter != std::end(connHandler.connPlainHttp_)) + { + isFound = true; + // TODO: check how we are going to handle ASYNC writes on HTTP connection? + } + } + if (!isFound) { + auto session = reinterpret_cast(connID); + std::lock_guard lock(connHandler.mSslHttp_); + auto iter = connHandler.connSslHttp_.find(session); + if (iter != std::end(connHandler.connSslHttp_)) + { + isFound = true; + // TODO: check how we are going to handle ASYNC writes on HTTP connection? + } + } +} + +void WebSockHttpFlexServer::Start() { + if (!isInitialized) + { + std::string err("Cannot start server, server not initialized!"); + logger_->Log(LogLevel::ERROR, err); + throw std::runtime_error(err); + } + + logger_->Log(LogLevel::INFO, "Starting Boost.Beast web-socket and http server"); + + // start listening for connections + connListener->run(); + + // run the I/O service on the requested number of threads + iocRunners.reserve(NumOfThreads); + for(auto i = 0; i < NumOfThreads; ++i) { + iocRunners.emplace_back( + [] + { + boost::system::error_code ec; + ioc->run(ec); + }); + } +} diff --git a/w3c-visserver-api/src/wsserver.cpp b/w3c-visserver-api/src/WsServer.cpp similarity index 69% rename from w3c-visserver-api/src/wsserver.cpp rename to w3c-visserver-api/src/WsServer.cpp index 23c8118..bcbf08e 100644 --- a/w3c-visserver-api/src/wsserver.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -11,25 +11,22 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#include "wsserver.hpp" +#include "WsServer.hpp" #include "ILogger.hpp" -#include "accesschecker.hpp" -#include "authenticator.hpp" -#include "subscriptionhandler.hpp" +#include "IVssCommandProcessor.hpp" #include "visconf.hpp" -#include "vsscommandprocessor.hpp" -#include "vssdatabase.hpp" #include +#include using namespace std; -using WssServer = SimpleWeb::SocketServer; -using WsServer = SimpleWeb::SocketServer; +using SecuredServer = SimpleWeb::SocketServer; +using SimpleServer = SimpleWeb::SocketServer; uint16_t connections[MAX_CLIENTS + 1] = {0}; -wsserver *wserver; +WsServer *wserver = NULL; uint32_t generateConnID() { uint32_t retValueValue = 0; @@ -43,35 +40,42 @@ uint32_t generateConnID() { return retValueValue; } -wsserver::wsserver(std::shared_ptr loggerUtil, int port, string configFileName, bool secure) { - logger = loggerUtil; - isSecure_ = secure; +WsServer::WsServer() +{ + isSecure_ = false; secureServer_ = nullptr; insecureServer_ = nullptr; - configFileName_ = configFileName; + isInitialized_ = false; + + wserver = this; +} + +bool WsServer::Initialize(std::shared_ptr loggerUtil, + std::shared_ptr processor, + bool secure, + int port) +{ + logger = loggerUtil; + cmdProcessor = processor; + isSecure_ = secure; + + // TODO: pass server instance as dependency if (isSecure_) { - secureServer_ = new WssServer("Server.pem", "Server.key"); + secureServer_ = new SecuredServer("Server.pem", "Server.key"); secureServer_->config.port = port; } else { - insecureServer_ = new WsServer(); + insecureServer_ = new SimpleServer(); insecureServer_->config.port = port; } + isInitialized_ = true; - tokenValidator = new authenticator(logger, "appstacle", "RS256"); - accessCheck = new accesschecker(tokenValidator); - subHandler = new subscriptionhandler(logger, this, tokenValidator, accessCheck); - database = new vssdatabase(logger, subHandler, accessCheck); - cmdProcessor = new vsscommandprocessor(logger, database, tokenValidator, subHandler); - wserver = this; + return isInitialized_; } -wsserver::~wsserver() { - delete cmdProcessor; - delete database; - delete subHandler; - delete accessCheck; - delete tokenValidator; +WsServer::~WsServer() { + cmdProcessor.reset(); + if (secureServer_) { delete secureServer_; } @@ -81,7 +85,7 @@ wsserver::~wsserver() { } static void onMessage(std::weak_ptr wLogger, - shared_ptr connection, + shared_ptr connection, string message) { auto logger = wLogger.lock(); if (logger) { @@ -92,13 +96,13 @@ static void onMessage(std::weak_ptr wLogger, string response = wserver->cmdProcessor->processQuery(message, connection->channel); - auto send_stream = make_shared(); + auto send_stream = make_shared(); *send_stream << response; connection->send(send_stream); } static void onMessage(std::weak_ptr wLogger, - shared_ptr connection, + shared_ptr connection, string message) { auto logger = wLogger.lock(); if (logger) { @@ -109,12 +113,12 @@ static void onMessage(std::weak_ptr wLogger, string response = wserver->cmdProcessor->processQuery(message, connection->channel); - auto send_stream = make_shared(); + auto send_stream = make_shared(); *send_stream << response; connection->send(send_stream); } -void wsserver::startServer(string endpointName) { +void WsServer::startServer(const string &endpointName) { (void) endpointName; auto wLogger = std::weak_ptr(logger); @@ -122,19 +126,19 @@ void wsserver::startServer(string endpointName) { if (isSecure_) { auto &vssEndpoint = secureServer_->endpoint["^/vss/?$"]; - vssEndpoint.on_message = [wLogger](shared_ptr connection, - shared_ptr message) { + vssEndpoint.on_message = [wLogger](shared_ptr connection, + shared_ptr message) { auto message_str = message->string(); onMessage(wLogger, connection, message_str); }; - vssEndpoint.on_open = [wLogger](shared_ptr connection) { + vssEndpoint.on_open = [wLogger](shared_ptr connection) { connection->channel.setConnID(generateConnID()); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::INFO, std::string("wsserver: Opened connection " + logger->Log(LogLevel::INFO, std::string("WsServer: Opened connection " + connection->remote_endpoint_address() + "conn ID " + to_string(connection->channel.getConnID()))); @@ -142,7 +146,7 @@ void wsserver::startServer(string endpointName) { }; // See RFC 6455 7.4.1. for status codes - vssEndpoint.on_close = [wLogger](shared_ptr connection, + vssEndpoint.on_close = [wLogger](shared_ptr connection, int status, const string & /*reason*/) { uint32_t clientID = connection->channel.getConnID() / CLIENT_MASK; connections[clientID] = 0; @@ -150,7 +154,7 @@ void wsserver::startServer(string endpointName) { auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::INFO, "wsserver: Closed connection " + logger->Log(LogLevel::INFO, "WsServer: Closed connection " + connection->remote_endpoint_address() + " with status code " + to_string(status)); @@ -160,14 +164,14 @@ void wsserver::startServer(string endpointName) { // See // http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, // Error Codes for error code meanings - vssEndpoint.on_error = [wLogger](shared_ptr connection, + vssEndpoint.on_error = [wLogger](shared_ptr connection, const SimpleWeb::error_code &ec) { uint32_t clientID = connection->channel.getConnID() / CLIENT_MASK; connections[clientID] = 0; // removeAllSubscriptions(clientID); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::ERROR, "wsserver: Connection " + logger->Log(LogLevel::ERROR, "WsServer: Connection " + connection->remote_endpoint_address() + " with con ID " + to_string(connection->channel.getConnID()) + ". " + "Error: " + to_string(ec.value()) + ", error message: " + ec.message()); @@ -179,33 +183,33 @@ void wsserver::startServer(string endpointName) { } else { auto &vssEndpoint = insecureServer_->endpoint["^/vss/?$"]; - vssEndpoint.on_message = [wLogger](shared_ptr connection, - shared_ptr message) { + vssEndpoint.on_message = [wLogger](shared_ptr connection, + shared_ptr message) { auto message_str = message->string(); onMessage(wLogger, connection, message_str); }; - vssEndpoint.on_open = [wLogger](shared_ptr connection) { + vssEndpoint.on_open = [wLogger](shared_ptr connection) { connection->channel.setConnID(generateConnID()); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::VERBOSE, "wsserver: Opened connection " + logger->Log(LogLevel::VERBOSE, "WsServer: Opened connection " + connection->remote_endpoint_address() + "conn ID " + to_string(connection->channel.getConnID())); } }; // See RFC 6455 7.4.1. for status codes - vssEndpoint.on_close = [wLogger](shared_ptr connection, + vssEndpoint.on_close = [wLogger](shared_ptr connection, int status, const string & /*reason*/) { uint32_t clientID = connection->channel.getConnID() / CLIENT_MASK; connections[clientID] = 0; // removeAllSubscriptions(clientID); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::INFO, "wsserver: Closed connection " + logger->Log(LogLevel::INFO, "WsServer: Closed connection " + connection->remote_endpoint_address() + " with status code " + to_string(status)); } @@ -214,14 +218,14 @@ void wsserver::startServer(string endpointName) { // See // http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, // Error Codes for error code meanings - vssEndpoint.on_error = [wLogger](shared_ptr connection, + vssEndpoint.on_error = [wLogger](shared_ptr connection, const SimpleWeb::error_code &ec) { uint32_t clientID = connection->channel.getConnID() / CLIENT_MASK; connections[clientID] = 0; // removeAllSubscriptions(clientID); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::ERROR, "wsserver: Connection " + logger->Log(LogLevel::ERROR, "WsServer: Connection " + connection->remote_endpoint_address() + " with con ID " + to_string(connection->channel.getConnID()) + ". " + "Error: " + to_string(ec.value()) + ", error message: " + ec.message()); @@ -233,9 +237,9 @@ void wsserver::startServer(string endpointName) { } } -void wsserver::sendToConnection(uint32_t connectionID, string message) { +void WsServer::SendToConnection(uint64_t connectionID, const string &message) { if (isSecure_) { - auto send_stream = make_shared(); + auto send_stream = make_shared(); *send_stream << message; for (auto &a_connection : secureServer_->get_connections()) { if (a_connection->channel.getConnID() == connectionID) { @@ -244,7 +248,7 @@ void wsserver::sendToConnection(uint32_t connectionID, string message) { } } } else { - auto send_stream = make_shared(); + auto send_stream = make_shared(); *send_stream << message; for (auto &a_connection : insecureServer_->get_connections()) { if (a_connection->channel.getConnID() == connectionID) { @@ -264,13 +268,21 @@ void *startWSServer(void *arg) { return NULL; } -vssdatabase* wsserver::start() { - this->database->initJsonTree(configFileName_); +bool WsServer::start() { pthread_t startWSServer_thread; /* create the web socket server thread. */ if (pthread_create(&startWSServer_thread, NULL, &startWSServer, NULL)) { logger->Log(LogLevel::ERROR, "main: Error creating websocket server run thread"); } - return this->database; + + return true; +} + +void WsServer::AddListener(ObserverType, std::shared_ptr) +{ +} + +void WsServer::RemoveListener(ObserverType, std::shared_ptr) +{ } diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index 1023398..3031a9f 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -15,15 +15,25 @@ #include #include #include +#include +#include +#include +#include + #include #include #include -#include "wsserver.hpp" -#include "vssdatabase.hpp" +#include "WsServer.hpp" #include "exception.hpp" - +#include "RestV1ApiHandler.hpp" #include "BasicLogger.hpp" +#include "Authenticator.hpp" +#include "AccessChecker.hpp" +#include "SubscriptionHandler.hpp" +#include "VssCommandProcessor.hpp" +#include "VssDatabase.hpp" +#include "WebSockHttpFlexServer.hpp" using namespace std; @@ -36,7 +46,7 @@ using jsoncons::json; // Websocket port #define PORT 8090 -vssdatabase* database = NULL; +static VssDatabase* gDatabase = NULL; static GDBusNodeInfo *introspection_data = NULL; @@ -86,9 +96,9 @@ handle_method_call (GDBusConnection *connection, (void) sender; json jsonVal; - const gchar *vss_path; + const gchar *vss_path = NULL; - if (database == NULL) { + if (gDatabase == NULL) { g_dbus_method_invocation_return_error (invocation, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED, @@ -146,7 +156,7 @@ handle_method_call (GDBusConnection *connection, // set the data in the db. try { string pathStr(vss_path); - database->setSignal(pathStr, jsonVal); + gDatabase->setSignal(pathStr, jsonVal); } catch (genException &e) { g_dbus_method_invocation_return_error (invocation, G_IO_ERROR, @@ -166,15 +176,7 @@ static const GDBusInterfaceVTable interface_vtable = NULL, NULL, /* Padding for future expansion */ - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - + {NULL} }; static void @@ -218,77 +220,177 @@ on_name_lost (GDBusConnection *connection, static void print_usage(const char *prog_name, - program_options::options_description desc) { + program_options::options_description& desc) { cerr << "Usage: " << prog_name << " OPTIONS" << endl; cerr << desc << std::endl; } int main(int argc, const char *argv[]) { + string configFile; + vector logLevels{"NONE"}; + uint8_t logLevelsActive = static_cast(LogLevel::NONE); + program_options::options_description desc{"Options"}; desc.add_options() ("help,h", "Help screen") + ("config-file,cfg", program_options::value(&configFile), + "Configuration file path for program parameters. " + "Can be provided instead of command line options") ("vss", program_options::value(), "vss_rel*.json file") - ("insecure", "Run insecure") + ("cert-path", program_options::value()->default_value("."), + "Path to directory where 'Server.pem' and 'Server.key' are located") + ("insecure", "Accept plain (no-SSL) connections") + ("use-keycloak", "Use KeyCloak for permission management") + ("wss-server", "Run old WSS server handler instead of Boost.Beast. Note: No REST API support") + ("address", program_options::value()->default_value("localhost"), "Address") ("port", program_options::value()->default_value(8090), "Port") - ("address", program_options::value()->default_value("localhost"), "Address"); + ("log-level", program_options::value>(&logLevels)->composing(), + "Log level event type to be enabled. " + "To enable different log levels, provide this option multiple times with required log levels. \n" + "Supported log levels: NONE, VERBOSE, INFO, WARNING, ERROR, ALL"); try { program_options::variables_map variables; program_options::store(parse_command_line(argc, argv, desc), variables); program_options::notify(variables); + // if config file passed, get configuration from it + if (configFile.size()) { + cout << configFile << std::endl; + std::ifstream ifs(configFile.c_str()); + if(!ifs) + { + std::cerr << "Could not open config file: " << configFile << std::endl; + return -1; + } + program_options::store(parse_config_file(ifs, desc), variables); + } + program_options::notify(variables); + + // verify parameters if (!variables.count("vss")) { print_usage(argv[0], desc); - cerr << "vss file (--vss) must be specified" << std::endl; + cerr << "the option '--vss' is required but missing" << std::endl; + return -1; + } + if (!variables.count("cert-path")) { + cerr << "the option '--cert-path' is required but missing" << std::endl; + print_usage(argv[0], desc); return -1; } - if (variables.count("help")) { print_usage(argv[0], desc); return -1; } + + for (auto const& token : logLevels) { + if (token == "NONE") + logLevelsActive |= static_cast(LogLevel::NONE); + else if (token == "VERBOSE") + logLevelsActive |= static_cast(LogLevel::VERBOSE); + else if (token == "INFO") + logLevelsActive |= static_cast(LogLevel::INFO); + else if (token == "WARNING") + logLevelsActive |= static_cast(LogLevel::WARNING); + else if (token == "ERROR") + logLevelsActive |= static_cast(LogLevel::ERROR); + else if (token == "ALL") + logLevelsActive |= static_cast(LogLevel::ALL); + else { + cerr << "Invalid input parameter for LogLevel" << std::endl; + return -1; + } + } + + // initialize server + auto port = variables["port"].as(); auto secure = !variables.count("insecure"); auto vss_filename = variables["vss"].as(); + auto useNewServer = !variables.count("wss-server"); + + if (variables.count("use-keycloak")) { + // Start D-Bus backend connection. + guint owner_id; + GMainLoop *loop; + + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, + "org.eclipse.kuksa.w3cbackend", + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + g_bus_unown_name (owner_id); + g_dbus_node_info_unref (introspection_data); + } - // Start D-Bus backend connection. - guint owner_id; - GMainLoop *loop; + // initialize pseudo random number generator + std::srand(std::time(nullptr)); - introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); - g_assert (introspection_data != NULL); - + auto logger = std::make_shared(logLevelsActive); - owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, - "org.eclipse.kuksa.w3cbackend", - G_BUS_NAME_OWNER_FLAGS_NONE, - on_bus_acquired, - on_name_acquired, - on_name_lost, - NULL, - NULL); + // define doc root path for server.. + // for now also add 'v1' to designate version 1 of REST API as default + // in future, we can add/update REST API with new versions but also support older + // by having API versioning through URIs + std::string docRoot{"/vss/api/v1/"}; + // TODO: refactor out old server when we can remove it + auto oldServer = std::make_shared(); - loop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (loop); - g_bus_unown_name (owner_id); - g_dbus_node_info_unref (introspection_data); + auto rest2JsonConverter = std::make_shared(logger, docRoot); + auto newServer = std::make_shared(logger, std::move(rest2JsonConverter)); - uint8_t logLevelsActive; -#ifdef DEBUG - logLevelsActive = static_cast(LogLevel::ALL); -#else - logLevelsActive = static_cast(LogLevel::INFO & LogLevel::WARNING & LogLevel::ERROR); -#endif - std::shared_ptr logger = std::make_shared(logLevelsActive); + std::shared_ptr server; + if (!useNewServer) { + server = std::static_pointer_cast(oldServer); + } + else { + server = std::static_pointer_cast(newServer); + } + + auto tokenValidator = std::make_shared(logger, "appstacle", "RS256"); + auto accessCheck = std::make_shared(tokenValidator); + auto subHandler = std::make_shared(logger, server, tokenValidator, accessCheck); + auto database = std::make_shared(logger, subHandler, accessCheck); + auto cmdProcessor = std::make_shared(logger, database, tokenValidator, subHandler); - wsserver server(logger, port, vss_filename, secure); - server.start(); + gDatabase = database.get(); + database->initJsonTree(vss_filename); + + if (!useNewServer) { + oldServer->Initialize(logger, cmdProcessor, secure, port); + oldServer->start(); + } + else { + newServer->AddListener(ObserverType::ALL, cmdProcessor); + newServer->Initialize(variables["address"].as(), + port, + std::move(docRoot), + variables["cert-path"].as(), + !secure); + newServer->Start(); + } + + while (1) { + usleep(1000000); + } } catch (const program_options::error &ex) { print_usage(argv[0], desc); cerr << ex.what() << std::endl; return -1; + } catch (const std::runtime_error &ex) { + cerr << ex.what() << std::endl; + return -1; } return 0; } diff --git a/w3c-visserver-api/test/testclient.cpp b/w3c-visserver-api/test/testclient.cpp index 92f1946..01b1972 100644 --- a/w3c-visserver-api/test/testclient.cpp +++ b/w3c-visserver-api/test/testclient.cpp @@ -14,19 +14,29 @@ #include "client_wss.hpp" #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace std; using namespace jsoncons; +using tcp = boost::asio::ip::tcp; // from +namespace ssl = boost::asio::ssl; // from +namespace websocket = boost::beast::websocket; // from using WssClient = SimpleWeb::SocketClient; -string url = "localhost:8090/vss"; - bool subThread = false; string getRequest(string path ) { - json req; req["requestId"] = 1234; req["action"]= "get"; @@ -34,11 +44,9 @@ string getRequest(string path ) { stringstream ss; ss << pretty_print(req); return ss.str(); - } string setRequest(string path, int val) { - json req; req["requestId"] = 1235; req["action"]= "set"; @@ -47,11 +55,9 @@ string setRequest(string path, int val) { stringstream ss; ss << pretty_print(req); return ss.str(); - } string getMetarequest( string path ) { - json req; req["requestId"] = 1236; req["action"]= "getMetadata"; @@ -62,7 +68,6 @@ string getMetarequest( string path ) { } string getSubRequest (string path) { - json req; req["requestId"] = 1237; req["action"]= "subscribe"; @@ -70,11 +75,9 @@ string getSubRequest (string path) { stringstream ss; ss << pretty_print(req); return ss.str(); - } string getAuthRequest(string token) { - json req; req["requestId"] = 1238; req["action"]= "authorize"; @@ -82,7 +85,6 @@ string getAuthRequest(string token) { stringstream ss; ss << pretty_print(req); return ss.str(); - } string getKuksaAuthRequest(string clientID , string secret) { @@ -133,15 +135,15 @@ void sendRequest(shared_ptr connection) { getline (cin, secret); command = getKuksaAuthRequest(clientid, secret); } - + auto send_stream = make_shared(); *send_stream << command; connection->send(send_stream); - } - -void startWSClient() { +void startWSClient(std::string host, int port, std::string rootDoc) { + std::string url; + url = host + ":" + to_string(port) + "/" + rootDoc; WssClient client(url , true ,"Client.pem", "Client.key","CA.pem"); @@ -150,7 +152,7 @@ void startWSClient() { sendRequest(connection); }; - client.on_open = [](shared_ptr connection) { + client.on_open = [url](shared_ptr connection) { cout << "Connection with server at " << url << " opened" << endl; sendRequest(connection); }; @@ -164,10 +166,221 @@ void startWSClient() { cout << "Error: " << ec << ", message: " << ec.message() << endl; }; -client.start(); + client.start(); } +void LoadCertData(ssl::context& ctx) { + std::string cert; + std::string key; + std::string line; + + { + ifstream inputFile ("Client.pem"); + if (inputFile.is_open()) + { + while ( getline (inputFile,line) ) + { + cert.append(line); + cert.append("\n"); + } + inputFile.close(); + } + } + { + ifstream inputFile ("Client.key"); + if (inputFile.is_open()) + { + while ( getline (inputFile,line) ) + { + key.append(line); + key.append("\n"); + } + inputFile.close(); + } + } + + ctx.use_certificate_chain( + boost::asio::buffer(cert.data(), cert.size())); + + ctx.use_private_key( + boost::asio::buffer(key.data(), key.size()), + boost::asio::ssl::context::file_format::pem); +} + +void StartBeastClient(std::string host, int port, std::string rootDoc) { + // The io_context is required for all I/O + boost::asio::io_context ioc; + + + tcp::resolver resolver{ioc}; + websocket::stream ws{ioc}; + + // Look up the domain name + auto const results = resolver.resolve(host, to_string(port)); + + // Make the connection on the IP address we get from a lookup + boost::asio::connect(ws.next_layer(), results.begin(), results.end()); + + // Perform the websocket handshake + ws.handshake(host, "/" + rootDoc); + + for(;;) { + string path, function; + cout << "Enter vss path eg : Signal.Drivetrain.InternalCombustionEngine.RPM " <> ws{ioc, ctx}; + + // Look up the domain name + auto const results = resolver.resolve(host, to_string(port)); + + // Make the connection on the IP address we get from a lookup + boost::asio::connect(ws.next_layer().next_layer(), results.begin(), results.end()); + + startWSClient(host, port, rootDoc); + + // Perform the SSL handshake + ws.next_layer().handshake(ssl::stream_base::client); + + // Perform the websocket handshake + ws.handshake(host, "/" + rootDoc); + + for(;;) { + string path, function; + cout << "Enter vss path eg : Signal.Drivetrain.InternalCombustionEngine.RPM " <()->default_value("localhost"), "Address") + ("port", boost::program_options::value()->default_value(8090), "Port"); + + try { + boost::program_options::variables_map variables; + boost::program_options::store(parse_command_line(argc, argv, desc), variables); + boost::program_options::notify(variables); + + if (variables.count("help")) { + print_usage(argv[0], desc); + return 0; + } + + if (variables.count("wss-client")) { + std::cout << "Starting Simple WebSocket client" << std::endl; + startWSClient(variables["address"].as(), variables["port"].as(), "vss"); + } + else + { + if (variables.count("insecure") > 0) + { + std::cout << "Starting Boost.Beast WebSocket client" << std::endl; + StartBeastClient(variables["address"].as(), + variables["port"].as(), + "vss"); + } + else + { + + std::cout << "Starting Secured Boost.Beast WebSocket client" << std::endl; + StartSecuredBeastClient(variables["address"].as(), + variables["port"].as(), + "vss"); + } + } -int main() { - startWSClient(); + } catch (const boost::program_options::error &ex) { + print_usage(argv[0], desc); + cerr << ex.what() << std::endl; + return -1; + } } diff --git a/w3c-visserver-api/test/web-client/index.html b/w3c-visserver-api/test/web-client/index.html new file mode 100644 index 0000000..2b11aed --- /dev/null +++ b/w3c-visserver-api/test/web-client/index.html @@ -0,0 +1,102 @@ + + + + + + + + GUI tool for exercising REST API functionality in W3C-VISServer-API + + + + + + + + + + + + +
+
+
+ +
+

W3C Vehicle Information Specification test client for REST and Web-Socket interfaces

+
+
+ +
+
+ Current authorization token:
+ +
+
+ +
+
+
+ Input resource request path to fetch for the W3C VIS Server below: +
+ +
+ + + + +
+
+
+
+ +
+
+
+ Log of REST resource requests and server responses
+

As an option, clicking on resource string of any request shown in log, given resource string will be copied back to input resouce text field. + Useful to speed-up testing when identical requests needs to be made or with slight modifications.

+ Color codes: REST Request OK REST Response ERROR REST Response +
+
    +
+
+
+
+
+ +
+
+
+
+ Server parameters: + HTTP
+ HTTPS
+ Address:
+ Port:
+ Doc root:
+
+
+
+
+
+
+
+ Client token and key configuration + Client token content: +
+ Client key certificate content: + +
+
+
+
+ +
+
+ + + + diff --git a/w3c-visserver-api/test/web-client/jsonview.css b/w3c-visserver-api/test/web-client/jsonview.css new file mode 100644 index 0000000..d9697cb --- /dev/null +++ b/w3c-visserver-api/test/web-client/jsonview.css @@ -0,0 +1,77 @@ +.line { + margin: 4px 0; + display: flex; + justify-content: flex-start; +} + +.caret-icon { + width: 18px; + text-align: center; + cursor: pointer; +} + +.empty-icon { + width: 18px; +} + +.json-type { + margin-right: 4px; + margin-left: 4px; +} + +.json-key { + color: #444; + margin-right: 4px; + margin-left: 4px; +} + +.json-index { + margin-right: 4px; + margin-left: 4px; +} + +.json-separator { + +} + +.json-value { + margin-left: 8px; +} + +.json-number { + color: #f9ae58; +} + +.json-boolean { + color: #ec5f66; +} + +.json-string { + color: #86b25c; +} + +.json-size { + margin-right: 4px; + margin-left: 4px; +} + +.hide { + display: none; +} + +.fas { + display: inline-block; + width: 0; + height: 0; + border-style: solid; +} + +.fa-caret-down { + border-width: 6px 5px 0 5px; + border-color: #808080 transparent +} + +.fa-caret-right { + border-width: 5px 0 5px 6px; + border-color: transparent transparent transparent #808080; +} diff --git a/w3c-visserver-api/test/web-client/jsonview.js b/w3c-visserver-api/test/web-client/jsonview.js new file mode 100644 index 0000000..885f6a4 --- /dev/null +++ b/w3c-visserver-api/test/web-client/jsonview.js @@ -0,0 +1,331 @@ +(function() { +'use strict'; +/** + * Create html element + * @param {String} type html element + * @param {Object} config + */ +function createElement(type, config) { + const htmlElement = document.createElement(type); + + if (config === undefined) { + return htmlElement; + } + + if (config.className) { + htmlElement.className = config.className; + } + + if (config.content) { + htmlElement.textContent = config.content; + } + + if (config.children) { + config.children.forEach((el) => { + if (el !== null) { + htmlElement.appendChild(el); + } + }); + } + + return htmlElement; +} + + +/** + * @param {Object} node + * @return {HTMLElement} + */ +function createExpandedElement(node) { + const iElem = createElement('i'); + + if (node.expanded) { + iElem.className = 'fas fa-caret-down'; + } else { + iElem.className = 'fas fa-caret-right'; + } + + const caretElem = createElement('div', { + className: 'caret-icon', + children: [iElem], + }); + + const handleClick = node.toggle.bind(node); + caretElem.addEventListener('click', handleClick); + + const indexElem = createElement('div', { + className: 'json-index', + content: node.key, + }); + + const typeElem = createElement('div', { + className: 'json-type', + content: node.type, + }); + + const keyElem = createElement('div', { + className: 'json-key', + content: node.key, + }); + + const sizeElem = createElement('div', { + className: 'json-size' + }); + + if (node.type === 'array') { + sizeElem.innerText = '[' + node.children.length + ']'; + } else if (node.type === 'object') { + sizeElem.innerText = '{' + node.children.length + '}'; + } + + let lineChildren; + if (node.key === null) { + lineChildren = [caretElem, typeElem, sizeElem] + } else if (node.parent.type === 'array') { + lineChildren = [caretElem, indexElem, sizeElem] + } else { + lineChildren = [caretElem, keyElem, sizeElem] + } + + const lineElem = createElement('div', { + className: 'line', + children: lineChildren + }); + + if (node.depth > 0) { + lineElem.style = 'margin-left: ' + node.depth * 24 + 'px;'; + } + + return lineElem; +} + + +/** + * @param {Object} node + * @return {HTMLElement} + */ +function createNotExpandedElement(node) { + const caretElem = createElement('div', { + className: 'empty-icon', + }); + + const keyElem = createElement('div', { + className: 'json-key', + content: node.key + }); + + const separatorElement = createElement('div', { + className: 'json-separator', + content: ':' + }); + + const valueType = ' json-' + typeof node.value; + const valueContent = String(node.value); + const valueElement = createElement('div', { + className: 'json-value' + valueType, + content: valueContent + }); + + const lineElem = createElement('div', { + className: 'line', + children: [caretElem, keyElem, separatorElement, valueElement] + }); + + if (node.depth > 0) { + lineElem.style = 'margin-left: ' + node.depth * 24 + 'px;'; + } + + return lineElem; +} + + +/** + * create tree node + * @return {Object} + */ +function createNode() { + return { + key: null, + parent: null, + value: null, + expanded: false, + type: null, + children: null, + elem: null, + depth: 0, + + setCaretIconRight() { + const icon = this.elem.querySelector('.fas'); + icon.classList.replace('fa-caret-down', 'fa-caret-right'); + }, + + setCaretIconDown() { + const icon = this.elem.querySelector('.fas'); + icon.classList.replace('fa-caret-right', 'fa-caret-down'); + }, + + hideChildren() { + if (this.children !== null) { + this.children.forEach((item) => { + item.elem.classList.add('hide'); + if (item.expanded) { + item.hideChildren(); + } + }); + } + }, + + showChildren() { + if (this.children !== null) { + this.children.forEach((item) => { + item.elem.classList.remove('hide'); + if (item.expanded) { + item.showChildren(); + } + }); + } + }, + + toggle: function() { + if (this.expanded) { + this.expanded = false; + this.hideChildren(); + this.setCaretIconRight(); + } else { + this.expanded = true; + this.showChildren(); + this.setCaretIconDown(); + } + } + } +} + + +/** + * Return object length + * @param {Object} obj + * @return {number} + */ +function getLength(obj) { + let length = 0; + for (let key in obj) { + length += 1; + }; + return length; +} + + +/** + * Return variable type + * @param {*} val + */ +function getType(val) { + let type = typeof val; + if (Array.isArray(val)) { + type = 'array'; + } else if (val === null) { + type = 'null'; + } + return type; +} + + +/** + * Recursively traverse json object + * @param {Object} obj parsed json object + * @param {Object} parent of object tree + */ +function traverseObject(obj, parent) { + for (let key in obj) { + const child = createNode(); + child.parent = parent; + child.key = key; + child.type = getType(obj[key]); + child.depth = parent.depth + 1; + child.expanded = false; + + if (typeof obj[key] === 'object') { + child.children = []; + parent.children.push(child); + traverseObject(obj[key], child); + child.elem = createExpandedElement(child); + } else { + child.value = obj[key]; + child.elem = createNotExpandedElement(child); + parent.children.push(child); + } + } +} + + +/** + * Create root of a tree + * @param {Object} obj Json object + * @return {Object} + */ +function createTree(obj) { + const tree = createNode(); + tree.type = getType(obj); + tree.children = []; + tree.expanded = true; + + traverseObject(obj, tree); + tree.elem = createExpandedElement(tree); + + return tree; +} + + +/** + * Recursively traverse Tree object + * @param {Object} node + * @param {Callback} callback + */ +function traverseTree(node, callback) { + callback(node); + if (node.children !== null) { + node.children.forEach((item) => { + traverseTree(item, callback); + }); + } +} + + +/** + * Render Tree object + * @param {Object} tree + * @param {String} targetElem + */ +function render(tree, targetElem) { + let rootElem; + if (targetElem) { + rootElem = document.querySelector(targetElem); + } else { + rootElem = document.body; + } + + traverseTree(tree, (node) => { + if (!node.expanded) { + node.hideChildren(); + } + rootElem.appendChild(node.elem); + }); +} + + +/* Export jsonView object */ +window.jsonView = { + /** + * Render JSON into DOM container + * @param {String} jsonData + * @param {String} targetElem + */ + format: function(jsonData, targetElem) { + let parsedData = jsonData; + if (typeof jsonData === 'string' || jsonData instanceof String) parsedData = JSON.parse(jsonData); + const tree = createTree(parsedData); + render(tree, targetElem); + } +} + +})(); diff --git a/w3c-visserver-api/test/web-client/jsrsasign-latest-all-min.js b/w3c-visserver-api/test/web-client/jsrsasign-latest-all-min.js new file mode 100644 index 0000000..cf81f44 --- /dev/null +++ b/w3c-visserver-api/test/web-client/jsrsasign-latest-all-min.js @@ -0,0 +1,246 @@ +/* + * jsrsasign(all) 8.0.12 (2018-04-22) (c) 2010-2018 Kenji Urushima | kjur.github.com/jsrsasign/license + */ + +/*! +Copyright (c) 2011, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.9.0 +*/ +if(YAHOO===undefined){var YAHOO={}}YAHOO.lang={extend:function(g,h,f){if(!h||!g){throw new Error("YAHOO.lang.extend failed, please check that all dependencies are included.")}var d=function(){};d.prototype=h.prototype;g.prototype=new d();g.prototype.constructor=g;g.superclass=h.prototype;if(h.prototype.constructor==Object.prototype.constructor){h.prototype.constructor=h}if(f){var b;for(b in f){g.prototype[b]=f[b]}var e=function(){},c=["toString","valueOf"];try{if(/MSIE/.test(navigator.userAgent)){e=function(j,i){for(b=0;b>>2]>>>(24-(r%4)*8))&255;q[(n+r)>>>2]|=o<<(24-((n+r)%4)*8)}}else{for(var r=0;r>>2]=p[r>>>2]}}this.sigBytes+=s;return this},clamp:function(){var o=this.words;var n=this.sigBytes;o[n>>>2]&=4294967295<<(32-(n%4)*8);o.length=e.ceil(n/4)},clone:function(){var n=j.clone.call(this);n.words=this.words.slice(0);return n},random:function(p){var o=[];for(var n=0;n>>2]>>>(24-(n%4)*8))&255;q.push((s>>>4).toString(16));q.push((s&15).toString(16))}return q.join("")},parse:function(p){var n=p.length;var q=[];for(var o=0;o>>3]|=parseInt(p.substr(o,2),16)<<(24-(o%8)*4)}return new l.init(q,n/2)}};var d=m.Latin1={stringify:function(q){var r=q.words;var p=q.sigBytes;var n=[];for(var o=0;o>>2]>>>(24-(o%4)*8))&255;n.push(String.fromCharCode(s))}return n.join("")},parse:function(p){var n=p.length;var q=[];for(var o=0;o>>2]|=(p.charCodeAt(o)&255)<<(24-(o%4)*8)}return new l.init(q,n)}};var c=m.Utf8={stringify:function(n){try{return decodeURIComponent(escape(d.stringify(n)))}catch(o){throw new Error("Malformed UTF-8 data")}},parse:function(n){return d.parse(unescape(encodeURIComponent(n)))}};var i=b.BufferedBlockAlgorithm=j.extend({reset:function(){this._data=new l.init();this._nDataBytes=0},_append:function(n){if(typeof n=="string"){n=c.parse(n)}this._data.concat(n);this._nDataBytes+=n.sigBytes},_process:function(w){var q=this._data;var x=q.words;var n=q.sigBytes;var t=this.blockSize;var v=t*4;var u=n/v;if(w){u=e.ceil(u)}else{u=e.max((u|0)-this._minBufferSize,0)}var s=u*t;var r=e.min(s*4,n);if(s){for(var p=0;p>>2]&255}};f.BlockCipher=n.extend({cfg:n.cfg.extend({mode:m,padding:h}),reset:function(){n.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1; +this._mode=c.call(a,this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var p=f.CipherParams=k.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),m=(g.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt; +return(a?l.create([1398893684,1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=l.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return p.create({ciphertext:a,salt:c})}},j=f.SerializableCipher=k.extend({cfg:k.extend({format:m}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var e=a.createEncryptor(c,d);b=e.finalize(b);e=e.cfg;return p.create({ciphertext:b,key:c,iv:e.iv,algorithm:a,mode:e.mode,padding:e.padding, +blockSize:a.blockSize,formatter:d.format})},decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),g=(g.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=l.random(8));a=v.create({keySize:b+c}).compute(a,d);c=l.create(a.words.slice(b),4*c);a.sigBytes=4*b;return p.create({key:a,iv:c,salt:d})}},s=f.PasswordBasedCipher=j.extend({cfg:j.cfg.extend({kdf:g}),encrypt:function(a, +b,c,d){d=this.cfg.extend(d);c=d.kdf.execute(c,a.keySize,a.ivSize);d.iv=c.iv;a=j.encrypt.call(this,a,b,c.key,d);a.mixIn(c);return a},decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);c=d.kdf.execute(c,a.keySize,a.ivSize,b.salt);d.iv=c.iv;return j.decrypt.call(this,a,b,c.key,d)}})}(); + +/* +CryptoJS v3.1.2 aes.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){for(var q=CryptoJS,x=q.lib.BlockCipher,r=q.algo,j=[],y=[],z=[],A=[],B=[],C=[],s=[],u=[],v=[],w=[],g=[],k=0;256>k;k++)g[k]=128>k?k<<1:k<<1^283;for(var n=0,l=0,k=0;256>k;k++){var f=l^l<<1^l<<2^l<<3^l<<4,f=f>>>8^f&255^99;j[n]=f;y[f]=n;var t=g[n],D=g[t],E=g[D],b=257*g[f]^16843008*f;z[n]=b<<24|b>>>8;A[n]=b<<16|b>>>16;B[n]=b<<8|b>>>24;C[n]=b;b=16843009*E^65537*D^257*t^16843008*n;s[f]=b<<24|b>>>8;u[f]=b<<16|b>>>16;v[f]=b<<8|b>>>24;w[f]=b;n?(n=t^g[g[g[E^t]]],l^=g[g[l]]):n=l=1}var F=[0,1,2,4,8, +16,32,64,128,27,54],r=r.AES=x.extend({_doReset:function(){for(var c=this._key,e=c.words,a=c.sigBytes/4,c=4*((this._nRounds=a+6)+1),b=this._keySchedule=[],h=0;h>>24]<<24|j[d>>>16&255]<<16|j[d>>>8&255]<<8|j[d&255]):(d=d<<8|d>>>24,d=j[d>>>24]<<24|j[d>>>16&255]<<16|j[d>>>8&255]<<8|j[d&255],d^=F[h/a|0]<<24);b[h]=b[h-a]^d}e=this._invKeySchedule=[];for(a=0;aa||4>=h?d:s[j[d>>>24]]^u[j[d>>>16&255]]^v[j[d>>> +8&255]]^w[j[d&255]]},encryptBlock:function(c,e){this._doCryptBlock(c,e,this._keySchedule,z,A,B,C,j)},decryptBlock:function(c,e){var a=c[e+1];c[e+1]=c[e+3];c[e+3]=a;this._doCryptBlock(c,e,this._invKeySchedule,s,u,v,w,y);a=c[e+1];c[e+1]=c[e+3];c[e+3]=a},_doCryptBlock:function(c,e,a,b,h,d,j,m){for(var n=this._nRounds,f=c[e]^a[0],g=c[e+1]^a[1],k=c[e+2]^a[2],p=c[e+3]^a[3],l=4,t=1;t>>24]^h[g>>>16&255]^d[k>>>8&255]^j[p&255]^a[l++],r=b[g>>>24]^h[k>>>16&255]^d[p>>>8&255]^j[f&255]^a[l++],s= +b[k>>>24]^h[p>>>16&255]^d[f>>>8&255]^j[g&255]^a[l++],p=b[p>>>24]^h[f>>>16&255]^d[g>>>8&255]^j[k&255]^a[l++],f=q,g=r,k=s;q=(m[f>>>24]<<24|m[g>>>16&255]<<16|m[k>>>8&255]<<8|m[p&255])^a[l++];r=(m[g>>>24]<<24|m[k>>>16&255]<<16|m[p>>>8&255]<<8|m[f&255])^a[l++];s=(m[k>>>24]<<24|m[p>>>16&255]<<16|m[f>>>8&255]<<8|m[g&255])^a[l++];p=(m[p>>>24]<<24|m[f>>>16&255]<<16|m[g>>>8&255]<<8|m[k&255])^a[l++];c[e]=q;c[e+1]=r;c[e+2]=s;c[e+3]=p},keySize:8});q.AES=x._createHelper(r)})(); + +/* +CryptoJS v3.1.2 tripledes-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){function j(b,c){var a=(this._lBlock>>>b^this._rBlock)&c;this._rBlock^=a;this._lBlock^=a<>>b^this._lBlock)&c;this._lBlock^=a;this._rBlock^=a<a;a++){var f=q[a]-1;c[a]=b[f>>>5]>>>31-f%32&1}b=this._subKeys=[];for(f=0;16>f;f++){for(var d=b[f]=[],e=r[f],a=0;24>a;a++)d[a/6|0]|=c[(p[a]-1+e)%28]<<31-a%6,d[4+(a/6|0)]|=c[28+(p[a+24]-1+e)%28]<<31-a%6;d[0]=d[0]<<1|d[0]>>>31;for(a=1;7>a;a++)d[a]>>>= +4*(a-1)+3;d[7]=d[7]<<5|d[7]>>>27}c=this._invSubKeys=[];for(a=0;16>a;a++)c[a]=b[15-a]},encryptBlock:function(b,c){this._doCryptBlock(b,c,this._subKeys)},decryptBlock:function(b,c){this._doCryptBlock(b,c,this._invSubKeys)},_doCryptBlock:function(b,c,a){this._lBlock=b[c];this._rBlock=b[c+1];j.call(this,4,252645135);j.call(this,16,65535);l.call(this,2,858993459);l.call(this,8,16711935);j.call(this,1,1431655765);for(var f=0;16>f;f++){for(var d=a[f],e=this._lBlock,h=this._rBlock,g=0,k=0;8>k;k++)g|=s[k][((h^ +d[k])&t[k])>>>0];this._lBlock=h;this._rBlock=e^g}a=this._lBlock;this._lBlock=this._rBlock;this._rBlock=a;j.call(this,1,1431655765);l.call(this,8,16711935);l.call(this,2,858993459);j.call(this,16,65535);j.call(this,4,252645135);b[c]=this._lBlock;b[c+1]=this._rBlock},keySize:2,ivSize:2,blockSize:2});h.DES=e._createHelper(m);g=g.TripleDES=e.extend({_doReset:function(){var b=this._key.words;this._des1=m.createEncryptor(n.create(b.slice(0,2)));this._des2=m.createEncryptor(n.create(b.slice(2,4)));this._des3= +m.createEncryptor(n.create(b.slice(4,6)))},encryptBlock:function(b,c){this._des1.encryptBlock(b,c);this._des2.decryptBlock(b,c);this._des3.encryptBlock(b,c)},decryptBlock:function(b,c){this._des3.decryptBlock(b,c);this._des2.encryptBlock(b,c);this._des1.decryptBlock(b,c)},keySize:6,ivSize:2,blockSize:2});h.TripleDES=e._createHelper(g)})(); + +/* +CryptoJS v3.1.2 enc-base64.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var h=CryptoJS,j=h.lib.WordArray;h.enc.Base64={stringify:function(b){var e=b.words,f=b.sigBytes,c=this._map;b.clamp();b=[];for(var a=0;a>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d< +e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})(); + +/* +CryptoJS v3.1.2 md5.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(E){function h(a,f,g,j,p,h,k){a=a+(f&g|~f&j)+p+k;return(a<>>32-h)+f}function k(a,f,g,j,p,h,k){a=a+(f&j|g&~j)+p+k;return(a<>>32-h)+f}function l(a,f,g,j,h,k,l){a=a+(f^g^j)+h+l;return(a<>>32-k)+f}function n(a,f,g,j,h,k,l){a=a+(g^(f|~j))+h+l;return(a<>>32-k)+f}for(var r=CryptoJS,q=r.lib,F=q.WordArray,s=q.Hasher,q=r.algo,a=[],t=0;64>t;t++)a[t]=4294967296*E.abs(E.sin(t+1))|0;q=q.MD5=s.extend({_doReset:function(){this._hash=new F.init([1732584193,4023233417,2562383102,271733878])}, +_doProcessBlock:function(m,f){for(var g=0;16>g;g++){var j=f+g,p=m[j];m[j]=(p<<8|p>>>24)&16711935|(p<<24|p>>>8)&4278255360}var g=this._hash.words,j=m[f+0],p=m[f+1],q=m[f+2],r=m[f+3],s=m[f+4],t=m[f+5],u=m[f+6],v=m[f+7],w=m[f+8],x=m[f+9],y=m[f+10],z=m[f+11],A=m[f+12],B=m[f+13],C=m[f+14],D=m[f+15],b=g[0],c=g[1],d=g[2],e=g[3],b=h(b,c,d,e,j,7,a[0]),e=h(e,b,c,d,p,12,a[1]),d=h(d,e,b,c,q,17,a[2]),c=h(c,d,e,b,r,22,a[3]),b=h(b,c,d,e,s,7,a[4]),e=h(e,b,c,d,t,12,a[5]),d=h(d,e,b,c,u,17,a[6]),c=h(c,d,e,b,v,22,a[7]), +b=h(b,c,d,e,w,7,a[8]),e=h(e,b,c,d,x,12,a[9]),d=h(d,e,b,c,y,17,a[10]),c=h(c,d,e,b,z,22,a[11]),b=h(b,c,d,e,A,7,a[12]),e=h(e,b,c,d,B,12,a[13]),d=h(d,e,b,c,C,17,a[14]),c=h(c,d,e,b,D,22,a[15]),b=k(b,c,d,e,p,5,a[16]),e=k(e,b,c,d,u,9,a[17]),d=k(d,e,b,c,z,14,a[18]),c=k(c,d,e,b,j,20,a[19]),b=k(b,c,d,e,t,5,a[20]),e=k(e,b,c,d,y,9,a[21]),d=k(d,e,b,c,D,14,a[22]),c=k(c,d,e,b,s,20,a[23]),b=k(b,c,d,e,x,5,a[24]),e=k(e,b,c,d,C,9,a[25]),d=k(d,e,b,c,r,14,a[26]),c=k(c,d,e,b,w,20,a[27]),b=k(b,c,d,e,B,5,a[28]),e=k(e,b, +c,d,q,9,a[29]),d=k(d,e,b,c,v,14,a[30]),c=k(c,d,e,b,A,20,a[31]),b=l(b,c,d,e,t,4,a[32]),e=l(e,b,c,d,w,11,a[33]),d=l(d,e,b,c,z,16,a[34]),c=l(c,d,e,b,C,23,a[35]),b=l(b,c,d,e,p,4,a[36]),e=l(e,b,c,d,s,11,a[37]),d=l(d,e,b,c,v,16,a[38]),c=l(c,d,e,b,y,23,a[39]),b=l(b,c,d,e,B,4,a[40]),e=l(e,b,c,d,j,11,a[41]),d=l(d,e,b,c,r,16,a[42]),c=l(c,d,e,b,u,23,a[43]),b=l(b,c,d,e,x,4,a[44]),e=l(e,b,c,d,A,11,a[45]),d=l(d,e,b,c,D,16,a[46]),c=l(c,d,e,b,q,23,a[47]),b=n(b,c,d,e,j,6,a[48]),e=n(e,b,c,d,v,10,a[49]),d=n(d,e,b,c, +C,15,a[50]),c=n(c,d,e,b,t,21,a[51]),b=n(b,c,d,e,A,6,a[52]),e=n(e,b,c,d,r,10,a[53]),d=n(d,e,b,c,y,15,a[54]),c=n(c,d,e,b,p,21,a[55]),b=n(b,c,d,e,w,6,a[56]),e=n(e,b,c,d,D,10,a[57]),d=n(d,e,b,c,u,15,a[58]),c=n(c,d,e,b,B,21,a[59]),b=n(b,c,d,e,s,6,a[60]),e=n(e,b,c,d,z,10,a[61]),d=n(d,e,b,c,q,15,a[62]),c=n(c,d,e,b,x,21,a[63]);g[0]=g[0]+b|0;g[1]=g[1]+c|0;g[2]=g[2]+d|0;g[3]=g[3]+e|0},_doFinalize:function(){var a=this._data,f=a.words,g=8*this._nDataBytes,j=8*a.sigBytes;f[j>>>5]|=128<<24-j%32;var h=E.floor(g/ +4294967296);f[(j+64>>>9<<4)+15]=(h<<8|h>>>24)&16711935|(h<<24|h>>>8)&4278255360;f[(j+64>>>9<<4)+14]=(g<<8|g>>>24)&16711935|(g<<24|g>>>8)&4278255360;a.sigBytes=4*(f.length+1);this._process();a=this._hash;f=a.words;for(g=0;4>g;g++)j=f[g],f[g]=(j<<8|j>>>24)&16711935|(j<<24|j>>>8)&4278255360;return a},clone:function(){var a=s.clone.call(this);a._hash=this._hash.clone();return a}});r.MD5=s._createHelper(q);r.HmacMD5=s._createHmacHelper(q)})(Math); + +/* +CryptoJS v3.1.2 sha1-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var k=CryptoJS,b=k.lib,m=b.WordArray,l=b.Hasher,d=[],b=k.algo.SHA1=l.extend({_doReset:function(){this._hash=new m.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(n,p){for(var a=this._hash.words,e=a[0],f=a[1],h=a[2],j=a[3],b=a[4],c=0;80>c;c++){if(16>c)d[c]=n[p+c]|0;else{var g=d[c-3]^d[c-8]^d[c-14]^d[c-16];d[c]=g<<1|g>>>31}g=(e<<5|e>>>27)+b+d[c];g=20>c?g+((f&h|~f&j)+1518500249):40>c?g+((f^h^j)+1859775393):60>c?g+((f&h|f&j|h&j)-1894007588):g+((f^h^ +j)-899497514);b=j;j=h;h=f<<30|f>>>2;f=e;e=g}a[0]=a[0]+e|0;a[1]=a[1]+f|0;a[2]=a[2]+h|0;a[3]=a[3]+j|0;a[4]=a[4]+b|0},_doFinalize:function(){var b=this._data,d=b.words,a=8*this._nDataBytes,e=8*b.sigBytes;d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=Math.floor(a/4294967296);d[(e+64>>>9<<4)+15]=a;b.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var b=l.clone.call(this);b._hash=this._hash.clone();return b}});k.SHA1=l._createHelper(b);k.HmacSHA1=l._createHmacHelper(b)})(); + +/* +CryptoJS v3.1.2 sha256-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(k){for(var g=CryptoJS,h=g.lib,v=h.WordArray,j=h.Hasher,h=g.algo,s=[],t=[],u=function(q){return 4294967296*(q-(q|0))|0},l=2,b=0;64>b;){var d;a:{d=l;for(var w=k.sqrt(d),r=2;r<=w;r++)if(!(d%r)){d=!1;break a}d=!0}d&&(8>b&&(s[b]=u(k.pow(l,0.5))),t[b]=u(k.pow(l,1/3)),b++);l++}var n=[],h=h.SHA256=j.extend({_doReset:function(){this._hash=new v.init(s.slice(0))},_doProcessBlock:function(q,h){for(var a=this._hash.words,c=a[0],d=a[1],b=a[2],k=a[3],f=a[4],g=a[5],j=a[6],l=a[7],e=0;64>e;e++){if(16>e)n[e]= +q[h+e]|0;else{var m=n[e-15],p=n[e-2];n[e]=((m<<25|m>>>7)^(m<<14|m>>>18)^m>>>3)+n[e-7]+((p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10)+n[e-16]}m=l+((f<<26|f>>>6)^(f<<21|f>>>11)^(f<<7|f>>>25))+(f&g^~f&j)+t[e]+n[e];p=((c<<30|c>>>2)^(c<<19|c>>>13)^(c<<10|c>>>22))+(c&d^c&b^d&b);l=j;j=g;g=f;f=k+m|0;k=b;b=d;d=c;c=m+p|0}a[0]=a[0]+c|0;a[1]=a[1]+d|0;a[2]=a[2]+b|0;a[3]=a[3]+k|0;a[4]=a[4]+f|0;a[5]=a[5]+g|0;a[6]=a[6]+j|0;a[7]=a[7]+l|0},_doFinalize:function(){var d=this._data,b=d.words,a=8*this._nDataBytes,c=8*d.sigBytes; +b[c>>>5]|=128<<24-c%32;b[(c+64>>>9<<4)+14]=k.floor(a/4294967296);b[(c+64>>>9<<4)+15]=a;d.sigBytes=4*b.length;this._process();return this._hash},clone:function(){var b=j.clone.call(this);b._hash=this._hash.clone();return b}});g.SHA256=j._createHelper(h);g.HmacSHA256=j._createHmacHelper(h)})(Math); + +/* +CryptoJS v3.1.2 sha224-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var b=CryptoJS,d=b.lib.WordArray,a=b.algo,c=a.SHA256,a=a.SHA224=c.extend({_doReset:function(){this._hash=new d.init([3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428])},_doFinalize:function(){var a=c._doFinalize.call(this);a.sigBytes-=4;return a}});b.SHA224=c._createHelper(a);b.HmacSHA224=c._createHmacHelper(a)})(); + +/* +CryptoJS v3.1.2 sha512-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){function a(){return d.create.apply(d,arguments)}for(var n=CryptoJS,r=n.lib.Hasher,e=n.x64,d=e.Word,T=e.WordArray,e=n.algo,ea=[a(1116352408,3609767458),a(1899447441,602891725),a(3049323471,3964484399),a(3921009573,2173295548),a(961987163,4081628472),a(1508970993,3053834265),a(2453635748,2937671579),a(2870763221,3664609560),a(3624381080,2734883394),a(310598401,1164996542),a(607225278,1323610764),a(1426881987,3590304994),a(1925078388,4068182383),a(2162078206,991336113),a(2614888103,633803317), +a(3248222580,3479774868),a(3835390401,2666613458),a(4022224774,944711139),a(264347078,2341262773),a(604807628,2007800933),a(770255983,1495990901),a(1249150122,1856431235),a(1555081692,3175218132),a(1996064986,2198950837),a(2554220882,3999719339),a(2821834349,766784016),a(2952996808,2566594879),a(3210313671,3203337956),a(3336571891,1034457026),a(3584528711,2466948901),a(113926993,3758326383),a(338241895,168717936),a(666307205,1188179964),a(773529912,1546045734),a(1294757372,1522805485),a(1396182291, +2643833823),a(1695183700,2343527390),a(1986661051,1014477480),a(2177026350,1206759142),a(2456956037,344077627),a(2730485921,1290863460),a(2820302411,3158454273),a(3259730800,3505952657),a(3345764771,106217008),a(3516065817,3606008344),a(3600352804,1432725776),a(4094571909,1467031594),a(275423344,851169720),a(430227734,3100823752),a(506948616,1363258195),a(659060556,3750685593),a(883997877,3785050280),a(958139571,3318307427),a(1322822218,3812723403),a(1537002063,2003034995),a(1747873779,3602036899), +a(1955562222,1575990012),a(2024104815,1125592928),a(2227730452,2716904306),a(2361852424,442776044),a(2428436474,593698344),a(2756734187,3733110249),a(3204031479,2999351573),a(3329325298,3815920427),a(3391569614,3928383900),a(3515267271,566280711),a(3940187606,3454069534),a(4118630271,4000239992),a(116418474,1914138554),a(174292421,2731055270),a(289380356,3203993006),a(460393269,320620315),a(685471733,587496836),a(852142971,1086792851),a(1017036298,365543100),a(1126000580,2618297676),a(1288033470, +3409855158),a(1501505948,4234509866),a(1607167915,987167468),a(1816402316,1246189591)],v=[],w=0;80>w;w++)v[w]=a();e=e.SHA512=r.extend({_doReset:function(){this._hash=new T.init([new d.init(1779033703,4089235720),new d.init(3144134277,2227873595),new d.init(1013904242,4271175723),new d.init(2773480762,1595750129),new d.init(1359893119,2917565137),new d.init(2600822924,725511199),new d.init(528734635,4215389547),new d.init(1541459225,327033209)])},_doProcessBlock:function(a,d){for(var f=this._hash.words, +F=f[0],e=f[1],n=f[2],r=f[3],G=f[4],H=f[5],I=f[6],f=f[7],w=F.high,J=F.low,X=e.high,K=e.low,Y=n.high,L=n.low,Z=r.high,M=r.low,$=G.high,N=G.low,aa=H.high,O=H.low,ba=I.high,P=I.low,ca=f.high,Q=f.low,k=w,g=J,z=X,x=K,A=Y,y=L,U=Z,B=M,l=$,h=N,R=aa,C=O,S=ba,D=P,V=ca,E=Q,m=0;80>m;m++){var s=v[m];if(16>m)var j=s.high=a[d+2*m]|0,b=s.low=a[d+2*m+1]|0;else{var j=v[m-15],b=j.high,p=j.low,j=(b>>>1|p<<31)^(b>>>8|p<<24)^b>>>7,p=(p>>>1|b<<31)^(p>>>8|b<<24)^(p>>>7|b<<25),u=v[m-2],b=u.high,c=u.low,u=(b>>>19|c<<13)^(b<< +3|c>>>29)^b>>>6,c=(c>>>19|b<<13)^(c<<3|b>>>29)^(c>>>6|b<<26),b=v[m-7],W=b.high,t=v[m-16],q=t.high,t=t.low,b=p+b.low,j=j+W+(b>>>0

>>0?1:0),b=b+c,j=j+u+(b>>>0>>0?1:0),b=b+t,j=j+q+(b>>>0>>0?1:0);s.high=j;s.low=b}var W=l&R^~l&S,t=h&C^~h&D,s=k&z^k&A^z&A,T=g&x^g&y^x&y,p=(k>>>28|g<<4)^(k<<30|g>>>2)^(k<<25|g>>>7),u=(g>>>28|k<<4)^(g<<30|k>>>2)^(g<<25|k>>>7),c=ea[m],fa=c.high,da=c.low,c=E+((h>>>14|l<<18)^(h>>>18|l<<14)^(h<<23|l>>>9)),q=V+((l>>>14|h<<18)^(l>>>18|h<<14)^(l<<23|h>>>9))+(c>>>0>>0?1: +0),c=c+t,q=q+W+(c>>>0>>0?1:0),c=c+da,q=q+fa+(c>>>0>>0?1:0),c=c+b,q=q+j+(c>>>0>>0?1:0),b=u+T,s=p+s+(b>>>0>>0?1:0),V=S,E=D,S=R,D=C,R=l,C=h,h=B+c|0,l=U+q+(h>>>0>>0?1:0)|0,U=A,B=y,A=z,y=x,z=k,x=g,g=c+b|0,k=q+s+(g>>>0>>0?1:0)|0}J=F.low=J+g;F.high=w+k+(J>>>0>>0?1:0);K=e.low=K+x;e.high=X+z+(K>>>0>>0?1:0);L=n.low=L+y;n.high=Y+A+(L>>>0>>0?1:0);M=r.low=M+B;r.high=Z+U+(M>>>0>>0?1:0);N=G.low=N+h;G.high=$+l+(N>>>0>>0?1:0);O=H.low=O+C;H.high=aa+R+(O>>>0>>0?1:0);P=I.low=P+D; +I.high=ba+S+(P>>>0>>0?1:0);Q=f.low=Q+E;f.high=ca+V+(Q>>>0>>0?1:0)},_doFinalize:function(){var a=this._data,d=a.words,f=8*this._nDataBytes,e=8*a.sigBytes;d[e>>>5]|=128<<24-e%32;d[(e+128>>>10<<5)+30]=Math.floor(f/4294967296);d[(e+128>>>10<<5)+31]=f;a.sigBytes=4*d.length;this._process();return this._hash.toX32()},clone:function(){var a=r.clone.call(this);a._hash=this._hash.clone();return a},blockSize:32});n.SHA512=r._createHelper(e);n.HmacSHA512=r._createHmacHelper(e)})(); + +/* +CryptoJS v3.1.2 sha384-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var c=CryptoJS,a=c.x64,b=a.Word,e=a.WordArray,a=c.algo,d=a.SHA512,a=a.SHA384=d.extend({_doReset:function(){this._hash=new e.init([new b.init(3418070365,3238371032),new b.init(1654270250,914150663),new b.init(2438529370,812702999),new b.init(355462360,4144912697),new b.init(1731405415,4290775857),new b.init(2394180231,1750603025),new b.init(3675008525,1694076839),new b.init(1203062813,3204075428)])},_doFinalize:function(){var a=d._doFinalize.call(this);a.sigBytes-=16;return a}});c.SHA384= +d._createHelper(a);c.HmacSHA384=d._createHmacHelper(a)})(); + +/* +CryptoJS v3.1.2 ripemd160-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +/* + +(c) 2012 by Cedric Mesnil. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +(function(){var q=CryptoJS,d=q.lib,n=d.WordArray,p=d.Hasher,d=q.algo,x=n.create([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]),y=n.create([5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]),z=n.create([11,14,15,12, +5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]),A=n.create([8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]),B=n.create([0,1518500249,1859775393,2400959708,2840853838]),C=n.create([1352829926,1548603684,1836072691, +2053994217,0]),d=d.RIPEMD160=p.extend({_doReset:function(){this._hash=n.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(e,v){for(var b=0;16>b;b++){var c=v+b,f=e[c];e[c]=(f<<8|f>>>24)&16711935|(f<<24|f>>>8)&4278255360}var c=this._hash.words,f=B.words,d=C.words,n=x.words,q=y.words,p=z.words,w=A.words,t,g,h,j,r,u,k,l,m,s;u=t=c[0];k=g=c[1];l=h=c[2];m=j=c[3];s=r=c[4];for(var a,b=0;80>b;b+=1)a=t+e[v+n[b]]|0,a=16>b?a+((g^h^j)+f[0]):32>b?a+((g&h|~g&j)+f[1]):48>b? +a+(((g|~h)^j)+f[2]):64>b?a+((g&j|h&~j)+f[3]):a+((g^(h|~j))+f[4]),a|=0,a=a<>>32-p[b],a=a+r|0,t=r,r=j,j=h<<10|h>>>22,h=g,g=a,a=u+e[v+q[b]]|0,a=16>b?a+((k^(l|~m))+d[0]):32>b?a+((k&m|l&~m)+d[1]):48>b?a+(((k|~l)^m)+d[2]):64>b?a+((k&l|~k&m)+d[3]):a+((k^l^m)+d[4]),a|=0,a=a<>>32-w[b],a=a+s|0,u=s,s=m,m=l<<10|l>>>22,l=k,k=a;a=c[1]+h+m|0;c[1]=c[2]+j+s|0;c[2]=c[3]+r+u|0;c[3]=c[4]+t+k|0;c[4]=c[0]+g+l|0;c[0]=a},_doFinalize:function(){var e=this._data,d=e.words,b=8*this._nDataBytes,c=8*e.sigBytes; +d[c>>>5]|=128<<24-c%32;d[(c+64>>>9<<4)+14]=(b<<8|b>>>24)&16711935|(b<<24|b>>>8)&4278255360;e.sigBytes=4*(d.length+1);this._process();e=this._hash;d=e.words;for(b=0;5>b;b++)c=d[b],d[b]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return e},clone:function(){var d=p.clone.call(this);d._hash=this._hash.clone();return d}});q.RIPEMD160=p._createHelper(d);q.HmacRIPEMD160=p._createHmacHelper(d)})(Math); + +/* +CryptoJS v3.1.2 hmac.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var c=CryptoJS,k=c.enc.Utf8;c.algo.HMAC=c.lib.Base.extend({init:function(a,b){a=this._hasher=new a.init;"string"==typeof b&&(b=k.parse(b));var c=a.blockSize,e=4*c;b.sigBytes>e&&(b=a.finalize(b));b.clamp();for(var f=this._oKey=b.clone(),g=this._iKey=b.clone(),h=f.words,j=g.words,d=0;d>6)+b64map.charAt(e&63)}if(b+1==d.length){e=parseInt(d.substring(b,b+1),16);a+=b64map.charAt(e<<2)}else{if(b+2==d.length){e=parseInt(d.substring(b,b+2),16);a+=b64map.charAt(e>>2)+b64map.charAt((e&3)<<4)}}if(b64pad){while((a.length&3)>0){a+=b64pad}}return a}function b64tohex(f){var d="";var e;var b=0;var c;var a;for(e=0;e>2);c=a&3;b=1}else{if(b==1){d+=int2char((c<<2)|(a>>4));c=a&15;b=2}else{if(b==2){d+=int2char(c);d+=int2char(a>>2);c=a&3;b=3}else{d+=int2char((c<<2)|(a>>4));d+=int2char(a&15);b=0}}}}if(b==1){d+=int2char(c<<2)}return d}function b64toBA(e){var d=b64tohex(e);var c;var b=new Array();for(c=0;2*c=0){var d=a*this[f++]+b[e]+h;h=Math.floor(d/67108864);b[e++]=d&67108863}return h}function am2(f,q,r,e,o,a){var k=q&32767,p=q>>15;while(--a>=0){var d=this[f]&32767;var g=this[f++]>>15;var b=p*d+g*k;d=k*d+((b&32767)<<15)+r[e]+(o&1073741823);o=(d>>>30)+(b>>>15)+p*g+(o>>>30);r[e++]=d&1073741823}return o}function am3(f,q,r,e,o,a){var k=q&16383,p=q>>14;while(--a>=0){var d=this[f]&16383;var g=this[f++]>>14;var b=p*d+g*k;d=k*d+((b&16383)<<14)+r[e]+o;o=(d>>28)+(b>>14)+p*g;r[e++]=d&268435455}return o}if(j_lm&&(navigator.appName=="Microsoft Internet Explorer")){BigInteger.prototype.am=am2;dbits=30}else{if(j_lm&&(navigator.appName!="Netscape")){BigInteger.prototype.am=am1;dbits=26}else{BigInteger.prototype.am=am3;dbits=28}}BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=((1<=0;--a){b[a]=this[a]}b.t=this.t;b.s=this.s}function bnpFromInt(a){this.t=1;this.s=(a<0)?-1:0;if(a>0){this[0]=a}else{if(a<-1){this[0]=a+this.DV}else{this.t=0}}}function nbv(a){var b=nbi();b.fromInt(a);return b}function bnpFromString(h,c){var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==256){e=8}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{this.fromRadix(h,c);return}}}}}}this.t=0;this.s=0;var g=h.length,d=false,f=0;while(--g>=0){var a=(e==8)?h[g]&255:intAt(h,g);if(a<0){if(h.charAt(g)=="-"){d=true}continue}d=false;if(f==0){this[this.t++]=a}else{if(f+e>this.DB){this[this.t-1]|=(a&((1<<(this.DB-f))-1))<>(this.DB-f))}else{this[this.t-1]|=a<=this.DB){f-=this.DB}}if(e==8&&(h[0]&128)!=0){this.s=-1;if(f>0){this[this.t-1]|=((1<<(this.DB-f))-1)<0&&this[this.t-1]==a){--this.t}}function bnToString(c){if(this.s<0){return"-"+this.negate().toString(c)}var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{return this.toRadix(c)}}}}}var g=(1<0){if(j>j)>0){a=true;h=int2char(l)}while(f>=0){if(j>(j+=this.DB-e)}else{l=(this[f]>>(j-=e))&g;if(j<=0){j+=this.DB;--f}}if(l>0){a=true}if(a){h+=int2char(l)}}}return a?h:"0"}function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a}function bnAbs(){return(this.s<0)?this.negate():this}function bnCompareTo(b){var d=this.s-b.s;if(d!=0){return d}var c=this.t;d=c-b.t;if(d!=0){return(this.s<0)?-d:d}while(--c>=0){if((d=this[c]-b[c])!=0){return d}}return 0}function nbits(a){var c=1,b;if((b=a>>>16)!=0){a=b;c+=16}if((b=a>>8)!=0){a=b;c+=8}if((b=a>>4)!=0){a=b;c+=4}if((b=a>>2)!=0){a=b;c+=2}if((b=a>>1)!=0){a=b;c+=1}return c}function bnBitLength(){if(this.t<=0){return 0}return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM))}function bnpDLShiftTo(c,b){var a;for(a=this.t-1;a>=0;--a){b[a+c]=this[a]}for(a=c-1;a>=0;--a){b[a]=0}b.t=this.t+c;b.s=this.s}function bnpDRShiftTo(c,b){for(var a=c;a=0;--d){e[d+f+1]=(this[d]>>a)|h;h=(this[d]&g)<=0;--d){e[d]=0}e[f]=h;e.t=this.t+f+1;e.s=this.s;e.clamp()}function bnpRShiftTo(g,d){d.s=this.s;var e=Math.floor(g/this.DB);if(e>=this.t){d.t=0;return}var b=g%this.DB;var a=this.DB-b;var f=(1<>b;for(var c=e+1;c>b}if(b>0){d[this.t-e-1]|=(this.s&f)<>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g-=d.s}f.s=(g<0)?-1:0;if(g<-1){f[e++]=this.DV+g}else{if(g>0){f[e++]=g}}f.t=e;f.clamp()}function bnpMultiplyTo(c,e){var b=this.abs(),f=c.abs();var d=b.t;e.t=d+f.t;while(--d>=0){e[d]=0}for(d=0;d=0){d[b]=0}for(b=0;b=a.DV){d[b+a.t]-=a.DV;d[b+a.t+1]=1}}if(d.t>0){d[d.t-1]+=a.am(b,a[b],d,2*b,0,1)}d.s=0;d.clamp()}function bnpDivRemTo(n,h,g){var w=n.abs();if(w.t<=0){return}var k=this.abs();if(k.t0){w.lShiftTo(v,d);k.lShiftTo(v,g)}else{w.copyTo(d);k.copyTo(g)}var p=d.t;var b=d[p-1];if(b==0){return}var o=b*(1<1)?d[p-2]>>this.F2:0);var A=this.FV/o,z=(1<=0){g[g.t++]=1;g.subTo(f,g)}BigInteger.ONE.dlShiftTo(p,f);f.subTo(d,d);while(d.t=0){var c=(g[--u]==b)?this.DM:Math.floor(g[u]*A+(g[u-1]+x)*z);if((g[u]+=d.am(0,c,g,s,0,p))0){g.rShiftTo(v,g)}if(a<0){BigInteger.ZERO.subTo(g,g)}}function bnMod(b){var c=nbi();this.abs().divRemTo(b,null,c);if(this.s<0&&c.compareTo(BigInteger.ZERO)>0){b.subTo(c,c)}return c}function Classic(a){this.m=a}function cConvert(a){if(a.s<0||a.compareTo(this.m)>=0){return a.mod(this.m)}else{return a}}function cRevert(a){return a}function cReduce(a){a.divRemTo(this.m,null,a)}function cMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}function cSqrTo(a,b){a.squareTo(b);this.reduce(b)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1){return 0}var a=this[0];if((a&1)==0){return 0}var b=a&3;b=(b*(2-(a&15)*b))&15;b=(b*(2-(a&255)*b))&255;b=(b*(2-(((a&65535)*b)&65535)))&65535;b=(b*(2-a*b%this.DV))%this.DV;return(b>0)?this.DV-b:-b}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<(a.DB-15))-1;this.mt2=2*a.t}function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);if(a.s<0&&b.compareTo(BigInteger.ZERO)>0){this.m.subTo(b,b)}return b}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b}function montReduce(a){while(a.t<=this.mt2){a[a.t++]=0}for(var c=0;c>15)*this.mpl)&this.um)<<15))&a.DM;b=c+this.m.t;a[b]+=this.m.am(0,d,a,c,0,this.m.t);while(a[b]>=a.DV){a[b]-=a.DV;a[++b]++}}a.clamp();a.drShiftTo(this.m.t,a);if(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function montSqrTo(a,b){a.squareTo(b);this.reduce(b)}function montMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return((this.t>0)?(this[0]&1):this.s)==0}function bnpExp(h,j){if(h>4294967295||h<1){return BigInteger.ONE}var f=nbi(),a=nbi(),d=j.convert(this),c=nbits(h)-1;d.copyTo(f);while(--c>=0){j.sqrTo(f,a);if((h&(1<0){j.mulTo(a,d,f)}else{var b=f;f=a;a=b}}return j.revert(f)}function bnModPowInt(b,a){var c;if(b<256||a.isEven()){c=new Classic(a)}else{c=new Montgomery(a)}return this.exp(b,c)}BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1); +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +function bnClone(){var a=nbi();this.copyTo(a);return a}function bnIntValue(){if(this.s<0){if(this.t==1){return this[0]-this.DV}else{if(this.t==0){return -1}}}else{if(this.t==1){return this[0]}else{if(this.t==0){return 0}}}return((this[1]&((1<<(32-this.DB))-1))<>24}function bnShortValue(){return(this.t==0)?this.s:(this[0]<<16)>>16}function bnpChunkSize(a){return Math.floor(Math.LN2*this.DB/Math.log(a))}function bnSigNum(){if(this.s<0){return -1}else{if(this.t<=0||(this.t==1&&this[0]<=0)){return 0}else{return 1}}}function bnpToRadix(c){if(c==null){c=10}if(this.signum()==0||c<2||c>36){return"0"}var f=this.chunkSize(c);var e=Math.pow(c,f);var i=nbv(e),j=nbi(),h=nbi(),g="";this.divRemTo(i,j,h);while(j.signum()>0){g=(e+h.intValue()).toString(c).substr(1)+g;j.divRemTo(i,j,h)}return h.intValue().toString(c)+g}function bnpFromRadix(m,h){this.fromInt(0);if(h==null){h=10}var f=this.chunkSize(h);var g=Math.pow(h,f),e=false,a=0,l=0;for(var c=0;c=f){this.dMultiply(g);this.dAddOffset(l,0);a=0;l=0}}if(a>0){this.dMultiply(Math.pow(h,a));this.dAddOffset(l,0)}if(e){BigInteger.ZERO.subTo(this,this)}}function bnpFromNumber(f,e,h){if("number"==typeof e){if(f<2){this.fromInt(1)}else{this.fromNumber(f,h);if(!this.testBit(f-1)){this.bitwiseTo(BigInteger.ONE.shiftLeft(f-1),op_or,this)}if(this.isEven()){this.dAddOffset(1,0)}while(!this.isProbablePrime(e)){this.dAddOffset(2,0);if(this.bitLength()>f){this.subTo(BigInteger.ONE.shiftLeft(f-1),this)}}}}else{var d=new Array(),g=f&7;d.length=(f>>3)+1;e.nextBytes(d);if(g>0){d[0]&=((1<0){if(e>e)!=(this.s&this.DM)>>e){c[a++]=f|(this.s<<(this.DB-e))}while(b>=0){if(e<8){f=(this[b]&((1<>(e+=this.DB-8)}else{f=(this[b]>>(e-=8))&255;if(e<=0){e+=this.DB;--b}}if((f&128)!=0){f|=-256}if(a==0&&(this.s&128)!=(f&128)){++a}if(a>0||f!=this.s){c[a++]=f}}}return c}function bnEquals(b){return(this.compareTo(b)==0)}function bnMin(b){return(this.compareTo(b)<0)?this:b}function bnMax(b){return(this.compareTo(b)>0)?this:b}function bnpBitwiseTo(c,h,e){var d,g,b=Math.min(c.t,this.t);for(d=0;d>=16;b+=16}if((a&255)==0){a>>=8;b+=8}if((a&15)==0){a>>=4;b+=4}if((a&3)==0){a>>=2;b+=2}if((a&1)==0){++b}return b}function bnGetLowestSetBit(){for(var a=0;a=this.t){return(this.s!=0)}return((this[a]&(1<<(b%this.DB)))!=0)}function bnpChangeBit(c,b){var a=BigInteger.ONE.shiftLeft(c);this.bitwiseTo(a,b,a);return a}function bnSetBit(a){return this.changeBit(a,op_or)}function bnClearBit(a){return this.changeBit(a,op_andnot)}function bnFlipBit(a){return this.changeBit(a,op_xor)}function bnpAddTo(d,f){var e=0,g=0,b=Math.min(d.t,this.t);while(e>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g+=d.s}f.s=(g<0)?-1:0;if(g>0){f[e++]=g}else{if(g<-1){f[e++]=this.DV+g}}f.t=e;f.clamp()}function bnAdd(b){var c=nbi();this.addTo(b,c);return c}function bnSubtract(b){var c=nbi();this.subTo(b,c);return c}function bnMultiply(b){var c=nbi();this.multiplyTo(b,c);return c}function bnSquare(){var a=nbi();this.squareTo(a);return a}function bnDivide(b){var c=nbi();this.divRemTo(b,c,null);return c}function bnRemainder(b){var c=nbi();this.divRemTo(b,null,c);return c}function bnDivideAndRemainder(b){var d=nbi(),c=nbi();this.divRemTo(b,d,c);return new Array(d,c)}function bnpDMultiply(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()}function bnpDAddOffset(b,a){if(b==0){return}while(this.t<=a){this[this.t++]=0}this[a]+=b;while(this[a]>=this.DV){this[a]-=this.DV;if(++a>=this.t){this[this.t++]=0}++this[a]}}function NullExp(){}function nNop(a){return a}function nMulTo(a,c,b){a.multiplyTo(c,b)}function nSqrTo(a,b){a.squareTo(b)}NullExp.prototype.convert=nNop;NullExp.prototype.revert=nNop;NullExp.prototype.mulTo=nMulTo;NullExp.prototype.sqrTo=nSqrTo;function bnPow(a){return this.exp(a,new NullExp())}function bnpMultiplyLowerTo(b,f,e){var d=Math.min(this.t+b.t,f);e.s=0;e.t=d;while(d>0){e[--d]=0}var c;for(c=e.t-this.t;d=0){d[c]=0}for(c=Math.max(e-this.t,0);c2*this.m.t){return a.mod(this.m)}else{if(a.compareTo(this.m)<0){return a}else{var b=nbi();a.copyTo(b);this.reduce(b);return b}}}function barrettRevert(a){return a}function barrettReduce(a){a.drShiftTo(this.m.t-1,this.r2);if(a.t>this.m.t+1){a.t=this.m.t+1;a.clamp()}this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);while(a.compareTo(this.r2)<0){a.dAddOffset(1,this.m.t+1)}a.subTo(this.r2,a);while(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function barrettSqrTo(a,b){a.squareTo(b);this.reduce(b)}function barrettMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Barrett.prototype.convert=barrettConvert;Barrett.prototype.revert=barrettRevert;Barrett.prototype.reduce=barrettReduce;Barrett.prototype.mulTo=barrettMulTo;Barrett.prototype.sqrTo=barrettSqrTo;function bnModPow(q,f){var o=q.bitLength(),h,b=nbv(1),v;if(o<=0){return b}else{if(o<18){h=1}else{if(o<48){h=3}else{if(o<144){h=4}else{if(o<768){h=5}else{h=6}}}}}if(o<8){v=new Classic(f)}else{if(f.isEven()){v=new Barrett(f)}else{v=new Montgomery(f)}}var p=new Array(),d=3,s=h-1,a=(1<1){var A=nbi();v.sqrTo(p[1],A);while(d<=a){p[d]=nbi();v.mulTo(A,p[d-2],p[d]);d+=2}}var l=q.t-1,x,u=true,c=nbi(),y;o=nbits(q[l])-1;while(l>=0){if(o>=s){x=(q[l]>>(o-s))&a}else{x=(q[l]&((1<<(o+1))-1))<<(s-o);if(l>0){x|=q[l-1]>>(this.DB+o-s)}}d=h;while((x&1)==0){x>>=1;--d}if((o-=d)<0){o+=this.DB;--l}if(u){p[x].copyTo(b);u=false}else{while(d>1){v.sqrTo(b,c);v.sqrTo(c,b);d-=2}if(d>0){v.sqrTo(b,c)}else{y=b;b=c;c=y}v.mulTo(c,p[x],b)}while(l>=0&&(q[l]&(1<0){b.rShiftTo(f,b);h.rShiftTo(f,h)}while(b.signum()>0){if((d=b.getLowestSetBit())>0){b.rShiftTo(d,b)}if((d=h.getLowestSetBit())>0){h.rShiftTo(d,h)}if(b.compareTo(h)>=0){b.subTo(h,b);b.rShiftTo(1,b)}else{h.subTo(b,h);h.rShiftTo(1,h)}}if(f>0){h.lShiftTo(f,h)}return h}function bnpModInt(e){if(e<=0){return 0}var c=this.DV%e,b=(this.s<0)?e-1:0;if(this.t>0){if(c==0){b=this[0]%e}else{for(var a=this.t-1;a>=0;--a){b=(c*b+this[a])%e}}}return b}function bnModInverse(f){var j=f.isEven();if((this.isEven()&&j)||f.signum()==0){return BigInteger.ZERO}var i=f.clone(),h=this.clone();var g=nbv(1),e=nbv(0),l=nbv(0),k=nbv(1);while(i.signum()!=0){while(i.isEven()){i.rShiftTo(1,i);if(j){if(!g.isEven()||!e.isEven()){g.addTo(this,g);e.subTo(f,e)}g.rShiftTo(1,g)}else{if(!e.isEven()){e.subTo(f,e)}}e.rShiftTo(1,e)}while(h.isEven()){h.rShiftTo(1,h);if(j){if(!l.isEven()||!k.isEven()){l.addTo(this,l);k.subTo(f,k)}l.rShiftTo(1,l)}else{if(!k.isEven()){k.subTo(f,k)}}k.rShiftTo(1,k)}if(i.compareTo(h)>=0){i.subTo(h,i);if(j){g.subTo(l,g)}e.subTo(k,e)}else{h.subTo(i,h);if(j){l.subTo(g,l)}k.subTo(e,k)}}if(h.compareTo(BigInteger.ONE)!=0){return BigInteger.ZERO}if(k.compareTo(f)>=0){return k.subtract(f)}if(k.signum()<0){k.addTo(f,k)}else{return k}if(k.signum()<0){return k.add(f)}else{return k}}var lowprimes=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];var lplim=(1<<26)/lowprimes[lowprimes.length-1];function bnIsProbablePrime(e){var d,b=this.abs();if(b.t==1&&b[0]<=lowprimes[lowprimes.length-1]){for(d=0;d>1;if(f>lowprimes.length){f=lowprimes.length}var b=nbi();for(var e=0;e>8)&255;rng_pool[rng_pptr++]^=(a>>16)&255;rng_pool[rng_pptr++]^=(a>>24)&255;if(rng_pptr>=rng_psize){rng_pptr-=rng_psize}}function rng_seed_time(){rng_seed_int(new Date().getTime())}if(rng_pool==null){rng_pool=new Array();rng_pptr=0;var t;if(window!==undefined&&(window.crypto!==undefined||window.msCrypto!==undefined)){var crypto=window.crypto||window.msCrypto;if(crypto.getRandomValues){var ua=new Uint8Array(32);crypto.getRandomValues(ua);for(t=0;t<32;++t){rng_pool[rng_pptr++]=ua[t]}}else{if(navigator.appName=="Netscape"&&navigator.appVersion<"5"){var z=window.crypto.random(32);for(t=0;t>>8;rng_pool[rng_pptr++]=t&255}rng_pptr=0;rng_seed_time()}function rng_get_byte(){if(rng_state==null){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr=0&&h>0){var f=e.charCodeAt(d--);if(f<128){g[--h]=f}else{if((f>127)&&(f<2048)){g[--h]=(f&63)|128;g[--h]=(f>>6)|192}else{g[--h]=(f&63)|128;g[--h]=((f>>6)&63)|128;g[--h]=(f>>12)|224}}}g[--h]=0;var b=new SecureRandom();var a=new Array();while(h>2){a[0]=0;while(a[0]==0){b.nextBytes(a)}g[--h]=a[0]}g[--h]=2;g[--h]=0;return new BigInteger(g)}function oaep_mgf1_arr(c,a,e){var b="",d=0;while(b.length>24,(d&16711680)>>16,(d&65280)>>8,d&255])));d+=1}return b}function oaep_pad(q,a,f,l){var c=KJUR.crypto.MessageDigest;var o=KJUR.crypto.Util;var b=null;if(!f){f="sha1"}if(typeof f==="string"){b=c.getCanonicalAlgName(f);l=c.getHashLength(b);f=function(i){return hextorstr(o.hashHex(rstrtohex(i),b))}}if(q.length+2*l+2>a){throw"Message too long for RSA"}var k="",e;for(e=0;e0&&a.length>0){this.n=parseBigInt(b,16);this.e=parseInt(a,16)}else{throw"Invalid RSA public key"}}}function RSADoPublic(a){return a.modPowInt(this.e,this.n)}function RSAEncrypt(d){var a=pkcs1pad2(d,(this.n.bitLength()+7)>>3);if(a==null){return null}var e=this.doPublic(a);if(e==null){return null}var b=e.toString(16);if((b.length&1)==0){return b}else{return"0"+b}}function RSAEncryptOAEP(f,e,b){var a=oaep_pad(f,(this.n.bitLength()+7)>>3,e,b);if(a==null){return null}var g=this.doPublic(a);if(g==null){return null}var d=g.toString(16);if((d.length&1)==0){return d}else{return"0"+d}}RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;RSAKey.prototype.encryptOAEP=RSAEncryptOAEP;RSAKey.prototype.type="RSA"; +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +function pkcs1unpad2(g,j){var a=g.toByteArray();var f=0;while(f=a.length){return null}}var e="";while(++f191)&&(h<224)){e+=String.fromCharCode(((h&31)<<6)|(a[f+1]&63));++f}else{e+=String.fromCharCode(((h&15)<<12)|((a[f+1]&63)<<6)|(a[f+2]&63));f+=2}}}return e}function oaep_mgf1_str(c,a,e){var b="",d=0;while(b.length>24,(d&16711680)>>16,(d&65280)>>8,d&255]));d+=1}return b}function oaep_unpad(o,b,g,p){var e=KJUR.crypto.MessageDigest;var r=KJUR.crypto.Util;var c=null;if(!g){g="sha1"}if(typeof g==="string"){c=e.getCanonicalAlgName(g);p=e.getHashLength(c);g=function(d){return hextorstr(r.hashHex(rstrtohex(d),c))}}o=o.toByteArray();var h;for(h=0;h0&&a.length>0){this.n=parseBigInt(c,16);this.e=parseInt(a,16);this.d=parseBigInt(b,16)}else{throw"Invalid RSA private key"}}}function RSASetPrivateEx(g,d,e,c,b,a,h,f){this.isPrivate=true;this.isPublic=false;if(g==null){throw"RSASetPrivateEx N == null"}if(d==null){throw"RSASetPrivateEx E == null"}if(g.length==0){throw"RSASetPrivateEx N.length == 0"}if(d.length==0){throw"RSASetPrivateEx E.length == 0"}if(g!=null&&d!=null&&g.length>0&&d.length>0){this.n=parseBigInt(g,16);this.e=parseInt(d,16);this.d=parseBigInt(e,16);this.p=parseBigInt(c,16);this.q=parseBigInt(b,16);this.dmp1=parseBigInt(a,16);this.dmq1=parseBigInt(h,16);this.coeff=parseBigInt(f,16)}else{throw"Invalid RSA private key in RSASetPrivateEx"}}function RSAGenerate(b,i){var a=new SecureRandom();var f=b>>1;this.e=parseInt(i,16);var c=new BigInteger(i,16);for(;;){for(;;){this.p=new BigInteger(b-f,1,a);if(this.p.subtract(BigInteger.ONE).gcd(c).compareTo(BigInteger.ONE)==0&&this.p.isProbablePrime(10)){break}}for(;;){this.q=new BigInteger(f,1,a);if(this.q.subtract(BigInteger.ONE).gcd(c).compareTo(BigInteger.ONE)==0&&this.q.isProbablePrime(10)){break}}if(this.p.compareTo(this.q)<=0){var h=this.p;this.p=this.q;this.q=h}var g=this.p.subtract(BigInteger.ONE);var d=this.q.subtract(BigInteger.ONE);var e=g.multiply(d);if(e.gcd(c).compareTo(BigInteger.ONE)==0){this.n=this.p.multiply(this.q);this.d=c.modInverse(e);this.dmp1=this.d.mod(g);this.dmq1=this.d.mod(d);this.coeff=this.q.modInverse(this.p);break}}this.isPrivate=true}function RSADoPrivate(a){if(this.p==null||this.q==null){return a.modPow(this.d,this.n)}var c=a.mod(this.p).modPow(this.dmp1,this.p);var b=a.mod(this.q).modPow(this.dmq1,this.q);while(c.compareTo(b)<0){c=c.add(this.p)}return c.subtract(b).multiply(this.coeff).mod(this.p).multiply(this.q).add(b)}function RSADecrypt(b){var d=parseBigInt(b,16);var a=this.doPrivate(d);if(a==null){return null}return pkcs1unpad2(a,(this.n.bitLength()+7)>>3)}function RSADecryptOAEP(e,d,b){var f=parseBigInt(e,16);var a=this.doPrivate(f);if(a==null){return null}return oaep_unpad(a,(this.n.bitLength()+7)>>3,d,b)}RSAKey.prototype.doPrivate=RSADoPrivate;RSAKey.prototype.setPrivate=RSASetPrivate;RSAKey.prototype.setPrivateEx=RSASetPrivateEx;RSAKey.prototype.generate=RSAGenerate;RSAKey.prototype.decrypt=RSADecrypt;RSAKey.prototype.decryptOAEP=RSADecryptOAEP; +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +function ECFieldElementFp(b,a){this.x=a;this.q=b}function feFpEquals(a){if(a==this){return true}return(this.q.equals(a.q)&&this.x.equals(a.x))}function feFpToBigInteger(){return this.x}function feFpNegate(){return new ECFieldElementFp(this.q,this.x.negate().mod(this.q))}function feFpAdd(a){return new ECFieldElementFp(this.q,this.x.add(a.toBigInteger()).mod(this.q))}function feFpSubtract(a){return new ECFieldElementFp(this.q,this.x.subtract(a.toBigInteger()).mod(this.q))}function feFpMultiply(a){return new ECFieldElementFp(this.q,this.x.multiply(a.toBigInteger()).mod(this.q))}function feFpSquare(){return new ECFieldElementFp(this.q,this.x.square().mod(this.q))}function feFpDivide(a){return new ECFieldElementFp(this.q,this.x.multiply(a.toBigInteger().modInverse(this.q)).mod(this.q))}ECFieldElementFp.prototype.equals=feFpEquals;ECFieldElementFp.prototype.toBigInteger=feFpToBigInteger;ECFieldElementFp.prototype.negate=feFpNegate;ECFieldElementFp.prototype.add=feFpAdd;ECFieldElementFp.prototype.subtract=feFpSubtract;ECFieldElementFp.prototype.multiply=feFpMultiply;ECFieldElementFp.prototype.square=feFpSquare;ECFieldElementFp.prototype.divide=feFpDivide;function ECPointFp(c,a,d,b){this.curve=c;this.x=a;this.y=d;if(b==null){this.z=BigInteger.ONE}else{this.z=b}this.zinv=null}function pointFpGetX(){if(this.zinv==null){this.zinv=this.z.modInverse(this.curve.q)}return this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q))}function pointFpGetY(){if(this.zinv==null){this.zinv=this.z.modInverse(this.curve.q)}return this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q))}function pointFpEquals(a){if(a==this){return true}if(this.isInfinity()){return a.isInfinity()}if(a.isInfinity()){return this.isInfinity()}var c,b;c=a.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(a.z)).mod(this.curve.q);if(!c.equals(BigInteger.ZERO)){return false}b=a.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(a.z)).mod(this.curve.q);return b.equals(BigInteger.ZERO)}function pointFpIsInfinity(){if((this.x==null)&&(this.y==null)){return true}return this.z.equals(BigInteger.ZERO)&&!this.y.toBigInteger().equals(BigInteger.ZERO)}function pointFpNegate(){return new ECPointFp(this.curve,this.x,this.y.negate(),this.z)}function pointFpAdd(l){if(this.isInfinity()){return l}if(l.isInfinity()){return this}var p=l.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(l.z)).mod(this.curve.q);var o=l.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(l.z)).mod(this.curve.q);if(BigInteger.ZERO.equals(o)){if(BigInteger.ZERO.equals(p)){return this.twice()}return this.curve.getInfinity()}var j=new BigInteger("3");var e=this.x.toBigInteger();var n=this.y.toBigInteger();var c=l.x.toBigInteger();var k=l.y.toBigInteger();var m=o.square();var i=m.multiply(o);var d=e.multiply(m);var g=p.square().multiply(this.z);var a=g.subtract(d.shiftLeft(1)).multiply(l.z).subtract(i).multiply(o).mod(this.curve.q);var h=d.multiply(j).multiply(p).subtract(n.multiply(i)).subtract(g.multiply(p)).multiply(l.z).add(p.multiply(i)).mod(this.curve.q);var f=i.multiply(this.z).multiply(l.z).mod(this.curve.q);return new ECPointFp(this.curve,this.curve.fromBigInteger(a),this.curve.fromBigInteger(h),f)}function pointFpTwice(){if(this.isInfinity()){return this}if(this.y.toBigInteger().signum()==0){return this.curve.getInfinity()}var g=new BigInteger("3");var c=this.x.toBigInteger();var h=this.y.toBigInteger();var e=h.multiply(this.z);var j=e.multiply(h).mod(this.curve.q);var i=this.curve.a.toBigInteger();var k=c.square().multiply(g);if(!BigInteger.ZERO.equals(i)){k=k.add(this.z.square().multiply(i))}k=k.mod(this.curve.q);var b=k.square().subtract(c.shiftLeft(3).multiply(j)).shiftLeft(1).multiply(e).mod(this.curve.q);var f=k.multiply(g).multiply(c).subtract(j.shiftLeft(1)).shiftLeft(2).multiply(j).subtract(k.square().multiply(k)).mod(this.curve.q);var d=e.square().multiply(e).shiftLeft(3).mod(this.curve.q);return new ECPointFp(this.curve,this.curve.fromBigInteger(b),this.curve.fromBigInteger(f),d)}function pointFpMultiply(b){if(this.isInfinity()){return this}if(b.signum()==0){return this.curve.getInfinity()}var g=b;var f=g.multiply(new BigInteger("3"));var l=this.negate();var d=this;var c;for(c=f.bitLength()-2;c>0;--c){d=d.twice();var a=f.testBit(c);var j=g.testBit(c);if(a!=j){d=d.add(a?this:l)}}return d}function pointFpMultiplyTwo(c,a,b){var d;if(c.bitLength()>b.bitLength()){d=c.bitLength()-1}else{d=b.bitLength()-1}var f=this.curve.getInfinity();var e=this.add(a);while(d>=0){f=f.twice();if(c.testBit(d)){if(b.testBit(d)){f=f.add(e)}else{f=f.add(this)}}else{if(b.testBit(d)){f=f.add(a)}}--d}return f}ECPointFp.prototype.getX=pointFpGetX;ECPointFp.prototype.getY=pointFpGetY;ECPointFp.prototype.equals=pointFpEquals;ECPointFp.prototype.isInfinity=pointFpIsInfinity;ECPointFp.prototype.negate=pointFpNegate;ECPointFp.prototype.add=pointFpAdd;ECPointFp.prototype.twice=pointFpTwice;ECPointFp.prototype.multiply=pointFpMultiply;ECPointFp.prototype.multiplyTwo=pointFpMultiplyTwo;function ECCurveFp(e,d,c){this.q=e;this.a=this.fromBigInteger(d);this.b=this.fromBigInteger(c);this.infinity=new ECPointFp(this,null,null)}function curveFpGetQ(){return this.q}function curveFpGetA(){return this.a}function curveFpGetB(){return this.b}function curveFpEquals(a){if(a==this){return true}return(this.q.equals(a.q)&&this.a.equals(a.a)&&this.b.equals(a.b))}function curveFpGetInfinity(){return this.infinity}function curveFpFromBigInteger(a){return new ECFieldElementFp(this.q,a)}function curveFpDecodePointHex(d){switch(parseInt(d.substr(0,2),16)){case 0:return this.infinity;case 2:case 3:return null;case 4:case 6:case 7:var a=(d.length-2)/2;var c=d.substr(2,a);var b=d.substr(a+2,a);return new ECPointFp(this,this.fromBigInteger(new BigInteger(c,16)),this.fromBigInteger(new BigInteger(b,16)));default:return null}}ECCurveFp.prototype.getQ=curveFpGetQ;ECCurveFp.prototype.getA=curveFpGetA;ECCurveFp.prototype.getB=curveFpGetB;ECCurveFp.prototype.equals=curveFpEquals;ECCurveFp.prototype.getInfinity=curveFpGetInfinity;ECCurveFp.prototype.fromBigInteger=curveFpFromBigInteger;ECCurveFp.prototype.decodePointHex=curveFpDecodePointHex; +/*! (c) Stefan Thomas | https://github.com/bitcoinjs/bitcoinjs-lib + */ +ECFieldElementFp.prototype.getByteLength=function(){return Math.floor((this.toBigInteger().bitLength()+7)/8)};ECPointFp.prototype.getEncoded=function(c){var d=function(h,f){var g=h.toByteArrayUnsigned();if(fg.length){g.unshift(0)}}return g};var a=this.getX().toBigInteger();var e=this.getY().toBigInteger();var b=d(a,32);if(c){if(e.isEven()){b.unshift(2)}else{b.unshift(3)}}else{b.unshift(4);b=b.concat(d(e,32))}return b};ECPointFp.decodeFrom=function(g,c){var f=c[0];var e=c.length-1;var d=c.slice(1,1+e/2);var b=c.slice(1+e/2,1+e);d.unshift(0);b.unshift(0);var a=new BigInteger(d);var h=new BigInteger(b);return new ECPointFp(g,g.fromBigInteger(a),g.fromBigInteger(h))};ECPointFp.decodeFromHex=function(g,c){var f=c.substr(0,2);var e=c.length-2;var d=c.substr(2,e/2);var b=c.substr(2+e/2,e/2);var a=new BigInteger(d,16);var h=new BigInteger(b,16);return new ECPointFp(g,g.fromBigInteger(a),g.fromBigInteger(h))};ECPointFp.prototype.add2D=function(c){if(this.isInfinity()){return c}if(c.isInfinity()){return this}if(this.x.equals(c.x)){if(this.y.equals(c.y)){return this.twice()}return this.curve.getInfinity()}var g=c.x.subtract(this.x);var e=c.y.subtract(this.y);var a=e.divide(g);var d=a.square().subtract(this.x).subtract(c.x);var f=a.multiply(this.x.subtract(d)).subtract(this.y);return new ECPointFp(this.curve,d,f)};ECPointFp.prototype.twice2D=function(){if(this.isInfinity()){return this}if(this.y.toBigInteger().signum()==0){return this.curve.getInfinity()}var b=this.curve.fromBigInteger(BigInteger.valueOf(2));var e=this.curve.fromBigInteger(BigInteger.valueOf(3));var a=this.x.square().multiply(e).add(this.curve.a).divide(this.y.multiply(b));var c=a.square().subtract(this.x.multiply(b));var d=a.multiply(this.x.subtract(c)).subtract(this.y);return new ECPointFp(this.curve,c,d)};ECPointFp.prototype.multiply2D=function(b){if(this.isInfinity()){return this}if(b.signum()==0){return this.curve.getInfinity()}var g=b;var f=g.multiply(new BigInteger("3"));var l=this.negate();var d=this;var c;for(c=f.bitLength()-2;c>0;--c){d=d.twice();var a=f.testBit(c);var j=g.testBit(c);if(a!=j){d=d.add2D(a?this:l)}}return d};ECPointFp.prototype.isOnCurve=function(){var d=this.getX().toBigInteger();var i=this.getY().toBigInteger();var f=this.curve.getA().toBigInteger();var c=this.curve.getB().toBigInteger();var h=this.curve.getQ();var e=i.multiply(i).mod(h);var g=d.multiply(d).multiply(d).add(f.multiply(d)).add(c).mod(h);return e.equals(g)};ECPointFp.prototype.toString=function(){return"("+this.getX().toBigInteger().toString()+","+this.getY().toBigInteger().toString()+")"};ECPointFp.prototype.validate=function(){var c=this.curve.getQ();if(this.isInfinity()){throw new Error("Point is at infinity.")}var a=this.getX().toBigInteger();var b=this.getY().toBigInteger();if(a.compareTo(BigInteger.ONE)<0||a.compareTo(c.subtract(BigInteger.ONE))>0){throw new Error("x coordinate out of bounds")}if(b.compareTo(BigInteger.ONE)<0||b.compareTo(c.subtract(BigInteger.ONE))>0){throw new Error("y coordinate out of bounds")}if(!this.isOnCurve()){throw new Error("Point is not on the curve.")}if(this.multiply(c).isInfinity()){throw new Error("Point is not a scalar multiple of G.")}return true}; +/*! Mike Samuel (c) 2009 | code.google.com/p/json-sans-eval + */ +var jsonParse=(function(){var e="(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)";var j='(?:[^\\0-\\x08\\x0a-\\x1f"\\\\]|\\\\(?:["/\\\\bfnrt]|u[0-9A-Fa-f]{4}))';var i='(?:"'+j+'*")';var d=new RegExp("(?:false|true|null|[\\{\\}\\[\\]]|"+e+"|"+i+")","g");var k=new RegExp("\\\\(?:([^u])|u(.{4}))","g");var g={'"':'"',"/":"/","\\":"\\",b:"\b",f:"\f",n:"\n",r:"\r",t:"\t"};function h(l,m,n){return m?g[m]:String.fromCharCode(parseInt(n,16))}var c=new String("");var a="\\";var f={"{":Object,"[":Array};var b=Object.hasOwnProperty;return function(u,q){var p=u.match(d);var x;var v=p[0];var l=false;if("{"===v){x={}}else{if("["===v){x=[]}else{x=[];l=true}}var t;var r=[x];for(var o=1-l,m=p.length;o=0;){delete D[n[A]]}}}return q.call(C,B,D)};x=s({"":x},"")}return x}})(); +if(typeof KJUR=="undefined"||!KJUR){KJUR={}}if(typeof KJUR.asn1=="undefined"||!KJUR.asn1){KJUR.asn1={}}KJUR.asn1.ASN1Util=new function(){this.integerToByteHex=function(a){var b=a.toString(16);if((b.length%2)==1){b="0"+b}return b};this.bigIntToMinTwosComplementsHex=function(j){var f=j.toString(16);if(f.substr(0,1)!="-"){if(f.length%2==1){f="0"+f}else{if(!f.match(/^[0-7]/)){f="00"+f}}}else{var a=f.substr(1);var e=a.length;if(e%2==1){e+=1}else{if(!f.match(/^[0-7]/)){e+=2}}var g="";for(var d=0;d15){throw"ASN.1 length too long to represent by 8x: n = "+i.toString(16)}var f=128+g;return f.toString(16)+h}};this.getEncodedHex=function(){if(this.hTLV==null||this.isModified){this.hV=this.getFreshValueHex();this.hL=this.getLengthHexFromValue();this.hTLV=this.hT+this.hL+this.hV;this.isModified=false}return this.hTLV};this.getValueHex=function(){this.getEncodedHex();return this.hV};this.getFreshValueHex=function(){return""}};KJUR.asn1.DERAbstractString=function(c){KJUR.asn1.DERAbstractString.superclass.constructor.call(this);var b=null;var a=null;this.getString=function(){return this.s};this.setString=function(d){this.hTLV=null;this.isModified=true;this.s=d;this.hV=utf8tohex(this.s).toLowerCase()};this.setStringHex=function(d){this.hTLV=null;this.isModified=true;this.s=null;this.hV=d};this.getFreshValueHex=function(){return this.hV};if(typeof c!="undefined"){if(typeof c=="string"){this.setString(c)}else{if(typeof c.str!="undefined"){this.setString(c.str)}else{if(typeof c.hex!="undefined"){this.setStringHex(c.hex)}}}}};YAHOO.lang.extend(KJUR.asn1.DERAbstractString,KJUR.asn1.ASN1Object);KJUR.asn1.DERAbstractTime=function(c){KJUR.asn1.DERAbstractTime.superclass.constructor.call(this);var b=null;var a=null;this.localDateToUTC=function(f){utc=f.getTime()+(f.getTimezoneOffset()*60000);var e=new Date(utc);return e};this.formatDate=function(m,o,e){var g=this.zeroPadding;var n=this.localDateToUTC(m);var p=String(n.getFullYear());if(o=="utc"){p=p.substr(2,2)}var l=g(String(n.getMonth()+1),2);var q=g(String(n.getDate()),2);var h=g(String(n.getHours()),2);var i=g(String(n.getMinutes()),2);var j=g(String(n.getSeconds()),2);var r=p+l+q+h+i+j;if(e===true){var f=n.getMilliseconds();if(f!=0){var k=g(String(f),3);k=k.replace(/[0]+$/,"");r=r+"."+k}}return r+"Z"};this.zeroPadding=function(e,d){if(e.length>=d){return e}return new Array(d-e.length+1).join("0")+e};this.getString=function(){return this.s};this.setString=function(d){this.hTLV=null;this.isModified=true;this.s=d;this.hV=stohex(d)};this.setByDateValue=function(h,j,e,d,f,g){var i=new Date(Date.UTC(h,j-1,e,d,f,g,0));this.setByDate(i)};this.getFreshValueHex=function(){return this.hV}};YAHOO.lang.extend(KJUR.asn1.DERAbstractTime,KJUR.asn1.ASN1Object);KJUR.asn1.DERAbstractStructured=function(b){KJUR.asn1.DERAbstractString.superclass.constructor.call(this);var a=null;this.setByASN1ObjectArray=function(c){this.hTLV=null;this.isModified=true;this.asn1Array=c};this.appendASN1Object=function(c){this.hTLV=null;this.isModified=true;this.asn1Array.push(c)};this.asn1Array=new Array();if(typeof b!="undefined"){if(typeof b.array!="undefined"){this.asn1Array=b.array}}};YAHOO.lang.extend(KJUR.asn1.DERAbstractStructured,KJUR.asn1.ASN1Object);KJUR.asn1.DERBoolean=function(){KJUR.asn1.DERBoolean.superclass.constructor.call(this);this.hT="01";this.hTLV="0101ff"};YAHOO.lang.extend(KJUR.asn1.DERBoolean,KJUR.asn1.ASN1Object);KJUR.asn1.DERInteger=function(a){KJUR.asn1.DERInteger.superclass.constructor.call(this);this.hT="02";this.setByBigInteger=function(b){this.hTLV=null;this.isModified=true;this.hV=KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex(b)};this.setByInteger=function(c){var b=new BigInteger(String(c),10);this.setByBigInteger(b)};this.setValueHex=function(b){this.hV=b};this.getFreshValueHex=function(){return this.hV};if(typeof a!="undefined"){if(typeof a.bigint!="undefined"){this.setByBigInteger(a.bigint)}else{if(typeof a["int"]!="undefined"){this.setByInteger(a["int"])}else{if(typeof a=="number"){this.setByInteger(a)}else{if(typeof a.hex!="undefined"){this.setValueHex(a.hex)}}}}}};YAHOO.lang.extend(KJUR.asn1.DERInteger,KJUR.asn1.ASN1Object);KJUR.asn1.DERBitString=function(b){if(b!==undefined&&typeof b.obj!=="undefined"){var a=KJUR.asn1.ASN1Util.newObject(b.obj);b.hex="00"+a.getEncodedHex()}KJUR.asn1.DERBitString.superclass.constructor.call(this);this.hT="03";this.setHexValueIncludingUnusedBits=function(c){this.hTLV=null;this.isModified=true;this.hV=c};this.setUnusedBitsAndHexValue=function(c,e){if(c<0||7=(l*2))){break}if(d>=200){break}g.push(b);c=b;d++}return g};ASN1HEX.getNthChildIdx=function(d,b,e){var c=ASN1HEX.getChildIdx(d,b);return c[e]};ASN1HEX.getIdxbyList=function(e,d,c,i){var g=ASN1HEX;var f,b;if(c.length==0){if(i!==undefined){if(e.substr(d,2)!==i){throw"checking tag doesn't match: "+e.substr(d,2)+"!="+i}}return d}f=c.shift();b=g.getChildIdx(e,d);return g.getIdxbyList(e,b[f],c,i)};ASN1HEX.getTLVbyList=function(d,c,b,f){var e=ASN1HEX;var a=e.getIdxbyList(d,c,b);if(a===undefined){throw"can't find nthList object"}if(f!==undefined){if(d.substr(a,2)!=f){throw"checking tag doesn't match: "+d.substr(a,2)+"!="+f}}return e.getTLV(d,a)};ASN1HEX.getVbyList=function(e,c,b,g,i){var f=ASN1HEX;var a,d;a=f.getIdxbyList(e,c,b,g);if(a===undefined){throw"can't find nthList object"}d=f.getV(e,a);if(i===true){d=d.substr(2)}return d};ASN1HEX.hextooidstr=function(e){var h=function(b,a){if(b.length>=a){return b}return new Array(a-b.length+1).join("0")+b};var l=[];var o=e.substr(0,2);var f=parseInt(o,16);l[0]=new String(Math.floor(f/40));l[1]=new String(f%40);var m=e.substr(2);var k=[];for(var g=0;g0){n=n+"."+j.join(".")}return n};ASN1HEX.dump=function(t,c,l,g){var p=ASN1HEX;var j=p.getV;var y=p.dump;var w=p.getChildIdx;var e=t;if(t instanceof KJUR.asn1.ASN1Object){e=t.getEncodedHex()}var q=function(A,i){if(A.length<=i*2){return A}else{var v=A.substr(0,i)+"..(total "+A.length/2+"bytes).."+A.substr(A.length-i,i);return v}};if(c===undefined){c={ommit_long_octet:32}}if(l===undefined){l=0}if(g===undefined){g=""}var x=c.ommit_long_octet;if(e.substr(l,2)=="01"){var h=j(e,l);if(h=="00"){return g+"BOOLEAN FALSE\n"}else{return g+"BOOLEAN TRUE\n"}}if(e.substr(l,2)=="02"){var h=j(e,l);return g+"INTEGER "+q(h,x)+"\n"}if(e.substr(l,2)=="03"){var h=j(e,l);return g+"BITSTRING "+q(h,x)+"\n"}if(e.substr(l,2)=="04"){var h=j(e,l);if(p.isASN1HEX(h)){var k=g+"OCTETSTRING, encapsulates\n";k=k+y(h,c,0,g+" ");return k}else{return g+"OCTETSTRING "+q(h,x)+"\n"}}if(e.substr(l,2)=="05"){return g+"NULL\n"}if(e.substr(l,2)=="06"){var m=j(e,l);var a=KJUR.asn1.ASN1Util.oidHexToInt(m);var o=KJUR.asn1.x509.OID.oid2name(a);var b=a.replace(/\./g," ");if(o!=""){return g+"ObjectIdentifier "+o+" ("+b+")\n"}else{return g+"ObjectIdentifier ("+b+")\n"}}if(e.substr(l,2)=="0c"){return g+"UTF8String '"+hextoutf8(j(e,l))+"'\n"}if(e.substr(l,2)=="13"){return g+"PrintableString '"+hextoutf8(j(e,l))+"'\n"}if(e.substr(l,2)=="14"){return g+"TeletexString '"+hextoutf8(j(e,l))+"'\n"}if(e.substr(l,2)=="16"){return g+"IA5String '"+hextoutf8(j(e,l))+"'\n"}if(e.substr(l,2)=="17"){return g+"UTCTime "+hextoutf8(j(e,l))+"\n"}if(e.substr(l,2)=="18"){return g+"GeneralizedTime "+hextoutf8(j(e,l))+"\n"}if(e.substr(l,2)=="30"){if(e.substr(l,4)=="3000"){return g+"SEQUENCE {}\n"}var k=g+"SEQUENCE\n";var d=w(e,l);var f=c;if((d.length==2||d.length==3)&&e.substr(d[0],2)=="06"&&e.substr(d[d.length-1],2)=="04"){var o=p.oidname(j(e,d[0]));var r=JSON.parse(JSON.stringify(c));r.x509ExtName=o;f=r}for(var u=0;u0){var m=new f({array:this.extensionsArray});var k=new c({explicit:true,tag:"a3",obj:m});this.asn1Array.push(k)}var n=new f({array:this.asn1Array});this.hTLV=n.getEncodedHex();this.isModified=false;return this.hTLV};this._initialize()};YAHOO.lang.extend(KJUR.asn1.x509.TBSCertificate,KJUR.asn1.ASN1Object);KJUR.asn1.x509.Extension=function(d){KJUR.asn1.x509.Extension.superclass.constructor.call(this);var f=null,a=KJUR,e=a.asn1,h=e.DERObjectIdentifier,i=e.DEROctetString,b=e.DERBitString,g=e.DERBoolean,c=e.DERSequence;this.getEncodedHex=function(){var m=new h({oid:this.oid});var l=new i({hex:this.getExtnValueHex()});var k=new Array();k.push(m);if(this.critical){k.push(new g())}k.push(l);var j=new c({array:k});return j.getEncodedHex()};this.critical=false;if(d!==undefined){if(d.critical!==undefined){this.critical=d.critical}}};YAHOO.lang.extend(KJUR.asn1.x509.Extension,KJUR.asn1.ASN1Object);KJUR.asn1.x509.Extension.appendByNameToArray=function(e,c,b){var g=e.toLowerCase(),f=KJUR.asn1.x509;if(g=="basicconstraints"){var d=new f.BasicConstraints(c);b.push(d)}else{if(g=="keyusage"){var d=new f.KeyUsage(c);b.push(d)}else{if(g=="crldistributionpoints"){var d=new f.CRLDistributionPoints(c);b.push(d)}else{if(g=="extkeyusage"){var d=new f.ExtKeyUsage(c);b.push(d)}else{if(g=="authoritykeyidentifier"){var d=new f.AuthorityKeyIdentifier(c);b.push(d)}else{if(g=="authorityinfoaccess"){var d=new f.AuthorityInfoAccess(c);b.push(d)}else{if(g=="subjectaltname"){var d=new f.SubjectAltName(c);b.push(d)}else{if(g=="issueraltname"){var d=new f.IssuerAltName(c);b.push(d)}else{throw"unsupported extension name: "+e}}}}}}}}};KJUR.asn1.x509.KeyUsage=function(f){KJUR.asn1.x509.KeyUsage.superclass.constructor.call(this,f);var a=X509.KEYUSAGE_NAME;this.getExtnValueHex=function(){return this.asn1ExtnValue.getEncodedHex()};this.oid="2.5.29.15";if(f!==undefined){if(f.bin!==undefined){this.asn1ExtnValue=new KJUR.asn1.DERBitString(f)}if(f.names!==undefined&&f.names.length!==undefined){var e=f.names;var d="000000000";for(var c=0;c-1){e.push(new KJUR.asn1.DERInteger({"int":this.pathLen}))}var d=new KJUR.asn1.DERSequence({array:e});this.asn1ExtnValue=d;return this.asn1ExtnValue.getEncodedHex()};this.oid="2.5.29.19";this.cA=false;this.pathLen=-1;if(c!==undefined){if(c.cA!==undefined){this.cA=c.cA}if(c.pathLen!==undefined){this.pathLen=c.pathLen}}};YAHOO.lang.extend(KJUR.asn1.x509.BasicConstraints,KJUR.asn1.x509.Extension);KJUR.asn1.x509.CRLDistributionPoints=function(d){KJUR.asn1.x509.CRLDistributionPoints.superclass.constructor.call(this,d);var b=KJUR,a=b.asn1,c=a.x509;this.getExtnValueHex=function(){return this.asn1ExtnValue.getEncodedHex()};this.setByDPArray=function(e){this.asn1ExtnValue=new a.DERSequence({array:e})};this.setByOneURI=function(h){var e=new c.GeneralNames([{uri:h}]);var g=new c.DistributionPointName(e);var f=new c.DistributionPoint({dpobj:g});this.setByDPArray([f])};this.oid="2.5.29.31";if(d!==undefined){if(d.array!==undefined){this.setByDPArray(d.array)}else{if(d.uri!==undefined){this.setByOneURI(d.uri)}}}};YAHOO.lang.extend(KJUR.asn1.x509.CRLDistributionPoints,KJUR.asn1.x509.Extension);KJUR.asn1.x509.ExtKeyUsage=function(c){KJUR.asn1.x509.ExtKeyUsage.superclass.constructor.call(this,c);var b=KJUR,a=b.asn1;this.setPurposeArray=function(d){this.asn1ExtnValue=new a.DERSequence();for(var e=0;e0){var h=new b({array:this.aRevokedCert});this.asn1Array.push(h)}var i=new b({array:this.asn1Array});this.hTLV=i.getEncodedHex();this.isModified=false;return this.hTLV};this._initialize=function(){this.asn1Version=null;this.asn1SignatureAlg=null;this.asn1Issuer=null;this.asn1ThisUpdate=null;this.asn1NextUpdate=null;this.aRevokedCert=new Array()};this._initialize()};YAHOO.lang.extend(KJUR.asn1.x509.TBSCertList,KJUR.asn1.ASN1Object);KJUR.asn1.x509.CRLEntry=function(e){KJUR.asn1.x509.CRLEntry.superclass.constructor.call(this);var d=null,c=null,b=KJUR,a=b.asn1;this.setCertSerial=function(f){this.sn=new a.DERInteger(f)};this.setRevocationDate=function(f){this.time=new a.x509.Time(f)};this.getEncodedHex=function(){var f=new a.DERSequence({array:[this.sn,this.time]});this.TLV=f.getEncodedHex();return this.TLV};if(e!==undefined){if(e.time!==undefined){this.setRevocationDate(e.time)}if(e.sn!==undefined){this.setCertSerial(e.sn)}}};YAHOO.lang.extend(KJUR.asn1.x509.CRLEntry,KJUR.asn1.ASN1Object);KJUR.asn1.x509.X500Name=function(f){KJUR.asn1.x509.X500Name.superclass.constructor.call(this);this.asn1Array=new Array();var d=KJUR,c=d.asn1,e=c.x509,b=pemtohex;this.setByString=function(g){var k=g.split("/");k.shift();var j=[];for(var l=0;l0;f++){var h=c.shift();if(e===true){var d=b.pop();var j=(d+","+h).replace(/\\,/g,",");b.push(j);e=false}else{b.push(h)}if(h.substr(-1,1)==="\\"){e=true}}b=b.map(function(a){return a.replace("/","\\/")});b.reverse();return"/"+b.join("/")};KJUR.asn1.x509.RDN=function(a){KJUR.asn1.x509.RDN.superclass.constructor.call(this);this.asn1Array=new Array();this.addByString=function(b){this.asn1Array.push(new KJUR.asn1.x509.AttributeTypeAndValue({str:b}))};this.addByMultiValuedString=function(d){var b=KJUR.asn1.x509.RDN.parseString(d);for(var c=0;c0;g++){var k=j.shift();if(h===true){var f=c.pop();var d=(f+"+"+k).replace(/\\\+/g,"+");c.push(d);h=false}else{c.push(k)}if(k.substr(-1,1)==="\\"){h=true}}var l=false;var b=[];for(var g=0;c.length>0;g++){var k=c.shift();if(l===true){var e=b.pop();if(k.match(/"$/)){var d=(e+"+"+k).replace(/^([^=]+)="(.*)"$/,"$1=$2");b.push(d);l=false}else{b.push(e+"+"+k)}}else{b.push(k)}if(k.match(/^[^=]+="/)){l=true}}return b};KJUR.asn1.x509.AttributeTypeAndValue=function(d){KJUR.asn1.x509.AttributeTypeAndValue.superclass.constructor.call(this);var f=null,e=null,a="utf8",c=KJUR,b=c.asn1;this.setByString=function(h){var g=h.match(/^([^=]+)=(.+)$/);if(g){this.setByAttrTypeAndValueStr(g[1],g[2])}else{throw"malformed attrTypeAndValueStr: "+h}};this.setByAttrTypeAndValueStr=function(i,h){this.typeObj=KJUR.asn1.x509.OID.atype2obj(i);var g=a;if(i=="C"){g="prn"}this.valueObj=this.getValueObj(g,h)};this.getValueObj=function(h,g){if(h=="utf8"){return new b.DERUTF8String({str:g})}if(h=="prn"){return new b.DERPrintableString({str:g})}if(h=="tel"){return new b.DERTeletexString({str:g})}if(h=="ia5"){return new b.DERIA5String({str:g})}throw"unsupported directory string type: type="+h+" value="+g};this.getEncodedHex=function(){var g=new b.DERSequence({array:[this.typeObj,this.valueObj]});this.TLV=g.getEncodedHex();return this.TLV};if(d!==undefined){if(d.str!==undefined){this.setByString(d.str)}}};YAHOO.lang.extend(KJUR.asn1.x509.AttributeTypeAndValue,KJUR.asn1.ASN1Object);KJUR.asn1.x509.SubjectPublicKeyInfo=function(f){KJUR.asn1.x509.SubjectPublicKeyInfo.superclass.constructor.call(this);var l=null,k=null,a=KJUR,j=a.asn1,i=j.DERInteger,b=j.DERBitString,m=j.DERObjectIdentifier,e=j.DERSequence,h=j.ASN1Util.newObject,d=j.x509,o=d.AlgorithmIdentifier,g=a.crypto,n=g.ECDSA,c=g.DSA;this.getASN1Object=function(){if(this.asn1AlgId==null||this.asn1SubjPKey==null){throw"algId and/or subjPubKey not set"}var p=new e({array:[this.asn1AlgId,this.asn1SubjPKey]});return p};this.getEncodedHex=function(){var p=this.getASN1Object();this.hTLV=p.getEncodedHex();return this.hTLV};this.setPubKey=function(q){try{if(q instanceof RSAKey){var u=h({seq:[{"int":{bigint:q.n}},{"int":{"int":q.e}}]});var s=u.getEncodedHex();this.asn1AlgId=new o({name:"rsaEncryption"});this.asn1SubjPKey=new b({hex:"00"+s})}}catch(p){}try{if(q instanceof KJUR.crypto.ECDSA){var r=new m({name:q.curveName});this.asn1AlgId=new o({name:"ecPublicKey",asn1params:r});this.asn1SubjPKey=new b({hex:"00"+q.pubKeyHex})}}catch(p){}try{if(q instanceof KJUR.crypto.DSA){var r=new h({seq:[{"int":{bigint:q.p}},{"int":{bigint:q.q}},{"int":{bigint:q.g}}]});this.asn1AlgId=new o({name:"dsa",asn1params:r});var t=new i({bigint:q.y});this.asn1SubjPKey=new b({hex:"00"+t.getEncodedHex()})}}catch(p){}};if(f!==undefined){this.setPubKey(f)}};YAHOO.lang.extend(KJUR.asn1.x509.SubjectPublicKeyInfo,KJUR.asn1.ASN1Object);KJUR.asn1.x509.Time=function(f){KJUR.asn1.x509.Time.superclass.constructor.call(this);var e=null,a=null,d=KJUR,c=d.asn1,b=c.DERUTCTime,g=c.DERGeneralizedTime;this.setTimeParams=function(h){this.timeParams=h};this.getEncodedHex=function(){var h=null;if(this.timeParams!=null){if(this.type=="utc"){h=new b(this.timeParams)}else{h=new g(this.timeParams)}}else{if(this.type=="utc"){h=new b()}else{h=new g()}}this.TLV=h.getEncodedHex();return this.TLV};this.type="utc";if(f!==undefined){if(f.type!==undefined){this.type=f.type}else{if(f.str!==undefined){if(f.str.match(/^[0-9]{12}Z$/)){this.type="utc"}if(f.str.match(/^[0-9]{14}Z$/)){this.type="gen"}}}this.timeParams=f}};YAHOO.lang.extend(KJUR.asn1.x509.Time,KJUR.asn1.ASN1Object);KJUR.asn1.x509.AlgorithmIdentifier=function(d){KJUR.asn1.x509.AlgorithmIdentifier.superclass.constructor.call(this);this.nameAlg=null;this.asn1Alg=null;this.asn1Params=null;this.paramEmpty=false;var b=KJUR,a=b.asn1;this.getEncodedHex=function(){if(this.nameAlg===null&&this.asn1Alg===null){throw"algorithm not specified"}if(this.nameAlg!==null&&this.asn1Alg===null){this.asn1Alg=a.x509.OID.name2obj(this.nameAlg)}var e=[this.asn1Alg];if(this.asn1Params!==null){e.push(this.asn1Params)}var f=new a.DERSequence({array:e});this.hTLV=f.getEncodedHex();return this.hTLV};if(d!==undefined){if(d.name!==undefined){this.nameAlg=d.name}if(d.asn1params!==undefined){this.asn1Params=d.asn1params}if(d.paramempty!==undefined){this.paramEmpty=d.paramempty}}if(this.asn1Params===null&&this.paramEmpty===false&&this.nameAlg!==null){var c=this.nameAlg.toLowerCase();if(c.substr(-7,7)!=="withdsa"&&c.substr(-9,9)!=="withecdsa"){this.asn1Params=new a.DERNull()}}};YAHOO.lang.extend(KJUR.asn1.x509.AlgorithmIdentifier,KJUR.asn1.ASN1Object);KJUR.asn1.x509.GeneralName=function(e){KJUR.asn1.x509.GeneralName.superclass.constructor.call(this);var m=null,i=null,k={rfc822:"81",dns:"82",dn:"a4",uri:"86",ip:"87"},b=KJUR,g=b.asn1,f=g.DERSequence,j=g.DEROctetString,d=g.DERIA5String,c=g.DERTaggedObject,l=g.ASN1Object,a=g.x509.X500Name,h=pemtohex;this.explicit=false;this.setByParam=function(p){var r=null;var u=null;if(p===undefined){return}if(p.rfc822!==undefined){this.type="rfc822";u=new d({str:p[this.type]})}if(p.dns!==undefined){this.type="dns";u=new d({str:p[this.type]})}if(p.uri!==undefined){this.type="uri";u=new d({str:p[this.type]})}if(p.dn!==undefined){this.type="dn";this.explicit=true;u=new a({str:p.dn})}if(p.ldapdn!==undefined){this.type="dn";this.explicit=true;u=new a({ldapstr:p.ldapdn})}if(p.certissuer!==undefined){this.type="dn";this.explicit=true;var o=p.certissuer;var w=null;if(o.match(/^[0-9A-Fa-f]+$/)){w==o}if(o.indexOf("-----BEGIN ")!=-1){w=h(o)}if(w==null){throw"certissuer param not cert"}var t=new X509();t.hex=w;var y=t.getIssuerHex();u=new l();u.hTLV=y}if(p.certsubj!==undefined){this.type="dn";this.explicit=true;var o=p.certsubj;var w=null;if(o.match(/^[0-9A-Fa-f]+$/)){w==o}if(o.indexOf("-----BEGIN ")!=-1){w=h(o)}if(w==null){throw"certsubj param not cert"}var t=new X509();t.hex=w;var y=t.getSubjectHex();u=new l();u.hTLV=y}if(p.ip!==undefined){this.type="ip";this.explicit=false;var q=p.ip;var s;var n="malformed IP address";if(q.match(/^[0-9.]+[.][0-9.]+$/)){s=intarystrtohex("["+q.split(".").join(",")+"]");if(s.length!==8){throw n}}else{if(q.match(/^[0-9A-Fa-f:]+:[0-9A-Fa-f:]+$/)){s=ipv6tohex(q)}else{if(q.match(/^([0-9A-Fa-f][0-9A-Fa-f]){1,}$/)){s=q}else{throw n}}}u=new j({hex:s})}if(this.type==null){throw"unsupported type in params="+p}this.asn1Obj=new c({explicit:this.explicit,tag:k[this.type],obj:u})};this.getEncodedHex=function(){return this.asn1Obj.getEncodedHex()};if(e!==undefined){this.setByParam(e)}};YAHOO.lang.extend(KJUR.asn1.x509.GeneralName,KJUR.asn1.ASN1Object);KJUR.asn1.x509.GeneralNames=function(d){KJUR.asn1.x509.GeneralNames.superclass.constructor.call(this);var a=null,c=KJUR,b=c.asn1;this.setByParamArray=function(g){for(var e=0;e0){r=new b({obj:this.dUnsignedAttrs,tag:"a1",explicit:false})}var q=[this.dCMSVersion,this.dSignerIdentifier,this.dDigestAlgorithm,o,this.dSigAlg,this.dSig,];if(r!=null){q.push(r)}var p=new h.DERSequence({array:q});this.hTLV=p.getEncodedHex();return this.hTLV}};YAHOO.lang.extend(KJUR.asn1.cms.SignerInfo,KJUR.asn1.ASN1Object);KJUR.asn1.cms.EncapsulatedContentInfo=function(g){var c=KJUR,b=c.asn1,e=b.DERTaggedObject,a=b.DERSequence,h=b.DERObjectIdentifier,d=b.DEROctetString,f=b.cms;f.EncapsulatedContentInfo.superclass.constructor.call(this);this.dEContentType=new h({name:"data"});this.dEContent=null;this.isDetached=false;this.eContentValueHex=null;this.setContentType=function(i){if(i.match(/^[0-2][.][0-9.]+$/)){this.dEContentType=new h({oid:i})}else{this.dEContentType=new h({name:i})}};this.setContentValue=function(i){if(i!==undefined){if(typeof i.hex=="string"){this.eContentValueHex=i.hex}else{if(typeof i.str=="string"){this.eContentValueHex=utf8tohex(i.str)}}}};this.setContentValueHex=function(i){this.eContentValueHex=i};this.setContentValueStr=function(i){this.eContentValueHex=utf8tohex(i)};this.getEncodedHex=function(){if(typeof this.eContentValueHex!="string"){throw"eContentValue not yet set"}var k=new d({hex:this.eContentValueHex});this.dEContent=new e({obj:k,tag:"a0",explicit:true});var i=[this.dEContentType];if(!this.isDetached){i.push(this.dEContent)}var j=new a({array:i});this.hTLV=j.getEncodedHex();return this.hTLV}};YAHOO.lang.extend(KJUR.asn1.cms.EncapsulatedContentInfo,KJUR.asn1.ASN1Object);KJUR.asn1.cms.ContentInfo=function(f){var c=KJUR,b=c.asn1,d=b.DERTaggedObject,a=b.DERSequence,e=b.x509;KJUR.asn1.cms.ContentInfo.superclass.constructor.call(this);this.dContentType=null;this.dContent=null;this.setContentType=function(g){if(typeof g=="string"){this.dContentType=e.OID.name2obj(g)}};this.getEncodedHex=function(){var h=new d({obj:this.dContent,tag:"a0",explicit:true});var g=new a({array:[this.dContentType,h]});this.hTLV=g.getEncodedHex();return this.hTLV};if(f!==undefined){if(f.type){this.setContentType(f.type)}if(f.obj&&f.obj instanceof b.ASN1Object){this.dContent=f.obj}}};YAHOO.lang.extend(KJUR.asn1.cms.ContentInfo,KJUR.asn1.ASN1Object);KJUR.asn1.cms.SignedData=function(e){var a=KJUR,h=a.asn1,j=h.ASN1Object,g=h.DERInteger,m=h.DERSet,f=h.DERSequence,b=h.DERTaggedObject,l=h.cms,i=l.EncapsulatedContentInfo,d=l.SignerInfo,n=l.ContentInfo,c=h.x509,k=c.AlgorithmIdentifier;KJUR.asn1.cms.SignedData.superclass.constructor.call(this);this.dCMSVersion=new g({"int":1});this.dDigestAlgs=null;this.digestAlgNameList=[];this.dEncapContentInfo=new i();this.dCerts=null;this.certificateList=[];this.crlList=[];this.signerInfoList=[new d()];this.addCertificatesByPEM=function(p){var q=pemtohex(p);var r=new j();r.hTLV=q;this.certificateList.push(r)};this.getEncodedHex=function(){if(typeof this.hTLV=="string"){return this.hTLV}if(this.dDigestAlgs==null){var u=[];for(var t=0;t0){var v=new m({array:this.certificateList});this.dCerts=new b({obj:v,tag:"a0",explicit:false})}}if(this.dCerts!=null){p.push(this.dCerts)}var r=new m({array:this.signerInfoList});p.push(r);var q=new f({array:p});this.hTLV=q.getEncodedHex();return this.hTLV};this.getContentInfo=function(){this.getEncodedHex();var o=new n({type:"signed-data",obj:this});return o};this.getContentInfoEncodedHex=function(){var o=this.getContentInfo();var p=o.getEncodedHex();return p};this.getPEM=function(){return hextopem(this.getContentInfoEncodedHex(),"CMS")}};YAHOO.lang.extend(KJUR.asn1.cms.SignedData,KJUR.asn1.ASN1Object);KJUR.asn1.cms.CMSUtil=new function(){};KJUR.asn1.cms.CMSUtil.newSignedData=function(d){var b=KJUR,j=b.asn1,q=j.cms,f=q.SignerInfo,n=q.SignedData,o=q.SigningTime,a=q.SigningCertificate,p=q.SigningCertificateV2,c=j.cades,e=c.SignaturePolicyIdentifier;var m=new n();m.dEncapContentInfo.setContentValue(d.content);if(typeof d.certs=="object"){for(var h=0;h0){var s=new f({array:this.extensionsArray});var r=new m({array:[s]});var q=new f({array:[new k({oid:"1.2.840.113549.1.9.14"}),r]});var p=new c({explicit:true,tag:"a0",obj:q});this.asn1Array.push(p)}else{var p=new c({explicit:false,tag:"a0",obj:new j()});this.asn1Array.push(p)}var t=new f({array:this.asn1Array});this.hTLV=t.getEncodedHex();this.isModified=false;return this.hTLV};this._initialize()};YAHOO.lang.extend(KJUR.asn1.csr.CertificationRequestInfo,KJUR.asn1.ASN1Object);KJUR.asn1.csr.CSRUtil=new function(){};KJUR.asn1.csr.CSRUtil.newCSRPEM=function(h){var c=KEYUTIL,b=KJUR.asn1.csr;if(h.subject===undefined){throw"parameter subject undefined"}if(h.sbjpubkey===undefined){throw"parameter sbjpubkey undefined"}if(h.sigalg===undefined){throw"parameter sigalg undefined"}if(h.sbjprvkey===undefined){throw"parameter sbjpubkey undefined"}var d=new b.CertificationRequestInfo();d.setSubjectByParam(h.subject);d.setSubjectPublicKeyByGetKey(h.sbjpubkey);if(h.ext!==undefined&&h.ext.length!==undefined){for(var e=0;ef.length){f=c[d]}}e=e.replace(f,"::");return e.slice(1,-1)}function hextoip(b){var d="malformed hex value";if(!b.match(/^([0-9A-Fa-f][0-9A-Fa-f]){1,}$/)){throw d}if(b.length==8){var c;try{c=parseInt(b.substr(0,2),16)+"."+parseInt(b.substr(2,2),16)+"."+parseInt(b.substr(4,2),16)+"."+parseInt(b.substr(6,2),16);return c}catch(a){throw d}}else{if(b.length==32){return hextoipv6(b)}else{return b}}}function iptohex(f){var j="malformed IP address";f=f.toLowerCase(f);if(f.match(/^[0-9.]+$/)){var b=f.split(".");if(b.length!==4){throw j}var g="";try{for(var e=0;e<4;e++){var h=parseInt(b[e]);g+=("0"+h.toString(16)).slice(-2)}return g}catch(c){throw j}}else{if(f.match(/^[0-9a-f:]+$/)&&f.indexOf(":")!==-1){return ipv6tohex(f)}else{throw j}}}function encodeURIComponentAll(a){var d=encodeURIComponent(a);var b="";for(var c=0;c"7"){return"00"+a}return a}function intarystrtohex(b){b=b.replace(/^\s*\[\s*/,"");b=b.replace(/\s*\]\s*$/,"");b=b.replace(/\s*/g,"");try{var c=b.split(/,/).map(function(g,e,h){var f=parseInt(g);if(f<0||255a.length){d=a.length}for(var b=0;bd){throw"key is too short for SigAlg: keylen="+j+","+a}var b="0001";var k="00"+c;var g="";var l=d-b.length-k.length;for(var f=0;f=0;--p){q=q.twice2D();q.z=BigInteger.ONE;if(o.testBit(p)){if(n.testBit(p)){q=q.add2D(t)}else{q=q.add2D(s)}}else{if(n.testBit(p)){q=q.add2D(r)}}}return q}this.getBigRandom=function(i){return new BigInteger(i.bitLength(),a).mod(i.subtract(BigInteger.ONE)).add(BigInteger.ONE)};this.setNamedCurve=function(i){this.ecparams=KJUR.crypto.ECParameterDB.getByName(i);this.prvKeyHex=null;this.pubKeyHex=null;this.curveName=i};this.setPrivateKeyHex=function(i){this.isPrivate=true;this.prvKeyHex=i};this.setPublicKeyHex=function(i){this.isPublic=true;this.pubKeyHex=i};this.getPublicKeyXYHex=function(){var k=this.pubKeyHex;if(k.substr(0,2)!=="04"){throw"this method supports uncompressed format(04) only"}var j=this.ecparams.keylen/4;if(k.length!==2+j*2){throw"malformed public key hex length"}var i={};i.x=k.substr(2,j);i.y=k.substr(2+j);return i};this.getShortNISTPCurveName=function(){var i=this.curveName;if(i==="secp256r1"||i==="NIST P-256"||i==="P-256"||i==="prime256v1"){return"P-256"}if(i==="secp384r1"||i==="NIST P-384"||i==="P-384"){return"P-384"}return null};this.generateKeyPairHex=function(){var k=this.ecparams.n;var n=this.getBigRandom(k);var l=this.ecparams.G.multiply(n);var q=l.getX().toBigInteger();var o=l.getY().toBigInteger();var i=this.ecparams.keylen/4;var m=("0000000000"+n.toString(16)).slice(-i);var r=("0000000000"+q.toString(16)).slice(-i);var p=("0000000000"+o.toString(16)).slice(-i);var j="04"+r+p;this.setPrivateKeyHex(m);this.setPublicKeyHex(j);return{ecprvhex:m,ecpubhex:j}};this.signWithMessageHash=function(i){return this.signHex(i,this.prvKeyHex)};this.signHex=function(o,j){var t=new BigInteger(j,16);var l=this.ecparams.n;var q=new BigInteger(o,16);do{var m=this.getBigRandom(l);var u=this.ecparams.G;var p=u.multiply(m);var i=p.getX().toBigInteger().mod(l)}while(i.compareTo(BigInteger.ZERO)<=0);var v=m.modInverse(l).multiply(q.add(t.multiply(i))).mod(l);return KJUR.crypto.ECDSA.biRSSigToASN1Sig(i,v)};this.sign=function(m,u){var q=u;var j=this.ecparams.n;var p=BigInteger.fromByteArrayUnsigned(m);do{var l=this.getBigRandom(j);var t=this.ecparams.G;var o=t.multiply(l);var i=o.getX().toBigInteger().mod(j)}while(i.compareTo(BigInteger.ZERO)<=0);var v=l.modInverse(j).multiply(p.add(q.multiply(i))).mod(j);return this.serializeSig(i,v)};this.verifyWithMessageHash=function(j,i){return this.verifyHex(j,i,this.pubKeyHex)};this.verifyHex=function(m,i,p){var l,j;var o=KJUR.crypto.ECDSA.parseSigHex(i);l=o.r;j=o.s;var k;k=ECPointFp.decodeFromHex(this.ecparams.curve,p);var n=new BigInteger(m,16);return this.verifyRaw(n,l,j,k)};this.verify=function(o,p,j){var l,i;if(Bitcoin.Util.isArray(p)){var n=this.parseSig(p);l=n.r;i=n.s}else{if("object"===typeof p&&p.r&&p.s){l=p.r;i=p.s}else{throw"Invalid value for signature"}}var k;if(j instanceof ECPointFp){k=j}else{if(Bitcoin.Util.isArray(j)){k=ECPointFp.decodeFrom(this.ecparams.curve,j)}else{throw"Invalid format for pubkey value, must be byte array or ECPointFp"}}var m=BigInteger.fromByteArrayUnsigned(o);return this.verifyRaw(m,l,i,k)};this.verifyRaw=function(o,i,w,m){var l=this.ecparams.n;var u=this.ecparams.G;if(i.compareTo(BigInteger.ONE)<0||i.compareTo(l)>=0){return false}if(w.compareTo(BigInteger.ONE)<0||w.compareTo(l)>=0){return false}var p=w.modInverse(l);var k=o.multiply(p).mod(l);var j=i.multiply(p).mod(l);var q=u.multiply(k).add(m.multiply(j));var t=q.getX().toBigInteger().mod(l);return t.equals(i)};this.serializeSig=function(k,j){var l=k.toByteArraySigned();var i=j.toByteArraySigned();var m=[];m.push(2);m.push(l.length);m=m.concat(l);m.push(2);m.push(i.length);m=m.concat(i);m.unshift(m.length);m.unshift(48);return m};this.parseSig=function(n){var m;if(n[0]!=48){throw new Error("Signature not a valid DERSequence")}m=2;if(n[m]!=2){throw new Error("First element in signature must be a DERInteger")}var l=n.slice(m+2,m+2+n[m+1]);m+=2+n[m+1];if(n[m]!=2){throw new Error("Second element in signature must be a DERInteger")}var i=n.slice(m+2,m+2+n[m+1]);m+=2+n[m+1];var k=BigInteger.fromByteArrayUnsigned(l);var j=BigInteger.fromByteArrayUnsigned(i);return{r:k,s:j}};this.parseSigCompact=function(m){if(m.length!==65){throw"Signature has the wrong length"}var j=m[0]-27;if(j<0||j>7){throw"Invalid signature type"}var o=this.ecparams.n;var l=BigInteger.fromByteArrayUnsigned(m.slice(1,33)).mod(o);var k=BigInteger.fromByteArrayUnsigned(m.slice(33,65)).mod(o);return{r:l,s:k,i:j}};this.readPKCS5PrvKeyHex=function(l){var n=ASN1HEX;var m=KJUR.crypto.ECDSA.getName;var p=n.getVbyList;if(n.isASN1HEX(l)===false){throw"not ASN.1 hex string"}var i,k,o;try{i=p(l,0,[2,0],"06");k=p(l,0,[1],"04");try{o=p(l,0,[3,0],"03").substr(2)}catch(j){}}catch(j){throw"malformed PKCS#1/5 plain ECC private key"}this.curveName=m(i);if(this.curveName===undefined){throw"unsupported curve name"}this.setNamedCurve(this.curveName);this.setPublicKeyHex(o);this.setPrivateKeyHex(k);this.isPublic=false};this.readPKCS8PrvKeyHex=function(l){var q=ASN1HEX;var i=KJUR.crypto.ECDSA.getName;var n=q.getVbyList;if(q.isASN1HEX(l)===false){throw"not ASN.1 hex string"}var j,p,m,k;try{j=n(l,0,[1,0],"06");p=n(l,0,[1,1],"06");m=n(l,0,[2,0,1],"04");try{k=n(l,0,[2,0,2,0],"03").substr(2)}catch(o){}}catch(o){throw"malformed PKCS#8 plain ECC private key"}this.curveName=i(p);if(this.curveName===undefined){throw"unsupported curve name"}this.setNamedCurve(this.curveName);this.setPublicKeyHex(k);this.setPrivateKeyHex(m);this.isPublic=false};this.readPKCS8PubKeyHex=function(l){var n=ASN1HEX;var m=KJUR.crypto.ECDSA.getName;var p=n.getVbyList;if(n.isASN1HEX(l)===false){throw"not ASN.1 hex string"}var k,i,o;try{k=p(l,0,[0,0],"06");i=p(l,0,[0,1],"06");o=p(l,0,[1],"03").substr(2)}catch(j){throw"malformed PKCS#8 ECC public key"}this.curveName=m(i);if(this.curveName===null){throw"unsupported curve name"}this.setNamedCurve(this.curveName);this.setPublicKeyHex(o)};this.readCertPubKeyHex=function(k,p){if(p!==5){p=6}var m=ASN1HEX;var l=KJUR.crypto.ECDSA.getName;var o=m.getVbyList;if(m.isASN1HEX(k)===false){throw"not ASN.1 hex string"}var i,n;try{i=o(k,0,[0,p,0,1],"06");n=o(k,0,[0,p,1],"03").substr(2)}catch(j){throw"malformed X.509 certificate ECC public key"}this.curveName=l(i);if(this.curveName===null){throw"unsupported curve name"}this.setNamedCurve(this.curveName);this.setPublicKeyHex(n)};if(h!==undefined){if(h.curve!==undefined){this.curveName=h.curve}}if(this.curveName===undefined){this.curveName=e}this.setNamedCurve(this.curveName);if(h!==undefined){if(h.prv!==undefined){this.setPrivateKeyHex(h.prv)}if(h.pub!==undefined){this.setPublicKeyHex(h.pub)}}};KJUR.crypto.ECDSA.parseSigHex=function(a){var b=KJUR.crypto.ECDSA.parseSigHexInHexRS(a);var d=new BigInteger(b.r,16);var c=new BigInteger(b.s,16);return{r:d,s:c}};KJUR.crypto.ECDSA.parseSigHexInHexRS=function(f){var j=ASN1HEX;var i=j.getChildIdx;var g=j.getV;if(f.substr(0,2)!="30"){throw"signature is not a ASN.1 sequence"}var h=i(f,0);if(h.length!=2){throw"number of signature ASN.1 sequence elements seem wrong"}var e=h[0];var d=h[1];if(f.substr(e,2)!="02"){throw"1st item of sequene of signature is not ASN.1 integer"}if(f.substr(d,2)!="02"){throw"2nd item of sequene of signature is not ASN.1 integer"}var c=g(f,e);var b=g(f,d);return{r:c,s:b}};KJUR.crypto.ECDSA.asn1SigToConcatSig=function(c){var d=KJUR.crypto.ECDSA.parseSigHexInHexRS(c);var b=d.r;var a=d.s;if(b.substr(0,2)=="00"&&(b.length%32)==2){b=b.substr(2)}if(a.substr(0,2)=="00"&&(a.length%32)==2){a=a.substr(2)}if((b.length%32)==30){b="00"+b}if((a.length%32)==30){a="00"+a}if(b.length%32!=0){throw"unknown ECDSA sig r length error"}if(a.length%32!=0){throw"unknown ECDSA sig s length error"}return b+a};KJUR.crypto.ECDSA.concatSigToASN1Sig=function(a){if((((a.length/2)*8)%(16*8))!=0){throw"unknown ECDSA concatinated r-s sig length error"}var c=a.substr(0,a.length/2);var b=a.substr(a.length/2);return KJUR.crypto.ECDSA.hexRSSigToASN1Sig(c,b)};KJUR.crypto.ECDSA.hexRSSigToASN1Sig=function(b,a){var d=new BigInteger(b,16);var c=new BigInteger(a,16);return KJUR.crypto.ECDSA.biRSSigToASN1Sig(d,c)};KJUR.crypto.ECDSA.biRSSigToASN1Sig=function(f,d){var c=KJUR.asn1;var b=new c.DERInteger({bigint:f});var a=new c.DERInteger({bigint:d});var e=new c.DERSequence({array:[b,a]});return e.getEncodedHex()};KJUR.crypto.ECDSA.getName=function(a){if(a==="2a8648ce3d030107"){return"secp256r1"}if(a==="2b8104000a"){return"secp256k1"}if(a==="2b81040022"){return"secp384r1"}if("|secp256r1|NIST P-256|P-256|prime256v1|".indexOf(a)!==-1){return"secp256r1"}if("|secp256k1|".indexOf(a)!==-1){return"secp256k1"}if("|secp384r1|NIST P-384|P-384|".indexOf(a)!==-1){return"secp384r1"}return null}; +if(typeof KJUR=="undefined"||!KJUR){KJUR={}}if(typeof KJUR.crypto=="undefined"||!KJUR.crypto){KJUR.crypto={}}KJUR.crypto.ECParameterDB=new function(){var b={};var c={};function a(d){return new BigInteger(d,16)}this.getByName=function(e){var d=e;if(typeof c[d]!="undefined"){d=c[e]}if(typeof b[d]!="undefined"){return b[d]}throw"unregistered EC curve name: "+d};this.regist=function(A,l,o,g,m,e,j,f,k,u,d,x){b[A]={};var s=a(o);var z=a(g);var y=a(m);var t=a(e);var w=a(j);var r=new ECCurveFp(s,z,y);var q=r.decodePointHex("04"+f+k);b[A]["name"]=A;b[A]["keylen"]=l;b[A]["curve"]=r;b[A]["G"]=q;b[A]["n"]=t;b[A]["h"]=w;b[A]["oid"]=d;b[A]["info"]=x;for(var v=0;v1){g=new BigInteger(i,16)}else{g=null}h=new BigInteger(j,16);this.setPrivate(c,a,e,g,h)};this.setPublic=function(c,b,a,d){this.isPublic=true;this.p=c;this.q=b;this.g=a;this.y=d;this.x=null};this.setPublicHex=function(f,e,d,g){var b,a,h,c;b=new BigInteger(f,16);a=new BigInteger(e,16);h=new BigInteger(d,16);c=new BigInteger(g,16);this.setPublic(b,a,h,c)};this.signWithMessageHash=function(d){var c=this.p;var b=this.q;var f=this.g;var i=this.y;var j=this.x;var e=KJUR.crypto.Util.getRandomBigIntegerMinToMax(BigInteger.ONE.add(BigInteger.ONE),b.subtract(BigInteger.ONE));var l=d.substr(0,b.bitLength()/4);var h=new BigInteger(l,16);var a=(f.modPow(e,c)).mod(b);var n=(e.modInverse(b).multiply(h.add(j.multiply(a)))).mod(b);var m=KJUR.asn1.ASN1Util.jsonToASN1HEX({seq:[{"int":{bigint:a}},{"int":{bigint:n}}]});return m};this.verifyWithMessageHash=function(h,f){var d=this.p;var b=this.q;var j=this.g;var l=this.y;var i=this.parseASN1Signature(f);var a=i[0];var t=i[1];var o=h.substr(0,b.bitLength()/4);var k=new BigInteger(o,16);if(BigInteger.ZERO.compareTo(a)>0||a.compareTo(b)>0){throw"invalid DSA signature"}if(BigInteger.ZERO.compareTo(t)>=0||t.compareTo(b)>0){throw"invalid DSA signature"}var m=t.modInverse(b);var e=k.multiply(m).mod(b);var c=a.multiply(m).mod(b);var n=j.modPow(e,d).multiply(l.modPow(c,d)).mod(d).mod(b);return n.compareTo(a)==0};this.parseASN1Signature=function(a){try{var d=new BigInteger(ASN1HEX.getVbyList(a,0,[0],"02"),16);var c=new BigInteger(ASN1HEX.getVbyList(a,0,[1],"02"),16);return[d,c]}catch(b){throw"malformed ASN.1 DSA signature"}};this.readPKCS5PrvKeyHex=function(c){var b,a,f,g,i;var j=ASN1HEX;var d=j.getVbyList;if(j.isASN1HEX(c)===false){throw"not ASN.1 hex string"}try{b=d(c,0,[1],"02");a=d(c,0,[2],"02");f=d(c,0,[3],"02");g=d(c,0,[4],"02");i=d(c,0,[5],"02")}catch(e){console.log("EXCEPTION:"+e);throw"malformed PKCS#1/5 plain DSA private key"}this.setPrivateHex(b,a,f,g,i)};this.readPKCS8PrvKeyHex=function(d){var f,c,b,g;var e=ASN1HEX;var i=e.getVbyList;if(e.isASN1HEX(d)===false){throw"not ASN.1 hex string"}try{f=i(d,0,[1,1,0],"02");c=i(d,0,[1,1,1],"02");b=i(d,0,[1,1,2],"02");g=i(d,0,[2,0],"02")}catch(a){console.log("EXCEPTION:"+a);throw"malformed PKCS#8 plain DSA private key"}this.setPrivateHex(f,c,b,null,g)};this.readPKCS8PubKeyHex=function(d){var f,c,b,g;var e=ASN1HEX;var i=e.getVbyList;if(e.isASN1HEX(d)===false){throw"not ASN.1 hex string"}try{f=i(d,0,[0,1,0],"02");c=i(d,0,[0,1,1],"02");b=i(d,0,[0,1,2],"02");g=i(d,0,[1,0],"02")}catch(a){console.log("EXCEPTION:"+a);throw"malformed PKCS#8 DSA public key"}this.setPublicHex(f,c,b,g)};this.readCertPubKeyHex=function(c,f){if(f!==5){f=6}var b,a,g,i;var j=ASN1HEX;var d=j.getVbyList;if(j.isASN1HEX(c)===false){throw"not ASN.1 hex string"}try{b=d(c,0,[0,f,0,1,0],"02");a=d(c,0,[0,f,0,1,1],"02");g=d(c,0,[0,f,0,1,2],"02");i=d(c,0,[0,f,1,0],"02")}catch(e){console.log("EXCEPTION:"+e);throw"malformed X.509 certificate DSA public key"}this.setPublicHex(b,a,g,i)}}; +var KEYUTIL=function(){var d=function(p,r,q){return k(CryptoJS.AES,p,r,q)};var e=function(p,r,q){return k(CryptoJS.TripleDES,p,r,q)};var a=function(p,r,q){return k(CryptoJS.DES,p,r,q)};var k=function(s,x,u,q){var r=CryptoJS.enc.Hex.parse(x);var w=CryptoJS.enc.Hex.parse(u);var p=CryptoJS.enc.Hex.parse(q);var t={};t.key=w;t.iv=p;t.ciphertext=r;var v=s.decrypt(t,w,{iv:p});return CryptoJS.enc.Hex.stringify(v)};var l=function(p,r,q){return g(CryptoJS.AES,p,r,q)};var o=function(p,r,q){return g(CryptoJS.TripleDES,p,r,q)};var f=function(p,r,q){return g(CryptoJS.DES,p,r,q)};var g=function(t,y,v,q){var s=CryptoJS.enc.Hex.parse(y);var x=CryptoJS.enc.Hex.parse(v);var p=CryptoJS.enc.Hex.parse(q);var w=t.encrypt(s,x,{iv:p});var r=CryptoJS.enc.Hex.parse(w.toString());var u=CryptoJS.enc.Base64.stringify(r);return u};var i={"AES-256-CBC":{proc:d,eproc:l,keylen:32,ivlen:16},"AES-192-CBC":{proc:d,eproc:l,keylen:24,ivlen:16},"AES-128-CBC":{proc:d,eproc:l,keylen:16,ivlen:16},"DES-EDE3-CBC":{proc:e,eproc:o,keylen:24,ivlen:8},"DES-CBC":{proc:a,eproc:f,keylen:8,ivlen:8}};var c=function(p){return i[p]["proc"]};var m=function(p){var r=CryptoJS.lib.WordArray.random(p);var q=CryptoJS.enc.Hex.stringify(r);return q};var n=function(v){var w={};var q=v.match(new RegExp("DEK-Info: ([^,]+),([0-9A-Fa-f]+)","m"));if(q){w.cipher=q[1];w.ivsalt=q[2]}var p=v.match(new RegExp("-----BEGIN ([A-Z]+) PRIVATE KEY-----"));if(p){w.type=p[1]}var u=-1;var x=0;if(v.indexOf("\r\n\r\n")!=-1){u=v.indexOf("\r\n\r\n");x=2}if(v.indexOf("\n\n")!=-1){u=v.indexOf("\n\n");x=1}var t=v.indexOf("-----END");if(u!=-1&&t!=-1){var r=v.substring(u+x*2,t-x);r=r.replace(/\s+/g,"");w.data=r}return w};var j=function(q,y,p){var v=p.substring(0,16);var t=CryptoJS.enc.Hex.parse(v);var r=CryptoJS.enc.Utf8.parse(y);var u=i[q]["keylen"]+i[q]["ivlen"];var x="";var w=null;for(;;){var s=CryptoJS.algo.MD5.create();if(w!=null){s.update(w)}s.update(r);s.update(t);w=s.finalize();x=x+CryptoJS.enc.Hex.stringify(w);if(x.length>=u*2){break}}var z={};z.keyhex=x.substr(0,i[q]["keylen"]*2);z.ivhex=x.substr(i[q]["keylen"]*2,i[q]["ivlen"]*2);return z};var b=function(p,v,r,w){var s=CryptoJS.enc.Base64.parse(p);var q=CryptoJS.enc.Hex.stringify(s);var u=i[v]["proc"];var t=u(q,r,w);return t};var h=function(p,s,q,u){var r=i[s]["eproc"];var t=r(p,q,u);return t};return{version:"1.0.0",parsePKCS5PEM:function(p){return n(p)},getKeyAndUnusedIvByPasscodeAndIvsalt:function(q,p,r){return j(q,p,r)},decryptKeyB64:function(p,r,q,s){return b(p,r,q,s)},getDecryptedKeyHex:function(y,x){var q=n(y);var t=q.type;var r=q.cipher;var p=q.ivsalt;var s=q.data;var w=j(r,x,p);var v=w.keyhex;var u=b(s,r,v,p);return u},getEncryptedPKCS5PEMFromPrvKeyHex:function(x,s,A,t,r){var p="";if(typeof t=="undefined"||t==null){t="AES-256-CBC"}if(typeof i[t]=="undefined"){throw"KEYUTIL unsupported algorithm: "+t}if(typeof r=="undefined"||r==null){var v=i[t]["ivlen"];var u=m(v);r=u.toUpperCase()}var z=j(t,A,r);var y=z.keyhex;var w=h(s,t,y,r);var q=w.replace(/(.{64})/g,"$1\r\n");var p="-----BEGIN "+x+" PRIVATE KEY-----\r\n";p+="Proc-Type: 4,ENCRYPTED\r\n";p+="DEK-Info: "+t+","+r+"\r\n";p+="\r\n";p+=q;p+="\r\n-----END "+x+" PRIVATE KEY-----\r\n";return p},parseHexOfEncryptedPKCS8:function(y){var B=ASN1HEX;var z=B.getChildIdx;var w=B.getV;var t={};var r=z(y,0);if(r.length!=2){throw"malformed format: SEQUENCE(0).items != 2: "+r.length}t.ciphertext=w(y,r[1]);var A=z(y,r[0]);if(A.length!=2){throw"malformed format: SEQUENCE(0.0).items != 2: "+A.length}if(w(y,A[0])!="2a864886f70d01050d"){throw"this only supports pkcs5PBES2"}var p=z(y,A[1]);if(A.length!=2){throw"malformed format: SEQUENCE(0.0.1).items != 2: "+p.length}var q=z(y,p[1]);if(q.length!=2){throw"malformed format: SEQUENCE(0.0.1.1).items != 2: "+q.length}if(w(y,q[0])!="2a864886f70d0307"){throw"this only supports TripleDES"}t.encryptionSchemeAlg="TripleDES";t.encryptionSchemeIV=w(y,q[1]);var s=z(y,p[0]);if(s.length!=2){throw"malformed format: SEQUENCE(0.0.1.0).items != 2: "+s.length}if(w(y,s[0])!="2a864886f70d01050c"){throw"this only supports pkcs5PBKDF2"}var x=z(y,s[1]);if(x.length<2){throw"malformed format: SEQUENCE(0.0.1.0.1).items < 2: "+x.length}t.pbkdf2Salt=w(y,x[0]);var u=w(y,x[1]);try{t.pbkdf2Iter=parseInt(u,16)}catch(v){throw"malformed format pbkdf2Iter: "+u}return t},getPBKDF2KeyHexFromParam:function(u,p){var t=CryptoJS.enc.Hex.parse(u.pbkdf2Salt);var q=u.pbkdf2Iter;var s=CryptoJS.PBKDF2(p,t,{keySize:192/32,iterations:q});var r=CryptoJS.enc.Hex.stringify(s);return r},_getPlainPKCS8HexFromEncryptedPKCS8PEM:function(x,y){var r=pemtohex(x,"ENCRYPTED PRIVATE KEY");var p=this.parseHexOfEncryptedPKCS8(r);var u=KEYUTIL.getPBKDF2KeyHexFromParam(p,y);var v={};v.ciphertext=CryptoJS.enc.Hex.parse(p.ciphertext);var t=CryptoJS.enc.Hex.parse(u);var s=CryptoJS.enc.Hex.parse(p.encryptionSchemeIV);var w=CryptoJS.TripleDES.decrypt(v,t,{iv:s});var q=CryptoJS.enc.Hex.stringify(w);return q},getKeyFromEncryptedPKCS8PEM:function(s,q){var p=this._getPlainPKCS8HexFromEncryptedPKCS8PEM(s,q);var r=this.getKeyFromPlainPrivatePKCS8Hex(p);return r},parsePlainPrivatePKCS8Hex:function(s){var v=ASN1HEX;var u=v.getChildIdx;var t=v.getV;var q={};q.algparam=null;if(s.substr(0,2)!="30"){throw"malformed plain PKCS8 private key(code:001)"}var r=u(s,0);if(r.length!=3){throw"malformed plain PKCS8 private key(code:002)"}if(s.substr(r[1],2)!="30"){throw"malformed PKCS8 private key(code:003)"}var p=u(s,r[1]);if(p.length!=2){throw"malformed PKCS8 private key(code:004)"}if(s.substr(p[0],2)!="06"){throw"malformed PKCS8 private key(code:005)"}q.algoid=t(s,p[0]);if(s.substr(p[1],2)=="06"){q.algparam=t(s,p[1])}if(s.substr(r[2],2)!="04"){throw"malformed PKCS8 private key(code:006)"}q.keyidx=v.getVidx(s,r[2]);return q},getKeyFromPlainPrivatePKCS8PEM:function(q){var p=pemtohex(q,"PRIVATE KEY");var r=this.getKeyFromPlainPrivatePKCS8Hex(p);return r},getKeyFromPlainPrivatePKCS8Hex:function(p){var q=this.parsePlainPrivatePKCS8Hex(p);var r;if(q.algoid=="2a864886f70d010101"){r=new RSAKey()}else{if(q.algoid=="2a8648ce380401"){r=new KJUR.crypto.DSA()}else{if(q.algoid=="2a8648ce3d0201"){r=new KJUR.crypto.ECDSA()}else{throw"unsupported private key algorithm"}}}r.readPKCS8PrvKeyHex(p);return r},_getKeyFromPublicPKCS8Hex:function(q){var p;var r=ASN1HEX.getVbyList(q,0,[0,0],"06");if(r==="2a864886f70d010101"){p=new RSAKey()}else{if(r==="2a8648ce380401"){p=new KJUR.crypto.DSA()}else{if(r==="2a8648ce3d0201"){p=new KJUR.crypto.ECDSA()}else{throw"unsupported PKCS#8 public key hex"}}}p.readPKCS8PubKeyHex(q);return p},parsePublicRawRSAKeyHex:function(r){var u=ASN1HEX;var t=u.getChildIdx;var s=u.getV;var p={};if(r.substr(0,2)!="30"){throw"malformed RSA key(code:001)"}var q=t(r,0);if(q.length!=2){throw"malformed RSA key(code:002)"}if(r.substr(q[0],2)!="02"){throw"malformed RSA key(code:003)"}p.n=s(r,q[0]);if(r.substr(q[1],2)!="02"){throw"malformed RSA key(code:004)"}p.e=s(r,q[1]);return p},parsePublicPKCS8Hex:function(t){var v=ASN1HEX;var u=v.getChildIdx;var s=v.getV;var q={};q.algparam=null;var r=u(t,0);if(r.length!=2){throw"outer DERSequence shall have 2 elements: "+r.length}var w=r[0];if(t.substr(w,2)!="30"){throw"malformed PKCS8 public key(code:001)"}var p=u(t,w);if(p.length!=2){throw"malformed PKCS8 public key(code:002)"}if(t.substr(p[0],2)!="06"){throw"malformed PKCS8 public key(code:003)"}q.algoid=s(t,p[0]);if(t.substr(p[1],2)=="06"){q.algparam=s(t,p[1])}else{if(t.substr(p[1],2)=="30"){q.algparam={};q.algparam.p=v.getVbyList(t,p[1],[0],"02");q.algparam.q=v.getVbyList(t,p[1],[1],"02");q.algparam.g=v.getVbyList(t,p[1],[2],"02")}}if(t.substr(r[1],2)!="03"){throw"malformed PKCS8 public key(code:004)"}q.key=s(t,r[1]).substr(2);return q},}}();KEYUTIL.getKey=function(l,k,n){var G=ASN1HEX,L=G.getChildIdx,v=G.getV,d=G.getVbyList,c=KJUR.crypto,i=c.ECDSA,C=c.DSA,w=RSAKey,M=pemtohex,F=KEYUTIL;if(typeof w!="undefined"&&l instanceof w){return l}if(typeof i!="undefined"&&l instanceof i){return l}if(typeof C!="undefined"&&l instanceof C){return l}if(l.curve!==undefined&&l.xy!==undefined&&l.d===undefined){return new i({pub:l.xy,curve:l.curve})}if(l.curve!==undefined&&l.d!==undefined){return new i({prv:l.d,curve:l.curve})}if(l.kty===undefined&&l.n!==undefined&&l.e!==undefined&&l.d===undefined){var P=new w();P.setPublic(l.n,l.e);return P}if(l.kty===undefined&&l.n!==undefined&&l.e!==undefined&&l.d!==undefined&&l.p!==undefined&&l.q!==undefined&&l.dp!==undefined&&l.dq!==undefined&&l.co!==undefined&&l.qi===undefined){var P=new w();P.setPrivateEx(l.n,l.e,l.d,l.p,l.q,l.dp,l.dq,l.co);return P}if(l.kty===undefined&&l.n!==undefined&&l.e!==undefined&&l.d!==undefined&&l.p===undefined){var P=new w();P.setPrivate(l.n,l.e,l.d);return P}if(l.p!==undefined&&l.q!==undefined&&l.g!==undefined&&l.y!==undefined&&l.x===undefined){var P=new C();P.setPublic(l.p,l.q,l.g,l.y);return P}if(l.p!==undefined&&l.q!==undefined&&l.g!==undefined&&l.y!==undefined&&l.x!==undefined){var P=new C();P.setPrivate(l.p,l.q,l.g,l.y,l.x);return P}if(l.kty==="RSA"&&l.n!==undefined&&l.e!==undefined&&l.d===undefined){var P=new w();P.setPublic(b64utohex(l.n),b64utohex(l.e));return P}if(l.kty==="RSA"&&l.n!==undefined&&l.e!==undefined&&l.d!==undefined&&l.p!==undefined&&l.q!==undefined&&l.dp!==undefined&&l.dq!==undefined&&l.qi!==undefined){var P=new w();P.setPrivateEx(b64utohex(l.n),b64utohex(l.e),b64utohex(l.d),b64utohex(l.p),b64utohex(l.q),b64utohex(l.dp),b64utohex(l.dq),b64utohex(l.qi));return P}if(l.kty==="RSA"&&l.n!==undefined&&l.e!==undefined&&l.d!==undefined){var P=new w();P.setPrivate(b64utohex(l.n),b64utohex(l.e),b64utohex(l.d));return P}if(l.kty==="EC"&&l.crv!==undefined&&l.x!==undefined&&l.y!==undefined&&l.d===undefined){var j=new i({curve:l.crv});var t=j.ecparams.keylen/4;var B=("0000000000"+b64utohex(l.x)).slice(-t);var z=("0000000000"+b64utohex(l.y)).slice(-t);var u="04"+B+z;j.setPublicKeyHex(u);return j}if(l.kty==="EC"&&l.crv!==undefined&&l.x!==undefined&&l.y!==undefined&&l.d!==undefined){var j=new i({curve:l.crv});var t=j.ecparams.keylen/4;var B=("0000000000"+b64utohex(l.x)).slice(-t);var z=("0000000000"+b64utohex(l.y)).slice(-t);var u="04"+B+z;var b=("0000000000"+b64utohex(l.d)).slice(-t);j.setPublicKeyHex(u);j.setPrivateKeyHex(b);return j}if(n==="pkcs5prv"){var J=l,G=ASN1HEX,N,P;N=L(J,0);if(N.length===9){P=new w();P.readPKCS5PrvKeyHex(J)}else{if(N.length===6){P=new C();P.readPKCS5PrvKeyHex(J)}else{if(N.length>2&&J.substr(N[1],2)==="04"){P=new i();P.readPKCS5PrvKeyHex(J)}else{throw"unsupported PKCS#1/5 hexadecimal key"}}}return P}if(n==="pkcs8prv"){var P=F.getKeyFromPlainPrivatePKCS8Hex(l);return P}if(n==="pkcs8pub"){return F._getKeyFromPublicPKCS8Hex(l)}if(n==="x509pub"){return X509.getPublicKeyFromCertHex(l)}if(l.indexOf("-END CERTIFICATE-",0)!=-1||l.indexOf("-END X509 CERTIFICATE-",0)!=-1||l.indexOf("-END TRUSTED CERTIFICATE-",0)!=-1){return X509.getPublicKeyFromCertPEM(l)}if(l.indexOf("-END PUBLIC KEY-")!=-1){var O=pemtohex(l,"PUBLIC KEY");return F._getKeyFromPublicPKCS8Hex(O)}if(l.indexOf("-END RSA PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")==-1){var m=M(l,"RSA PRIVATE KEY");return F.getKey(m,null,"pkcs5prv")}if(l.indexOf("-END DSA PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")==-1){var I=M(l,"DSA PRIVATE KEY");var E=d(I,0,[1],"02");var D=d(I,0,[2],"02");var K=d(I,0,[3],"02");var r=d(I,0,[4],"02");var s=d(I,0,[5],"02");var P=new C();P.setPrivate(new BigInteger(E,16),new BigInteger(D,16),new BigInteger(K,16),new BigInteger(r,16),new BigInteger(s,16));return P}if(l.indexOf("-END PRIVATE KEY-")!=-1){return F.getKeyFromPlainPrivatePKCS8PEM(l)}if(l.indexOf("-END RSA PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")!=-1){var o=F.getDecryptedKeyHex(l,k);var H=new RSAKey();H.readPKCS5PrvKeyHex(o);return H}if(l.indexOf("-END EC PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")!=-1){var I=F.getDecryptedKeyHex(l,k);var P=d(I,0,[1],"04");var f=d(I,0,[2,0],"06");var A=d(I,0,[3,0],"03").substr(2);var e="";if(KJUR.crypto.OID.oidhex2name[f]!==undefined){e=KJUR.crypto.OID.oidhex2name[f]}else{throw"undefined OID(hex) in KJUR.crypto.OID: "+f}var j=new i({curve:e});j.setPublicKeyHex(A);j.setPrivateKeyHex(P);j.isPublic=false;return j}if(l.indexOf("-END DSA PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")!=-1){var I=F.getDecryptedKeyHex(l,k);var E=d(I,0,[1],"02");var D=d(I,0,[2],"02");var K=d(I,0,[3],"02");var r=d(I,0,[4],"02");var s=d(I,0,[5],"02");var P=new C();P.setPrivate(new BigInteger(E,16),new BigInteger(D,16),new BigInteger(K,16),new BigInteger(r,16),new BigInteger(s,16));return P}if(l.indexOf("-END ENCRYPTED PRIVATE KEY-")!=-1){return F.getKeyFromEncryptedPKCS8PEM(l,k)}throw"not supported argument"};KEYUTIL.generateKeypair=function(a,c){if(a=="RSA"){var b=c;var h=new RSAKey();h.generate(b,"10001");h.isPrivate=true;h.isPublic=true;var f=new RSAKey();var e=h.n.toString(16);var i=h.e.toString(16);f.setPublic(e,i);f.isPrivate=false;f.isPublic=true;var k={};k.prvKeyObj=h;k.pubKeyObj=f;return k}else{if(a=="EC"){var d=c;var g=new KJUR.crypto.ECDSA({curve:d});var j=g.generateKeyPairHex();var h=new KJUR.crypto.ECDSA({curve:d});h.setPublicKeyHex(j.ecpubhex);h.setPrivateKeyHex(j.ecprvhex);h.isPrivate=true;h.isPublic=false;var f=new KJUR.crypto.ECDSA({curve:d});f.setPublicKeyHex(j.ecpubhex);f.isPrivate=false;f.isPublic=true;var k={};k.prvKeyObj=h;k.pubKeyObj=f;return k}else{throw"unknown algorithm: "+a}}};KEYUTIL.getPEM=function(b,D,y,m,q,j){var F=KJUR,k=F.asn1,z=k.DERObjectIdentifier,f=k.DERInteger,l=k.ASN1Util.newObject,a=k.x509,C=a.SubjectPublicKeyInfo,e=F.crypto,u=e.DSA,r=e.ECDSA,n=RSAKey;function A(s){var G=l({seq:[{"int":0},{"int":{bigint:s.n}},{"int":s.e},{"int":{bigint:s.d}},{"int":{bigint:s.p}},{"int":{bigint:s.q}},{"int":{bigint:s.dmp1}},{"int":{bigint:s.dmq1}},{"int":{bigint:s.coeff}}]});return G}function B(G){var s=l({seq:[{"int":1},{octstr:{hex:G.prvKeyHex}},{tag:["a0",true,{oid:{name:G.curveName}}]},{tag:["a1",true,{bitstr:{hex:"00"+G.pubKeyHex}}]}]});return s}function x(s){var G=l({seq:[{"int":0},{"int":{bigint:s.p}},{"int":{bigint:s.q}},{"int":{bigint:s.g}},{"int":{bigint:s.y}},{"int":{bigint:s.x}}]});return G}if(((n!==undefined&&b instanceof n)||(u!==undefined&&b instanceof u)||(r!==undefined&&b instanceof r))&&b.isPublic==true&&(D===undefined||D=="PKCS8PUB")){var E=new C(b);var w=E.getEncodedHex();return hextopem(w,"PUBLIC KEY")}if(D=="PKCS1PRV"&&n!==undefined&&b instanceof n&&(y===undefined||y==null)&&b.isPrivate==true){var E=A(b);var w=E.getEncodedHex();return hextopem(w,"RSA PRIVATE KEY")}if(D=="PKCS1PRV"&&r!==undefined&&b instanceof r&&(y===undefined||y==null)&&b.isPrivate==true){var i=new z({name:b.curveName});var v=i.getEncodedHex();var h=B(b);var t=h.getEncodedHex();var p="";p+=hextopem(v,"EC PARAMETERS");p+=hextopem(t,"EC PRIVATE KEY");return p}if(D=="PKCS1PRV"&&u!==undefined&&b instanceof u&&(y===undefined||y==null)&&b.isPrivate==true){var E=x(b);var w=E.getEncodedHex();return hextopem(w,"DSA PRIVATE KEY")}if(D=="PKCS5PRV"&&n!==undefined&&b instanceof n&&(y!==undefined&&y!=null)&&b.isPrivate==true){var E=A(b);var w=E.getEncodedHex();if(m===undefined){m="DES-EDE3-CBC"}return this.getEncryptedPKCS5PEMFromPrvKeyHex("RSA",w,y,m,j)}if(D=="PKCS5PRV"&&r!==undefined&&b instanceof r&&(y!==undefined&&y!=null)&&b.isPrivate==true){var E=B(b);var w=E.getEncodedHex();if(m===undefined){m="DES-EDE3-CBC"}return this.getEncryptedPKCS5PEMFromPrvKeyHex("EC",w,y,m,j)}if(D=="PKCS5PRV"&&u!==undefined&&b instanceof u&&(y!==undefined&&y!=null)&&b.isPrivate==true){var E=x(b);var w=E.getEncodedHex();if(m===undefined){m="DES-EDE3-CBC"}return this.getEncryptedPKCS5PEMFromPrvKeyHex("DSA",w,y,m,j)}var o=function(G,s){var I=c(G,s);var H=new l({seq:[{seq:[{oid:{name:"pkcs5PBES2"}},{seq:[{seq:[{oid:{name:"pkcs5PBKDF2"}},{seq:[{octstr:{hex:I.pbkdf2Salt}},{"int":I.pbkdf2Iter}]}]},{seq:[{oid:{name:"des-EDE3-CBC"}},{octstr:{hex:I.encryptionSchemeIV}}]}]}]},{octstr:{hex:I.ciphertext}}]});return H.getEncodedHex()};var c=function(N,O){var H=100;var M=CryptoJS.lib.WordArray.random(8);var L="DES-EDE3-CBC";var s=CryptoJS.lib.WordArray.random(8);var I=CryptoJS.PBKDF2(O,M,{keySize:192/32,iterations:H});var J=CryptoJS.enc.Hex.parse(N);var K=CryptoJS.TripleDES.encrypt(J,I,{iv:s})+"";var G={};G.ciphertext=K;G.pbkdf2Salt=CryptoJS.enc.Hex.stringify(M);G.pbkdf2Iter=H;G.encryptionSchemeAlg=L;G.encryptionSchemeIV=CryptoJS.enc.Hex.stringify(s);return G};if(D=="PKCS8PRV"&&n!=undefined&&b instanceof n&&b.isPrivate==true){var g=A(b);var d=g.getEncodedHex();var E=l({seq:[{"int":0},{seq:[{oid:{name:"rsaEncryption"}},{"null":true}]},{octstr:{hex:d}}]});var w=E.getEncodedHex();if(y===undefined||y==null){return hextopem(w,"PRIVATE KEY")}else{var t=o(w,y);return hextopem(t,"ENCRYPTED PRIVATE KEY")}}if(D=="PKCS8PRV"&&r!==undefined&&b instanceof r&&b.isPrivate==true){var g=new l({seq:[{"int":1},{octstr:{hex:b.prvKeyHex}},{tag:["a1",true,{bitstr:{hex:"00"+b.pubKeyHex}}]}]});var d=g.getEncodedHex();var E=l({seq:[{"int":0},{seq:[{oid:{name:"ecPublicKey"}},{oid:{name:b.curveName}}]},{octstr:{hex:d}}]});var w=E.getEncodedHex();if(y===undefined||y==null){return hextopem(w,"PRIVATE KEY")}else{var t=o(w,y);return hextopem(t,"ENCRYPTED PRIVATE KEY")}}if(D=="PKCS8PRV"&&u!==undefined&&b instanceof u&&b.isPrivate==true){var g=new f({bigint:b.x});var d=g.getEncodedHex();var E=l({seq:[{"int":0},{seq:[{oid:{name:"dsa"}},{seq:[{"int":{bigint:b.p}},{"int":{bigint:b.q}},{"int":{bigint:b.g}}]}]},{octstr:{hex:d}}]});var w=E.getEncodedHex();if(y===undefined||y==null){return hextopem(w,"PRIVATE KEY")}else{var t=o(w,y);return hextopem(t,"ENCRYPTED PRIVATE KEY")}}throw"unsupported object nor format"};KEYUTIL.getKeyFromCSRPEM=function(b){var a=pemtohex(b,"CERTIFICATE REQUEST");var c=KEYUTIL.getKeyFromCSRHex(a);return c};KEYUTIL.getKeyFromCSRHex=function(a){var c=KEYUTIL.parseCSRHex(a);var b=KEYUTIL.getKey(c.p8pubkeyhex,null,"pkcs8pub");return b};KEYUTIL.parseCSRHex=function(d){var i=ASN1HEX;var f=i.getChildIdx;var c=i.getTLV;var b={};var g=d;if(g.substr(0,2)!="30"){throw"malformed CSR(code:001)"}var e=f(g,0);if(e.length<1){throw"malformed CSR(code:002)"}if(g.substr(e[0],2)!="30"){throw"malformed CSR(code:003)"}var a=f(g,e[0]);if(a.length<3){throw"malformed CSR(code:004)"}b.p8pubkeyhex=c(g,a[2]);return b};KEYUTIL.getJWKFromKey=function(d){var b={};if(d instanceof RSAKey&&d.isPrivate){b.kty="RSA";b.n=hextob64u(d.n.toString(16));b.e=hextob64u(d.e.toString(16));b.d=hextob64u(d.d.toString(16));b.p=hextob64u(d.p.toString(16));b.q=hextob64u(d.q.toString(16));b.dp=hextob64u(d.dmp1.toString(16));b.dq=hextob64u(d.dmq1.toString(16));b.qi=hextob64u(d.coeff.toString(16));return b}else{if(d instanceof RSAKey&&d.isPublic){b.kty="RSA";b.n=hextob64u(d.n.toString(16));b.e=hextob64u(d.e.toString(16));return b}else{if(d instanceof KJUR.crypto.ECDSA&&d.isPrivate){var a=d.getShortNISTPCurveName();if(a!=="P-256"&&a!=="P-384"){throw"unsupported curve name for JWT: "+a}var c=d.getPublicKeyXYHex();b.kty="EC";b.crv=a;b.x=hextob64u(c.x);b.y=hextob64u(c.y);b.d=hextob64u(d.prvKeyHex);return b}else{if(d instanceof KJUR.crypto.ECDSA&&d.isPublic){var a=d.getShortNISTPCurveName();if(a!=="P-256"&&a!=="P-384"){throw"unsupported curve name for JWT: "+a}var c=d.getPublicKeyXYHex();b.kty="EC";b.crv=a;b.x=hextob64u(c.x);b.y=hextob64u(c.y);return b}}}}throw"not supported key object"}; +RSAKey.getPosArrayOfChildrenFromHex=function(a){return ASN1HEX.getChildIdx(a,0)};RSAKey.getHexValueArrayOfChildrenFromHex=function(f){var n=ASN1HEX;var i=n.getV;var k=RSAKey.getPosArrayOfChildrenFromHex(f);var e=i(f,k[0]);var j=i(f,k[1]);var b=i(f,k[2]);var c=i(f,k[3]);var h=i(f,k[4]);var g=i(f,k[5]);var m=i(f,k[6]);var l=i(f,k[7]);var d=i(f,k[8]);var k=new Array();k.push(e,j,b,c,h,g,m,l,d);return k};RSAKey.prototype.readPrivateKeyFromPEMString=function(d){var c=pemtohex(d);var b=RSAKey.getHexValueArrayOfChildrenFromHex(c);this.setPrivateEx(b[1],b[2],b[3],b[4],b[5],b[6],b[7],b[8])};RSAKey.prototype.readPKCS5PrvKeyHex=function(c){var b=RSAKey.getHexValueArrayOfChildrenFromHex(c);this.setPrivateEx(b[1],b[2],b[3],b[4],b[5],b[6],b[7],b[8])};RSAKey.prototype.readPKCS8PrvKeyHex=function(e){var c,j,l,b,a,f,d,k;var m=ASN1HEX;var g=m.getVbyList;if(m.isASN1HEX(e)===false){throw"not ASN.1 hex string"}try{c=g(e,0,[2,0,1],"02");j=g(e,0,[2,0,2],"02");l=g(e,0,[2,0,3],"02");b=g(e,0,[2,0,4],"02");a=g(e,0,[2,0,5],"02");f=g(e,0,[2,0,6],"02");d=g(e,0,[2,0,7],"02");k=g(e,0,[2,0,8],"02")}catch(i){throw"malformed PKCS#8 plain RSA private key"}this.setPrivateEx(c,j,l,b,a,f,d,k)};RSAKey.prototype.readPKCS5PubKeyHex=function(c){var e=ASN1HEX;var b=e.getV;if(e.isASN1HEX(c)===false){throw"keyHex is not ASN.1 hex string"}var a=e.getChildIdx(c,0);if(a.length!==2||c.substr(a[0],2)!=="02"||c.substr(a[1],2)!=="02"){throw"wrong hex for PKCS#5 public key"}var f=b(c,a[0]);var d=b(c,a[1]);this.setPublic(f,d)};RSAKey.prototype.readPKCS8PubKeyHex=function(b){var c=ASN1HEX;if(c.isASN1HEX(b)===false){throw"not ASN.1 hex string"}if(c.getTLVbyList(b,0,[0,0])!=="06092a864886f70d010101"){throw"not PKCS8 RSA public key"}var a=c.getTLVbyList(b,0,[1,0]);this.readPKCS5PubKeyHex(a)};RSAKey.prototype.readCertPubKeyHex=function(b,d){var a,c;a=new X509();a.readCertHex(b);c=a.getPublicKeyHex();this.readPKCS8PubKeyHex(c)}; +var _RE_HEXDECONLY=new RegExp("");_RE_HEXDECONLY.compile("[^0-9a-f]","gi");function _rsasign_getHexPaddedDigestInfoForString(d,e,a){var b=function(f){return KJUR.crypto.Util.hashString(f,a)};var c=b(d);return KJUR.crypto.Util.getPaddedDigestInfoHex(c,a,e)}function _zeroPaddingOfSignature(e,d){var c="";var a=d/4-e.length;for(var b=0;b>24,(d&16711680)>>16,(d&65280)>>8,d&255]))));d+=1}return b}RSAKey.prototype.signPSS=function(e,a,d){var c=function(f){return KJUR.crypto.Util.hashHex(f,a)};var b=c(rstrtohex(e));if(d===undefined){d=-1}return this.signWithMessageHashPSS(b,a,d)};RSAKey.prototype.signWithMessageHashPSS=function(l,a,k){var b=hextorstr(l);var g=b.length;var m=this.n.bitLength()-1;var c=Math.ceil(m/8);var d;var o=function(i){return KJUR.crypto.Util.hashHex(i,a)};if(k===-1||k===undefined){k=g}else{if(k===-2){k=c-g-2}else{if(k<-2){throw"invalid salt length"}}}if(c<(g+k+2)){throw"data too long"}var f="";if(k>0){f=new Array(k);new SecureRandom().nextBytes(f);f=String.fromCharCode.apply(String,f)}var n=hextorstr(o(rstrtohex("\x00\x00\x00\x00\x00\x00\x00\x00"+b+f)));var j=[];for(d=0;d>(8*c-m))&255;q[0]&=~p;for(d=0;dthis.n.bitLength()){return 0}var i=this.doPublic(b);var e=i.toString(16).replace(/^1f+00/,"");var g=_rsasign_getAlgNameAndHashFromHexDisgestInfo(e);if(g.length==0){return false}var d=g[0];var h=g[1];var a=function(k){return KJUR.crypto.Util.hashString(k,d)};var c=a(f);return(h==c)};RSAKey.prototype.verifyWithMessageHash=function(e,a){a=a.replace(_RE_HEXDECONLY,"");a=a.replace(/[ \n]+/g,"");var b=parseBigInt(a,16);if(b.bitLength()>this.n.bitLength()){return 0}var h=this.doPublic(b);var g=h.toString(16).replace(/^1f+00/,"");var c=_rsasign_getAlgNameAndHashFromHexDisgestInfo(g);if(c.length==0){return false}var d=c[0];var f=c[1];return(f==e)};RSAKey.prototype.verifyPSS=function(c,b,a,f){var e=function(g){return KJUR.crypto.Util.hashHex(g,a)};var d=e(rstrtohex(c));if(f===undefined){f=-1}return this.verifyWithMessageHashPSS(d,b,a,f)};RSAKey.prototype.verifyWithMessageHashPSS=function(f,s,l,c){var k=new BigInteger(s,16);if(k.bitLength()>this.n.bitLength()){return false}var r=function(i){return KJUR.crypto.Util.hashHex(i,l)};var j=hextorstr(f);var h=j.length;var g=this.n.bitLength()-1;var m=Math.ceil(g/8);var q;if(c===-1||c===undefined){c=h}else{if(c===-2){c=m-h-2}else{if(c<-2){throw"invalid salt length"}}}if(m<(h+c+2)){throw"data too long"}var a=this.doPublic(k).toByteArray();for(q=0;q>(8*m-g))&255;if((d.charCodeAt(0)&p)!==0){throw"bits beyond keysize not zero"}var n=pss_mgf1_str(e,d.length,r);var o=[];for(q=0;q0){var b=":"+n.join(":")+":";if(b.indexOf(":"+k+":")==-1){throw"algorithm '"+k+"' not accepted in the list"}}if(k!="none"&&B===null){throw"key shall be specified to verify."}if(typeof B=="string"&&B.indexOf("-----BEGIN ")!=-1){B=KEYUTIL.getKey(B)}if(z=="RS"||z=="PS"){if(!(B instanceof m)){throw"key shall be a RSAKey obj for RS* and PS* algs"}}if(z=="ES"){if(!(B instanceof p)){throw"key shall be a ECDSA obj for ES* algs"}}if(k=="none"){}var u=null;if(t.jwsalg2sigalg[l.alg]===undefined){throw"unsupported alg name: "+k}else{u=t.jwsalg2sigalg[k]}if(u=="none"){throw"not supported"}else{if(u.substr(0,4)=="Hmac"){var o=null;if(B===undefined){throw"hexadecimal key shall be specified for HMAC"}var j=new s({alg:u,pass:B});j.updateString(c);o=j.doFinal();return A==o}else{if(u.indexOf("withECDSA")!=-1){var h=null;try{h=p.concatSigToASN1Sig(A)}catch(v){return false}var g=new d({alg:u});g.init(B);g.updateString(c);return g.verify(h)}else{var g=new d({alg:u});g.init(B);g.updateString(c);return g.verify(A)}}}};KJUR.jws.JWS.parse=function(g){var c=g.split(".");var b={};var f,e,d;if(c.length!=2&&c.length!=3){throw"malformed sJWS: wrong number of '.' splitted elements"}f=c[0];e=c[1];if(c.length==3){d=c[2]}b.headerObj=KJUR.jws.JWS.readSafeJSONString(b64utoutf8(f));b.payloadObj=KJUR.jws.JWS.readSafeJSONString(b64utoutf8(e));b.headerPP=JSON.stringify(b.headerObj,null," ");if(b.payloadObj==null){b.payloadPP=b64utoutf8(e)}else{b.payloadPP=JSON.stringify(b.payloadObj,null," ")}if(d!==undefined){b.sigHex=b64utohex(d)}return b};KJUR.jws.JWS.verifyJWT=function(e,l,r){var d=KJUR,j=d.jws,o=j.JWS,n=o.readSafeJSONString,p=o.inArray,f=o.includedArray;var k=e.split(".");var c=k[0];var i=k[1];var q=c+"."+i;var m=b64utohex(k[2]);var h=n(b64utoutf8(c));var g=n(b64utoutf8(i));if(h.alg===undefined){return false}if(r.alg===undefined){throw"acceptField.alg shall be specified"}if(!p(h.alg,r.alg)){return false}if(g.iss!==undefined&&typeof r.iss==="object"){if(!p(g.iss,r.iss)){return false}}if(g.sub!==undefined&&typeof r.sub==="object"){if(!p(g.sub,r.sub)){return false}}if(g.aud!==undefined&&typeof r.aud==="object"){if(typeof g.aud=="string"){if(!p(g.aud,r.aud)){return false}}else{if(typeof g.aud=="object"){if(!f(g.aud,r.aud)){return false}}}}var b=j.IntDate.getNow();if(r.verifyAt!==undefined&&typeof r.verifyAt==="number"){b=r.verifyAt}if(r.gracePeriod===undefined||typeof r.gracePeriod!=="number"){r.gracePeriod=0}if(g.exp!==undefined&&typeof g.exp=="number"){if(g.exp+r.gracePeriodl){this.aHeader.pop()}if(this.aSignature.length>l){this.aSignature.pop()}throw"addSignature failed: "+i}};this.verifyAll=function(h){if(this.aHeader.length!==h.length||this.aSignature.length!==h.length){return false}for(var g=0;g0){this.aHeader=g.headers}else{throw"malformed header"}if(typeof g.payload==="string"){this.sPayload=g.payload}else{throw"malformed signatures"}if(g.signatures.length>0){this.aSignatures=g.signatures}else{throw"malformed signatures"}}catch(e){throw"malformed JWS-JS JSON object: "+e}}};this.getJSON=function(){return{headers:this.aHeader,payload:this.sPayload,signatures:this.aSignature}};this.isEmpty=function(){if(this.aHeader.length==0){return 1}return 0}}; diff --git a/w3c-visserver-api/test/web-client/rest-client.js b/w3c-visserver-api/test/web-client/rest-client.js new file mode 100644 index 0000000..634d570 --- /dev/null +++ b/w3c-visserver-api/test/web-client/rest-client.js @@ -0,0 +1,171 @@ +var requestObj = null; +var requestUrl = ''; + +// JWT header +var jwtHeader = { + "alg": "RS256", + "typ": "JWT" +}; + +//////////////////////// +// Startup configuration + +// setup default values for server +document.getElementById('server-address').value = "localhost"; +document.getElementById('server-port').value = 8090; +document.getElementById('server-doc-root').value = "vss/api/v1"; + +// setup event handlers +document.getElementById('make-get-request').onclick = function req() {return makeRestRequest("GET");}; +document.getElementById('make-put-request').onclick = function req() {return makeRestRequest("PUT");}; +document.getElementById('make-post-request').onclick = function req() {return makeRestRequest("POST");}; +document.getElementById('make-auth-request').onclick = function req() {return makeAuthRequest("POST");}; + +document.getElementById('client-token').addEventListener('paste', onTokenUpdate); +document.getElementById('client-token').addEventListener('keyup', onTokenUpdate); +document.getElementById('client-token').addEventListener('change', onTokenUpdate); + +document.getElementById('client-key-cert').addEventListener('paste', onTokenUpdate); +document.getElementById('client-key-cert').addEventListener('keyup', onTokenUpdate); +document.getElementById('client-key-cert').addEventListener('change', onTokenUpdate); + +/////////////////// +// Implementation + +function onTokenUpdate() { + if((document.getElementById("client-token").value != '') && + (document.getElementById('client-key-cert').value != '')) { + getEncodedToken(); + } + return false; +} + +function onListResourceClick() { + document.getElementById('resource-path').value = this.innerText; + return false; +} + +function getEncodedToken() { + var data = document.getElementById("client-token").value; + var sHead = JSON.stringify(jwtHeader); + var head = KJUR.jws.JWS.readSafeJSONString(sHead); + var sPayload = data;//newline_toDos(document.form1.jwspayload1.value); + var sPemPrvKey = document.getElementById("client-key-cert").value; + + var jws = new KJUR.jws.JWS(); + var sResult = null; + try { + prv = KEYUTIL.getKey(sPemPrvKey); + + sResult = KJUR.jws.JWS.sign(head.alg, sHead, sPayload, prv); + document.getElementById("auth-token-string").value = sResult; + } + catch (ex) { + alert("Error: " + ex); + } + + return false; +} + +function addReqToLog(type, resource) { + var newListItem = document.createElement("LI"); + newListItem.setAttribute('class', 'li-request'); + + var newTypeDiv = document.createElement("div"); + newTypeDiv.setAttribute('class', 'liType'); + newTypeDiv.textContent = type; + newListItem.appendChild(newTypeDiv); + + var newResourceDiv = document.createElement("div"); + newResourceDiv.setAttribute('class', 'liResource'); + newResourceDiv.textContent = resource; + newResourceDiv.onclick = onListResourceClick; + newListItem.appendChild(newResourceDiv); + + var list = document.getElementById("log-list"); + list.insertBefore(newListItem, list.childNodes[0]); + +} + +function addRespToLog(htmlStatus, message) { + var type = 'SUCCESS'; + + if (htmlStatus == 200) { + } else { + type = 'ERROR: HTML status = ' + htmlStatus; + // if no response + if (message == '') { + message = 'No response: Check server status or if requested HTTP method is supported by VIS Server'; + } + } + var newListItem = document.createElement("LI"); + if (htmlStatus == 200) { + newListItem.setAttribute('class', 'li-response-ok'); + } else { + newListItem.setAttribute('class', 'li-response-nok'); + } + + var newTypeDiv = document.createElement("div"); + newTypeDiv.setAttribute('class', 'liType'); + newTypeDiv.textContent = type; + newListItem.appendChild(newTypeDiv); + + var newResourceDiv = document.createElement("div"); + newResourceDiv.setAttribute('class', 'liResource'); + newResourceDiv.textContent = message; + newListItem.appendChild(newResourceDiv); + + var list = document.getElementById("log-list"); + list.insertBefore(newListItem, list.childNodes[0]); + +} + +function makeHttpRequest(type, requestUrl, resourcePath) { + requestObj = new XMLHttpRequest(); + var completeUrl = requestUrl + resourcePath; + requestObj.open(type, completeUrl, true); + addReqToLog(type, resourcePath); + + // callback handler when positive response received + requestObj.onreadystatechange = function () { + if (this.readyState == XMLHttpRequest.DONE) { + addRespToLog(this.status, this.responseText); + if (this.status == 200) { + let target = '#json-table'; + document.getElementById('json-table').innerHTML = ''; + jsonView.format(this.responseText, target); + } + } + }; + requestObj.send(null); +} + +function makeAuthRequest(type) { + let server_type = document.querySelector('input[name="HTTPType"]:checked').value; + let server_address = document.getElementById('server-address').value; + let server_port = document.getElementById('server-port').value; + let doc_root = document.getElementById('server-doc-root').value; + let resource = document.getElementById('auth-token-string').value; + + if (server_address != "" && server_port != "") { + requestUrl = server_type + '://' + server_address + ':' + server_port + '/' + doc_root + '/'; + makeHttpRequest(type, requestUrl, 'authorize?token=' + resource); + } + // prevent reloading of page + return false; +} + +function makeRestRequest(type) { + let server_type = document.querySelector('input[name="HTTPType"]:checked').value; + let server_address = document.getElementById('server-address').value; + let server_port = document.getElementById('server-port').value; + let doc_root = document.getElementById('server-doc-root').value; + let resource = document.getElementById('resource-path').value; + + if (server_address != "" && server_port != "") { + requestUrl = server_type + '://' + server_address + ':' + server_port + '/' + doc_root + '/'; + makeHttpRequest(type, requestUrl, resource); + } + // prevent reloadig of page + return false; +} diff --git a/w3c-visserver-api/test/web-client/style.css b/w3c-visserver-api/test/web-client/style.css new file mode 100644 index 0000000..702f2db --- /dev/null +++ b/w3c-visserver-api/test/web-client/style.css @@ -0,0 +1,189 @@ +html { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-family: 'Dosis', sans-serif; + font-size: 13px; + line-height: 1.6; + color: #444; + background-color: white; + box-sizing: border-box; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +#root { + width: auto; +} + +#container { + /*background-color: #f6feff; */ + border-color: #1e9b8a; +} + +#main-content-tab { + margin-right: -360px; + width: 100%; + float: left; +} + +#config-content-tab { + width: 350px; + float: left; +} + +.clear { + clear: both; +} + +.row { + margin-right: 370px; +} + +.row::after { + content: ""; + clear: both; + display: table; +} + +[class*="col-"] { + float: left; + padding: 15px; +} + +.col-logo { +} + +.col-logo img { + width: 100px; + height: 100px; +} + +.col-title { + box-sizing: border-box; +} + +#auth-token { + margin-left: 30px; +} +#auth-token-string { + background-color: #F0F8FF; + width: 100%; + border: 1px solid; +} + +.col-app { + width: 100%; +} + +.col-form { + width: 100%; +} + +#logging { + margin-left: 30px; +} + +#log { + width: 46%; + border: 1px solid; + border-color: #1e9b8a; + border-radius: 6px; + padding: 0; +} + +#log ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +#log ul li { + border-radius: 6px; +} + +.li-request { + background-color: #d6f9ff; +} + +.li-response-ok { + background-color: #d8ffd6; +} + +.li-response-nok { + background-color: #ffd6d6; +} + +.liType { + width: auto; +} + +.liResource { + width: auto; + white-space: pre-wrap; + word-wrap: break-word; +} + +#json-table { + width: 47%; + border: 1px solid; + border-color: #1e9b8a; + border-radius: 6px; +} + +fieldset { + border: 1px solid; + border-color: #1e9b8a; + border-radius: 6px; + width: auto; +} +input { + font-family: 'Dosis', sans-serif; + font-size: 13px; + line-height: 1.6; + color: #444; + border: 1px solid; + border-color: #1e9b8a; + border-radius: 6px; + margin: 4px; +} +input:[type=text]focus { + color: #fffbf0; +} +input:[type=number]focus { + color: #fffbf0; +} + +textarea { + font-family: monospace; + font-size: 8px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + border: 1px solid; + border-color: #1e9b8a; +} + +#auth-token { + width: 100%; +} +#auth-token textarea{ + width: 96%; + height: 70px; + font-size: 9px; + resize: none; + /*user-select: none;*/ + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + border: 1px solid; + border-color: #1e9b8a; +} + +#resource-path { + width: 96%; +} + +#make-get-request #make-post-request { + width: 100px; +} diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index 4db3c76..c9b2d99 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -22,13 +22,13 @@ #include "exception.hpp" // #include -#include "accesschecker.hpp" -#include "authenticator.hpp" -#include "signing.hpp" -#include "subscriptionhandler.hpp" -#include "vssdatabase.hpp" -#include "vsscommandprocessor.hpp" -#include "wsserver.hpp" +#include "AccessChecker.hpp" +#include "Authenticator.hpp" +#include "SigningHandler.hpp" +#include "SubscriptionHandler.hpp" +#include "VssDatabase.hpp" +#include "VssCommandProcessor.hpp" +#include "WsServer.hpp" #include "ILogger.hpp" #include "BasicLogger.hpp" @@ -48,7 +48,7 @@ using namespace std; "w3c-vss": { "Signal.OBD.*": "rw", "Signal.Chassis.Axle.*": "rw", - "Signal.Drivetrain.*": "rw", + "Vehicle.Drivetrain.*": "rw", "Signal.Cabin.Infotainment.*": "rw" } } @@ -62,33 +62,34 @@ namespace bt = boost::unit_test; #define PORT 8090 std::shared_ptr logger; -wsserver* webSocket; -subscriptionhandler* subhandler; -authenticator* authhandler; -accesschecker* accesshandler; -vssdatabase* database; -signing* json_signer; -vsscommandprocessor* commandProc; +std::shared_ptr webSocket; +std::shared_ptr subhandler; +std::shared_ptr authhandler; +std::shared_ptr accesshandler; +std::shared_ptr database; +std::shared_ptr json_signer; +std::shared_ptr commandProc; w3cunittest unittestObj(false); w3cunittest::w3cunittest(bool secure) { + webSocket = std::make_shared(); logger = std::make_shared(static_cast(LogLevel::ALL)); - webSocket = new wsserver(logger, PORT, "vss_rel_2.0.json", secure); - authhandler = new authenticator(logger, "",""); - accesshandler = new accesschecker(authhandler); - subhandler = new subscriptionhandler(logger, webSocket, authhandler, accesshandler); - database = new vssdatabase(logger, subhandler, accesshandler); - commandProc = new vsscommandprocessor(logger, database, authhandler , subhandler); - json_signer = new signing(logger); + authhandler = std::make_shared(logger, "",""); + accesshandler = std::make_shared(authhandler); + subhandler = std::make_shared(logger, webSocket, authhandler, accesshandler); + database = std::make_shared(logger, subhandler, accesshandler); + commandProc = std::make_shared(logger, database, authhandler , subhandler); + json_signer = std::make_shared(logger); database->initJsonTree("vss_rel_2.0.json"); + + webSocket->Initialize(logger, + commandProc, + secure, + PORT); } w3cunittest::~w3cunittest() { - // delete json_signer; - // delete database; - // delete webSocket; - // delete commandProc; } list w3cunittest::test_wrap_getPathForGet(string path , bool &isBranch) { @@ -100,7 +101,7 @@ json w3cunittest::test_wrap_getPathForSet(string path, json value) { } //--------Do not change the order of the tests in this file, because some results are dependent on the previous tests and data in the db------- -//----------------------------------------------------vssdatabase Tests ------------------------------------------------------------------------ +//----------------------------------------------------VssDatabase Tests ------------------------------------------------------------------------ // Get method tests BOOST_AUTO_TEST_CASE(path_for_get_without_wildcard_simple) @@ -384,7 +385,7 @@ BOOST_AUTO_TEST_CASE(set_get_test_all_datatypes_boundary_conditions) string test_path_string = "Vehicle.Cabin.Infotainment.Media.Played.URI"; json result; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1165,11 +1166,11 @@ BOOST_AUTO_TEST_CASE(test_metadata_branch_with_wildcard) } -//----------------------------------------------------vsscommandprocessor Tests ------------------------------------------------------------------------ +//----------------------------------------------------VssCommandProcessor Tests ------------------------------------------------------------------------ BOOST_AUTO_TEST_CASE(process_query_set_get_simple) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1234,7 +1235,7 @@ BOOST_AUTO_TEST_CASE(process_query_set_get_simple) BOOST_AUTO_TEST_CASE(process_query_get_withwildcard) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1276,7 +1277,7 @@ BOOST_AUTO_TEST_CASE(process_query_get_withwildcard) BOOST_AUTO_TEST_CASE(process_query_set_get_withwildcard) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1355,7 +1356,7 @@ BOOST_AUTO_TEST_CASE(process_query_set_get_withwildcard) BOOST_AUTO_TEST_CASE(process_query_get_withwildcard_invalid) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1393,7 +1394,7 @@ BOOST_AUTO_TEST_CASE(process_query_get_withwildcard_invalid) BOOST_AUTO_TEST_CASE(process_query_set_withwildcard_invalid) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1429,7 +1430,7 @@ BOOST_AUTO_TEST_CASE(process_query_set_withwildcard_invalid) BOOST_AUTO_TEST_CASE(process_query_set_invalid_value, *utf::expected_failures(1)) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1465,7 +1466,7 @@ BOOST_AUTO_TEST_CASE(process_query_set_invalid_value, *utf::expected_failures(1) BOOST_AUTO_TEST_CASE(process_query_set_one_valid_one_invalid_value, *utf::expected_failures(1)) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1530,12 +1531,12 @@ BOOST_AUTO_TEST_CASE(process_query_set_one_valid_one_invalid_value, *utf::expect BOOST_TEST(response_response_getvalid_json == expected_getvalid); } -//----------------------------------------------------json signing Tests ------------------------------------------------------------------------ +//----------------------------------------------------json SigningHandler Tests ------------------------------------------------------------------------ -BOOST_AUTO_TEST_CASE(json_signing) +BOOST_AUTO_TEST_CASE(json_SigningHandler) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1607,7 +1608,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJyIn19.jbhdMq5hEWXXNfZn9xE4rECIWEVsw-q3g-jxp5lLS0VAZ2WYOGoSd5JX2P9YG0Lals7Ue0wdXtgLSFtvIGU4Ol2MuPaF-Rbb-Q5O4njxg60AZ00kr6RywpyGZHK0eT9MuFCnVMN8Krf3lo2pPB1ms-WAHX6rfArbXxx0FsMau9Ewn_3m3Sc-6sz5alQw1Y7Rk0GD9Y7WP_mbICU__gd40Ypu_ki1i59M8ba5GNfd8RytEIJXAg7RTcKREWDZfMdFH5X7F6gAPA7h_tVH3-bsbT-nOsKCbM-3PM0EKAOl104SwmKcqoWnfXbUow5zt25O8LYwmrukuRBtWiLI5FxeW6ovmS-1acvS3w1LXlQZVGWtM_ZC7shtHh-iz7nyL1WCTpZswHgoqVrvnJ0PVZQkBMBFKvsbu9WkWPUqHe0sx2cDUOdolelspfClO6iP7CYTUQQqyDov9zByDiBfQ7rILQ_LcwPG6UAAbEgM0pC_lntsPzbdcq0V-rE_OMO6y7HtmGN7GPhYHGU0K4qQBuYI_Pdn2gqyCEciI6_awB1LG4RwfoC8ietGUuGmxdcl2PWm0DI-Kj1f1bNlwc-54LKg8v5K54zsBdmK4SrrJ6Nt6OgCqq3On7zHfTDFN01dqWP6EoQHhEn6Akx5HiioTW3CHSVq6pd09Po5cgAAIoQE2U0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1661,7 +1662,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_path) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJyIn19.jbhdMq5hEWXXNfZn9xE4rECIWEVsw-q3g-jxp5lLS0VAZ2WYOGoSd5JX2P9YG0Lals7Ue0wdXtgLSFtvIGU4Ol2MuPaF-Rbb-Q5O4njxg60AZ00kr6RywpyGZHK0eT9MuFCnVMN8Krf3lo2pPB1ms-WAHX6rfArbXxx0FsMau9Ewn_3m3Sc-6sz5alQw1Y7Rk0GD9Y7WP_mbICU__gd40Ypu_ki1i59M8ba5GNfd8RytEIJXAg7RTcKREWDZfMdFH5X7F6gAPA7h_tVH3-bsbT-nOsKCbM-3PM0EKAOl104SwmKcqoWnfXbUow5zt25O8LYwmrukuRBtWiLI5FxeW6ovmS-1acvS3w1LXlQZVGWtM_ZC7shtHh-iz7nyL1WCTpZswHgoqVrvnJ0PVZQkBMBFKvsbu9WkWPUqHe0sx2cDUOdolelspfClO6iP7CYTUQQqyDov9zByDiBfQ7rILQ_LcwPG6UAAbEgM0pC_lntsPzbdcq0V-rE_OMO6y7HtmGN7GPhYHGU0K4qQBuYI_Pdn2gqyCEciI6_awB1LG4RwfoC8ietGUuGmxdcl2PWm0DI-Kj1f1bNlwc-54LKg8v5K54zsBdmK4SrrJ6Nt6OgCqq3On7zHfTDFN01dqWP6EoQHhEn6Akx5HiioTW3CHSVq6pd09Po5cgAAIoQE2U0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1713,7 +1714,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_branch_path) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJyIn19.jbhdMq5hEWXXNfZn9xE4rECIWEVsw-q3g-jxp5lLS0VAZ2WYOGoSd5JX2P9YG0Lals7Ue0wdXtgLSFtvIGU4Ol2MuPaF-Rbb-Q5O4njxg60AZ00kr6RywpyGZHK0eT9MuFCnVMN8Krf3lo2pPB1ms-WAHX6rfArbXxx0FsMau9Ewn_3m3Sc-6sz5alQw1Y7Rk0GD9Y7WP_mbICU__gd40Ypu_ki1i59M8ba5GNfd8RytEIJXAg7RTcKREWDZfMdFH5X7F6gAPA7h_tVH3-bsbT-nOsKCbM-3PM0EKAOl104SwmKcqoWnfXbUow5zt25O8LYwmrukuRBtWiLI5FxeW6ovmS-1acvS3w1LXlQZVGWtM_ZC7shtHh-iz7nyL1WCTpZswHgoqVrvnJ0PVZQkBMBFKvsbu9WkWPUqHe0sx2cDUOdolelspfClO6iP7CYTUQQqyDov9zByDiBfQ7rILQ_LcwPG6UAAbEgM0pC_lntsPzbdcq0V-rE_OMO6y7HtmGN7GPhYHGU0K4qQBuYI_Pdn2gqyCEciI6_awB1LG4RwfoC8ietGUuGmxdcl2PWm0DI-Kj1f1bNlwc-54LKg8v5K54zsBdmK4SrrJ6Nt6OgCqq3On7zHfTDFN01dqWP6EoQHhEn6Akx5HiioTW3CHSVq6pd09Po5cgAAIoQE2U0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1765,7 +1766,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_non_permitted_path, *utf::expect string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJyIn19.jbhdMq5hEWXXNfZn9xE4rECIWEVsw-q3g-jxp5lLS0VAZ2WYOGoSd5JX2P9YG0Lals7Ue0wdXtgLSFtvIGU4Ol2MuPaF-Rbb-Q5O4njxg60AZ00kr6RywpyGZHK0eT9MuFCnVMN8Krf3lo2pPB1ms-WAHX6rfArbXxx0FsMau9Ewn_3m3Sc-6sz5alQw1Y7Rk0GD9Y7WP_mbICU__gd40Ypu_ki1i59M8ba5GNfd8RytEIJXAg7RTcKREWDZfMdFH5X7F6gAPA7h_tVH3-bsbT-nOsKCbM-3PM0EKAOl104SwmKcqoWnfXbUow5zt25O8LYwmrukuRBtWiLI5FxeW6ovmS-1acvS3w1LXlQZVGWtM_ZC7shtHh-iz7nyL1WCTpZswHgoqVrvnJ0PVZQkBMBFKvsbu9WkWPUqHe0sx2cDUOdolelspfClO6iP7CYTUQQqyDov9zByDiBfQ7rILQ_LcwPG6UAAbEgM0pC_lntsPzbdcq0V-rE_OMO6y7HtmGN7GPhYHGU0K4qQBuYI_Pdn2gqyCEciI6_awB1LG4RwfoC8ietGUuGmxdcl2PWm0DI-Kj1f1bNlwc-54LKg8v5K54zsBdmK4SrrJ6Nt6OgCqq3On7zHfTDFN01dqWP6EoQHhEn6Akx5HiioTW3CHSVq6pd09Po5cgAAIoQE2U0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1816,7 +1817,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_invalid_permission_valid_path) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQxIjoiciJ9fQ.hwJVPN9jBuu5vePuLh3wrn2wGXmkzbnKBKJI5FRcP_40AgQcaYlxKZf5OE3cb-CLXJLGWqnQAGKwfWawFaFIMumibIoruZghbeW3mMPtD2PRLXh5HYZuAxVHInvLqVvR4BGEFCvAx36b5aXWlmtYG69Qr1u77qEi62vVR7bh9K05ZDxaW_iQgpURlzLNQYnfzQ0POFhWeHr9RoL8xypnE8QcAb-AtS4flXov1glt2XSgVxaBf0tYAnsrsBDSPpK6_ldRAY-9LbBRK4YLyTzCRXK3KYr9hdh1pWodeKmVGAObsCUwInPdj2C17vFxBLigdPccbfViY399rPCsH6lLRU3sQo-5UWpP9pvQgieu8XpewrIXJxDTu31PpTgc60UuaoDfnPnOvgQzucBNJRGTd6sc6defijjEpLZjX2HfIs50j8uAsMjYKqJqs0LGsNBZFkKHA4ZxpgxUwWoznlponYWN_G-iSy8ASAPjsLMnYKaC0whT3lTWjpPg-yGAIAlyvH7c-QwPf1t-jUjhffXRWp3_t824-Cey93P8ZLwegL2ww6QHyzRQKnAmEnn8KgAdl99zpFfeEB6Or8o--ILIraVN4xe2CfHMKXFD3RxJ2CCbOL5gREuChoXmFY0dYZ34SoOzsnDQ-Jbvu9JTewI9DCnSCbjGw5tJZzAetuckT7w"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1867,7 +1868,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_branch_permission_valid_path) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQiOiJyIn19.aFM4kmqmupJmxkCXXPHUnmC5JC4ioIYwczk_YcGLy8ULfT4As6fzZNnAFTZVk-axBbIHnU3aUTigI5wZroF0ZkdWzJ1FA1-ZEXRLwUug9pigOC4hvTn4layT4tSEylHm3Mhh8M3Z0O4QKr-TuKZgZcbIICjrI3GQvm0kdkAO1hUTDPF2yQv16qCPuWvmJEDmy70MjzZfKIy94LCXL6Gf1OdIo4hXIbhLFPOR4ea8iaEz35VEjEZ828KbP8DCXRNWlad-CjAx7f4whS_YHctVZelFoa5G-MzNXRcr54VmkGbYM4WGDn6Twamsfb7YmwROzNmsI8DNahWWXciWxVZElsAexKx5yFXr3CslxG8dbgZgHWQ1tZe0Nq1b-4XUkomz6e6hd0iUF913D-TjvBACz4fpl5_28Wr0TMy84w1DvkpfNmgQ_1fiZNbho2uBxDoisfDWq_sI_ph0xO5mu-HKrs2T_tkuFveCPDHvIm1uZIzT8hX2cFDoBNClUlf4BOo6EHw3Ax9ISr28WzRxpZPOs9bYh4AIlnkqh28P91emK7pdb4eXhZKm3ZVWGEA17pLUUraxEhTYWXAFMsafwqTAaUhYt-JUBiExvHRvZpa49UDkXd4lJvl9gYO9YPiyrG6dioIsK9QSw-Mhob2WacRUzbG0O8V9uVApjw73tK4FYhA"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1920,7 +1921,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_branch_permission_valid_path_2) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQiOiJyIn19.aFM4kmqmupJmxkCXXPHUnmC5JC4ioIYwczk_YcGLy8ULfT4As6fzZNnAFTZVk-axBbIHnU3aUTigI5wZroF0ZkdWzJ1FA1-ZEXRLwUug9pigOC4hvTn4layT4tSEylHm3Mhh8M3Z0O4QKr-TuKZgZcbIICjrI3GQvm0kdkAO1hUTDPF2yQv16qCPuWvmJEDmy70MjzZfKIy94LCXL6Gf1OdIo4hXIbhLFPOR4ea8iaEz35VEjEZ828KbP8DCXRNWlad-CjAx7f4whS_YHctVZelFoa5G-MzNXRcr54VmkGbYM4WGDn6Twamsfb7YmwROzNmsI8DNahWWXciWxVZElsAexKx5yFXr3CslxG8dbgZgHWQ1tZe0Nq1b-4XUkomz6e6hd0iUF913D-TjvBACz4fpl5_28Wr0TMy84w1DvkpfNmgQ_1fiZNbho2uBxDoisfDWq_sI_ph0xO5mu-HKrs2T_tkuFveCPDHvIm1uZIzT8hX2cFDoBNClUlf4BOo6EHw3Ax9ISr28WzRxpZPOs9bYh4AIlnkqh28P91emK7pdb4eXhZKm3ZVWGEA17pLUUraxEhTYWXAFMsafwqTAaUhYt-JUBiExvHRvZpa49UDkXd4lJvl9gYO9YPiyrG6dioIsK9QSw-Mhob2WacRUzbG0O8V9uVApjw73tK4FYhA"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1973,7 +1974,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_permission) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS4qLkVuZ2luZVNwZWVkIjoiciJ9fQ.fUyHFOh3rD2IwOVMIYzNKdR4Y6IbKmQhN3Y2pGfcOy8SjXcE5MS6owIYRUVCxnlnH9-ywpNrwvePgKPHnjnWq8wSHr6I22zh3dNty0dFn-gJ82LQ-aNRKcweFqZXXP7-b-__Lc_ivEZpl-w0T9IzPWsUhZyt82XIPkzOZrfULv-DhXpoVIFTr-nz7KSpypcp0j_zXvbkf35bLLwcca7nMY5a9ftMKzMcv4xWekjPQQYvGchtLi1lOG1k8iHlf_cGsVEE4CK55x3bkrEjOYagT7WlRkMgR4F4HOzG0LNHsiXEVpNf17cs1Fsy6K5ObY-_J4BCx8wWGc7Bnkg4yap_3jG1IW_o7gcINcx4WiTNHz42LU6d0GQ9spc3vSP5vPm7J1aglBqazYf-tWRHp7QF8WDtAgenLpb4Ld4n_Aq5gHBWfOlt4tCyMgOgLlnzUJT1yc65vNesB7zUAFCdJ49kSV4Lwf0hv4W-hXl3wUPvb06yaff4U2_zrDQOc7GhoVLMzHmAYccNlDEMfM6HjQAnGLLIdvMxfs5g4a2CLKfxbOusRAQYNd4XwU4CpNAWabiu0FHIC43vy578zY3dpCHBOtpEC5csNEnHqyTSWdJwMy9BLmPneNM04NIHni-4ir4ExzK1TUmIDisk5_KBWmcjKyW-HX8k_u2gxylCf9I82Y0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2026,7 +2027,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_write_permission) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS4qLkVuZ2luZVNwZWVkIjoidyJ9fQ.Z6sph6d_z6CQ66nawtQsx300u1uxREGreJ70zrKiB9_leskohqwzxGJLG3uIHRAP2SajmMWXAfxrECQsiop_ayblUf7tnhjt4wljJBreZ7P16UJxv7jQVVLf2oke9b2EF9SJCCxEYEirasChwq8kA-oQkZGdoC5lIGDepT8-tM3NZ17nNdQzQJzypLCRfUz1eA7yNnG2C7mfAdSC1aWoLNGwsy62DbgXl-S7gvRYFHZV9Kk9KCjscvGuj3550IqQfMwqnpPn92E_f82b0ip4jtm2pE4tJTw-oPjbojxN9eMFTYSM6krXDQXcCxY-bZ7zARza3qfPA8sePX33WtJPDhKt8pQ-H6mI-9wEVvwDAP9deQD4ljeiOrROKlQ6yh9CNq5ixrOnH9sYnuTe5TaGxwLDTqc4NKd76dicfAMd_dDiAs7C-wl836Xj8Aq-n4CnLW5Bh_DRpnOhyK5FgbQR5DxefVBDfDP43t6STozfk0hrU44GFpWjjedjWoWZoHqjMEcoM3Y890_mxwEoJYJgu3ZH031pY6zCuGpPtm9gysMSUy-KKkPe5jYVZcfXRR9aJRJDIki1MVHBFkbdgWy9ZTfvjQRDSY1LrAysFR6bPDs8_HZFJA7aZZRO2JNxZuizky34Pp2ZqmBQRHo_cehsXDfw5F6yh4gjxrvLprGHhBc"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2077,7 +2078,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_permission_wildcard_req string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS4qLkVuZ2luZVNwZWVkIjoiciJ9fQ.fUyHFOh3rD2IwOVMIYzNKdR4Y6IbKmQhN3Y2pGfcOy8SjXcE5MS6owIYRUVCxnlnH9-ywpNrwvePgKPHnjnWq8wSHr6I22zh3dNty0dFn-gJ82LQ-aNRKcweFqZXXP7-b-__Lc_ivEZpl-w0T9IzPWsUhZyt82XIPkzOZrfULv-DhXpoVIFTr-nz7KSpypcp0j_zXvbkf35bLLwcca7nMY5a9ftMKzMcv4xWekjPQQYvGchtLi1lOG1k8iHlf_cGsVEE4CK55x3bkrEjOYagT7WlRkMgR4F4HOzG0LNHsiXEVpNf17cs1Fsy6K5ObY-_J4BCx8wWGc7Bnkg4yap_3jG1IW_o7gcINcx4WiTNHz42LU6d0GQ9spc3vSP5vPm7J1aglBqazYf-tWRHp7QF8WDtAgenLpb4Ld4n_Aq5gHBWfOlt4tCyMgOgLlnzUJT1yc65vNesB7zUAFCdJ49kSV4Lwf0hv4W-hXl3wUPvb06yaff4U2_zrDQOc7GhoVLMzHmAYccNlDEMfM6HjQAnGLLIdvMxfs5g4a2CLKfxbOusRAQYNd4XwU4CpNAWabiu0FHIC43vy578zY3dpCHBOtpEC5csNEnHqyTSWdJwMy9BLmPneNM04NIHni-4ir4ExzK1TUmIDisk5_KBWmcjKyW-HX8k_u2gxylCf9I82Y0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2129,7 +2130,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_permission_branch_path_ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS4qLkVuZ2luZVNwZWVkIjoiciJ9fQ.fUyHFOh3rD2IwOVMIYzNKdR4Y6IbKmQhN3Y2pGfcOy8SjXcE5MS6owIYRUVCxnlnH9-ywpNrwvePgKPHnjnWq8wSHr6I22zh3dNty0dFn-gJ82LQ-aNRKcweFqZXXP7-b-__Lc_ivEZpl-w0T9IzPWsUhZyt82XIPkzOZrfULv-DhXpoVIFTr-nz7KSpypcp0j_zXvbkf35bLLwcca7nMY5a9ftMKzMcv4xWekjPQQYvGchtLi1lOG1k8iHlf_cGsVEE4CK55x3bkrEjOYagT7WlRkMgR4F4HOzG0LNHsiXEVpNf17cs1Fsy6K5ObY-_J4BCx8wWGc7Bnkg4yap_3jG1IW_o7gcINcx4WiTNHz42LU6d0GQ9spc3vSP5vPm7J1aglBqazYf-tWRHp7QF8WDtAgenLpb4Ld4n_Aq5gHBWfOlt4tCyMgOgLlnzUJT1yc65vNesB7zUAFCdJ49kSV4Lwf0hv4W-hXl3wUPvb06yaff4U2_zrDQOc7GhoVLMzHmAYccNlDEMfM6HjQAnGLLIdvMxfs5g4a2CLKfxbOusRAQYNd4XwU4CpNAWabiu0FHIC43vy578zY3dpCHBOtpEC5csNEnHqyTSWdJwMy9BLmPneNM04NIHni-4ir4ExzK1TUmIDisk5_KBWmcjKyW-HX8k_u2gxylCf9I82Y0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2181,7 +2182,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_full_read_permission) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZSI6InIifX0.AKCWRDrtl4UkFzAGEzsyUZIKH4Q7jHg_Y5p8xVMLWtGTXIeFdQlz7FW-6rAy1Os9ad-H8ghEepcy2gs6DK3kr3FNDxqUyIaz8p_CDkwrgqp0Wczi1DsgwbyTsY2wsDa94Mja3ng2VszQgs4FZmsdbnzFzJeAsGucOZeIQj8w68up6YI6KXiCjO1094I2eixkclNnb1psPiucTkyListTLHxK3029fZT1EGGcrt7ziFYGiT5Z0Zk7x0PSN7dvmaT1rMOBWjbpluLCepkWZYt73ipO8YoAWhluMiW0sJI7ezrIU3UAKDkna_6kjAapm-9vAVTA63Tv4ovnRaALb_V2oxbIGCPTNoAY7ui00uFcxuN2B5l4M05OFgMIcxwS9-UEVQIWbFUxvPTQLNkOp12jd73ic786lUOs7fvuwseMZ2KX9cpV03TSAN1BIwG_TJ0iOQJ_5wuyDFRqwBK8zubg-zohUPMwsLpZgc7fTVI7AhXzGLZ57fE977NsluzfjFS0smtuzN-8JTvAMnCrgTQwu5GyTiL64a3NlYcQ2qt5K6D8xOIs7xSe8i0_PlKSgG8ECMOBE3JwSCwU1q83mTPCTSGHxTaNYnPhputc6gEiPv2VYmppGrMCeG2d5oLNzGhecIaLf_PGTTSmrUUZNK6HkuybgZStgMdN5uMTXrBkEvw"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2234,7 +2235,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuU3BlZWQiOiJ3In19.dQGq3-OFFZSdhDYYd0IpoNvDh-p9Ak6twajzQVqY_MAWqdwc5BG6_AE6QJzvALAlCp2d-I8kE46YFM9kMGkyR7G5LUhT3h3_iEbsceyFBoPFgAwjgff-c5mZ7l7zFUEJAwtPPFysIku_uNOqmQnvUMLCJ6mBApfAaUc_08S7IUe6e6n3eu7mUdG7ZXBsg1ufcFzPZJHEjzwS4c5Nc2ZqJ4zJBMJJqq0K-TgcjsvNDYBz9is2_ClaeoWqwIZmKLszXfVViFww1Ou81ftuAfUcozyCvK3U1LIloIDxyhQ4kuz5J-Hzy2Y2SAWIZs_zwy34fijKTAj9AJSkZz_3X1dd0P0pFPkeuJUL0BVkC0PZRgtVk3IAWgSoCXOg5ra-vEPKemyoZVxGyUEbDM5D3hpSw01bl_YkZZEKvGniYGmrHelofp2NLLq9_ZkO_7_mYXedXoG82CxO315imv2x31tLg-9_oJSYP40PCRd2n4zALnywJEegn143GvSurOAwPRERtJhiI5WKbhPPwuhnhmvZVYkicPO6ZTO-8RwAB0CVaV9HjrY8Cutqb9gXppW3ajZEVRMxN4BHObqfAHeD_snZIp7GeGPRKEvshCKy7A3lynXpNYUAtkSSXUaqesjp9YoOvLM3ka1zQWNFCuLvpOFjCnR-myE4Vw4Kp-iC__Y1OxU"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2286,7 +2287,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_not_permitted) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuU3BlZWQiOiJ3In19.dQGq3-OFFZSdhDYYd0IpoNvDh-p9Ak6twajzQVqY_MAWqdwc5BG6_AE6QJzvALAlCp2d-I8kE46YFM9kMGkyR7G5LUhT3h3_iEbsceyFBoPFgAwjgff-c5mZ7l7zFUEJAwtPPFysIku_uNOqmQnvUMLCJ6mBApfAaUc_08S7IUe6e6n3eu7mUdG7ZXBsg1ufcFzPZJHEjzwS4c5Nc2ZqJ4zJBMJJqq0K-TgcjsvNDYBz9is2_ClaeoWqwIZmKLszXfVViFww1Ou81ftuAfUcozyCvK3U1LIloIDxyhQ4kuz5J-Hzy2Y2SAWIZs_zwy34fijKTAj9AJSkZz_3X1dd0P0pFPkeuJUL0BVkC0PZRgtVk3IAWgSoCXOg5ra-vEPKemyoZVxGyUEbDM5D3hpSw01bl_YkZZEKvGniYGmrHelofp2NLLq9_ZkO_7_mYXedXoG82CxO315imv2x31tLg-9_oJSYP40PCRd2n4zALnywJEegn143GvSurOAwPRERtJhiI5WKbhPPwuhnhmvZVYkicPO6ZTO-8RwAB0CVaV9HjrY8Cutqb9gXppW3ajZEVRMxN4BHObqfAHeD_snZIp7GeGPRKEvshCKy7A3lynXpNYUAtkSSXUaqesjp9YoOvLM3ka1zQWNFCuLvpOFjCnR-myE4Vw4Kp-iC__Y1OxU"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2339,7 +2340,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_wildcard_permission) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuKiI6InJ3In19.LLdJFIuoViNF4uVv1IL30fXPySoM1oCHxLrWm6xM_4eynvXqPvrCI9fW__GMI0d8PxBLpM8FhgG3ynVVlOuLV_Sl6lDImZlkNQiR02lJwFqf67RWVdI4f4uhdHdKjEpe0a0F-6e7McS__3qQYyjNuQAZIIXkZUIDyXye9upwNARj1wGPtZyNzSY1uyxmuc7MMPaILAIzL8ZnY_D9qgbpbiInGavZtDE_X1iy9GhxbUguP8oiVYn14-H6RBDIF0s5dXwXnJ0cm9Q2DTFpb0YRq4sMgTC4PT1Smdda_6Pj2ELmBjGbH7PYlqfVk1jVdSPGcUpU48e__qVitSRkEK_unM5CihrDVIy7nw3_KclIZJa8_af3kQ4x-leg-ErRCt78j5l0DDxIo5EtCxAeLbO7baZD6D1tPrb3vYkI_9zl1vzydp_nmNMS9QzCRq5yEJfP07gpvG0Z1O0tSLedSCG9cZJ-lJ3Oj3bqxNxk3ih6vKfjnvjAgli7wEP_etHofZmqs3NI-qtP6ldz93fBfAK_iApsSnG4PV7oS_-3UQIow6fUHAA8szn4Ad1ZYiaDsXRcHbdIoLEemGDllkRTYNBe_5vFDT3s1gY82L3fvgwAzTGZ2k46eh66Zx3SmuPgHlCQK6gR-6eAVn0jh_Tjk5vubtin6UdRjHF0Y4BvCwvE0bk"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2417,7 +2418,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_branch_permission) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQiOiJ3In19.AzvUr20ZqIMt2mKhkYhM0b3RgBSnVii6oFpU6tgkP7vLOAURSqN8Lazx07eqz8CXTONqIQrmSFY9l6SFxAXNmYRCTM3t_WW3LohAmXvOajS2FQ-JrMMdSmrpWtEFlKfbvUA83qxEc9ywuPECxCxbaeWRQsK2llhsx3JdiLxkuNGdWvDpbWUTecxtedFXFhX2kQAobdhWVbxcb8qgsDvKy-WbcVjP8bJ_6-5PEAa3LggHkG3LWet9ZItBmYYX6D0ClvN12r3K0r8W1tu7NPEkB6dRZiJJ0iGg-mvTbMr3Mvprq4vTPt1xIXxWQ62d7XNm6HAV6TemUxaABcNSHtV9g753iGBOU11SwEcfZ6nAmYpKasScdRaJJJoVGgUQ1xsrTqa6Szb1jts9H1UGBTdGk1gzuCxsI_V_DQqsOjvPm-QiNr5Yl_WSZjGRoF8cpmDHW7IlEmsZl4C7X3XFRzCMzlyw8MUGq3JRTgGLdv9pJFP_pqPw2CwHai5HJJsjxJ6KgM760fIy0IiXzSdpmBHurVf25vKtcni-wAgE33wo5nVjZtFTRsSAxnXdmUdJ1YlSXUR2vm8rTVMbBP55sshFWvM_qhI-KMVe4KqDKN0XY6tzUKli1sFBLiQMRCZFXVIGyIkSRW6XIJJJqOIhk2hD7eMyCv2ZDwhMv6q7-UX-mRo"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2492,7 +2493,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_branch_permission) */ string GET_AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuU3BlZWQiOiJyIn19.d9SFeuz9_GPIlFCJNdeHMZMQzDmRMv9AtAl7H4AjTfKC4P_ivqWoH3WSfYvyrSwobjQH5EZwR8QEn0MHjOppVm0IR4nGL0Zy9KWeCTdrt97hn_Dx_dsK0FMgBgBN7KhMQmf1FKD58DDFQpkikryJCiUZ1NUHoJabWbCdOeqg0cPai2wuqd5gXwmegGb8-e4x4QB7Qvx3e2PV7bzzVI3HoDdgiNcpi1aovgqVUxY9glIP66823JzVA0dRNuooMS-pNBp26dPzxna35dWQkJ4Ps5faY1QIVTx_6LYdaN8odyWoEeeVG63RvLZPpIaMJdUUhCkJwaXXz1cZduxi6XjqdLDIK5EJjAWXyimGECLc4DoRzdlIa2Zl3Jr1u4MtZyTdl2QknWmxul9memCSxXIBq9UK0GHbhE-c8xcGY7QwvEY9l5Z5Lfd9nJVnOHGrdf24wpHKXEWCuJV5m3Bb2WzHlTttvAWrPsWMFGPnd5YmA2jio6NTPIvpbRsNcHMVWfoX4jmvdVH6XLsoGhfnFojLNSdzCQ942qO5Gg0o78hBdGCxRwlnIoqP6dMtgxS31lqs9Fct6wBS8ObngbiV7YghAKioVX7ldchJfQhWPD_xESj4YC_fs5wunCoWy-xqGFJltOC-RHS93F_5HSmBnwXaEkQwSeImyw8sVrjqHu1eOos"; - wschannel get_channel; + WsChannel get_channel; get_channel.setConnID(1234); string get_authReq(R"({ "action": "authorize", @@ -2546,7 +2547,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_wildcard_in_permitted_path) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2624,7 +2625,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_wildcard_in_unpermitted_path, * */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciJ9fQ.a7qIqBsDEcBfGRGaXvmk6f8Bg3T7DkBJEnJf9iLLiney6F6XDKk-Q_5pk1vhGEKfkNpVj908JRu2Hqx7e9-Dd6XpkUtr85YztXA7bJYMelsn4ANCipcZp36xLVLYRXqSbafSbQ08BKHa9Omd1j2k7hbldd0-QhU7trdxmnbhHE7QVdE6q1CB8ogt5QRenrax4q0AjaZ49LalDU9tsxWyu2Da2_OO7M-k-LysDAL7JKabApX10sxVGhXiToxL7kYCwUAXfNSXR_wHreWhnm47h-346URjBn-kX5FeC8p2t08IkGKv-ecKEHNhmKMRQ52PLpvuVgQhASCWX8q11VomdEMZHKWJxknd535h9AS4g7aAlbquGMGi1NRWkJc1eBR9DBicA2B1kjME35Tr3Xc954W5zatN3FJCbSQJtSpcHipJUsPzmV231PMfIZJOGSeAreJTtS3F2NqMhJiafem0JkxVgMFkCOv3vHPV2-hJJ-nKwfE7TFFvjfbl3X2GVj3s-tbEelYZZyfdCHaAHmPa4W4sK5WYKUqlSnJN9_l7sLYqzziCxSR8tTeNHBddjPXx6UoZm7D8U_K1HdJcWeDkZfCIkMGYaiBMj8WPVkTym2iQxkc9pgpXs-N1SRL9Zp84FNzXZ1qGfCicceZhkTWlzOm4_SD4te3LVD14RpXaF18"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2704,7 +2705,7 @@ BOOST_AUTO_TEST_CASE(subscription_test) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2792,7 +2793,7 @@ BOOST_AUTO_TEST_CASE(subscription_test_wildcard_permission) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuKiI6InJ3In19.LLdJFIuoViNF4uVv1IL30fXPySoM1oCHxLrWm6xM_4eynvXqPvrCI9fW__GMI0d8PxBLpM8FhgG3ynVVlOuLV_Sl6lDImZlkNQiR02lJwFqf67RWVdI4f4uhdHdKjEpe0a0F-6e7McS__3qQYyjNuQAZIIXkZUIDyXye9upwNARj1wGPtZyNzSY1uyxmuc7MMPaILAIzL8ZnY_D9qgbpbiInGavZtDE_X1iy9GhxbUguP8oiVYn14-H6RBDIF0s5dXwXnJ0cm9Q2DTFpb0YRq4sMgTC4PT1Smdda_6Pj2ELmBjGbH7PYlqfVk1jVdSPGcUpU48e__qVitSRkEK_unM5CihrDVIy7nw3_KclIZJa8_af3kQ4x-leg-ErRCt78j5l0DDxIo5EtCxAeLbO7baZD6D1tPrb3vYkI_9zl1vzydp_nmNMS9QzCRq5yEJfP07gpvG0Z1O0tSLedSCG9cZJ-lJ3Oj3bqxNxk3ih6vKfjnvjAgli7wEP_etHofZmqs3NI-qtP6ldz93fBfAK_iApsSnG4PV7oS_-3UQIow6fUHAA8szn4Ad1ZYiaDsXRcHbdIoLEemGDllkRTYNBe_5vFDT3s1gY82L3fvgwAzTGZ2k46eh66Zx3SmuPgHlCQK6gR-6eAVn0jh_Tjk5vubtin6UdRjHF0Y4BvCwvE0bk"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2881,7 +2882,7 @@ BOOST_AUTO_TEST_CASE(subscription_test_no_permission, *utf::expected_failures(1) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2934,7 +2935,7 @@ BOOST_AUTO_TEST_CASE(subscription_test_invalidpath, *utf::expected_failures(1)) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2972,7 +2973,7 @@ BOOST_AUTO_TEST_CASE(process_sub_with_wildcard) { string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); channel.setAuthorized(true); channel.setAuthToken(AUTH_TOKEN); @@ -3008,7 +3009,7 @@ BOOST_AUTO_TEST_CASE(process_sub_without_wildcard) { string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); channel.setAuthorized(true); channel.setAuthToken(AUTH_TOKEN); @@ -3063,7 +3064,7 @@ BOOST_AUTO_TEST_CASE(subscription_test_invalid_wildcard, *utf::expected_failures string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", diff --git a/w3c-visserver-api/unit-test/w3cunittest.hpp b/w3c-visserver-api/unit-test/w3cunittest.hpp index 6241972..b048576 100644 --- a/w3c-visserver-api/unit-test/w3cunittest.hpp +++ b/w3c-visserver-api/unit-test/w3cunittest.hpp @@ -19,12 +19,12 @@ #include -// #include "vssdatabase.hpp" -// #include "wsserver.hpp" -// #include "subscriptionhandler.hpp" -// #include "authenticator.hpp" -// #include "accesschecker.hpp" -// #include "signing.hpp" +// #include "VssDatabase.hpp" +// #include "WsServer.hpp" +// #include "SubscriptionHandler.hpp" +// #include "Authenticator.hpp" +// #include "AccessChecker.hpp" +// #include "SigningHandler.hpp" // using namespace std; // using namespace jsoncons;