In my view.py I have the following check on several consecutive pages:
if(request.META.get('HTTP_REFERER') != request.build_absolute_uri(reverse('page1'))):
return redirect('page1')
This is performed on every page, checking that the user was redirected from the previous page, so (for example), if a user tries to enter the url for page4 in the address bar, the test will fail and he will be sent to page3, then that pages test will fail and he will fall back to page2, and so on.
Im doing this because I have several pages linked together which users must visit consecutively.
The problem comes when I want to unit test. The following test would throw an error because it fails the redirect test and therefore cannot test the logic of the page which im trying to test:
def test_user_information_updated_on_validation_success(self):
user = User.objects.create_superuser('username')
self.client.force_login(user)
self.client.post(reverse('page_4'), {
'exampleQuestion': 'exampleAnswer'
})
user.refresh_from_db()
self.assertEqual(user.exampleform.somefield, 'exampleAnswer')
How can I access the page within a unit test as if it had been redirected.
Thank you.
A request in the test client accepts keywords that are mapped to WSGI environment variables. Environment variables that start with HTTP and are all uppercase, with dashes mapped to underscores - are Http headers, so the short version is that we can set HTTP headers as such:
# Wrong: Generates absolute paths without hosts
self.client.post(
reverse('page_4'), {'exampleQuestion': 'exampleAnswer'},
HTTP_REFERER=reverse('page_3')
)
Edit:
It's a little too simple, because HTTP referrers are fully qualified, so we need:
referer = 'http://testserver{}'.format(reverse('page_3'))
self.client.post(
reverse('page_4'), {'exampleQuestion': 'exampleAnswer'},
HTTP_REFERER=referer
)
FYI: The protocol (wsgi.scheme) and host name (HTTP_SERVER) come from djang.test.client.RequestFactory._base_environ(). If you use a modified client that changes servername and/or protocol, you should adjust accordingly. In that case it would be wise to override _base_environ in your modified test client and derive values from that.
Related
https://github.com/haricot/django-cookie-consent
https://django-cookie-consent.readthedocs.io/en/latest/index.html
I found a fork of the django-cookie-consent github project for managing cookies on your website and I got it to work most of the time but it is not 100% perfect.
Here is how I got it to run (either install via pip from that fork link or):
Do not use pip3 install django-cookie-consent from the default PyPi. Download the zip file from github and copy the cookie_consent folder to your site packages folder. For example for me it was - /home/user/.local/share/virtualenvs/project_name/lib/python3.7/site-packages/cookie_consent. Then pip3 install django-appconf. Then follow the documentation instructions.
Links:
http://127.0.0.1:8000/cookies/
http://127.0.0.1:8000/cookies/accept/
http://127.0.0.1:8000/cookies/accept/variable_name/
http://127.0.0.1:8000/cookies/decline/
http://127.0.0.1:8000/cookies/decline/variable_name/
I found some code for the consent banner https://github.com/haricot/django-cookie-consent/tree/master/tests/core/templates but was having problems with it. I copied the test_page.html template code to my own project's base.html but this entire script tag did not work for me -> <script type="{% cc_receipts "social" %}" data-varname="social">. I got django.template.exceptions.TemplateSyntaxError: 'cc_receipts' did not receive value(s) for the argument(s): 'request'. Copying the rest of the code from that file and not including that one script tag did cause the banner to show up on my project's base.html file.
Accepting a cookie from clicking accept on the banner code found from the tests directory just redirects me to a blank /cookies/accept/social/ page. This acceptance does not get logged either.
Accepting a cookie from /cookies/ does get logged but it gave me this error:
TypeError: cannot use a string pattern on a bytes-like object
[20/Jan/2020 16:00:43] "POST /cookies/accept/social/ HTTP/1.1" 500 121416
Method Not Allowed (GET): /cookies/accept/social/
Method Not Allowed: /cookies/accept/social/
[20/Jan/2020 16:00:44] "GET /cookies/accept/social/ HTTP/1.1" 405 0
Is this error a possible python3 incompatibility issue?
How would I configure, for example, where a group variable name called social and a cookie named 1P_JAR (this is an example of a recaptcha v3 cookie on my site).
Noticed that the username is not being logged or the the user's IP address. It would be nice to include these once they accept or decline.
I am not sure if this fork automatically blocks cookies until the user accepts. Can someone verify this? If this feature is or is not included, how do you implement it?
When accepting cookies or declining cookies, an actual cookie called cookie_consent gets created in your browser and it tells you which cookies are accepted or declined.
Can someone please help me get this to work? It seems very close to being GDPR compliant.
Check your runserver log. You have to set up COOKIE_CONSENT_NAME setting. Because there is no default value for this setting.
Then you have to go to django admin panel and create the cookies with their respective names and domains which you can find in the browser inspector.
how does it works: in creates a cookie labelled cookie_consent, this stores all the data necessary for this package to work. to make it work properly, several tweaks are required:
1)into setting.py you must indicate COOKIE_CONSENT_NAME = "cookie_consent" (or whatever you like probably works too)
into TEMPLATE_CONTEXT_PROCESSORS copy: 'django.template.context_processors.request' --OR-- copy into:
TEMPLATES = [
{
...
'OPTIONS': {
'context_processors': [
"django.template.context_processors.request",
...
3)[NOT MANDATORY BUT HELPFUL, see instuctions on django documentation] i have also indicated into setting COOKIE_CONSENT_CACHE_BACKEND = "default" plus
setted the whole website cache to: django.core.cache.backends.db.DatabaseCache
then you create on the admin page firstly a cookie group into cookie groups window, WITHOUT checking the Is required checkbox (otherwise you are not able to manage the cookie group because the checkbox implies that these cookies are always on and thus the user cannot decide or not to eliminate them); then you must also add at least 1 cookie for cookie group
(otherwise the get_version will not work for cookie groups and its mandatory to the correct use of the library)
consensus is gathered for groups and not for each cookie (As GDPR suggests), meaning that the accept_cookies(request, response, varname="your_cookie_group") will only works if you use cookie groups.
this is an exaple of the functions (not perfect) telling to accept the cookie from view, to work it requires obviously two refresh, the first to set the cookie, the second to see it:
---views.py
def homepage(request):
#to render the page
response = render(request = request,
...
#to accept a cookie group
accept_cookies(request, response, varname="cookie_group")#probabilmente solo per cookie groups
#to check the cookie group
cc = get_cookie_value_from_request(request, varname='cookie_group')
print("cookie value from request: ",cc)
if cc == True:
print("Consensus given", cc)
elif cc==False:
print("Consensus not given",cc)
else:
print("probable error in getting cookie value from request: ", cc) # problem with cookie group
return response
---urls.py
path('', views.homepage, name ="home")
I have been trying to get a Django webapp run in local. What I have done was set my debug = True and basically carried my settings.py file over from my test server. I finally got my python manage.py runserver [::]:8000 to run smoothly without errors. The problem that I am now having is that I cant access the correct idea.
Sorry for any incorrect terms but lets say my application is at three links www.webapp.com, www.buying.webapp.com and www.selling.webapp.com where the SITE_IDs are 1,2,3. I have learned to use localhost:8000 to access webapps before but that was with my simpler webapps.
Any help would be appreciated! Thank you.
EDIT:
I followed DOMAINS_URLCONF and found this
class SubdomainMiddleware:
""" Make the subdomain publicly available to classes """
def process_request(self, request):
domain_parts = request.get_host().split('.')
if (len(domain_parts) > 2):
subdomain = domain_parts[0]
if (subdomain.lower() == 'www'):
subdomain = None
domain = '.'.join(domain_parts[1:])
else:
subdomain = None
domain = request.get_host()
# if subdomain in settings.DOMAINS_URLCONF:
# request.__setattr__('urlconf',settings.DOMAINS_URLCONF[subdomain])
try:
current_site = Site.objects.get(domain=request.get_host())
except Site.DoesNotExist:
current_site = Site.objects.get(id=settings.SITE_ID)
request.current_site = current_site
#settings.SITE_ID = current_site.id
request.subdomain = subdomain
request.domain = domain
For that to work, you simply need different domains on your local machine. That can be simply achieved by editing your local /etc/hosts file (on windows it is C:\Windows\System32\drivers\etc\hosts). Simply add this line in it:
127.0.0.1 buying.x.localhost selling.x.localhost
Update:
After seeing your middleware code, there is one more caveat: local domains for buying and selling should have at least 3 parts. I've just added .x in the middle of domains above, so it should be parsed correctly by this middleware. You can structure those urls as you wish, but they have to have at least 3 parts and the first part has to match one of the subdomains of your project.
After doing this, if you type just localhost:8000 in the address bar of your browser, you should see the main page, by entering buying.x.localhost:8000 you will see the content of buying. subdomain and by entering selling.x.localhost:8000 you will see content of selling. subdomain.
I'm working with google API lately and use simple flask method to retrieve some id_token.
here is my code with explanations in comment:
#app.route('/afterlogin/id_token')
def afterlogin(id): # get the id
print(id) # print it
return render_template(r'creds_view.html', data=id) # and render the template with 'id' in it (for test purposes)
So what happens is that after the user logins, the api redirects the id_token to http://localhost:8000/afterlogin/#id_token=some_id_token.
but for some reason it is showing me 404 error.
i think it is because of the '#' in the url , i want the id_token. i know that '#' in html means for path linking or routing in 'href'.
so for that i tried.
#app.route('/afterlogin/<path:id>')
but the error still persists.
any guesses?
Everything after # is processed locally by the browser, it's not sent to the server, so you can't use it in routing. Leave out the #:
http://localhost:8000/afterlogin/some_id_token
Apologies because the only web development I know is of the django/python kind and am probably guilty of mixing my code idioms ( REST vs django URL dispatch workflow)
I have a URL handler which serves as a callbackUrl to a subscription for my Glassware. I am getting a POST to the handler , but the request object seems empty.
I am sure I am understanding this wrong but can someone point me in the direction of getting the "REPLY" information from a POST notification to a callbackURL.
My URL Handler is
class A600Handler(webapp2.RequestHandler):
def post(self):
"""Process the value of A600 received and return a plot"""
# I am seeing this in my logs proving that I am getting a POST when glass replies
logging.info("Received POST to logA600")
# This is returning None
my_collection = self.request.get("collection")
logging.info(my_collection)
# I also tried this but self.sequest.POST is empty '[]' and of type UnicodeMultiDict
# json_request_data = json.loads(self.request.POST)
#util.auth_required
def get(self):
"""Process the value of A600 received and return a plot"""
logging.info("Received GET to this logA600")
I have the following URL Handler defined and can verify that the post function is getting a "ping" when the user hits reply by looking at the app-engine logs.
MAIN_ROUTES = [
('/', MainHandler),('/logA600',A600Handler),
]
How do I extract the payload in the form of the voice transcribed text sent by the user?. I am not understanding The "parse_notification" example given in the docs
Did you try request.body? The docs for request.POST state
"If you need to access raw or non-form data posted in the request, access this through the HttpRequest.body attribute instead."
If the API isn't using form data in its post, you'll likely find the contents in request.body. The docs to which you linked indicate that the content will be placed as JSON in the body instead of form data ("containing a JSON request body"). I would try json.loads(request.body).
I am also having this issue of Mirror API calling my application for notifications, and those notifications are empty. My app runs on tomcat so its a java stack. All the samples process the notification like this:
BufferedReader notificationReader = new BufferedReader(
new InputStreamReader(request.getInputStream()));
String notificationString = "";
// Count the lines as a very basic way to prevent Denial of Service
// attacks
int lines = 0;
while (notificationReader.ready()) {
notificationString += notificationReader.readLine();
lines++;
// No notification would ever be this long. Something is very wrong.
if (lines > 1000) {
throw new IOException(
"Attempted to parse notification payload that was unexpectedly long.");
}
}
log.info("got raw notification " + notificationString);
For me this is always logging as empty. Since a notification url must be https, and for testing I could not use an IP address, I have setup dyndns service to point to my localhost:8080 running service. This all seems to work but I suspect how dyndns works is some type of forward or redirect here post data is removed.
How can I work around this for local development?
Updated:
Solved for me.
I found closing the response before reading request caused issue that request.inputStream was already closed. MOving this
response.setContentType("text/html");
Writer writer = response.getWriter();
writer.append("OK");
writer.close();
To after I fully read in request notification into a String solved the issue.
On my Pylons website, I have my login form sending it's data to 'https://mysite.com'. Upon a successful login, a redirect takes place to send them to their profile page.
redirect(url(controller='profile'))
This sends the user to http://mysite.com/profile instead of https://mysite.com/profile. The only way I've found to fix this is to change the redirect to:
redirect(url(controller='profile', protocol='https'))
The problem I have with this is "what if, for whatever reason, my cert goes away and I have to drop SSL" I don't want to have to go through my entire code looking for all redirects I specify the 'https' protocol in. I want my login to send the user to HTTPS and that's it...
Is there a reason the redirect drops to HTTP? Is there a way to stop it? :/
Since I spent a couple of hours wading through the pylons/routes/beaker/etc. source I thought I'd share my solution.
First a bit of context. I'm using an elastic load balancer (ELB) on AWS with SSL termination. The application is built to run solely over https; this is a post-firesheep world after all. It's layered like so:
ELB -> nginx -> pasteWSGI -> pylons
ELB is jolly good in terms of simplicity but any call to pylons.controllers.util.redirect would trigger a 302 Redirect to "http://mysite/". The ELB would not change that on the way back (no reason to) and so my browser would be sent back to port 80 and there is no ELB listening on that port.
I've tried updating the Mapper as suggested above.
it did not work,
I wanted my redirects to be relative. Switching to https in pylons means that the URL generator goes and fetches the host to create a new URL (https://localhost/....)
Note that Mapper.redirect_to works out of the box and uses relative redirects so there is no need to mess with that. The fundamental problem is that controllers.redirect uses a slightly different code path. In particular, in Routes, the controllers.util.redirect is not a redirect (there's an "if routes and routes.redirect" which evals to False).
My solution: replace all calls to redirect by a new controller method (called redirect too) to change redirects from absolute to relative redirects.
The code is as follows:
lib/helpers.py
def relative_redirect(to_url, start_response):
"""Returns a redirect that is compatible with AWS ELB (no absolute http responses)
Using pylons.controllers.util.redirect triggers an exception that'll be turned into a 302
But with an absolute path so the response does not contains https but simple http
"""
start_response("302 Found", [("Content-Type", "text/plain; charset=utf-8"), ("Location", url(to_url))])
return ["You are being redirected to {0}".format(url(to_url))]
With that bit called from the base class of my controllers:
class BaseController(WSGIController):
...
def redirect(self, to_url):
"""Force a relative redirection, to work with AWS ELB"""
return relative_redirect(to_url, self.start_response)
I'd customize the Mapper so that every call to "url" would force the correct protocol...
Inside routing.py:
class CustomMapper(Mapper):
def generate(self, *args, **kwargs):
kwargs["protocol"] = "https"
return Mapper.generate(self, *args, **kwargs)
def make_map(config):
"""Create, configure and return the routes Mapper"""
map = CustomMapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])