Incorrect credentials with Django Rest Framework? - python

I'm trying to set up token authentication with the Django Rest Framework. I'm currently writing some tests to see if I can get a token returned for a user. Below is the code for the unit test (which is inside of a test case).
def test_create_valid_request(self):
u = User.objects.create(username='test1', password='thisis8chars')
Token.objects.create(user=u)
# these assertions all pass
self.assertEqual(User.objects.get(username='test1'), u)
self.assertEqual(u.username, 'test1')
self.assertEqual(u.password, 'thisis8chars')
data = {'username': 'test1', 'password': 'thisis8chars'}
url = "/api-token-auth/"
response = self.client.post(url, data, format="json")
print response.status_code
print response.content
This prints:
400
{"non_field_errors":["Unable to log in with provided credentials."]}
I understand that there must be something wrong with my credentials, but I can't see it. I create a user, tests its attributes, and make a post request to retrieve the token. I've manually tested this on the Django development server with httpie, and it works and returns the token. Any ideas what the problem could be? Is this a problem with my testing setup? If so, what?
I can post/describe more code if necessary.
Thanks

Okay so the error was very simple: I wanted User.objects.create_user rather than User.objects.create.
The password that I was trying to use with my code above was problematic because it wasn't hashed or salted, and because Django doesn't store or send plain-text passwords, me sending the plain-text password was resulting in a bad credentials error.

As you've already stated, you need to use User.objects.create_user.
To add to this, if you already have a User object instantiated and want to change their password you'll need to call the user.set_password(raw_password) method.

Related

How to programmatically delete an email from list of bounced emails?

I am working on a GAE(Google App Engine) based python app and which have sendgrid python SDK(v3.2.10) integrated into it. What I am trying do is right now that whenever sendgrid pushes an event webhook of type "bounce" I want to delete that bounced email from the list of bounced emails present on sendgrid.
I have already gone through the documentation provided on the official site. First I tried to delete email address using SDK and it worked fine on localhost. But after deploying it to the live server it just doesn't do anything and falls in the exception clause.
Code snippet:
try:
send_grid_client = sendgrid.SendGridAPIClient(apikey=SENDGRID_API_KEY)
data = {"emails": [email.strip()]}
delete_response = send_grid_client.client.suppression.bounces.delete(
request_body=data)
except Exception as exception:
logging.info('Exception is: {}'.format(exception))
pass
As it did not work as expected, I am now trying to do the same using REST API.
Code snippet:
import requests
data = {"emails": [email]}
headers = {"Authorization": "Bearer {}".format(SENDGRID_API_KEY)}
delete_response = requests.delete("https://api.sendgrid.com/v3/suppression/bounces", data=json.dumps(data), headers=headers)
logging.info(delete_response)
logging.info(delete_response.status_code)
logging.info(delete_response.text)
Now, sendgrid API is continuously returning error 400 with message {"errors":[{"field":null,"message":"emails or delete_all params required"}]}. I simply could not figure out how to overcome this issue. Maybe I am missing how to pass request body in the delete function but, I could not figure it out.
I just figured out the issue.
It's the SendGrid API docs here which causes confusion as it is not mentioned clearly that they have a different way of calling the same endpoint when you want to delete a single email address or list of emails.
For a single email, it needs to be passed in the URL i.e. https://api.sendgrid.com/v3/suppression/bounces/{email_address}
For a list of emails, the list needs to be passed in the body of the delete request. i.e. it will look like this {"emails": [email_address_1, email_address_1, ...]}
As in the question above a single email was meant to be deleted and it was being passed as {"emails": [email_address_1]} in the delete request. Sendgrid API was not able to digest this info and was throwing an error. The email address was to be passed in the URL.
This issue has been resolved. But, I wonder why Sendgrid API was not able to digest this info {"emails": [email_address_1]}. Why they have a hard assumption that list will always have elements greater than one in it.

Need a Python script for Slack to deactivate a user [duplicate]

I have tried multiple approaches to this. Tried first getting the user without any user id - this returns me just my user, then tried getting user with other id's and it also retrieves data correctly. However, I can't seem to be able to set user attribute 'deleted'. i'm using this python approach.
slack_client.api_call('users.profile.set', deleted=True, user='U36D86MNK')
However I get the error message of:
{u'error': u'invalid_user', u'ok': False}
Maybe someone has already done this? It says in documentation that it's a paid service mentioning this message under a user property:
This argument may only be specified by team admins on paid teams.
But shouldn't it give me a 'paid service' response in that case then?
The users.profile.set apparently does not work for for setting each and every property of a user.
To set the deleted property there is another API method called users.admin.setInactive. Its an undocumented method and it will only work on paid teams.
Note: This requires a legacy token and doesn't work with App tokens - these are only available on paid plans and new legacy tokens can't be created anymore
in python you can do the following:
import requests
def del_slack_user(user_id): # the user_id can be found under get_slack_users()
key = 'TOKEN KEY' #replace token key with your actual token key
payload = {'token': key, 'user': user_id}
response = requests.delete('https://slack.com/api/users.admin.setInactive', params=payload)
print(response.content)
def get_slack_users():
url = 'https://slack.com/api/users.list?token=ACCESSTOKEN&pretty=1'
response = requests.get(url=url)
response_data = response.json() # turns the query into a json object to search through`
You can use Slack's SCIM API to enable and disable a user. Note that, as with the undocumented API endpoint mentioned in other answers this requires a Plus/Enterprise account.

Insufficient permissions when trying to create a Quizlet set

I am trying to create a set on Quizlet.com, using its API found here: https://quizlet.com/api/2.0/docs/sets#add
Here is my code of a set I am trying to create:
import requests
quizkey = my_client_id
authcode = my_secret_code # I'm not sure if I need this or not
data = {"client_id":quizkey, "whitespace":1, "title":"my-api-set",
"lang_terms":"it", "lang_definitions":"en",
"terms":['uno','due'], "definitions":["one","two"]}
apiPrefix = "https://api.quizlet.com/2.0/sets"
r = requests.post(url=apiPrefix, params=data)
print r.text
The response is:
{
"http_code": 401,
"error": "invalid_scope",
"error_title": "Not Allowed",
"error_description": "You do not have sufficient permissions to perform the requested action."
}
I also tried "access_token":authcode instead of "client_id":quizkey, but this resulted in the error: "You do not have sufficient permissions to perform the requested action."
How can I fix this and not get a 401 error?
Alright so 3 and a half years later (!!) I've looked into this again and here's what I've discovered.
To add a set you need an access token - this is different to the client_id (what I call quizkey in my code), and to be quite honest I don't remember what authcode in my code is.
This token is obtained by going through the user authentication flow. To summarise it:
Send a POST request to https://quizlet.com/authorize like so:
https://quizlet.com/authorize?response_type=code&client_id=MY_CLIENT_ID&scope=read&state=RANDOM_STRING
Keep the response_type as code, replace client_id with your client_id, keep the scope as read, and state can be anything
I believe this requires human intervention because you're literally authorising your own account? Not sure of another way...
You'll receive a response back with a code
Let's call this RESPONSE_CODE for now
Send a POST request to https://api.quizlet.com/oauth/token, specifying 4 mandatory parameters:
grant_type="authorization_code" (this never changes)
code=RESPONSE_CODE
redirect_uri=https://yourredirecturi.com (this can be found at your personal API dashboard)
client ID and secret token separated by a colon and then base64-encoded (the user authentication flow link above tells you what this is if you don't want to do any of the encoding)
You'll receive the access_token from this API call
Now you can use that access_token in your call to create a set like I've done above (just replace "client_id":quizkey with "access_token":access_token)
You will need to authenticate in order to make sets. This link gives an overview:
https://quizlet.com/api/2.0/docs/making_api_calls
And this one provides details about the authentication process:
https://quizlet.com/api/2.0/docs/authorization_code_flow

How do you use cookies and HTTP Basic Authentication in CherryPy?

I have a CherryPy web application that requires authentication. I have working HTTP Basic Authentication with a configuration that looks like this:
app_config = {
'/' : {
'tools.sessions.on': True,
'tools.sessions.name': 'zknsrv',
'tools.auth_basic.on': True,
'tools.auth_basic.realm': 'zknsrv',
'tools.auth_basic.checkpassword': checkpassword,
}
}
HTTP auth works great at this point. For example, this will give me the successful login message that I defined inside AuthTest:
curl http://realuser:realpass#localhost/AuthTest/
Since sessions are on, I can save cookies and examine the one that CherryPy sets:
curl --cookie-jar cookie.jar http://realuser:realpass#localhost/AuthTest/
The cookie.jar file will end up looking like this:
# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This file was generated by libcurl! Edit at your own risk.
localhost FALSE / FALSE 1348640978 zknsrv 821aaad0ba34fd51f77b2452c7ae3c182237deb3
However, I'll get an HTTP 401 Not Authorized failure if I provide this session ID without the username and password, like this:
curl --cookie 'zknsrv=821aaad0ba34fd51f77b2452c7ae3c182237deb3' http://localhost/AuthTest
What am I missing?
Thanks very much for any help.
So, the short answer is you can do this, but you have to write your own CherryPy tool (a before_handler), and you must not enable Basic Authentication in the CherryPy config (that is, you shouldn't do anything like tools.auth.on or tools.auth.basic... etc) - you have to handle HTTP Basic Authentication yourself. The reason for this is that the built-in Basic Authentication stuff is apparently pretty primitive. If you protect something by enabling Basic Authentication like I did above, it will do that authentication check before it checks the session, and your cookies will do nothing.
My solution, in prose
Fortunately, even though CherryPy doesn't have a way to do both built-in, you can still use its built-in session code. You still have to write your own code for handling the Basic Authentication part, but in total this is not so bad, and using the session code is a big win because writing a custom session manager is a good way to introduce security bugs into your webapp.
I ended up being able to take a lot of things from a page on the CherryPy wiki called Simple authentication and access restrictions helpers. That code uses CP sessions, but rather than Basic Auth it uses a special page with a login form that submits ?username=USERNAME&password=PASSWORD. What I did is basically nothing more than changing the provided check_auth function from using the special login page to using the HTTP auth headers.
In general, you need a function you can add as a CherryPy tool - specifically a before_handler. (In the original code, this function was called check_auth(), but I renamed it to protect().) This function first tries to see if the cookies contain a (valid) session ID, and if that fails, it tries to see if there is HTTP auth information in the headers.
You then need a way to require authentication for a given page; I do this with require(), plus some conditions, which are just callables that return True. In my case, those conditions are zkn_admin(), and user_is() functions; if you have more complex needs, you might want to also look at member_of(), any_of(), and all_of() from the original code.
If you do it like that, you already have a way to log in - you just submit a valid session cookie or HTTPBA credentials to any URL you protect with the #require() decorator. All you need now is a way to log out.
(The original code instead has an AuthController class which contains login() and logout(), and you can use the whole AuthController object in your HTTP document tree by just putting auth = AuthController() inside your CherryPy root class, and get to it with a URL of e.g. http://example.com/auth/login and http://example.com/auth/logout. My code doesn't use an authcontroller object, just a few functions.)
Some notes about my code
Caveat: Because I wrote my own parser for HTTP auth headers, it only parses what I told it about, which means just HTTP Basic Auth - not, for example, Digest Auth or anything else. For my application that's fine; for yours, it may not be.
It assumes a few functions defined elsewhere in my code: user_verify() and user_is_admin()
I also use a debugprint() function which only prints output when a DEBUG variable is set, and I've left these calls in for clarity.
You can call it cherrypy.tools.WHATEVER (see the last line); I called it zkauth based on the name of my app. Take care NOT to call it auth, or the name of any other built-in tool, though .
You then have to enable cherrypy.tools.WHATEVER in your CherryPy configuration.
As you can see by all the TODO: messages, this code is still in a state of flux and not 100% tested against edge cases - sorry about that! It will still give you enough of an idea to go on, though, I hope.
My solution, in code
import base64
import re
import cherrypy
SESSION_KEY = '_zkn_username'
def protect(*args, **kwargs):
debugprint("Inside protect()...")
authenticated = False
conditions = cherrypy.request.config.get('auth.require', None)
debugprint("conditions: {}".format(conditions))
if conditions is not None:
# A condition is just a callable that returns true or false
try:
# TODO: I'm not sure if this is actually checking for a valid session?
# or if just any data here would work?
this_session = cherrypy.session[SESSION_KEY]
# check if there is an active session
# sessions are turned on so we just have to know if there is
# something inside of cherrypy.session[SESSION_KEY]:
cherrypy.session.regenerate()
# I can't actually tell if I need to do this myself or what
email = cherrypy.request.login = cherrypy.session[SESSION_KEY]
authenticated = True
debugprint("Authenticated with session: {}, for user: {}".format(
this_session, email))
except KeyError:
# If the session isn't set, it either wasn't present or wasn't valid.
# Now check if the request includes HTTPBA?
# FFR The auth header looks like: "AUTHORIZATION: Basic <base64shit>"
# TODO: cherrypy has got to handle this for me, right?
authheader = cherrypy.request.headers.get('AUTHORIZATION')
debugprint("Authheader: {}".format(authheader))
if authheader:
#b64data = re.sub("Basic ", "", cherrypy.request.headers.get('AUTHORIZATION'))
# TODO: what happens if you get an auth header that doesn't use basic auth?
b64data = re.sub("Basic ", "", authheader)
decodeddata = base64.b64decode(b64data.encode("ASCII"))
# TODO: test how this handles ':' characters in username/passphrase.
email,passphrase = decodeddata.decode().split(":", 1)
if user_verify(email, passphrase):
cherrypy.session.regenerate()
# This line of code is discussed in doc/sessions-and-auth.markdown
cherrypy.session[SESSION_KEY] = cherrypy.request.login = email
authenticated = True
else:
debugprint ("Attempted to log in with HTTBA username {} but failed.".format(
email))
else:
debugprint ("Auth header was not present.")
except:
debugprint ("Client has no valid session and did not provide HTTPBA credentials.")
debugprint ("TODO: ensure that if I have a failure inside the 'except KeyError'"
+ " section above, it doesn't get to this section... I'd want to"
+ " show a different error message if that happened.")
if authenticated:
for condition in conditions:
if not condition():
debugprint ("Authentication succeeded but authorization failed.")
raise cherrypy.HTTPError("403 Forbidden")
else:
raise cherrypy.HTTPError("401 Unauthorized")
cherrypy.tools.zkauth = cherrypy.Tool('before_handler', protect)
def require(*conditions):
"""A decorator that appends conditions to the auth.require config
variable."""
def decorate(f):
if not hasattr(f, '_cp_config'):
f._cp_config = dict()
if 'auth.require' not in f._cp_config:
f._cp_config['auth.require'] = []
f._cp_config['auth.require'].extend(conditions)
return f
return decorate
#### CONDITIONS
#
# Conditions are callables that return True
# if the user fulfills the conditions they define, False otherwise
#
# They can access the current user as cherrypy.request.login
# TODO: test this function with cookies, I want to make sure that cherrypy.request.login is
# set properly so that this function can use it.
def zkn_admin():
return lambda: user_is_admin(cherrypy.request.login)
def user_is(reqd_email):
return lambda: reqd_email == cherrypy.request.login
#### END CONDITIONS
def logout():
email = cherrypy.session.get(SESSION_KEY, None)
cherrypy.session[SESSION_KEY] = cherrypy.request.login = None
return "Logout successful"
Now all you have to do is enable both builtin sessions and your own cherrypy.tools.WHATEVER in your CherryPy configuration. Again, take care not to enable cherrypy.tools.auth. My configuration ended up looking like this:
config_root = {
'/' : {
'tools.zkauth.on': True,
'tools.sessions.on': True,
'tools.sessions.name': 'zknsrv',
}
}

Im using python (django framework) to gain a request token from google api, but the request token always comes back empty

Here is sample code that I'm working with.
def index(request):
flow = OAuth2WebServerFlow(
client_id='xyz.apps.googleusercontent.com',
client_secret='xyz',
scope='https://www.googleapis.com/auth/plus.me',
user_agent='sample/1.0')
callback = 'http://%s/oauth2callback' % request.META[ 'HTTP_HOST' ]
authorize_url = flow.step1_get_authorize_url(callback)
return HttpResponse(flow)
For some reason 'flow' is always set to " " or empty instead of a request token. I have searched for days on this issue.
Can anyone tell me why I can't get a request token from google using this method?
fyi: I know that I should be redirecting the user to the authorize url, but I want to see if flow is set before I do since Google will provide the authorize url even if a request token wasn't returned.
Before you can use OAuth 2.0, you must register your application using
the Google APIs Console. After you've registered, go to the API Access
tab and copy the "Client ID" and "Client secret" values, which you'll
need later.
http://code.google.com/p/google-api-python-client/wiki/OAuth2#Registering
If this answer actually helps with your problem then I must bid an R.I.P. to S.O.

Categories