From a5888f5402ee2cf93b4a7090234d6aec2a97f8f0 Mon Sep 17 00:00:00 2001 From: WonYoung Choi Date: Wed, 18 Nov 2015 09:42:31 +0900 Subject: [PATCH] Introduce privileges and arguments-validator module Change-Id: Id647048c6713ec8cad778412774bfc9b3402fdcc --- CMakeLists.txt | 2 + appfw/CMakeLists.txt | 2 +- appfw/lib/appfw.js | 2 +- arguments-validator/CMakeLists.txt | 12 + arguments-validator/package.json | 10 + arguments-validator/validator.js | 584 ++++++++++++++++++++++++++++++++++++ node-xwalk/lib/loader.js | 2 +- packaging/jsnative.spec | 5 + privileges/CMakeLists.txt | 34 +++ privileges/lib/privileges.js | 135 +++++++++ privileges/package.json | 10 + privileges/src/log.h | 27 ++ privileges/src/privileges_native.cc | 198 ++++++++++++ privileges/src/privileges_native.h | 38 +++ 14 files changed, 1058 insertions(+), 3 deletions(-) create mode 100644 arguments-validator/CMakeLists.txt create mode 100644 arguments-validator/package.json create mode 100644 arguments-validator/validator.js create mode 100644 privileges/CMakeLists.txt create mode 100644 privileges/lib/privileges.js create mode 100644 privileges/package.json create mode 100644 privileges/src/log.h create mode 100644 privileges/src/privileges_native.cc create mode 100644 privileges/src/privileges_native.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d5f315..0336ff6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,3 +28,5 @@ SET(GLOBAL_NODE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/lib/node) ADD_SUBDIRECTORY(appfw) ADD_SUBDIRECTORY(gcontext) ADD_SUBDIRECTORY(node-xwalk) +ADD_SUBDIRECTORY(privileges) +ADD_SUBDIRECTORY(arguments-validator) diff --git a/appfw/CMakeLists.txt b/appfw/CMakeLists.txt index 965911c..1b08eff 100755 --- a/appfw/CMakeLists.txt +++ b/appfw/CMakeLists.txt @@ -1,5 +1,5 @@ SET(TARGET_APPFW "appfw") -SET(TARGET_APPFW_NATIVE "appfw-native") +SET(TARGET_APPFW_NATIVE "appfw_native") SET(TARGET_APPFW_SRC ${PROJECT_SOURCE_DIR}/appfw) # Copy Project diff --git a/appfw/lib/appfw.js b/appfw/lib/appfw.js index c41ba63..a86275d 100755 --- a/appfw/lib/appfw.js +++ b/appfw/lib/appfw.js @@ -37,7 +37,7 @@ Application.prototype.init = function(name) { internal(this).initialized = true; process.title = process.argv[1]; - var appfw = require('../build/Release/appfw-native.node'); + var appfw = require('../build/Release/appfw_native'); var EventEmitter = require('events'); // gcontext module should be installed as global module diff --git a/arguments-validator/CMakeLists.txt b/arguments-validator/CMakeLists.txt new file mode 100644 index 0000000..adb18e7 --- /dev/null +++ b/arguments-validator/CMakeLists.txt @@ -0,0 +1,12 @@ +SET(TARGET_ARGVALIDATOR "arguments-validator") +SET(TARGET_ARGVALIDATOR_SRC ${PROJECT_SOURCE_DIR}/${TARGET_ARGVALIDATOR}) +# Copy Project +INSTALL(FILES + ${TARGET_ARGVALIDATOR_SRC}/package.json + DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${TARGET_ARGVALIDATOR} +) + +INSTALL(FILES + ${TARGET_ARGVALIDATOR_SRC}/validator.js + DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${TARGET_ARGVALIDATOR} +) diff --git a/arguments-validator/package.json b/arguments-validator/package.json new file mode 100644 index 0000000..d7db5c3 --- /dev/null +++ b/arguments-validator/package.json @@ -0,0 +1,10 @@ +{ + "name": "arguments-validator", + "version": "0.0.1", + "description": "Arguments Validator for Tizen Device API", + "main": "validator.js", + "author": { + "name": "Wonyoung Choi", + "email": "wy80.choi@samsung.com" + } +} diff --git a/arguments-validator/validator.js b/arguments-validator/validator.js new file mode 100644 index 0000000..a62f2df --- /dev/null +++ b/arguments-validator/validator.js @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +var Type = function() {}; + +Type.prototype.isBoolean = function(obj) { + return typeof obj === 'boolean'; +}; + +Type.prototype.isObject = function(obj) { + return (null !== obj && typeof obj === 'object' && !this.isArray(obj)); +}; + +Type.prototype.isArray = function(obj) { + return Array.isArray(obj); +}; + +Type.prototype.isFunction = function(obj) { + return typeof obj === 'function'; +}; + +Type.prototype.isNumber = function(obj) { + return typeof obj === 'number'; +}; + +Type.prototype.isString = function(obj) { + return typeof obj === 'string'; +}; + +Type.prototype.isDate = function(obj) { + return obj instanceof Date; +}; + +Type.prototype.isNull = function(obj) { + return obj === null; +}; + +Type.prototype.isNullOrUndefined = function(obj) { + return (obj === null || obj === undefined); +}; + +Type.prototype.isUndefined = function(obj) { + return obj === void 0; +}; + +Type.prototype.isA = function(obj, type) { + var clas = Object.prototype.toString.call(obj).slice(8, -1); + return (obj !== undefined) && (obj !== null) && (clas === type); +}; + +Type.prototype.isEmptyObject = function(obj) { + for (var property in obj) { + if (obj.hasOwnProperty(property)) { + return false; + } + } + return true; +}; + +Type.prototype.hasProperty = function(obj, prop) { + return prop in obj; +}; + +Type.prototype.arrayContains = function(arr, value) { + return (arr.indexOf(value) > -1); +}; + +Type.prototype.getValues = function(obj) { + var ret = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + ret.push(obj[key]); + } + } + return ret; +}; + +var _type = new Type(); + +var Converter = function() {}; + +function _nullableGeneric(func, nullable, val) { + if (_type.isNull(val) && nullable === true) { + return val; + } else { + return func.apply(null, [].slice.call(arguments, 2)); + } +} + +function _toBoolean(val) { + return Boolean(val); +} + +Converter.prototype.toBoolean = function(val, nullable) { + return _nullableGeneric(_toBoolean, nullable, val); +}; + +function _toLong(val) { + var ret = parseInt(val, 10); + return isNaN(ret) ? (val === true ? 1 : 0) : ret; +} + +Converter.prototype.toLong = function(val, nullable) { + return _nullableGeneric(_toLong, nullable, val); +}; + +function _toLongLong(val) { + // According to WebIDL specification this will not be a precise representation + // of requested val. We're converting the val to signed long and then pass it + // to C++ to get the value in required range. + return native_.getResultObject(native_.callSync('Utils_toLongLong', { + n : _toLong(val) + })); +} + +Converter.prototype.toLongLong = function(val, nullable) { + return _nullableGeneric(_toLongLong, nullable, val); +}; + +function _toUnsignedLong(val) { + return _toLong(val) >>> 0; +} + +Converter.prototype.toUnsignedLong = function(val, nullable) { + return _nullableGeneric(_toUnsignedLong, nullable, val); +}; + +function _toUnsignedLongLong(val) { + // According to WebIDL specification this will not be a precise representation + // of requested val. We're converting the val to signed long and then pass it + // to C++ to get the value in required range. + return native_.getResultObject(native_.callSync('Utils_toUnsignedLongLong', { + n : _toLong(val) + })); +} + +Converter.prototype.toUnsignedLongLong = function(val, nullable) { + return _nullableGeneric(_toUnsignedLongLong, nullable, val); +}; + +function _toShort(val) { + return ((_toLong(val) + 32768) & 0xFFFF) - 32768; +} + +Converter.prototype.toShort = function(val, nullable) { + return _nullableGeneric(_toShort, nullable, val); +}; + +function _toUnsignedShort(val) { + return (Math.abs(_toLong(val)) & 0xFFFF); +} + +Converter.prototype.toUnsignedShort = function(val, nullable) { + return _nullableGeneric(_toUnsignedShort, nullable, val); +}; + +function _toByte(val) { + return ((_toLong(val) + 128) & 0xFF) - 128; +} + +Converter.prototype.toByte = function(val, nullable) { + return _nullableGeneric(_toByte, nullable, val); +}; + +function _toOctet(val) { + return _toLong(val) & 0xFF; +} + +Converter.prototype.toOctet = function(val, nullable) { + return _nullableGeneric(_toOctet, nullable, val); +}; + +function _toDouble(val) { + var ret = Number(val); + if (isNaN(ret) || !isFinite(ret)) { + throw new Error('Cannot convert ' + String(val) + ' to double.'); + } + return ret; +} + +Converter.prototype.toDouble = function(val, nullable) { + return _nullableGeneric(_toDouble, nullable, val); +}; + +function _toString(val) { + return String(val); +} + +Converter.prototype.toString = function(val, nullable) { + return _nullableGeneric(_toString, nullable, val); +}; + +function _toPlatformObject(val, types) { + var v; + var t; + if (_type.isArray(val)) { + v = val; + } else { + v = [val]; + } + + if (_type.isArray(types)) { + t = types; + } else { + t = [types]; + } + var match = false; + for (var i = 0; i < t.length; ++i) { + for (var j = 0; j < v.length; ++j) { + match = match || (v[j] instanceof t[i]); + } + } + if (match) { + return val; + } + + throw new Error('Cannot convert ' + + String(val) + ' to ' + String(t[0].name) + '.'); +} + +Converter.prototype.toPlatformObject = function(val, types, nullable) { + return _nullableGeneric(_toPlatformObject, nullable, val, types); +}; + +function _toFunction(val) { + if (_type.isFunction(val)) { + return val; + } + + throw new Error('Cannot convert ' + String(val) + ' to function.'); +} + +Converter.prototype.toFunction = function(val, nullable) { + return _nullableGeneric(_toFunction, nullable, val); +}; + +function _toArray(val) { + if (_type.isArray(val)) { + return val; + } + + throw new Error('Cannot convert ' + String(val) + ' to array.'); +} + +Converter.prototype.toArray = function(val, nullable) { + return _nullableGeneric(_toArray, nullable, val); +}; + +function _toDictionary(val) { + if (_type.isObject(val) || _type.isFunction(val)) { + return val; + } + + throw new Error('Cannot convert ' + String(val) + ' to dictionary.'); +} + +Converter.prototype.toDictionary = function(val, nullable) { + return _nullableGeneric(_toDictionary, nullable, val); +}; + +function _toEnum(val, e) { + var v = _toString(val); + if (_type.arrayContains(e, v)) { + return v; + } + + throw new Error('Cannot convert ' + v + ' to enum.'); +} + +Converter.prototype.toEnum = function(val, e, nullable) { + return _nullableGeneric(_toEnum, nullable, val, e); +}; + +var _converter = new Converter(); + +var Validator = function() { + this.Types = { + BOOLEAN: 'BOOLEAN', + LONG: 'LONG', + LONG_LONG: 'LONG_LONG', + UNSIGNED_LONG: 'UNSIGNED_LONG', + UNSIGNED_LONG_LONG: 'UNSIGNED_LONG_LONG', + BYTE: 'BYTE', + OCTET: 'OCTET', + DOUBLE: 'DOUBLE', + STRING: 'STRING', + FUNCTION: 'FUNCTION', + DICTIONARY: 'DICTIONARY', + PLATFORM_OBJECT: 'PLATFORM_OBJECT', + LISTENER: 'LISTENER', + ARRAY: 'ARRAY', + ENUM: 'ENUM', + FILE_REFERENCE: 'FILE_REFERENCE' + }; +}; + + +/** + * Verifies if arguments passed to function are valid. + * + * Description of expected arguments. + * This is an array of objects, each object represents one argument. + * First object in this array describes first argument, second object describes second + * argument, and so on. + * Object describing an argument needs to have two properties: + * - name - name of the argument, + * - type - type of the argument, only values specified in Validator.Types are allowed. + * Other properties, which may appear: + * - optional - if set to value which evaluates to true, argument is optional + * - nullable - if set to to true, argument may be set to null + * - values - required in case of some objects, value depends on type + * - validator - function which accepts a single parameter and returns true or false; + * if this property is present, this function will be executed, + * argument converted to expected type is going to be passed to this function + * + * @param {Array} a - arguments of a method + * @param {Array} d - description of expected arguments + * @return {Object} which holds all available arguments. + * @throws TypeMismatchError if arguments are not valid + * + * @code + * [ + * { + * name: 'first', + * type: 'aType' + * } + * ] + * @code + * [ + * { + * name: 'first', + * type: 'aType', + * optional: true + * } + * ] + * @code + * [ + * { + * name: 'first', + * type: 'aType', + * nullable: true + * } + * ] + * @code + * [ + * { + * name: 'first', + * type: 'aType', + * optional: true, + * nullable: true + * } + * ] + * @code + * [ + * { + * name: 'first', + * type: Validator.Types.PLATFORM_OBJECT, + * values: ApplicationControl // type of platform object + * } + * ] + * @code + * [ + * { + * name: 'first', + * type: Validator.Types.PLATFORM_OBJECT, + * values: [Alarm, AlarmRelative, AlarmAbsolute] // accepted types + * } + * ] + * @code + * [ + * { + * name: 'first', + * type: Validator.Types.LISTENER, + * values: ['onsuccess', 'onfailure'] // array of callbacks' names + * } + * ] + * @code + * [ + * { + * name: 'first', + * type: Validator.Types.ARRAY, + * values: ApplicationControlData // type of each element in array, + * // tested with instanceof + * } + * ] + * @code + * [ + * { + * name: 'first', + * type: Validator.Types.ARRAY, + * values: Validator.Types.DOUBLE // converts elements, only primitive types are supported + * } + * ] + * @code + * [ + * { + * name: 'first', + * type: Validator.Types.ENUM, + * values: ['SCREEN_DIM', 'SCREEN_NORMAL', 'CPU_AWAKE'] // array of allowed values + * } + * ] + */ +Validator.prototype.validateArgs = function(a, d) { + var args = {has: {}}; + + for (var i = 0; i < d.length; ++i) { + var name = d[i].name; + args.has[name] = (i < a.length); + + var optional = d[i].optional; + var nullable = d[i].nullable; + var val = a[i]; + + if (args.has[name] || !optional) { + var type = d[i].type; + var values = d[i].values; + + switch (type) { + case this.Types.BOOLEAN: + val = _converter.toBoolean(val, nullable); + break; + + case this.Types.LONG: + val = _converter.toLong(val, nullable); + break; + + case this.Types.LONG_LONG: + val = _converter.toLongLong(val, nullable); + break; + + case this.Types.UNSIGNED_LONG: + val = _converter.toUnsignedLong(val, nullable); + break; + + case this.Types.UNSIGNED_LONG_LONG: + val = _converter.toUnsignedLongLong(val, nullable); + break; + + case this.Types.BYTE: + val = _converter.toByte(val, nullable); + break; + + case this.Types.OCTET: + val = _converter.toOctet(val, nullable); + break; + + case this.Types.DOUBLE: + val = _converter.toDouble(val, nullable); + break; + + case this.Types.STRING: + val = _converter.toString(val, nullable); + break; + + case this.Types.FUNCTION: + val = _converter.toFunction(val, nullable); + break; + + case this.Types.DICTIONARY: + val = _converter.toDictionary(val, nullable); + break; + + case this.Types.PLATFORM_OBJECT: + val = _converter.toPlatformObject(val, values, nullable); + break; + + case this.Types.LISTENER: + if (_type.isNull(val)) { + if (!nullable) { + throw new Error('Argument "' + name + '" cannot be null.'); + } + } else { + if (!_type.isObject(val)) { + throw new Error('Argument "' + name + '" should be an object.'); + } + for (var ii = 0; ii < values.length; ++ii) { + if (_type.hasProperty(val, values[ii])) { + val[values[ii]] = _converter.toFunction(val[values[ii]], false); + } + } + } + break; + + case this.Types.ARRAY: + val = _converter.toArray(val, nullable); + if (!_type.isNull(val) && values) { + var func; + + switch (values) { + case this.Types.BOOLEAN: + func = _converter.toBoolean; + break; + + case this.Types.LONG: + func = _converter.toLong; + break; + + case this.Types.LONG_LONG: + func = _converter.toLongLong; + break; + + case this.Types.UNSIGNED_LONG: + func = _converter.toUnsignedLong; + break; + + case this.Types.UNSIGNED_LONG_LONG: + func = _converter.toUnsignedLongLong; + break; + + case this.Types.BYTE: + func = _converter.toByte; + break; + + case this.Types.OCTET: + func = _converter.toOctet; + break; + + case this.Types.DOUBLE: + func = _converter.toDouble; + break; + + case this.Types.STRING: + func = _converter.toString; + break; + + default: + func = function(val) { + if (!(val instanceof values)) { + throw new Error('Items of array "' + name + + '" should be of type: ' + values + '.'); + } + return val; + }; + } + + for (var j = 0; j < val.length; ++j) { + val[j] = func(val[j]); + } + } + break; + + case this.Types.ENUM: + val = _converter.toEnum(val, values, nullable); + break; + + case this.Types.FILE_REFERENCE: + if (_type.isObject(val) && 'File' === val.constructor.name && val.fullPath) { + val = val.fullPath; + } + val = _converter.toString(val, nullable); + break; + + default: + throw new Error('Unknown type: "' + type + '".'); + } + + var _validator = d[i].validator; + + if (_type.isFunction(_validator) && !_validator(val)) { + throw new Error('Argument "' + name + + '" did not pass additional validation.'); + } + + args[name] = val; + } + } + + return args; +}; + +module.exports = new Validator(); diff --git a/node-xwalk/lib/loader.js b/node-xwalk/lib/loader.js index ff63d84..9b47673 100644 --- a/node-xwalk/lib/loader.js +++ b/node-xwalk/lib/loader.js @@ -6,7 +6,7 @@ var fs = require('fs'); var path = require('path'); var process = require('process'); -var native_ = require("../build/Release/native.node"); +var native_ = require("../build/Release/native"); function error(msg) { console.error("ERR: " + msg); diff --git a/packaging/jsnative.spec b/packaging/jsnative.spec index 9ad276b..677a6f3 100644 --- a/packaging/jsnative.spec +++ b/packaging/jsnative.spec @@ -15,6 +15,11 @@ BuildRequires: pkgconfig(appcore-efl) BuildRequires: pkgconfig(aul) BuildRequires: pkgconfig(dlog) BuildRequires: pkgconfig(nodejs) +BuildRequires: pkgconfig(libsmack) +BuildRequires: pkgconfig(cynara-client) +BuildRequires: pkgconfig(capi-appfw-app-manager) +BuildRequires: pkgconfig(pkgmgr-info) + Requires: nodejs %description diff --git a/privileges/CMakeLists.txt b/privileges/CMakeLists.txt new file mode 100644 index 0000000..4842664 --- /dev/null +++ b/privileges/CMakeLists.txt @@ -0,0 +1,34 @@ +SET(TARGET_PRIVILEGES "privileges") +SET(TARGET_PRIVILEGES_NATIVE "privileges_native") +SET(TARGET_PRIVILEGES_SRC ${PROJECT_SOURCE_DIR}/privileges) + +# Copy Project +INSTALL(FILES + ${TARGET_PRIVILEGES_SRC}/package.json + DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${TARGET_PRIVILEGES} +) + +INSTALL(FILES + ${TARGET_PRIVILEGES_SRC}/lib/privileges.js + DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${TARGET_PRIVILEGES}/lib +) + +# Native Binding Module +PKG_CHECK_MODULES(TARGET_PRIVILEGES_NATIVE_DEPS REQUIRED + nodejs + cynara-client libsmack + pkgmgr-info capi-appfw-app-manager +) +INCLUDE_DIRECTORIES ( + ${TARGET_PRIVILEGES_SRC}/src + ${TARGET_PRIVILEGES_NATIVE_DEPS_INCLUDE_DIRS} +) +ADD_LIBRARY(${TARGET_PRIVILEGES_NATIVE} MODULE + ${TARGET_PRIVILEGES_SRC}/src/privileges_native.cc +) +SET_TARGET_PROPERTIES(${TARGET_PRIVILEGES_NATIVE} PROPERTIES PREFIX "" SUFFIX ".node") +TARGET_LINK_LIBRARIES(${TARGET_PRIVILEGES_NATIVE} + ${TARGET_PRIVILEGES_NATIVE_DEPS_LIBRARIES} +) +INSTALL(TARGETS ${TARGET_PRIVILEGES_NATIVE} DESTINATION + ${GLOBAL_NODE_MODULE_PATH}/${TARGET_PRIVILEGES}/build/Release) diff --git a/privileges/lib/privileges.js b/privileges/lib/privileges.js new file mode 100644 index 0000000..2007794 --- /dev/null +++ b/privileges/lib/privileges.js @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +var native_ = require("../build/Release/privileges_native"); + +function Privileges() { + /** + * Cynara(since tizen 3.0) only support native privilege. + * simply web privilege convert native privilege for checking access. + */ + var privilege = { + ACCOUNT_READ: 'http://tizen.org/privilege/account.read', + ACCOUNT_WRITE: 'http://tizen.org/privilege/account.write', + ALARM: 'http://tizen.org/privilege/alarm.get', + APPLICATION_INFO: 'http://tizen.org/privilege/packagemanager.info', + APPLICATION_LAUNCH: 'http://tizen.org/privilege/appmanager.launch', + APPMANAGER_CERTIFICATE: 'http://tizen.org/privilege/notexist', + APPMANAGER_KILL: 'http://tizen.org/privilege/appmanager.kill', + BLUETOOTH_ADMIN: 'http://tizen.org/privilege/bluetooth.admin', + BLUETOOTH_GAP: 'http://tizen.org/privilege/bluetooth.admin', + BLUETOOTH_HEALTH: 'http://tizen.org/privilege/bluetooth.admin', + BLUETOOTH_SPP: 'http://tizen.org/privilege/bluetooth.admin', + BLUETOOTHMANAGER: 'http://tizen.org/privilege/bluetooth.admin', + BLUETOOTH: 'http://tizen.org/privilege/bluetooth', + BOOKMARK_READ: 'http://tizen.org/privilege/bookmark.admin', + BOOKMARK_WRITE: 'http://tizen.org/privilege/bookmark.admin', + CALENDAR_READ: 'http://tizen.org/privilege/calendar.read', + CALENDAR_WRITE: 'http://tizen.org/privilege/calendar.write', + CALLHISTORY_READ: 'http://tizen.org/privilege/callhistory.read', + CALLHISTORY_WRITE: 'http://tizen.org/privilege/callhistory.write', + CONTACT_READ: 'http://tizen.org/privilege/contact.read', + CONTACT_WRITE: 'http://tizen.org/privilege/contact.write', + CONTENT_READ: 'http://tizen.org/privilege/content.write', + CONTENT_WRITE: 'http://tizen.org/privilege/content.write', + DATACONTROL_CONSUMER: 'http://tizen.org/privilege/datasharing', + DATASYNC: 'http://tizen.org/privilege/notexist', + DOWNLOAD: 'http://tizen.org/privilege/download', + FILESYSTEM_READ: 'http://tizen.org/privilege/systemsettings.admin', + FILESYSTEM_WRITE: 'http://tizen.org/privilege/systemsettings.admin', + HEALTHINFO: 'http://tizen.org/privilege/healthinfo', + INTERNET: 'http://tizen.org/privilege/internet', + KEYMANAGER: 'http://tizen.org/privilege/keymanager', + LED: 'http://tizen.org/privilege/led', + LOCATION: 'http://tizen.org/privilege/location', + MEDIACONTROLLER_SERVER: 'http://tizen.org/privilege/mediacontroller.server', + MEDIACONTROLLER_CLIENT: 'http://tizen.org/privilege/mediacontroller.client', + MESSAGING_READ: 'http://tizen.org/privilege/message.read', + MESSAGING_WRITE: 'http://tizen.org/privilege/message.write', + NETWORKBEARERSELECTION: 'http://tizen.org/privilege/network.set', + NFC_ADMIN: 'http://tizen.org/privilege/nfc.admin', + NFC_CARDEMULATION: 'http://tizen.org/privilege/nfc.cardemulation', + NFC_COMMON: 'http://tizen.org/privilege/nfc', + NFC_P2P: 'http://tizen.org/privilege/nfc', + NFC_TAG: 'http://tizen.org/privilege/nfc', + NOTIFICATION: 'http://tizen.org/privilege/notification', + PACKAGE_INFO: 'http://tizen.org/privilege/packagemanager.info', + PACKAGEMANAGER_INSTALL: 'http://tizen.org/privilege/packagemanager.admin', + POWER: 'http://tizen.org/privilege/display', + PUSH: 'http://tizen.org/privilege/push', + SECUREELEMENT: 'http://tizen.org/privilege/secureelement', + SETTING: 'http://tizen.org/privilege/systemsettings.admin', + SYSTEM: 'http://tizen.org/privilege/telephony', + SYSTEMMANAGER: 'http://tizen.org/privilege/systemmanager', + TELEPHONY: 'http://tizen.org/privilege/telephony', + VOLUME_SET: 'http://tizen.org/privilege/volume.set' + }; + + Object.freeze(privilege); + + Object.defineProperty(this, 'privilege', { + value: privilege, + writable: false, + enumerable: true, + configurable: false + }); +} + +Privileges.prototype.getPkgApiVersion = function() { + return native_.getPkgApiVersion(); +}; + +Privileges.prototype.checkPrivilegeAccess = function(privilege) { + return native_.checkPrivilegeAccess(privilege); +}; + +Privileges.prototype.checkPrivilegeAccess4Ver = function(new_ver, new_priv, old_priv) { + var app_ver = this.getPkgApiVersion(); + + var arr_new_ver = new_ver.split("."); + var arr_app_ver = app_ver.split("."); + var num_new; + var num_app; + var sel = 0; + + var i; + var length = Math.min(arr_new_ver.length, arr_app_ver.length); + for (i = 0; i < length; i++) { + num_new = parseInt(arr_new_ver[i]); + num_app = parseInt(arr_app_ver[i]); + if (num_app < num_new) { + sel = 1; + break; + } else if (num_app > num_new) { + sel = -1; + break; + } + } + + if (sel === 0 && arr_new_ver.length > arr_app_ver.length) { + sel = 1; + } + + if (sel !== 1) { + this.checkPrivilegeAccess(new_priv); + } else if (old_priv !== undefined) { + this.checkPrivilegeAccess(old_priv); + } +}; + +Object.freeze(Privileges.prototype); + +module.exports = new Privileges(); diff --git a/privileges/package.json b/privileges/package.json new file mode 100644 index 0000000..9c6f2fb --- /dev/null +++ b/privileges/package.json @@ -0,0 +1,10 @@ +{ + "name": "privileges", + "version": "0.0.1", + "description": "Privilege checker for Tizen", + "main": "./lib/privileges.js", + "author": { + "name": "Wonyoung Choi", + "email": "wy80.choi@samsung.com" + } +} diff --git a/privileges/src/log.h b/privileges/src/log.h new file mode 100644 index 0000000..eac251d --- /dev/null +++ b/privileges/src/log.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +#ifndef PRIVILEGES_LOG_H_ +#define PRIVILEGES_LOG_H_ + +#include + +#define LOGD(fmt, args...) printf(fmt "\n", ##args) +#define LOGI(fmt, args...) printf(fmt "\n", ##args) +#define LOGW(fmt, args...) printf(fmt "\n", ##args) +#define LOGE(fmt, args...) printf(fmt "\n", ##args) + +#endif // PRIVILEGES_LOG_H_ diff --git a/privileges/src/privileges_native.cc b/privileges/src/privileges_native.cc new file mode 100644 index 0000000..3018562 --- /dev/null +++ b/privileges/src/privileges_native.cc @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 "privileges_native.h" + +#include + +#include +#include +#include +#include + +#include + +#include "log.h" + +namespace privileges { + +namespace { + +class AccessControl { + public: + static AccessControl* GetInstance() { + static AccessControl* self = nullptr; + if (!self) { + self = new AccessControl(); + } + return self; + } + + ~AccessControl() { + if (cynara_) { + auto ret = cynara_finish(cynara_); + if (CYNARA_API_SUCCESS != ret) { + LOGE("Failed to finalize Cynara"); + } + cynara_ = nullptr; + } + } + + bool CheckAccess(const std::string privilege) { + if (cynara_) { + if (CYNARA_API_ACCESS_ALLOWED != + cynara_simple_check(cynara_, smack_label_.c_str(), "", + uid_.c_str(), privilege.c_str())) { + return false; + } + return true; + } else { + return false; + } + } + + private: + AccessControl() : cynara_(nullptr) { + char* smack_label = nullptr; + int len = smack_new_label_from_self(&smack_label); + + if (0 < len && nullptr != smack_label) { + auto uid = getuid(); + uid_ = std::to_string(uid); + smack_label_ = smack_label; + free(smack_label); + } else { + LOGE("Failed to get smack label"); + return; + } + + int ret = cynara_initialize(&cynara_, nullptr); + if (CYNARA_API_SUCCESS != ret) { + LOGE("Failed to initialize Cynara"); + cynara_ = nullptr; + } + } + + cynara* cynara_; + std::string uid_; + std::string smack_label_; +}; + +} // namespace + +#define THROW_EXCEPTION(isolate,msg) \ + isolate->ThrowException(v8::String::NewFromUtf8(isolate, msg)); + +// static +void NativeBinding::Init(v8::Handle target) { + NODE_SET_METHOD(target, "getPkgApiVersion", GetPkgApiVersion); + NODE_SET_METHOD(target, "checkPrivilegeAccess", CheckPrivilegeAccess); +} + +// static +void NativeBinding::GetPkgApiVersion( + const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handle_scope(isolate); + v8::ReturnValue result(args.GetReturnValue()); + + char* app_id = nullptr; + char* pkgid = nullptr; + app_info_h app_handle = nullptr; + pkgmgrinfo_pkginfo_h pkginfo_handle = nullptr; + char *api_version = nullptr; + + pid_t pid = getpid(); + int ret = app_manager_get_app_id(pid, &app_id); + if (ret != APP_MANAGER_ERROR_NONE) { + result.Set(v8::Undefined(isolate)); + THROW_EXCEPTION(isolate, "Failed to get app id"); + goto CLEANUP; + } + + ret = app_info_create(app_id, &app_handle); + if (ret != APP_MANAGER_ERROR_NONE) { + result.Set(v8::Undefined(isolate)); + THROW_EXCEPTION(isolate, "Fail to get app info"); + goto CLEANUP; + } + + ret = app_info_get_package(app_handle, &pkgid); + if ((ret != APP_MANAGER_ERROR_NONE) || (pkgid == nullptr)) { + result.Set(v8::Undefined(isolate)); + THROW_EXCEPTION(isolate, "Fail to get pkg id"); + goto CLEANUP; + } + + ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgid, getuid(), &pkginfo_handle); + if (ret != PMINFO_R_OK) { + result.Set(v8::Undefined(isolate)); + THROW_EXCEPTION(isolate, "Fail to get pkginfo_h"); + goto CLEANUP; + } + + ret = pkgmgrinfo_pkginfo_get_version(pkginfo_handle, &api_version); + if (ret != PMINFO_R_OK) { + result.Set(v8::Undefined(isolate)); + THROW_EXCEPTION(isolate, "Fail to get api version"); + goto CLEANUP; + } + + result.Set(v8::String::NewFromUtf8(isolate, api_version)); + +CLEANUP: + if (app_id) { + free(app_id); + } + if (pkgid) { + free(pkgid); + } + if (app_handle) { + app_info_destroy(app_handle); + } + if (pkginfo_handle) { + pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle); + } +} + +// static +void NativeBinding::CheckPrivilegeAccess( + const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handle_scope(isolate); + v8::ReturnValue result(args.GetReturnValue()); + + if (args.Length() < 1 || !args[0]->IsString()) { + result.Set(v8::Undefined(isolate)); + return; + } + + v8::String::Utf8Value privilege(args[0]->ToString()); + + AccessControl* ctrl = AccessControl::GetInstance(); + bool ret = ctrl->CheckAccess(std::string(*privilege)); + + result.Set(v8::Boolean::New(isolate, ret)); +} + +} // namespace privileges + +extern "C" { + static void NodeInit(v8::Handle target) { + privileges::NativeBinding::Init(target); + } + NODE_MODULE(privileges_native, NodeInit); +} diff --git a/privileges/src/privileges_native.h b/privileges/src/privileges_native.h new file mode 100644 index 0000000..922fd79 --- /dev/null +++ b/privileges/src/privileges_native.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +#ifndef PRIVILEGES_PRIVILEGES_NATIVE_H_ +#define PRIVILEGES_PRIVILEGES_NATIVE_H_ + +#include +#include +#include + +namespace privileges { + +class NativeBinding { + public: + static void Init(v8::Handle target); + + static void GetPkgApiVersion( + const v8::FunctionCallbackInfo& args); + static void CheckPrivilegeAccess( + const v8::FunctionCallbackInfo& args); +}; + +} // namespace privileges + +#endif // PRIVILEGES_PRIVILEGES_NATIVE_H_ -- 2.7.4