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.
Related
I've a slack app that is sending to a service written in typescript that is forwarding the message to my python script where I'm trying to validate the request. However, for some reason, the validation always fails.
The typescript relevant code:
const rp = require('request-promise');
var qs = require('querystring')
export const handler = async (event: any, context: Context, callback: Callback): Promise<any> => {
const options = {
method: method,
uri: some_url,
body: qs.parse(event.body),
headers: {
signature: event.headers['X-Slack-Signature'],
timestamp: event.headers['X-Slack-Request-Timestamp']
},
json: true
};
return rp(options);
The python code (based on this article) :
def authenticate_message(self, request: Request) -> bool:
slack_signing_secret = bytes(SLACK_SIGNING_SECRET, 'utf-8')
slack_signature = request.headers['signature']
slack_timestamp = request.headers['timestamp']
request_body = json.loads(request.body)['payload']
basestring = f"v0:{slack_timestamp}:{request_body}".encode('utf-8')
my_signature = 'v0=' + hmac.new(slack_signing_secret, basestring, hashlib.sha256).hexdigest()
return hmac.compare_digest(my_signature, slack_signature))
I'm pretty sure the issue is the way I'm taking the body but tried several options and still no luck.
Any ideas?
Thanks,
Nir.
I had the same issue. My solution was to parse the payload to replace '/' by %2F and ':' by %3A. It's not explicit in the Slack doc but if you see the example, that's how it's shown:
'v0:1531420618:token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c'
You see command and response_url are parsed.
I managed to get this working in Python. I see you ask in Typescript, but I hope this python script helps:
#app.route('/slack-validation', methods=['GET', 'POST'])
def slack_secutiry():
headers = request.headers
timestamp = request.headers['X-Slack-Request-Timestamp']
slack_payload = request.form
dict_slack = slack_payload.to_dict()
### This is the key that solved the issue for me, where urllib.parse.quote(val, safe='')] ###
payload= "&".join(['='.join([key, urllib.parse.quote(val, safe='')]) for key, val in dict_slack.items()])
### compose the message:
sig_basestring = 'v0:' + timestamp + ':' + payload
sig_basestring = sig_basestring.encode('utf-8')
### secret
signing_secret = slack_signing_secret.encode('utf-8') # I had an env variable declared with slack_signing_secret
my_signature = 'v0=' + hmac.new(signing_secret, sig_basestring, hashlib.sha256).hexdigest()
print('my signature: ')
print(my_signature)
return '', 200
It might be useful for you to check how the request validation feature is implemented in the Bolt framework:
https://github.com/slackapi/bolt-python/blob/4e0709f0578080833f9aeab984a778be81a30178/slack_bolt/middleware/request_verification/request_verification.py
Note that it is implemented as a middleware, enabled by default when you instantiate the app (see attribute request_verification_enabled).
You can inspect this behaviour and/or change it if you want to validate the requests manually:
app = App(
token=SLACK_BOT_TOKEN,
signing_secret=SLACK_SIGNING_SECRET,
request_verification_enabled=False
)
The following solution solves the problem of verification of signing secret of slack
#!/usr/bin/env python3
import hashlib
import hmac
import base64
def verify_slack_request(event: dict, slack_signing_secret: str) -> bool:
"""Verify slack requests.
Borrowed from https://janikarhunen.fi/verify-slack-requests-in-aws-lambda-and-python.html
- Removed optional args
- Checks isBase64Encoded
:param event: standard event handler
:param slack_signing_secret: slack secret for the slash command
:return: True if verification worked
"""
slack_signature = event['headers']['x-slack-signature']
slack_time = event['headers']['x-slack-request-timestamp']
body = event['body']
if event['isBase64Encoded']:
body = base64.b64decode(body).decode("utf-8")
""" Form the basestring as stated in the Slack API docs. We need to make a bytestring"""
base_string = f'v0:{slack_time}:{body}'.encode('utf-8')
""" Make the Signing Secret a bytestring too. """
slack_signing_secret = bytes(slack_signing_secret, 'utf-8')
""" Create a new HMAC 'signature', and return the string presentation."""
my_signature = 'v0=' + hmac.new(
slack_signing_secret, base_string, hashlib.sha256
).hexdigest()
''' Compare the the Slack provided signature to ours.
If they are equal, the request should be verified successfully.
Log the unsuccessful requests for further analysis
(along with another relevant info about the request).'''
result = hmac.compare_digest(my_signature, slack_signature)
if not result:
logger.error('Verification failed. my_signature: ')
logger.error(f'{my_signature} != {slack_signature}')
return result
if __name__ == '__main__':
# add correct params here
print(verify_slack_request({}, None))
Borrowed From:
https://gist.github.com/nitrocode/288bb104893698011720d108e9841b1f
Credits: https://gist.github.com/nitrocode
I wrote a Python script that compares a user input to database queries, then either allows a login or doesn't. I will break down my problem so that it is easier to understand:
My database has the passwords automatically encrypted for security reasons.
The script queries the database and then the encrypted password is stored in Python
If the correct password that the user has to type in in order to login is encrypted, and the user has to type in a normal (unencrypted) password, how do I then decrypt the password inside Python?
Clarifications:
My program encrypts and decrypts the password and unique id of the user already for double security.
I want everything to be as secure as possible. I started object orientated Python not very long ago, so please feel free to be as harsh as possible.
I'm making this unprofessionally, but it will reach production!
Information
I'm using MySQL for the database, Python 3.7 for the scripting, and Flask for sessions.
# Imports
from passlib.context import CryptContext
import mysql.connector
import json
from pprint import pprint
# Config file loaded as a json
with open("database_connect.json") as config:
config = json.load(config)
config = config[0]
try:
# Json is argued as a kwarg
cnx = mysql.connector.connect(**config)
cursor = cnx.cursor()
# Query is made
cursor.execute("SELECT first,id,pwd,uid FROM user")
args = cursor.fetchone()
# Any integer queries have the string function mapped to them
args = tuple(map(lambda i: str(i), args))
except:
print("Connection error!")
finally:
cnx.close()
# Passlib encryption method
passlib_context = CryptContext(
schemes=["pbkdf2_sha256"],
default="pbkdf2_sha256",
pbkdf2_sha256__default_rounds=300000)
# Base class for all users
class default:
priviliges = {
"Ban": False,
"Kick": False,
"Broadcast": False,
"Iplookup": False }
# Instantiating the default class
def __init__(self, name, uniqueid, pwd, usernameid):
self.name = name
self.__pwd = passlib_context.hash(pwd)
self.__uniqueid = passlib_context.hash(uniqueid)
self.usernameid = usernameid
# Encryption method for users
def encrypt_method(self):
encrypt_data = [self.__pwd, self.__uniqueid]
return encrypt_data
class decrypt():
# Instantiating the decryption class
def __init__(self, attempted_pwd, hashpwd):
self.__attempted_pwd = attempted_pwd
self.__hashpwd = hashpwd
# Decryption method for decryption class
def decrypt_method(self):
decrypt_data = passlib_context.verify(self.__attempted_pwd, self.__hashpwd)
if decrypt_data is False:
allow_login = False
elif decrypt_data is True:
allow_login = True
return allow_login
# Information fetched from the database in tuple form, used as an argument
user = default(*args)
# Attempt corresponds to user input. The first argument is the attempted password.
attempt = decrypt("",user.encrypt_method()[0])
print(attempt.decrypt_method())
I am writing REST api in flask for the first time,
so now I have something like this:
import uuid
import pytz
from datetime import datetime
from flask_restplus import Resource, Api, fields
from ..models import publicip_schema
from ..controller import (
jsonified,
get_user_ip,
add_new_userIp,
get_specificIp,
get_all_publicIp
)
from flask import request, jsonify
from src import app
from src import db
from src import models
api = Api(app, endpoint="/api", versio="0.0.1", title="Capture API", description="Capture API to get, modify or delete system services")
add_userIp = api.model("Ip", {"ip": fields.String("An IP address.")})
get_userIp = api.model("userIp", {
"ipid": fields.String("ID of an ip address."),
"urlmap" : fields.String("URL mapped to ip address.")
})
class CaptureApi(Resource):
# decorator = ["jwt_required()"]
# #jwt_required()
#api.expect(get_userIp)
def get(self, ipid=None, urlmap=None):
"""
this function handles request to provide all or specific ip
:return:
"""
# handle request to get detail of site with specific location id.
if ipid:
ipobj = get_user_ip({"id": ipid})
return jsonified(ipobj)
# handle request to get detail of site based on site abbreviation
if urlmap:
locate = get_user_ip({"urlmap": urlmap})
return jsonified(locate)
return jsonify(get_all_publicIp())
# #jwt_required()
#api.expect(add_userIp)
def post(self, username=None):
"""
Add a new location.
URI /location/add
:return: json response of newly added location
"""
data = request.get_json(force=True)
if not data:
return jsonify({"status": "no data passed"}), 200
if not data["ip"]:
return jsonify({"status" : "please pass the new ip you want to update"})
if get_user_ip({"ipaddress": data["ip"]}):
return jsonify({"status": "IP: {} is already registered.".format(data["ip"])})
_capIpObj = get_user_ip({"user_name": username})
if _capIpObj:
# update existing ip address
if "ip" in data:
if _capIpObj.ipaddress == data["ip"]:
return jsonify({"status": "nothing to update."}), 200
else:
_capIpObj.ipaddress = data["ip"]
else:
return jsonify({
"status" : "please pass the new ip you want to update"
})
db.session.commit()
return jsonified(_capIpObj)
else:
device = ""
service = ""
ipaddress = data["ip"]
if "port" in data:
port = data["port"]
else:
port = 80
if "device" in data:
device = data["device"]
if "service" in data:
service = data["service"]
date_modified = datetime.now(tz=pytz.timezone('UTC'))
urlmap = str(uuid.uuid4().get_hex().upper()[0:8])
new_public_ip = add_new_userIp(username, ipaddress, port, urlmap, device, service, date_modified)
return publicip_schema.jsonify(new_public_ip)
api.add_resource(
CaptureApi,
"/getallips", # GET
"/getip/id/<ipid>", # GET
"/getip/urlmap/<urlmap>", # GET
"/updateip/username/<username>" # POST
)
I have faced two problems
if I specify
get_userIp = api.model("userIp", {
"ipid": fields.String("ID of an ip address."),
"urlmap" : fields.String("URL mapped to ip address.")
})
and add #api.expect(get_userIp) on get method above. I am forced to pass optional parameters with any value (even to get list of all ip's i.e. from "/getallips"): see screenshot below.
but these option parameters are not required tog et all IP's, but I do need to use those parameters to get ip based on ipid, or urlmap using the get method.
looking at swagger documentation generated by flask_restplus.Api I am seeing
get and post for all the endpoints, whereas I have defined endpoint get and post only. So technically updateip/username/<username> should not be listing get
How do I fix this ?
Good question! You can fix both problems by defining separate Resource subclasses for each of your endpoints. Here is an example where I split the endpoints for "/getallips", "/getip/id/", and "/getip/urlmap/".
Ip = api.model("Ip", {"ip": fields.String("An IP address.")})
Urlmap = api.model("UrlMap", {"urlmap": fields.String("URL mapped to ip address.")})
#api.route("/getallips")
class IpList(Resource):
def get(self):
return jsonify(get_all_publicIp())
#api.route("/getip/id/<ipid>")
class IpById(Resource):
#api.expect(Ip)
def get(self, ipid):
ipobj = get_user_ip({"id": ipid})
return jsonified(ipobj)
#api.route("/getip/urlmap/<urlmap>")
class IpByUrlmap(Resource):
#api.expect(Urlmap)
def get(self, urlmap):
ipobj = get_user_ip({"id": ipid})
return jsonified(ipobj)
Notice that you solve your expect problem for free - because each endpoint now fully defines its interface, it's easy to attach a clear expectation to it. You also solve your "get and post defined for endpoints that shouldn't", you can decide for each endpoint whether it should have a get or post.
I'm using the api.route decorator instead of calling api.add_resource for each class because of personal preference. You can get the same behavior by calling api.add_resource(<resource subclass>, <endpoint>) for each new Resource subclass (e.g. api.add_resource(IpList, "/getallips"))
I have asked a few questions about this before, but still haven't solved my problem.
I am trying to allow Salesforce to remotely send commands to a Raspberry Pi via JSON (REST API). The Raspberry Pi controls the power of some RF Plugs via an RF Transmitter called a TellStick. This is all setup, and I can use Python to send these commands. All I need to do now is make the Pi accept JSON, then work out how to send the commands from Salesforce.
Someone kindly forked my repo on GitHub, and provided me with some code which should make it work. But unfortunately it still isn't working.
Here is the previous question: How to accept a JSON POST?
And here is the forked repo: https://github.com/bfagundez/RemotePiControl/blob/master/power.py
What do I need to do? I have sent test JSON messages n the Postman extension and in cURL but keep getting errors.
I just want to be able to send various variables, and let the script work the rest out.
I can currently post to a .py script I have with some URL variables, so /python.py?power=on&device=1&time=10&pass=whatever and it figures it out. Surely there's a simple way to send this in JSON?
Here is the power.py code:
# add flask here
from flask import Flask
app = Flask(__name__)
app.debug = True
# keep your code
import time
import cgi
from tellcore.telldus import TelldusCore
core = TelldusCore()
devices = core.devices()
# define a "power ON api endpoint"
#app.route("/API/v1.0/power-on/<deviceId>",methods=['POST'])
def powerOnDevice(deviceId):
payload = {}
#get the device by id somehow
device = devices[deviceId]
# get some extra parameters
# let's say how long to stay on
params = request.get_json()
try:
device.turn_on()
payload['success'] = True
return payload
except:
payload['success'] = False
# add an exception description here
return payload
# define a "power OFF api endpoint"
#app.route("/API/v1.0/power-off/<deviceId>",methods=['POST'])
def powerOffDevice(deviceId):
payload = {}
#get the device by id somehow
device = devices[deviceId]
try:
device.turn_off()
payload['success'] = True
return payload
except:
payload['success'] = False
# add an exception description here
return payload
app.run()
Your deviceID variable is a string, not an integer; it contains a '1' digit, but that's not yet an integer.
You can either convert it explicitly:
device = devices[int(deviceId)]
or tell Flask you wanted an integer parameter in the route:
#app.route("/API/v1.0/power-on/<int:deviceId>", methods=['POST'])
def powerOnDevice(deviceId):
where the int: part is a URL route converter.
Your views should return a response object, a string or a tuple instead of a dictionary (as you do now), see About Responses. If you wanted to return JSON, use the flask.json.jsonify() function:
# define a "power ON api endpoint"
#app.route("/API/v1.0/power-on/<int:deviceId>", methods=['POST'])
def powerOnDevice(deviceId):
device = devices[deviceId]
# get some extra parameters
# let's say how long to stay on
params = request.get_json()
try:
device.turn_on()
return jsonify(success=True)
except SomeSpecificException as exc:
return jsonify(success=False, exception=str(exc))
where I also altered the exception handler to handle a specific exception only; try to avoid Pokemon exception handling; do not try to catch them all!
To retrieve the Json Post values you must use request.json
if request.json and 'email' in request.json:
request.json['email']
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