Arguments disappear from a dictionary when passed to a function - python

In my function I read user's data from session and store them in a dictionary. Next I'm sending it to 'register' function from registration.backend but the function somehow get's it empty and a KeyError is thrown. Where are my data gone ? The code from function calling 'register' function :
data = request.session['temp_data']
email = data['email']
logging.debug(email)
password1 = data['password1']
userdata = {'email': email, 'password1': password1}
logging.debug(userdata)
backend = request.session['backend']
logging.debug(backend)
user = backend.register(userdata)
And the register function (whole source here : http://bitbucket.org/ubernostrum/django-registration/src/tip/registration/backends/default/init.py ) :
class DefaultBackend(object):
def register(self, request, **kwargs):
logging.debug("backend.register")
logging.debug(kwargs)
username, email, password = kwargs['email'], kwargs['email'], kwargs['password1']
Debug after invoking them :
2010-07-09 19:24:35,020 DEBUG my#email.com
2010-07-09 19:24:35,020 DEBUG {'password1': u'a', 'email': u'my#email.com'}
2010-07-09 19:24:35,020 DEBUG <registration.backends.default.DefaultBackend object at 0x15c6090>
2010-07-09 19:24:35,021 DEBUG backend.register
2010-07-09 19:24:35,021 DEBUG {}
Why the data could be missing ? Am I doing something wrong ?
#edit for Silent-Ghost
register() takes exactly 2 arguments (3 given)
112. backend = request.session['backend']
113. logging.debug(backend)
114. user = backend.register(request, userdata)

No need to mess with ** in register method. What you want to do is simply pass dictionary to register method:
user = backend.register( request, userdata ) # you need to pass request as definition says
def register( self, request, userdata ): # note lack of **
logging.debug("backend.register")
logging.debug( userdata ) # should work as expected
username, email, password = userdata['email'], userdata['email'], userdata['password1']

Judging by the method's signature:
you need to unpack your dictionary
you need to pass relevant request variable
Something like this:
backend.register(request, **userdata)
Assuming register is a method on backend instance.

this perfectly work
class Logging():
def debug(self,f):
print f
class DefaultBackend(object):
def register(self, request, **kwargs):
logging.debug("backend.register")
logging.debug(kwargs)
username, email, password = kwargs['email'], kwargs['email'], kwargs['password1']
class Request:
def __init__(self):
self.session = {}
request = Request()
logging=Logging()
request.session['temp_data']={'password1': u'a', 'email': u'my#email.com'}
request.session['backend']=DefaultBackend()
data = request.session['temp_data']
email = data['email']
logging.debug(email)
password1 = data['password1']
userdata = {'email': email, 'password1': password1}
logging.debug(userdata)
backend = request.session['backend']
logging.debug(backend)
user = backend.register(request,**userdata)

Related

Render template with params from another app (microservice)

I'm making a web app with a microservice architecture. When it comes to login I want to redirect the successfully logged in user to his dashboard (which is on another microservice). I have a problem with passing parameters such as name, surname, email, and token.
microservice 1 (views.py):
class SignInView(viewsets.ViewSet):
def post(self, request):
email = request.data['email']
password = request.data['password']
user = User.objects.filter(email=email).first()
print(bool(user.is_coordinator))
if user is None:
messages.error(request, 'User not found. Please try again')
return HttpResponseRedirect('/sign-in')
if not user.check_password(password):
messages.error(request, 'Invalid password. Please try again')
return HttpResponseRedirect('/sign-in')
payload = {
'id': user.id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=60),
'iat': datetime.datetime.utcnow()
}
token = jwt.encode(payload, 'secret', algorithm='HS256')
response = Response()
response.set_cookie(key='jwt', value=token, httponly=True)
response.data = ({
'jwt': token
})
if user.is_coordinator:
return redirect('http://127.0.0.1:8001/dashboard-coordinator')
else:
return HttpResponseRedirect('http://127.0.0.1:8001/dashboard-employee', {'name': user.name, 'surname': user.surname, 'email': user.email, 'response.data': response.data})
...
microservice 2 (views.py):
class HomeView(viewsets.ViewSet):
def get(self, request):
return render(request, 'dashboard-employee.html', {'name': name, 'surname': surname, 'email': email, 'response.data': response.data})
What I've included in microservice 2 does not work. How can I get and process params from microservice 1? Is there any better solution/approach?
The Python context of these two microservices is not directly shared, but it wouldn't be shared in the way microservice 2 needs it right now, even if the get() and the post() were in the same microservice. Set aside the microservices and HTTP for a moment and imagine if we had these two functions side by side:
def post():
name = 'First'
surname = 'Last'
return get()
def get():
return name, surname
We would get a NameError, because name and surname don't exist inside the scope of the get() function. So, how could we fix this? We could set up some sort of global state, but that's not necessary. Instead, we can pass some information from post() to get():
def post():
name = 'First'
surname = 'Last'
data = {'name': name, 'surname': surname}
return get(data)
def get(data):
return data['name'], data['surname']
This is of course a silly example, but maybe you can already see the similarities between it and your real-world code. You have actually already done the work in put() to transmit the data, now in get() you need to grab it back out from request.query_params:
class HomeView(viewsets.ViewSet):
def get(self, request):
name = request.query_params['name']
surname = request.query_params['surname']
email = request.query_params['email']
response_data = request.query_params['response.data']
return render(request, 'dashboard-employee.html', {'name': name, 'surname': surname, 'email': email, 'response.data': response_data})
Or more simply,
class HomeView(viewsets.ViewSet):
def get(self, request):
return render(request, 'dashboard-employee.html', request.query_params)

Python Flask "Invalid Credentials"

I am new to Flask learning it from a tutorial video. I am trying to authenticate and get a access token. I have created user class which is in user.py :
class User:
def __init__(self, _id, username, password):
self.id = _id,
self.username = username,
self.password = password
in my security.py :
from werkzeug.security import safe_str_cmp
from user import User
users = [
User(1, 'user1', 'abcxyz'),
]
username_table = {u.username: u for u in users}
userid_table = {u.id: u for u in users}
def authenticate(username, password):
user = username_table.get(username, None)
print(username) # user1
print(password) # abcxyz
print(username_table) #{('user1',): <user.User object at 0x000001AD37543358>}
print(user) # None
if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):
return user
def identity(payload):
user_id = payload['identity']
return userid_table.get(user_id, None)
As you can see I was trying to debug with printing out everything. Inside authentication function I got username and password successfully from the request. But In result the user variable is None. Looks like .get method is not working and that is causing the error but I can not figure out why am I getting None there. I have showed the outputs in front of print functions
I think you have a typo in your program. Please remove the , after the username in __init__ of the User class. That is syntactically correct and way of creating a tuple with a single element which is not what you need.
>>> tuple_with_single_element = "python",
>>> tuple_with_single_element
... ('python',)
>>> python_string = "python"
>>> python_string
... 'python'

How to update User with webapp2 and simpleauth?

I have a profile page for my users where they should be able to update their information. For now they can update their names but I also want phonenumbers, addresses, etc.
The code for updating the name of my user is
class AccountPage(BaseRequestHandler):
def get(self):
self.render('accountpage.html', {'request': self.request, 'user': self.current_user,'loggedin': self.logged_in, 'session': self.auth.get_user_by_session(),})
def post(self):
user = self.current_user
user.name = self.request.POST['name']
user.put()
self.auth.set_session(
self.auth.store.user_to_dict(user))
self.render('accountpage.html', {'request': self.request, 'loggedin': self.logged_in,'user': self.current_user})
But how can I use extra variables such as phonenumbers, address variable etc? The webapp2 User model is an expando model. It did not work to just add the variables to the model:
class User(model.Expando):
"""Stores user authentication credentials or authorization ids."""
#: The model used to ensure uniqueness.
unique_model = Unique
#: The model used to store tokens.
token_model = UserToken
created = model.DateTimeProperty(auto_now_add=True)
updated = model.DateTimeProperty(auto_now=True)
# ID for third party authentication, e.g. 'google:username'. UNIQUE.
auth_ids = model.StringProperty(repeated=True)
# Hashed password. Not required because third party authentication
# doesn't use password.
password = model.StringProperty()
phonenumber = model.StringProperty()
address = model.StringProperty()
I use simpleauth and I get this error msg from simpleauth:
INFO 2015-07-20 06:09:34,426 authhandlers.py:78] user_dict | {'name': u'DAC', 'user_id': 5620703441190912, 'token': u'c9BbE72EmrgTDpG1Dl4tlo', 'token_ts': 1437371676, 'cache_ts': 1437371676, 'remember': 0}
ERROR 2015-07-20 06:09:34,437 authhandlers.py:42] 'phonenumber'
INFO 2015-07-20 06:09:34,445 module.py:812] default: "POST /account/ HTTP/1.1" 404 -
INFO 2015-07-20 06:09:34,501 module.py:812] default: "GET /favicon.ico HTTP/1.1" 200 450
In my BaseRequestHandler I have this cached_property that creates an object.
#webapp2.cached_property
def current_user(self):
"""Returns currently logged in user"""
user_dict = self.auth.get_user_by_session()
logging.info('user_dict | %s ' % user_dict)
if user_dict:
return self.auth.store.user_model.get_by_id(user_dict['user_id'])
else:
return api.users.get_current_user()
Then I tried changing the user model but I still get the ERR phone_number when making these changes.
class BaseRequestHandler(webapp2.RequestHandler):
class User(auth_models.User):
address = ndb.StringProperty(indexed=False)
phone_number = ndb.IntegerProperty(indexed=False)
def dispatch(self):
# Get a session store for this request.
self.session_store = sessions.get_store(request=self.request)
if self.request.host.find('.br') > 0:
i18n.get_i18n().set_locale('pt-br')
elif self.request.host.find('klok') > 0:
i18n.get_i18n().set_locale('sv')
elif self.request.host.find('business') > 0:
i18n.get_i18n().set_locale('en')
else:
lang_code_get = self.request.get('hl', None)
if lang_code_get is None:
lang_code = self.session.get('HTTP_ACCEPT_LANGUAGE', None)
lang_code_browser = os.environ.get('HTTP_ACCEPT_LANGUAGE')
if lang_code:
i18n.get_i18n().set_locale(lang_code)
if lang_code_browser and lang_code is None:
self.session['HTTP_ACCEPT_LANGUAGE'] = lang_code_browser
i18n.get_i18n().set_locale(lang_code_browser)
else:
i18n.get_i18n().set_locale(lang_code_get)
try:
# Dispatch the request.
logging.info('trying to dispatch')
webapp2.RequestHandler.dispatch(self)
except Exception, ex:
logging.error(ex)
self.error(404)
finally:
# Save all sessions.
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def jinja2(self):
"""Returns a Jinja2 renderer cached in the app registry"""
return jinja2.get_jinja2(app=self.app)
#webapp2.cached_property
def session(self):
"""Returns a session using the default cookie key"""
return self.session_store.get_session()
#webapp2.cached_property
def auth(self):
return auth.get_auth()
#webapp2.cached_property
def session_store(self):
return sessions.get_store(request=self.request)
#webapp2.cached_property
def auth_config(self):
"""
..........Dict to hold urls for login/logout
......"""
return {'login_url': self.uri_for('login'),
'logout_url': self.uri_for('logout')}
#webapp2.cached_property
def current_user(self):
"""Returns currently logged in user"""
user_dict = self.auth.get_user_by_session()
logging.info('user_dict | %s ' % user_dict)
if user_dict:
return self.auth.store.user_model.get_by_id(user_dict['user_id'])
else:
return api.users.get_current_user()
As mentioned in the comment above - you should NOT be making any changes in any of the built-in libraries, instead, you can extend them and then add any additional code/properties you need.
So first, you'd need to define your own User model, which would look simmilar to this:
from google.appengine.ext import ndb
import webapp2_extras.appengine.auth.models as auth_models
class User(auth_models.User):
address = ndb.StringProperty(indexed=False)
phone_number = ndb.IntegerProperty(indexed=False)
You are only adding the new properties you need or the ones you need to override, so no created / updated / etc as they're inherited from the model you were referring to.
You then need to work with this model inside your BaseRequestHandler class (I'm not sure what the line self.current_user does, you might need to include the code for that as well).
You can also read this article to get some more ideas: http://gosurob.com/post/20024043690/gaewebapp2accounts

How can I retrieve the email address from a Google Account and store it in my app's user profile when my user logs in using gae-simpleauth?

I'm attempting to build a very simple user permissions system with webapp2's auth library. I'm using gae-simpleauth to log users in with their Google account. I'm hoping to compare the user's email address to a list of permitted email addresses to determine if a user has access to a resource, but I'm not clear on how to get the email address from the Google account into the account on my app. Users are currently able to log in, but the email address doesn't seem to be something simpleauth adds to their account by default.
How can I retrieve the email address from Google and store it in my app's user profile using gae-simpleauth?
My implementation of gae-simpleauth is nearly identical to the example with the addition of the get_user_and_flags function which fetches the logged in user and sets the admin flag if the user's email is in a list in secrets.py. Unfortunately, that doesn't work because user doesn't have an email attribute.
# -*- coding: utf-8 -*-
import logging, secrets, webapp2
from google.appengine.api import users
from webapp2_extras import auth, sessions, jinja2
from jinja2.runtime import TemplateNotFound
from lib.simpleauth import SimpleAuthHandler
def get_user_and_flags(self):
"""Returns the current user and permission flags for that user"""
flags = {}
user = None
if self.logged_in:
user = self.current_user
flags = {
'admin': user.email in secrets.ADMIN_USERS,
}
return user, flags
def simpleauth_login_required(handler_method):
"""A decorator to require that a user be logged in to access a handler.
To use it, decorate your get() method like this:
#simpleauth_login_required
def get(self):
user = self.current_user
self.response.out.write('Hello, ' + user.name())
"""
def check_login(self, *args, **kwargs):
if self.request.method != 'GET':
self.abort(400, detail='The login_required decorator '
'can only be used for GET requests.')
if self.logged_in:
handler_method(self, *args, **kwargs)
else:
self.session['original_url'] = self.request.url.encode('ascii', 'ignore')
self.redirect('/login/')
return check_login
class BaseRequestHandler(webapp2.RequestHandler):
def dispatch(self):
# Get a session store for this request.
self.session_store = sessions.get_store(request=self.request)
try:
# Dispatch the request.
webapp2.RequestHandler.dispatch(self)
finally:
# Save all sessions.
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def jinja2(self):
"""Returns a Jinja2 renderer cached in the app registry"""
return jinja2.get_jinja2(app=self.app)
#webapp2.cached_property
def session(self):
"""Returns a session using the default cookie key"""
return self.session_store.get_session()
#webapp2.cached_property
def auth(self):
return auth.get_auth()
#webapp2.cached_property
def current_user(self):
"""Returns currently logged in user"""
user_dict = self.auth.get_user_by_session()
return self.auth.store.user_model.get_by_id(user_dict['user_id'])
#webapp2.cached_property
def logged_in(self):
"""Returns true if a user is currently logged in, false otherwise"""
return self.auth.get_user_by_session() is not None
def render(self, template_name, template_vars={}):
# Preset values for the template
values = {
'url_for': self.uri_for,
'logged_in': self.logged_in,
'flashes': self.session.get_flashes()
}
# Add manually supplied template values
values.update(template_vars)
# read the template or 404.html
try:
self.response.write(self.jinja2.render_template(template_name, **values))
except TemplateNotFound:
self.abort(404)
def head(self, *args):
"""Head is used by Twitter. If not there the tweet button shows 0"""
pass
class ProfileHandler(BaseRequestHandler):
def get(self):
"""Handles GET /profile"""
if self.logged_in:
self.render('profile.html', {
'user': self.current_user,
'session': self.auth.get_user_by_session()
})
else:
self.redirect('/')
class AuthHandler(BaseRequestHandler, SimpleAuthHandler):
"""Authentication handler for OAuth 2.0, 1.0(a) and OpenID."""
# Enable optional OAuth 2.0 CSRF guard
OAUTH2_CSRF_STATE = True
USER_ATTRS = {
'facebook' : {
'id' : lambda id: ('avatar_url',
'http://graph.facebook.com/{0}/picture?type=large'.format(id)),
'name' : 'name',
'link' : 'link'
},
'google' : {
'picture': 'avatar_url',
'name' : 'name',
'link' : 'link'
},
'windows_live': {
'avatar_url': 'avatar_url',
'name' : 'name',
'link' : 'link'
},
'twitter' : {
'profile_image_url': 'avatar_url',
'screen_name' : 'name',
'link' : 'link'
},
'linkedin' : {
'picture-url' : 'avatar_url',
'first-name' : 'name',
'public-profile-url': 'link'
},
'foursquare' : {
'photo' : lambda photo: ('avatar_url', photo.get('prefix') + '100x100' + photo.get('suffix')),
'firstName': 'firstName',
'lastName' : 'lastName',
'contact' : lambda contact: ('email',contact.get('email')),
'id' : lambda id: ('link', 'http://foursquare.com/user/{0}'.format(id))
},
'openid' : {
'id' : lambda id: ('avatar_url', '/img/missing-avatar.png'),
'nickname': 'name',
'email' : 'link'
}
}
def _on_signin(self, data, auth_info, provider):
"""Callback whenever a new or existing user is logging in.
data is a user info dictionary.
auth_info contains access token or oauth token and secret.
"""
auth_id = '%s:%s' % (provider, data['id'])
logging.info('Looking for a user with id %s', auth_id)
user = self.auth.store.user_model.get_by_auth_id(auth_id)
_attrs = self._to_user_model_attrs(data, self.USER_ATTRS[provider])
if user:
logging.info('Found existing user to log in')
# Existing users might've changed their profile data so we update our
# local model anyway. This might result in quite inefficient usage
# of the Datastore, but we do this anyway for demo purposes.
#
# In a real app you could compare _attrs with user's properties fetched
# from the datastore and update local user in case something's changed.
user.populate(**_attrs)
user.put()
self.auth.set_session(
self.auth.store.user_to_dict(user))
else:
# check whether there's a user currently logged in
# then, create a new user if nobody's signed in,
# otherwise add this auth_id to currently logged in user.
if self.logged_in:
logging.info('Updating currently logged in user')
u = self.current_user
u.populate(**_attrs)
# The following will also do u.put(). Though, in a real app
# you might want to check the result, which is
# (boolean, info) tuple where boolean == True indicates success
# See webapp2_extras.appengine.auth.models.User for details.
u.add_auth_id(auth_id)
else:
logging.info('Creating a brand new user')
ok, user = self.auth.store.user_model.create_user(auth_id, **_attrs)
if ok:
self.auth.set_session(self.auth.store.user_to_dict(user))
# Remember auth data during redirect, just for this demo. You wouldn't
# normally do this.
self.session.add_flash(data, 'data - from _on_signin(...)')
self.session.add_flash(auth_info, 'auth_info - from _on_signin(...)')
# Go to the last page viewed
target = str(self.session['original_url'])
self.redirect(target)
def logout(self):
self.auth.unset_session()
self.redirect('/')
def handle_exception(self, exception, debug):
logging.error(exception)
self.render('error.html', {'exception': exception})
def _callback_uri_for(self, provider):
return self.uri_for('auth_callback', provider=provider, _full=True)
def _get_consumer_info_for(self, provider):
"""Returns a tuple (key, secret) for auth init requests."""
return secrets.AUTH_CONFIG[provider]
def _to_user_model_attrs(self, data, attrs_map):
"""Get the needed information from the provider dataset."""
user_attrs = {}
for k, v in attrs_map.iteritems():
attr = (v, data.get(k)) if isinstance(v, str) else v(data.get(k))
user_attrs.setdefault(*attr)
return user_attrs
Hope this help( I have same probblem )
First change in secrets.py in line:
'google': (GOOGLE_APP_ID, GOOGLE_APP_SECRET, 'https://www.googleapis.com/auth/userinfo.profile'),
to
'google': (GOOGLE_APP_ID,GOOGLE_APP_SECRET, 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'),
and in auth.py change USER_ATTRS =
{
...
'google' : {
'picture': 'avatar_url',
'email' : 'email', <-- new attr
'name' : 'name',
'link' : 'link'
},
}
Since your question includes no code snippet, I can only guess at what you have done so far. In light of that, the below code should work:
from google.appengine.api import users
user = users.get_current_user()
email = user.email()
Following the idea of nguyên, I add customize also the "_to_user_model_attrs" method.
Here my piece of code:
def _to_user_model_attrs(self, data, attrs_map):
"""Get the needed information from the provider dataset."""
user_attrs = {}
for k, v in attrs_map.iteritems():
if v =="email":
attr = (v, data.get(k)[0].get('value'))
else:
attr = (v, data.get(k)) if isinstance(v, str) else v(data.get(k))
user_attrs.setdefault(*attr)
return user_attrs
It works for me!
There seem to be several methods of authentication, and mix-matching does not work. If you havne't already, make sure you read through this, https://developers.google.com/appengine/articles/auth
There are a few sections that might be relative depending on what else you have been doing with Google.
Changes from the Google Apps account transition
Configuring Google Apps to Authenticate on Appspot

Tornado RESTful PUT handler method not getting request arguments

I'm trying to unit test my RESTful API. Here's my API:
class BaseHandler(tornado.web.RequestHandler):
def __init__(self, *args, **kwargs):
tornado.web.RequestHandler.__init__(self, *args, **kwargs)
self.log = self.application.log
self.db = self.application.db
class ProductHandler(BaseHandler):
#tornado.web.removeslash
def put(self, id = None, *args, **kwargs):
try:
self.log.info("Handling PUT request")
if not id:
raise Exception('Object Id Required')
id = { '_id' : id }
new_values = dict()
name = self.get_argument('name', None)
description = self.get_argument('description', None)
if name:
new_values['name'] = name
if description:
new_values['description'] = description
self.db.products.update(id, new_values, safe = True)
except:
self.log.error("".join(tb.format_exception(*sys.exc_info())))
raise
class Application(tornado.web.Application):
def __init__(self, config_path, test = False, *args, **kwargs):
handlers = [
(r"/product/?(.*)", ProductHandler),
]
settings = dict(debug=True)
tornado.web.Application.__init__(self, handlers, **settings)
self.log = logging.getLogger(__name__)
self.config = ConfigParser()
self.config.read(config_path)
self.mongo_connection = Connection(
host = self.config.get('mongo','host'),
port = self.config.getint('mongo','port'),
)
if test:
db_name = self.config.get('test', 'mongo.db')
else:
db_name = self.config.get('mongo', 'db')
self.log.debug("Using db: %s" % db_name)
self.db = self.mongo_connection[db_name]
But, here's my problem: the handler isn't seeing the name or description arguments. :(
Any suggestions?
As a work-around, I found them in the request.body and parsed the encoded parameters manually. It was kindof annoying, but it works.
new_values = urlparse.parse_qs(self.request.body)
# values show as lists with only one item
for k in new_values:
new_values[k] = new_values[k][0]
Say if you are using jQuery to send this PUT request:
$.ajax({
type: "PUT",
url: "/yourURL",
data: JSON.stringify({'json':'your json here'),
dataType: 'json'
})
The data should not be like:
data: {'json': 'your json here'}, because it will automatically be encoded into query string, which needs to be parsed by parse_qs
Then in Tornado
def put(self, pid):
d = json.loads(self.request.body)
print d
put handler will parse request.body, if request had proper content-type header (application/x-www-form-urlencoded), for example if you are using tornado http client:
headers = HTTPHeaders({'content-type': 'application/x-www-form-urlencoded'})
http_client.fetch(
HTTPRequest(url, 'PUT', body=urllib.urlencode(body), headers=headers))
Have you tried using a get method instead? Because depending on how you test your program, if you test it via your browser like Firefox or Chrome, they might be able to do it. Doing a HTTP PUT from a browser
If I were you I would write get instead of put. Cause then you can definitely test it in your browser.
For example, instead of:
def put ...
Try:
def get ...
Or Actually in your:
name = self.get_argument('name', None)
description = self.get_argument('description', None)
Why is the None there? According to the documentation:
RequestHandler.get_argument(name, default=[], strip=True)
...
If default is not provided, the argument is considered to be required,
and we throw an HTTP 400 exception if it is missing.
So in your case because you are not providing a proper default, therefore your app is returning HTTP 400. Miss out the default! (i.e.)
name = self.get_argument('name')
description = self.get_argument('description')

Categories

Resources