Safari 14: chrome.permissions.request() not working seamless and tab.url comes blanks always - Extension - xcode

I have working cross-platform extension which I converted for Safari using xcrun safari-web-extension-converter PATH. Goal of extension is to bookmark any URL into my website account (https://ourdomain.in).
My extension is perfectly working fine for Chrome but for Safari version, everything works well if user allows access permission for every website.
The issue is, even though I have proper optional_permissions and used chrome.permissions.request() method to ask user-consent for ourdomain.in, and we see user allowed access, still tab.url comes blank for chrome.tabs.onUpdated every time.
Here is the case in detail, when user presses extension button, we are checking user is logged into its account or not by opening our website URL into another tab.
var openSignin = function openSignin() {
console.log('hey');
chrome.tabs.create({ url: _config.baseURL + '?extension=1', active: false });
};
When this AUTH tab is loaded, following method will be called as it happens in Chrome which in turn extracts public and private tokens generated when any user logs into our website.
chrome.tabs.onUpdated.addListener(function (tabID, changeInfo, tab) {
if (tab.status == 'complete' && tab.url.startsWith(_config.baseURL)) {
chrome.tabs.executeScript(tab.id, { code: 'localStorage.getItem("PUBLIC")' }, function (r) {
localStorage['PUBLIC'] = JSON.parse(r[0]);
chrome.tabs.executeScript(tab.id, { code: 'localStorage.getItem("PRIVATE")' }, function (r) {
localStorage['PRIVATE'] = JSON.parse(r[0]);
if (localStorage['PRIVATE'] && tab.url === _config.baseURL + '?extension=1') {
chrome.tabs.remove(tabID);
}
});
});
}
});
The issue lies here is that until user does not grant for "Always Allow on Every Website" (I mean grant permission for https://ourdomain.in/extension?extension=1), chrome.tabs.onUpdate is giving tab.url = "" and it does not give proper URL value so our conditions don't match and know that particular user is signed in or not.
Following is our manifest.json where I have event used optional_permissions:
{
"name": "EXT NAME",
"description": "DESCRIPTION",
"manifest_version": 2,
"version": "1.0.181",
"minimum_chrome_version": "23",
"offline_enabled": true,
"browser_action" : {
"default_icon" : {
"64": "logo/icon.png"
},
"default_title" : "Add here"
},
"background" : {
"scripts" : [
"background.js"
]
},
"content_scripts": [{
"js": [ "content.js" ],
"matches": [ "<all_urls>" ]
}],
"icons": {
"16": "logo/icon_small.png",
"64": "logo/icon.png"
},
"permissions" : [
"https://*.ourdomain.in/*",
"activeTab",
"gcm",
"storage",
"notifications",
"identity",
"contextMenus",
"tabs",
"idle"
],
"externally_connectable": {
"matches": ["https://*.ourdomain.in/*"]
},
"optional_permissions": ["activeTab", "tabs", "storage", "contextMenus", "*://*.ourdomain.in/*"],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"web_accessible_resources": [
"corner.css",
"js/init.js",
"content.js",
"js/jquery.min.js",
"js/taggle.min.js",
"js/typeahead.bundle.min.js",
"ext.html.js",
"assets/*"
],
"commands": {
"_execute_browser_action": {
"suggested_key": {
"default": "Ctrl+Shift+S",
"mac": "Command+Shift+S"
},
"description": "Send a link to DOMAIN!"
}
}
}
And following is the code for permission request which is implemented in click event handler of extension button.
chrome.browserAction.onClicked.addListener(function (tab) {
var reqPerm = {
permissions: ["activeTab", "tabs", "storage", "contextMenus"],
origins: ['https://ourdomain.in/']
};
chrome.permissions.request(reqPerm, function (granted) {
if (granted) {
return go(tab.url);
} else {
console.log("Requested not granted.");
chrome.tabs.sendMessage(tabID, { action: 'signin', text: 'Please allow stash.ai to proceed.' });
}
});
});
Here I am able to see Privacy dialog and I do press Allow for the Day.
Now if I see in Safari > Preferences > Websites > Stash Extension, I am clearly able to see ourdomain.in -> Allowed which proves prompt worked as expected I believe.
Still when new tab is opened for authentication, the above mentioned code for chrome.tabs.onUpdate is executed and gives tab.url = ''. This definitely works when Allow for Every website is turned on.
And other thing is, when I open https://ourdomain.in, my extension icon still shows disabled and on click of the icon it again asks me for the permission. If on this tab, I do give permission, everything works smooth.
Thus, chrome.permissions.request() is no use if I have to manually give permission from tab.
Please let me know any suggestions here.

The answer was so simple, I have to change my reqPerm like,
var reqPerm = {
permissions: ["activeTab", "tabs", "storage", "contextMenus"],
origins: ['https://ourdomain.in/*']
};
So every endpoint in ourdomain.in works.

Related

How can I get data inside parent component in strapi?

I have this single type in my strapi dashboard :
I Have a component called Logo
Another component called Links, it contains another component called Link
Finally a component called MenuButton.
When I go to http://localhost:1337/api/global?populate=* I got :
{
"data": {
"id": 1,
"attributes": {
"createdAt": "2021-12-27T11:54:36.177Z",
"updatedAt": "2021-12-27T11:54:54.737Z",
"publishedAt": "2021-12-27T11:54:54.731Z",
"logo": {
"id": 1,
"name": null
},
"navigation": {
"id": 1 // why I don't get links here ?
},
"menuButton": {
"id": 1,
"icon": ""
}
}
},
"meta": {
}
}
I Already published my content and allowed permissions for public.
My question is :
How can I access to the links inside navigation object ?
See my earlier answer here
Strapi 4 requires you to populate your request (see: population documentation )
which could look like this (for level 2 population):
// populate request
const qs = require('qs')
const query = qs.stringify(
{
populate: {
Product: {
populate: ['Image']
}
}
},
{
encodeValuesOnly: true
}
)
// get id
const id = yourId
// get rquest
const Response= await axios.get(
`http://localhost:1337/api/[your api]/${id }/?${query}`
)
Now media links should be included in your response
To retrieve up to 5 levels deep, you can install this package npm i strapi-plugin-populate-deep

Is there way to redirect strapi error messages which I see in UI to the stdout?

I am using the Strapi v3.0.0-beta.18.7 UI and the error in the UI are shown partly so it is impossible to read the full text of the error message.
I suggest use a custom middleware to manage your needs.
Here is the documentation to create a middleware - https://strapi.io/documentation/3.0.0-beta.x/concepts/middlewares.html
Step 1: Create the middleware
Path — middlewares/log/index.js
module.exports = strapi => {
return {
initialize() {
strapi.app.use(async (ctx, next) => {
await next();
const status = ctx.status;
if (status < 200 || status >= 300) {
console.log(ctx.body);
}
});
},
};
};
Step 2: Enable the middleware
Path — config/environments/development/middleware.json
{
"log": {
"enabled": true
}
}
Step 3: Set the middleware in the right order
Path — config/middleware.json
{
"timeout": 100,
"load": {
"before": [
"log",
"responseTime",
"logger",
"cors",
"responses",
"gzip"
],
"order": [
"Define the middlewares' load order by putting their name in this array is the right order"
],
"after": [
"parser",
"router"
]
}
}

How to constraint a route with a specific role in my case with vue-auth?

I develop my first Laravel application with Vue-js in SPA.
I try to forbid access to a route (view-router) through a role.
For roles, I use the spatie/laravel-permission package.
I set up view-auth rolesVar like this:
const config = {
rolesVar: 'roles',
…
}
Here is an example of a user in json
{
"status":"success",
"data":{
"id":1,
"first_name":"John",
"last_name":"Doe",
"email":"john#Doe",
"created_at":"2019-07-11 11:20:20",
"updated_at":"2019-07-11 11:20:20",
"all_permissions":[
],
"can":[
],
"permissions":[
],
"roles":[
{
"id":1,
"name":"super-admin",
"guard_name":"api",
"created_at":"2019-07-11 11:20:20",
"updated_at":"2019-07-11 11:20:20",
"pivot":{
"model_id":1,
"role_id":1,
"model_type":"App\\Models\\User"
},
"permissions":[
]
}
]
}
}
I try to force my role like this, but it does not work. I am redirected to / 403 (as requested).
I conclude that he does not take my role.
{
path: '/admin',
name: 'admin.dashboard',
component: AdminDashboard,
meta: {
auth: {
roles: { name: 'super-admin' },
redirect: { name: 'login' },
forbiddenRedirect: { path: '/403' }
}
}
},
I think it's because I have several json objects in "roles" but I don't know what to call it for it to work
Thanks for your help !
use server side security instead of client side like this
Route::group(['middleware' => ['permission:permission1|permission2']], function () {
Route::get('/protected_URL',function(){
return "I can only access this if i only have permission1 and permission2";
});
});
this was only authenticated user and user having "permission1" and "permission2" have access to this page, otherwise it will thought access denied error where you can easily catch this error with axios and display Access Denied message

Alexa lambda function is able to execute all intents except custom one?

The launch (New Session), unhandled, and Amazon default intents (cancel, help, stop) are working properly when I test them in the service simulator, but any one that I write doesn't seem to work. Below is an example of a test intent:
var handlers = {
'NewSession': function() {
this.emit(':tell', 'Hello');
'Test': function() {
this.emit(':tell','This intent is working');
},
'Unhandled': function() {
this.emit(':tell','Sorry, I don\'t know what to do');
},
'AMAZON.HelpIntent': function(){
this.emit(':ask', 'What can I help you with?', 'How can I help?');
},
'AMAZON.CancelIntent': function(){
this.emit(':tell', 'Okay');
},
'AMAZON.StopIntent': function(){
this.emit(':tell', 'Goodbye');
},
exports.handler = function(event,context){
var alexa = Alexa.handler(event,context);
alexa.registerHandlers(handlers);
alexa.execute();
};
The code snippet for the intents:
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "ColorIntent",
"samples": [],
"slots": [
{
"name": "Test",
"samples": [
"Test me"
],
"slots": []
No matter what I do, I can't get the test intent to work and return "This intent is working'. Please help!
Maybe it's because your first invocation always gets handled by the NewSession handler, which then responds with 'Hello' and ends the session.
I see two ways to activate your Test handler, i.e. to get your Skill to respond with 'This intent is working':
You create a multi-turn conversation by ending your NewSession handler with this.emit(':ask', 'Hello! What do you want to do next?');, and then uttering 'Test me'.
You replace your NewSession handler with a LaunchRequest handler, and invoke your Skill with 'Alexa, tell tie picker to test me'.
Hope that helps! :)
By the way, because I can't comment everywhere yet: You can log your lambda's state, e.g. for debugging, by using console.log( 'Test handler invoked');, and then looking up the logs in AWS CloudWatch.

IndexedDB "updates" every browser restart and erases data

I wrote a Firefox WebExtension that downloads data files from a website and uses IndexedDB to store/update the data. The .SQLite file that is created is ~2GB in size. Whenever I restart Firefox, the extension executes the onupgradeneeded event, even though I always use version "1". I create the database object stores and indexes in that event, so all my data ends up getting deleted.
The only time this doesn't happen is when I close Firefox while the data is being downloaded or stored. The next time I start Firefox, it does not execute the event (as should be the case). It then continues to update the database as it was programmed to do.
I installed the SQLite Manager extension in hopes that I could identify something causing the issue to the database, but nothing was obvious to me.
Here is part of my background script:
init().then(fetchData).then(addData).catch(dberror);
function init() {
req = indexedDB.open("db", 1);
req.onupgradeneeded = e => {
var name;
var key;
console.log("Upgrading database...", e.oldVersion, e.newVersion);
db = e.currentTarget.result;
var store = db.createObjectStore("db", { keyPath: "KEY" });
db.createObjectStore("version", { keyPath: "version" });
for (name in indexes) {
key = ...
store.createIndex(name, key);
};
};
return new Promise( (resolve, reject) => {
req.onsuccess = e => {
db = e.currentTarget.result;
db.onerror = dberror;
var cursor = db.transaction("MECs").objectStore("MECs").index("STATUS_DATE").openCursor(null, 'prev');
cursor.onsuccess = e => {
if (e.target.result) {
lastMod = e.target.result.key;
fileYear = lastMod.getFullYear();
}
else lastMod = new Date(startingfileYear, 0);
resolve(lastMod);
}
cursor.onerror = reject;
};
req.onerror = e => {
dberror(e);
reject(e);
}
});
}
function fetchData(param) {
// Get data based on the param and return it
return fetchFile(filename);
}
function addData(data) {
var trans = db.transaction("db", "readwrite");
var store = trans.objectStore("db");
var req;
var n = 0;
var data2 = [];
var addPromise;
trans.onerror = event => console.log("Error! Error! ", event.target.error);
trans.onabort = event => console.log("Abort! Abort! ", event.target.error);
data.forEach((row, index) => {
//process data here
data2 = ...
});
(function storeRegData(n) {
var row = data[n];
if (!row) return;
req = store.put(row);
req.onsuccess = event => {
numUpdated++;
storeRegData(++n);
}
req.onabort = event => console.log("Abort! Abort! ", event.target.error);
req.onerror = event => console.log("Error! Error! ", event.target.error);
})(0); // I'm storing one row at a time because the transaction is failing when I queue too many rows.
addPromise = fetchData(data2).then(
response => {
var trans2 = db.transaction("db", "readwrite");
var store2 = trans2.objectStore("db");
var req2;
response.forEach(row => {
req2 = store2.put(row);
req2.onsuccess = event => numUpdated++;
req2.onerror = console.log;
});
return new Promise((resolve, reject) => trans2.oncomplete = e => resolve(response));
},
console.log)
);
return new Promise((resolve, reject) => trans.oncomplete = e => {
if (noMoreData)
resolve(addPromise);
else if (moreData)
resolve( addPromise.then(fetchData).then(addData) );
});
}
And here is my manifest
{
"author": "Name",
"manifest_version": 2,
"name": "Extension",
"description": "Extension",
"version": "3.0",
"applications": {
"gecko": {
"strict_min_version": "50.0",
"id": "myID",
"update_url": "https://update.me"
}
},
"background": {
"scripts": [
"js/background.js"
]
},
"content_scripts": [
{
"matches": [ "https://match.me/*" ],
"js": [
"script.js"
],
"css": [
"style.css"
]
}
],
"icons": {
"48": "icon.png"
},
"options_ui": {
"page": "options.html"
},
"page_action": {
"browser_style": true,
"default_icon": {
"19": "icon-19.png",
"38": "icon-38.png"
},
"default_title": "Extension",
"default_popup": "popup.html"
},
"permissions": [
"https://web.address/*",
"downloads",
"notifications",
"storage",
"tabs",
"webRequest",
"webNavigation"
],
"web_accessible_resources": [
"pictures.png"
]
}
Why does Firefox think the database is at version 0 when I restart the browser? I can use the stored data after I download it, so why does it overwrite it on every restart? I could possibly do a workaround where I only create the store and indexes on extension installation or update, but that's not a solution to the actual issue.
UPDATE: I tried the following to no avail -
Close the database and re-open after storing each data file
Create a new object store for each data file
UPDATE 2: It appears this is related to a storage issue. Apparently, 2GB is the storage limit for non-persistent storage. In Firefox you can by-pass this by making the storage persistent with the following command:
indexedDB.open("db", { version: 1, storage: "persistent" })
See the bugzilla report here.
Unfortunately, when run from a background page, the popup asking for confirmation is not handled, so you can never acknowledge it. Supposedly, when Firefox 56 comes out, you'll be able to use the "unlimitedStorage" permission, which will by-pass the confirmation popup, so it should work from the background page.
Update 3: So it looks like the limit is actually ~1.5 GB. I just spent over a week re-coding the extension to create and use a different database for each year of data, making each database no larger than 150 MB. And still onupgradeneeded executes when I restart the browser and wipes all my data. If, however, I limit the total amount of data in all the databases to the above limit, it works. Unfortunately, I'm still in the same boat.
Does no one have any ideas?
As I mentioned in the updates to my question, there appears to be a limit of ~1.5GB for the "default" storage of indexedDB. Changing the storage to "persistent" will remove that limit. Because persistent storage currently requires user input, however, the database has to be opened from a window that can handle a UI response.
This can be done from the background script by creating a new window with browser.window.create() and opening the database from there. There are security restrictions that prevent inline scripts from running in the new page, so I had to link to local javascript files for that (i.e. <script src="db.js"></script>. I think you can also change the content security policy with a manifest instruction, but I didn't do that.
Hopefully, the unlimitedStorage permission will be supported in Firefox 56, which will remove the popup, allowing a persistent database to be created/accessed directly from the background script.

Resources