From b13719ee11e8ef5cb14e2310a12e9f790ecc7809 Mon Sep 17 00:00:00 2001 From: Loïc Collignon Date: Thu, 11 Jan 2018 14:41:57 +0100 Subject: removed unused files and useless subdirectories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie0ebe0104589ff99ae24c7410a484eeef18821e7 Signed-off-by: Loïc Collignon --- CMakeLists.txt | 56 +- agl-identity-agent.service.in | 11 - agl-identity-service/CMakeLists.txt | 25 - agl-identity-service/conf.d/cmake/config.cmake | 201 ----- agl-identity-service/conf.d/wgt/config.xml.in | 21 - agl-identity-service/etc/config.json | 23 - .../htdocs/identity/AFB-websock.js | 174 ----- .../htdocs/identity/binding-debug.css | 61 -- .../htdocs/identity/identity-binding.js | 158 ---- agl-identity-service/htdocs/identity/index.html | 41 -- agl-identity-service/src/CMakeLists.txt | 49 -- agl-identity-service/src/agl-forgerock.c | 209 ------ agl-identity-service/src/agl-forgerock.h | 24 - agl-identity-service/src/agl-identity-binding.c | 333 --------- agl-identity-service/src/aia-get.c | 245 ------- agl-identity-service/src/aia-get.h | 28 - agl-identity-service/src/authorization.c | 43 -- agl-identity-service/src/authorization.h | 23 - agl-identity-service/src/base64.c | 108 --- agl-identity-service/src/base64.h | 51 -- agl-identity-service/src/curl-wrap.c | 210 ------ agl-identity-service/src/curl-wrap.h | 45 -- agl-identity-service/src/escape.c | 428 ----------- agl-identity-service/src/escape.h | 23 - agl-identity-service/src/export.map | 1 - agl-identity-service/src/memo.txt | 11 - agl-identity-service/src/oidc-agent.c | 812 --------------------- agl-identity-service/src/oidc-agent.h | 119 --- agl-identity-service/src/test-aia-uds-bluez.c | 65 -- conf.d/cmake/config.cmake | 201 +++++ conf.d/wgt/config.xml.in | 21 + etc/config.json | 23 + htdocs/identity/AFB-websock.js | 174 +++++ htdocs/identity/binding-debug.css | 61 ++ htdocs/identity/identity-binding.js | 158 ++++ htdocs/identity/index.html | 41 ++ src/CMakeLists.txt | 49 ++ src/agl-forgerock.c | 209 ++++++ src/agl-forgerock.h | 24 + src/agl-identity-binding.c | 333 +++++++++ src/aia-get.c | 245 +++++++ src/aia-get.h | 28 + src/authorization.c | 43 ++ src/authorization.h | 23 + src/base64.c | 108 +++ src/base64.h | 51 ++ src/curl-wrap.c | 210 ++++++ src/curl-wrap.h | 45 ++ src/escape.c | 428 +++++++++++ src/escape.h | 23 + src/export.map | 1 + src/memo.txt | 11 + src/oidc-agent.c | 812 +++++++++++++++++++++ src/oidc-agent.h | 119 +++ src/test-aia-uds-bluez.c | 65 ++ uds-ble-id-init-service/CMakeLists.txt | 21 - uds-ble-id-init-service/btle-advise.service | 8 - uds-ble-id-init-service/btle-advise.timer | 6 - uds-ble-id-init-service/conf.d/cmake/config.cmake | 201 ----- uds-ble-id-init-service/conf.d/wgt/config.xml.in | 19 - uds-ble-id-init-service/src/CMakeLists.txt | 35 - uds-ble-id-init-service/src/aia-uds-bluez.c | 607 --------------- uds-ble-id-init-service/src/aia-uds-bluez.h | 46 -- .../src/uds-ble-id-init-service.c | 146 ---- 64 files changed, 3513 insertions(+), 4680 deletions(-) delete mode 100644 agl-identity-agent.service.in delete mode 100644 agl-identity-service/CMakeLists.txt delete mode 100644 agl-identity-service/conf.d/cmake/config.cmake delete mode 100644 agl-identity-service/conf.d/wgt/config.xml.in delete mode 100644 agl-identity-service/etc/config.json delete mode 100644 agl-identity-service/htdocs/identity/AFB-websock.js delete mode 100644 agl-identity-service/htdocs/identity/binding-debug.css delete mode 100644 agl-identity-service/htdocs/identity/identity-binding.js delete mode 100644 agl-identity-service/htdocs/identity/index.html delete mode 100644 agl-identity-service/src/CMakeLists.txt delete mode 100644 agl-identity-service/src/agl-forgerock.c delete mode 100644 agl-identity-service/src/agl-forgerock.h delete mode 100644 agl-identity-service/src/agl-identity-binding.c delete mode 100644 agl-identity-service/src/aia-get.c delete mode 100644 agl-identity-service/src/aia-get.h delete mode 100644 agl-identity-service/src/authorization.c delete mode 100644 agl-identity-service/src/authorization.h delete mode 100644 agl-identity-service/src/base64.c delete mode 100644 agl-identity-service/src/base64.h delete mode 100644 agl-identity-service/src/curl-wrap.c delete mode 100644 agl-identity-service/src/curl-wrap.h delete mode 100644 agl-identity-service/src/escape.c delete mode 100644 agl-identity-service/src/escape.h delete mode 100644 agl-identity-service/src/export.map delete mode 100644 agl-identity-service/src/memo.txt delete mode 100644 agl-identity-service/src/oidc-agent.c delete mode 100644 agl-identity-service/src/oidc-agent.h delete mode 100644 agl-identity-service/src/test-aia-uds-bluez.c create mode 100644 conf.d/cmake/config.cmake create mode 100644 conf.d/wgt/config.xml.in create mode 100644 etc/config.json create mode 100644 htdocs/identity/AFB-websock.js create mode 100644 htdocs/identity/binding-debug.css create mode 100644 htdocs/identity/identity-binding.js create mode 100644 htdocs/identity/index.html create mode 100644 src/CMakeLists.txt create mode 100644 src/agl-forgerock.c create mode 100644 src/agl-forgerock.h create mode 100644 src/agl-identity-binding.c create mode 100644 src/aia-get.c create mode 100644 src/aia-get.h create mode 100644 src/authorization.c create mode 100644 src/authorization.h create mode 100644 src/base64.c create mode 100644 src/base64.h create mode 100644 src/curl-wrap.c create mode 100644 src/curl-wrap.h create mode 100644 src/escape.c create mode 100644 src/escape.h create mode 100644 src/export.map create mode 100644 src/memo.txt create mode 100644 src/oidc-agent.c create mode 100644 src/oidc-agent.h create mode 100644 src/test-aia-uds-bluez.c delete mode 100644 uds-ble-id-init-service/CMakeLists.txt delete mode 100644 uds-ble-id-init-service/btle-advise.service delete mode 100644 uds-ble-id-init-service/btle-advise.timer delete mode 100644 uds-ble-id-init-service/conf.d/cmake/config.cmake delete mode 100644 uds-ble-id-init-service/conf.d/wgt/config.xml.in delete mode 100644 uds-ble-id-init-service/src/CMakeLists.txt delete mode 100644 uds-ble-id-init-service/src/aia-uds-bluez.c delete mode 100644 uds-ble-id-init-service/src/aia-uds-bluez.h delete mode 100644 uds-ble-id-init-service/src/uds-ble-id-init-service.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d9d7bd1..7ef4f7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,7 @@ ########################################################################### -# Copyright 2016 IoT.bzh +# Copyright 2017 IoT.bzh # -# author: José Bollo -# author: Stéphane Desneux +# author: Jose Bollo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,51 +16,10 @@ # limitations under the License. ########################################################################### -cmake_minimum_required(VERSION 3.3) - -project(agl-identity-agent VERSION 0.1) - -include(GNUInstallDirs) -include(FindPkgConfig) - -########################################################################### - -set(CMAKE_BUILD_TYPE Debug CACHE STRING "set debug build by default") - -link_libraries(-Wl,--as-needed -Wl,--gc-sections) - -add_compile_options(-Wall -Wextra -Wconversion) -add_compile_options(-Wno-unused-parameter) # frankly not using a parameter does it care? -add_compile_options(-Wno-unused-but-set-variable) -add_compile_options(-Werror=maybe-uninitialized) -add_compile_options(-Werror=implicit-function-declaration) -add_compile_options(-ffunction-sections -fdata-sections) -add_compile_options(-Wl,--as-needed -Wl,--gc-sections) -add_compile_options(-fPIC) - -set(CMAKE_C_FLAGS_PROFILING "-g -O0 -pg -Wp,-U_FORTIFY_SOURCE") -set(CMAKE_C_FLAGS_DEBUG "-g -O0 -ggdb -Wp,-U_FORTIFY_SOURCE") -set(CMAKE_C_FLAGS_RELEASE "-g -O2") -set(CMAKE_C_FLAGS_CCOV "-g -O2 --coverage") - -########################################################################### - -set(PROJECT_DESTINATION ${CMAKE_INSTALL_FULL_LIBEXECDIR}/${PROJECT_NAME}) - -########################################################################### - -add_subdirectory(binding) - -########################################################################### - -configure_file(agl-identity-agent.service.in agl-identity-agent.service @ONLY) - -INSTALL(FILES - ${CMAKE_CURRENT_BINARY_DIR}/agl-identity-agent.service - ${CMAKE_CURRENT_SOURCE_DIR}/btle-advise.service - ${CMAKE_CURRENT_SOURCE_DIR}/btle-advise.timer - DESTINATION - ${PROJECT_DESTINATION} - ) +cmake_minimum_required(VERSION 3.6) +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/) +include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) +install(DIRECTORY etc DESTINATION ./) +install(DIRECTORY htdocs/identity DESTINATION htdocs) diff --git a/agl-identity-agent.service.in b/agl-identity-agent.service.in deleted file mode 100644 index 5381946..0000000 --- a/agl-identity-agent.service.in +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=AGL identity agent -Requires=btle-advise.service - -[Service] -User=root -ExecStartPre=/usr/sbin/rfkill unblock 0 -ExecStart=/usr/bin/afb-daemon --rootdir=@PROJECT_DESTINATION@ --ldpaths=@PROJECT_DESTINATION@ --port=1212 --token= --verbose --verbose --verbose - -[Install] -WantedBy=default.target diff --git a/agl-identity-service/CMakeLists.txt b/agl-identity-service/CMakeLists.txt deleted file mode 100644 index 7ef4f7a..0000000 --- a/agl-identity-service/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -########################################################################### -# Copyright 2017 IoT.bzh -# -# author: Jose Bollo -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - -cmake_minimum_required(VERSION 3.6) -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/) -include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) - - -install(DIRECTORY etc DESTINATION ./) -install(DIRECTORY htdocs/identity DESTINATION htdocs) diff --git a/agl-identity-service/conf.d/cmake/config.cmake b/agl-identity-service/conf.d/cmake/config.cmake deleted file mode 100644 index c5fdae6..0000000 --- a/agl-identity-service/conf.d/cmake/config.cmake +++ /dev/null @@ -1,201 +0,0 @@ -########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh -# -# author: Fulup Ar Foll -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - -# Project Info -# ------------------ -set(PROJECT_NAME agl-identity-service) -set(PROJECT_VERSION "0.1") -set(PROJECT_PRETTY_NAME "AGL identity service") -set(PROJECT_DESCRIPTION "AGL identity service") -set(PROJECT_URL "https://github.com/iotbzh/aia-binding") -set(PROJECT_ICON "icon.png") -set(PROJECT_AUTHOR "José Bollo") -set(PROJECT_AUTHOR_MAIL "jose.bollo@iot.bzh") -set(PROJECT_LICENSE "APL2.0") -set(PROJECT_LANGUAGES,"C") - -# Where are stored default templates files from submodule or subtree app-templates in your project tree -# relative to the root project directory -set(PROJECT_APP_TEMPLATES_DIR "../conf.d/app-templates") - -# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain -# but used and must be built and linked. -# set(PROJECT_LIBDIR "libs") - -# Where are stored data for your application. Pictures, static resources must be placed in that folder. -# set(PROJECT_RESOURCES "data") - -# Which directories inspect to find CMakeLists.txt target files -# set(PROJECT_SRC_DIR_PATTERN "*") - -# Compilation Mode (DEBUG, RELEASE) -# ---------------------------------- -set(USE_EFENCE 0) - -# Kernel selection if needed. You can choose between a -# mandatory version to impose a minimal version. -# Or check Kernel minimal version and just print a Warning -# about missing features and define a preprocessor variable -# to be used as preprocessor condition in code to disable -# incompatibles features. Preprocessor define is named -# KERNEL_MINIMAL_VERSION_OK. -# -# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and -# Yocto SDK Kernel version. -# ----------------------------------------------- -#set (kernel_mandatory_version 4.8) -#set (kernel_minimal_version 4.8) - -# Compiler selection if needed. Impose a minimal version. -# ----------------------------------------------- -set (gcc_minimal_version 4.9) - -# PKG_CONFIG required packages -# ----------------------------- -set (PKG_REQUIRED_LIST - json-c - afb-daemon -) - -# Prefix path where will be installed the files -# Default: /usr/local (need root permission to write in) -# ------------------------------------------------------ -#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) - -# Customize link option -# ----------------------------- -#list(APPEND link_libraries -an-option) - -# Compilation options definition -# Use CMake generator expressions to specify only for a specific language -# Values are prefilled with default options that is currently used. -# Either separate options with ";", or each options must be quoted separately -# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED ! -# ---------------------------------------------------------------------------- -#set(COMPILE_OPTIONS -# -Wall -# -Wextra -# -Wconversion -# -Wno-unused-parameter -# -Wno-sign-compare -# -Wno-sign-conversion -# -Werror=maybe-uninitialized -# -Werror=implicit-function-declaration -# -ffunction-sections -# -fdata-sections -# -fPIC -# CACHE STRING "Compilation flags") -#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.") -#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.") -#set(PROFILING_COMPILE_OPTIONS -# -g -# -O0 -# -pg -# -Wp,-U_FORTIFY_SOURCE -# CACHE STRING "Compilation flags for PROFILING build type.") -#set(DEBUG_COMPILE_OPTIONS -# -g -# -ggdb -# -Wp,-U_FORTIFY_SOURCE -# CACHE STRING "Compilation flags for DEBUG build type.") -#set(CCOV_COMPILE_OPTIONS -# -g -# -O2 -# --coverage -# CACHE STRING "Compilation flags for CCOV build type.") -#set(RELEASE_COMPILE_OPTIONS -# -g -# -O2 -# CACHE STRING "Compilation flags for RELEASE build type.") - -# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] -# --------------------------------------------------------------------- -set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) -set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) - -# Optional location for config.xml.in -# ----------------------------------- -#set(WIDGET_ICON conf.d/wgt/${PROJECT_ICON} CACHE PATH "Path to the widget icon") -set(WIDGET_CONFIG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)") - -# Mandatory widget Mimetype specification of the main unit -# -------------------------------------------------------------------------- -# Choose between : -#- text/html : HTML application, -# content.src designates the home page of the application -# -#- application/vnd.agl.native : AGL compatible native, -# content.src designates the relative path of the binary. -# -# - application/vnd.agl.service: AGL service, content.src is not used. -# -#- ***application/x-executable***: Native application, -# content.src designates the relative path of the binary. -# For such application, only security setup is made. -# -set(WIDGET_TYPE application/vnd.agl.service) - -# Mandatory Widget entry point file of the main unit -# -------------------------------------------------------------- -# This is the file that will be executed, loaded, -# at launch time by the application framework. -# -set(WIDGET_ENTRY_POINT config.xml) - -# Optional dependencies order -# --------------------------- -#set(EXTRA_DEPENDENCIES_ORDER) - -# Optional Extra global include path -# ----------------------------------- -#set(EXTRA_INCLUDE_DIRS) - -# Optional extra libraries -# ------------------------- -#set(EXTRA_LINK_LIBRARIES) - -# Optional force binding Linking flag -# ------------------------------------ -# set(BINDINGS_LINK_FLAG LinkOptions ) - -# Optional force package prefix generation, like widget -# ----------------------------------------------------- -# set(PKG_PREFIX DestinationPath) - -# Optional Application Framework security token -# and port use for remote debugging. -#------------------------------------------------------------ -set(AFB_TOKEN "" CACHE PATH "Default binder security token") -set(AFB_REMPORT "1234" CACHE PATH "Default binder listening port") - -# Print a helper message when every thing is finished -# ---------------------------------------------------- -set(CLOSING_MESSAGE "Typical binding launch: afb-daemon --port=${AFB_REMPORT} --workdir=${CMAKE_BINARY_DIR}/package --ldpaths=lib --roothttp=htdocs --token=\"${AFB_TOKEN}\" --tracereq=common --verbose") -set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") - -# Optional schema validator about now only XML, LUA and JSON -# are supported -#------------------------------------------------------------ -#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler") -#set(XML_CHECKER "xmllint" CACHE STRING "XML linter") -#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter") - -# This include is mandatory and MUST happens at the end -# of this file, else you expose you to unexpected behavior -# ----------------------------------------------------------- -include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake) diff --git a/agl-identity-service/conf.d/wgt/config.xml.in b/agl-identity-service/conf.d/wgt/config.xml.in deleted file mode 100644 index cd66c2b..0000000 --- a/agl-identity-service/conf.d/wgt/config.xml.in +++ /dev/null @@ -1,21 +0,0 @@ - - - @PROJECT_NAME@ - - - @PROJECT_DESCRIPTION@ - @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> - @PROJECT_LICENSE@ - - - - - - - - - - - - - diff --git a/agl-identity-service/etc/config.json b/agl-identity-service/etc/config.json deleted file mode 100644 index c69e5be..0000000 --- a/agl-identity-service/etc/config.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "endpoint": "https://agl-graphapi.forgerocklabs.org:443/getuserprofilefromtoken", - "vin": "WVGGF7BP7HD005986", - "autoadvise": true, - "delay": 5, - "idp": { - "authorization_endpoint": "", - "token_endpoint": "https://agl-am.forgerocklabs.org:8043/openam/oauth2/stateless/access_token" - }/*, - "appli": { - "authorization": "Basic c3RhdGVsZXNzOnBhc3N3b3JkMg==", - "username": "bjensen", - "password": "Passw0rd", - "scope": "openid profile email cn sn givenName ou mail postalAddress departmentNumber physicalDeliveryOfficeName facsimileTelephoneNumber" - } -*/ -} -/* - "endpoint": "https://frdemo-graphapi.forgerocklabs.org/getuserprofilefromtoken", - "vin": "JTEBU4BF6EK189816", - "delay": 10 -*/ - diff --git a/agl-identity-service/htdocs/identity/AFB-websock.js b/agl-identity-service/htdocs/identity/AFB-websock.js deleted file mode 100644 index 08a7ffe..0000000 --- a/agl-identity-service/htdocs/identity/AFB-websock.js +++ /dev/null @@ -1,174 +0,0 @@ -AFB = function(base, initialtoken){ - -var urlws = "ws://"+window.location.host+"/"+base; -var urlhttp = "http://"+window.location.host+"/"+base; - -/*********************************************/ -/**** ****/ -/**** AFB_context ****/ -/**** ****/ -/*********************************************/ -var AFB_context; -{ - var UUID = undefined; - var TOKEN = initialtoken; - - var context = function(token, uuid) { - this.token = token; - this.uuid = uuid; - } - - context.prototype = { - get token() {return TOKEN;}, - set token(tok) {if(tok) TOKEN=tok;}, - get uuid() {return UUID;}, - set uuid(id) {if(id) UUID=id;} - }; - - AFB_context = new context(); -} -/*********************************************/ -/**** ****/ -/**** AFB_websocket ****/ -/**** ****/ -/*********************************************/ -var AFB_websocket; -{ - var CALL = 2; - var RETOK = 3; - var RETERR = 4; - var EVENT = 5; - - var PROTO1 = "x-afb-ws-json1"; - - AFB_websocket = function(onopen, onabort) { - var u = urlws; - if (AFB_context.token) { - u = u + '?x-afb-token=' + AFB_context.token; - if (AFB_context.uuid) - u = u + '&x-afb-uuid=' + AFB_context.uuid; - } - this.ws = new WebSocket(u, [ PROTO1 ]); - this.pendings = {}; - this.awaitens = {}; - this.counter = 0; - this.ws.onopen = onopen.bind(this); - this.ws.onerror = onerror.bind(this); - this.ws.onclose = onclose.bind(this); - this.ws.onmessage = onmessage.bind(this); - this.onopen = onopen; - this.onabort = onabort; - this.onclose = onabort; - } - - function onerror(event) { - var f = this.onabort; - if (f) { - delete this.onopen; - delete this.onabort; - f && f(this); - } - this.onerror && this.onerror(this); - } - - function onopen(event) { - var f = this.onopen; - delete this.onopen; - delete this.onabort; - f && f(this); - } - - function onclose(event) { - for (var id in this.pendings) { - var ferr = this.pendings[id].onerror; - ferr && ferr(null, this); - } - this.pendings = {}; - this.onclose && this.onclose(); - } - - function fire(awaitens, name, data) { - var a = awaitens[name]; - if (a) - a.forEach(function(handler){handler(data);}); - var i = name.indexOf("/"); - if (i >= 0) { - a = awaitens[name.substring(0,i)]; - if (a) - a.forEach(function(handler){handler(data);}); - } - a = awaitens["*"]; - if (a) - a.forEach(function(handler){handler(data);}); - } - - function reply(pendings, id, ans, offset) { - if (id in pendings) { - var p = pendings[id]; - delete pendings[id]; - var f = p[offset]; - f(ans); - } - } - - function onmessage(event) { - var obj = JSON.parse(event.data); - var code = obj[0]; - var id = obj[1]; - var ans = obj[2]; - AFB_context.token = obj[3]; - switch (code) { - case RETOK: - reply(this.pendings, id, ans, 0); - break; - case RETERR: - reply(this.pendings, id, ans, 1); - break; - case EVENT: - default: - fire(this.awaitens, id, ans); - break; - } - } - - function close() { - this.ws.close(); - this.onabort(); - } - - function call(method, request) { - return new Promise((function(resolve, reject){ - var id, arr; - do { - id = String(this.counter = 4095 & (this.counter + 1)); - } while (id in this.pendings); - this.pendings[id] = [ resolve, reject ]; - arr = [CALL, id, method, request ]; - if (AFB_context.token) arr.push(AFB_context.token); - this.ws.send(JSON.stringify(arr)); - }).bind(this)); - } - - function onevent(name, handler) { - var id = name; - var list = this.awaitens[id] || (this.awaitens[id] = []); - list.push(handler); - } - - AFB_websocket.prototype = { - close: close, - call: call, - onevent: onevent - }; -} -/*********************************************/ -/**** ****/ -/**** ****/ -/**** ****/ -/*********************************************/ -return { - context: AFB_context, - ws: AFB_websocket -}; -}; - diff --git a/agl-identity-service/htdocs/identity/binding-debug.css b/agl-identity-service/htdocs/identity/binding-debug.css deleted file mode 100644 index f41c940..0000000 --- a/agl-identity-service/htdocs/identity/binding-debug.css +++ /dev/null @@ -1,61 +0,0 @@ -#debug-panel { - float: right; -} - -#debug-panel.collapsed { - background-color: transparent; - overflow-x: hidden; -} - -#debug-panel.expanded { - background-color: lightyellow; - max-width: 25%; - overflow-x: scroll; -} - -#debug-panel.collapsed > #debug-panel-collapse { - display: none; -} - -#debug-panel.expanded > #debug-panel-collapse { - display: block; -} - -#debug-panel.collapsed > #debug-panel-expand { - display: block; -} - -#debug-panel.expanded > #debug-panel-expand { - display: none; -} - -#debug-panel.expanded > #debug-panel-content { - display: block; -} - -#debug-panel.collapsed > #debug-panel-content { - display: none; -} - -.json-key { - color: cornflowerblue; - font-weight: bold; -} - -.json-string { - color: crimson; -} - -.json-number { - color: sandybrown; -} - -.json-boolean { - color: fuchsia; - font-weight: bold; -} - -.json-null { - color: black; - font-weight: bold; -} diff --git a/agl-identity-service/htdocs/identity/identity-binding.js b/agl-identity-service/htdocs/identity/identity-binding.js deleted file mode 100644 index 55dbb96..0000000 --- a/agl-identity-service/htdocs/identity/identity-binding.js +++ /dev/null @@ -1,158 +0,0 @@ -var afb = new AFB("api", "HELLO"); -var ws; - -function add_debbug_panel() { - - if (document.getElementById("debug-panel")) - return; - - var itm = document.getElementById("debug-panel-container"); - if (itm) - { - var pnl = - "
\n" + - " \n" + - " \n" + - "
\n" + - "

Debug

\n" + - "

Call

\n" + - "
    \n" + - "
  • api :
  • \n" + - "
  • verb :
  • \n" + - "
  • query :
  • \n" + - "
\n" + - "
\n" +
-			"        
\n" + - "

Response

\n" +
-			"        

Event

\n" +
-			"    
\n" + - "
\n"; - itm.insertAdjacentHTML("afterbegin", pnl); - } -} - -function createClass(name,rules) { - var style = document.createElement('style'); - style.type = 'text/css'; - document.getElementsByTagName('head')[0].appendChild(style); - if(!(style.sheet||{}).insertRule) - (style.styleSheet || style.sheet).addRule(name, rules); - else - style.sheet.insertRule(name+"{"+rules+"}",0); -} - -function syntaxHighlight(json) { - if (typeof json != 'string') - json = JSON.stringify(json, undefined, 2); - - json = json.replace(/&/g, '&').replace(//g, '>'); - return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { - var cls = 'json-number'; - if (/^"/.test(match)) { - if (/:$/.test(match)) { - cls = 'json-key'; - } else { - cls = 'json-string'; - } - } else if (/true|false/.test(match)) { - cls = 'json-boolean'; - } else if (/null/.test(match)) { - cls = 'json-null'; - } - return '' + match + ''; - }); -} - -function set_item_html(id, text) -{ - var itm = document.getElementById(id); - if (itm) itm.innerHTML = text; -} - -function set_item_text(id, text) -{ - var itm = document.getElementById(id); - if (itm) itm.innerText = text; -} - -function debug_panel_collapse() { - var pnl = document.getElementById('debug-panel'); - if (pnl) - { - pnl.classList.remove('expanded'); - pnl.classList.add('collapsed'); - } -} - -function debug_panel_expand() { - var pnl = document.getElementById('debug-panel'); - if (pnl) - { - pnl.classList.remove('collapsed'); - pnl.classList.add('expanded'); - } -} - -function init() { - add_debbug_panel(); - ws = new afb.ws(onopen, onabort); -} - -function onopen() { - //callbinder("ll-auth", "getuser", ""); - ws.onevent("*", gotevent); -} - -function onabort() { -} - -function replyok(obj) { - console.log("replyok:" + JSON.stringify(obj)); - set_item_html("debug-panel-response", syntaxHighlight(JSON.stringify(obj, null, 4))); -} - -function replyerr(obj) { - console.log("replyerr:" + JSON.stringify(obj)); - set_item_html("debug-panel-response", syntaxHighlight(JSON.stringify(obj, null, 4))); -} - -function gotevent(obj) { - console.log("gotevent:" + JSON.stringify(obj)); - set_item_html("debug-panel-event", syntaxHighlight(JSON.stringify(obj, null, 4))); -} - -function callbinder(api, verb, query) { - console.log ("subscribe api="+api+" verb="+verb+" query=" +query); - - set_item_text("debug-panel-call-api", api); - set_item_text("debug-panel-call-verb", verb); - set_item_html("debug-panel-call-query", syntaxHighlight(JSON.stringify(query, null, 4))); - - ws.call(api+"/"+verb, query).then(replyok, replyerr); -} - -function subscribe() { - callbinder("identity", "subscribe", {}); -} - -function getIdentity() { - callbinder("identity", "get", {}); -} - -function logout() { - callbinder("identity", "logout", {}); -} - -function fakeAuth() { - - var e = document.getElementById("fake-auth-kind"); - var arg = { - "kind": e.options[e.selectedIndex].value, - "key": (document.getElementById("fake-auth-key").value === "custom" ? document.getElementById("fake-auth-key-custom").value : document.getElementById("fake-auth-key").value) - } - callbinder("identity", "fake-auth", arg); -} - -function updateNfcUi() { - document.getElementById('fake-auth-key-custom').disabled = !(document.getElementById("fake-auth-key").value === "custom"); -} diff --git a/agl-identity-service/htdocs/identity/index.html b/agl-identity-service/htdocs/identity/index.html deleted file mode 100644 index bbeee51..0000000 --- a/agl-identity-service/htdocs/identity/index.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - agl-service-identity - - - - - - - -
-

agl-service-identity

-

-

    -
  • -
  • - - - - -
  • -
  • -
  • -
-

- - diff --git a/agl-identity-service/src/CMakeLists.txt b/agl-identity-service/src/CMakeLists.txt deleted file mode 100644 index e4a744b..0000000 --- a/agl-identity-service/src/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -########################################################################### -# Copyright 2016, 2017 IoT.bzh -# -# author: José Bollo -# author: Stéphane Desneux -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - -PROJECT_TARGET_ADD(afb-identity-binding) - -add_library(afb-identity-binding MODULE - agl-identity-binding.c - agl-forgerock.c - aia-get.c - authorization.c - base64.c - curl-wrap.c - escape.c - oidc-agent.c -) - -pkg_check_modules(EXTRAS REQUIRED libcurl) - -set_target_properties(afb-identity-binding PROPERTIES - LABELS "BINDING" - PREFIX "" - COMPILE_FLAGS "${EXTRAS_CFLAGS} -DFOR_AFB_BINDING" - LINK_FLAGS "${BINDINGS_LINK_FLAG}" - LINK_LIBRARIES "${EXTRAS_LIBRARIES}" - OUTPUT_NAME "${TARGET_NAME}" -) - -add_custom_command(TARGET ${TARGET_NAME} -PRE_BUILD -COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/htdocs -COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../htdocs ${CMAKE_CURRENT_BINARY_DIR}/../package/ -COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/etc -COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../etc ${CMAKE_CURRENT_BINARY_DIR}/../package/) diff --git a/agl-identity-service/src/agl-forgerock.c b/agl-identity-service/src/agl-forgerock.c deleted file mode 100644 index 97cc4b9..0000000 --- a/agl-identity-service/src/agl-forgerock.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2015, 2016, 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define _GNU_SOURCE - -#include -#include - -#include - -#define AFB_BINDING_VERSION 2 -#include - -#include "oidc-agent.h" -#include "aia-get.h" - -#ifndef NULL -#define NULL 0 -#endif - -static int expiration_delay = 5; - -static const char default_endpoint[] = "https://agl-graphapi.forgerocklabs.org/getuserprofilefromtoken"; -static const char *oidc_name; - -static char *endpoint; - -static void (*onloaded)(struct json_object *data, const char *error); - -/***** configuration ********************************************/ - -static void confsetstr(struct json_object *conf, const char *name, char **value, const char *def) -{ - struct json_object *v; - const char *s; - char *p; - - s = conf && json_object_object_get_ex(conf, name, &v) ? json_object_get_string(v) : def; - p = *value; - if (s && p != s) { - *value = strdup(s); - free(p); - } -} - -static void confsetint(struct json_object *conf, const char *name, int *value, int def) -{ - struct json_object *v; - - *value = conf && json_object_object_get_ex(conf, name, &v) ? json_object_get_int(v) : def; -} - -static void confsetoidc(struct json_object *conf, const char *name) -{ - struct json_object *idp, *appli; - - if (conf - && json_object_object_get_ex(conf, "idp", &idp) - && json_object_object_get_ex(conf, "appli", &appli)) { - if (oidc_idp_set(name, idp) && oidc_appli_set(name, name, appli, 1)) { - oidc_name = name; - } - } -} - -/****************************************************************/ - -static void loaded(struct json_object *data, const char *error) -{ - if (onloaded) - onloaded(data, error); -} - -static void downloaded(void *closure, int status, const void *buffer, size_t size) -{ - struct json_object *object, *subobj; - struct json_object *objkey = closure; - struct json_object *tmp; - - json_object_object_get_ex(objkey, "url", &tmp); - const char *url = json_object_get_string(tmp); - - /* checks whether discarded */ - if (status == 0 && !buffer) { - AFB_ERROR("discarded"); - loaded(NULL, "discarded"); - goto end; /* discarded */ - } - - /* scan for the status */ - if (status == 0 || !buffer) { - AFB_ERROR("uploading %s failed %s", url ? : "?", (const char*)buffer ? : ""); - loaded(NULL, "failed"); - goto end; - } - - /* get the object */ - AFB_DEBUG("received data: %.*s", (int)size, (char*)buffer); - object = json_tokener_parse(buffer); /* okay because 0 appended */ - - /* extract useful part */ - subobj = NULL; - if (object && !json_object_object_get_ex(object, "results", &subobj)) - subobj = NULL; - if (subobj) - subobj = json_object_array_get_idx(subobj, 0); - if (subobj && !json_object_object_get_ex(subobj, "data", &subobj)) - subobj = NULL; - if (subobj) - subobj = json_object_array_get_idx(subobj, 0); - if (subobj && !json_object_object_get_ex(subobj, "row", &subobj)) - subobj = NULL; - if (subobj) - subobj = json_object_array_get_idx(subobj, 0); - - /* is it a recognized user ? */ - if (!subobj) { - /* not recognized!! */ - AFB_INFO("unrecognized key for %s", url ? : "?"); - json_object_put(object); - loaded(NULL, "malformed"); - goto end; - } - - // Save the profile to the database - struct json_object* dbr; - struct json_object* record = json_object_new_object(); - json_object_object_add(record, "key", objkey); - json_object_object_add(record, "value", json_object_get(subobj)); - afb_service_call_sync("persistence", "update", record, &dbr); - - loaded(subobj, NULL); - json_object_put(object); -end: - json_object_put(objkey); -} - -/** public **************************************************************/ - -void agl_forgerock_setconfig(struct json_object *conf) -{ - confsetstr(conf, "endpoint", &endpoint, endpoint ? : default_endpoint); - confsetint(conf, "delay", &expiration_delay, expiration_delay); - confsetoidc(conf, "oidc-aia"); - AFB_NOTICE("Forgerock endpoint is: %s", endpoint); -} - -void agl_forgerock_setcb(void (*callback)(struct json_object *data, const char *error)) -{ - onloaded = callback; -} - -void reply_from_db(void* closure, int status, struct json_object* result) -{ - if (status) - { - AFB_WARNING("Failed to retrieve profile from persistence!"); - return; - } - - struct json_object* tmp; - json_object_object_get_ex(result, "response", &tmp); - json_object_object_get_ex(tmp, "value", &tmp); - AFB_NOTICE("User profile retrieved from persistence: %s", json_object_to_json_string(tmp)); - loaded(json_object_get(tmp), NULL); -} - -void agl_forgerock_download_request(const char *vin, const char *kind, const char *key) -{ - int rc; - char *url; - - rc = asprintf(&url, "%s?vin=%s&kind=%s&keytoken=%s", endpoint, vin, kind, key); - if (rc >= 0) - { - struct json_object* obj = json_object_new_object(); - json_object_object_add(obj, "url", json_object_new_string(url)); - json_object_object_add(obj, "vin", json_object_new_string(vin)); - json_object_object_add(obj, "kind", json_object_new_string(kind)); - json_object_object_add(obj, "key", json_object_new_string(key)); - - // Async get from database and from forgerock - struct json_object* key = json_object_new_object(); - json_object_object_add(key, "key", json_object_get(obj)); - afb_service_call("persistence", "read", key, reply_from_db, NULL); - - // Async get from forgerock - aia_get(url, expiration_delay, oidc_name, oidc_name, downloaded, obj); - free(url); - } - else - AFB_ERROR("out of memory"); -} - -/* vim: set colorcolumn=80: */ - diff --git a/agl-identity-service/src/agl-forgerock.h b/agl-identity-service/src/agl-forgerock.h deleted file mode 100644 index 1aee680..0000000 --- a/agl-identity-service/src/agl-forgerock.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -struct json_object; - -extern void agl_forgerock_setconfig(struct json_object *conf); -extern void agl_forgerock_setcb(void (*callback)(struct json_object *data, const char *error)); -extern void agl_forgerock_download_request(const char *vin, const char *kind, const char *key); diff --git a/agl-identity-service/src/agl-identity-binding.c b/agl-identity-service/src/agl-identity-binding.c deleted file mode 100644 index 12f43bd..0000000 --- a/agl-identity-service/src/agl-identity-binding.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 2015, 2016, 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include - -#include - -#define AFB_BINDING_VERSION 2 -#include - -#include "agl-forgerock.h" - -#ifndef NULL -#define NULL 0 -#endif - -static struct afb_event event; - -static struct json_object *current_identity; - -static const char default_vin[] = "4T1BF1FK5GU260429"; -static char *vin; - -/***** configuration ********************************************/ - -static struct json_object *readjson(int fd) -{ - char *buffer; - struct stat s; - struct json_object *result = NULL; - int rc; - - rc = fstat(fd, &s); - if (rc == 0 && S_ISREG(s.st_mode)) { - buffer = alloca((size_t)(s.st_size)+1); - if (read(fd, buffer, (size_t)s.st_size) == (ssize_t)s.st_size) { - buffer[s.st_size] = 0; - //AFB_NOTICE("Config file: %s", buffer); - result = json_tokener_parse(buffer); - //if (!result) - //{ - // AFB_ERROR("Config file is not a valid JSON: %s", json_tokener_error_desc(json_tokener_get_error(NULL))); - //} - } - } - close(fd); - - return result; -} - -static struct json_object *get_global_config(const char *name, const char *locale) -{ - int fd = afb_daemon_rootdir_open_locale(name, O_RDONLY, locale); - if (fd < 0) AFB_ERROR("Config file not found: %s", name); - return fd < 0 ? NULL : readjson(fd); -} - -static struct json_object *get_local_config(const char *name) -{ - int fd = openat(AT_FDCWD, name, O_RDONLY, 0); - return fd < 0 ? NULL : readjson(fd); -} - -static void confsetstr(struct json_object *conf, const char *name, char **value, const char *def) -{ - struct json_object *v; - const char *s; - char *p; - - s = conf && json_object_object_get_ex(conf, name, &v) ? json_object_get_string(v) : def; - p = *value; - if (s && p != s) { - *value = strdup(s); - free(p); - } -} - -static void setconfig(struct json_object *conf) -{ - if (conf) { - confsetstr(conf, "vin", &vin, vin ? : default_vin); - agl_forgerock_setconfig(conf); - } -} - -static void readconfig() -{ - setconfig(get_global_config("etc/config.json", NULL)); - setconfig(get_local_config("/etc/agl/identity-agent-config.json")); - setconfig(get_local_config("config.json")); -} - -/****************************************************************/ - -static struct json_object *make_event_object(const char *name, const char *id, const char *nick) -{ - struct json_object *object = json_object_new_object(); - - /* TODO: errors */ - json_object_object_add(object, "eventName", json_object_new_string(name)); - json_object_object_add(object, "accountid", json_object_new_string(id)); - if (nick) - json_object_object_add(object, "nickname", json_object_new_string(nick)); - return object; -} - -static int send_event_object(const char *name, const char *id, const char *nick) -{ - return afb_event_push(event, make_event_object(name, id, nick)); -} - -static void do_login(struct json_object *desc) -{ - if (current_identity == NULL && desc == NULL) return; // Switching from NULL to NULL -> do nothing - if (current_identity && desc) - { - const char* a = json_object_to_json_string(current_identity); - const char* b = json_object_to_json_string(desc); - if (strcmp(a, b) == 0) - { - AFB_NOTICE("Reloging of the same user."); - return; // Switching from one user to the same user -> do nothing - } - } - - struct json_object *object; - - /* switching the user */ - AFB_INFO("Switching to user %s", desc ? json_object_to_json_string(desc) : "null"); - object = current_identity; - current_identity = json_object_get(desc); - json_object_put(object); - - if (!json_object_object_get_ex(desc, "name", &object)) - object = 0; - send_event_object("login", !object ? "null" : json_object_get_string(object)? : "?", 0); -} - -static void do_logout() -{ - struct json_object *object; - - AFB_INFO("Switching to no user"); - object = current_identity; - current_identity = 0; - json_object_put(object); - - send_event_object("logout", "null", 0); -} - -static void on_forgerock_data(struct json_object *data, const char *error) -{ - if (error) { - AFB_ERROR("Can't get data: %s", error); - } else { - do_login(data); - } -} - -/****************************************************************/ - -static void subscribe (struct afb_req request) -{ - int rc; - - rc = afb_req_subscribe(request, event); - if (rc < 0) - afb_req_fail(request, "failed", "subscribtion failed"); - else - afb_req_success(request, NULL, NULL); -} - -static void unsubscribe (struct afb_req request) -{ - afb_req_unsubscribe(request, event); - afb_req_success(request, NULL, NULL); -} - -static void logout (struct afb_req request) -{ - do_logout(); - afb_req_success(request, NULL, NULL); -} - -static void fake_login (struct afb_req request) -{ - struct json_object *desc = afb_req_json(request); - do_logout(); - if (desc) - do_login(desc); - afb_req_success(request, NULL, NULL); -} - -static void get (struct afb_req request) -{ - afb_req_success(request, json_object_get(current_identity), NULL); -} - -static void on_nfc_subscribed(void *closure, int status, struct json_object *result) -{ - if(status) - AFB_ERROR("Failed to subscribe to nfc events."); -} - -static void on_nfc_started(void *closure, int status, struct json_object *result) -{ - if (!status) { - afb_service_call("nfc", "subscribe", NULL, on_nfc_subscribed, NULL); - } - else - AFB_ERROR("Failed to start nfc polling."); -} - -static int service_init() -{ - agl_forgerock_setcb(on_forgerock_data); - event = afb_daemon_make_event("event"); - if (!afb_event_is_valid(event)) - return -1; - - readconfig(); - - if (afb_daemon_require_api("nfc", 1)) - return -1; - - if (afb_daemon_require_api("persistence", 1)) - return -1; - - afb_service_call("nfc", "start", NULL, on_nfc_started, NULL); - - return 0; -} - -static void on_nfc_target_add(struct json_object *object) -{ - struct json_object * json_uid; - const char *uid; - - if (json_object_object_get_ex(object, "UID", &json_uid)) - { - uid = json_object_get_string(json_uid); - AFB_NOTICE("nfc tag detected, call forgerock with vincode=%s and key=%s", vin ? vin : default_vin, uid); - send_event_object("incoming", uid, uid); - agl_forgerock_download_request(vin ? vin : default_vin, "nfc", uid); - } - else AFB_ERROR("nfc target add event is received but no UID found: %s", json_object_to_json_string(object)); -} - -static void onevent(const char *event, struct json_object *object) -{ - AFB_NOTICE("Received event: %s", event); - if (!strcmp("nfc/on-nfc-target-add", event)) - { - on_nfc_target_add(object); - return; - } - AFB_WARNING("Unhandled event: %s", event); -} - -static void fake_auth(struct afb_req req) -{ - struct json_object* req_object; - struct json_object* kind_object; - struct json_object* key_object; - - req_object = afb_req_json(req); - - if (!json_object_object_get_ex(req_object, "kind", &kind_object)) - { - afb_req_fail(req, "Missing arg: kind", NULL); - return; - } - - if (!json_object_object_get_ex(req_object, "key", &key_object)) - { - afb_req_fail(req, "Missing arg: key", NULL); - return; - } - - const char* kind = json_object_get_string(kind_object); - const char* key = json_object_get_string(key_object); - - send_event_object("incoming", key, key); - agl_forgerock_download_request(vin ? vin : default_vin, kind, key); - - afb_req_success(req, NULL, "fake auth success!"); -} - -// NOTE: this sample does not use session to keep test a basic as possible -// in real application most APIs should be protected with AFB_SESSION_CHECK -static const struct afb_verb_v2 verbs[]= -{ - {"subscribe" , subscribe , NULL, "subscribe to events" , AFB_SESSION_NONE }, - {"unsubscribe", unsubscribe , NULL, "unsubscribe to events" , AFB_SESSION_NONE }, - {"fake-login" , fake_login , NULL, "fake a login" , AFB_SESSION_NONE }, - {"logout" , logout , NULL, "log the current user out", AFB_SESSION_NONE }, - {"get" , get , NULL, "get data" , AFB_SESSION_NONE }, - {"fake-auth" , fake_auth , NULL, "fake an authentication" , AFB_SESSION_NONE }, - {NULL} -}; - -const struct afb_binding_v2 afbBindingV2 = -{ - .api = "identity", - .specification = NULL, - .info = "AGL identity service", - .verbs = verbs, - .preinit = NULL, - .init = service_init, - .onevent = onevent, - .noconcurrency = 0 -}; - -/* vim: set colorcolumn=80: */ diff --git a/agl-identity-service/src/aia-get.c b/agl-identity-service/src/aia-get.c deleted file mode 100644 index 56c82b0..0000000 --- a/agl-identity-service/src/aia-get.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2015, 2016 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define _GNU_SOURCE - -#include -#include -#include -#include - -#include -#include - -#include "curl-wrap.h" -#include "oidc-agent.h" - -/* -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - - -#include "u2f-bluez.h" -*/ - - -struct keyrequest { - struct keyrequest *next; - int dead; - time_t expiration; - const char *url; - const char *appli; - const char *idp; - void (*callback)(void *closure, int status, const void *buffer, size_t size); - void *closure; - json_object *token; - pthread_t tid; -}; - -static struct keyrequest *keyrequests = 0; - -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - -static int curl_initialized = 0; - -static void perform_query_callback(void *closure, int status, CURL *curl, const char *result, size_t size) -{ - struct keyrequest *kr = closure; - kr->callback(kr->closure, status, result, size); -} - -static void perform_query(struct keyrequest *kr) -{ - CURL *curl; - - curl = curl_wrap_prepare_get_url(kr->url); - if (!curl) - kr->callback(kr->closure, 0, "out of memory", 0); - else { - oidc_add_bearer(curl, kr->token); - curl_wrap_do(curl, perform_query_callback, kr); - } -} - -static void token_success(void *closure, struct json_object *token) -{ - struct keyrequest *kr = closure; - kr->token = token; - perform_query(kr); -} - -static void token_error(void *closure, const char *message, const char *indice) -{ - struct keyrequest *kr = closure; - kr->callback(kr->closure, 0, message, 0); -} - -static void *kr_get_thread(void *closure) -{ - struct oidc_grant_cb cb; - struct keyrequest *kr = closure; - - if (!kr->appli) - perform_query(kr); - else { - cb.closure = kr; - cb.success = token_success; - cb.error = token_error; - oidc_grant_owner_password(kr->appli, kr->idp, NULL, &cb); - } - kr->dead = 1; - return NULL; -} - -static void kr_free(struct keyrequest *kr) -{ - json_object_put(kr->token); - free(kr); -} - -static struct keyrequest *kr_alloc( - const char *url, - time_t expiration, - const char *appli, - const char *idp, - void (*callback)(void *closure, int status, const void *buffer, size_t size), - void *closure -) -{ - struct keyrequest *result; - char *buf; - size_t surl, sappli, sidp; - - surl = 1 + strlen(url); - sappli = appli ? 1 + strlen(appli) : 0; - sidp = idp ? 1 + strlen(idp) : 0; - - result = calloc(1, surl + sappli + sidp + sizeof *result); - if (result) { - result->next = NULL; - result->dead = 0; - result->expiration = expiration; - result->callback = callback; - result->closure = closure; - result->token = NULL; - result->tid = 0; - - buf = (char*)(&result[1]); - result->url = buf; - buf = mempcpy(buf, url, surl); - if (!appli) - result->appli = NULL; - else { - result->appli = buf; - buf = mempcpy(buf, appli, sappli); - } - if (!idp) - result->idp = NULL; - else { - result->idp = buf; - buf = mempcpy(buf, idp, sidp); - } - } - return result; -} - -void aia_get( - const char *url, - int delay, - const char *appli, - const char *idp, - void (*callback)(void *closure, int status, const void *buffer, size_t size), - void *closure -) -{ - int rc; - time_t now; - struct keyrequest **pkr, *kr, *found_kr; - - /* initialize CURL component */ - pthread_mutex_lock(&mutex); - if (!curl_initialized) { - curl_initialized = 1; - curl_global_init(CURL_GLOBAL_DEFAULT); - } - - /* search for the same request and also cleanup deads */ - now = time(NULL); - found_kr = 0; - pkr = &keyrequests; - kr = *pkr; - while (kr) { - if (now > kr->expiration) { - if (kr->dead) { - *pkr = kr->next; - kr_free(kr); - } else { - pkr = &kr->next; - } - } else { - if (!strcmp(url, kr->url)) - found_kr = kr; - pkr = &kr->next; - } - kr = *pkr; - } - - /* check if found and pending */ - if (found_kr) { - /* found -> cancel */ - pthread_mutex_unlock(&mutex); - callback(closure, 0, NULL, 0); - return; - } - - /* allocates the keyrequest */ - kr = kr_alloc(url, now + delay, appli, idp, callback, closure); - if (!kr) { - pthread_mutex_unlock(&mutex); - callback(closure, 0, "Out of memory", 0); - return; - } - - /* link the request */ - kr->next = keyrequests; - keyrequests = kr; - - /* makes the request in a new thread */ - rc = pthread_create(&kr->tid, 0, kr_get_thread, kr); - if (rc != 0) { - /* error, unlink */ - keyrequests = kr->next; - pthread_mutex_unlock(&mutex); - kr_free(kr); - callback(closure, 0, "Can't create a new thread", 0); - return; - } - pthread_mutex_unlock(&mutex); -} - -/* vim: set colorcolumn=80: */ - diff --git a/agl-identity-service/src/aia-get.h b/agl-identity-service/src/aia-get.h deleted file mode 100644 index be1a1f8..0000000 --- a/agl-identity-service/src/aia-get.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -extern void aia_get( - const char *url, - int delay, - const char *appli, - const char *idp, - void (*callback)(void *closure, int status, const void *buffer, size_t size), - void *closure -); - diff --git a/agl-identity-service/src/authorization.c b/agl-identity-service/src/authorization.c deleted file mode 100644 index ae00923..0000000 --- a/agl-identity-service/src/authorization.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE - -#include -#include - -#include "base64.h" - -char *authorization_basic_make(const char *user, const char *password) -{ - const char *array[3] = { user, ":", password }; - return base64_encode_array(array, 3); -} - -char *authorization_basic_make_header(const char *user, const char *password) -{ - char *key, *result; - - key = authorization_basic_make(user, password); - if (!key || asprintf(&result, "Authorization: Basic %s", key) < 0) - result = NULL; - free(key); - return result; -} - -/* vim: set colorcolumn=80: */ - diff --git a/agl-identity-service/src/authorization.h b/agl-identity-service/src/authorization.h deleted file mode 100644 index bd26c1a..0000000 --- a/agl-identity-service/src/authorization.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -extern char *authorization_basic_make(const char *user, const char *password); -extern char *authorization_basic_make_header(const char *user, const char *password); - -/* vim: set colorcolumn=80: */ diff --git a/agl-identity-service/src/base64.c b/agl-identity-service/src/base64.c deleted file mode 100644 index 0841aba..0000000 --- a/agl-identity-service/src/base64.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -const char base64_variant_standard[] = "+/="; -const char base64_variant_url[] = "-_="; -const char base64_variant_trunc[] = "+/"; -const char base64_variant_url_trunc[] = "-_"; - -static char eb64(int x, const char *variant) -{ - if (x < 52) - return (char)(x + (x < 26 ? 'A' : 'a'-26)); - else - return x < 62 ? (char)(x + '0' - 52) : variant[x - 62]; -} - -char *base64_encode_array_variant(const char * const *args, size_t count, const char *variant) -{ - const char *v, *s; - char *buffer, c; - size_t i, j, n; - uint16_t x; - - /* compute size and allocate */ - i = n = 0; - while (n < count) - i += strlen(args[n++]); - buffer = malloc(5 + ((i / 3) << 2)); - if (!buffer) - return NULL; - - /* encode */ - v = variant ? : base64_variant_standard; - n = 0; - j = 0; - x = 0; - while (n < count) { - s = args[n++]; - c = s[i = 0]; - while (c) { - x = (uint16_t)((uint16_t)(x << 8) | (uint16_t)(uint8_t)c); - switch (j & 3) { - case 0: - buffer[j++] = eb64((x >> 2) & 63, v); - break; - case 1: - buffer[j++] = eb64((x >> 4) & 63, v); - break; - case 2: - buffer[j++] = eb64((x >> 6) & 63, v); - buffer[j++] = eb64(x & 63, v); - break; - } - c = s[++i]; - } - } - - /* pad */ - if (v[2]) { - switch (j & 3) { - case 1: - buffer[j++] = eb64((x << 4) & 63, v); - buffer[j++] = v[2]; - buffer[j++] = v[2]; - break; - case 2: - buffer[j++] = eb64((x << 2) & 63, v); - buffer[j++] = v[2]; - break; - } - } - - /* done */ - buffer[j] = 0; - return buffer; -} - -char *base64_encode_multi_variant(const char * const *args, const char *variant) -{ - size_t count; - - for (count = 0 ; args[count] ; count++); - return base64_encode_array_variant(args, count, variant); -} - -char *base64_encode_variant(const char *arg, const char *variant) -{ - return base64_encode_array_variant(&arg, !!arg, variant); -} - diff --git a/agl-identity-service/src/base64.h b/agl-identity-service/src/base64.h deleted file mode 100644 index 5a944df..0000000 --- a/agl-identity-service/src/base64.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -extern const char base64_variant_standard[]; -extern const char base64_variant_trunc[]; - -extern const char base64_variant_url[]; -extern const char base64_variant_url_trunc[]; - -extern char *base64_encode_array_variant(const char * const *args, size_t count, const char *variant); -extern char *base64_encode_multi_variant(const char * const *args, const char *variant); -extern char *base64_encode_variant(const char *arg, const char *variant); - -#define base64_encode_array(args,count) base64_encode_array_variant(args,count,base64_variant_standard) -#define base64_encode_multi(args) base64_encode_multi_variant(args,base64_variant_standard) -#define base64_encode(arg) base64_encode_variant(arg,base64_variant_standard) - -#define base64_encode_array_standard(args,count) base64_encode_array_variant(args,count,base64_variant_standard) -#define base64_encode_multi_standard(args) base64_encode_multi_variant(args,base64_variant_standard) -#define base64_encode_standard(arg) base64_encode_variant(arg,base64_variant_standard) - -#define base64_encode_array_url(args,count) base64_encode_array_variant(args,count,base64_variant_url) -#define base64_encode_multi_url(args) base64_encode_multi_variant(args,base64_variant_url) -#define base64_encode_url(arg) base64_encode_variant(arg,base64_variant_url) - -#define base64_encode_array_trunc(args,count) base64_encode_array_variant(args,count,base64_variant_trunc) -#define base64_encode_multi_trunc(args) base64_encode_multi_variant(args,base64_variant_trunc) -#define base64_encode_trunc(arg) base64_encode_variant(arg,base64_variant_trunc) - -#define base64_encode_array_url_trunc(args,count) base64_encode_array_variant(args,count,base64_variant_url_trunc) -#define base64_encode_multi_url_trunc(args) base64_encode_multi_variant(args,base64_variant_url_trunc) -#define base64_encode_url_trunc(arg) base64_encode_variant(arg,base64_variant_url_trunc) - - - diff --git a/agl-identity-service/src/curl-wrap.c b/agl-identity-service/src/curl-wrap.c deleted file mode 100644 index 3c566a3..0000000 --- a/agl-identity-service/src/curl-wrap.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2015, 2016 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define _GNU_SOURCE - -#include -#include -#include -#include - -#include - -#include "curl-wrap.h" -#include "escape.h" - - -/* internal representation of buffers */ -struct buffer { - size_t size; - char *data; -}; - -/* write callback for filling buffers with the response */ -static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) -{ - struct buffer *buffer = userdata; - size_t sz = size * nmemb; - size_t old_size = buffer->size; - size_t new_size = old_size + sz; - char *data = realloc(buffer->data, new_size + 1); - if (!data) - return 0; - memcpy(&data[old_size], ptr, sz); - data[new_size] = 0; - buffer->size = new_size; - buffer->data = data; - return sz; -} - -/* - * Perform the CURL operation for 'curl' and put the result in - * memory. If 'result' isn't NULL it receives the returned content - * that then must be freed. If 'size' isn't NULL, it receives the - * size of the returned content. Note that if not NULL, the real - * content is one byte greater than the read size and the last byte - * zero. This facility allows to handle the returned content as a - * null terminated C-string. - */ -int curl_wrap_perform(CURL *curl, char **result, size_t *size) -{ - int rc; - struct buffer buffer; - CURLcode code; - - /* init tthe buffer */ - buffer.size = 0; - buffer.data = NULL; - - /* Perform the request, res will get the return code */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); - - /* Perform the request, res will get the return code */ - code = curl_easy_perform(curl); - rc = code == CURLE_OK; - - /* Check for no errors */ - if (rc) { - /* no error */ - if (size) - *size = buffer.size; - if (result) - *result = buffer.data; - else - free(buffer.data); - } else { - /* had error */ - if (size) - *size = 0; - if (result) - *result = NULL; - free(buffer.data); - } - - return rc; -} - -void curl_wrap_do(CURL *curl, void (*callback)(void *closure, int status, CURL *curl, const char *result, size_t size), void *closure) -{ - int rc; - char *result; - size_t size; - char errbuf[CURL_ERROR_SIZE]; - - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); - rc = curl_wrap_perform(curl, &result, &size); - if (rc) - callback(closure, rc, curl, result, size); - else - callback(closure, rc, curl, errbuf, 0); - free(result); - curl_easy_cleanup(curl); -} - -int curl_wrap_content_type_is(CURL *curl, const char *value) -{ - char *actual; - CURLcode code; - - code = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &actual); - if (code != CURLE_OK || !actual) - return 0; - - return !strncasecmp(actual, value, strcspn(actual, "; ")); -} - -CURL *curl_wrap_prepare_get_url(const char *url) -{ - CURL *curl; - CURLcode code; - - curl = curl_easy_init(); - if(curl) { - code = curl_easy_setopt(curl, CURLOPT_URL, url); - if (code == CURLE_OK) - return curl; - curl_easy_cleanup(curl); - } - return NULL; -} - -CURL *curl_wrap_prepare_get(const char *base, const char *path, const char * const *args) -{ - CURL *res; - char *url; - - url = escape_url(base, path, args, NULL); - res = url ? curl_wrap_prepare_get_url(url) : NULL; - free(url); - return res; -} - -int curl_wrap_add_header(CURL *curl, const char *header) -{ - int rc; - struct curl_slist *list; - - list = curl_slist_append(NULL, header); - rc = list ? curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list) == CURLE_OK : 0; -/* - curl_slist_free_all(list); -*/ - return rc; -} - -int curl_wrap_add_header_value(CURL *curl, const char *name, const char *value) -{ - char *h; - int rc; - - rc = asprintf(&h, "%s: %s", name, value); - rc = rc < 0 ? 0 : curl_wrap_add_header(curl, h); - free(h); - return rc; -} - - -CURL *curl_wrap_prepare_post_url_data(const char *url, const char *datatype, const char *data, size_t szdata) -{ - CURL *curl; - - curl = curl_easy_init(); - if (curl - && CURLE_OK == curl_easy_setopt(curl, CURLOPT_URL, url) - && (!szdata || CURLE_OK == curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, szdata)) - && CURLE_OK == curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data) - && (!datatype || curl_wrap_add_header_value(curl, "content-type", datatype))) - return curl; - curl_easy_cleanup(curl); - return NULL; -} - -CURL *curl_wrap_prepare_post(const char *base, const char *path, const char * const *args) -{ - CURL *res; - char *url; - char *data; - size_t szdata; - - url = escape_url(base, path, NULL, NULL); - data = escape_args(args, &szdata); - res = url ? curl_wrap_prepare_post_url_data(url, NULL, data, szdata) : NULL; - free(url); - return res; -} - -/* vim: set colorcolumn=80: */ diff --git a/agl-identity-service/src/curl-wrap.h b/agl-identity-service/src/curl-wrap.h deleted file mode 100644 index 2e44f47..0000000 --- a/agl-identity-service/src/curl-wrap.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2015, 2016 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#pragma once - -#include - -extern char *curl_wrap_url (const char *base, const char *path, - const char *const *query, size_t * size); - -extern int curl_wrap_perform (CURL * curl, char **result, size_t * size); - -extern void curl_wrap_do(CURL *curl, void (*callback)(void *closure, int status, CURL *curl, const char *result, size_t size), void *closure); - -extern int curl_wrap_content_type_is (CURL * curl, const char *value); - -extern CURL *curl_wrap_prepare_get_url(const char *url); - -extern CURL *curl_wrap_prepare_get(const char *base, const char *path, const char * const *args); - -extern CURL *curl_wrap_prepare_post_url_data(const char *url, const char *datatype, const char *data, size_t szdata); - -extern CURL *curl_wrap_prepare_post(const char *base, const char *path, const char * const *args); - -extern int curl_wrap_add_header(CURL *curl, const char *header); - -extern int curl_wrap_add_header_value(CURL *curl, const char *name, const char *value); - -/* vim: set colorcolumn=80: */ - diff --git a/agl-identity-service/src/escape.c b/agl-identity-service/src/escape.c deleted file mode 100644 index 3bb25c2..0000000 --- a/agl-identity-service/src/escape.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright (C) 2015, 2016 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define _GNU_SOURCE - -#include -#include -#include - -/* - * Test if 'c' is to be escaped or not. - * Any character that is not in [-.0-9A-Z_a-z~] - * must be escaped. - * Note that space versus + is not managed here. - */ -static inline int should_escape(char c) -{ -/* ASCII CODES OF UNESCAPED CHARS -car hx/oct idx -'-' 2d/055 1 -'.' 2e/056 2 -'0' 30/060 3 -... .. -'9' 39/071 12 -'A' 41/101 13 -... .. -'Z' 5a/132 38 -'_' 5f/137 39 -'a' 61/141 40 -... .. -'z' 7a/172 65 -'~' 7e/176 66 -*/ - /* [-.0-9A-Z_a-z~] */ - if (c <= 'Z') { - /* [-.0-9A-Z] */ - if (c < '0') { - /* [-.] */ - return c != '-' && c != '.'; - } else { - /* [0-9A-Z] */ - return c < 'A' && c > '9'; - } - } else { - /* [_a-z~] */ - if (c <= 'z') { - /* [_a-z] */ - return c < 'a' && c != '_'; - } else { - /* [~] */ - return c != '~'; - } - } -} - -/* - * returns the ASCII char for the hexadecimal - * digit of the binary value 'f'. - * returns 0 if f isn't in [0 ... 15]. - */ -static inline char bin2hex(int f) -{ - if ((f & 15) != f) - f = 0; - else if (f < 10) - f += '0'; - else - f += 'A' - 10; - return (char)f; -} - -/* - * returns the binary value for the hexadecimal - * digit whose char is 'c'. - * returns -1 if c isn't i n[0-9A-Fa-f] - */ -static inline int hex2bin(char c) -{ - /* [0-9A-Fa-f] */ - if (c <= 'F') { - /* [0-9A-F] */ - if (c <= '9') { - /* [0-9] */ - if (c >= '0') { - return (int)(c - '0'); - } - } else if (c >= 'A') { - /* [A-F] */ - return (int)(c - ('A' - 10)); - } - } else { - /* [a-f] */ - if (c >= 'a' && c <= 'f') { - return (int)(c - ('a' - 10)); - } - } - return -1; -} - -/* - * returns the length that will have the text 'itext' of length - * 'ilen' when escaped. When 'ilen' == 0, strlen is used to - * compute the size of 'itext'. - */ -static size_t escaped_length(const char *itext, size_t ilen) -{ - char c; - size_t i, r; - - if (!ilen) - ilen = strlen(itext); - c = itext[i = r = 0]; - while (i < ilen) { - r += c != ' ' && should_escape(c) ? 3 : 1; - c = itext[++i]; - } - return r; -} - -/* - * Escapes the text 'itext' of length 'ilen'. - * When 'ilen' == 0, strlen is used to compute the size of 'itext'. - * The escaped text is put in 'otext' of length 'olen'. - * Returns the length of the escaped text (it can be greater than 'olen'). - * When 'olen' is greater than the needed length, an extra null terminator - * is appened to the escaped string. - */ -static size_t escape_to(const char *itext, size_t ilen, char *otext, size_t olen) -{ - char c; - size_t i, r; - - if (!ilen) - ilen = strlen(itext); - c = itext[i = r = 0]; - while (i < ilen) { - if (c == ' ') - c = '+'; - else if (should_escape(c)) { - if (r < olen) - otext[r] = '%'; - r++; - if (r < olen) - otext[r] = bin2hex((c >> 4) & 15); - r++; - c = bin2hex(c & 15); - } - if (r < olen) - otext[r] = c; - r++; - c = itext[++i]; - } - if (r < olen) - otext[r] = 0; - return r; -} - -/* - * returns the length of 'itext' of length 'ilen' that can be unescaped. - * compute the size of 'itext' when 'ilen' == 0. - */ -static size_t unescapable_length(const char *itext, size_t ilen) -{ - char c; - size_t i; - - c = itext[i = 0]; - while (i < ilen) { - if (c != '%') - i++; - else { - if (i + 3 > ilen - || hex2bin(itext[i + 1]) < 0 - || hex2bin(itext[i + 2]) < 0) - break; - i += 3; - } - c = itext[i]; - } - return i; -} - -/* - * returns the length that will have the text 'itext' of length - * 'ilen' when escaped. When 'ilen' == 0, strlen is used to - * compute the size of 'itext'. - */ -static size_t unescaped_length(const char *itext, size_t ilen) -{ - char c; - size_t i, r; - - c = itext[i = r = 0]; - while (i < ilen) { - i += (size_t)(1 + ((c == '%') << 1)); - r++; - c = itext[i]; - } - return r; -} - -static size_t unescape_to(const char *itext, size_t ilen, char *otext, size_t olen) -{ - char c; - size_t i, r; - int h, l; - - ilen = unescapable_length(itext, ilen); - c = itext[i = r = 0]; - while (i < ilen) { - if (c != '%') { - if (c == '+') - c = ' '; - i++; - } else { - if (i + 2 >= ilen) - break; - h = hex2bin(itext[i + 1]); - l = hex2bin(itext[i + 2]); - c = (char)((h << 4) | l); - i += 3; - } - if (r < olen) - otext[r] = c; - r++; - c = itext[i]; - } - if (r < olen) - otext[r] = 0; - return r; -} - -/* create an url */ -char *escape_url(const char *base, const char *path, const char * const *args, size_t *length) -{ - int i; - size_t lb, lp, lq, l, L; - const char *null; - char *result; - - /* ensure args */ - if (!args) { - null = NULL; - args = &null; - } - - /* compute lengths */ - lb = base ? strlen(base) : 0; - lp = path ? strlen(path) : 0; - lq = 0; - i = 0; - while (args[i]) { - lq += 1 + escaped_length(args[i], strlen(args[i])); - i++; - if (args[i]) - lq += 1 + escaped_length(args[i], strlen(args[i])); - i++; - } - - /* allocation */ - L = lb + lp + lq + 1; - result = malloc(L + 1); - if (result) { - /* make the resulting url */ - l = lb; - if (lb) { - memcpy(result, base, lb); - if (result[l - 1] != '/' && path && path[0] != '/') - result[l++] = '/'; - } - if (lp) { - memcpy(result + l, path, lp); - l += lp; - } - i = 0; - while (args[i]) { - if (i) { - result[l++] = '&'; - } else if (base || path) { - result[l] = memchr(result, '?', l) ? '&' : '?'; - l++; - } - l += escape_to(args[i], strlen(args[i]), result + l, L - l); - i++; - if (args[i]) { - result[l++] = '='; - l += escape_to(args[i], strlen(args[i]), result + l, L - l); - } - i++; - } - result[l] = 0; - if (length) - *length = l; - } - return result; -} - -char *escape_args(const char * const *args, size_t *length) -{ - return escape_url(NULL, NULL, args, length); -} - -const char **unescape_args(const char *args) -{ - const char **r, **q; - char c, *p; - size_t j, z, l, n, lt; - - lt = n = 0; - if (args[0]) { - z = 0; - do { - l = strcspn(&args[z], "&="); - j = 1 + unescaped_length(&args[z], l); - lt += j; - z += l; - c = args[z++]; - if (c == '=') { - l = strcspn(&args[z], "&"); - j = 1 + unescaped_length(&args[z], l); - lt += j; - z += l; - c = args[z++]; - } - n++; - } while(c); - } - - l = lt + (2 * n + 1) * sizeof(char *); - r = malloc(l); - if (!r) - return r; - - q = r; - p = (void*)&r[2 * n + 1]; - if (args[0]) { - z = 0; - do { - q[0] = p; - l = strcspn(&args[z], "&="); - j = 1 + unescape_to(&args[z], l, p, lt); - lt -= j; - p += j; - z += l; - c = args[z++]; - if (c != '=') - q[1] = NULL; - else { - q[1] = p; - l = strcspn(&args[z], "&"); - j = 1 + unescape_to(&args[z], l, p, lt); - lt -= j; - p += j; - z += l; - c = args[z++]; - } - q = &q[2]; - } while(c); - } - q[0] = NULL; - return r; -} - -char *escape(const char *text, size_t textlen, size_t *reslength) -{ - size_t len; - char *result; - - len = 1 + escaped_length(text, textlen); - result = malloc(len); - if (result) - escape_to(text, textlen, result, len); - if (reslength) - *reslength = len - 1; - return result; -} - -char *unescape(const char *text, size_t textlen, size_t *reslength) -{ - size_t len; - char *result; - - len = 1 + unescaped_length(text, textlen); - result = malloc(len); - if (result) - unescape_to(text, textlen, result, len); - if (reslength) - *reslength = len - 1; - return result; -} - -#if 1 -#include -int main(int ac, char **av) -{ - int i; - char *x = escape_args((void*)++av, NULL); - char *y = escape(x, strlen(x), NULL); - char *z = unescape(y, strlen(y), NULL); - const char **v = unescape_args(x); - - printf("%s\n%s\n%s\n", x, y, z); - free(x); - free(y); - free(z); - i = 0; - while(v[i]) { - printf("%s=%s / %s=%s\n", av[i], av[i+1], v[i], v[i+1]); - i += 2; - } - free(v); - return 0; -} -#endif -/* vim: set colorcolumn=80: */ diff --git a/agl-identity-service/src/escape.h b/agl-identity-service/src/escape.h deleted file mode 100644 index 7d548db..0000000 --- a/agl-identity-service/src/escape.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -extern char *escape_url(const char *base, const char *path, const char * const *args, size_t *length); -extern char *escape_args(const char * const *args, size_t *length); -extern const char **unescape_args(const char *args); - -/* vim: set colorcolumn=80: */ diff --git a/agl-identity-service/src/export.map b/agl-identity-service/src/export.map deleted file mode 100644 index ee2f413..0000000 --- a/agl-identity-service/src/export.map +++ /dev/null @@ -1 +0,0 @@ -{ global: afbBindingV*; local: *; }; diff --git a/agl-identity-service/src/memo.txt b/agl-identity-service/src/memo.txt deleted file mode 100644 index 3a6ed50..0000000 --- a/agl-identity-service/src/memo.txt +++ /dev/null @@ -1,11 +0,0 @@ - -gcc --shared -fPIC -o aia.so [^t]*.c -lcurl -lsystemd -ljson-c - -afb-daemon --rootdir=. --ldpaths=. --port=1212 --token= --verbose --verbose --verbose - - -afb-client-demo localhost:1212/api?token=t -agl-identity-agent subscribe - - -gcc -o taia *aia-uds-bluez.c -lsystemd diff --git a/agl-identity-service/src/oidc-agent.c b/agl-identity-service/src/oidc-agent.c deleted file mode 100644 index 1f09e7a..0000000 --- a/agl-identity-service/src/oidc-agent.c +++ /dev/null @@ -1,812 +0,0 @@ -/* - * Copyright (C) 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include - -#include - -#include "oidc-agent.h" -#include "escape.h" -#include "curl-wrap.h" - -/***************** utilities *************************/ - -static const char string_empty[] = ""; -static const char string_authorization_endpoint[] = "authorization_endpoint"; -static const char string_token_endpoint[] = "token_endpoint"; -#if 0 -static const char string_issuer[] = "issuer"; -static const char string_userinfo_endpoint[] = "userinfo_endpoint"; -static const char string_revocation_endpoint[] = "revocation_endpoint"; -static const char string_jwks_uri[] = "jwks_uri"; -#endif - -#define MAX_IDP_COUNT 20 -#define MAX_APPLI_COUNT 100 - -static struct json_object *idps; -static struct json_object *applis; - -static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; - -/***************** utilities *************************/ - -/* - * Get the object of 'name' in the 'container' and return it. - * Creates the result if needed and add it to the container. - * When 'maxcount' isn't zero the final count will not exceed 'maxcount'. - */ -static struct json_object *j_container_item(struct json_object *container, const char *name, int maxcount) -{ - struct json_object *result; - - /* ensure object of 'name' exists */ - if (!json_object_object_get_ex(container, name, &result)) { - if (maxcount && json_object_object_length(container) >= maxcount) - return NULL; - result = json_object_new_object(); - if (!result) - return NULL; - json_object_object_add (container, name, result); - } - return result; -} - -/* - * Like 'j_container_item' but also creates the 'container' if needed. - */ -static struct json_object *j_container_item_make(struct json_object **container, const char *name, int maxcount) -{ - struct json_object *cont; - - /* ensure container exists */ - cont = *container; - if (!cont) { - cont = json_object_new_object(); - if (!cont) - return NULL; - *container = cont; - } - return j_container_item(cont, name, maxcount); -} - -/* - * Adds in 'dest' the fields of 'src' - * Also when the value of a field of 'src' is null, delete the field of 'dst' - */ -static void j_merge(struct json_object *dest, struct json_object *src) -{ - struct json_object_iter i; - json_object_object_foreachC(src, i) { - if (json_object_is_type(i.val, json_type_null)) - json_object_object_del(dest, i.key); - else - json_object_object_add(dest, i.key, json_object_get(i.val)); - } -} - -/***************** IDP **************************/ - -/* - * Set the values of 'desc' for the idp of 'name'. - * Return 0 on error or 1 on success. - */ -int oidc_idp_set(const char *name, struct json_object *desc) -{ - struct json_object *idp; - int result = 0; - - pthread_rwlock_wrlock(&rwlock); - idp = j_container_item_make(&idps, name, MAX_IDP_COUNT); - if (idp) { - j_merge(idp, desc); - result = 1; - } - pthread_rwlock_unlock(&rwlock); - return result; -} - -/* - * Return 1 if idp of 'name' exists or 0 otherwise. - */ -int oidc_idp_exists(const char *name) -{ - int result; - - pthread_rwlock_rdlock(&rwlock); - result = json_object_object_get_ex(idps, name, NULL); - pthread_rwlock_unlock(&rwlock); - - return result; -} - -/* - * Deletes the idp of 'name'. - */ -void oidc_idp_delete(const char *name) -{ - pthread_rwlock_wrlock(&rwlock); - json_object_object_del(idps, name); - pthread_rwlock_unlock(&rwlock); -} - -/***************** APPLI **************************/ - -/* - * Returns the name of the idp of the 'appli'. - * Returns NULL when appli isn't set or default idp isn't set. - */ -static const char *get_default_idp(const char *appli) -{ - struct json_object *a, *i; - - if (!json_object_object_get_ex(applis, appli, &a)) - return NULL; - if (!json_object_object_get_ex(a, string_empty, &i)) - return NULL; - return json_object_get_string(i); -} - -/* - * Returns the application data related to the 'appli' for the 'idp'. - * If 'ja' isn't null, returns in it the object for the application 'appli'. - * Returns NULL in case of error. - */ -static struct json_object *get_appli_idp(const char *appli, const char *idp, struct json_object **ja) -{ - struct json_object *a, *i; - - if (!json_object_object_get_ex(applis, appli, &a) || !json_object_object_get_ex(a, idp, &i)) - return NULL; - if (ja) - *ja = a; - return i; -} - -/* - * Set the description 'desc' for the application of 'name' and - * the 'idp'. When 'make_default' is set it, make it the default idp - * for the application. - * Return 0 on error or 1 on success. - */ -int oidc_appli_set(const char *name, const char *idp, struct json_object *desc, int make_default) -{ - struct json_object *a, *ai; - int result = 0; - - pthread_rwlock_wrlock(&rwlock); - a = j_container_item_make(&applis, name, MAX_APPLI_COUNT); - if (a) { - ai = j_container_item(a, idp, 0); - if (ai) { - j_merge(ai, desc); - if (make_default || !json_object_object_get_ex(a, string_empty, NULL)) - json_object_object_add(a, string_empty, json_object_new_string(idp)); - result = 1; - } - } - pthread_rwlock_unlock(&rwlock); - return result; -} - -/* - * Is the appli of 'name' defined? - * Return 1 if answer is yes or 0 for no. - */ -int oidc_appli_exists(const char *name) -{ - int result; - - pthread_rwlock_rdlock(&rwlock); - result = json_object_object_get_ex(applis, name, NULL); - pthread_rwlock_unlock(&rwlock); - - return result; -} - -/* - * Does the appli of 'name' has the 'idp' defined? - * Return 1 if answer is yes or 0 for no. - */ -int oidc_appli_has_idp(const char *name, const char *idp) -{ - int result; - - pthread_rwlock_rdlock(&rwlock); - result = !!get_appli_idp(name, idp, NULL); - pthread_rwlock_unlock(&rwlock); - - return result; -} - -/* - * Set 'idp' as default for the application of 'name'. - * Returns 0 on error (appli or idp for appli not existing) - * or 1 in case of success. - */ -int oidc_appli_set_default_idp(const char *name, const char *idp) -{ - struct json_object *a, *i; - - pthread_rwlock_wrlock(&rwlock); - i = get_appli_idp(name, idp, &a); - if (i) - json_object_object_add(a, string_empty, json_object_new_string(idp)); - pthread_rwlock_unlock(&rwlock); - - return !!i; -} - -/* - * Deletes the application of 'name' - */ -void oidc_appli_delete(const char *name) -{ - pthread_rwlock_wrlock(&rwlock); - json_object_object_del(applis, name); - pthread_rwlock_unlock(&rwlock); -} - -/***************** AUTHORISATION **************************/ - -/* parameters */ -enum param -{ - Param_Access_Token, - Param_Acr_Values, - Param_Authorization, - Param_Client_Id, - Param_Client_Secret, - Param_Code, - Param_Display, - Param_Error, - Param_Error_Description, - Param_Error_Uri, - Param_Expires_In, - Param_Grant_Type, - Param_Id_Token, - Param_Id_Token_Hint, - Param_Login_Hint, - Param_Max_Age, - Param_Nonce, - Param_Password, - Param_Prompt, - Param_Redirect_Uri, - Param_Refresh_Token, - Param_Response_Type, - Param_Scope, - Param_State, - Param_Token_Type, - Param_Ui_Locales, - Param_Username, - PARAM_COUNT -}; - -#if PARAM_COUNT > 30 -# error "Too much parameters" -#endif -#define PARAM(p) ((uint32_t)((uint32_t)1 << (Param_##p))) - -/* args of authorization requests */ -struct args -{ - struct json_object *appli; - struct json_object *idp; - struct json_object *args; - struct oidc_grant_cb cb; - int locked; - int refresh; - uint32_t mandatory; - uint32_t all; - struct json_object *header; - struct json_object *query; -}; - -/* Release the lock if needed */ -static void args_unlock(struct args *args) -{ - if (!args->locked) { - pthread_rwlock_unlock(&rwlock); - args->locked = 0; - } -} - -/* Release the memory needed by args */ -static void args_destroy(struct args *args) -{ - json_object_put(args->appli); - json_object_put(args->idp); - json_object_put(args->header); - json_object_put(args->query); - free(args); -} - -/* Send the success event with the gained tokens */ -static void args_send_success(struct args *args, struct json_object *result) -{ - args_unlock(args); - args->cb.success(args->cb.closure, result); - args_destroy(args); -} - -/* Sends the error event with the indice to the client of args */ -static void args_send_error(struct args *args, const char *message, const char *indice) -{ - args_unlock(args); - args->cb.error(args->cb.closure, message, indice); - args_destroy(args); -} - -/* Send the error and also return NULL */ -static inline struct args *args_send_error_null(struct args *args, const char *message, const char *indice) -{ - args_send_error(args, message, indice); - return NULL; -} - -/* creates a struct args from the arguments, returns NULL on error */ -struct args *mkargs(const char *appli, const char *idp, struct json_object *args, const struct oidc_grant_cb *cb) -{ - struct args *result; - struct json_object *obj; - - /* allocates the args */ - result = calloc(1, sizeof *result); - if (!result) { - cb->error(cb->closure, "Out of memory", NULL); - return NULL; - } - - /* init of the structure */ - result->cb = *cb; - result->args = args; - result->header = json_object_new_object(); - result->query = json_object_new_object(); - - /* lock in read */ - pthread_rwlock_rdlock(&rwlock); - result->locked = 1; - - /* check previous allocations */ - if (!result->query || !result->header) { - return args_send_error_null(result, "Out of memory", NULL); - } - - /* check whether default idp */ - if (!idp) { - idp = get_default_idp(appli); - if (!idp) - return args_send_error_null(result, "No default IDP", NULL); - } - - /* get the IDP */ - if (!json_object_object_get_ex(idps, idp, &obj)) - return args_send_error_null(result, "Unknown IDP", idp); - result->idp = json_object_get(obj); - - /* get the appli */ - obj = get_appli_idp(appli, idp, NULL); - if (!obj) - return args_send_error_null(result, "Unknown APPLI for IDP", appli); - result->appli = json_object_get(obj); - - return result; -} - -/* get a value for a struct args */ -static struct json_object *args_object(struct args *args, const char *name) -{ - struct json_object *result; - - if (!json_object_object_get_ex(args->appli, name, &result) - && !json_object_object_get_ex(args->idp, name, &result) - && !json_object_object_get_ex(args->args, name, &result)) - result = NULL; - return result; -} - -/* get a string value for a struct args */ -static const char *args_string(struct args *args, const char *name) -{ - struct json_object *object = args_object(args, name); - return object ? json_object_get_string(object) : NULL; -} - -/* add a data */ -static int args_add(struct args *args, uint32_t val, const char *name, int query) -{ - struct json_object *obj, *dest; - - if (val & args->all) { - obj = args_object(args, name); - if (obj) { - dest = query ? args->query : args->header; - json_object_object_add(dest, name, json_object_get(obj)); - } - else if (val & args->mandatory) { - args_send_error(args, "Mandatory field missing", name); - return 0; - } - } - - return 1; -} - -/* - * Makes the CURL object for the given 'url' for either GET or POST depending - * on 'post' with the added 'header' fields and the given query parameters. - * Returns NULL on error. - * Ex: - * - * curl_json("http://iot.bzh/api", 0, {"X-Index": "no"}, {"fast":true,"item":"2345-hellfest"}) - * - * produces the query: - * - * GET /api?fast=true&item=2345-hellfest HTTP/1.1 - * Host: iot.bzh - * X-Index: no - * - * while the same but with post not null produces: - * - * POST /api HTTP/1.1 - * Host: iot.bzh - * X-Index: no - * Content-Type: application/x-www-form-urlencoded - * - * fast=true&item=2345-hellfest - * - */ -static CURL *curl_json(const char *url, int post, struct json_object *header, struct json_object *query) -{ - const char **args, *str; - struct json_object_iter i; - int idx; - CURL *result; - - /* create args array */ - idx = 1 + (json_object_object_length(query) << 1); - args = malloc((unsigned)idx * sizeof *args); - if (!args) - return NULL; - - /* fill the args array */ - args[--idx] = NULL; - json_object_object_foreachC(query, i) { - str = json_object_get_string(i.val); - args[--idx] = str; - args[--idx] = i.key; - } - - /* prepare the query */ - if (post) - result = curl_wrap_prepare_post(url, NULL, args); - else - result = curl_wrap_prepare_get(url, NULL, args); - free(args); - if(!result) - return NULL; - - /* add headers */ - if (header) { - json_object_object_foreachC(header, i) { - str = json_object_get_string(i.val); - if (!curl_wrap_add_header_value(result, i.key, str)) { - curl_easy_cleanup(result); - return NULL; - } - } - } - return result; -} - -/* - * Extract from the answer of 'curl' whose 'content' has 'size' bytes the - * embeded JSON object (if any). - * Returns it or returns NULL if the answer can't be interpreted. - */ -static struct json_object *decode_perform_result(CURL *curl, const char *content, size_t size) -{ - int i; - const char **args; - struct json_object *result; - - /* is it an url encoded answer? */ - if (curl_wrap_content_type_is(curl, "application/x-www-form-urlencoded")) { - /* yes, unescape as an array of strings */ - args = unescape_args(content); - if (!args) - result = NULL; - else { - /* wrap the key=value pairs in an object */ - result = json_object_new_object(); - if (result) { - for (i = 0 ; args[i] ; i += 2) - json_object_object_add(result, args[i], - json_object_new_string(args[i+1])); - } - free(args); - } - } else if (curl_wrap_content_type_is(curl, "application/json")) { - /* interpret the json */ - result = json_tokener_parse (content); - } else { - /* by default, still try to interpret the answer as if json */ - result = json_tokener_parse (content); - } - return result; -} - -/* - * Treats the result of the query 'curl' of 'content' of 'size' bytes for the 'args' - */ -static void perform_result(struct args *args, CURL *curl, const char *content, size_t size) -{ - struct json_object *obj, *at, *tt; - char *txt; - - /* get answer */ - obj = decode_perform_result(curl, content, size); - if (!obj) - return args_send_error(args, "unable to extract answer", content); - - /* process the answer */ - if (json_object_object_get_ex(obj, "access_token", &at) && json_object_object_get_ex(obj, "token_type", &tt)) { - if (!strcmp(json_object_get_string(tt), "bearer")) { - if (asprintf(&txt, "Bearer %s", json_object_get_string(at)) > 0) { - json_object_object_add(obj, "authorization", json_object_new_string(txt)); - free(txt); - } - } - } - - /* merge the answer to the token args in case of refresh */ - if (args->refresh) - j_merge(args->args, obj); - - /* send the answer */ - args_send_success(args, obj); -} - -/* - * Treats the redirect answer of the query 'curl' of 'content' of 'size' bytes for the 'args' - */ -static void perform_redirect(struct args *args, CURL *curl, const char *content, size_t size) -{ - /* TODO: handle redirection for the normal flow */ - return args_send_error(args, "unhandled redirection", content); -} - -/* - * Handle the result of the query 'curl' of 'status'. If a data is returned, it is available in - * 'content' of 'size' bytes. - * When 'status' is 0, an error occured. Otherwise, 'statu' isn't zero. - */ -static void perform_callback(void *closure, int status, CURL *curl, const char *content, size_t size) -{ - long code; - struct args *args = closure; - - /* query error ? */ - if (!status - || curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code) != CURLE_OK) - return args_send_error(args, "query error", NULL); /* TODO: IMPROVE? */ - - /* get the returned code */ - switch (code) { - case 200: - return perform_result(args, curl, content, size); - case 302: - return perform_redirect(args, curl, content, size); - case 400: - case 401: - return args_send_error(args, content ? : "returned code error", content ? "returned code error" : content); - default: - return args_send_error(args, content ? : "unexpected code error", content ? "unexpected code error" : content); - } -} - -/* - * Main function for performing OAuth2/OpenId Connect transactions. - * The structure 'args' is filled with the needed values: - * Application data as json object, IDP data as json object - * Contextual arguments for the transaction. - * 'endpoint' must be the name of an endpoint in the context of 'args'. - * 'operation' is the value that will get either response_type or grant_type, - * depending on the nature of the required parameters 'mandatory'. - * 'mandatory' designates the mandatory parameters. - * 'optional' designate the optional parameters. - */ -static void perform(struct args *args, const char *endpoint, const char *operation, uint32_t mandatory, uint32_t optional) -{ - int post; - const char *url, *type; - CURL *curl; - - /* set the flags */ - args->mandatory = mandatory; - args->all = mandatory | optional; - - /* get the endpoint */ - url = args_string(args, endpoint); - if (!url) - return args_send_error(args, "No endpoint", endpoint); - - /* get the operation type */ - if ((mandatory & PARAM(Response_Type)) == PARAM(Response_Type)) { - type = "response_type"; - post = 0; /* can be 1 sometimes so not risk here */ - } else if ((mandatory & PARAM(Grant_Type)) == PARAM(Grant_Type)) { - type = "grant_type"; - post = 1; /* must be 1 */ - } else - return args_send_error(args, "Unexpected operation Type", NULL); - - json_object_object_add(args->query, type, json_object_new_string(operation)); - - /* get the arguments */ - if (1 - && args_add(args, PARAM(Access_Token), "access_token", 1) - && args_add(args, PARAM(Acr_Values), "acr_values", 1) - && args_add(args, PARAM(Authorization), "authorization", 0) - && args_add(args, PARAM(Client_Id), "client_id", 1) - && args_add(args, PARAM(Client_Secret), "client_secret", 1) - && args_add(args, PARAM(Code), "code", 1) - && args_add(args, PARAM(Display), "display", 1) - && args_add(args, PARAM(Expires_In), "expires_in", 1) - && args_add(args, PARAM(Id_Token_Hint), "id_token_hint", 1) - && args_add(args, PARAM(Login_Hint), "login_hint", 1) - && args_add(args, PARAM(Max_Age), "max_age", 1) - && args_add(args, PARAM(Nonce), "nonce", 1) - && args_add(args, PARAM(Password), "password", 1) - && args_add(args, PARAM(Prompt), "prompt", 1) - && args_add(args, PARAM(Redirect_Uri), "redirect_uri", 1) - && args_add(args, PARAM(Refresh_Token), "refresh_token", 1) - && args_add(args, PARAM(Scope), "scope", 1) - && args_add(args, PARAM(State), "state", 1) - && args_add(args, PARAM(Token_Type), "token_type", 1) - && args_add(args, PARAM(Ui_Locales), "ui_locales", 1) - && args_add(args, PARAM(Username), "username", 1) - ) { - /* creates the curl query */ - curl = curl_json(url, post, args->header, args->query); - if (!curl) - return args_send_error(args, "out of memory", NULL); - - /* release data */ - args_unlock(args); - - /* perform the request to the server */ - curl_wrap_do(curl, perform_callback, args); - } -} - -/* perform a grant of flow Flow_Resource_Owner_Password_Credentials_Grant */ -static void grant_owner_password(struct args *args) -{ - perform(args, string_token_endpoint, "password", - PARAM(Grant_Type) | PARAM(Username) | PARAM(Password), - PARAM(Scope) | PARAM(Authorization) - ); -} - -/* perform a grant of flow Flow_Client_Credentials_Grant */ -static void grant_client_credentials(struct args *args) -{ - perform(args, string_token_endpoint, "client_credentials", - PARAM(Grant_Type), - PARAM(Scope) | PARAM(Authorization) - ); -} - -/* switches the requests depending on 'flow' */ -static void grant(struct args *args, enum oidc_grant_flow flow) -{ - /* ensure args is valid */ - if (!args) - return; - - /* process for flow */ - switch(flow) { - - case Flow_Resource_Owner_Password_Credentials_Grant: - grant_owner_password(args); - break; - - case Flow_Client_Credentials_Grant: - grant_client_credentials(args); - break; - - case Flow_Authorization_Code_Grant: - case Flow_Implicit_Grant: - case Flow_Extension_Grant: - args_send_error(args, "Unsupported flow", NULL); - break; - - case Flow_Invalid: - default: - args_send_error(args, "Invalid flow", NULL); - break; - } -} - -/* - * Initiates a grant with the given 'flow'. - * 'appli' and 'idp' designates the appli and the idp that have been recorded. - * when idp == NULL or idp == "", the default idp of 'appli' is used. - * 'args' contains parameters expected in plus for the grant transaction. - * 'cb' describes the callback actions that are called before - * the function returns. - */ -void oidc_grant(const char *appli, const char *idp, struct json_object *args, const struct oidc_grant_cb *cb, enum oidc_grant_flow flow) -{ - grant(mkargs(appli, idp, args, cb), flow); -} - -/* - * Like oidc_grant for flow Flow_Resource_Owner_Password_Credentials_Grant - */ -void oidc_grant_owner_password(const char *appli, const char *idp, struct json_object *args, const struct oidc_grant_cb *cb) -{ - grant(mkargs(appli, idp, args, cb), Flow_Resource_Owner_Password_Credentials_Grant); -} - -/* - * Like oidc_grant for flow Flow_Client_Credentials_Grant - */ -void oidc_grant_client_credentials(const char *appli, const char *idp, struct json_object *args, const struct oidc_grant_cb *cb) -{ - grant(mkargs(appli, idp, args, cb), Flow_Client_Credentials_Grant); -} - -/* - * Refreshes the 'token' for the 'appli' and the 'idp'. - * 'cb' describes the callback actions that are called before - * the function returns. - */ -void oidc_token_refresh(const char *appli, const char *idp, struct json_object *token, const struct oidc_grant_cb *cb) -{ - struct args *args; - - args = mkargs(appli, idp, token, cb); - if (!args) - return; - args->refresh = 1; - perform(args, string_token_endpoint, "refresh_token", - PARAM(Grant_Type) | PARAM(Refresh_Token), - PARAM(Scope) | PARAM(Authorization) - ); -} - -/* - * Adds the header "authorisation" with the bearer access_token of 'token'. - * Return 1 on case of success or 0 otherwise. - */ -int oidc_add_bearer(CURL *curl, struct json_object *token) -{ - struct json_object *bearer; - - return json_object_object_get_ex(token, "authorization", &bearer) - && curl_wrap_add_header_value(curl, "authorization", json_object_get_string(bearer)); -} - - diff --git a/agl-identity-service/src/oidc-agent.h b/agl-identity-service/src/oidc-agent.h deleted file mode 100644 index de5918d..0000000 --- a/agl-identity-service/src/oidc-agent.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -struct json_object; -#include - -/***************** IDP **************************/ - -extern int oidc_idp_set( - const char *name, - struct json_object *desc - ); - -extern int oidc_idp_exists( - const char *name - ); - -extern void oidc_idp_delete( - const char *name - ); - - -/***************** APPLI **************************/ - -extern int oidc_appli_set( - const char *name, - const char *idp, - struct json_object *desc, - int make_default - ); - -extern int oidc_appli_exists( - const char *name - ); - -extern int oidc_appli_has_idp( - const char *name, - const char *idp - ); - -extern int oidc_appli_set_default_idp( - const char *name, - const char *idp - ); - -extern void oidc_appli_delete( - const char *name - ); - -/***************** APPLI **************************/ - -struct oidc_grant_cb -{ - void *closure; - void (*success)(void *closure, struct json_object *result); - void (*error)(void *closure, const char *message, const char *indice); -}; - -enum oidc_grant_flow -{ - Flow_Invalid, - Flow_Authorization_Code_Grant, - Flow_Implicit_Grant, - Flow_Resource_Owner_Password_Credentials_Grant, - Flow_Client_Credentials_Grant, - Flow_Extension_Grant -}; - - -extern void oidc_grant( - const char *appli, - const char *idp, - struct json_object *args, - const struct oidc_grant_cb *cb, - enum oidc_grant_flow flow - ); - -extern void oidc_grant_owner_password( - const char *appli, - const char *idp, - struct json_object *args, - const struct oidc_grant_cb *cb - ); - -extern void oidc_grant_client_credentials( - const char *appli, - const char *idp, - struct json_object *args, - const struct oidc_grant_cb *cb - ); - -extern void oidc_token_refresh( - const char *appli, - const char *idp, - struct json_object *token, - const struct oidc_grant_cb *cb - ); - -extern int oidc_add_bearer( - CURL *curl, - struct json_object *token - ); - diff --git a/agl-identity-service/src/test-aia-uds-bluez.c b/agl-identity-service/src/test-aia-uds-bluez.c deleted file mode 100644 index 4a084a5..0000000 --- a/agl-identity-service/src/test-aia-uds-bluez.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2015, 2016, 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include - -#include - -#include "aia-uds-bluez.h" - - -void p(const char *tag, const struct aia_uds_value *value) -{ - printf("%10s %c %.*s\n", tag, value->changed?'*':' ', (int)value->length, value->data); -} - -void onchg(const struct aia_uds *uds) -{ - printf("\n"); - p("first name", &uds->first_name); - p("last name", &uds->last_name); - p("email", &uds->email); - p("language", &uds->language); -} - -int main() -{ - sd_bus *bus; - sd_id128_t id; - int rc; - - aia_uds_set_on_change(onchg); - rc = sd_bus_default_system(&bus); - rc = sd_id128_randomize(&id); - rc = sd_bus_set_server(bus, 1, id); - rc = sd_bus_start(bus); - rc = aia_uds_init(bus); - rc = aia_uds_advise(1, NULL, NULL); - for (;;) { - rc = sd_bus_process(bus, NULL); - if (rc < 0) - break; - else if (rc == 0) - rc = sd_bus_wait(bus, (uint64_t) -1); - } -} - diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake new file mode 100644 index 0000000..6e2aa26 --- /dev/null +++ b/conf.d/cmake/config.cmake @@ -0,0 +1,201 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# Project Info +# ------------------ +set(PROJECT_NAME agl-identity-service) +set(PROJECT_VERSION "0.1") +set(PROJECT_PRETTY_NAME "AGL identity service") +set(PROJECT_DESCRIPTION "AGL identity service") +set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/p/apps/agl-service-identity-agent.git") +set(PROJECT_ICON "icon.png") +set(PROJECT_AUTHOR "José Bollo") +set(PROJECT_AUTHOR_MAIL "jose.bollo@iot.bzh") +set(PROJECT_LICENSE "APL2.0") +set(PROJECT_LANGUAGES,"C") + +# Where are stored default templates files from submodule or subtree app-templates in your project tree +# relative to the root project directory +set(PROJECT_APP_TEMPLATES_DIR "conf.d/app-templates") + +# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain +# but used and must be built and linked. +# set(PROJECT_LIBDIR "libs") + +# Where are stored data for your application. Pictures, static resources must be placed in that folder. +# set(PROJECT_RESOURCES "data") + +# Which directories inspect to find CMakeLists.txt target files +# set(PROJECT_SRC_DIR_PATTERN "*") + +# Compilation Mode (DEBUG, RELEASE) +# ---------------------------------- +set(USE_EFENCE 0) + +# Kernel selection if needed. You can choose between a +# mandatory version to impose a minimal version. +# Or check Kernel minimal version and just print a Warning +# about missing features and define a preprocessor variable +# to be used as preprocessor condition in code to disable +# incompatibles features. Preprocessor define is named +# KERNEL_MINIMAL_VERSION_OK. +# +# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and +# Yocto SDK Kernel version. +# ----------------------------------------------- +#set (kernel_mandatory_version 4.8) +#set (kernel_minimal_version 4.8) + +# Compiler selection if needed. Impose a minimal version. +# ----------------------------------------------- +set (gcc_minimal_version 4.9) + +# PKG_CONFIG required packages +# ----------------------------- +set (PKG_REQUIRED_LIST + json-c + afb-daemon +) + +# Prefix path where will be installed the files +# Default: /usr/local (need root permission to write in) +# ------------------------------------------------------ +#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) + +# Customize link option +# ----------------------------- +#list(APPEND link_libraries -an-option) + +# Compilation options definition +# Use CMake generator expressions to specify only for a specific language +# Values are prefilled with default options that is currently used. +# Either separate options with ";", or each options must be quoted separately +# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED ! +# ---------------------------------------------------------------------------- +#set(COMPILE_OPTIONS +# -Wall +# -Wextra +# -Wconversion +# -Wno-unused-parameter +# -Wno-sign-compare +# -Wno-sign-conversion +# -Werror=maybe-uninitialized +# -Werror=implicit-function-declaration +# -ffunction-sections +# -fdata-sections +# -fPIC +# CACHE STRING "Compilation flags") +#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.") +#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.") +#set(PROFILING_COMPILE_OPTIONS +# -g +# -O0 +# -pg +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for PROFILING build type.") +#set(DEBUG_COMPILE_OPTIONS +# -g +# -ggdb +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for DEBUG build type.") +#set(CCOV_COMPILE_OPTIONS +# -g +# -O2 +# --coverage +# CACHE STRING "Compilation flags for CCOV build type.") +#set(RELEASE_COMPILE_OPTIONS +# -g +# -O2 +# CACHE STRING "Compilation flags for RELEASE build type.") + +# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] +# --------------------------------------------------------------------- +set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) +set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) + +# Optional location for config.xml.in +# ----------------------------------- +#set(WIDGET_ICON conf.d/wgt/${PROJECT_ICON} CACHE PATH "Path to the widget icon") +set(WIDGET_CONFIG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)") + +# Mandatory widget Mimetype specification of the main unit +# -------------------------------------------------------------------------- +# Choose between : +#- text/html : HTML application, +# content.src designates the home page of the application +# +#- application/vnd.agl.native : AGL compatible native, +# content.src designates the relative path of the binary. +# +# - application/vnd.agl.service: AGL service, content.src is not used. +# +#- ***application/x-executable***: Native application, +# content.src designates the relative path of the binary. +# For such application, only security setup is made. +# +set(WIDGET_TYPE application/vnd.agl.service) + +# Mandatory Widget entry point file of the main unit +# -------------------------------------------------------------- +# This is the file that will be executed, loaded, +# at launch time by the application framework. +# +set(WIDGET_ENTRY_POINT config.xml) + +# Optional dependencies order +# --------------------------- +#set(EXTRA_DEPENDENCIES_ORDER) + +# Optional Extra global include path +# ----------------------------------- +#set(EXTRA_INCLUDE_DIRS) + +# Optional extra libraries +# ------------------------- +#set(EXTRA_LINK_LIBRARIES) + +# Optional force binding Linking flag +# ------------------------------------ +# set(BINDINGS_LINK_FLAG LinkOptions ) + +# Optional force package prefix generation, like widget +# ----------------------------------------------------- +# set(PKG_PREFIX DestinationPath) + +# Optional Application Framework security token +# and port use for remote debugging. +#------------------------------------------------------------ +set(AFB_TOKEN "" CACHE PATH "Default binder security token") +set(AFB_REMPORT "1234" CACHE PATH "Default binder listening port") + +# Print a helper message when every thing is finished +# ---------------------------------------------------- +set(CLOSING_MESSAGE "Typical binding launch: afb-daemon --port=${AFB_REMPORT} --workdir=${CMAKE_BINARY_DIR}/package --ldpaths=lib --roothttp=htdocs --token=\"${AFB_TOKEN}\" --tracereq=common --verbose") +set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") + +# Optional schema validator about now only XML, LUA and JSON +# are supported +#------------------------------------------------------------ +#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler") +#set(XML_CHECKER "xmllint" CACHE STRING "XML linter") +#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter") + +# This include is mandatory and MUST happens at the end +# of this file, else you expose you to unexpected behavior +# ----------------------------------------------------------- +include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake) diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in new file mode 100644 index 0000000..cd66c2b --- /dev/null +++ b/conf.d/wgt/config.xml.in @@ -0,0 +1,21 @@ + + + @PROJECT_NAME@ + + + @PROJECT_DESCRIPTION@ + @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> + @PROJECT_LICENSE@ + + + + + + + + + + + + + diff --git a/etc/config.json b/etc/config.json new file mode 100644 index 0000000..c69e5be --- /dev/null +++ b/etc/config.json @@ -0,0 +1,23 @@ +{ + "endpoint": "https://agl-graphapi.forgerocklabs.org:443/getuserprofilefromtoken", + "vin": "WVGGF7BP7HD005986", + "autoadvise": true, + "delay": 5, + "idp": { + "authorization_endpoint": "", + "token_endpoint": "https://agl-am.forgerocklabs.org:8043/openam/oauth2/stateless/access_token" + }/*, + "appli": { + "authorization": "Basic c3RhdGVsZXNzOnBhc3N3b3JkMg==", + "username": "bjensen", + "password": "Passw0rd", + "scope": "openid profile email cn sn givenName ou mail postalAddress departmentNumber physicalDeliveryOfficeName facsimileTelephoneNumber" + } +*/ +} +/* + "endpoint": "https://frdemo-graphapi.forgerocklabs.org/getuserprofilefromtoken", + "vin": "JTEBU4BF6EK189816", + "delay": 10 +*/ + diff --git a/htdocs/identity/AFB-websock.js b/htdocs/identity/AFB-websock.js new file mode 100644 index 0000000..08a7ffe --- /dev/null +++ b/htdocs/identity/AFB-websock.js @@ -0,0 +1,174 @@ +AFB = function(base, initialtoken){ + +var urlws = "ws://"+window.location.host+"/"+base; +var urlhttp = "http://"+window.location.host+"/"+base; + +/*********************************************/ +/**** ****/ +/**** AFB_context ****/ +/**** ****/ +/*********************************************/ +var AFB_context; +{ + var UUID = undefined; + var TOKEN = initialtoken; + + var context = function(token, uuid) { + this.token = token; + this.uuid = uuid; + } + + context.prototype = { + get token() {return TOKEN;}, + set token(tok) {if(tok) TOKEN=tok;}, + get uuid() {return UUID;}, + set uuid(id) {if(id) UUID=id;} + }; + + AFB_context = new context(); +} +/*********************************************/ +/**** ****/ +/**** AFB_websocket ****/ +/**** ****/ +/*********************************************/ +var AFB_websocket; +{ + var CALL = 2; + var RETOK = 3; + var RETERR = 4; + var EVENT = 5; + + var PROTO1 = "x-afb-ws-json1"; + + AFB_websocket = function(onopen, onabort) { + var u = urlws; + if (AFB_context.token) { + u = u + '?x-afb-token=' + AFB_context.token; + if (AFB_context.uuid) + u = u + '&x-afb-uuid=' + AFB_context.uuid; + } + this.ws = new WebSocket(u, [ PROTO1 ]); + this.pendings = {}; + this.awaitens = {}; + this.counter = 0; + this.ws.onopen = onopen.bind(this); + this.ws.onerror = onerror.bind(this); + this.ws.onclose = onclose.bind(this); + this.ws.onmessage = onmessage.bind(this); + this.onopen = onopen; + this.onabort = onabort; + this.onclose = onabort; + } + + function onerror(event) { + var f = this.onabort; + if (f) { + delete this.onopen; + delete this.onabort; + f && f(this); + } + this.onerror && this.onerror(this); + } + + function onopen(event) { + var f = this.onopen; + delete this.onopen; + delete this.onabort; + f && f(this); + } + + function onclose(event) { + for (var id in this.pendings) { + var ferr = this.pendings[id].onerror; + ferr && ferr(null, this); + } + this.pendings = {}; + this.onclose && this.onclose(); + } + + function fire(awaitens, name, data) { + var a = awaitens[name]; + if (a) + a.forEach(function(handler){handler(data);}); + var i = name.indexOf("/"); + if (i >= 0) { + a = awaitens[name.substring(0,i)]; + if (a) + a.forEach(function(handler){handler(data);}); + } + a = awaitens["*"]; + if (a) + a.forEach(function(handler){handler(data);}); + } + + function reply(pendings, id, ans, offset) { + if (id in pendings) { + var p = pendings[id]; + delete pendings[id]; + var f = p[offset]; + f(ans); + } + } + + function onmessage(event) { + var obj = JSON.parse(event.data); + var code = obj[0]; + var id = obj[1]; + var ans = obj[2]; + AFB_context.token = obj[3]; + switch (code) { + case RETOK: + reply(this.pendings, id, ans, 0); + break; + case RETERR: + reply(this.pendings, id, ans, 1); + break; + case EVENT: + default: + fire(this.awaitens, id, ans); + break; + } + } + + function close() { + this.ws.close(); + this.onabort(); + } + + function call(method, request) { + return new Promise((function(resolve, reject){ + var id, arr; + do { + id = String(this.counter = 4095 & (this.counter + 1)); + } while (id in this.pendings); + this.pendings[id] = [ resolve, reject ]; + arr = [CALL, id, method, request ]; + if (AFB_context.token) arr.push(AFB_context.token); + this.ws.send(JSON.stringify(arr)); + }).bind(this)); + } + + function onevent(name, handler) { + var id = name; + var list = this.awaitens[id] || (this.awaitens[id] = []); + list.push(handler); + } + + AFB_websocket.prototype = { + close: close, + call: call, + onevent: onevent + }; +} +/*********************************************/ +/**** ****/ +/**** ****/ +/**** ****/ +/*********************************************/ +return { + context: AFB_context, + ws: AFB_websocket +}; +}; + diff --git a/htdocs/identity/binding-debug.css b/htdocs/identity/binding-debug.css new file mode 100644 index 0000000..f41c940 --- /dev/null +++ b/htdocs/identity/binding-debug.css @@ -0,0 +1,61 @@ +#debug-panel { + float: right; +} + +#debug-panel.collapsed { + background-color: transparent; + overflow-x: hidden; +} + +#debug-panel.expanded { + background-color: lightyellow; + max-width: 25%; + overflow-x: scroll; +} + +#debug-panel.collapsed > #debug-panel-collapse { + display: none; +} + +#debug-panel.expanded > #debug-panel-collapse { + display: block; +} + +#debug-panel.collapsed > #debug-panel-expand { + display: block; +} + +#debug-panel.expanded > #debug-panel-expand { + display: none; +} + +#debug-panel.expanded > #debug-panel-content { + display: block; +} + +#debug-panel.collapsed > #debug-panel-content { + display: none; +} + +.json-key { + color: cornflowerblue; + font-weight: bold; +} + +.json-string { + color: crimson; +} + +.json-number { + color: sandybrown; +} + +.json-boolean { + color: fuchsia; + font-weight: bold; +} + +.json-null { + color: black; + font-weight: bold; +} diff --git a/htdocs/identity/identity-binding.js b/htdocs/identity/identity-binding.js new file mode 100644 index 0000000..55dbb96 --- /dev/null +++ b/htdocs/identity/identity-binding.js @@ -0,0 +1,158 @@ +var afb = new AFB("api", "HELLO"); +var ws; + +function add_debbug_panel() { + + if (document.getElementById("debug-panel")) + return; + + var itm = document.getElementById("debug-panel-container"); + if (itm) + { + var pnl = + "
\n" + + " \n" + + " \n" + + "
\n" + + "

Debug

\n" + + "

Call

\n" + + "
    \n" + + "
  • api :
  • \n" + + "
  • verb :
  • \n" + + "
  • query :
  • \n" + + "
\n" + + "
\n" +
+			"        
\n" + + "

Response

\n" +
+			"        

Event

\n" +
+			"    
\n" + + "
\n"; + itm.insertAdjacentHTML("afterbegin", pnl); + } +} + +function createClass(name,rules) { + var style = document.createElement('style'); + style.type = 'text/css'; + document.getElementsByTagName('head')[0].appendChild(style); + if(!(style.sheet||{}).insertRule) + (style.styleSheet || style.sheet).addRule(name, rules); + else + style.sheet.insertRule(name+"{"+rules+"}",0); +} + +function syntaxHighlight(json) { + if (typeof json != 'string') + json = JSON.stringify(json, undefined, 2); + + json = json.replace(/&/g, '&').replace(//g, '>'); + return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { + var cls = 'json-number'; + if (/^"/.test(match)) { + if (/:$/.test(match)) { + cls = 'json-key'; + } else { + cls = 'json-string'; + } + } else if (/true|false/.test(match)) { + cls = 'json-boolean'; + } else if (/null/.test(match)) { + cls = 'json-null'; + } + return '' + match + ''; + }); +} + +function set_item_html(id, text) +{ + var itm = document.getElementById(id); + if (itm) itm.innerHTML = text; +} + +function set_item_text(id, text) +{ + var itm = document.getElementById(id); + if (itm) itm.innerText = text; +} + +function debug_panel_collapse() { + var pnl = document.getElementById('debug-panel'); + if (pnl) + { + pnl.classList.remove('expanded'); + pnl.classList.add('collapsed'); + } +} + +function debug_panel_expand() { + var pnl = document.getElementById('debug-panel'); + if (pnl) + { + pnl.classList.remove('collapsed'); + pnl.classList.add('expanded'); + } +} + +function init() { + add_debbug_panel(); + ws = new afb.ws(onopen, onabort); +} + +function onopen() { + //callbinder("ll-auth", "getuser", ""); + ws.onevent("*", gotevent); +} + +function onabort() { +} + +function replyok(obj) { + console.log("replyok:" + JSON.stringify(obj)); + set_item_html("debug-panel-response", syntaxHighlight(JSON.stringify(obj, null, 4))); +} + +function replyerr(obj) { + console.log("replyerr:" + JSON.stringify(obj)); + set_item_html("debug-panel-response", syntaxHighlight(JSON.stringify(obj, null, 4))); +} + +function gotevent(obj) { + console.log("gotevent:" + JSON.stringify(obj)); + set_item_html("debug-panel-event", syntaxHighlight(JSON.stringify(obj, null, 4))); +} + +function callbinder(api, verb, query) { + console.log ("subscribe api="+api+" verb="+verb+" query=" +query); + + set_item_text("debug-panel-call-api", api); + set_item_text("debug-panel-call-verb", verb); + set_item_html("debug-panel-call-query", syntaxHighlight(JSON.stringify(query, null, 4))); + + ws.call(api+"/"+verb, query).then(replyok, replyerr); +} + +function subscribe() { + callbinder("identity", "subscribe", {}); +} + +function getIdentity() { + callbinder("identity", "get", {}); +} + +function logout() { + callbinder("identity", "logout", {}); +} + +function fakeAuth() { + + var e = document.getElementById("fake-auth-kind"); + var arg = { + "kind": e.options[e.selectedIndex].value, + "key": (document.getElementById("fake-auth-key").value === "custom" ? document.getElementById("fake-auth-key-custom").value : document.getElementById("fake-auth-key").value) + } + callbinder("identity", "fake-auth", arg); +} + +function updateNfcUi() { + document.getElementById('fake-auth-key-custom').disabled = !(document.getElementById("fake-auth-key").value === "custom"); +} diff --git a/htdocs/identity/index.html b/htdocs/identity/index.html new file mode 100644 index 0000000..bbeee51 --- /dev/null +++ b/htdocs/identity/index.html @@ -0,0 +1,41 @@ + + + + agl-service-identity + + + + + + + +
+

agl-service-identity

+

+

    +
  • +
  • + + + + +
  • +
  • +
  • +
+

+ + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..e4a744b --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,49 @@ +########################################################################### +# Copyright 2016, 2017 IoT.bzh +# +# author: José Bollo +# author: Stéphane Desneux +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +PROJECT_TARGET_ADD(afb-identity-binding) + +add_library(afb-identity-binding MODULE + agl-identity-binding.c + agl-forgerock.c + aia-get.c + authorization.c + base64.c + curl-wrap.c + escape.c + oidc-agent.c +) + +pkg_check_modules(EXTRAS REQUIRED libcurl) + +set_target_properties(afb-identity-binding PROPERTIES + LABELS "BINDING" + PREFIX "" + COMPILE_FLAGS "${EXTRAS_CFLAGS} -DFOR_AFB_BINDING" + LINK_FLAGS "${BINDINGS_LINK_FLAG}" + LINK_LIBRARIES "${EXTRAS_LIBRARIES}" + OUTPUT_NAME "${TARGET_NAME}" +) + +add_custom_command(TARGET ${TARGET_NAME} +PRE_BUILD +COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/htdocs +COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../htdocs ${CMAKE_CURRENT_BINARY_DIR}/../package/ +COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/etc +COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../etc ${CMAKE_CURRENT_BINARY_DIR}/../package/) diff --git a/src/agl-forgerock.c b/src/agl-forgerock.c new file mode 100644 index 0000000..97cc4b9 --- /dev/null +++ b/src/agl-forgerock.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include +#include + +#include + +#define AFB_BINDING_VERSION 2 +#include + +#include "oidc-agent.h" +#include "aia-get.h" + +#ifndef NULL +#define NULL 0 +#endif + +static int expiration_delay = 5; + +static const char default_endpoint[] = "https://agl-graphapi.forgerocklabs.org/getuserprofilefromtoken"; +static const char *oidc_name; + +static char *endpoint; + +static void (*onloaded)(struct json_object *data, const char *error); + +/***** configuration ********************************************/ + +static void confsetstr(struct json_object *conf, const char *name, char **value, const char *def) +{ + struct json_object *v; + const char *s; + char *p; + + s = conf && json_object_object_get_ex(conf, name, &v) ? json_object_get_string(v) : def; + p = *value; + if (s && p != s) { + *value = strdup(s); + free(p); + } +} + +static void confsetint(struct json_object *conf, const char *name, int *value, int def) +{ + struct json_object *v; + + *value = conf && json_object_object_get_ex(conf, name, &v) ? json_object_get_int(v) : def; +} + +static void confsetoidc(struct json_object *conf, const char *name) +{ + struct json_object *idp, *appli; + + if (conf + && json_object_object_get_ex(conf, "idp", &idp) + && json_object_object_get_ex(conf, "appli", &appli)) { + if (oidc_idp_set(name, idp) && oidc_appli_set(name, name, appli, 1)) { + oidc_name = name; + } + } +} + +/****************************************************************/ + +static void loaded(struct json_object *data, const char *error) +{ + if (onloaded) + onloaded(data, error); +} + +static void downloaded(void *closure, int status, const void *buffer, size_t size) +{ + struct json_object *object, *subobj; + struct json_object *objkey = closure; + struct json_object *tmp; + + json_object_object_get_ex(objkey, "url", &tmp); + const char *url = json_object_get_string(tmp); + + /* checks whether discarded */ + if (status == 0 && !buffer) { + AFB_ERROR("discarded"); + loaded(NULL, "discarded"); + goto end; /* discarded */ + } + + /* scan for the status */ + if (status == 0 || !buffer) { + AFB_ERROR("uploading %s failed %s", url ? : "?", (const char*)buffer ? : ""); + loaded(NULL, "failed"); + goto end; + } + + /* get the object */ + AFB_DEBUG("received data: %.*s", (int)size, (char*)buffer); + object = json_tokener_parse(buffer); /* okay because 0 appended */ + + /* extract useful part */ + subobj = NULL; + if (object && !json_object_object_get_ex(object, "results", &subobj)) + subobj = NULL; + if (subobj) + subobj = json_object_array_get_idx(subobj, 0); + if (subobj && !json_object_object_get_ex(subobj, "data", &subobj)) + subobj = NULL; + if (subobj) + subobj = json_object_array_get_idx(subobj, 0); + if (subobj && !json_object_object_get_ex(subobj, "row", &subobj)) + subobj = NULL; + if (subobj) + subobj = json_object_array_get_idx(subobj, 0); + + /* is it a recognized user ? */ + if (!subobj) { + /* not recognized!! */ + AFB_INFO("unrecognized key for %s", url ? : "?"); + json_object_put(object); + loaded(NULL, "malformed"); + goto end; + } + + // Save the profile to the database + struct json_object* dbr; + struct json_object* record = json_object_new_object(); + json_object_object_add(record, "key", objkey); + json_object_object_add(record, "value", json_object_get(subobj)); + afb_service_call_sync("persistence", "update", record, &dbr); + + loaded(subobj, NULL); + json_object_put(object); +end: + json_object_put(objkey); +} + +/** public **************************************************************/ + +void agl_forgerock_setconfig(struct json_object *conf) +{ + confsetstr(conf, "endpoint", &endpoint, endpoint ? : default_endpoint); + confsetint(conf, "delay", &expiration_delay, expiration_delay); + confsetoidc(conf, "oidc-aia"); + AFB_NOTICE("Forgerock endpoint is: %s", endpoint); +} + +void agl_forgerock_setcb(void (*callback)(struct json_object *data, const char *error)) +{ + onloaded = callback; +} + +void reply_from_db(void* closure, int status, struct json_object* result) +{ + if (status) + { + AFB_WARNING("Failed to retrieve profile from persistence!"); + return; + } + + struct json_object* tmp; + json_object_object_get_ex(result, "response", &tmp); + json_object_object_get_ex(tmp, "value", &tmp); + AFB_NOTICE("User profile retrieved from persistence: %s", json_object_to_json_string(tmp)); + loaded(json_object_get(tmp), NULL); +} + +void agl_forgerock_download_request(const char *vin, const char *kind, const char *key) +{ + int rc; + char *url; + + rc = asprintf(&url, "%s?vin=%s&kind=%s&keytoken=%s", endpoint, vin, kind, key); + if (rc >= 0) + { + struct json_object* obj = json_object_new_object(); + json_object_object_add(obj, "url", json_object_new_string(url)); + json_object_object_add(obj, "vin", json_object_new_string(vin)); + json_object_object_add(obj, "kind", json_object_new_string(kind)); + json_object_object_add(obj, "key", json_object_new_string(key)); + + // Async get from database and from forgerock + struct json_object* key = json_object_new_object(); + json_object_object_add(key, "key", json_object_get(obj)); + afb_service_call("persistence", "read", key, reply_from_db, NULL); + + // Async get from forgerock + aia_get(url, expiration_delay, oidc_name, oidc_name, downloaded, obj); + free(url); + } + else + AFB_ERROR("out of memory"); +} + +/* vim: set colorcolumn=80: */ + diff --git a/src/agl-forgerock.h b/src/agl-forgerock.h new file mode 100644 index 0000000..1aee680 --- /dev/null +++ b/src/agl-forgerock.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +struct json_object; + +extern void agl_forgerock_setconfig(struct json_object *conf); +extern void agl_forgerock_setcb(void (*callback)(struct json_object *data, const char *error)); +extern void agl_forgerock_download_request(const char *vin, const char *kind, const char *key); diff --git a/src/agl-identity-binding.c b/src/agl-identity-binding.c new file mode 100644 index 0000000..12f43bd --- /dev/null +++ b/src/agl-identity-binding.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include + +#define AFB_BINDING_VERSION 2 +#include + +#include "agl-forgerock.h" + +#ifndef NULL +#define NULL 0 +#endif + +static struct afb_event event; + +static struct json_object *current_identity; + +static const char default_vin[] = "4T1BF1FK5GU260429"; +static char *vin; + +/***** configuration ********************************************/ + +static struct json_object *readjson(int fd) +{ + char *buffer; + struct stat s; + struct json_object *result = NULL; + int rc; + + rc = fstat(fd, &s); + if (rc == 0 && S_ISREG(s.st_mode)) { + buffer = alloca((size_t)(s.st_size)+1); + if (read(fd, buffer, (size_t)s.st_size) == (ssize_t)s.st_size) { + buffer[s.st_size] = 0; + //AFB_NOTICE("Config file: %s", buffer); + result = json_tokener_parse(buffer); + //if (!result) + //{ + // AFB_ERROR("Config file is not a valid JSON: %s", json_tokener_error_desc(json_tokener_get_error(NULL))); + //} + } + } + close(fd); + + return result; +} + +static struct json_object *get_global_config(const char *name, const char *locale) +{ + int fd = afb_daemon_rootdir_open_locale(name, O_RDONLY, locale); + if (fd < 0) AFB_ERROR("Config file not found: %s", name); + return fd < 0 ? NULL : readjson(fd); +} + +static struct json_object *get_local_config(const char *name) +{ + int fd = openat(AT_FDCWD, name, O_RDONLY, 0); + return fd < 0 ? NULL : readjson(fd); +} + +static void confsetstr(struct json_object *conf, const char *name, char **value, const char *def) +{ + struct json_object *v; + const char *s; + char *p; + + s = conf && json_object_object_get_ex(conf, name, &v) ? json_object_get_string(v) : def; + p = *value; + if (s && p != s) { + *value = strdup(s); + free(p); + } +} + +static void setconfig(struct json_object *conf) +{ + if (conf) { + confsetstr(conf, "vin", &vin, vin ? : default_vin); + agl_forgerock_setconfig(conf); + } +} + +static void readconfig() +{ + setconfig(get_global_config("etc/config.json", NULL)); + setconfig(get_local_config("/etc/agl/identity-agent-config.json")); + setconfig(get_local_config("config.json")); +} + +/****************************************************************/ + +static struct json_object *make_event_object(const char *name, const char *id, const char *nick) +{ + struct json_object *object = json_object_new_object(); + + /* TODO: errors */ + json_object_object_add(object, "eventName", json_object_new_string(name)); + json_object_object_add(object, "accountid", json_object_new_string(id)); + if (nick) + json_object_object_add(object, "nickname", json_object_new_string(nick)); + return object; +} + +static int send_event_object(const char *name, const char *id, const char *nick) +{ + return afb_event_push(event, make_event_object(name, id, nick)); +} + +static void do_login(struct json_object *desc) +{ + if (current_identity == NULL && desc == NULL) return; // Switching from NULL to NULL -> do nothing + if (current_identity && desc) + { + const char* a = json_object_to_json_string(current_identity); + const char* b = json_object_to_json_string(desc); + if (strcmp(a, b) == 0) + { + AFB_NOTICE("Reloging of the same user."); + return; // Switching from one user to the same user -> do nothing + } + } + + struct json_object *object; + + /* switching the user */ + AFB_INFO("Switching to user %s", desc ? json_object_to_json_string(desc) : "null"); + object = current_identity; + current_identity = json_object_get(desc); + json_object_put(object); + + if (!json_object_object_get_ex(desc, "name", &object)) + object = 0; + send_event_object("login", !object ? "null" : json_object_get_string(object)? : "?", 0); +} + +static void do_logout() +{ + struct json_object *object; + + AFB_INFO("Switching to no user"); + object = current_identity; + current_identity = 0; + json_object_put(object); + + send_event_object("logout", "null", 0); +} + +static void on_forgerock_data(struct json_object *data, const char *error) +{ + if (error) { + AFB_ERROR("Can't get data: %s", error); + } else { + do_login(data); + } +} + +/****************************************************************/ + +static void subscribe (struct afb_req request) +{ + int rc; + + rc = afb_req_subscribe(request, event); + if (rc < 0) + afb_req_fail(request, "failed", "subscribtion failed"); + else + afb_req_success(request, NULL, NULL); +} + +static void unsubscribe (struct afb_req request) +{ + afb_req_unsubscribe(request, event); + afb_req_success(request, NULL, NULL); +} + +static void logout (struct afb_req request) +{ + do_logout(); + afb_req_success(request, NULL, NULL); +} + +static void fake_login (struct afb_req request) +{ + struct json_object *desc = afb_req_json(request); + do_logout(); + if (desc) + do_login(desc); + afb_req_success(request, NULL, NULL); +} + +static void get (struct afb_req request) +{ + afb_req_success(request, json_object_get(current_identity), NULL); +} + +static void on_nfc_subscribed(void *closure, int status, struct json_object *result) +{ + if(status) + AFB_ERROR("Failed to subscribe to nfc events."); +} + +static void on_nfc_started(void *closure, int status, struct json_object *result) +{ + if (!status) { + afb_service_call("nfc", "subscribe", NULL, on_nfc_subscribed, NULL); + } + else + AFB_ERROR("Failed to start nfc polling."); +} + +static int service_init() +{ + agl_forgerock_setcb(on_forgerock_data); + event = afb_daemon_make_event("event"); + if (!afb_event_is_valid(event)) + return -1; + + readconfig(); + + if (afb_daemon_require_api("nfc", 1)) + return -1; + + if (afb_daemon_require_api("persistence", 1)) + return -1; + + afb_service_call("nfc", "start", NULL, on_nfc_started, NULL); + + return 0; +} + +static void on_nfc_target_add(struct json_object *object) +{ + struct json_object * json_uid; + const char *uid; + + if (json_object_object_get_ex(object, "UID", &json_uid)) + { + uid = json_object_get_string(json_uid); + AFB_NOTICE("nfc tag detected, call forgerock with vincode=%s and key=%s", vin ? vin : default_vin, uid); + send_event_object("incoming", uid, uid); + agl_forgerock_download_request(vin ? vin : default_vin, "nfc", uid); + } + else AFB_ERROR("nfc target add event is received but no UID found: %s", json_object_to_json_string(object)); +} + +static void onevent(const char *event, struct json_object *object) +{ + AFB_NOTICE("Received event: %s", event); + if (!strcmp("nfc/on-nfc-target-add", event)) + { + on_nfc_target_add(object); + return; + } + AFB_WARNING("Unhandled event: %s", event); +} + +static void fake_auth(struct afb_req req) +{ + struct json_object* req_object; + struct json_object* kind_object; + struct json_object* key_object; + + req_object = afb_req_json(req); + + if (!json_object_object_get_ex(req_object, "kind", &kind_object)) + { + afb_req_fail(req, "Missing arg: kind", NULL); + return; + } + + if (!json_object_object_get_ex(req_object, "key", &key_object)) + { + afb_req_fail(req, "Missing arg: key", NULL); + return; + } + + const char* kind = json_object_get_string(kind_object); + const char* key = json_object_get_string(key_object); + + send_event_object("incoming", key, key); + agl_forgerock_download_request(vin ? vin : default_vin, kind, key); + + afb_req_success(req, NULL, "fake auth success!"); +} + +// NOTE: this sample does not use session to keep test a basic as possible +// in real application most APIs should be protected with AFB_SESSION_CHECK +static const struct afb_verb_v2 verbs[]= +{ + {"subscribe" , subscribe , NULL, "subscribe to events" , AFB_SESSION_NONE }, + {"unsubscribe", unsubscribe , NULL, "unsubscribe to events" , AFB_SESSION_NONE }, + {"fake-login" , fake_login , NULL, "fake a login" , AFB_SESSION_NONE }, + {"logout" , logout , NULL, "log the current user out", AFB_SESSION_NONE }, + {"get" , get , NULL, "get data" , AFB_SESSION_NONE }, + {"fake-auth" , fake_auth , NULL, "fake an authentication" , AFB_SESSION_NONE }, + {NULL} +}; + +const struct afb_binding_v2 afbBindingV2 = +{ + .api = "identity", + .specification = NULL, + .info = "AGL identity service", + .verbs = verbs, + .preinit = NULL, + .init = service_init, + .onevent = onevent, + .noconcurrency = 0 +}; + +/* vim: set colorcolumn=80: */ diff --git a/src/aia-get.c b/src/aia-get.c new file mode 100644 index 0000000..56c82b0 --- /dev/null +++ b/src/aia-get.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include +#include + +#include "curl-wrap.h" +#include "oidc-agent.h" + +/* +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + + +#include "u2f-bluez.h" +*/ + + +struct keyrequest { + struct keyrequest *next; + int dead; + time_t expiration; + const char *url; + const char *appli; + const char *idp; + void (*callback)(void *closure, int status, const void *buffer, size_t size); + void *closure; + json_object *token; + pthread_t tid; +}; + +static struct keyrequest *keyrequests = 0; + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +static int curl_initialized = 0; + +static void perform_query_callback(void *closure, int status, CURL *curl, const char *result, size_t size) +{ + struct keyrequest *kr = closure; + kr->callback(kr->closure, status, result, size); +} + +static void perform_query(struct keyrequest *kr) +{ + CURL *curl; + + curl = curl_wrap_prepare_get_url(kr->url); + if (!curl) + kr->callback(kr->closure, 0, "out of memory", 0); + else { + oidc_add_bearer(curl, kr->token); + curl_wrap_do(curl, perform_query_callback, kr); + } +} + +static void token_success(void *closure, struct json_object *token) +{ + struct keyrequest *kr = closure; + kr->token = token; + perform_query(kr); +} + +static void token_error(void *closure, const char *message, const char *indice) +{ + struct keyrequest *kr = closure; + kr->callback(kr->closure, 0, message, 0); +} + +static void *kr_get_thread(void *closure) +{ + struct oidc_grant_cb cb; + struct keyrequest *kr = closure; + + if (!kr->appli) + perform_query(kr); + else { + cb.closure = kr; + cb.success = token_success; + cb.error = token_error; + oidc_grant_owner_password(kr->appli, kr->idp, NULL, &cb); + } + kr->dead = 1; + return NULL; +} + +static void kr_free(struct keyrequest *kr) +{ + json_object_put(kr->token); + free(kr); +} + +static struct keyrequest *kr_alloc( + const char *url, + time_t expiration, + const char *appli, + const char *idp, + void (*callback)(void *closure, int status, const void *buffer, size_t size), + void *closure +) +{ + struct keyrequest *result; + char *buf; + size_t surl, sappli, sidp; + + surl = 1 + strlen(url); + sappli = appli ? 1 + strlen(appli) : 0; + sidp = idp ? 1 + strlen(idp) : 0; + + result = calloc(1, surl + sappli + sidp + sizeof *result); + if (result) { + result->next = NULL; + result->dead = 0; + result->expiration = expiration; + result->callback = callback; + result->closure = closure; + result->token = NULL; + result->tid = 0; + + buf = (char*)(&result[1]); + result->url = buf; + buf = mempcpy(buf, url, surl); + if (!appli) + result->appli = NULL; + else { + result->appli = buf; + buf = mempcpy(buf, appli, sappli); + } + if (!idp) + result->idp = NULL; + else { + result->idp = buf; + buf = mempcpy(buf, idp, sidp); + } + } + return result; +} + +void aia_get( + const char *url, + int delay, + const char *appli, + const char *idp, + void (*callback)(void *closure, int status, const void *buffer, size_t size), + void *closure +) +{ + int rc; + time_t now; + struct keyrequest **pkr, *kr, *found_kr; + + /* initialize CURL component */ + pthread_mutex_lock(&mutex); + if (!curl_initialized) { + curl_initialized = 1; + curl_global_init(CURL_GLOBAL_DEFAULT); + } + + /* search for the same request and also cleanup deads */ + now = time(NULL); + found_kr = 0; + pkr = &keyrequests; + kr = *pkr; + while (kr) { + if (now > kr->expiration) { + if (kr->dead) { + *pkr = kr->next; + kr_free(kr); + } else { + pkr = &kr->next; + } + } else { + if (!strcmp(url, kr->url)) + found_kr = kr; + pkr = &kr->next; + } + kr = *pkr; + } + + /* check if found and pending */ + if (found_kr) { + /* found -> cancel */ + pthread_mutex_unlock(&mutex); + callback(closure, 0, NULL, 0); + return; + } + + /* allocates the keyrequest */ + kr = kr_alloc(url, now + delay, appli, idp, callback, closure); + if (!kr) { + pthread_mutex_unlock(&mutex); + callback(closure, 0, "Out of memory", 0); + return; + } + + /* link the request */ + kr->next = keyrequests; + keyrequests = kr; + + /* makes the request in a new thread */ + rc = pthread_create(&kr->tid, 0, kr_get_thread, kr); + if (rc != 0) { + /* error, unlink */ + keyrequests = kr->next; + pthread_mutex_unlock(&mutex); + kr_free(kr); + callback(closure, 0, "Can't create a new thread", 0); + return; + } + pthread_mutex_unlock(&mutex); +} + +/* vim: set colorcolumn=80: */ + diff --git a/src/aia-get.h b/src/aia-get.h new file mode 100644 index 0000000..be1a1f8 --- /dev/null +++ b/src/aia-get.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +extern void aia_get( + const char *url, + int delay, + const char *appli, + const char *idp, + void (*callback)(void *closure, int status, const void *buffer, size_t size), + void *closure +); + diff --git a/src/authorization.c b/src/authorization.c new file mode 100644 index 0000000..ae00923 --- /dev/null +++ b/src/authorization.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include + +#include "base64.h" + +char *authorization_basic_make(const char *user, const char *password) +{ + const char *array[3] = { user, ":", password }; + return base64_encode_array(array, 3); +} + +char *authorization_basic_make_header(const char *user, const char *password) +{ + char *key, *result; + + key = authorization_basic_make(user, password); + if (!key || asprintf(&result, "Authorization: Basic %s", key) < 0) + result = NULL; + free(key); + return result; +} + +/* vim: set colorcolumn=80: */ + diff --git a/src/authorization.h b/src/authorization.h new file mode 100644 index 0000000..bd26c1a --- /dev/null +++ b/src/authorization.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +extern char *authorization_basic_make(const char *user, const char *password); +extern char *authorization_basic_make_header(const char *user, const char *password); + +/* vim: set colorcolumn=80: */ diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..0841aba --- /dev/null +++ b/src/base64.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +const char base64_variant_standard[] = "+/="; +const char base64_variant_url[] = "-_="; +const char base64_variant_trunc[] = "+/"; +const char base64_variant_url_trunc[] = "-_"; + +static char eb64(int x, const char *variant) +{ + if (x < 52) + return (char)(x + (x < 26 ? 'A' : 'a'-26)); + else + return x < 62 ? (char)(x + '0' - 52) : variant[x - 62]; +} + +char *base64_encode_array_variant(const char * const *args, size_t count, const char *variant) +{ + const char *v, *s; + char *buffer, c; + size_t i, j, n; + uint16_t x; + + /* compute size and allocate */ + i = n = 0; + while (n < count) + i += strlen(args[n++]); + buffer = malloc(5 + ((i / 3) << 2)); + if (!buffer) + return NULL; + + /* encode */ + v = variant ? : base64_variant_standard; + n = 0; + j = 0; + x = 0; + while (n < count) { + s = args[n++]; + c = s[i = 0]; + while (c) { + x = (uint16_t)((uint16_t)(x << 8) | (uint16_t)(uint8_t)c); + switch (j & 3) { + case 0: + buffer[j++] = eb64((x >> 2) & 63, v); + break; + case 1: + buffer[j++] = eb64((x >> 4) & 63, v); + break; + case 2: + buffer[j++] = eb64((x >> 6) & 63, v); + buffer[j++] = eb64(x & 63, v); + break; + } + c = s[++i]; + } + } + + /* pad */ + if (v[2]) { + switch (j & 3) { + case 1: + buffer[j++] = eb64((x << 4) & 63, v); + buffer[j++] = v[2]; + buffer[j++] = v[2]; + break; + case 2: + buffer[j++] = eb64((x << 2) & 63, v); + buffer[j++] = v[2]; + break; + } + } + + /* done */ + buffer[j] = 0; + return buffer; +} + +char *base64_encode_multi_variant(const char * const *args, const char *variant) +{ + size_t count; + + for (count = 0 ; args[count] ; count++); + return base64_encode_array_variant(args, count, variant); +} + +char *base64_encode_variant(const char *arg, const char *variant) +{ + return base64_encode_array_variant(&arg, !!arg, variant); +} + diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..5a944df --- /dev/null +++ b/src/base64.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +extern const char base64_variant_standard[]; +extern const char base64_variant_trunc[]; + +extern const char base64_variant_url[]; +extern const char base64_variant_url_trunc[]; + +extern char *base64_encode_array_variant(const char * const *args, size_t count, const char *variant); +extern char *base64_encode_multi_variant(const char * const *args, const char *variant); +extern char *base64_encode_variant(const char *arg, const char *variant); + +#define base64_encode_array(args,count) base64_encode_array_variant(args,count,base64_variant_standard) +#define base64_encode_multi(args) base64_encode_multi_variant(args,base64_variant_standard) +#define base64_encode(arg) base64_encode_variant(arg,base64_variant_standard) + +#define base64_encode_array_standard(args,count) base64_encode_array_variant(args,count,base64_variant_standard) +#define base64_encode_multi_standard(args) base64_encode_multi_variant(args,base64_variant_standard) +#define base64_encode_standard(arg) base64_encode_variant(arg,base64_variant_standard) + +#define base64_encode_array_url(args,count) base64_encode_array_variant(args,count,base64_variant_url) +#define base64_encode_multi_url(args) base64_encode_multi_variant(args,base64_variant_url) +#define base64_encode_url(arg) base64_encode_variant(arg,base64_variant_url) + +#define base64_encode_array_trunc(args,count) base64_encode_array_variant(args,count,base64_variant_trunc) +#define base64_encode_multi_trunc(args) base64_encode_multi_variant(args,base64_variant_trunc) +#define base64_encode_trunc(arg) base64_encode_variant(arg,base64_variant_trunc) + +#define base64_encode_array_url_trunc(args,count) base64_encode_array_variant(args,count,base64_variant_url_trunc) +#define base64_encode_multi_url_trunc(args) base64_encode_multi_variant(args,base64_variant_url_trunc) +#define base64_encode_url_trunc(arg) base64_encode_variant(arg,base64_variant_url_trunc) + + + diff --git a/src/curl-wrap.c b/src/curl-wrap.c new file mode 100644 index 0000000..3c566a3 --- /dev/null +++ b/src/curl-wrap.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include + +#include "curl-wrap.h" +#include "escape.h" + + +/* internal representation of buffers */ +struct buffer { + size_t size; + char *data; +}; + +/* write callback for filling buffers with the response */ +static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + struct buffer *buffer = userdata; + size_t sz = size * nmemb; + size_t old_size = buffer->size; + size_t new_size = old_size + sz; + char *data = realloc(buffer->data, new_size + 1); + if (!data) + return 0; + memcpy(&data[old_size], ptr, sz); + data[new_size] = 0; + buffer->size = new_size; + buffer->data = data; + return sz; +} + +/* + * Perform the CURL operation for 'curl' and put the result in + * memory. If 'result' isn't NULL it receives the returned content + * that then must be freed. If 'size' isn't NULL, it receives the + * size of the returned content. Note that if not NULL, the real + * content is one byte greater than the read size and the last byte + * zero. This facility allows to handle the returned content as a + * null terminated C-string. + */ +int curl_wrap_perform(CURL *curl, char **result, size_t *size) +{ + int rc; + struct buffer buffer; + CURLcode code; + + /* init tthe buffer */ + buffer.size = 0; + buffer.data = NULL; + + /* Perform the request, res will get the return code */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); + + /* Perform the request, res will get the return code */ + code = curl_easy_perform(curl); + rc = code == CURLE_OK; + + /* Check for no errors */ + if (rc) { + /* no error */ + if (size) + *size = buffer.size; + if (result) + *result = buffer.data; + else + free(buffer.data); + } else { + /* had error */ + if (size) + *size = 0; + if (result) + *result = NULL; + free(buffer.data); + } + + return rc; +} + +void curl_wrap_do(CURL *curl, void (*callback)(void *closure, int status, CURL *curl, const char *result, size_t size), void *closure) +{ + int rc; + char *result; + size_t size; + char errbuf[CURL_ERROR_SIZE]; + + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); + rc = curl_wrap_perform(curl, &result, &size); + if (rc) + callback(closure, rc, curl, result, size); + else + callback(closure, rc, curl, errbuf, 0); + free(result); + curl_easy_cleanup(curl); +} + +int curl_wrap_content_type_is(CURL *curl, const char *value) +{ + char *actual; + CURLcode code; + + code = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &actual); + if (code != CURLE_OK || !actual) + return 0; + + return !strncasecmp(actual, value, strcspn(actual, "; ")); +} + +CURL *curl_wrap_prepare_get_url(const char *url) +{ + CURL *curl; + CURLcode code; + + curl = curl_easy_init(); + if(curl) { + code = curl_easy_setopt(curl, CURLOPT_URL, url); + if (code == CURLE_OK) + return curl; + curl_easy_cleanup(curl); + } + return NULL; +} + +CURL *curl_wrap_prepare_get(const char *base, const char *path, const char * const *args) +{ + CURL *res; + char *url; + + url = escape_url(base, path, args, NULL); + res = url ? curl_wrap_prepare_get_url(url) : NULL; + free(url); + return res; +} + +int curl_wrap_add_header(CURL *curl, const char *header) +{ + int rc; + struct curl_slist *list; + + list = curl_slist_append(NULL, header); + rc = list ? curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list) == CURLE_OK : 0; +/* + curl_slist_free_all(list); +*/ + return rc; +} + +int curl_wrap_add_header_value(CURL *curl, const char *name, const char *value) +{ + char *h; + int rc; + + rc = asprintf(&h, "%s: %s", name, value); + rc = rc < 0 ? 0 : curl_wrap_add_header(curl, h); + free(h); + return rc; +} + + +CURL *curl_wrap_prepare_post_url_data(const char *url, const char *datatype, const char *data, size_t szdata) +{ + CURL *curl; + + curl = curl_easy_init(); + if (curl + && CURLE_OK == curl_easy_setopt(curl, CURLOPT_URL, url) + && (!szdata || CURLE_OK == curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, szdata)) + && CURLE_OK == curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data) + && (!datatype || curl_wrap_add_header_value(curl, "content-type", datatype))) + return curl; + curl_easy_cleanup(curl); + return NULL; +} + +CURL *curl_wrap_prepare_post(const char *base, const char *path, const char * const *args) +{ + CURL *res; + char *url; + char *data; + size_t szdata; + + url = escape_url(base, path, NULL, NULL); + data = escape_args(args, &szdata); + res = url ? curl_wrap_prepare_post_url_data(url, NULL, data, szdata) : NULL; + free(url); + return res; +} + +/* vim: set colorcolumn=80: */ diff --git a/src/curl-wrap.h b/src/curl-wrap.h new file mode 100644 index 0000000..2e44f47 --- /dev/null +++ b/src/curl-wrap.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include + +extern char *curl_wrap_url (const char *base, const char *path, + const char *const *query, size_t * size); + +extern int curl_wrap_perform (CURL * curl, char **result, size_t * size); + +extern void curl_wrap_do(CURL *curl, void (*callback)(void *closure, int status, CURL *curl, const char *result, size_t size), void *closure); + +extern int curl_wrap_content_type_is (CURL * curl, const char *value); + +extern CURL *curl_wrap_prepare_get_url(const char *url); + +extern CURL *curl_wrap_prepare_get(const char *base, const char *path, const char * const *args); + +extern CURL *curl_wrap_prepare_post_url_data(const char *url, const char *datatype, const char *data, size_t szdata); + +extern CURL *curl_wrap_prepare_post(const char *base, const char *path, const char * const *args); + +extern int curl_wrap_add_header(CURL *curl, const char *header); + +extern int curl_wrap_add_header_value(CURL *curl, const char *name, const char *value); + +/* vim: set colorcolumn=80: */ + diff --git a/src/escape.c b/src/escape.c new file mode 100644 index 0000000..3bb25c2 --- /dev/null +++ b/src/escape.c @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include +#include +#include + +/* + * Test if 'c' is to be escaped or not. + * Any character that is not in [-.0-9A-Z_a-z~] + * must be escaped. + * Note that space versus + is not managed here. + */ +static inline int should_escape(char c) +{ +/* ASCII CODES OF UNESCAPED CHARS +car hx/oct idx +'-' 2d/055 1 +'.' 2e/056 2 +'0' 30/060 3 +... .. +'9' 39/071 12 +'A' 41/101 13 +... .. +'Z' 5a/132 38 +'_' 5f/137 39 +'a' 61/141 40 +... .. +'z' 7a/172 65 +'~' 7e/176 66 +*/ + /* [-.0-9A-Z_a-z~] */ + if (c <= 'Z') { + /* [-.0-9A-Z] */ + if (c < '0') { + /* [-.] */ + return c != '-' && c != '.'; + } else { + /* [0-9A-Z] */ + return c < 'A' && c > '9'; + } + } else { + /* [_a-z~] */ + if (c <= 'z') { + /* [_a-z] */ + return c < 'a' && c != '_'; + } else { + /* [~] */ + return c != '~'; + } + } +} + +/* + * returns the ASCII char for the hexadecimal + * digit of the binary value 'f'. + * returns 0 if f isn't in [0 ... 15]. + */ +static inline char bin2hex(int f) +{ + if ((f & 15) != f) + f = 0; + else if (f < 10) + f += '0'; + else + f += 'A' - 10; + return (char)f; +} + +/* + * returns the binary value for the hexadecimal + * digit whose char is 'c'. + * returns -1 if c isn't i n[0-9A-Fa-f] + */ +static inline int hex2bin(char c) +{ + /* [0-9A-Fa-f] */ + if (c <= 'F') { + /* [0-9A-F] */ + if (c <= '9') { + /* [0-9] */ + if (c >= '0') { + return (int)(c - '0'); + } + } else if (c >= 'A') { + /* [A-F] */ + return (int)(c - ('A' - 10)); + } + } else { + /* [a-f] */ + if (c >= 'a' && c <= 'f') { + return (int)(c - ('a' - 10)); + } + } + return -1; +} + +/* + * returns the length that will have the text 'itext' of length + * 'ilen' when escaped. When 'ilen' == 0, strlen is used to + * compute the size of 'itext'. + */ +static size_t escaped_length(const char *itext, size_t ilen) +{ + char c; + size_t i, r; + + if (!ilen) + ilen = strlen(itext); + c = itext[i = r = 0]; + while (i < ilen) { + r += c != ' ' && should_escape(c) ? 3 : 1; + c = itext[++i]; + } + return r; +} + +/* + * Escapes the text 'itext' of length 'ilen'. + * When 'ilen' == 0, strlen is used to compute the size of 'itext'. + * The escaped text is put in 'otext' of length 'olen'. + * Returns the length of the escaped text (it can be greater than 'olen'). + * When 'olen' is greater than the needed length, an extra null terminator + * is appened to the escaped string. + */ +static size_t escape_to(const char *itext, size_t ilen, char *otext, size_t olen) +{ + char c; + size_t i, r; + + if (!ilen) + ilen = strlen(itext); + c = itext[i = r = 0]; + while (i < ilen) { + if (c == ' ') + c = '+'; + else if (should_escape(c)) { + if (r < olen) + otext[r] = '%'; + r++; + if (r < olen) + otext[r] = bin2hex((c >> 4) & 15); + r++; + c = bin2hex(c & 15); + } + if (r < olen) + otext[r] = c; + r++; + c = itext[++i]; + } + if (r < olen) + otext[r] = 0; + return r; +} + +/* + * returns the length of 'itext' of length 'ilen' that can be unescaped. + * compute the size of 'itext' when 'ilen' == 0. + */ +static size_t unescapable_length(const char *itext, size_t ilen) +{ + char c; + size_t i; + + c = itext[i = 0]; + while (i < ilen) { + if (c != '%') + i++; + else { + if (i + 3 > ilen + || hex2bin(itext[i + 1]) < 0 + || hex2bin(itext[i + 2]) < 0) + break; + i += 3; + } + c = itext[i]; + } + return i; +} + +/* + * returns the length that will have the text 'itext' of length + * 'ilen' when escaped. When 'ilen' == 0, strlen is used to + * compute the size of 'itext'. + */ +static size_t unescaped_length(const char *itext, size_t ilen) +{ + char c; + size_t i, r; + + c = itext[i = r = 0]; + while (i < ilen) { + i += (size_t)(1 + ((c == '%') << 1)); + r++; + c = itext[i]; + } + return r; +} + +static size_t unescape_to(const char *itext, size_t ilen, char *otext, size_t olen) +{ + char c; + size_t i, r; + int h, l; + + ilen = unescapable_length(itext, ilen); + c = itext[i = r = 0]; + while (i < ilen) { + if (c != '%') { + if (c == '+') + c = ' '; + i++; + } else { + if (i + 2 >= ilen) + break; + h = hex2bin(itext[i + 1]); + l = hex2bin(itext[i + 2]); + c = (char)((h << 4) | l); + i += 3; + } + if (r < olen) + otext[r] = c; + r++; + c = itext[i]; + } + if (r < olen) + otext[r] = 0; + return r; +} + +/* create an url */ +char *escape_url(const char *base, const char *path, const char * const *args, size_t *length) +{ + int i; + size_t lb, lp, lq, l, L; + const char *null; + char *result; + + /* ensure args */ + if (!args) { + null = NULL; + args = &null; + } + + /* compute lengths */ + lb = base ? strlen(base) : 0; + lp = path ? strlen(path) : 0; + lq = 0; + i = 0; + while (args[i]) { + lq += 1 + escaped_length(args[i], strlen(args[i])); + i++; + if (args[i]) + lq += 1 + escaped_length(args[i], strlen(args[i])); + i++; + } + + /* allocation */ + L = lb + lp + lq + 1; + result = malloc(L + 1); + if (result) { + /* make the resulting url */ + l = lb; + if (lb) { + memcpy(result, base, lb); + if (result[l - 1] != '/' && path && path[0] != '/') + result[l++] = '/'; + } + if (lp) { + memcpy(result + l, path, lp); + l += lp; + } + i = 0; + while (args[i]) { + if (i) { + result[l++] = '&'; + } else if (base || path) { + result[l] = memchr(result, '?', l) ? '&' : '?'; + l++; + } + l += escape_to(args[i], strlen(args[i]), result + l, L - l); + i++; + if (args[i]) { + result[l++] = '='; + l += escape_to(args[i], strlen(args[i]), result + l, L - l); + } + i++; + } + result[l] = 0; + if (length) + *length = l; + } + return result; +} + +char *escape_args(const char * const *args, size_t *length) +{ + return escape_url(NULL, NULL, args, length); +} + +const char **unescape_args(const char *args) +{ + const char **r, **q; + char c, *p; + size_t j, z, l, n, lt; + + lt = n = 0; + if (args[0]) { + z = 0; + do { + l = strcspn(&args[z], "&="); + j = 1 + unescaped_length(&args[z], l); + lt += j; + z += l; + c = args[z++]; + if (c == '=') { + l = strcspn(&args[z], "&"); + j = 1 + unescaped_length(&args[z], l); + lt += j; + z += l; + c = args[z++]; + } + n++; + } while(c); + } + + l = lt + (2 * n + 1) * sizeof(char *); + r = malloc(l); + if (!r) + return r; + + q = r; + p = (void*)&r[2 * n + 1]; + if (args[0]) { + z = 0; + do { + q[0] = p; + l = strcspn(&args[z], "&="); + j = 1 + unescape_to(&args[z], l, p, lt); + lt -= j; + p += j; + z += l; + c = args[z++]; + if (c != '=') + q[1] = NULL; + else { + q[1] = p; + l = strcspn(&args[z], "&"); + j = 1 + unescape_to(&args[z], l, p, lt); + lt -= j; + p += j; + z += l; + c = args[z++]; + } + q = &q[2]; + } while(c); + } + q[0] = NULL; + return r; +} + +char *escape(const char *text, size_t textlen, size_t *reslength) +{ + size_t len; + char *result; + + len = 1 + escaped_length(text, textlen); + result = malloc(len); + if (result) + escape_to(text, textlen, result, len); + if (reslength) + *reslength = len - 1; + return result; +} + +char *unescape(const char *text, size_t textlen, size_t *reslength) +{ + size_t len; + char *result; + + len = 1 + unescaped_length(text, textlen); + result = malloc(len); + if (result) + unescape_to(text, textlen, result, len); + if (reslength) + *reslength = len - 1; + return result; +} + +#if 1 +#include +int main(int ac, char **av) +{ + int i; + char *x = escape_args((void*)++av, NULL); + char *y = escape(x, strlen(x), NULL); + char *z = unescape(y, strlen(y), NULL); + const char **v = unescape_args(x); + + printf("%s\n%s\n%s\n", x, y, z); + free(x); + free(y); + free(z); + i = 0; + while(v[i]) { + printf("%s=%s / %s=%s\n", av[i], av[i+1], v[i], v[i+1]); + i += 2; + } + free(v); + return 0; +} +#endif +/* vim: set colorcolumn=80: */ diff --git a/src/escape.h b/src/escape.h new file mode 100644 index 0000000..7d548db --- /dev/null +++ b/src/escape.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +extern char *escape_url(const char *base, const char *path, const char * const *args, size_t *length); +extern char *escape_args(const char * const *args, size_t *length); +extern const char **unescape_args(const char *args); + +/* vim: set colorcolumn=80: */ diff --git a/src/export.map b/src/export.map new file mode 100644 index 0000000..ee2f413 --- /dev/null +++ b/src/export.map @@ -0,0 +1 @@ +{ global: afbBindingV*; local: *; }; diff --git a/src/memo.txt b/src/memo.txt new file mode 100644 index 0000000..3a6ed50 --- /dev/null +++ b/src/memo.txt @@ -0,0 +1,11 @@ + +gcc --shared -fPIC -o aia.so [^t]*.c -lcurl -lsystemd -ljson-c + +afb-daemon --rootdir=. --ldpaths=. --port=1212 --token= --verbose --verbose --verbose + + +afb-client-demo localhost:1212/api?token=t +agl-identity-agent subscribe + + +gcc -o taia *aia-uds-bluez.c -lsystemd diff --git a/src/oidc-agent.c b/src/oidc-agent.c new file mode 100644 index 0000000..1f09e7a --- /dev/null +++ b/src/oidc-agent.c @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include + +#include "oidc-agent.h" +#include "escape.h" +#include "curl-wrap.h" + +/***************** utilities *************************/ + +static const char string_empty[] = ""; +static const char string_authorization_endpoint[] = "authorization_endpoint"; +static const char string_token_endpoint[] = "token_endpoint"; +#if 0 +static const char string_issuer[] = "issuer"; +static const char string_userinfo_endpoint[] = "userinfo_endpoint"; +static const char string_revocation_endpoint[] = "revocation_endpoint"; +static const char string_jwks_uri[] = "jwks_uri"; +#endif + +#define MAX_IDP_COUNT 20 +#define MAX_APPLI_COUNT 100 + +static struct json_object *idps; +static struct json_object *applis; + +static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; + +/***************** utilities *************************/ + +/* + * Get the object of 'name' in the 'container' and return it. + * Creates the result if needed and add it to the container. + * When 'maxcount' isn't zero the final count will not exceed 'maxcount'. + */ +static struct json_object *j_container_item(struct json_object *container, const char *name, int maxcount) +{ + struct json_object *result; + + /* ensure object of 'name' exists */ + if (!json_object_object_get_ex(container, name, &result)) { + if (maxcount && json_object_object_length(container) >= maxcount) + return NULL; + result = json_object_new_object(); + if (!result) + return NULL; + json_object_object_add (container, name, result); + } + return result; +} + +/* + * Like 'j_container_item' but also creates the 'container' if needed. + */ +static struct json_object *j_container_item_make(struct json_object **container, const char *name, int maxcount) +{ + struct json_object *cont; + + /* ensure container exists */ + cont = *container; + if (!cont) { + cont = json_object_new_object(); + if (!cont) + return NULL; + *container = cont; + } + return j_container_item(cont, name, maxcount); +} + +/* + * Adds in 'dest' the fields of 'src' + * Also when the value of a field of 'src' is null, delete the field of 'dst' + */ +static void j_merge(struct json_object *dest, struct json_object *src) +{ + struct json_object_iter i; + json_object_object_foreachC(src, i) { + if (json_object_is_type(i.val, json_type_null)) + json_object_object_del(dest, i.key); + else + json_object_object_add(dest, i.key, json_object_get(i.val)); + } +} + +/***************** IDP **************************/ + +/* + * Set the values of 'desc' for the idp of 'name'. + * Return 0 on error or 1 on success. + */ +int oidc_idp_set(const char *name, struct json_object *desc) +{ + struct json_object *idp; + int result = 0; + + pthread_rwlock_wrlock(&rwlock); + idp = j_container_item_make(&idps, name, MAX_IDP_COUNT); + if (idp) { + j_merge(idp, desc); + result = 1; + } + pthread_rwlock_unlock(&rwlock); + return result; +} + +/* + * Return 1 if idp of 'name' exists or 0 otherwise. + */ +int oidc_idp_exists(const char *name) +{ + int result; + + pthread_rwlock_rdlock(&rwlock); + result = json_object_object_get_ex(idps, name, NULL); + pthread_rwlock_unlock(&rwlock); + + return result; +} + +/* + * Deletes the idp of 'name'. + */ +void oidc_idp_delete(const char *name) +{ + pthread_rwlock_wrlock(&rwlock); + json_object_object_del(idps, name); + pthread_rwlock_unlock(&rwlock); +} + +/***************** APPLI **************************/ + +/* + * Returns the name of the idp of the 'appli'. + * Returns NULL when appli isn't set or default idp isn't set. + */ +static const char *get_default_idp(const char *appli) +{ + struct json_object *a, *i; + + if (!json_object_object_get_ex(applis, appli, &a)) + return NULL; + if (!json_object_object_get_ex(a, string_empty, &i)) + return NULL; + return json_object_get_string(i); +} + +/* + * Returns the application data related to the 'appli' for the 'idp'. + * If 'ja' isn't null, returns in it the object for the application 'appli'. + * Returns NULL in case of error. + */ +static struct json_object *get_appli_idp(const char *appli, const char *idp, struct json_object **ja) +{ + struct json_object *a, *i; + + if (!json_object_object_get_ex(applis, appli, &a) || !json_object_object_get_ex(a, idp, &i)) + return NULL; + if (ja) + *ja = a; + return i; +} + +/* + * Set the description 'desc' for the application of 'name' and + * the 'idp'. When 'make_default' is set it, make it the default idp + * for the application. + * Return 0 on error or 1 on success. + */ +int oidc_appli_set(const char *name, const char *idp, struct json_object *desc, int make_default) +{ + struct json_object *a, *ai; + int result = 0; + + pthread_rwlock_wrlock(&rwlock); + a = j_container_item_make(&applis, name, MAX_APPLI_COUNT); + if (a) { + ai = j_container_item(a, idp, 0); + if (ai) { + j_merge(ai, desc); + if (make_default || !json_object_object_get_ex(a, string_empty, NULL)) + json_object_object_add(a, string_empty, json_object_new_string(idp)); + result = 1; + } + } + pthread_rwlock_unlock(&rwlock); + return result; +} + +/* + * Is the appli of 'name' defined? + * Return 1 if answer is yes or 0 for no. + */ +int oidc_appli_exists(const char *name) +{ + int result; + + pthread_rwlock_rdlock(&rwlock); + result = json_object_object_get_ex(applis, name, NULL); + pthread_rwlock_unlock(&rwlock); + + return result; +} + +/* + * Does the appli of 'name' has the 'idp' defined? + * Return 1 if answer is yes or 0 for no. + */ +int oidc_appli_has_idp(const char *name, const char *idp) +{ + int result; + + pthread_rwlock_rdlock(&rwlock); + result = !!get_appli_idp(name, idp, NULL); + pthread_rwlock_unlock(&rwlock); + + return result; +} + +/* + * Set 'idp' as default for the application of 'name'. + * Returns 0 on error (appli or idp for appli not existing) + * or 1 in case of success. + */ +int oidc_appli_set_default_idp(const char *name, const char *idp) +{ + struct json_object *a, *i; + + pthread_rwlock_wrlock(&rwlock); + i = get_appli_idp(name, idp, &a); + if (i) + json_object_object_add(a, string_empty, json_object_new_string(idp)); + pthread_rwlock_unlock(&rwlock); + + return !!i; +} + +/* + * Deletes the application of 'name' + */ +void oidc_appli_delete(const char *name) +{ + pthread_rwlock_wrlock(&rwlock); + json_object_object_del(applis, name); + pthread_rwlock_unlock(&rwlock); +} + +/***************** AUTHORISATION **************************/ + +/* parameters */ +enum param +{ + Param_Access_Token, + Param_Acr_Values, + Param_Authorization, + Param_Client_Id, + Param_Client_Secret, + Param_Code, + Param_Display, + Param_Error, + Param_Error_Description, + Param_Error_Uri, + Param_Expires_In, + Param_Grant_Type, + Param_Id_Token, + Param_Id_Token_Hint, + Param_Login_Hint, + Param_Max_Age, + Param_Nonce, + Param_Password, + Param_Prompt, + Param_Redirect_Uri, + Param_Refresh_Token, + Param_Response_Type, + Param_Scope, + Param_State, + Param_Token_Type, + Param_Ui_Locales, + Param_Username, + PARAM_COUNT +}; + +#if PARAM_COUNT > 30 +# error "Too much parameters" +#endif +#define PARAM(p) ((uint32_t)((uint32_t)1 << (Param_##p))) + +/* args of authorization requests */ +struct args +{ + struct json_object *appli; + struct json_object *idp; + struct json_object *args; + struct oidc_grant_cb cb; + int locked; + int refresh; + uint32_t mandatory; + uint32_t all; + struct json_object *header; + struct json_object *query; +}; + +/* Release the lock if needed */ +static void args_unlock(struct args *args) +{ + if (!args->locked) { + pthread_rwlock_unlock(&rwlock); + args->locked = 0; + } +} + +/* Release the memory needed by args */ +static void args_destroy(struct args *args) +{ + json_object_put(args->appli); + json_object_put(args->idp); + json_object_put(args->header); + json_object_put(args->query); + free(args); +} + +/* Send the success event with the gained tokens */ +static void args_send_success(struct args *args, struct json_object *result) +{ + args_unlock(args); + args->cb.success(args->cb.closure, result); + args_destroy(args); +} + +/* Sends the error event with the indice to the client of args */ +static void args_send_error(struct args *args, const char *message, const char *indice) +{ + args_unlock(args); + args->cb.error(args->cb.closure, message, indice); + args_destroy(args); +} + +/* Send the error and also return NULL */ +static inline struct args *args_send_error_null(struct args *args, const char *message, const char *indice) +{ + args_send_error(args, message, indice); + return NULL; +} + +/* creates a struct args from the arguments, returns NULL on error */ +struct args *mkargs(const char *appli, const char *idp, struct json_object *args, const struct oidc_grant_cb *cb) +{ + struct args *result; + struct json_object *obj; + + /* allocates the args */ + result = calloc(1, sizeof *result); + if (!result) { + cb->error(cb->closure, "Out of memory", NULL); + return NULL; + } + + /* init of the structure */ + result->cb = *cb; + result->args = args; + result->header = json_object_new_object(); + result->query = json_object_new_object(); + + /* lock in read */ + pthread_rwlock_rdlock(&rwlock); + result->locked = 1; + + /* check previous allocations */ + if (!result->query || !result->header) { + return args_send_error_null(result, "Out of memory", NULL); + } + + /* check whether default idp */ + if (!idp) { + idp = get_default_idp(appli); + if (!idp) + return args_send_error_null(result, "No default IDP", NULL); + } + + /* get the IDP */ + if (!json_object_object_get_ex(idps, idp, &obj)) + return args_send_error_null(result, "Unknown IDP", idp); + result->idp = json_object_get(obj); + + /* get the appli */ + obj = get_appli_idp(appli, idp, NULL); + if (!obj) + return args_send_error_null(result, "Unknown APPLI for IDP", appli); + result->appli = json_object_get(obj); + + return result; +} + +/* get a value for a struct args */ +static struct json_object *args_object(struct args *args, const char *name) +{ + struct json_object *result; + + if (!json_object_object_get_ex(args->appli, name, &result) + && !json_object_object_get_ex(args->idp, name, &result) + && !json_object_object_get_ex(args->args, name, &result)) + result = NULL; + return result; +} + +/* get a string value for a struct args */ +static const char *args_string(struct args *args, const char *name) +{ + struct json_object *object = args_object(args, name); + return object ? json_object_get_string(object) : NULL; +} + +/* add a data */ +static int args_add(struct args *args, uint32_t val, const char *name, int query) +{ + struct json_object *obj, *dest; + + if (val & args->all) { + obj = args_object(args, name); + if (obj) { + dest = query ? args->query : args->header; + json_object_object_add(dest, name, json_object_get(obj)); + } + else if (val & args->mandatory) { + args_send_error(args, "Mandatory field missing", name); + return 0; + } + } + + return 1; +} + +/* + * Makes the CURL object for the given 'url' for either GET or POST depending + * on 'post' with the added 'header' fields and the given query parameters. + * Returns NULL on error. + * Ex: + * + * curl_json("http://iot.bzh/api", 0, {"X-Index": "no"}, {"fast":true,"item":"2345-hellfest"}) + * + * produces the query: + * + * GET /api?fast=true&item=2345-hellfest HTTP/1.1 + * Host: iot.bzh + * X-Index: no + * + * while the same but with post not null produces: + * + * POST /api HTTP/1.1 + * Host: iot.bzh + * X-Index: no + * Content-Type: application/x-www-form-urlencoded + * + * fast=true&item=2345-hellfest + * + */ +static CURL *curl_json(const char *url, int post, struct json_object *header, struct json_object *query) +{ + const char **args, *str; + struct json_object_iter i; + int idx; + CURL *result; + + /* create args array */ + idx = 1 + (json_object_object_length(query) << 1); + args = malloc((unsigned)idx * sizeof *args); + if (!args) + return NULL; + + /* fill the args array */ + args[--idx] = NULL; + json_object_object_foreachC(query, i) { + str = json_object_get_string(i.val); + args[--idx] = str; + args[--idx] = i.key; + } + + /* prepare the query */ + if (post) + result = curl_wrap_prepare_post(url, NULL, args); + else + result = curl_wrap_prepare_get(url, NULL, args); + free(args); + if(!result) + return NULL; + + /* add headers */ + if (header) { + json_object_object_foreachC(header, i) { + str = json_object_get_string(i.val); + if (!curl_wrap_add_header_value(result, i.key, str)) { + curl_easy_cleanup(result); + return NULL; + } + } + } + return result; +} + +/* + * Extract from the answer of 'curl' whose 'content' has 'size' bytes the + * embeded JSON object (if any). + * Returns it or returns NULL if the answer can't be interpreted. + */ +static struct json_object *decode_perform_result(CURL *curl, const char *content, size_t size) +{ + int i; + const char **args; + struct json_object *result; + + /* is it an url encoded answer? */ + if (curl_wrap_content_type_is(curl, "application/x-www-form-urlencoded")) { + /* yes, unescape as an array of strings */ + args = unescape_args(content); + if (!args) + result = NULL; + else { + /* wrap the key=value pairs in an object */ + result = json_object_new_object(); + if (result) { + for (i = 0 ; args[i] ; i += 2) + json_object_object_add(result, args[i], + json_object_new_string(args[i+1])); + } + free(args); + } + } else if (curl_wrap_content_type_is(curl, "application/json")) { + /* interpret the json */ + result = json_tokener_parse (content); + } else { + /* by default, still try to interpret the answer as if json */ + result = json_tokener_parse (content); + } + return result; +} + +/* + * Treats the result of the query 'curl' of 'content' of 'size' bytes for the 'args' + */ +static void perform_result(struct args *args, CURL *curl, const char *content, size_t size) +{ + struct json_object *obj, *at, *tt; + char *txt; + + /* get answer */ + obj = decode_perform_result(curl, content, size); + if (!obj) + return args_send_error(args, "unable to extract answer", content); + + /* process the answer */ + if (json_object_object_get_ex(obj, "access_token", &at) && json_object_object_get_ex(obj, "token_type", &tt)) { + if (!strcmp(json_object_get_string(tt), "bearer")) { + if (asprintf(&txt, "Bearer %s", json_object_get_string(at)) > 0) { + json_object_object_add(obj, "authorization", json_object_new_string(txt)); + free(txt); + } + } + } + + /* merge the answer to the token args in case of refresh */ + if (args->refresh) + j_merge(args->args, obj); + + /* send the answer */ + args_send_success(args, obj); +} + +/* + * Treats the redirect answer of the query 'curl' of 'content' of 'size' bytes for the 'args' + */ +static void perform_redirect(struct args *args, CURL *curl, const char *content, size_t size) +{ + /* TODO: handle redirection for the normal flow */ + return args_send_error(args, "unhandled redirection", content); +} + +/* + * Handle the result of the query 'curl' of 'status'. If a data is returned, it is available in + * 'content' of 'size' bytes. + * When 'status' is 0, an error occured. Otherwise, 'statu' isn't zero. + */ +static void perform_callback(void *closure, int status, CURL *curl, const char *content, size_t size) +{ + long code; + struct args *args = closure; + + /* query error ? */ + if (!status + || curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code) != CURLE_OK) + return args_send_error(args, "query error", NULL); /* TODO: IMPROVE? */ + + /* get the returned code */ + switch (code) { + case 200: + return perform_result(args, curl, content, size); + case 302: + return perform_redirect(args, curl, content, size); + case 400: + case 401: + return args_send_error(args, content ? : "returned code error", content ? "returned code error" : content); + default: + return args_send_error(args, content ? : "unexpected code error", content ? "unexpected code error" : content); + } +} + +/* + * Main function for performing OAuth2/OpenId Connect transactions. + * The structure 'args' is filled with the needed values: + * Application data as json object, IDP data as json object + * Contextual arguments for the transaction. + * 'endpoint' must be the name of an endpoint in the context of 'args'. + * 'operation' is the value that will get either response_type or grant_type, + * depending on the nature of the required parameters 'mandatory'. + * 'mandatory' designates the mandatory parameters. + * 'optional' designate the optional parameters. + */ +static void perform(struct args *args, const char *endpoint, const char *operation, uint32_t mandatory, uint32_t optional) +{ + int post; + const char *url, *type; + CURL *curl; + + /* set the flags */ + args->mandatory = mandatory; + args->all = mandatory | optional; + + /* get the endpoint */ + url = args_string(args, endpoint); + if (!url) + return args_send_error(args, "No endpoint", endpoint); + + /* get the operation type */ + if ((mandatory & PARAM(Response_Type)) == PARAM(Response_Type)) { + type = "response_type"; + post = 0; /* can be 1 sometimes so not risk here */ + } else if ((mandatory & PARAM(Grant_Type)) == PARAM(Grant_Type)) { + type = "grant_type"; + post = 1; /* must be 1 */ + } else + return args_send_error(args, "Unexpected operation Type", NULL); + + json_object_object_add(args->query, type, json_object_new_string(operation)); + + /* get the arguments */ + if (1 + && args_add(args, PARAM(Access_Token), "access_token", 1) + && args_add(args, PARAM(Acr_Values), "acr_values", 1) + && args_add(args, PARAM(Authorization), "authorization", 0) + && args_add(args, PARAM(Client_Id), "client_id", 1) + && args_add(args, PARAM(Client_Secret), "client_secret", 1) + && args_add(args, PARAM(Code), "code", 1) + && args_add(args, PARAM(Display), "display", 1) + && args_add(args, PARAM(Expires_In), "expires_in", 1) + && args_add(args, PARAM(Id_Token_Hint), "id_token_hint", 1) + && args_add(args, PARAM(Login_Hint), "login_hint", 1) + && args_add(args, PARAM(Max_Age), "max_age", 1) + && args_add(args, PARAM(Nonce), "nonce", 1) + && args_add(args, PARAM(Password), "password", 1) + && args_add(args, PARAM(Prompt), "prompt", 1) + && args_add(args, PARAM(Redirect_Uri), "redirect_uri", 1) + && args_add(args, PARAM(Refresh_Token), "refresh_token", 1) + && args_add(args, PARAM(Scope), "scope", 1) + && args_add(args, PARAM(State), "state", 1) + && args_add(args, PARAM(Token_Type), "token_type", 1) + && args_add(args, PARAM(Ui_Locales), "ui_locales", 1) + && args_add(args, PARAM(Username), "username", 1) + ) { + /* creates the curl query */ + curl = curl_json(url, post, args->header, args->query); + if (!curl) + return args_send_error(args, "out of memory", NULL); + + /* release data */ + args_unlock(args); + + /* perform the request to the server */ + curl_wrap_do(curl, perform_callback, args); + } +} + +/* perform a grant of flow Flow_Resource_Owner_Password_Credentials_Grant */ +static void grant_owner_password(struct args *args) +{ + perform(args, string_token_endpoint, "password", + PARAM(Grant_Type) | PARAM(Username) | PARAM(Password), + PARAM(Scope) | PARAM(Authorization) + ); +} + +/* perform a grant of flow Flow_Client_Credentials_Grant */ +static void grant_client_credentials(struct args *args) +{ + perform(args, string_token_endpoint, "client_credentials", + PARAM(Grant_Type), + PARAM(Scope) | PARAM(Authorization) + ); +} + +/* switches the requests depending on 'flow' */ +static void grant(struct args *args, enum oidc_grant_flow flow) +{ + /* ensure args is valid */ + if (!args) + return; + + /* process for flow */ + switch(flow) { + + case Flow_Resource_Owner_Password_Credentials_Grant: + grant_owner_password(args); + break; + + case Flow_Client_Credentials_Grant: + grant_client_credentials(args); + break; + + case Flow_Authorization_Code_Grant: + case Flow_Implicit_Grant: + case Flow_Extension_Grant: + args_send_error(args, "Unsupported flow", NULL); + break; + + case Flow_Invalid: + default: + args_send_error(args, "Invalid flow", NULL); + break; + } +} + +/* + * Initiates a grant with the given 'flow'. + * 'appli' and 'idp' designates the appli and the idp that have been recorded. + * when idp == NULL or idp == "", the default idp of 'appli' is used. + * 'args' contains parameters expected in plus for the grant transaction. + * 'cb' describes the callback actions that are called before + * the function returns. + */ +void oidc_grant(const char *appli, const char *idp, struct json_object *args, const struct oidc_grant_cb *cb, enum oidc_grant_flow flow) +{ + grant(mkargs(appli, idp, args, cb), flow); +} + +/* + * Like oidc_grant for flow Flow_Resource_Owner_Password_Credentials_Grant + */ +void oidc_grant_owner_password(const char *appli, const char *idp, struct json_object *args, const struct oidc_grant_cb *cb) +{ + grant(mkargs(appli, idp, args, cb), Flow_Resource_Owner_Password_Credentials_Grant); +} + +/* + * Like oidc_grant for flow Flow_Client_Credentials_Grant + */ +void oidc_grant_client_credentials(const char *appli, const char *idp, struct json_object *args, const struct oidc_grant_cb *cb) +{ + grant(mkargs(appli, idp, args, cb), Flow_Client_Credentials_Grant); +} + +/* + * Refreshes the 'token' for the 'appli' and the 'idp'. + * 'cb' describes the callback actions that are called before + * the function returns. + */ +void oidc_token_refresh(const char *appli, const char *idp, struct json_object *token, const struct oidc_grant_cb *cb) +{ + struct args *args; + + args = mkargs(appli, idp, token, cb); + if (!args) + return; + args->refresh = 1; + perform(args, string_token_endpoint, "refresh_token", + PARAM(Grant_Type) | PARAM(Refresh_Token), + PARAM(Scope) | PARAM(Authorization) + ); +} + +/* + * Adds the header "authorisation" with the bearer access_token of 'token'. + * Return 1 on case of success or 0 otherwise. + */ +int oidc_add_bearer(CURL *curl, struct json_object *token) +{ + struct json_object *bearer; + + return json_object_object_get_ex(token, "authorization", &bearer) + && curl_wrap_add_header_value(curl, "authorization", json_object_get_string(bearer)); +} + + diff --git a/src/oidc-agent.h b/src/oidc-agent.h new file mode 100644 index 0000000..de5918d --- /dev/null +++ b/src/oidc-agent.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +struct json_object; +#include + +/***************** IDP **************************/ + +extern int oidc_idp_set( + const char *name, + struct json_object *desc + ); + +extern int oidc_idp_exists( + const char *name + ); + +extern void oidc_idp_delete( + const char *name + ); + + +/***************** APPLI **************************/ + +extern int oidc_appli_set( + const char *name, + const char *idp, + struct json_object *desc, + int make_default + ); + +extern int oidc_appli_exists( + const char *name + ); + +extern int oidc_appli_has_idp( + const char *name, + const char *idp + ); + +extern int oidc_appli_set_default_idp( + const char *name, + const char *idp + ); + +extern void oidc_appli_delete( + const char *name + ); + +/***************** APPLI **************************/ + +struct oidc_grant_cb +{ + void *closure; + void (*success)(void *closure, struct json_object *result); + void (*error)(void *closure, const char *message, const char *indice); +}; + +enum oidc_grant_flow +{ + Flow_Invalid, + Flow_Authorization_Code_Grant, + Flow_Implicit_Grant, + Flow_Resource_Owner_Password_Credentials_Grant, + Flow_Client_Credentials_Grant, + Flow_Extension_Grant +}; + + +extern void oidc_grant( + const char *appli, + const char *idp, + struct json_object *args, + const struct oidc_grant_cb *cb, + enum oidc_grant_flow flow + ); + +extern void oidc_grant_owner_password( + const char *appli, + const char *idp, + struct json_object *args, + const struct oidc_grant_cb *cb + ); + +extern void oidc_grant_client_credentials( + const char *appli, + const char *idp, + struct json_object *args, + const struct oidc_grant_cb *cb + ); + +extern void oidc_token_refresh( + const char *appli, + const char *idp, + struct json_object *token, + const struct oidc_grant_cb *cb + ); + +extern int oidc_add_bearer( + CURL *curl, + struct json_object *token + ); + diff --git a/src/test-aia-uds-bluez.c b/src/test-aia-uds-bluez.c new file mode 100644 index 0000000..4a084a5 --- /dev/null +++ b/src/test-aia-uds-bluez.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include + +#include "aia-uds-bluez.h" + + +void p(const char *tag, const struct aia_uds_value *value) +{ + printf("%10s %c %.*s\n", tag, value->changed?'*':' ', (int)value->length, value->data); +} + +void onchg(const struct aia_uds *uds) +{ + printf("\n"); + p("first name", &uds->first_name); + p("last name", &uds->last_name); + p("email", &uds->email); + p("language", &uds->language); +} + +int main() +{ + sd_bus *bus; + sd_id128_t id; + int rc; + + aia_uds_set_on_change(onchg); + rc = sd_bus_default_system(&bus); + rc = sd_id128_randomize(&id); + rc = sd_bus_set_server(bus, 1, id); + rc = sd_bus_start(bus); + rc = aia_uds_init(bus); + rc = aia_uds_advise(1, NULL, NULL); + for (;;) { + rc = sd_bus_process(bus, NULL); + if (rc < 0) + break; + else if (rc == 0) + rc = sd_bus_wait(bus, (uint64_t) -1); + } +} + diff --git a/uds-ble-id-init-service/CMakeLists.txt b/uds-ble-id-init-service/CMakeLists.txt deleted file mode 100644 index 14b6d55..0000000 --- a/uds-ble-id-init-service/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -########################################################################### -# Copyright 2017 IoT.bzh -# -# author: Jose Bollo -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - -cmake_minimum_required(VERSION 3.6) -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/) -include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) diff --git a/uds-ble-id-init-service/btle-advise.service b/uds-ble-id-init-service/btle-advise.service deleted file mode 100644 index 1a34237..0000000 --- a/uds-ble-id-init-service/btle-advise.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=activates advertising of BlueTooth Low-Energy on hci0 - -[Service] -Type=oneshot -ExecStart=/bin/bash -c "rfkill list|sed '/[bB]luetooth/!d;s/:.*//'|xargs -n1 rfkill unblock" -ExecStart=/bin/bash -c "hcitool dev|tail -n+2|cut -f2|xargs -n1 -IX hciconfig X leadv" - diff --git a/uds-ble-id-init-service/btle-advise.timer b/uds-ble-id-init-service/btle-advise.timer deleted file mode 100644 index b02594c..0000000 --- a/uds-ble-id-init-service/btle-advise.timer +++ /dev/null @@ -1,6 +0,0 @@ -[Unit] -Description=Ensure BLE advise is up - -[Timer] -AccuracySec=1min -OnUnitActiveSec=1min diff --git a/uds-ble-id-init-service/conf.d/cmake/config.cmake b/uds-ble-id-init-service/conf.d/cmake/config.cmake deleted file mode 100644 index 4ea1d79..0000000 --- a/uds-ble-id-init-service/conf.d/cmake/config.cmake +++ /dev/null @@ -1,201 +0,0 @@ -########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh -# -# author: Fulup Ar Foll -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - -# Project Info -# ------------------ -set(PROJECT_NAME uds-ble-id-init) -set(PROJECT_VERSION "0.1") -set(PROJECT_PRETTY_NAME "AGL Identitity initiator above BLE's User Data Service") -set(PROJECT_DESCRIPTION "AGL Identitity initiator above BLE's User Data Service") -set(PROJECT_URL "https://github.com/iotbzh/aia-binding") -set(PROJECT_ICON "icon.png") -set(PROJECT_AUTHOR "José Bollo") -set(PROJECT_AUTHOR_MAIL "jose.bollo@iot.bzh") -set(PROJECT_LICENSE "APL2.0") -set(PROJECT_LANGUAGES,"C") - -# Where are stored default templates files from submodule or subtree app-templates in your project tree -# relative to the root project directory -set(PROJECT_APP_TEMPLATES_DIR "../conf.d/app-templates") - -# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain -# but used and must be built and linked. -# set(PROJECT_LIBDIR "libs") - -# Where are stored data for your application. Pictures, static resources must be placed in that folder. -# set(PROJECT_RESOURCES "data") - -# Which directories inspect to find CMakeLists.txt target files -# set(PROJECT_SRC_DIR_PATTERN "*") - -# Compilation Mode (DEBUG, RELEASE) -# ---------------------------------- - -# Kernel selection if needed. You can choose between a -# mandatory version to impose a minimal version. -# Or check Kernel minimal version and just print a Warning -# about missing features and define a preprocessor variable -# to be used as preprocessor condition in code to disable -# incompatibles features. Preprocessor define is named -# KERNEL_MINIMAL_VERSION_OK. -# -# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and -# Yocto SDK Kernel version. -# ----------------------------------------------- -#set (kernel_mandatory_version 4.8) -#set (kernel_minimal_version 4.8) - -# Compiler selection if needed. Impose a minimal version. -# ----------------------------------------------- -set (gcc_minimal_version 4.9) - -# PKG_CONFIG required packages -# ----------------------------- -set (PKG_REQUIRED_LIST - json-c - libsystemd>=222 - afb-daemon -) - -# Prefix path where will be installed the files -# Default: /usr/local (need root permission to write in) -# ------------------------------------------------------ -#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) - -# Customize link option -# ----------------------------- -#list(APPEND link_libraries -an-option) - -# Compilation options definition -# Use CMake generator expressions to specify only for a specific language -# Values are prefilled with default options that is currently used. -# Either separate options with ";", or each options must be quoted separately -# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED ! -# ---------------------------------------------------------------------------- -#set(COMPILE_OPTIONS -# -Wall -# -Wextra -# -Wconversion -# -Wno-unused-parameter -# -Wno-sign-compare -# -Wno-sign-conversion -# -Werror=maybe-uninitialized -# -Werror=implicit-function-declaration -# -ffunction-sections -# -fdata-sections -# -fPIC -# CACHE STRING "Compilation flags") -#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.") -#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.") -#set(PROFILING_COMPILE_OPTIONS -# -g -# -O0 -# -pg -# -Wp,-U_FORTIFY_SOURCE -# CACHE STRING "Compilation flags for PROFILING build type.") -#set(DEBUG_COMPILE_OPTIONS -# -g -# -ggdb -# -Wp,-U_FORTIFY_SOURCE -# CACHE STRING "Compilation flags for DEBUG build type.") -#set(CCOV_COMPILE_OPTIONS -# -g -# -O2 -# --coverage -# CACHE STRING "Compilation flags for CCOV build type.") -#set(RELEASE_COMPILE_OPTIONS -# -g -# -O2 -# CACHE STRING "Compilation flags for RELEASE build type.") - -# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] -# --------------------------------------------------------------------- -set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) -set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) - -# Optional location for config.xml.in -# ----------------------------------- -#set(WIDGET_ICON conf.d/wgt/${PROJECT_ICON} CACHE PATH "Path to the widget icon") -set(WIDGET_CONFIG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)") - -# Mandatory widget Mimetype specification of the main unit -# -------------------------------------------------------------------------- -# Choose between : -#- text/html : HTML application, -# content.src designates the home page of the application -# -#- application/vnd.agl.native : AGL compatible native, -# content.src designates the relative path of the binary. -# -# - application/vnd.agl.service: AGL service, content.src is not used. -# -#- ***application/x-executable***: Native application, -# content.src designates the relative path of the binary. -# For such application, only security setup is made. -# -set(WIDGET_TYPE application/vnd.agl.service) - -# Mandatory Widget entry point file of the main unit -# -------------------------------------------------------------- -# This is the file that will be executed, loaded, -# at launch time by the application framework. -# -set(WIDGET_ENTRY_POINT irrelevant) - -# Optional dependencies order -# --------------------------- -#set(EXTRA_DEPENDENCIES_ORDER) - -# Optional Extra global include path -# ----------------------------------- -#set(EXTRA_INCLUDE_DIRS) - -# Optional extra libraries -# ------------------------- -#set(EXTRA_LINK_LIBRARIES) - -# Optional force binding Linking flag -# ------------------------------------ -# set(BINDINGS_LINK_FLAG LinkOptions ) - -# Optional force package prefix generation, like widget -# ----------------------------------------------------- -# set(PKG_PREFIX DestinationPath) - -# Optional Application Framework security token -# and port use for remote debugging. -#------------------------------------------------------------ -set(AFB_TOKEN "" CACHE PATH "Default binder security token") -set(AFB_REMPORT "1234" CACHE PATH "Default binder listening port") - -# Print a helper message when every thing is finished -# ---------------------------------------------------- -set(CLOSING_MESSAGE "Typical binding launch: afb-daemon --port=${AFB_REMPORT} --workdir=${CMAKE_BINARY_DIR}/package --ldpaths=lib --roothttp=htdocs --token=\"${AFB_TOKEN}\" --tracereq=common --verbose") -set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") - -# Optional schema validator about now only XML, LUA and JSON -# are supported -#------------------------------------------------------------ -#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler") -#set(XML_CHECKER "xmllint" CACHE STRING "XML linter") -#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter") - -# This include is mandatory and MUST happens at the end -# of this file, else you expose you to unexpected behavior -# ----------------------------------------------------------- -include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake) diff --git a/uds-ble-id-init-service/conf.d/wgt/config.xml.in b/uds-ble-id-init-service/conf.d/wgt/config.xml.in deleted file mode 100644 index 0ccd9b5..0000000 --- a/uds-ble-id-init-service/conf.d/wgt/config.xml.in +++ /dev/null @@ -1,19 +0,0 @@ - - - @PROJECT_NAME@ - - - @PROJECT_DESCRIPTION@ - @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> - @PROJECT_LICENSE@ - - - - - - - - - - - diff --git a/uds-ble-id-init-service/src/CMakeLists.txt b/uds-ble-id-init-service/src/CMakeLists.txt deleted file mode 100644 index 97e229a..0000000 --- a/uds-ble-id-init-service/src/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -########################################################################### -# Copyright 2017 IoT.bzh -# -# author: Jose Bollo -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - -PROJECT_TARGET_ADD(afb-uds-ble-id-init-binding) - -add_library(afb-uds-ble-id-init-binding MODULE - uds-ble-id-init-service.c - aia-uds-bluez.c -) - -set_target_properties(afb-uds-ble-id-init-binding PROPERTIES - LABELS "BINDING" - PREFIX "" - COMPILE_FLAGS "${libsystemd_CFLAGS}" - INCLUDE_DIRECTORIES "${libsystemd_INCLUDE_DIRS}" - LINK_FLAGS "${BINDINGS_LINK_FLAG}" - LINK_LIBRARIES "${libsystemd_LIBRARIES}" - OUTPUT_NAME "${TARGET_NAME}" -) - diff --git a/uds-ble-id-init-service/src/aia-uds-bluez.c b/uds-ble-id-init-service/src/aia-uds-bluez.c deleted file mode 100644 index 35ba61d..0000000 --- a/uds-ble-id-init-service/src/aia-uds-bluez.c +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (C) 2015, 2016, 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define _GNU_SOURCE - -#include -#include -#include -#include - -#include - -#include "aia-uds-bluez.h" - -#define ROOT "/uds" - -#define ITF_SERVICE "org.bluez.GattService1" -#define ITF_CHARACTERISTIC "org.bluez.GattCharacteristic1" -#define ITF_DESCRIPTOR "org.bluez.GattDescriptor1" - -/****** internal types **********/ - -struct item -{ - const char *uuid; - const char *path; - struct item *parent; - struct aia_uds_value *value; -}; - -/****** internal data **********/ - -static sd_bus *busini; -static int activation; - -static aia_uds_on_change on_change_callback; - -static struct aia_uds uds; - -static struct aia_uds uds_descs = -{ - .first_name = { .data = "First name of the user" }, - .last_name = { .data = "Last name of the user" }, - .email = { .data = "Email of the user" }, - .language = { .data = "The Language definition is based on ISO639-1" } -}; - -static struct aia_uds_value woutf8 = { .data = "\031\000\047\000\001\000\000", .length = 7 }; - -static struct item services[1] = -{ - { - .uuid = "0000181C-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0" - } -}; - -static struct item characteristics[4] = -{ - { - .uuid = "00002A8A-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char0", - .parent = &services[0], - .value = &uds.first_name - }, { - .uuid = "00002A90-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char1", - .parent = &services[0], - .value = &uds.last_name - }, { - .uuid = "00002A87-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char2", - .parent = &services[0], - .value = &uds.email - }, { - .uuid = "00002AA2-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char3", - .parent = &services[0], - .value = &uds.language - } -}; - -static struct item descriptors[8] = -{ - { - .uuid = "00002901-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char0/desc0", - .parent = &characteristics[0], - .value = &uds_descs.first_name - }, { - .uuid = "00002904-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char0/desc1", - .parent = &characteristics[0], - .value = &woutf8 - }, { - .uuid = "00002901-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char1/desc0", - .parent = &characteristics[1], - .value = &uds_descs.last_name - }, { - .uuid = "00002904-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char1/desc1", - .parent = &characteristics[1], - .value = &woutf8 - }, { - .uuid = "00002901-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char2/desc0", - .parent = &characteristics[2], - .value = &uds_descs.email - }, { - .uuid = "00002904-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char2/desc1", - .parent = &characteristics[2], - .value = &woutf8 - }, { - .uuid = "00002901-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char3/desc0", - .parent = &characteristics[3], - .value = &uds_descs.language - }, { - .uuid = "00002904-0000-1000-8000-00805f9b34fb", - .path = ROOT"/service0/char3/desc1", - .parent = &characteristics[3], - .value = &woutf8 - } -}; - -/****** utility ***********/ - -static uint16_t get_offset(sd_bus_message *m) -{ - uint16_t offset; - const char *key; - - if (0 < sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) { - while (sd_bus_message_read_basic(m, 's', &key) > 0) { - if (strcmp(key,"offset")) - sd_bus_message_skip(m, "v"); - else { - if (sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "q")) { - sd_bus_message_read_basic(m, 'q', &offset); - sd_bus_message_exit_container(m); - return offset; - } - } - } - sd_bus_message_exit_container(m); - } - return 0; -} - -static int message_append_strings(sd_bus_message *m, const char **s) -{ - int rc = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, "s"); - while (rc >= 0 && *s) - rc = sd_bus_message_append_basic(m, 's', *s++); - if (rc >= 0) - rc = sd_bus_message_close_container(m); - return rc; -} - -/****** common callbacks ***********/ - -static int get_uuid( - struct sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) -{ - struct item *item = userdata; - return sd_bus_message_append_basic(reply, 's', item->uuid); -} - -static int get_parent( - struct sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) -{ - struct item *item = userdata; - return sd_bus_message_append_basic(reply, 'o', item->parent->path); -} - -static int get_children( - struct sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) -{ - size_t i, n; - int rc; - - rc = sd_bus_message_open_container(reply, SD_BUS_TYPE_ARRAY, "o"); - n = sizeof characteristics / sizeof *characteristics; - i = 0; - while (rc >= 0 && i < n) { - if (characteristics[i].parent == userdata) - rc = sd_bus_message_append_basic(reply, 'o', characteristics[i].path); - i++; - } - n = sizeof descriptors / sizeof *descriptors; - i = 0; - while (rc >= 0 && i < n) { - if (descriptors[i].parent == userdata) - rc = sd_bus_message_append_basic(reply, 'o', descriptors[i].path); - i++; - } - if (rc >= 0) - rc = sd_bus_message_close_container(reply); - return rc; -} - -static int read_value_offset( - struct item *item, - size_t offset, - sd_bus_message *reply) -{ - struct aia_uds_value *value = item->value; - const char *data = value ? value->data : NULL; - size_t size = !value ? 0 : value->length ? value->length : data ? strlen(data) : 0; - return sd_bus_message_append_array(reply, 'y', &data[offset], offset > size ? 0 : size - offset); -} - -static int read_value( - sd_bus_message *m, - void *userdata, - sd_bus_error *ret_error) -{ - int rc; - sd_bus_message *r; - - rc = sd_bus_message_new_method_return(m, &r); - rc = read_value_offset(userdata, get_offset(m), r); - rc = sd_bus_send(NULL, r, NULL); - return 1; -} - -static int write_value_offset( - struct item *item, - size_t offset, - const char *buffer, - size_t length) -{ - struct aia_uds_value *value = item->value; - const char *data = value ? value->data : NULL; - size_t size = !value ? 0 : value->length ? value->length : data ? strlen(data) : 0; - - - char *next = malloc(offset + length + 1); - if (!next) - return -ENOMEM; - if (offset) { - if (size >= offset) - memcpy(next, data, offset); - else { - memcpy(next, data, size); - memset(&next[size], 0, offset - size); - } - } - memcpy(&next[offset], buffer, length); - next[offset + length] = 0; - value->data = next; - value->length = offset + length; - free((char*)data); - - return 0; -} - -static int write_value( - sd_bus_message *m, - void *userdata, - sd_bus_error *ret_error) -{ - int rc; - struct item *item = userdata; - const void *data; - size_t size; - sd_bus_message *r; - - rc = sd_bus_message_read_array(m, 'y', &data, &size); - rc = write_value_offset(item, get_offset(m), data, size); - - rc = sd_bus_message_new_method_return(m, &r); - rc = sd_bus_send(NULL, r, NULL); - - sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), item->path, ITF_CHARACTERISTIC, "Value", NULL); - - if (on_change_callback) { - item->value->changed = 1; - on_change_callback(&uds); - item->value->changed = 0; - } - - return 1; -} - -static int get_value( - struct sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) -{ - return read_value_offset(userdata, 0, reply); -} - -static int get_true( - struct sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) -{ - int v = 1; - return sd_bus_message_append_basic(reply, 'b', &v); -} - -static int get_false( - struct sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) -{ - int v = 0; - return sd_bus_message_append_basic(reply, 'b', &v); -} - -static int not_supported( - sd_bus_message *m, - void *userdata, - sd_bus_error *ret_error) -{ - return sd_bus_reply_method_errorf(m, "org.bluez.Error.NotSupported", "not supported"); -} - -static int not_permitted( - sd_bus_message *m, - void *userdata, - sd_bus_error *ret_error) -{ - return sd_bus_reply_method_errorf(m, "org.bluez.Error.NotPermitted", "not permitted"); -} - -static int failed( - sd_bus_message *m, - void *userdata, - sd_bus_error *ret_error) -{ - return sd_bus_reply_method_errorf(m, "org.bluez.Error.Failed", "failed"); -} - -/****** service's callbacks ***********/ - -static int get_srv_includes( - struct sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) -{ - int rc = sd_bus_message_open_container(reply, SD_BUS_TYPE_ARRAY, "o"); - if (rc >= 0) - rc = sd_bus_message_close_container(reply); - return rc; -} - -/****** characteristic's callbacks ***********/ - -static int get_char_flags( - struct sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) -{ - static const char *flags[] = { "read", "write", NULL }; - return message_append_strings(reply, flags); -} - -/****** descriptor's callbacks ***********/ - -static int get_desc_flags( - struct sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) -{ - static const char *flags[] = { "read", NULL }; - return message_append_strings(reply, flags); -} - -/****** description ***********/ - -static struct sd_bus_vtable vservice[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("UUID", "s", get_uuid, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Primary", "b", get_true, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Includes", "ao", get_srv_includes, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Characteristics", "ao", get_children, 0, SD_BUS_VTABLE_PROPERTY_CONST), - - SD_BUS_VTABLE_END -}; - - -static struct sd_bus_vtable vcharacteristic[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_METHOD("ReadValue", "a{sv}", "ay", read_value, 0), - SD_BUS_METHOD("WriteValue", "aya{sv}", "", write_value, 0), - SD_BUS_METHOD("StartNotify", "", "", not_supported, 0), - SD_BUS_METHOD("StopNotify", "", "", failed, 0), - - SD_BUS_PROPERTY("UUID", "s", get_uuid, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Service", "o", get_parent, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Value", "ay", get_value, 0, 0), - SD_BUS_PROPERTY("WriteAcquired", "b", get_false, 0, 0), - SD_BUS_PROPERTY("NotifyAcquired", "b", get_false, 0, 0), - SD_BUS_PROPERTY("Notifying", "b", get_false, 0, 0), - SD_BUS_PROPERTY("Flags", "as", get_char_flags, 0, 0), - SD_BUS_PROPERTY("Descriptors", "ao", get_children, 0, 0), - - SD_BUS_VTABLE_END -}; - - -static struct sd_bus_vtable vdescriptor[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_METHOD("ReadValue", "a{sv}", "ay", read_value, 0), - SD_BUS_METHOD("WriteValue", "aya{sv}", "", not_permitted, 0), - - SD_BUS_PROPERTY("UUID", "s", get_uuid, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Characteristic", "o", get_parent, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Value", "ay", get_value, 0, 0), - SD_BUS_PROPERTY("Flags", "as", get_desc_flags, 0, 0), - - SD_BUS_VTABLE_END -}; - - -/******** Integration ***************/ - -aia_uds_on_change aia_uds_set_on_change(aia_uds_on_change callback) -{ - aia_uds_on_change prev = on_change_callback; - on_change_callback = callback; - return prev; -} - -int aia_uds_init(struct sd_bus *bus) -{ - int rc; - - if (busini) - return 0; - - rc = sd_bus_add_object_manager(bus, NULL, ROOT); - - rc = sd_bus_add_object_vtable(bus, NULL, services[0].path, - ITF_SERVICE, vservice, &services[0]); - - rc = sd_bus_add_object_vtable(bus, NULL, characteristics[0].path, - ITF_CHARACTERISTIC, vcharacteristic, &characteristics[0]); - rc = sd_bus_add_object_vtable(bus, NULL, characteristics[1].path, - ITF_CHARACTERISTIC, vcharacteristic, &characteristics[1]); - rc = sd_bus_add_object_vtable(bus, NULL, characteristics[2].path, - ITF_CHARACTERISTIC, vcharacteristic, &characteristics[2]); - rc = sd_bus_add_object_vtable(bus, NULL, characteristics[3].path, - ITF_CHARACTERISTIC, vcharacteristic, &characteristics[3]); - - rc = sd_bus_add_object_vtable(bus, NULL, descriptors[0].path, - ITF_DESCRIPTOR, vdescriptor, &descriptors[0]); - rc = sd_bus_add_object_vtable(bus, NULL, descriptors[1].path, - ITF_DESCRIPTOR, vdescriptor, &descriptors[1]); - rc = sd_bus_add_object_vtable(bus, NULL, descriptors[2].path, - ITF_DESCRIPTOR, vdescriptor, &descriptors[2]); - rc = sd_bus_add_object_vtable(bus, NULL, descriptors[3].path, - ITF_DESCRIPTOR, vdescriptor, &descriptors[3]); - rc = sd_bus_add_object_vtable(bus, NULL, descriptors[4].path, - ITF_DESCRIPTOR, vdescriptor, &descriptors[4]); - rc = sd_bus_add_object_vtable(bus, NULL, descriptors[5].path, - ITF_DESCRIPTOR, vdescriptor, &descriptors[5]); - rc = sd_bus_add_object_vtable(bus, NULL, descriptors[6].path, - ITF_DESCRIPTOR, vdescriptor, &descriptors[6]); - rc = sd_bus_add_object_vtable(bus, NULL, descriptors[7].path, - ITF_DESCRIPTOR, vdescriptor, &descriptors[7]); - - busini = sd_bus_ref(bus); - return 0; -} - -struct cb { - void *callback; - void *closure; - int onoff; -}; - -static struct cb *alloccb(void *callback, void *closure, int onoff) -{ - struct cb *cb = malloc(sizeof *cb); - if (cb) { - cb->callback = callback; - cb->closure = closure; - } - return cb; -} - -static int register_uds_cb(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) -{ - struct cb *cb = userdata; - void (*callback)(void *closure, int error, int state); - int iserr; - - iserr = sd_bus_message_is_method_error(m, NULL); - if (!iserr) - activation = cb->onoff; - callback = cb->callback; - if (callback) - callback(cb->closure, iserr, activation); - free(cb); - - return 1; -} - -static int register_uds(int onoff, void *data) -{ - int rc; - sd_bus_message *m; - - rc = sd_bus_message_new_method_call(busini, &m, "org.bluez", - "/org/bluez/hci0", "org.bluez.GattManager1", - onoff ? "RegisterApplication" : "UnregisterApplication" ); - rc = sd_bus_message_append_basic(m, 'o', ROOT); - if (onoff) { - rc = sd_bus_message_open_container(m, 'a', "{sv}"); - rc = sd_bus_message_close_container(m); - } - rc = sd_bus_call_async(busini, NULL, m, register_uds_cb, data, 5*1000*1000); - sd_bus_message_unref(m); - return rc; -} - -int aia_uds_activate(int onoff, void (*callback)(void *closure, int error, int state), void *closure) -{ - int rc; - struct cb *cb; - - if (!busini) - return -EINVAL; - - onoff = !!onoff; - if (activation == onoff) - return 0; - - cb = alloccb(callback, closure, onoff); - if (!cb) - return -ENOMEM; - - rc = register_uds(onoff, cb); - if (rc < 0) - free(cb); - - return rc < 0 ? rc : 1; -} - -int aia_uds_advise(int onoff, void (*callback)(void *closure, int error, int state), void *closure) -{ - return aia_uds_activate(onoff, callback, closure); -} - diff --git a/uds-ble-id-init-service/src/aia-uds-bluez.h b/uds-ble-id-init-service/src/aia-uds-bluez.h deleted file mode 100644 index 856da4e..0000000 --- a/uds-ble-id-init-service/src/aia-uds-bluez.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2015, 2016, 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define _GNU_SOURCE - - -#pragma once - -struct aia_uds_value -{ - const char *data; - size_t length; - int changed; -}; - -struct aia_uds -{ - struct aia_uds_value first_name; - struct aia_uds_value last_name; - struct aia_uds_value email; - struct aia_uds_value language; -}; - -typedef void (*aia_uds_on_change)(const struct aia_uds *); - -extern aia_uds_on_change aia_uds_set_on_change(aia_uds_on_change callback); - -extern int aia_uds_activate(int onoff, void (*callback)(void *closure, int error, int state), void *closure); - -extern int aia_uds_advise(int onoff, void (*callback)(void *closure, int error, int state), void *closure); - -extern int aia_uds_init(struct sd_bus *bus); - diff --git a/uds-ble-id-init-service/src/uds-ble-id-init-service.c b/uds-ble-id-init-service/src/uds-ble-id-init-service.c deleted file mode 100644 index f887963..0000000 --- a/uds-ble-id-init-service/src/uds-ble-id-init-service.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2015, 2016, 2017 "IoT.bzh" - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AFB_BINDING_VERSION 2 -#include - -#include "aia-uds-bluez.h" - -#if !defined(AUTO_START_ADVISE) -#define AUTO_START_ADVISE 1 -#endif - -static int advising; - -static struct afb_event event; - -static int autoadvise = AUTO_START_ADVISE; - -/****************************************************************/ - -static void on_uds_change(const struct aia_uds *uds) -{ - struct json_object *object; - - AFB_INFO("UDS changed" - " first-name%s[%.*s]" - " last-name%s[%.*s]" - " email%s[%.*s]" - " language%s[%.*s]", - uds->first_name.changed ? "*" : "", (int)uds->first_name.length, uds->first_name.data ?:"", - uds->last_name.changed ? "*" : "", (int)uds->last_name.length, uds->last_name.data ?:"", - uds->email.changed ? "*" : "", (int)uds->email.length, uds->email.data ?:"", - uds->language.changed ? "*" : "", (int)uds->language.length, uds->language.data ?:""); - - if (uds->email.changed) { - object = json_object_new_object(); - json_object_object_add(object, "incoming", json_object_new_string(uds->email.data)); - afb_event_push(event, object); - } -} - -static void start (struct afb_req request) -{ - int rc; - - if (!advising) { - rc = aia_uds_advise(1, NULL, NULL); - if (rc < 0) { -/* -TODO: solve the issue - afb_req_fail(request, "failed", "start scan failed"); - return; -*/ - AFB_ERROR("Ignoring scan start failed, because probably already in progress"); - } - } - advising = advising + 1; - afb_req_subscribe(request, event); - afb_req_success(request, NULL, NULL); -} - - -static void stop (struct afb_req request) -{ - if (advising) { - advising = advising - 1; - if (!advising) - aia_uds_advise(0, NULL, NULL); - } - afb_req_success(request, NULL, NULL); -} - -static int init() -{ - sd_bus *bus; - int rc; - - bus = afb_daemon_get_system_bus(); - rc = bus ? aia_uds_init(bus) : -ENOTSUP; - if (rc < 0) { - errno = -rc; - return -1; - } - - aia_uds_set_on_change(on_uds_change); - - event = afb_daemon_make_event("event"); - if (!afb_event_is_valid(event)) - return -1; - - rc = aia_uds_advise(autoadvise, NULL, NULL); - advising = autoadvise && rc >= 0; - return rc < 0 ? rc : 0; -} - - -// NOTE: this sample does not use session to keep test a basic as possible -// in real application most APIs should be protected with AFB_SESSION_CHECK -static const struct afb_verb_v2 verbs[]= -{ - {"start" , start, NULL, "start User Data Service", AFB_SESSION_NONE }, - {"stop" , stop , NULL, "stop User Data Service" , AFB_SESSION_NONE }, - {NULL} -}; - -const struct afb_binding_v2 afbBindingV2 = -{ - .api = "uds-ble-init-id", - .specification = NULL, - .info = "AGL Identitity initiator above BLE's User Data Service", - .verbs = verbs, - .preinit = NULL, - .init = init, - .onevent = NULL, - .noconcurrency = 1 -}; - -/* vim: set colorcolumn=80: */ - -- cgit 1.2.3-korg