Bottle.py session with Beaker - python

first time questioner here.
I'm currently struggling on how to use Beaker properly using the Bottle micro-framework. Here's the problematic program:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# filename: server.py
import bottle as app
from beaker.middleware import SessionMiddleware
session_options = {
'session.type': 'file',
'session.data_dir': './session/',
'session.auto': True,
}
app_middlware = SessionMiddleware(app.app(), session_options)
app_session = app.request.environ.get('beaker.session')
#app.route('/login')
def login():
app_session = app.request.environ.get('beaker.session')
app_session['logged_in'] = True
#app.route('/logout')
def logout():
app_session = app.request.environ.get('beaker.session')
if app_session.get('logged_in'):
app_session['logged_in'] = False
return 'You are logged out'
app.redirect('/login')
#app.route('/dashboard')
def dashboard():
app_session = app.request.environ.get('beaker.session')
if app_session.get('logged_in'):
return 'You are logged in'
app.redirect('/login')
app.debug(True)
app.run(app=app_middlware, reloader=True)
If you noticed, I keep on calling app_session = app.request.environ.get('beaker.session') on every def block so just it will not return an error like: TypeError: 'NoneType' object does not support item assignment --- it seems that Python doesn't recognize variables that is outside its function (correct me if I'm wrong).
And here are the questions:
What should I do to only have one instance of app_session = app.request.environ.get('beaker.session') so it can be available to every def blocks (I really need one instance since it's the same session to be checked and used).
If this is the only way (it's ugly though), then should I just combine all routes that requires a session just so I can achieve the single instance of app_session?
Something like:
#app.route('/<path:re:(login|dashboard|logout)\/?>')
def url(path):
app_session = app.request.environ.get('beaker.session')
if 'login' in path:
app_session['logged_in'] = True
elif 'logout' in path:
if app_session.get('logged_in'):
# app_session.delete() it doesn't seem to work?
app_session['logged_in'] = False
return 'You are logged out'
app.redirect('/login')
elif 'dashboard' in path:
if app_session.get('logged_in'):
return 'You are logged in'
app.redirect('/login')

Using beaker in your bottle application is easy. First, set up your Bottle app:
import bottle
from bottle import request, route, hook
import beaker.middleware
session_opts = {
'session.type': 'file',
'session.data_dir': './session/',
'session.auto': True,
}
app = beaker.middleware.SessionMiddleware(bottle.app(), session_opts)
And later on:
bottle.run(app=app)
With this in place, every time you receive a request, your Beaker session will be available as
request.environ['beaker_session']. I usually do something like this for convenience:
#hook('before_request')
def setup_request():
request.session = request.environ['beaker.session']
This arranges to run setup_request before handling any request; we're using the bottle.request variable (see the earlier import statement), which is a thread-local variable with information about the current request. From this point on, I can just refer to request.session whenever I need it, e.g.:
#route('/')
def index():
if 'something' in request.session:
return 'It worked!'
request.session['something'] = 1

Related

I am getting key error when trying to access flask session data from another handler function

I am using dialogflow fulfillment flask package to build a simple chatbot. When I try to access the session variable in the def goalName(agent) handler that I set previously in the get_Name_ID(agent) handler, I get a key error message from the Heroku logs.
here is the webhook I am using:
#app.route('/webhook', methods=['POST', 'GET'])
def webhook() -> Dict:
"""Handle webhook requests from Dialogflow."""
# Get WebhookRequest object
request_ = request.get_json(force=True)
# Log request headers and body
logger.info(f'Request headers: {dict(request.headers)}')
logger.info(f'Request body: {request_}')
# Handle request
agent = WebhookClient(request_)
action = request_.get('queryResult').get('action')
if action == "get.secret.key":
agent.handle_request(get_Name_ID)
if action == "goal.setting.name":
agent.handle_request(goalName)
here is the first handler function
def get_Name_ID(agent):
task = TASK.query.filter_by(status="active").first()
if not USER.query.filter_by(passcode = agent.parameters["id"]).first():
user = USER(agent.parameters["id"], agent.parameters["name"])
db.session.add(user)
db.session.commit()
# store variables into session for later usage
key = id_generator()
user_session = SESSION(task.id, key)
db.session.add(user_session)
db.session.flush()
# store values to session
session['s_id'] = user_session.id
session['u_id'] = agent.parameters["id"]
session['user_name'] = agent.parameters["name"]
db.session.commit()
here is the second handler function:
def goalName(agent):
task = TASK.query.filter_by(status="active").first()
# print(type(redish.get('u_id')))
# print(redish.get('u_id'))
# get values from session
uid = session['u_id']
sid = session['s_id']
goal = GOAL(uid, task.id, sid, agent.parameters["goalName"], "", "", "", "", "")
db.session.add(goal)
db.session.flush()
session['goal_id'] = goal.id
db.session.commit()
I have setup the flask-session in the following manner:
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') or \
'e5ac358c-f0bf-11e5-9e39-d3b532c10a28'
app.config['SESSION_TYPE'] = 'sqlalchemy'
db = SQLAlchemy(app)
app.config['SESSION_SQLALCHEMY'] = db
sess = Session(app)
I have tried the following methods:
removing the flask-session package and using built-in flask session but with no success.
I have set up simple routes to test the session and it was working fine. But it fails to work within the handler functions.
I am getting the key error when accessing session data from the second handler:
_ 2021-08-05T10:47:48.928371+00:00 app[web.1]: return super().getitem(key) 2021-08-05T10:47:48.928372+00:00 app[web.1]: KeyError: 'u_id
I am not sure what is going on? Any help would be much appreciated!
You can use redis server for session. It Will be solved your issue

TypeError: encoding without a string argument on twitter account activity api

I am setting up a flask server that will act as a webhook to the twitter account activity api. However I came up with this issue that I have no idea how to solve, I'm fairly new to programming so please bear with me. I just used this repository https://github.com/RickRedSix/twitter-webhook-boilerplate-python/blob/master/Main.py
This is the error:
line 28, in twitterCrcValidation
key=bytes(CONSUMER_SECRET, encoding ='utf-8'),
TypeError: encoding without a string argument
Here's the code:
#!/usr/bin/env python
from flask import Flask, request, send_from_directory, make_response
from http import HTTPStatus
import Twitter, hashlib, hmac, base64, os, logging, json
from dotenv import load_dotenv
load_dotenv('.env')
CONSUMER_SECRET = os.getenv('CONSUMER_SECRET')
CURRENT_USER_ID = os.getenv('CURRENT_USER_ID')
app = Flask(__name__)
#generic index route
#app.route('/')
def default_route():
return send_from_directory('www', 'index.html')
#The GET method for webhook should be used for the CRC check
#TODO: add header validation (compare_digest https://docs.python.org/3.6/library/hmac.html)
#app.route("/webhook", methods=["GET"])
def twitterCrcValidation():
crc = request.args['crc_token']
validation = hmac.new(
key=bytes(CONSUMER_SECRET, encoding ='utf-8'),
msg=bytes(crc, encoding = 'utf-8'),
digestmod = hashlib.sha256
)
digested = base64.b64encode(validation.digest())
response = {
'response_token': 'sha256=' + format(str(digested)[2:-1])
}
print('responding to CRC call')
return json.dumps(response)
#The POST method for webhook should be used for all other API events
#TODO: add event-specific behaviours beyond Direct Message and Like
#app.route("/webhook", methods=["POST"])
def twitterEventReceived():
requestJson = request.get_json()
#dump to console for debugging purposes
print(json.dumps(requestJson, indent=4, sort_keys=True))
if 'favorite_events' in requestJson.keys():
#Tweet Favourite Event, process that
likeObject = requestJson['favorite_events'][0]
userId = likeObject.get('user', {}).get('id')
#event is from myself so ignore (Favourite event fires when you send a DM too)
if userId == CURRENT_USER_ID:
return ('', HTTPStatus.OK)
Twitter.processLikeEvent(likeObject)
elif 'direct_message_events' in requestJson.keys():
#DM recieved, process that
eventType = requestJson['direct_message_events'][0].get("type")
messageObject = requestJson['direct_message_events'][0].get('message_create', {})
messageSenderId = messageObject.get('sender_id')
#event type isnt new message so ignore
if eventType != 'message_create':
return ('', HTTPStatus.OK)
#message is from myself so ignore (Message create fires when you send a DM too)
if messageSenderId == CURRENT_USER_ID:
return ('', HTTPStatus.OK)
Twitter.processDirectMessageEvent(messageObject)
else:
#Event type not supported
return ('', HTTPStatus.OK)
return ('', HTTPStatus.OK)
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 65010.
port = int(os.environ.get('PORT', 65010))
gunicorn_logger = logging.getLogger('gunicorn.error')
app.logger.handlers = gunicorn_logger.handlers
app.logger.setLevel(gunicorn_logger.level)
app.run(host='0.0.0.0', port=port, debug=True)
You need to verify input to your program and environment variables are no exception. As a minumum, check that these variables actually exist. os.getenv returns None if the environment variable doesn't exist. None is not a string and thus your error. You could do this with a slightly different os call.
CONSUMER_SECRET = os.environ['CONSUMER_SECRET']
CURRENT_USER_ID = os.environ['CURRENT_USER_ID']
Now an exception is raised on failure. This could be wrapped in an exception handler if you want different error reporting than the standard traceback.

How to store cherrypy.session() in a variable?

First, I store an email in my session:
#cherrypy.expose
def setter(self):
email = "email#email.com"
cherrypy.session["email"] = email
return "Variable passed to session" // This works fine!
Second, I return the session data:
#cherrypy.expose
def getter(self):
return cherrypy.session("email") // This works fine too!
But now, I would like to store this data in a variable and return it:
#cherrypy.expose
def getter(self):
variable = cherrypy.session("email")
return variable
When doing this, I get a 500 Internal: KeyError 'variable'
Don't forget to turn sessions on in the config. It's disabled by default. Also, you use cherrypy.session as a dictionary, it's not a function you call.
Take this example code:
# rimoldi.py
import cherrypy
class TestApp:
#cherrypy.expose
def setter(self):
email = "email#email.com"
cherrypy.session["email"] = email
return 'Variable stored in session object. Now check out the getter function'
#cherrypy.expose
def getter(self):
return "The email you set earlier, was " + cherrypy.session.get("email")
if __name__ == '__main__':
cherrypy.quickstart(TestApp(), "/", {
"/": {
"tools.sessions.on": True,
}
})
You run the above example with:
python rimoldi.py
CherryPy says:
[09/Jan/2017:16:34:32] ENGINE Serving on http://127.0.0.1:8080
[09/Jan/2017:16:34:32] ENGINE Bus STARTED
Now point your browser at http://127.0.0.1:8080/setter, and you'll see:
Variable stored in session object. Now check out the getter function
Click the 'getter' link. The browser shows:
The email you set earlier, was email#email.com
Voila! This is how you use sessions in CherryPy. I hope this example helps you.

using Python with Twilio how can I implement reject() and callback?

I want to call my Twilio number from my cellphone, Twilio recognizes my number, rejects the call and then calls me back. Here is the code:
#application.route("/test", methods=['GET', 'POST'])
def test():
whitelist = ['81808730xxxx', '44753810xxxx', '+44753810xxxx', '+44792834xxxx', '44792834xxxx']
call_from = request.values.get('From', 'None')
if call_from in whitelist:
# "<Response><Reject /></Response>"
resp = twilio.twiml.Response()
resp.reject()
time.sleep( 10 )
account_sid = "account_sid"
auth_token = "account_auth_token"
client = TwilioRestClient(account_sid, auth_token)
call = client.calls.create(to=call_from, from_="+1646480xxxx", url="https://zshizuka.herokuapp.com/gather")
print call.sid
else:
resp = twilio.twiml.Response()
call_to = "+44792834xxxx"
resp.dial(call_to)
#return json.dumps({'success':True}), 200
return str(resp)
This produces the dreaded response: "We are sorry a system error has occurred. Goodbye".
If, however, I dial the number and hangup after the first ring it works perfectly. So I am assuming that the problem is with the reject verb. As you can see I have tried with and without twiml.
I would like to get a busy signal, call rejected (so no cost to cell phone bill) and then callback.
Grateful for help.
I'd love to better understand your use case but I'm going to give this a go using the Twilio Python Helper Library.
As far as <Reject> TwiML goes, you can do something like this and be sure to specify a reason setting it to 'busy':
from flask import Flask
from flask import request
from twilio import twiml
account_sid = "YOU_ACCOUNT_SID"
auth_token = "YOUR_ACCOUNT_TOKEN"
client = TwilioRestClient(account_sid, auth_token)
app = Flask(__name__)
#app.route('/test', methods=["GET", "POST"])
def test():
whitelist = ['+14151XXXXXX', '+14152XXXXXX']
call_from = request.form['From']
if call_from in whitelist:
resp = twiml.Response()
resp.reject(reason="busy")
return str(resp)
else:
resp = twiml.Response()
call_to = "+14153XXXXXX"
resp.dial(call_to)
return str(resp)
if __name__ == "__main__":
app.run()
As there is no callback within <Reject> you will have a few options as to how you can proceed.
If you're using Python 3.3 or greater you might consider doing the whole process with asyncio.
With older versions of Python, then I would recommend you try completing the client call through a task queue as demonstrated in this post.
Please let me know if this helps at all.

Flask Session loses key values

I have a trouble with Flask session. It loses values after redirect, here's code I'm using:
#app.route('/')
def index():
session['permanent'] = True
return flask.render_template('index.html') ##Here is template with form that passes value via GET to /station
#app.route('/station', methods=['GET'])
def station():
if 'st' not in session:
p = flask.request.args.get('Station', '')
session['st'] = p
print session['st']
if 'credentials' not in session:
return flask.redirect(flask.url_for('oauth2callback'))
credentials = client.OAuth2Credentials.from_json(session['credentials'])
if credentials.access_token_expired:
return flask.redirect(flask.url_for('oauth2callback'))
else:
##DO SOME STUFF WITH st and credentials##
return "Done!"
#app.route('/oauth2callback')
def oauth2callback():
flow = client.flow_from_clientsecrets('client_secret.json',scope='https://www.googleapis.com/auth/youtube',redirect_uri=flask.url_for('oauth2callback', _external=True))
if 'code' not in flask.request.args:
auth_uri = flow.step1_get_authorize_url()
return flask.redirect(auth_uri)
else:
auth_code = flask.request.args.get('code')
credentials = flow.step2_exchange(auth_code)
session['credentials'] = credentials.to_json()
return flask.redirect(flask.url_for('station'))
So what happens: Firts browser renders index.html. It's fine, user clicks something and passes the GET param to the station(). It goes thru the:
if 'st' not in session
session key is being set correctly. I see it in the print. Then user is being redirected to the /oauth2callback. There is whole auth process with youtube api and when script takes user back from /oauth2callback to /station via
return flask.redirect(flask.url_for('station'))
for some reason code enters the
if 'st' not in session
again. Like there was no session['st'] anymore. In the result I don't have the value from the form...
I have no clue what to do. (Using Flask 0.10 and hosting stuff on Heroku)

Categories