Merge pull request #887 from overte-org/feature/script_security

Added simple protection for avatar URL
This commit is contained in:
HifiExperiments 2024-04-18 15:16:27 -07:00 committed by GitHub
commit edb8cc55b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 429 additions and 11 deletions

View file

@ -109,7 +109,7 @@ public class PermissionChecker extends Activity {
JSONObject obj = new JSONObject();
try {
obj.put("firstRun",false);
obj.put("Avatar/fullAvatarURL", avatarPaths[which]);
obj.put(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/Avatar/fullAvatarURL", avatarPaths[which]);
File directory = new File(pathForJson);
if(!directory.exists()) directory.mkdirs();

View file

@ -0,0 +1,152 @@
//
// ScriptPermissions.cpp
// libraries/script-engine/src/ScriptPermissions.cpp
//
// Created by dr Karol Suprynowicz on 2024/03/24.
// Copyright 2024 Overte e.V.
//
// Based on EntityScriptQMLWhitelist.qml
// Created by Kalila L. on 2019.12.05 | realities.dev | somnilibertas@gmail.com
// Copyright 2019 Kalila L.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Security settings for the script engines
import Hifi 1.0 as Hifi
import QtQuick 2.8
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.12
import stylesUit 1.0 as HifiStylesUit
import controlsUit 1.0 as HiFiControls
import PerformanceEnums 1.0
import "../../../windows"
Rectangle {
id: parentBody;
function getWhitelistAsText() {
var whitelist = Settings.getValue("private/scriptPermissionGetAvatarURLSafeURLs");
var arrayWhitelist = whitelist.replace(",", "\n");
return arrayWhitelist;
}
function setWhitelistAsText(whitelistText) {
Settings.setValue("private/scriptPermissionGetAvatarURLSafeURLs", whitelistText.text);
notificationText.text = "Whitelist saved.";
}
function setAvatarProtection(enabled) {
Settings.setValue("private/scriptPermissionGetAvatarURLEnable", enabled);
console.info("Setting Protect Avatar URLs to:", enabled);
}
anchors.fill: parent
width: parent.width;
height: 120;
color: "#80010203";
HifiStylesUit.RalewayRegular {
id: titleText;
text: "Protect Avatar URLs"
// Text size
size: 24;
// Style
color: "white";
elide: Text.ElideRight;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.right: parent.right;
anchors.rightMargin: 20;
height: 60;
CheckBox {
id: whitelistEnabled;
checked: Settings.getValue("private/scriptPermissionGetAvatarURLEnable", true);
anchors.right: parent.right;
anchors.top: parent.top;
anchors.topMargin: 10;
onToggled: {
setAvatarProtection(whitelistEnabled.checked)
}
Label {
text: "Enabled"
color: "white"
font.pixelSize: 18;
anchors.right: parent.left;
anchors.top: parent.top;
anchors.topMargin: 10;
}
}
}
Rectangle {
id: textAreaRectangle;
color: "black";
width: parent.width;
height: 250;
anchors.top: titleText.bottom;
ScrollView {
id: textAreaScrollView
anchors.fill: parent;
width: parent.width
height: parent.height
contentWidth: parent.width
contentHeight: parent.height
clip: false;
TextArea {
id: whitelistTextArea
text: getWhitelistAsText();
onTextChanged: notificationText.text = "";
width: parent.width;
height: parent.height;
font.family: "Ubuntu";
font.pointSize: 12;
color: "white";
}
}
Button {
id: saveChanges
anchors.topMargin: 5;
anchors.leftMargin: 20;
anchors.rightMargin: 20;
x: textAreaRectangle.x + textAreaRectangle.width - width - 15;
y: textAreaRectangle.y + textAreaRectangle.height - height;
contentItem: Text {
text: saveChanges.text
font.family: "Ubuntu";
font.pointSize: 12;
opacity: enabled ? 1.0 : 0.3
color: "black"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
text: "Save Changes"
onClicked: setWhitelistAsText(whitelistTextArea)
HifiStylesUit.RalewayRegular {
id: notificationText;
text: ""
// Text size
size: 16;
// Style
color: "white";
elide: Text.ElideLeft;
// Anchors
anchors.right: parent.left;
anchors.rightMargin: 10;
}
}
}
}

View file

@ -3305,7 +3305,7 @@ void Application::initializeUi() {
// END PULL SAFEURLS FROM INTERFACE.JSON Settings
if (AUTHORIZED_EXTERNAL_QML_SOURCE.isParentOf(url)) {
if (QUrl(NetworkingConstants::OVERTE_COMMUNITY_APPLICATIONS).isParentOf(url)) {
return true;
} else {
for (const auto& str : safeURLS) {

View file

@ -41,6 +41,15 @@
#include <QtQuick/QQuickWindow>
#include <memory>
#include "WarningsSuppression.h"
#include "ScriptPermissions.h"
QVariantMap AvatarBookmarks::getBookmarks() {
if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) {
return _bookmarks;
} else {
return {};
}
}
void addAvatarEntities(const QVariantList& avatarEntities) {
auto nodeList = DependencyManager::get<NodeList>();
@ -123,6 +132,12 @@ AvatarBookmarks::AvatarBookmarks() {
}
void AvatarBookmarks::addBookmark(const QString& bookmarkName) {
if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) {
addBookmarkInternal(bookmarkName);
}
}
void AvatarBookmarks::addBookmarkInternal(const QString& bookmarkName) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "addBookmark", Q_ARG(QString, bookmarkName));
return;
@ -134,6 +149,12 @@ void AvatarBookmarks::addBookmark(const QString& bookmarkName) {
}
void AvatarBookmarks::saveBookmark(const QString& bookmarkName) {
if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) {
saveBookmarkInternal(bookmarkName);
}
}
void AvatarBookmarks::saveBookmarkInternal(const QString& bookmarkName) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "saveBookmark", Q_ARG(QString, bookmarkName));
return;
@ -145,6 +166,12 @@ void AvatarBookmarks::saveBookmark(const QString& bookmarkName) {
}
void AvatarBookmarks::removeBookmark(const QString& bookmarkName) {
if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) {
removeBookmarkInternal(bookmarkName);
}
}
void AvatarBookmarks::removeBookmarkInternal(const QString& bookmarkName) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "removeBookmark", Q_ARG(QString, bookmarkName));
return;
@ -200,6 +227,12 @@ void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) {
*/
void AvatarBookmarks::loadBookmark(const QString& bookmarkName) {
if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) {
loadBookmarkInternal(bookmarkName);
}
}
void AvatarBookmarks::loadBookmarkInternal(const QString& bookmarkName) {
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "loadBookmark", Q_ARG(QString, bookmarkName));
return;
@ -268,6 +301,15 @@ void AvatarBookmarks::readFromFile() {
}
QVariantMap AvatarBookmarks::getBookmark(const QString &bookmarkName)
{
if (ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) {
return getBookmarkInternal(bookmarkName);
} else {
return {};
}
}
QVariantMap AvatarBookmarks::getBookmarkInternal(const QString &bookmarkName)
{
if (QThread::currentThread() != thread()) {
QVariantMap result;

View file

@ -100,7 +100,7 @@ public slots:
* print("- " + key + " " + bookmarks[key].avatarUrl);
* };
*/
QVariantMap getBookmarks() { return _bookmarks; }
QVariantMap getBookmarks();
signals:
/*@jsdoc
@ -147,6 +147,11 @@ protected slots:
void deleteBookmark() override;
private:
QVariantMap getBookmarkInternal(const QString &bookmarkName);
void addBookmarkInternal(const QString& bookmarkName);
void saveBookmarkInternal(const QString& bookmarkName);
void loadBookmarkInternal(const QString& bookmarkName);
void removeBookmarkInternal(const QString& bookmarkName);
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
const QString ENTRY_AVATAR_URL = "avatarUrl";
const QString ENTRY_AVATAR_ICON = "avatarIcon";

View file

@ -258,10 +258,11 @@ void CrashRecoveryHandler::handleCrash(CrashRecoveryHandler::Action action) {
// Display name and avatar
settings.beginGroup(AVATAR_GROUP);
displayName = settings.value(DISPLAY_NAME_KEY).toString();
fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl();
fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString();
settings.endGroup();
fullAvatarURL = settings.value(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/" + AVATAR_GROUP + "/" + FULL_AVATAR_URL_KEY).toUrl();
// Tutorial complete
tutorialComplete = settings.value(TUTORIAL_COMPLETE_FLAG_KEY).toBool();
}
@ -280,12 +281,12 @@ void CrashRecoveryHandler::handleCrash(CrashRecoveryHandler::Action action) {
// Display name and avatar
settings.beginGroup(AVATAR_GROUP);
settings.setValue(DISPLAY_NAME_KEY, displayName);
settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL);
settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName);
settings.endGroup();
settings.setValue(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/" + AVATAR_GROUP + "/" + FULL_AVATAR_URL_KEY, fullAvatarURL);
// Tutorial complete
settings.setValue(TUTORIAL_COMPLETE_FLAG_KEY, tutorialComplete);
}
}

View file

@ -323,6 +323,19 @@ Menu::Menu() {
}
});
// Settings > Script Security
action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::ScriptSecurity);
connect(action, &QAction::triggered, [] {
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
tablet->pushOntoStack("hifi/dialogs/security/ScriptSecurity.qml");
if (!hmd->getShouldShowTablet()) {
hmd->toggleShouldShowTablet();
}
});
// Settings > Developer Menu
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus()));

View file

@ -190,6 +190,7 @@ namespace MenuOption {
const QString RunTimingTests = "Run Timing Tests";
const QString ScriptedMotorControl = "Enable Scripted Motor Control";
const QString EntityScriptQMLWhitelist = "Entity Script / QML Whitelist";
const QString ScriptSecurity = "Script Security";
const QString ShowTrackedObjects = "Show Tracked Objects";
const QString SelfieCamera = "Selfie";
const QString SendWrongDSConnectVersion = "Send wrong DS connect version";

View file

@ -73,6 +73,7 @@
#include "MovingEntitiesOperator.h"
#include "SceneScriptingInterface.h"
#include "WarningsSuppression.h"
#include "ScriptPermissions.h"
using namespace std;
@ -226,7 +227,7 @@ MyAvatar::MyAvatar(QThread* thread) :
_yawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "yawSpeed", _yawSpeed),
_hmdYawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hmdYawSpeed", _hmdYawSpeed),
_pitchSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "pitchSpeed", _pitchSpeed),
_fullAvatarURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarURL",
_fullAvatarURLSetting(QStringList() << SETTINGS_FULL_PRIVATE_GROUP_NAME << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarURL",
AvatarData::defaultFullAvatarModelUrl()),
_fullAvatarModelNameSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarModelName", _fullAvatarModelName),
_animGraphURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "animGraphURL", QUrl("")),
@ -2236,6 +2237,9 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString&
return attachment;
}
bool MyAvatar::isMyAvatarURLProtected() const {
return !ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL);
}
int MyAvatar::parseDataFromBuffer(const QByteArray& buffer) {
qCDebug(interfaceapp) << "Error: ignoring update packet for MyAvatar"

View file

@ -2683,6 +2683,7 @@ private:
void setEnableDrawAverageFacing(bool drawAverage) { _drawAverageFacingEnabled = drawAverage; }
bool getEnableDrawAverageFacing() const { return _drawAverageFacingEnabled; }
virtual bool isMyAvatar() const override { return true; }
virtual bool isMyAvatarURLProtected() const override;
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
virtual glm::vec3 getSkeletonPosition() const override;
int _skeletonModelChangeCount { 0 };

View file

@ -21,6 +21,9 @@ SettingsScriptingInterface* SettingsScriptingInterface::getInstance() {
}
QVariant SettingsScriptingInterface::getValue(const QString& setting) {
if (_restrictPrivateValues && setting.startsWith(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/")) {
return {""};
}
QVariant value = Setting::Handle<QVariant>(setting).get();
if (!value.isValid()) {
value = "";
@ -29,6 +32,9 @@ QVariant SettingsScriptingInterface::getValue(const QString& setting) {
}
QVariant SettingsScriptingInterface::getValue(const QString& setting, const QVariant& defaultValue) {
if (_restrictPrivateValues && setting.startsWith(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/")) {
return {""};
}
QVariant value = Setting::Handle<QVariant>(setting, defaultValue).get();
if (!value.isValid()) {
value = "";
@ -40,7 +46,7 @@ void SettingsScriptingInterface::setValue(const QString& setting, const QVariant
if (getValue(setting) == value) {
return;
}
if (setting.startsWith("private/")) {
if (setting.startsWith("private/") || setting.startsWith(SETTINGS_FULL_PRIVATE_GROUP_NAME + "/")) {
if (_restrictPrivateValues) {
qWarning() << "SettingsScriptingInterface::setValue -- restricted write: " << setting << value;
return;

View file

@ -2106,6 +2106,14 @@ const QUrl& AvatarData::getSkeletonModelURL() const {
}
}
QString AvatarData::getSkeletonModelURLFromScript() const {
if (isMyAvatar() && !isMyAvatarURLProtected()) {
return _skeletonModelURL.toString();
}
return QString();
};
QByteArray AvatarData::packSkeletonData() const {
// Send an avatar trait packet with the skeleton data before the mesh is loaded
int avatarDataSize = 0;

View file

@ -610,6 +610,8 @@ public:
AvatarData();
virtual ~AvatarData();
virtual bool isMyAvatarURLProtected() const { return false; } // This needs to be here because both MyAvatar and AvatarData inherit from MyAvatar
static const QUrl& defaultFullAvatarModelUrl();
const QUuid getSessionUUID() const { return getID(); }
@ -1355,7 +1357,7 @@ public:
*/
Q_INVOKABLE virtual void detachAll(const QString& modelURL, const QString& jointName = QString());
QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); }
QString getSkeletonModelURLFromScript() const;
void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); }
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }

View file

@ -204,7 +204,11 @@ bool ScriptAvatarData::getLookAtSnappingEnabled() const {
//
QString ScriptAvatarData::getSkeletonModelURLFromScript() const {
if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) {
return sharedAvatarData->getSkeletonModelURLFromScript();
if (sharedAvatarData->isMyAvatar() && !sharedAvatarData->isMyAvatarURLProtected()) {
return sharedAvatarData->getSkeletonModelURLFromScript();
}
return QString();
} else {
return QString();
}

View file

@ -58,6 +58,8 @@ namespace NetworkingConstants {
const QString HF_PUBLIC_CDN_URL = "";
const QString HF_MARKETPLACE_CDN_HOSTNAME = "";
const QString OVERTE_CONTENT_CDN_URL = "https://content.overte.org/";
const QString OVERTE_COMMUNITY_APPLICATIONS = { "https://more.overte.org/applications" };
const QString OVERTE_TUTORIAL_SCRIPTS = { "https://more.overte.org/tutorial" };
#if USE_STABLE_GLOBAL_SERVICES
const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.overte.org";

View file

@ -542,6 +542,10 @@ QString ScriptManager::getFilename() const {
return lastPart;
}
QString ScriptManager::getAbsoluteFilename() const {
return _fileNameString;
}
bool ScriptManager::hasValidScriptSuffix(const QString& scriptFileName) {
QFileInfo fileInfo(scriptFileName);
QString scriptSuffixToLower = fileInfo.completeSuffix().toLower();

View file

@ -430,6 +430,13 @@ public:
*/
QString getFilename() const;
/**
* @brief Get the filename of the running script, with absolute path.
*
* @return QString Filename
*/
QString getAbsoluteFilename() const;
/**
* @brief Underlying scripting engine
*

View file

@ -0,0 +1,124 @@
//
// ScriptPermissions.cpp
// libraries/script-engine/src/ScriptPermissions.cpp
//
// Created by dr Karol Suprynowicz on 2024/03/24.
// Copyright 2024 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ScriptPermissions.h"
#include <array>
#include <QJsonArray>
#include "ScriptEngine.h"
#include "ScriptManager.h"
#include "Scriptable.h"
static const bool PERMISSIONS_DEBUG_ENABLED = false;
extern const std::array<QString, static_cast<int>(ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE)> scriptPermissionNames {
"Permission to get user's avatar URL" //SCRIPT_PERMISSION_GET_AVATAR_URL
};
extern const std::array<QString, static_cast<int>(ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE)> scriptPermissionSettingKeyNames {
"private/scriptPermissionGetAvatarURLSafeURLs" //SCRIPT_PERMISSION_GET_AVATAR_URL
};
extern const std::array<QString, static_cast<int>(ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE)> scriptPermissionSettingEnableKeyNames {
"private/scriptPermissionGetAvatarURLEnable" //SCRIPT_PERMISSION_GET_AVATAR_URL
};
extern const std::array<bool, static_cast<int>(ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE)> scriptPermissionSettingEnableDefaultValues {
true //SCRIPT_PERMISSION_GET_AVATAR_URL
};
bool ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission permission) {
if (permission >= ScriptPermissions::Permission::SCRIPT_PERMISSIONS_SIZE) {
return false;
}
int permissionIndex = static_cast<int>(permission);
// Check if the permission checking is active
Setting::Handle<bool> isCheckingEnabled(scriptPermissionSettingEnableKeyNames[permissionIndex], scriptPermissionSettingEnableDefaultValues[permissionIndex]);
if (!isCheckingEnabled.get()) {
return true;
}
// Get the script manager:
auto engine = Scriptable::engine();
if (!engine) {
// When this happens it means that function was called from QML or C++ and should always be allowed
if (PERMISSIONS_DEBUG_ENABLED) {
qDebug() << "ScriptPermissions::isCurrentScriptAllowed called outside script engine for permission: "
<< scriptPermissionNames[permissionIndex];
}
return true;
}
auto manager = engine->manager();
if (!manager) {
qDebug() << "ScriptPermissions::isCurrentScriptAllowed called from script engine with no script manager for permission: " << scriptPermissionNames[permissionIndex];
return false;
}
std::vector<QString> urlsToCheck;
QString scriptURL = manager->getAbsoluteFilename();
// If this is an entity script manager, we need to find the file name of the current script instead
if (!scriptURL.startsWith("about:Entities")) {
urlsToCheck.push_back(scriptURL);
}
auto currentURL = Scriptable::context()->currentFileName();
if (!currentURL.isEmpty() && currentURL != scriptURL) {
urlsToCheck.push_back(currentURL);
}
if (PERMISSIONS_DEBUG_ENABLED) {
qDebug() << "ScriptPermissions::isCurrentScriptAllowed: filename: " << scriptURL;
}
auto parentContext = Scriptable::context()->parentContext();
while (parentContext) {
QString parentFilename = parentContext->currentFileName();
if (!parentFilename.isEmpty()) {
urlsToCheck.push_back(parentContext->currentFileName());
if (PERMISSIONS_DEBUG_ENABLED) {
qDebug() << "ScriptPermissions::isCurrentScriptAllowed: parent filename: " << parentContext->currentFileName();
}
}
parentContext = parentContext->parentContext();
}
// Check if the script is allowed:
QList<QString> safeURLPrefixes = { "file:///", "qrc:/", NetworkingConstants::OVERTE_COMMUNITY_APPLICATIONS,
NetworkingConstants::OVERTE_TUTORIAL_SCRIPTS, "about:console"};
Setting::Handle<QString> allowedURLsSetting(scriptPermissionSettingKeyNames[permissionIndex]);
QList<QString> allowedURLs = allowedURLsSetting.get().split("\n");
for (auto entry : allowedURLs) {
safeURLPrefixes.push_back(entry);
}
for (auto urlToCheck : urlsToCheck) {
bool urlIsAllowed = false;
for (const auto& str : safeURLPrefixes) {
if (!str.isEmpty() && urlToCheck.startsWith(str)) {
urlIsAllowed = true;
if (PERMISSIONS_DEBUG_ENABLED) {
qDebug() << "ScriptPermissions::isCurrentScriptAllowed: " << scriptPermissionNames[permissionIndex]
<< " for script " << urlToCheck << " accepted with rule: " << str;
}
}
}
if (!urlIsAllowed) {
if (PERMISSIONS_DEBUG_ENABLED) {
qDebug() << "ScriptPermissions::isCurrentScriptAllowed: " << scriptPermissionNames[permissionIndex]
<< " for script " << urlToCheck << " rejected.";
}
return false;
}
}
return true;
}

View file

@ -0,0 +1,31 @@
//
// ScriptPermissions.h
// libraries/script-engine/src/ScriptPermissions.h
//
// Created by dr Karol Suprynowicz on 2024/03/24.
// Copyright 2024 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include <vector>
#include "SettingHandle.h"
#include "DependencyManager.h"
class ScriptPermissions {
public:
enum class Permission {
SCRIPT_PERMISSION_GET_AVATAR_URL,
SCRIPT_PERMISSIONS_SIZE
};
static bool isCurrentScriptAllowed(Permission permission);
//TODO: add a function to request permission through a popup
};
// TODO: add ScriptPermissionsScriptingInterface, where script can check if they have permissions
// and request permissions through a tablet popup.

View file

@ -18,6 +18,8 @@
Q_LOGGING_CATEGORY(settings_handle, "settings.handle")
const QString SETTINGS_FULL_PRIVATE_GROUP_NAME = "fullPrivate";
const QString Settings::firstRun { "firstRun" };

View file

@ -31,6 +31,15 @@
Q_DECLARE_LOGGING_CATEGORY(settings_handle)
/**
* @brief Name of the fully private settings group
*
* Settings in this group will be protected from reading and writing from script engines.
*
*/
extern const QString SETTINGS_FULL_PRIVATE_GROUP_NAME;
/**
* @brief QSettings analog
*

View file

@ -43,7 +43,7 @@
"Avatar/dominantHand": "right",
"Avatar/flyingHMD": false,
"Avatar/fullAvatarModelName": "Default",
"Avatar/fullAvatarURL": "",
"fullPrivate/Avatar/fullAvatarURL": "",
"Avatar/headPitch": 0,
"Avatar/pitchSpeed": 75,
"Avatar/scale": 1,