I have a custom button which is to query and possibly update an Administration App in Quickbase, which the current user doesn't require access to.
I have JS code which is executed on a button click by the user to check the admin app, etc...
my API call to check the app has the appropriate apptoken and usertoken. However, the browser still has the current user's session cached, so the API call errors out with an access denied error message.
I'm looking for either a way to make a hidden incognito window, to then execute this code, or a way to problematically force the usertoken to supersede the current user access/permissions.
I've seen where chrome extensions can use chrome.windows.create... but I have no experience with extensions, and Ideally, I don't want to have to have an extension for just this functionality, and have to possibly install it on every user's PC for this to work...
Here is a snippet of my current code... This code does work if someone has permissions to the Administration App... but this code is residing in a different application:
PreProcURL = "https://<domain>.quickbase.com/db/<dbid>?a=API_DoQuery&apptoken=<>&usertoken=<>&query={'3'.EX.'1'}";
PreProcQuery.open('GET', PreProcURL, 'async');
PreProcQuery.send();
PreProcQuery.onload = function(){
console.log(PreProcQuery.responseXML);
RunBit = (PreProcQuery.responseXML.documentElement.getElementsByTagName("runbit"))[0].innerHTML;
SupportData = (PreProcQuery.responseXML.documentElement.getElementsByTagName("supportdata"))[0].innerHTML;
if(RunBit != "1"){
$.get("https://<domain>.quickbase.com/db/<dbid>?a=API_EditRecord&rid=1&_fid_6=1&_fid_7="+rid+"&apptoken=<>&usertoken=<>");
}else{
if(SupportData == rid){
alert("This PreProc File is already in progress... please wait.");
}else{
alert("Another PreProc is already in progress... please wait.");
}
}
};
Thanks in advance for any assistance on this.
API calls executed in JavaScript that is hosted within quickbase.com (button, pages, etc.) will run as that logged in user that triggered the script. The usertoken gets ignored.
The most common way to accomplish what you are after is to write the API_DoQuery code on a server side location and then trigger it from your JS code.
Related
I'm currently working on making my code compatible with Safari ITP 2.0. In a method that is triggered on an onClick, I have code similar to the code below:
if (document.hasStorageAccess && document.requestStorageAccess) {
console.log('On Safari 12');
document.hasStorageAccess().then(
function successful(hasAccess) {
console.log('Testing if hasAccess');
if (hasAccess) {
console.log('Access granted already');
} else {
console.log('Requesting access');
document.requestStorageAccess().then(
function successful() {
console.log('Access request was a success');
window.location.reload();
},
function fail() {
console.log('Storage Access API call failed...');
});
}
},
function rejected(reason) {
console.log('hasStorageAccess failed: ', reason);
});
}
However, running this gets me the logging statement "'Storage Access API call failed...'" without a prompt from Safari - what's more frustrating is that it previously worked but is now starting to fail again. Is there any way to debug why the call to requestStorageAccess failed?
I tried enabling the ITP debug mode logs as per the instructions, and I did get some use out of that. It gave me this error once:
2018-09-04 15:15:40.930157-0700 0x110c87 Info 0x0
69100 Safari Technology Preview: (WebKit)
[com.apple.WebKit:ResourceLoadStatisticsDebug] Cannot grant storage
access to example.com since its cookies are blocked in third-party
contexts and it has not received user interaction as first-party.
But when I accessed it in a first party context and reloaded the page, I got no further reasons why the call to requestStorageAccess was failing. If anyone has any ideas, please let me know what you suggest I try to debug the issue.
Thank you!
There are updated debug instructions: https://stackoverflow.com/a/61532464/13443904
But I also wanted to provide some more concrete steps for people struggling with Safari ITP, since it took ages to figure out all the rules.
1) Don't embed requestStorageAccess inside hasStorageAccess. This loses the required user interaction (button click) needed to prompt for requestStorageAccess.
2) hasStorageAccess and requestStorageAccess are promises. Make sure any follow-up actions are nested -inside- the success closures of the promise (ie, if you have a submit button, don't let it submit the form before you've finished asking for requestStorageAccess).
3) You have to set a 1st party cookie and have a user interaction from a top-level window for your subdomain BEFORE you can requestStorageAccess for a 3rd party cookie via user interaction in an iframe for your subdomain. Setting a cookie/interaction in the main domain/parent window does not count.
4) Testing in Safari Technology Preview makes resetting the ITP choices easier - just clear history and quit/reopen and you should go back to scratch. Safari seems to cling to the values forever.
Did you interact (tap/click/form entry) with your website as first party? A mere visit does not suffice. The user has to interact with a webpage with the same eTLD+1 as the domain that is requesting storage access.
Example:
1) service.example is classified with tracking abilities by ITP.
2) The user visits and interacts with a page from service.example or *.service.example.
3) service.example calls the Storage Access API under othersite.example when the user taps in service.example’s iframe.
I've been trying to figure out a way to use notifications on a background process and couldnt find anything online about it. So, I figured out one way around it and wanted to share (Not sure if this is the best way to go about doing this but here goes:)
Problem: I want to notify the user of new info when the page is running but in the background (blurred). I could use alert('new info!'); to get the taskbar icon to flash, but then you have to manually dismiss it (tried it and it's hella annoying). I really liked the notifications, but they only work if the user performs an action, so not helpful...
I hope I won't be telling something stupid, but from where I see it (and remember from school) that's basically how http works : a request is sent to the server, which issues a response eventually after executing some server-side code.
Basically you're asking for a "PUSH" functionality from server to client, and in that case you can't make use of HTTP.
Some tricks exist to work around this limitation, but basically they're all issuing requests at a certain frequency (Dave's answer does exactly that). If your site doesn't change that much, that means a lot of requests are issued for no reason (nothing has changed), consuming bandwith for nothing.
From what I know, the answer to this is called Websockets, which are supported by recent browsers only. I never had the chance to use it though so I couldn't tell much more about it. This allows full duplex communication, thus allowing server to "push" data to the client. I guess that's what SO uses for "new message" notifications (top left of the screen - you see immediately when you receive a new message)
My solution: I made a chrome extension that runs in the background and triggers the notifications. It's a little limited in scope as you need to have chrome to do it, but it does what i need it to, and for the purposes of the problem i'm working on, i can just make my user group use chrome ;D
The specifics: The extension only has two components, the manifest and a script. Currently, i setup the manifest so that it only works on my site using the match identifier... and i set the permissions to include notifications.
The JS script has a window.setinterval that looks for an element in the page with the id NOTIFIER. If it's empty, it does nothing, otherwise it creates a notification based on the content and then clears the content to prevent showing the same notification multiple times... (I tried using .onchange for that element, but couldn't get the event to trigger... I'd prefer to do this on an event rather then setInterval)
Notify.js
function onExtLoad() {
var timer = setInterval(refresh,1000);
}
document.addEventListener('DOMContentLoaded', onExtLoad());
function refresh() {
if (document.getElementById('NOTIFIER').innerHTML == "") {
//do nothing?
} else {
var notification = webkitNotifications.createNotification("",
"You got a new message",
document.getElementById('NOTIFIER').innerHTML);
notification.show();
document.getElementById('NOTIFIER').innerHTML = "";
}
}
Then, all i need to do is have the JS on the page control when it adds info the the NOTIFIER and voila! notifications!
Hope this helps someone else.
#ExpertSystem: I messed around with the MutationObserver but I can only get it to trigger once. Here's a JSFiddle: http://jsfiddle.net/BTX8x/1/
Am I missing something? Is there a way to reset it?
EDIT: Figured it out, i needed subtree:true
I want to make a Chrome extension that does this (automatically logs onto a website, probably not the best approach since it was my first time doing JS) and has an option page where the user can set up his info and get it saved (localstorage?) so that the content script can access it as well as the options page.
Here's what I've come to after a whole lot of researching, looking at examples etc.:
http://pastebin.com/yTiXp4VY (source code of all files there).
In the end I gave up and just used trial and error so there's tons of errors there. The console is reporting it can't run for security reasons. Please explain me what is wrong with this version. I don't need you to fix my code, I am just learning JS.
For most part you code seems to be correct. Only one note: your options page can access localStorage directly, it doesn't need the background page for that. But the main issue seems to be the way your content script is built - it assumes that the user/password data will be available immediately (synchronously). That's not how it works: you are sending a message to the background page, eventually the background page will answer and your callback in the content script will be called. The simplest approach is to delay all actions until that happens and you have the data. And it should be easier to send a single message to the background page that will give you all the data. Something like this:
var userName = null;
var passWord = null;
chrome.extension.sendRequest( { eventName: "getLogin" },
function(response) {
userName = response.user;
passWord = response.pass;
// Now we can continue doing whatever we are doing
checkIfAutoUrl();
}
);
And the message processing in your background page would look like this:
function onRequest(request, sender, sendResponse) {
if (request.eventName == "getLogin") {
sendResponse({user: getStorage("user"), pass: getStorage("pass")});
}
}
Note that the response is an object with two properties user and pass here - this way the content script can immediately continue once it got the response, it has all the necessary data and doesn't need to wait for a second response.
Update: Concerning the error message you get: the Content Security Policy is meant to protect your extension from being exploited by a malicious website. One part of that protection is disallowing inline scripts in HTML - like onload="load_options()". All scripts that will run need to be placed in your JavaScript files. So instead of this onload attribute you can put the following into options.js:
window.addEventListener("load", load_options, false);
See addEventListener documentation on MDN.
I'm getting conflicting results between the facebook javascript SDK and the python requesthandler variables. The Javascript SDK says my user is not logged in, which is correct, while my template variable that comes from the base request handler says that my user is logged in and displays the name of the user. Is there enough info to tell what is wrong or should I paste the code I think is relevant here? A link to the login page that has the error is here. The example I used is called the runwithfriends demo app from facebook and everything with that app worked except using the logic from the app just from a website without requiring the user to be in the iframe of the app.
Plus I can't seem to get the real-time API working. I can only save userID and not refresh user data - why? I have the code but I'm not sure what's most relevant but here's some of the request handler, the relevant code is basically exactly the same as the one from the demo app:
def render(self, name, **data):
logging.debug('render')
"""Render a template"""
if not data:
logging.debug('no data')
data = {}
data[u'js_conf'] = json.dumps({
u'appId': facebookconf.FACEBOOK_APP_ID,
u'canvasName': facebookconf.FACEBOOK_CANVAS_NAME,
u'userIdOnServer': self.user.id if self.user else None,
})
data[u'logged_in_user'] = self.user #variable that is the problem
data[u'message'] = self.get_message()
data[u'csrf_token'] = self.csrf_token
data[u'canvas_name'] = facebookconf.FACEBOOK_CANVAS_NAME
self.response.out.write(template.render(
os.path.join(
os.path.dirname(__file__), 'templates', name + '.html'),
data))
And even more strange, I can also get the application in a state where the javascript SDK says the user is logged in and the template variable logged_in_user says otherwise. Why are the variables conflicting?
Update: Here are screenshots from the strange login flow. I can go to my page and my name from facebook appears:
Then when I go to next page it also looks alright and has my name
But if I log out then I gets in impossible state: my name + logged out
How can I resolve this strange conflict between js and back-end?
Update: Since I only have this problem for one of my apps I can take what works from my other app and integrate. This page seems to work from my other app: http://cyberfaze.appspot.com/file/20985
Your 'user' is probably referring to the Django user not the Facebook user. Make sure you synchronize the two accounts correctly using a custom authentication backend. It's possible that the accounts get out of sync i.e. if the user switches browsers.
Keep in mind that the Facebook Python SDK will stop working after October 1st unless they update it to Oauth2.0 which is unlikely.
I just updated django-facebook-graph to work with the new authentication flow.
There are some apps that require login. May I know how the app manage user session on android? For example, once the user has sign in to the app, the next time user start the app, it will straight go to the home page instead of the login page. While if user start the app for the first time or before login, the app will start with the login page. Can phonegap handle this? Thank you.
Depending on which version of Android you are targeting and which version of PhoneGap you are running, you should use one of the built-in offline storage mechanisms available in the browser. These include localStorage and WebKitSQLite.
There is also a fantastic open source library that you can use that abstracts any specific offline storage mechanism and allows for swappable underlying storage adapters, and provides a single unified key/value interface. The library is called Lawnchair - check it out!
So on-load, you would instantiate Lawnchair and see if your saved user parameters exist:
function onLoad() {
myStore = new Lawnchair();
myStore.get('login', function(i) {
if (i == null) {
// user did not login before, no saved credentials.
} else {
// user DID login, we can now auto-login for the user.
}
});
}
Additionally, following a successful manual login, you can save the credentials to Lawnchair so that they will be available next time your PhoneGap app loads and checks for their existence:
function login(username, password) {
/*
* Do the login stuff here
*/
if (/* login was successful */) {
myStore.save({key:'login',value:{username:username, password:password}});
} else {
alert('Could not log you in!');
}
}
The most important bit between the two chunks of code you see here is the first parameter to the get() function, and the key property to the object passed into the save() function. Both of these need to match to retrieve the same object.
Hope that helps!
You can store it in the prefs. Here is how
SharedPreferences settings = .getSharedPreferences("some_key, MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString("token", "123456");
editor.commit();
Next time the app starts up you can check to see if this exists. Hope it helps
Don't know what is phonegap, but I think usually apps simply store the login somewhere in shared preferences. So, all you need is to check on startup if there is some particular preference. If there isn't any, then the login page should be shown.