Testing unauthorized pages with zope test browser - plone

What's the correct procedure to check in the functional test case whether or not the page is accessible by the currently logged in user?
Please point a working example :)

To check if the page IS accessible is easy. You typically do
browser.open(url)
and check for something in browser.contents.
To make sure the page is inaccessible and for instance raises, you could import HTTPError
from urllib2 import HTTPError
obtain a browser instance, login with it, and do
self.assertRaises(HTTPError, browser.open, url)

>>> browser.handleErrors = False
>>> browser.open(unauthorized_url)
Traceback (most recent call last):
Unauthorized: ...

Related

Mocked request doing actual requests call, not mock

I am mocking a get request in my unittest code using requests-mock, but when I run the code during testing, it still tries to hit the actual URL instead of returning the mocked data.
This is my code:
try:
response = requests.get(api_url, auth=requests.auth.HTTPBasicAuth(username, password))
response.raise_for_status()
except requests.ConnectionError as e:
raise dke.CLIError(f"Could not connect to Artifactory server to get NPM auth information: {str(e)}")
This is my test code
class MyTest(unittest.TestCase):
#classmethod
def setUpClass(cls):
m = requests_mock.Mocker()
m.get('https://artifactory.apps.openshift-sandbox.example.com/artifactory/api/npm/auth',
text=("_auth = base64string==\n"
"always-auth = true\n"
"email = shareduser#fake.com"))
The api_url in my code matches the URL I pass to m.get(). However, when I run the test, I do not get the value of "text" but instead I get a 401 Client Error: Unauthorized and a response from the server indicating "Bad credentials" which tells me it actually tried to contact the server instead of returning the text I had requested in the Mock.
I'm lost. As far as I can tell, I'm using this exactly as the docs indicate. Any ideas?
So, it seems you can't use the request_mocker that way. It has to be a decorator or context manager.
I mean the context manager/decorator is just a pattern around normal code. I haven't tested it but i think you just have to call m.start()/m.stop().
It's generally used as a context manager or decorator because if you instantiate it once like that then your request history is going to include all requests across all unit tests which is very hard to make assertions about.

Qt WebView - intercept loading of JS/CSS Libraries to load local ones

I've been looking for a while through documentation to find a way to accomplish this and haven't been successful yet. The basic idea is, that I have a piece of html that I load through Qt's webview. The same content can be exported to a single html file.
This file uses Libraries such as Bootstrap and jQuery. Currently I load them through CDN which works when online just fine. However, my application also needs to run offline. So I'm looking for a way to intercept loading of the Libraries in Qt and serve a locally saved file instead. I've tried installing a https QWebEngineUrlSchemeHandler, but that never seems to trigger the requestStarted method on it.
(PyQT example follows)
QWebEngineProfile.defaultProfile().installUrlSchemeHandler(b'https', self)
If I use a different text for the scheme and embed that into the page it works, so my assumption is that it doesn't work as Qt has a default handler for it already registered. But that different scheme would fail in the file export.
Anyway, back to the core question; Is there a way to intercept loading of libraries, or to change the url scheme specifically within Qt only?
Got Further with QWebEngineUrlRequestInterceptor, now redirecting https requests to my own uri, which has a uri handler. However, the request never gets through to it, because: Redirect location 'conapp://webresource/bootstrap.min.css' has a disallowed scheme for cross-origin requests.
How do I whitelist my own conapp uri scheme?
Edit: For completeness sake, it turns out back when I originally stated the question, it was impossible to accomplish with PySide 5.11 due to bugs in it. The bug I reported back then is nowadays flagged as fixed (5.12.1 I believe) so it should now be possible to accomplish this again using Qt methods, however for my own project I'll stick to jinja for now which has become a solution for many other problems.
The following example shows how I've done it. It uses the QWebEngineUrlRequestInterceptor to redirect content to a local server.
As an example, I intercept the stacks.css for stackoverflow and make an obvious change.
import requests
import sys
import threading
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, QWebEngineProfile
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor, QWebEngineUrlRequestInfo
from http.server import HTTPServer, SimpleHTTPRequestHandler
from socketserver import ThreadingMixIn
# Set these to the address you want your local patch server to run
HOST = '127.0.0.1'
PORT = 1235
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
def patch_css(self, url):
print('patching', url)
r = requests.get(url)
new_css = r.text + '#mainbar {background-color: cyan;}' # Example of some css change
with open('local_stacks.css', 'w') as outfile:
outfile.write(new_css)
def interceptRequest(self, info: QWebEngineUrlRequestInfo):
url = info.requestUrl().url()
if url == "https://cdn.sstatic.net/Shared/stacks.css?v=596945d5421b":
self.patch_css(url)
print('Using local file for', url)
info.redirect(QtCore.QUrl('http:{}:{}/local_stacks.css'.format(HOST, PORT)))
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
"""Threaded HTTPServer"""
app = QtWidgets.QApplication(sys.argv)
# Start up thread to server patched content
server = ThreadingHTTPServer((HOST, PORT), SimpleHTTPRequestHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
# Install an interceptor to redirect to patched content
interceptor = WebEngineUrlRequestInterceptor()
profile = QWebEngineProfile.defaultProfile()
profile.setRequestInterceptor(interceptor)
w = QWebEngineView()
w.load(QtCore.QUrl('https://stackoverflow.com'))
w.show()
app.exec_()
So, the solution I went with in the end was, first, introduce jinja templates. Then, using those the template would have variables and blocks set based on export or internal use and from there I did not need the interceptor anymore.

Change Plone default URL for authenticated users

I need to change the default URL for authenticated users. For example, a request for root site will response:
[site-root]/wellcome01 (default url, set by plone configuration interface), for an anonymous user
[site-root]/wellcome02 (set by some resource) for an authenticated user
So what's the best solution to implement this resource?
You got several options
1.
You can add a index_html Python Script in your plone root, with the following code.
if context.portal_membership.isAnonymousUser():
return context.REQUEST.RESPONSE.redirect('welcome01')
else:
return context.REQUEST.RESPONSE.redirect('welcome02')
2.
I personally don't like not versioned code on my site, so I advice you to write a BrowserView, which handles de redirect
from Products.Five.browser import BrowserView
from plone import api
class RootRedirector(BrowserView):
def __call__(self):
if api.user.is_anonymous()
return self.request.RESPONSE.redirect('welcome01')
else:
return self.request.RESPONSE.redirect('welcome02')
You may regsiter this view only for the SiteRoot (Products.CMFPlone.interfaces.siteroot.IPloneSiteRoot)
If you're willing to just react to login events, you can adapt the following, which will redirect on the first-ever login only (and you probably want IUserLoggedInEvent instead):
configure.zcml:
<subscriber for="Products.PlonePAS.events.IUserInitialLoginInEvent"
handler=".hellonewbie.showIntroPage" />
hellonewbie.py:
# -*- coding: utf-8 -*-
from zope.app.component.hooks import getSite
USER_ROLE = 'Member'
INTRO_PAGE_ID = 'new-user-info'
def showIntroPage(event):
"""Login event handler: first login.
For proper users (i.e. Member role) that have
never logged in before, redirect them to a page
that tells them about the system.
"""
user = event.object
if user.has_role(USER_ROLE):
# yup, redirect the guy, he/she's new.
portal = getSite()
request = getattr(portal, "REQUEST", None)
if request:
infopage = portal.restrictedTraverse(INTRO_PAGE_ID, None)
if infopage:
request.response.redirect(infopage.absolute_url())
Or, just use one view with two elements, one only visible if not logged-in, the other only, if logged-in, via TAL-conditions.

Basic HTTP Authentication with python 3.2 (urllib.request)

This is my first post with this account, and Ive been struggling for the last week to get this to work, so I hope someone can help me get this working.
Im trying to pull some data from https://api.connect2field.com/ but its rejecting all of my authentication attempts from python (not from a browser though).
The code Im using
import urllib.request as url
import urllib.error as urlerror
urlp = 'https://api.connect2field.com/api/Login.aspx'
# Create an OpenerDirector with support for Basic HTTP Authentication...
auth_handler = url.HTTPBasicAuthHandler()
auth_handler.add_password(realm='Connect2Field API',
uri=urlp,
user='*****',
passwd='*****')
opener = url.build_opener(auth_handler)
# ...and install it globally so it can be used with urlopen.
url.install_opener(opener)
try:
f = url.urlopen(urlp)
print (f.read())
except urlerror.HTTPError as e:
if hasattr(e, 'code'):
if e.code != 401:
print ('We got another error')
print (e.code)
else:
print (e.headers)
Im pretty sure the code is doing everything right, which makes me think that maybe theres another authentication step that ASP.net requires. Does anybody have any experience with ASP.Net's authentication protocol?
Im gonna be checking this post throughout the day, so I can post more info if required.
Edit: Ive also tried running my script against a basic http auth server running at home, and it authenticates, so Im pretty sure the request is set up properly.
It appears that IIS is set up to do basic authentication, ASP.NET will be most probably be configured to use windows authentication.
As you have said that authentication works via browser, so the best bet for you is to use tool such as fiddler to capture request/response when connecting via browser and also when connecting via your code. Compare them to troubleshoot the issue.
For example, I remember a case where the web site first requested authentication credentials and then re-directed to different url which prompted for different credentials.

Is there a way to add detailed remote crash reporting to a Flex Air application?

I will be releasing my Air/Flex application soon, but I am pretty sure there are a couple of bugs that may pop up on the various platforms that Air is available for. So I was wondering if there is a way to implement a mechanism, that would send an error report, logging where the error happened, to a remote server each time an app crashes? This way I might catch errors that otherwise would go unnoticed.
Global error handling is now supported in Flash 10 and AIR2. More info on that here: http://help.adobe.com/en_US/air/reference/html/flash/events/UncaughtErrorEvent.html
Using that kind of functionality to catch uncaught exceptions; you can submit the trace to some web service set up specifically to grab them. Using Google App Engine is excellent for this purpose since it already has a logging feature which grabs all kinds of meta data from the client calling the application. Also, if your logs become huge for some reason - at least you wont have to worry about storing them. Google does that for you :)
I've set up such a service as outlined below (granted it has some flaws, in particular anyone can call it and add "traces", but you could add some shared secret and post over HTTPS to have some tiny measure of security).
App Engine Logging Service
#!/usr/bin/env python
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
class MainHandler(webapp.RequestHandler):
def post(self):
import logging
if self.request.get('trace'):
logging.error(self.request.get('trace')) #Adds a row to GAE:s own logs :)
self.response.out.write('trace logged')
else:
set_status(501)
def get(self):
""" Kill this function when done testing """
test_form = """
<form action="/" method="POST">
<textarea name="trace"></textarea>
<input type="submit">
</form>"""
self.response.out.write(test_form)
def main():
application = webapp.WSGIApplication([('/', MainHandler)],
debug=False)
util.run_wsgi_app(application)
if __name__ == '__main__':
main()
I wrote a little AIR-app containing this little test function which simply POST:ed the app engine service with the parameter "trace" specified.
Posting to the logging service (ActionScript)
private function postToLogger(event:MouseEvent):void
{
var service:HTTPService = new HTTPService();
var parameters:Object = {'trace': "omg something went wrong"};
service.url = "https://YOURSUPERSIMPLELOGGINGSERVICE.APPSPOT.COM";
service.method = HTTPRequestMessage.POST_METHOD;
service.resultFormat = HTTPService.RESULT_FORMAT_E4X;
service.addEventListener("result", onSuccess);
service.addEventListener("fault", onError);
service.send(parameters);
}
And finally, this is how it looks in the logs, lots of metadata, and of the trace you caught in your AIR app.

Resources