ckan.toolkit.redirect_to does not redirect - python-2.7

I'm currently developing an ckan extension, where i need to redirect to a url on a different domain.
In my plugin i defined a custom action function:
#side_effect_free
def download_json(context, data_dict):
toolkit.redirect_to('http://my.json-builder.com?id=1234')
But when i call this endpoint i just get following response:
response screenshot
So i assume that the action function is called, but the redirect_to call does not redirect to the url i defined.
Thanks for your help!
Florian

It's a bit hard to figure out what you're trying to accomplish but here's a few things I hope will help.
Short Answer:
No, you can't redirect from an API endpoint in CKAN. The endpoint response in CKAN is built up and expects certain things from your action. Your action should return some kind of result. In your case it's returning nothing but trying to redirect. A logic action function with IActions is not the same as a Blueprint or pylons controller action.
See Making an API request docs, specifically the breakdown of an API response in CKAN. Also, you can review the pylons implementation that builds up the API response or the flask blueprints implementation.
More Info to help with your approach:
You say you are trying to call an endpoint that redirects a user to a different domain url. Based on this consider the following:
The first thing I thought you wanted was to have a url that someone goes to through the web interface of your site and are redirected to another site. In this case your example code of toolkit.redirect_to('http://my.json-builder.com?id=1234') makes sense and works for a custom controller action using/implemented with IRoutes or if you're using flask then IBlueprint. A User would go to a URL on your site such as http://localhost.com/download_json and be redirected to the new URL/site in their browser.
If you are intending this to be an API call for other users this starts to feel a little bit odd. If a user is using your API, they would expect to get results from your site in JSON CKAN's API is designed to return JSON. Someone consuming your API endpoint would not expect to be redirected to another site e.g. if I called http://localhost.com/api/3/action/download_json I would expect to get a JSON object like
{
help: "http://localhost/api/3/action/help_show?name=download_json",
success: true,
result: {
...
}
}
They would look for success to make sure the call worked and then they would use the result to keep moving forward with their desired processes. If you do want someone via an API to get redirect info I'd likely return the redirect url as the result e.g. result: {'redirect_url': 'http://my.json-builder.com?id=1234'} and document this well in your extension's API docs (e.g. why you're returning this endpoint, what you expect someone to do with it, etc).
If this is an API call for your own extension I'm guessing what you are trying to do is use my.json-builder.com to build a json of something (a dataset maybe?) and return that json as the result at your endpoint or maybe even consume the result to make something else? If that's the case, then in your function you could make the call to my.json-builder.com, process the results and return the results to the user. In this case, you're not actually wanting to redirect a user to a new site but instead make a call to the new site to get some results. If you actually want the results for your extension you don't need an additional endpoint. You could make the call from your extension, consume the results and return the desired object you're trying to create.
Hope this helps and sorry if I've miss-understood completely.

Related

Access Cookies in Postman Native App Pre-Request Script

Apologies if been asked before. Tried a quick search and couldn't find.
Situation :
The api is using an authentication token as a cookie name "abc-auth" and this is returned when i hit a /login endpoint. It is returned as a set-cookie header in the response which postman the native app happily accepts and sets up as a domain cookie in the ui
I hoped to basically as a pre-request step hit the login endpoint if the cookie doesn't exist but not hit it if we're already authenticated. So we only login once for the 20 requests rather than 20 times
I had hoped to do this accessing the pm.cookies object which I believe is now fully baked in to the native apps ref -> https://www.getpostman.com/docs/v6/postman/scripts/postman_sandbox_api_reference
So was hoping to do something like this
console.log(pm.cookies.toObject())
if (pm.cookies.has("abc-auth")){
console.log("Found Cookie");
} else {
//send the request
}
Expected :
That it runs the first time logs in and then next time finds the cookie and continues
Actual :
It never finds the cookie. Printing out the cookie list finds an empty array. I am seemingly unable to check cookies from the script.
Anyone know what I'm doing wrong?
A lot of the docs refers to interceptor but as the chrome app is being retired and native app was meant to assume that functionality I would really like the answer to be contained within the native app
Thanks!
Would something like this work for you to do the check:
if (_.keys(pm.cookies.toObject())[0] === "abc-auth"){
console.log("Found Cookie")
} else {
//Do something
}
It's using the Postman cookies function but also the Lodash keys function (which is comes with the native app) It's basically assuming that the first key is the one you want - That's probably not right as it might have several keys.

Qt QDesktopServices::openUrl - launch browser with post values

I'm trying to write a simple application that will launch a browser and send it to a URL based on a user's input.
QDesktopServices::openUrl(QUrl(url));
However, I'd like to pass variables along with whatever URL they submit using POST.
For GET, all I'd need to do is simply embed the values into the URL string, but how would I go about adding POST variables?.
Thanks.
QDesktopServices wasn't designed for this, I'd suggest doing your HTTP POST using QNetworkAccessManager::post instead.
You can then possibly take some information from the HTTP response to open the desktop browser if this is necessary.
From the official documentation:
bool QDesktopServices::openUrl(const QUrl & url) [static]
Opens the given url in the appropriate Web browser for the user's desktop environment, and returns true if successful; otherwise returns false.
If the URL is a reference to a local file (i.e., the URL scheme is "file") then it will be opened with a suitable application instead of a Web browser.
The short answer is that it was not meant to be a network managet. For that purpose, one could already use the QNetworkAccessManager. It was just a convenient way to add support for opening up an URL as that would require quite a bit of work otherwise. There were no further plans to it to replicate QtNetwork more closely.
Thereby, I would suggest to use something like this to achieve working with post methods given your url:
QUrlQuery urlQuery;
urlQuery.addQueryItem("param1", "value1");
urlQuery.addQueryItem("param2", "value2");
QUrl url = QUrl("http://foo.com");
QNetworkRequest networkRequest(url);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
networkManager->post(networkRequest, urlQuery.toString(QUrl::FullyEncoded).toUtf8());
If you have no issue with maintaining an external web service, you could set up a GET-to-POST redirection service (since QDesktopService::openUrl can pass url query strings to browsers without issue). Two things to keep in mind when going this route are to a) properly validate the requests the service recieves against some sort of whitelist to avoid security issues that stem from open http redirection, and b) to consider URL length limitations of both the user's desktop browser and server handling the redirects.
If we ignore IE and edge, desktop web browsers seem capable of handling URLs 32k-bytes long or better (figure obtained from a quick web search, may be inaccurate). If you're also targeting older android phones, the length limit drops to 8k.
Another way is to use QWebView which doesn't suffer from the same flaws as QDesktopServices: https://doc.qt.io/archives/qt-5.5/qwebview.html#load-1 . The only issue with this is that it will require use of the webkitwidgets module which may or may not be an issue for you.
Side note: I'm also still trying to find a way deal with the QDesktopServices problem. If you found a better way to send a POST request through the user's default browser, please post it here so that others can benifit.
Cheers.

How to monitor an action by user on Glass

I have a mirror API based app in which i have assigned a custom menu item, clicking on which should insert a new card. I have a bit of problem in doing that. I need to know of ways i can debug this.
Check if the subscription to the glass timeline was successful.
Print out something on console on click of the menu.
Any other way i can detect whether on click of the menu, the callback URL was called or not.
It sounds like you have a problem, but aren't sure how to approach debugging it? A few things to look at and try:
Question 1 re: checking subscriptions
The object returned from the subscriptions.insert should indicate that the subscription is a success. Depending on your language, an exception or error would indicate a problem.
You can also call subscriptions.list to make sure the subscriptions are there and are set to the values you expect. If a user removes authorization for your Glassware, this list will be cleared out.
Some things to remember about the URL used for subscriptions:
It must be an HTTPS URL and cannot use a self-signed certificate
The address must be resolvable from the public internet. "localhost" and local name aliases won't work.
The machine must be accessible from the public internet. Machines with addresses like "192.168.1.10" probably won't be good enough.
Question 2 re: printing when clicked
You need to make sure the subscription is setup correctly and that you have a webapp listening at the address you specified that will handle POST operations at that URL. The method called when that URL is hit is up to you, of course, so you can add logging to it. Language specifics may help here.
Try testing it yourself by going to the URL you specify using your own browser. You should see the log message printed out, at a minimum.
If you want it printed for only the specific menu item, you will need to make sure you can decode the JSON body that is sent as part of the POST and respond based on the operation and id of the menu item.
You should also make sure you return HTTP code 200 as quickly as possible - if you don't, Google's servers may retry for a while or eventually give up if they never get a response.
Update: From the sample code you posted, I noticed that you're either logging at INFO or sending to stdout, which should log to INFO (see https://developers.google.com/appengine/docs/java/#Java_Logging). Are you getting the logging from the doGet() method? This StackOverflow question suggests that appengine doesn't display items logged at INFO unless you change the logging.properties file.
Question 3 re: was it clicked or not?
Depending on the configuration of your web server and app server, there should be logs about what URLs have been hit (as noted by #scarygami in the comments to your question).
You can test it yourself to make sure you can hit the URL and it is logging. Keep in mind, however, the warnings I mentioned above about what makes a valid URL for a Mirror API callback.
Update: From your comment below, it sounds like you are seeing the URL belonging to the TimelineUpdateServlet is being hit, but are not seeing any evidence that the log message in TimelineUpdateServlet.doPost() is being called. What return code is logged? Have you tried calling this URL manually via POST to make sure the URL is going to the servlet you expect?

Using Request, HttpNtlmAuth to make a system call with authentication

I've got the following snip of code the has the audacity to tell me it is "FAIL to load undefnied" (the nerve...) I'm trying to pass my authenticated session to a system call that uses javascript.
import requests
from requests_ntlm import HttpNtlmAuth
from subprocess import call
# THIS WORKS - 200 returned
s = requests.Session()
r = s.get("http://example.com",auth=HttpNtlmAuth('domain\MyUserName','password'))
call(["phantomjs", "yslow.js", r.url])
The issue is when "calL" gets called - all I get is the following
FAIL to load undefined.
Im guessing that just passing the correct authenticated session should work - but the question is how do I do it such that I can extract the info I want. Out of all the other attempts this has been the most fruitful. Please help - thanks!
There seem to be couple things going on here so I'll address them one-by-one.
The subprocess module in python is meant to be used to call out to the system as if you were using the command line. It knows nothing of "authenticated session"s and the command line (or shell) has no knowledge of how to use a python object, like a session, to work with phantomjs.
phantomjs has python bindings since version 1.8 so I would expect this might be made easier by using them. I have not used them, however, so I can not tell you with certainty that they will be helpful.
I looked at yslow's website and there appears to be no way to pass it the content that you are downloading with requests. Even then, the content would not have everything (for example: any externally hosted javascript that would be loaded by selenium/phantomjs or a browser, is not loaded by requests)
yslow seems as though it normally just downloads the URL for you and performs its analysis. When the website is behind NTLM, however, it first sends the client a 401 response which should indicate to the client that it must authenticate. Further, information is sent to the client that tells it how to authenticate and provides it parameters to use when authenticating for NTLM. This is how requests_ntlm works with requests. The first request is made and generates a 401 response, then the authentication handler generates the proper header(s) and re-sends the request which is why you see the 200 response bound to r.
yslow accepts a JSON representation of the headers you want to send so you can try to use the headers found in r.request.headers but I doubt they will work.
In short, this is not a question that the people who normally follow the requests tag can help you with. And looking at the documentation for yslow it seems that it (technically) does not support authentication of any type. yslow developers might argue though that it supports Basic Authentication because it allows you to specify headers.

Django redirect to site root with variable....?

I'm trying to redirect the browser back to the site root and also pass a variable in order to trigger a JS notification function... This is all with Django.
What I have now is this:
urls.py:
url(r'^accounts/password/reset/complete/$', views.passwordResetComplete,
name='password_reset_complete'),
views.py:
def passwordResetComplete(theRequest):
return redirect(home(theRequest, 'Password reset successful'))
def home(theRequest, myMessage=None):
.........
return render_to_response('new/index.html',
{
"myTopbar": myTopbar,
"isLoggedIn": isLoggedIn,
"myMessage": myMessage
},
context_instance=RequestContext(theRequest)
)
I get this error:
NoReverseMatch: Error importing 'Content-Type: text/html; charset=utf-8.......(gives full HTML of page)
I've been working around a few different solutions and nothing seems to work in the way that I need. The closest I've got is to redirect to '/?query-string' with a JS function in root to check for that query-string and run the function if it's present. However, that leaves the query-string in the URL for the duration of the user's navigation of the site (which is 100% AJAX). I want to avoid having any strings/long hrefs in the URL.
Would be really grateful if anyone can tell me how to solve this problem.
HTTP is a stateless protocol, which means that each and every request is entirely unique and separated from anything and everything that has ever been done before. Put more simply, the only way (in HTTP) to "pass a variable" with a URL is to add it to the URL itself (/someobject/1/, for example, where 1 is an object id) or in the querystring (?someobject=1). Either way, the information is embedded in the URL and it's up to your application to decipher that information out of the URL and do something with it.
The concept of a "session" was introduced as a way to provide state to the stateless protocol that is HTTP. The way it works is that the server sends the client a cookie containing some identifiable information (usually, just a session id). Then, the client sends the cookie back to the server in the request headers with every request. The server sees the cookie, looks up the session and continues on seamlessly with whatever is in progress. This is not true state, but it does provide the ability to essentially mimic state, and it's the only way to pass data between requests without actually embedding the data in the URL.
If all you need to return back is a message to the user such as "Password reset successful", you can and should simply use Django's messages framework, which itself uses the session pass the message. It sets a cookie for the client, so that you can redirect to any URL. The cookie will be passed back with the request for that new URL, and Django will add the message from the session into the appropriate place in your template for that URL.
If you need to actually invoke a bit of JavaScript, then you should make the request via AJAX. In the response, you can return any data you want in via JSON (and act on that data however you like) or even return Javascript to be run.
Following the redirect docs, you cannot simply redirect to a view, but only to a url or an object/view that is a assigned to a url already. Thus, you have 2 options:
a) Call the view directly like that:
return home(theRequest, 'Password reset successful')
b) Add a Url patterns like that:
url(r'^your_patterns/$', views.home, msg='',name='home'),
Then you will be able to do what you initally did:
return redirect(views.home,('Password reset successful',))
or from my point of view, even tidier:
return redirect('home',('Password reset successful',))