I am writing test cases for my Django application but I am using requests package in Python to access the status code, and then use the assert statement. This test case is for Staff Users only:
class StaffClientServerTestCase(TestCase):
def test_login_staff(self):
User.objects.create_superuser('abc', 'abc#gmail.com', '1234')
self.client.login(username='abc', password='1234')
self.client.get('/dashboard/')
s = requests.get('http://localhost:8000/dashboard/')
print s.status_code
self.assertEqual(200, s.status_code)
Only staff users have access to the dashboard, so I created a staff user object. The following line
self.client.login(username='abc', password='1234') is coming to be true.
self.client.get('/dashboard/') if I print out this line, it shows the html content of the page which means that my staff user can access the dashboard.
But when I use the request module to see the status code of dashboard url it shows that status code = 500, and my test is failing.
s = requests.get('http://localhost:8000/dashboard/')
print s.status_code
Could anyone tell me where I am going wrong here? Why my status code is coming out be 500, even though the staff user can access the dashboard and print out its contents using print self.client.get('/dashboard/'). Help!
you can test the case in other way:
protect your views with decorator:
#user_passes_test(lambda u: u.is_staff)
def dashboard(request, ...):
# ...
and then request with requests to see if you are banned to see the page. if you are banned (403 forbidden), then your views is working correctly. To verify if your views works correctly for staff users, remove the decorator and request again, if you go thru this time, then everything is fine, the only thing to test is then the logic inside the views.
but right now, you are getting 500 instead of 403, which means you have backend errors. fix them first
Related
I am having a great deal of trouble trying to authorize Spotipy. It does fine in IDLE, but as soon I try to do it together with Django, hell lets loose. Where I have gotten the farthest, is where I have gotten the code from the callback. But what do I do after that?
This piece of code below takes
def add_song(request, pk, uri):
scope = "user-read-recently-played user-top-read user-read-playback-position user-read-playback-state user-modify-playback-state user-read-currently-playing user-read-private playlist-modify-private playlist-read-private"
token = util.prompt_for_user_token("username", scope, client_id=SECRET,client_secret=SECRET,redirect_uri='http://localhost:8000/rooms/callback/')
sp = spotipy.Spotify(auth=token)
sp.add_to_queue(uri)
auth_manager = spotipy.oauth2.SpotifyOAuth(scope='user-read-currently-playing playlist-modify-private', show_dialog=True)
if request.GET.get("code"):
# Step 3. Being redirected from Spotify auth page
auth_manager.get_access_token(request.GET.get("code"))
return redirect('/')
return render(request, 'rooms/{0}.html'.format(pk))
def callback(request):
return redirect(reverse("index"))
The URL patterns:
urlpatterns = [
path('<int:pk>/add_song/<str:uri>', views.add_song, name='add_song'),
path('callback/<str:token>', views.callback, name="callback")
]
I really need something to continue on. I have been googling so much, but there isn't really anything I know what to do with. Could you provide an example of Django Python authorization or try to help me?
Don't use util.prompt_for_user_token. Assuming you want different users to use your app, how would you pre-know their username?
Use a SpotifyOAuth object instead (you were using it, just not in the way I'm about to show). You can refer to the Client Authorization Code Flow section on the official docs.
Here is the basic of it:
# Seprating the login from add_song()
def login(request):
sp_auth = SpotifyOAuth(
scope=scope,
client_id=client_id,
client_secret=client_secret,
redirect_uri=redirect_uri,
)
# This url will prompt you a Spotify login page, then redirect user to your /callback upon authorization
redirect_url = sp_auth.get_authorize_url() # Note: You should parse this somehow. It may not be in a pretty format.
return redirect(redirect_url)
# You can make add_song() and callback() the same function.
# If you have multiple pages where you need to use the API, I suggest caching the token here as well.
def callback_and_add_song():
sp_auth = [the same as above. You should make a method to modularize it.]
# Get code from url
code = request.GET.get("code", "")
token = sp_auth.get_access_token(code=code)
<You have token now. Do what you need.>
A few more things I noticed in your code that may be not-needed/ wrong:
path('callback/<str:token>': I don't think <str:token> have to be there.
return redirect('/'): In the first if in add_song(), assuming your previous code works, you are still just redirecting somewhere without caching the token/ passing it on.
return redirect(reverse("index")): The same as above. I think for performance and all, you should cache token in callback() then redirect to page(s) where you actually use it.
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")
import requests
r=requests.Session()
name="user"
pas="pass123"
url="http://someurl/login.php"
r.get(url)
Response<200>
login_data=dict(username=name,password=pas)
r.post(url,data=login_data)
Response<200>
page=r.get("http://someurl/user") #this takes me to the user page
print(page.content)
Works fine and takes me to the user's home page.
when i give the wrong user credentials it loads again the same page like facebook etc.the user is not taken to the hoempage.In this case how to know the user has entered is wrong password so I can prompt the user.
It really depends on the service you're talking to. For example API endpoints will likely respond with a 40X status to a failed authentication. On the other hand normal websites are likely to respond with a success and a normal page. In that case you need to figure out if you're logged in either by:
the url you're redirected to,
contents of the cookies set by the web app,
if all else fails - the contents of the website you get back
seached and found that /me/accounts/ is no more available, in the past i used it, so i posted on a page's application without any problem, using that Gist,
But i deleted that application and tried to add another new one, when changed the code to:
class MainHandler(BaseHandler, tornado.auth.FacebookGraphMixin):
#tornado.web.authenticated
#tornado.web.asynchronous
def get(self):
self.facebook_request("/me/applications/developer/", self._on_accounts, access_token=self.current_user["access_token"])
def _on_accounts(self, account):
if account is None:
# Session may have expired
print "on accounts failed"
sys.exit()
print "here we go!"
print account # this will return me a json
for acc in account["data"]:
if acc["id"] == "157547381099830":
self.facebook_request("/157547381099830/feed", post_args={"message":"hi there!", "link": "http://www.example.com"}, access_token=self.current_user["access_token"], callback=self.async_callback(self._on_page_post))
what i can to do, is only post on my wall (changing self.facebook_request("/157547381099830/feed"... by self.facebook_request("/me/feed"...
)
I entered to the page, and Facebook knows that am the admin, but if i use the python code, it will publish the stream a normal user!
Edit: It seems that the problem is kind of new Facebook rules? when switch to my name, i cant publish on the wall, even if i'm the admin?
In the management setting, it is written:
Manager
Abdelouahab can manage admin roles, send messages and create posts as the Page, create ads, and view insights.
But, it seems it is not the case, because when i choose:
You are posting, commenting, and liking as Abdelouahab Abdenour Aliane — Change to Essog Community then i can post, only if i switch to Essog Community (because i blocked external users from posting on the wall).
Update: added manage_pages to the scope, and even this did not work
Sorry, found the answer:
Dident use manage_pages in the first time, so i used the page token!
i must get the page access token and not the user token!
To perform the following operations as a Page, and not the current user, you must use the Page's access token, not the user access token commonly used for reading Graph API objects.
(deleted and begin a new page)
this will work:
for acc in account["data"]:
if acc["id"] == "345632575539519":
print acc["access_token"]
self.facebook_request("/345632575539519/feed", post_args={"message":"hello", "link": "http://www.example.com"}, access_token=acc["access_token"], callback=self.async_callback(self._on_page_post))
im using FriendlyFormPlugin, but would like to retrieve the username that was input as part of the request.params, but its no longer there when i check. this way i can set the default for username if the password is incorrect. thanks
I think what you need to do is to setup a post login handler action when you setup the middleware. In that action you can then check params, set a session var, etc. I had to hook into here in order to create a message to the user that their login had failed. I check for a 'login_failed' param on the login form.
def post_login(self):
""" Handle logic post a user's login
I want to create a login_handler that's redirected to after login. This would
check
- if user was logged in, if not then send back to login
- if user is admin, go to job list
- adjust the max age on the existing cookie to XX remember me timeframe
"""
if auth.check(not_anonymous()):
log.debug('checked auth')
else:
# login failed, redirect back to login
log.debug('failed auth')
redirect_to(controller="root", action="login", login_failed=True)
# expire this cookie into the future
ck = request.cookies['authtkt']
response.set_cookie('authtkt', ck,
max_age=60*60*24*7,
path='/'
)
redirect_to(controller="job", action="list")
In response for more details, too big to add as another comment:
So I've got a few things you can look at. First, this is my docs I'm writing as a repoze 'summary' to help explain to other devs how this stuff works/terminology used:
http://72.14.191.199/docs/morpylons/auth_overview.html
I started out using the repoze sql quickstart plugin:
http://code.gustavonarea.net/repoze.what-quickstart/
I then ripped out their setup_sql_auth and modified it for our own needs since we do both SQL and LDAP auth in our apps. Go make sure to look at the plugin source for setup_sql_auth and go through it until you really understand what it's doing.
and since you asked on middleware config...
app = setup_morpace_auth(app, User, Group, Permission, meta.Session,
post_login_url='/root/post_login',
post_logout_url='/login',
log_level='debug',
log_file='stdout'
)